-
Notifications
You must be signed in to change notification settings - Fork 27
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #99 from boschresearch/38-cythonize-rainflow-recor…
…ders
- Loading branch information
Showing
15 changed files
with
362 additions
and
96 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
name: Build wheels and perform benchmarks | ||
|
||
on: | ||
pull_request: | ||
branches: [develop, master] | ||
|
||
env: | ||
CIBW_BUILD: cp37-* cp38-* cp39-* cp310-* cp311-* cp312-* | ||
CIBW_MANYLINUX_X86_64_IMAGE: manylinux2014 | ||
CIBW_MANYLINUX_I686_IMAGE: manylinux2014 | ||
|
||
jobs: | ||
build_wheels: | ||
name: Build wheels on for various systems | ||
runs-on: ${{ matrix.os }} | ||
strategy: | ||
matrix: | ||
os: [ubuntu-latest] #, windows-latest] | ||
|
||
steps: | ||
- uses: actions/checkout@v3 | ||
with: | ||
fetch-depth: 0 | ||
|
||
- uses: actions/setup-python@v4 | ||
name: Install Python | ||
with: | ||
python-version: '3.12' | ||
|
||
- name: Build wheels | ||
run: | | ||
pip wheel --no-deps -w dist . | ||
- uses: actions/upload-artifact@v4 | ||
with: | ||
name: wheel | ||
path: dist/ | ||
|
||
benchmark: | ||
name: Benchmark tests | ||
needs: [build_wheels] | ||
runs-on: ubuntu-latest | ||
|
||
steps: | ||
- uses: actions/checkout@v3 | ||
with: | ||
fetch-depth: 0 | ||
- uses: actions/download-artifact@v4 | ||
with: | ||
name: wheel | ||
path: dist/ | ||
- uses: actions/setup-python@v4 | ||
name: Install Python | ||
with: | ||
python-version: '3.12' | ||
- name: Install wheel | ||
run: pip install $(ls -1 dist/*.whl) | ||
- name: Install pytest | ||
run: pip install pytest pytest-cov | ||
- name: Generate test signal | ||
run: python benchmarks/generate_time_signal.py | ||
- name: Run benchmarks | ||
run: pytest --no-cov -rP benchmarks |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -61,4 +61,5 @@ coverage_report* | |
|
||
.hypothesis | ||
.mutmut-cache | ||
.python-version | ||
.python-version | ||
/load_signal.csv |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
import pytest | ||
|
||
import time | ||
import numpy as np | ||
|
||
|
||
@pytest.fixture | ||
def benchmarker(): | ||
|
||
def the_benchmarker(rainflow_counter): | ||
load_signal = np.loadtxt('load_signal.csv') | ||
|
||
tic = time.perf_counter() | ||
|
||
rainflow_counter.process(load_signal) | ||
|
||
toc = time.perf_counter() | ||
elapsed = toc - tic | ||
|
||
classname = rainflow_counter.__class__.__name__ | ||
print(f"Processing {classname} took {elapsed:0.4f} seconds") | ||
|
||
return elapsed | ||
|
||
return the_benchmarker |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
|
||
import numpy as np | ||
import pylife.stress.timesignal as TS | ||
|
||
np.random.seed(23424711) | ||
|
||
load_signal = TS.TimeSignalGenerator( | ||
10, | ||
{ | ||
'number': 50_000, | ||
'amplitude_median': 50.0, | ||
'amplitude_std_dev': 0.5, | ||
'frequency_median': 4, | ||
'frequency_std_dev': 3, | ||
'offset_median': 0, | ||
'offset_std_dev': 0.4, | ||
}, | ||
None, | ||
None, | ||
).query(1_000_000) | ||
|
||
np.savetxt('load_signal.csv', load_signal) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
import pylife.stress.rainflow as RF | ||
|
||
|
||
def test_threepoint(benchmarker): | ||
benchmark_time = 0.05 # seconds | ||
elapsed = benchmarker(RF.ThreePointDetector(recorder=RF.FullRecorder())) | ||
|
||
assert ( | ||
elapsed < benchmark_time | ||
), f"Benchmark time of {benchmark_time} s not exceeded. Needed {elapsed:0.4f} s." | ||
|
||
|
||
def test_fourpoint(benchmarker): | ||
benchmark_time = 0.05 # seconds | ||
elapsed = benchmarker(RF.FourPointDetector(recorder=RF.FullRecorder())) | ||
|
||
assert ( | ||
elapsed < benchmark_time | ||
), f"Benchmark time of {benchmark_time} s not exceeded. Needed {elapsed:0.4f} s." |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,138 @@ | ||
|
||
|
||
cimport cython | ||
import numpy as np | ||
from libc.math cimport fabs | ||
|
||
|
||
@cython.boundscheck(False) # Deactivate bounds checking | ||
@cython.wraparound(False) # Deactivate negative indexing. | ||
def fourpoint_loop(double [::1] turns, size_t [::1] turns_index): | ||
cdef Py_ssize_t len_turns = len(turns) | ||
|
||
from_vals = np.empty(len_turns//2, dtype=np.float64) | ||
to_vals = np.empty(len_turns//2, dtype=np.float64) | ||
from_index = np.empty(len_turns//2, dtype=np.uintp) | ||
to_index = np.empty(len_turns//2, dtype=np.uintp) | ||
|
||
cdef double [::1] from_vals_v = from_vals | ||
cdef double [::1] to_vals_v = to_vals | ||
cdef size_t [::1] from_index_v = from_index | ||
cdef size_t [::1] to_index_v = to_index | ||
|
||
residual_index = np.empty(len_turns, dtype=np.uintp) | ||
cdef size_t [::1] residual_index_v = residual_index | ||
|
||
residual_index_v[0] = 0 | ||
residual_index_v[1] = 1 | ||
|
||
cdef size_t i = 2 | ||
cdef size_t ri = 2 | ||
cdef size_t t = 0 | ||
|
||
cdef double a | ||
cdef double b | ||
cdef double c | ||
cdef double d | ||
cdef double ab | ||
cdef double bc | ||
cdef double cd | ||
|
||
while i < len_turns: | ||
if ri < 3: | ||
residual_index_v[ri] = i | ||
ri += 1 | ||
i += 1 | ||
continue | ||
|
||
a = turns[residual_index_v[ri-3]] | ||
b = turns[residual_index_v[ri-2]] | ||
c = turns[residual_index_v[ri-1]] | ||
d = turns[i] | ||
|
||
ab = fabs(a - b) | ||
bc = fabs(b - c) | ||
cd = fabs(c - d) | ||
if bc <= ab and bc <= cd: | ||
from_vals_v[t] = b | ||
to_vals_v[t] = c | ||
|
||
ri -= 1 | ||
to_index_v[t] = turns_index[residual_index_v[ri]] | ||
ri -= 1 | ||
from_index_v[t] = turns_index[residual_index_v[ri]] | ||
t += 1 | ||
continue | ||
|
||
residual_index_v[ri] = i | ||
ri += 1 | ||
i += 1 | ||
|
||
return from_vals[:t], to_vals[:t], from_index[:t], to_index[:t], residual_index[:ri] | ||
|
||
|
||
cpdef double _max(double a, double b): | ||
return a if a > b else b | ||
|
||
|
||
@cython.boundscheck(False) # Deactivate bounds checking | ||
@cython.wraparound(False) # Deactivate negative indexing. | ||
def threepoint_loop(double [::1] turns, size_t [::1] turns_index, size_t highest_front, | ||
size_t lowest_front, size_t residual_length): | ||
cdef Py_ssize_t len_turns = len(turns) | ||
|
||
from_vals = np.empty(len_turns//2, dtype=np.float64) | ||
to_vals = np.empty(len_turns//2, dtype=np.float64) | ||
from_index = np.empty(len_turns//2, dtype=np.uintp) | ||
to_index = np.empty(len_turns//2, dtype=np.uintp) | ||
|
||
cdef double [::1] from_vals_v = from_vals | ||
cdef double [::1] to_vals_v = to_vals | ||
cdef size_t [::1] from_index_v = from_index | ||
cdef size_t [::1] to_index_v = to_index | ||
|
||
residual_index = np.empty(len_turns, dtype=np.uintp) | ||
residual_index[:residual_length] = np.arange(residual_length) | ||
cdef size_t [::1] residual_index_v = residual_index | ||
|
||
residual_index_v[0] = 0 | ||
residual_index_v[1] = 1 | ||
|
||
cdef size_t ri = 2 | ||
cdef size_t t = 0 | ||
|
||
cdef size_t back = residual_index_v[1] + 1 | ||
cdef size_t front | ||
cdef size_t start | ||
|
||
cdef double start_val | ||
cdef double front_val | ||
cdef double back_valstar | ||
|
||
while back < len_turns: | ||
if ri >= 2: | ||
start = residual_index_v[ri-2] | ||
front = residual_index_v[ri-1] | ||
start_val, front_val, back_val = turns[start], turns[front], turns[back] | ||
|
||
if front_val > turns[highest_front]: | ||
highest_front = front | ||
elif front_val < turns[lowest_front]: | ||
lowest_front = front | ||
elif (start >= _max(lowest_front, highest_front) and | ||
fabs(back_val - front_val) >= fabs(front_val - start_val)): | ||
from_vals_v[t] = start_val | ||
to_vals_v[t] = front_val | ||
|
||
from_index_v[t] = turns_index[start] | ||
to_index_v[t] = turns_index[front] | ||
|
||
t += 1 | ||
ri -= 2 | ||
continue | ||
|
||
residual_index[ri] = back | ||
ri += 1 | ||
back += 1 | ||
|
||
return from_vals[:t], to_vals[:t], from_index[:t], to_index[:t], residual_index[:ri] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.