Closed till-m closed 1 year ago
NB: Installation also fails on my linux machine and on github codespaces
Please check, if rfcnt-0.4.5 solves your problem.
Since the extension from now on no longer requires a specific numpy version, but is compiled against the current numpy version and thus depends on it, the installation cannot be run in an isolated environment. The latter would install a different numpy version and thus render the extension unusable.
Therefore, the --no-build-isolation
and --no-deps
options must be used during installation with pip.
Thanks for the help. It looks like I get one step further in the building process & I can install on GH codespaces (so presumably it works on Linux), but with MacOS I run into a different error now.
pip install rfcnt --no-build-isolation --no-deps
Collecting rfcnt
Using cached rfcnt-0.4.5.post1.tar.gz (225 kB)
Preparing metadata (pyproject.toml) ... done
Building wheels for collected packages: rfcnt
Building wheel for rfcnt (pyproject.toml) ... error
error: subprocess-exited-with-error
× Building wheel for rfcnt (pyproject.toml) did not run successfully.
│ exit code: 1
╰─> [31 lines of output]
running bdist_wheel
running build
running build_py
creating build
creating build/lib.macosx-11.0-arm64-cpython-310
creating build/lib.macosx-11.0-arm64-cpython-310/rfcnt
copying run_tests.py -> build/lib.macosx-11.0-arm64-cpython-310/rfcnt
copying __init__.py -> build/lib.macosx-11.0-arm64-cpython-310/rfcnt
copying run_examples.py -> build/lib.macosx-11.0-arm64-cpython-310/rfcnt
copying setup.py -> build/lib.macosx-11.0-arm64-cpython-310/rfcnt
copying utils.py -> build/lib.macosx-11.0-arm64-cpython-310/rfcnt
creating build/lib.macosx-11.0-arm64-cpython-310/rfcnt/tests
copying tests/__init__.py -> build/lib.macosx-11.0-arm64-cpython-310/rfcnt/tests
copying tests/examples.py -> build/lib.macosx-11.0-arm64-cpython-310/rfcnt/tests
copying tests/test_rfcnt.py -> build/lib.macosx-11.0-arm64-cpython-310/rfcnt/tests
creating build/lib.macosx-11.0-arm64-cpython-310/rfcnt/_ext
copying _ext/rfcnt_npy_1_19_5.cp39-win_amd64.pyd -> build/lib.macosx-11.0-arm64-cpython-310/rfcnt/_ext
copying _ext/rfcnt_npy_1_22_4.cp39-win_amd64.pyd -> build/lib.macosx-11.0-arm64-cpython-310/rfcnt/_ext
copying _ext/rfcnt_npy_1_21_6.cp39-win_amd64.pyd -> build/lib.macosx-11.0-arm64-cpython-310/rfcnt/_ext
copying _ext/rfcnt_npy_1_23_5.cp39-win_amd64.pyd -> build/lib.macosx-11.0-arm64-cpython-310/rfcnt/_ext
copying requirements.txt -> build/lib.macosx-11.0-arm64-cpython-310/rfcnt
copying README.md -> build/lib.macosx-11.0-arm64-cpython-310/rfcnt
copying LICENSE -> build/lib.macosx-11.0-arm64-cpython-310/rfcnt
copying tests/long_series.csv -> build/lib.macosx-11.0-arm64-cpython-310/rfcnt/tests
running build_ext
building 'rfcnt.rfcnt' extension
creating build/temp.macosx-11.0-arm64-cpython-310
creating build/temp.macosx-11.0-arm64-cpython-310/src
clang -Wno-unused-result -Wsign-compare -Wunreachable-code -DNDEBUG -fwrapv -O2 -Wall -fPIC -O2 -isystem /opt/homebrew/Caskroom/miniconda/base/envs/hydro/include -arch arm64 -fPIC -O2 -isystem /opt/homebrew/Caskroom/miniconda/base/envs/hydro/include -arch arm64 -DNPY_NO_DEPRECATED_API=NPY_1_7_API_VERSION -DRFC_HAVE_CONFIG_H=0 -DRFC_VERSION_MAJOR=0 -DRFC_VERSION_MINOR=4 -DRFC_USE_INTEGRAL_COUNTS=0 -DRFC_USE_HYSTERESIS_FILTER=1 -DRFC_MINIMAL=0 -DRFC_TP_SUPPORT=1 -DRFC_HCM_SUPPORT=1 -DRFC_ASTM_SUPPORT=1 -DRFC_USE_DELEGATES=1 -DRFC_GLOBAL_EXTREMA=1 -DRFC_DAMAGE_FAST=1 -DRFC_DH_SUPPORT=1 -DRFC_AT_SUPPORT=1 -DRFC_AR_SUPPORT=1 -DRFC_DEBUG_FLAGS=0 -DRFC_EXPORT_MEX=0 -Isrc -I/opt/homebrew/Caskroom/miniconda/base/envs/hydro/lib/python3.10/site-packages/numpy/core/include -I/opt/homebrew/Caskroom/miniconda/base/envs/hydro/include/python3.10 -c src/rainflow.c -o build/temp.macosx-11.0-arm64-cpython-310/src/rainflow.o -std=c++11
error: invalid argument '-std=c++11' not allowed with 'C'
error: command '/usr/bin/clang' failed with exit code 1
[end of output]
note: This error originates from a subprocess, and is likely not a problem with pip.
ERROR: Failed building wheel for rfcnt
Failed to build rfcnt
ERROR: Could not build wheels for rfcnt, which is required to install pyproject.toml-based projects
As far as I can tell, the compiler errors because the C++ specific flag is invalid for C files. I tried following the directions here, which allowed me to complete the installation, but I can't seem to run the code:
ImportError: dlopen(/opt/homebrew/Caskroom/miniconda/base/envs/hydro/lib/python3.10/site-packages/rfcnt/rfcnt.cpython-310-darwin.so, 0x0002): symbol not found in flat namespace '_RFC_class_count'
Unfortunately I have no experience with python/C or python/C++ interop, so I'm a bit out of my depth here. I can just use a linux VM to get the package to run, but it would of course be nice the have it work on Mac.
Installing works without problems now, so that's good, but importing the package fails:
$ python -m rfcnt.run_tests
/opt/homebrew/Caskroom/miniconda/base/envs/hydro/bin/python: Error while finding module specification for 'rfcnt.run_tests' (ImportError: dlopen(/opt/homebrew/Caskroom/miniconda/base/envs/hydro/lib/python3.10/site-packages/rfcnt/rfcnt.cpython-310-darwin.so, 0x0002): symbol not found in flat namespace '_RFC_class_count')
Did you surely import the rainflow.c in your extension? (see https://stackoverflow.com/a/41078737) Please let me see your modifications on setup.py.
Hmm I think I might have some caching issues with pip, I did modify setup.py
at some point, so maybe that's that. I tried replicating the problem using GH Workflow runners, see till-m/rainflow#1. Looks like wrapping the include
statement didn't solve the initial error :/ so we're back at the previous state. Sorry for the confusion.
Yes, I deleted my comment shortly after, since the extern "C" is already part of rainflow.hpp... Could you commit your changes on setup.py to your fork, so I could keep track?
Commit changes. I tried to build using the advice given here but I couldn't get it to work.
Please check recent commit, seems to work now.
I checked it locally and it seems to work! Thanks very much I appreciate the help!
Hi @a-ma72,
tested it some more and it looks good!
Is there any chance you have more documentation on the package available somewhere? The README lists quite a few things but even after researching the terms I don't know what they are exactly, Google doesn't seem to produce meaningful results. I guess a problem is that of the references do not seem to be available online, e.g. the HCM paper. I'm also having trouble with understanding the call signature of the rfc
function -- what keywords are optional, what the default values are, etc.
I know that this is probably mostly intended for internal use, so I hope I'm not bothering you too much.
Hi @till-m ,
no, questions are welcome.
You can call rfc
with the input series only, to get a rainflow counting with defaults. Additionally following keywords are supported:
class_count: int = 100
The numer of bins for counting. The range of the input series will be spread evenly over all classes (bins).class_offset: float = np.min(input_series)
The lower bound of the first bin. If offset and width are set manually, take into account that np.max(input_series) < class_offset + class_count * class_width
. Counting fails otherwise, if auto_resize
is not set to True.class_width: float = np.ptp(input_series) / class_count
The evenly width of all bins.hysteresis: float = class_width
The width of the hysteresis filter (also called "Rückstellbreite" in german)res_method: int = 7
How to deal with the residuum (non closed cycles)
0-3 = Ignore
4 = Related to ASTM, count as half cycles
5 = Count half cycles as full cycles
6 = Clormann/Seeger method
7 = Repeat residue and count closed cycles
8 = Count residue according to range pair in DIN-45667enforce_margin: bool = True | 1
Ensuring first and last turning point match to the input_series, disregarding hysteresis filtering.auto_resize: bool = False | 0
Expand the class range, if value range exeeds while counting. The width of the bins are kept.use_hcm: bool = False | 0
Use HCM (Cloorman/Seeger) counting method.use_astm: bool = False | 0
Use ASTM counting method.spread_damage: int = 8
How to distribute damage over turning points.
* (P4)
(P2) * / (P3c)
/ \ /
/ * (P3)
(P1) *
0 = Halfway damage each point (P2,P3) 1 = Damages for linear amplitude ramp over P2 to P3 2 = Damage linear distributed over P2 to P3 3 = Damages for linear amplitude ramp over P2 to P4 4 = Damage linear distributed over P2 to P4 5 = Full damage assigned to P2 6 = Full damage assigned to P3 7 = Damages transient distributed over P2 to P3 8 = Damages transient distributed over P2 to P3c
lc_method: int = 0
How to count level crossings.
0 = rising slopes only
1 = falling slopes only
2 = rising and falling slopeswl: dict = dict(sd=1000, nd=1e7, k=5, k2=k)
Definition of the Wöhler curve. k
defines the slope for sa>=sd and k2
for sa<sd.Thank you @a-ma72, this is super helpful!
I am particularly interested in a solid (i.e. physically realistic) historical description of the damage.
Based on your description I assume the most relevant parameter for this is spread_damage
, which dictates when the damage of one cycle will be counted. Would you say this is correct?
Additionally, I was wondering whether you know of any literature on this topic?
Also, can you confirm my interpretation of the results is correct?
damage
total damage done over entire input (yes)rp
ranges and number of cycles with corresponding range found in the series (yes)lc
Number of level crossings occuring in a particular bin? Presumably irrelevant for ASTM rainflow counting. (yes, level crossing is another discipline)tp
Turning points (t, y, ?) (yes, index-position, y-value of the turning point, and local damage increment)res
the y-coordinates of residual points, in t-order. (yes)res_raw
? (residuum before finally applying residuum methods)rfm
the rainflow matrix (yes)dh
the damage history (I noted that sometimes the length of this is not the same as the original series, any idea why this happens?) (yes, and dh
should have the same length. Do you have a small example?)Hi @till-m ,
yes, using spread_damage
seems to be the right way. I must say, however, that the temporal assignment of the partial damage was developed according to my own judgment and experience. For this, I have no verified physical evidence from linear-elastic fracture mechanics. For this reason, and because there was no open source code to do this task, I decided to do my own project.
You may find these papers of interest: https://www.sciencedirect.com/science/article/pii/S0142112315003333 ("Download full issue")
Hey @a-ma72,
thanks this is again super helpful. My idea to construct a physically accurate damage history was to calculate the damage of $0...t_i$ for all $1 < ti < t{end}$. Ignoring for a moment the problem of residuals, this should -- to my understanding -- produce a physically accurate damage history as long as the cycle counting method is accurate. This lines up to some degree with 'dh' (the exact level of closeness depends on the parameters).
I've attached the code below.
Do you have a small example?
If you change residual_method
to 7
, dh
should only have 9998 timesteps compared to the signal's 10001.
Hi @till-m:
short answer: The damage history is only as long as can be addressed by the indices of the reversal points. So you can just fill them up with zeros. (I obviously had the same problem in MATLAB, so the length is determined differently at this point. I will adapt this for python in the next release.)
Long answer: The specific point in time when a partial damage occurs within a closed cycle cannot be clearly determined. Example: If the crack opens and gains length, but can be closed again without dissipating energy, the partial damage is to be assigned to the crack opening process. If, on the other hand, material distortions form during opening, part of the damage can also be attributed to the closing process. Nevertheless, you will always need a fourth reversal point, which may occur by far later, to detect a closed cycle. Therefore, it is difficult to interpret the damage history, even for experienced experts. This was the reason for me to make the distribution of the damage parametric and offer solutions that have proven themselves in practice. Finally, even own ideas can be easily implemented in C by offering to fork to own code, by making usage of delegates at neuralgic points (RFC_USE_DELEGATES). So ideas are welcome!
Thank you for your code example, I will send you my comments on it:
Hi @a-ma72,
my apologies for bugging you again.
I noticed that for a particular script, I get less damage by adding another point to the time series.
from rfcnt import rfcnt
import numpy as np
RFC_KWARGS = {
"class_width": 0.2,
"auto_resize": True,
"use_HCM": 0,
"use_ASTM": 1,
"residual_method": 0,
"wl": {"sd": 865/2, "nd": 2e6, "k": 8}
}
def calculate_damage(signal_):
damage = rfcnt.rfc(signal_, **RFC_KWARGS)['damage']
return damage
a = np.array([0.08592532, 0.12260052, -0.16610635, -0.45372438, -0.46113973, -0.95846404, -1.02254961, -1.46266948, -1.65390141, -2.24422445, -2.45138246, -3.16835894, -3.24951033, -4.52535113, -6.40255541, -8.34149745 -10.4478217, -12.75260431, -15.23542341, -18.03041047, -21.06661139, -24.24869862, -23.20667037, -26.39752143, -25.66467476, -28.78903656, -28.28465694, -28.03190764])
#print(a)
print(calculate_damage(a))
a_extended = np.append(a, -31.18122411830486)
print(calculate_damage(a_extended))
Output:
2.679483900941182e-24
2.6794838741463435e-24
I figured that when residual_method=0
is used, this should not happen. Am I missing something?
Thank you very much!
Hi @till-m,
as far as I can tell, the problem is due to autoranging, leads to a rounding errors (since the glitch is small).
The class offset is shifted multiple times, following the data points, so small rounding errors accumulate (see autoresize()
in rainflow.c). Changing the offset impacts the quantization of the data points.
If you set the class_offset
to values smaller than -31.18122411830486 the effect vanishes.
Rounding errors due to the location of class boundaries are a troublesome thing in the damage calculation. You can observe the effect if you vary class_offset
and observe the calculated damage.
I can report from practical experience that divergences of up to 5% in damage are common if you only change parameters.
Best regards, Andreas
Hi Andreas,
thank you once again, this is super helpful and will let me mitigate the problem.
Cheers, Till
Numpy 1.19 does not work on Apple Silicon chips, see numpy/numpy#17807. This causes the installation of this package to fail.