API reference¶
Generated from the docstrings of the public modules. The package facade
import lcf re-exports the everyday names, the module pages below are the
authoritative surface. Unit conventions everywhere: true stress in MPa,
strain as a dimensionless fraction, life in reversals, exponents b and c
negative.
Ingestion and reduction¶
lcf.ingest
¶
Ingestion and normalization: raw test data -> true stress/strain series.
Entry points:
- :func:
from_timeseries: build a :class:Testfrom arrays(time, strain, force). - :func:
from_dataframe: build a :class:Testfrom a raw DataFrame. - :func:
read_csv: read a delimited file (with optional column mapping).
All paths run :func:normalize, which adds the derived true-stress/true-strain
columns (ADR-0002). If metadata.already_true is set, the supplied values are
treated as true and the conversion is skipped (no double conversion).
TestRun
dataclass
¶
One ingested, normalized test: metadata + per-sample true stress/strain series.
normalize(df, metadata, *, validate=True)
¶
Return a copy of df with derived stress/strain columns added.
Required raw columns: time, strain, and force or stress_eng
(see :mod:lcf.schema).
Stress precedence: if a stress_eng column is present it is used as-is and
area is not required. Otherwise stress is derived as force / area.
If metadata.already_true is True, the (engineering-named) strain and stress
are taken to be true values directly and the eng->true conversion is skipped.
With validate=True (default), NaN values in the required columns raise, and
non-monotonic time emits a warning.
from_dataframe(df, metadata)
¶
Build a normalized :class:TestRun from a raw DataFrame.
from_timeseries(time, strain, force, *, metadata)
¶
Build a normalized :class:TestRun from parallel (time, strain, force) arrays.
Mirrors the py-fatigue CycleCount.from_timeseries constructor shape
(ADR-0001), adapted to strain-controlled (time, strain, force) input.
read_csv(path, *, metadata, column_map=None, **read_csv_kwargs)
¶
Read a delimited file into a normalized :class:TestRun.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
str | Path
|
File to read. |
required |
metadata
|
TestMetadata
|
Test scalars (area, E, ...). |
required |
column_map
|
dict
|
Maps source column names -> canonical names ( |
None
|
**read_csv_kwargs
|
Passed through to :func: |
{}
|
lcf.cycles
¶
Cycle reduction: segment a strain-controlled series into ordered cycles.
For constant-amplitude, fully-reversed strain control we segment by peak-valley (turning-point) detection on the strain waveform (ADR-0003). This preserves cycle order, so per-cycle evolution (hardening/softening, peak/valley drift, energy per cycle) is retained. This is the tool's differentiator vs. rainflow-based, order-discarding libraries.
Outputs an ordered per-cycle table plus the half-life cycle and the
cycles-to-failure N_f (configurable percent load-drop criterion, ADR-0004).
ReducedCycles
dataclass
¶
Ordered per-cycle reduction of one test.
find_turning_points(x, *, min_range=0.0)
¶
Indices of local extrema (reversals) in x.
Flats (consecutive equal values) do not count as reversals: the direction is forward-filled across them. Endpoints are not returned.
min_range applies an amplitude gate (hysteresis filter): small reversal
pairs whose swing is below min_range are removed, so sensor noise does not
fabricate cycles. With min_range=0 no gating is applied. For noisy lab
data set min_range to a few times the noise amplitude (ADR-0003, H7).
find_failure_cycle(peak_stress, *, pct, stabilized_value=None)
¶
Locate the failure cycle from a per-cycle peak (tensile) stress series.
Failure = the first cycle at or after the maximum-load (cyclically hardened)
cycle whose peak stress drops below (1 - pct/100) * stabilized_value
(ADR-0004).
The stabilized reference defaults to the maximum peak stress, which is robust to two common artifacts the naive series-midpoint is not (H2/M3): acquisition continuing past failure (a low tail cannot lower the max) and the initial hardening transient (searching only from the peak-load cycle ignores the rising part). A non-positive reference (e.g. an all-compressive or sign-flipped column) is rejected.
Returns (n_f, runout). n_f is a 1-based cycle count. runout is
True if the threshold was never crossed (then n_f is the total cycles).
reduce_cycles(test, params=None)
¶
Segment a :class:~lcf.ingest.TestRun into an ordered per-cycle table.
Cycles are bounded by consecutive strain peaks (each peak->next-peak window is one closed loop). Per cycle we record the loop sample-index window (for energy integration), the peak/valley sample indices, and the max/min true stress and strain within the loop window.
lcf.metrics
¶
Per-cycle metrics derived from the reduced cycles.
For each cycle: stress amplitude, mean stress, total/elastic/plastic strain
amplitude, tension/compression ratio, and the hysteresis energy density. Plastic
strain amplitude uses the computed form Δε_p/2 = Δε_t/2 − Δσ/(2E)
(the practical standard default, ADR-0005, IMPLEMENTATION_REFERENCE §1).
Also provides :func:estimate_modulus for when E is not supplied.
PerCycleMetrics
dataclass
¶
Per-cycle metric table plus stabilized summaries.
estimate_modulus(strain, stress, *, frac=0.25)
¶
Estimate Young's modulus from the initial elastic unloading of one loop.
Regresses stress on strain over the first frac of the samples following a
strain reversal (where the response is elastic), returning |slope| (MPa).
Best-effort only, supplying a measured E is strongly preferred. The fixed
frac assumes the window begins at a reversal (true for the loops produced
by :func:lcf.cycles.reduce_cycles) and that the initial segment is elastic.
A long plastic plateau right after the peak will bias the slope low.
per_cycle_metrics(test, reduced, *, E=None)
¶
Compute per-cycle metrics for a reduced test.
E resolution order: explicit argument -> test.metadata.E -> estimate
from the largest-amplitude loop's elastic unloading.
Fitting and life¶
lcf.fits
¶
Strain-life model fitting: Basquin, Coffin-Manson, Ramberg-Osgood.
Per-branch log-log linear least-squares is the primary fit (ADR-0005), with an
optional nonlinear refinement of the combined total-strain curve. K' and
n' are fit independently and also derived from b/c. Divergence (non-Masing
behavior) is flagged rather than silently forced.
Sign convention: b and c are negative. Units: stress/E in MPa.
PowerLawFit
dataclass
¶
Result of y = coeff · x**exponent fit by log-log linear regression.
BasquinFit
dataclass
¶
Elastic branch: Δσ/2 = σ'_f · (2N_f)**b.
CoffinMansonFit
dataclass
¶
Plastic branch: Δε_p/2 = ε'_f · (2N_f)**c.
RambergOsgoodFit
dataclass
¶
Cyclic stress-strain: Δσ/2 = K' · (Δε_p/2)**n'.
ConsistencyCheck
dataclass
¶
Compatibility (Masing) check between fitted and b/c-derived K'/n'.
StrainLifeFit
dataclass
¶
Full strain-life fit result.
power_law_fit(x, y)
¶
Fit y = coeff · x**exponent via OLS on log10(y) vs log10(x).
Only strictly-positive, finite pairs are used (log space). Requires >= 2
usable points. coeff_stderr is propagated from the intercept stderr.
fit_basquin(stress_amp, reversals)
¶
Fit Basquin constants from stress amplitude vs reversals to failure.
fit_coffin_manson(plastic_strain_amp, reversals, *, min_plastic_strain=None)
¶
Fit Coffin-Manson constants from plastic strain amplitude vs reversals.
The plastic line is only physically meaningful where plastic strain is
significant (the LCF regime). Near-runout points with plastic strain at
measurement-noise level corrupt the fit, so pass min_plastic_strain to
exclude them (ASTM E739 cautions against fitting outside the valid interval,
IMPLEMENTATION_REFERENCE §1-2). With no threshold, all points are used.
fit_ramberg_osgood(stress_amp, plastic_strain_amp, *, min_plastic_strain=None)
¶
Fit Ramberg-Osgood cyclic constants: Δσ/2 = K'·(Δε_p/2)**n'.
As with Coffin-Manson, min_plastic_strain excludes elastic-dominated,
noisy-plastic points.
transition_reversals(sigma_f, b, eps_f, c, E)
¶
Elastic-plastic transition life 2N_t (where Δε_e/2 == Δε_p/2).
2N_t = (ε'_f · E / σ'_f) ** (1 / (b − c)).
check_consistency(basquin, coffin_manson, ramberg_osgood, *, tolerance=0.15)
¶
Compare fitted K'/n' to the b/c-derived values (ADR-0005).
Compatibility predicts n' = b/c and K' = σ'_f / (ε'_f)**(b/c).
masing_ok is True when both relative differences are within tolerance.
If the relations are undefined (c == 0, or ε'_f <= 0 so the power is complex),
the check returns masing_ok=False with NaN differences rather than raising.
fit_strain_life(total_strain_amp, stress_amp, reversals, E, *, plastic_strain_amp=None, min_plastic_strain=None, refine_nonlinear=False)
¶
Fit the complete strain-life model from per-test reduced data.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
total_strain_amp
|
array - like
|
Per-test total strain amplitude, half-life stress amplitude, and
reversals to failure |
required |
stress_amp
|
array - like
|
Per-test total strain amplitude, half-life stress amplitude, and
reversals to failure |
required |
reversals
|
array - like
|
Per-test total strain amplitude, half-life stress amplitude, and
reversals to failure |
required |
E
|
float
|
Young's modulus (MPa). |
required |
plastic_strain_amp
|
array - like
|
If omitted, computed as |
None
|
min_plastic_strain
|
float
|
Minimum plastic strain amplitude for a point to enter the plastic (Coffin-Manson) and cyclic (Ramberg-Osgood) fits. The elastic (Basquin) fit always uses all points. Use this to exclude near-runout points whose plastic strain is at measurement-noise level (ADR-0005). |
None
|
refine_nonlinear
|
bool
|
If True, refine |
False
|
lcf.life
¶
Life prediction: evaluate strain-life models and invert them for life.
Forward model curves (strain amplitude given life) and inverse solvers (life
given a strain amplitude or an SWT cycle). Lives are expressed in reversals
2N_f, divide by two for cycles.
elastic_strain_life(reversals, sigma_f, b, E)
¶
Basquin elastic strain amplitude: (σ'_f/E)·(2N_f)^b.
plastic_strain_life(reversals, eps_f, c)
¶
Coffin-Manson plastic strain amplitude: ε'_f·(2N_f)^c.
total_strain_life(reversals, sigma_f, b, eps_f, c, E)
¶
Total strain amplitude: elastic + plastic.
predict_reversals_from_total_strain(total_strain_amp, sigma_f, b, eps_f, c, E, *, bracket=(1.0, 1000000000000.0))
¶
Reversals to failure 2N_f for a given total strain amplitude.
Inverts the combined strain-life equation by bracketed root finding (the curve is monotonically decreasing in life). Results are clamped to the bracket when the target lies outside the curve's range.
predict_reversals_basquin(stress_amp, sigma_f, b)
¶
Reversals from stress amplitude via inverted Basquin: (σa/σ'_f)^(1/b).
predict_reversals_swt(sigma_max, strain_amp, sigma_f, b, eps_f, c, E, *, bracket=(1.0, 1000000000000.0))
¶
Reversals from an SWT cycle by solving σ_max·ε_a = SWT_curve(2N_f).
predict_reversals_morrow(total_strain_amp, mean_stress, sigma_f, b, eps_f, c, E, *, bracket=(1.0, 1000000000000.0))
¶
Reversals for a total strain amplitude under a Morrow mean-stress shift.
Solves the Morrow strain-life curve, the elastic term reduced by the mean stress, for the life that gives the requested total strain amplitude.
predict_reversals(fit, total_strain_amp)
¶
Convenience: predict reversals from a fitted model and a strain amplitude.
lcf.meanstress
¶
Mean-stress corrections: Morrow, modified Morrow, SWT, Walker.
Two complementary APIs (ADR-0006, IMPLEMENTATION_REFERENCE §2):
- :func:
equivalent_fully_reversed_stress, the practical, model-agnostic API: convert a cycle(σa, σm)into the equivalent fully-reversed amplitudeσ_arthat produces the same life. All models reduce toσawhenσm = 0. Walker withγ = 0.5reduces exactly to SWT. - strain-life curve forms, :func:
morrow_strain_life, :func:modified_morrow_strain_life, and the SWT parameter curve :func:swt_parameter_curve, for plotting mean-stress-corrected ε-N curves.
Sign convention: b, c negative. Stresses/E in MPa.
walker_gamma_steel(sigma_u)
¶
Estimate the Walker exponent γ for steels from ultimate strength.
γ = 0.8818 − 2.00e-4·σ_u (Dowling, Calhoun & Arcari 2009, as in Dowling
4th ed. Eq. 9.20). σ_u is in MPa, widely rounded to 0.883 in secondary sources.
For 2000/7000-series aluminium, γ ≈ 0.5 (≈ SWT) is recommended instead.
equivalent_fully_reversed_stress(sigma_a, mean_stress, model, *, sigma_f=None, gamma=None)
¶
Equivalent fully-reversed stress amplitude σ_ar for a cycle.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
sigma_a
|
array - like
|
Stress amplitude Δσ/2 (MPa). |
required |
mean_stress
|
array - like
|
Mean stress σ_m (MPa). σ_max = σ_a + σ_m. |
required |
model
|
MeanStressModel | str
|
One of |
required |
sigma_f
|
float
|
Fatigue strength coefficient σ'_f (MPa), required for Morrow. |
None
|
gamma
|
float
|
Walker exponent γ, required for Walker (γ = 0.5 == SWT). |
None
|
Returns:
| Type | Description |
|---|---|
σ_ar (MPa). All models give ``σ_ar = σ_a`` when ``σ_m = 0``.
|
|
morrow_strain_life(reversals, *, sigma_f, b, eps_f, c, E, mean_stress)
¶
Morrow mean-stress-corrected total strain amplitude vs reversals.
Δε/2 = ((σ'_f − σ_m)/E)·(2N_f)^b + ε'_f·(2N_f)^c.
modified_morrow_strain_life(reversals, *, sigma_f, b, eps_f, c, E, mean_stress)
¶
Modified Morrow (correction on both terms).
Δε/2 = ((σ'_f − σ_m)/E)·(2N_f)^b + ε'_f·((σ'_f − σ_m)/σ'_f)^(c/b)·(2N_f)^c.
swt_parameter(sigma_max, strain_amp)
¶
SWT damage parameter σ_max · ε_a for a measured cycle.
swt_parameter_curve(reversals, *, sigma_f, b, eps_f, c, E)
¶
SWT parameter as a function of life: σ_max·ε_a vs 2N_f.
σ_max·ε_a = (σ'_f²/E)·(2N_f)^(2b) + σ'_f·ε'_f·(2N_f)^(b+c).
Solve swt_parameter(σ_max, ε_a) == swt_parameter_curve(2N_f) for life.
lcf.estimate
¶
Strain-life constant estimation from monotonic properties.
When no strain-controlled test data exists, these methods estimate the four strain-life constants (sigma_f', b, eps_f', c) from tensile properties or hardness. Every method is published and citable, and every result carries its citation, its validity warnings, and nothing that the source did not publish.
Methods and sources:
- Medians method: Meggiolaro and Castro, Int. J. Fatigue 26 (2004) 463-476. Median constants from an 845-metal database (724 steels, 81 aluminum, 15 titanium alloys). Their evaluation found it the best average predictor, so it is the recommended default.
- Uniform Material Law: Baeumel and Seeger, Materials Data for Cyclic Loading, Supplement 1, Elsevier, 1990.
- Universal slopes: Manson, Experimental Mechanics 5 (1965) 193-226.
- Modified universal slopes: Muralidharan and Manson, J. Eng. Mater. Technol. 110 (1988) 55-58. Steels only.
- Hardness method: Roessle and Fatemi, Int. J. Fatigue 22 (2000) 495-511. Steels only, from Brinell hardness and modulus.
Units follow the package convention: stress and modulus in MPa, strains as fractions. The exponents b and c are negative. Estimates are starting points for screening, not substitutes for test data, and the accuracy caveats reported by the sources are repeated in the per-method warnings.
EstimatedConstants
dataclass
¶
Estimated strain-life constants with provenance.
K and n (cyclic strength coefficient and exponent) are filled only
when the source method publishes them. warnings repeats the validity
caveats of the source that apply to the given inputs.
estimate_medians(material_class, Su)
¶
Meggiolaro-Castro medians method (2004), the recommended default.
material_class is steel or aluminum. The paper also reports medians
for titanium, cast iron, and nickel alloys from small samples, exposed
here with an explicit small-sample warning.
estimate_uniform_material_law(material_class, Su, E)
¶
Baeumel-Seeger Uniform Material Law (1990).
material_class is steel (unalloyed and low-alloy) or aluminum_titanium.
For steel the ductility correction is psi = 1 for Su/E <= 0.003, otherwise
psi = 1.375 - 125 Su/E. The law loses validity as Su approaches 2.2 GPa,
where psi reaches zero, and this function refuses non-positive psi.
estimate_universal_slopes(Su, E, RA)
¶
Manson's original universal slopes method (1965), any metal.
RA is the reduction in area as a fraction. Later evaluations found the
method non-conservative at short lives and conservative at long lives for
steels (Meggiolaro and Castro, 2004), so the newer methods are preferred.
estimate_modified_universal_slopes(Su, E, RA)
¶
Muralidharan-Manson modified universal slopes (1988), steels only.
The source derived the correlation for steels. Applying it to aluminum or titanium is unsupported, their exponents differ significantly (Meggiolaro and Castro, 2004), so this function is steel-specific.
estimate_hardness_method(HB, E)
¶
Roessle-Fatemi hardness method (2000), steels only.
Needs only Brinell hardness and modulus. The source correlation covers steels with hardness roughly 150 to 700 HB, values outside that range get a warning. The source notes the sigma_f' offset overestimates strength at low hardness and that the ductility correlation is statistically weak.
estimate_strain_life_constants(method, *, material_class='steel', Su=None, E=None, HB=None, RA=None)
¶
Dispatch to one estimation method by name.
method is one of medians, uniform_material_law, universal_slopes,
modified_universal_slopes, hardness. Each method needs a subset of the
inputs: medians needs Su, uniform_material_law needs Su and E,
universal_slopes and modified_universal_slopes need Su, E, and RA,
hardness needs HB and E.
Variable amplitude and damage¶
lcf.counting
¶
Rainflow cycle counting for variable-amplitude histories (ASTM E1049-85).
This is an in-house three-point rainflow counter, the Downing and Socie form embodied in ASTM E1049. It preserves the original sample indices of every counted cycle, which is what lets the rest of the toolkit recover per-cycle stress and strain evolution rather than just a histogram (ADR-0011).
The counter operates on one signal, usually the strain history. To get a mean
stress per cycle, map the returned i_start and i_end indices into the
paired stress signal with :func:mean_stress_per_cycle.
Residue handling follows E1049: leftover reversals are reported as half cycles.
Set close_residue=True for the common repeat-history closure, which counts
the residue against a repeat of itself so the largest range closes as a full
cycle.
Cycle
dataclass
¶
One counted cycle with indices into the original series.
reversals(series)
¶
Yield (index, value) turning points of series, including the ends.
Consecutive equal values are collapsed so flats do not create spurious reversals. The first and last retained points are always yielded, because they bound the history for counting.
extract_cycles(series, *, close_residue=False)
¶
Count rainflow cycles in series (ASTM E1049 three-point).
With close_residue=False the history is counted once and any unclosed
reversals are reported as half cycles, which reproduces the ASTM E1049 worked
example. With close_residue=True the reversal sequence is rotated to begin
and end at the global maximum before counting, the standard treatment for a
repeating history, so every reversal closes into a full cycle.
count_rainflow(series, *, close_residue=False)
¶
Rainflow count as a tidy DataFrame, ready for damage accumulation.
Columns: range, amplitude (range/2), mean, count,
i_start, i_end, peak (max of the two turning values), and
valley (min of the two turning values).
racetrack_filter(series, gate)
¶
Condense a history to the reversals larger than a gate (racetrack filter).
The racetrack or gate filter of Fuchs, Nelson, Burke, and Toomay (SAE
paper 730565, 1973, also Nelson and Fuchs in Fatigue Under Complex
Loading, SAE, 1977) removes swings smaller than the gate while keeping
the sequence of the large reversals, which shortens long histories before
counting or testing. Returns the retained turning points as
(indices, values) arrays with indices into the original series.
Endpoints are always retained because they bound the history.
count_level_crossings(series, *, levels=None, ref=0.0)
¶
Level-crossing count of a history (ASTM E1049 section 5.2).
Counts positive-slope crossings at and above the reference level and
negative-slope crossings below it, the E1049 convention. levels
defaults to 32 evenly spaced levels spanning the signal. Returns a tidy
DataFrame with columns level and count.
count_peaks(series, *, ref=0.0)
¶
Peak count of a history (ASTM E1049 section 5.3).
Counts peaks (local maxima) at and above the reference level and valleys
(local minima) below it, the E1049 convention. Returns a tidy DataFrame
with columns value, kind (peak or valley), and index into the
original series. History endpoints are not peaks or valleys.
mean_stress_per_cycle(cycles, stress)
¶
Mean stress for each counted cycle from a paired stress signal.
Uses the stress values at the cycle turning-point indices, so the strain
history can be counted while the stress history supplies the mean. Returns an
array aligned with the rows of cycles.
lcf.damage
¶
Cumulative damage accumulation under variable-amplitude loading.
Palmgren-Miner is the default and the validated primary rule (ADR-0010). The Double Linear Damage Rule (Manson-Halford) and Corten-Dolan are sequence and load-level sensitive alternatives.
Lives passed in here are per-counted-cycle reversals or cycles to failure. Apply any mean-stress correction upstream when computing those lives, so this module only sums damage (research section 2.3).
Validation status, stated honestly: - Miner: validated against a published block example (Golden D). - DLDR accumulation: validated against a published two-phase example (Golden C). - Manson-Halford phase-life split: a documented parametric knee model, property tested only. The damage answer comes from the validated accumulation. - Corten-Dolan: tested by its exact reduction to Miner when the exponent equals the inverse S-N slope.
DamageResult
dataclass
¶
Outcome of a damage calculation for one loading block.
miner(counts, lives, *, d_crit=1.0)
¶
Palmgren-Miner linear damage for one loading block.
damage is the sum of cycle-count over life for the block. The block is
assumed to repeat, so blocks_to_failure is d_crit / damage. The
critical sum defaults to 1.0. Codes use other values, for example 0.5 for
out-of-phase loading under IIW and Eurocode 3.
manson_halford_phase_lives(lives, *, knee_coeff=0.35, exponent=0.25)
¶
Split each life into Phase I and Phase II for the Double Linear Damage Rule.
Uses the Manson-Halford knee, where the Phase I fraction of a level is
f_I = knee_coeff * (N_f/N_long)**exponent referenced to the longest life
in the spectrum, so longer-life levels spend proportionally more of their
life in Phase I. With the standard constants 0.35 and 0.25 the shortest level
in an N_short to N_long spectrum gets a Phase I life of
N_short * 0.35 * (N_short/N_long)**0.25, which reproduces the published
Manson-Halford table value. Returns (phase1_lives, phase2_lives).
dldr_from_phase_lives(counts, phase1_lives, phase2_lives, *, d_crit=1.0)
¶
Double Linear Damage Rule accumulation from explicit phase lives.
Phase I runs until its linear damage sum reaches d_crit, then Phase II
runs until its sum reaches d_crit, when failure occurs. Blocks to failure
is the sum of the two phase contributions. This is the validated DLDR core.
dldr(counts, lives, *, exponent=0.25, d_crit=1.0)
¶
Double Linear Damage Rule using the Manson-Halford knee split.
Convenience wrapper: split lives into phases with
:func:manson_halford_phase_lives, then accumulate with
:func:dldr_from_phase_lives.
sn_curve_life(stress_amp, *, k, sd, nd, variant='original')
¶
Allowable cycles from a one-slope Woehler line with a knee at (SD, ND).
Above the knee stress sd the line is N = nd * (s / sd) ** -k. Below
it the treatment follows the named Miner variant:
- original: infinite life below the knee (Miner, J. Appl. Mech. 12 (1945) A159-A164, with the fatigue limit taken literally).
- elementary: the slope
kcontinues below the knee, the conservative elementary variant. - haibach: the line continues with the flatter fictitious slope
2k - 1below the knee (Haibach, 1970, described in Haibach, Betriebsfestigkeit, Springer, 3rd ed., 2006).
Returns an array of cycles to failure aligned with stress_amp. These
lives feed :func:miner for spectrum damage of stress-based collectives.
corten_dolan(counts, stresses, lives, *, d)
¶
Corten-Dolan cumulative damage for one loading block.
Cycles to failure is N_f,1 / sum(alpha_i (sigma_i/sigma_1)**d) where
sigma_1 is the maximum stress in the block, N_f,1 its life, and
alpha_i the cycle fractions. The exponent d controls sequence and
level sensitivity. When d equals the inverse S-N slope the rule reduces
exactly to Miner.
lcf.spectrum
¶
Spectrum life: the end-to-end variable-amplitude chain.
Ties the Phase 2 pieces together. A strain history and a paired stress history go in. Rainflow counts the strain, each counted cycle gets a mean-stress correction and a life from the strain-life curve, and the chosen damage rule returns blocks and cycles to failure (research sections 1.4 and 2).
Mean-stress methods for the per-cycle life: none, morrow, and swt.
SWT is the default for variable amplitude.
SpectrumResult
dataclass
¶
Result of a spectrum life calculation.
spectrum_life(strain_history, stress_history, *, sigma_f, b, eps_f, c, E, mean_stress_method='swt', rule='miner', close_residue=False, d_crit=1.0)
¶
Predict life under a variable-amplitude strain history.
Parameters mirror the strain-life constants from Phase 1. strain_history
and stress_history must be aligned sample arrays. Returns a
:class:SpectrumResult whose cycles table carries the per-cycle
amplitude, mean stress, reversals to failure, and damage.
lcf.simulate
¶
Variable-amplitude local strain simulation with material memory (ADR-0016).
Walks a strain reversal history through the cyclic stress-strain response: the initial loading follows the Ramberg-Osgood cyclic curve from zero to the first (rotated, largest) reversal, every subsequent branch follows the doubled Masing curve from its reversal origin, and material memory follows the same stack rule as three-point rainflow counting: when an excursion from the current reversal covers the previous branch range, the interior loop closes and the path continues on the outer branch as if the interruption never happened.
For damage the history is treated as a repeating block, rotated to the
global maximum and wrapped so every reversal closes, the same convention as
:func:lcf.counting.extract_cycles with close_residue=True. Per-loop
life comes from the existing solvers in :mod:lcf.life, SWT by default.
Model limits, stated plainly: stabilized cyclic properties are assumed throughout, and cycle-dependent mean stress relaxation and ratcheting are not modeled. Validation status: the strain-input engine is checked against the Conle SAE smooth-specimen dataset (within 2x on two of three histories, about 3x non-conservative on the third), and the load-input Neuber mode against the SAE keyhole benchmark (SM2 within 2x of experiment, CR1 within 4 percent of the benchmark's own reference calculation). See the evidence notes each result carries and examples/validate_sae_*.py.
References: Masing, Proc. 2nd Int. Congress for Applied Mechanics, Zurich, 1926 (the doubled branch). Dowling, Mechanical Behavior of Materials, 4th ed., ch. 14 (the local strain approach). ASTM E1049 (the memory rule).
ClosedLoop
dataclass
¶
One closed (or, in raw mode, half-counted) hysteresis loop.
HysteresisSimulation
dataclass
¶
Simulated stress-strain response of a strain reversal history.
simulate_hysteresis(strain_history, *, E, K_prime, n_prime, close_residue=True)
¶
Simulate the cyclic stress response of a strain history.
strain_history may be a raw sampled series, it is reduced to turning
points first. With close_residue=True (the default, and the damage
convention) the reversal sequence is rotated to the global maximum and
wrapped, treating the history as one repeating block so every reversal
closes into a full loop. With close_residue=False the original order
is kept, unclosed reversals are reported as half cycles, and the
path traces the stress state reversal by reversal for inspection.
simulate_hysteresis_from_nominal(nominal_stress_history, *, Kt, E, K_prime, n_prime, close_residue=True)
¶
Simulate the local notch response of a nominal stress history.
The classic load-input local strain approach: the initial loading
follows Neuber's rule on the cyclic curve to the rotated peak nominal
stress, every branch follows the modified Neuber rule on the doubled
(Masing) curve for its nominal stress range (reusing
:func:lcf.notch.neuber_local and :func:lcf.notch.neuber_local_range),
and material memory follows the rainflow closure rule on the nominal
ranges, which map monotonically to the local ones. Loops carry the
LOCAL strain and stress at the notch root.
variable_amplitude_life(strain_history=None, *, E, K_prime, n_prime, sigma_f, b, eps_f, c, mean_stress_model='swt', nominal_stress_history=None, Kt=None)
¶
Blocks to failure for a repeating history block.
Give either strain_history (local strain, smooth specimen) or
nominal_stress_history with Kt (load input, the local response
comes from Neuber's rule at every branch). Simulates the response with
material memory, aggregates the closed loops, computes each loop's life
with the chosen mean-stress model (swt from the loop's maximum
stress, morrow from its mean stress, none for the uncorrected
curve), and Miner-sums the damage. blocks_to_failure is None when
no loop is damaging.
Notch¶
lcf.notch
¶
Notch effects and the local-strain approach.
Turn a nominal stress at a notch into the local notch stress and strain using Neuber's rule or Glinka's equivalent strain energy density rule, both solved on the cyclic Ramberg-Osgood curve fitted in Phase 1. From the local strain amplitude the strain-life solver gives notch life.
Neuber is the default and tends to overestimate local strain, so it is conservative. Glinka tends to underestimate, and the measured strain usually lies between the two (ADR-0010). Validated against the SAE 1005 worked example (Golden E), see dev/docs/design/IMPLEMENTATION_REFERENCE_PHASE2.md section 2a.5.
ramberg_osgood_strain(stress, E, K, n)
¶
Total strain on the cyclic Ramberg-Osgood curve for a given stress.
neuber_local(nominal, Kt, E, K, n)
¶
Local notch stress and strain by Neuber's rule on the cyclic curve.
Solves sigma * (sigma/E + (sigma/K)**(1/n)) = (Kt*nominal)**2 / E for the
local stress, then returns (sigma, strain). Use stress amplitudes with
the cyclic K' and n' to get local amplitudes.
neuber_local_range(delta_nominal, Kt, E, K, n)
¶
Local stress and strain ranges by modified Neuber on the doubled curve.
Solves dsigma * (dsigma/E + 2*(dsigma/(2K))**(1/n)) = (Kt*dS)**2 / E for
the local stress range, then returns (dsigma, depsilon) using the
hysteresis (Massing doubled) branch.
glinka_local(nominal, Kt, E, K, n)
¶
Local notch stress and strain by the Glinka ESED rule on the cyclic curve.
Solves (Kt*nominal)**2/(2E) = sigma**2/(2E) + (sigma/(n+1))*(sigma/K)**(1/n)
for the local stress, then returns (sigma, strain). Glinka generally
predicts a lower local strain than Neuber.
kf_peterson(Kt, a, r)
¶
Fatigue notch factor by Peterson: Kf = 1 + (Kt-1)/(1 + a/r).
a is the Peterson material length and r the notch radius, same units.
kf_neuber(Kt, beta, r)
¶
Fatigue notch factor by Neuber: Kf = 1 + (Kt-1)/(1 + sqrt(beta/r)).
notch_sensitivity(Kt, Kf)
¶
Notch sensitivity q = (Kf-1)/(Kt-1), between 0 and 1.
notch_local_life(nominal_amp, Kt, *, E, K, n, sigma_f, b, eps_f, c, method='neuber')
¶
End-to-end notch life from a nominal stress amplitude.
Computes the local stress and strain amplitude (Neuber or Glinka), then inverts the strain-life curve for reversals to failure. Returns a dict with local stress, local strain, reversals, and cycles.
Statistics¶
lcf.stats
¶
Statistical analysis of strain-life data, post-E739.
ASTM E739 was withdrawn in 2024 with no superseding standard. Its linearized
regression remains the de facto method and is implemented here (ADR-0010).
Life is the dependent variable: log10(N) = A + B log10(amplitude).
The module also implements the modern maximum-likelihood layer the ASTM replacement effort points to, work item WK88010 and its technical basis, Meeker, Escobar, Pascual et al., arXiv:2212.04550: censored fits that treat runouts by likelihood instead of deletion, lognormal or Weibull life scatter, observed-information standard errors, profile-likelihood design bounds (Venzon and Moolgavkar 1988), a quantified comparison against the delete-runouts legacy, and a censored nonlinear fit of the full strain-life curve, which the linearized E739 method could not represent.
LogLifeFit
dataclass
¶
Linear fit of log10(life) on log10(amplitude).
amp_min/amp_max record the amplitude interval the fit actually
used, so callers can flag predictions outside it. E739's own caveat is
that the curve should not be extrapolated outside the interval of
testing. NaN when the fit was built without that information.
MlLogLifeFit
dataclass
¶
Bases: LogLifeFit
Censored maximum-likelihood fit with uncertainty information.
Extends :class:LogLifeFit. distribution is lognormal, normal
scatter of log10 life, or weibull, smallest-extreme-value scatter of
log10 life, which is a Weibull life distribution. residual_std is the
ML scale parameter of the chosen distribution. Standard errors come from
the observed information, the inverse Hessian of the negative log
likelihood at the optimum, and are NaN when that matrix is not
invertible. cov orders the parameters (intercept, slope, log sigma).
MlStrainLifeFit
dataclass
¶
Censored nonlinear ML fit of the full total strain-life curve.
Constants follow the project conventions, b and c negative,
sigma_log10_life is the lognormal scatter of log10 life about the
curve. Standard errors come from the observed information with the delta
method back to natural units, NaN when unavailable.
fit_log_life(amplitude, life)
¶
Fit log10(N) = A + B log10(amplitude) by ordinary least squares.
grubbs_test(values, *, alpha=0.05)
¶
Two-sided Grubbs test for a single outlier in a normal sample.
Grubbs, Technometrics 11 (1969) 1-21, with the critical value in the form given by the NIST/SEMATECH e-Handbook, section 1.3.5.17.1. Returns the statistic, the critical value, the index of the most extreme point, and whether it is flagged at the given significance level.
generalized_esd(values, *, max_outliers, alpha=0.05)
¶
Generalized extreme studentized deviate test for up to k outliers.
Rosner, Technometrics 25 (1983) 165-172, following the NIST/SEMATECH e-Handbook recipe, section 1.3.5.17.3. Returns the indices of the flagged outliers (possibly empty) and the per-step statistics. The approximation is intended for roughly n >= 15, smaller samples get a warning entry.
regression_diagnostics(amplitude, life, *, alpha=0.05)
¶
Influence diagnostics for the log-log life regression.
Computes leverage, internally and externally studentized residuals, and
Cook's distance (Cook, Technometrics 19 (1977) 15-18) for each point of
the log10(N) = A + B log10(amplitude) fit. A point is flagged when its
externally studentized residual exceeds the Bonferroni-corrected t
critical value (the standard mean-shift outlier test in regression), or
when Cook's distance exceeds 4/n, a common screening threshold.
predict_life(fit, amplitude)
¶
Median (50% reliability) life at a given amplitude.
confidence_interval(fit, amplitude, confidence=0.95)
¶
Two-sided confidence interval on the mean life line, as (low, high).
prediction_interval(fit, amplitude, confidence=0.95)
¶
Two-sided prediction interval for a single future life, as (low, high).
owen_tolerance_factor(n, reliability=0.9, confidence=0.9)
¶
One-sided normal tolerance factor k (Owen), via the noncentral t.
k = nct.ppf(confidence, df=n-1, nc=z_p*sqrt(n)) / sqrt(n) with
z_p = norm.ppf(reliability). This is the standard one-sided tolerance
factor and matches published tables, for example k(n=10, R90, C95)=2.355.
basis_value(*, mean, std, n, basis='B')
¶
A- or B-basis value: the one-sided lower tolerance bound mean - k*std.
Following MMPDS practice, the B-basis is the 95 percent confidence lower
bound on the 10th percentile (90 percent reliability) and the A-basis on
the 1st percentile (99 percent reliability). k is the exact Owen
one-sided tolerance factor from the noncentral t. Assumes the property is
normally distributed in the analyzed units, fit and check the sample
before relying on the bound.
lack_of_fit(amplitude, life)
¶
ASTM E739-style lack-of-fit F test for the linearized life regression.
Requires replicate tests: at least one amplitude level tested more than once, and at least three distinct levels. Partitions the residual sum of squares into pure error (within replicate levels) and lack of fit (between the level means and the regression line), in log10 space with life as the dependent variable. A significant F says the straight line does not represent the data, whatever the r squared says.
design_life(fit, amplitude, *, reliability=0.9, confidence=0.9)
¶
Design (R-C) life at an amplitude: mean - k*s in log10 life.
Uses the Owen one-sided tolerance factor for the given reliability and confidence, for example R90C90. Returns the lower-bound life.
fit_log_life_censored(amplitude, life, censored, *, distribution='lognormal')
¶
Maximum-likelihood fit with right-censored (runout) observations.
Observed lives contribute the density of log10(N) about the line,
runouts contribute the survival probability that the true life exceeds
the observed value. Runouts are never deleted. distribution selects
the life scatter model: lognormal (default) or weibull. Returns
a :class:MlLogLifeFit whose residual_std is the ML scale and which
carries observed-information standard errors, the log likelihood, and
AIC. Method per Meeker, Escobar, Pascual et al., arXiv:2212.04550, the
technical basis of ASTM work item WK88010.
design_life_ml(amplitude, life, censored, *, at_amplitude, reliability=0.9, confidence=0.9, distribution='lognormal', method='profile')
¶
One-sided lower confidence bound on the life quantile, censored ML.
The modern replacement for the Owen tolerance factor when runouts are
present. The Owen factor assumes a complete normal sample, maximum
likelihood with censoring does not. The bound is on the reliability
quantile of life at at_amplitude, at the given one-sided
confidence. method is profile (default), inverting the
likelihood ratio per Venzon and Moolgavkar 1988, or wald, the delta
method on the observed information. Profile bounds keep their meaning at
small samples where Wald intervals go symmetric and optimistic. Aligned
with the framework of Meeker, Escobar, Pascual et al., arXiv:2212.04550,
behind ASTM work item WK88010.
compare_runout_handling(amplitude, life, censored, *, at_amplitude, reliability=0.9, confidence=0.9)
¶
Quantify what deleting runouts does to the design curve.
Fits the same data three ways and evaluates each at at_amplitude:
naive, runouts deleted, OLS with the Owen tolerance factor, the
legacy practice. ml_owen, censored lognormal ML with the Owen factor
applied to the ML sigma, an approximation because the factor assumes a
complete sample. ml_profile, censored ML with the
profile-likelihood bound, the modern method. The design_life_ratio
entries divide the alternatives by the naive value, whether deletion was
optimistic or pessimistic depends on the data, the point is that the
difference is quantified instead of hidden.
fit_strain_life_censored(total_strain_amp, reversals, censored=None, *, E, stress_amp=None, max_iter=20000)
¶
Censored maximum-likelihood fit of the full strain-life curve.
Fits sigma_f, b, eps_f, c and the lognormal scatter of log10 life
directly on the combined curve, with runouts contributing survival
probability. ASTM E739 restricted itself to linearized fits and could
not represent the combined curve or runouts, the ASTM replacement work
item WK88010 names nonlinear regression and censored data as the point
of the rewrite, method per Meeker, Escobar, Pascual et al.,
arXiv:2212.04550. Lognormal life scatter only.
stress_amp is optional and used only to seed the optimizer through
the standard linear fits. Without it the seed is heuristic. The sign
conventions are enforced by parametrization, the fitted b and c are
always negative. Needs at least 5 points and at least 3 uncensored.
Identifiability caveat, stated because it is intrinsic to the model: the four constants of the combined curve are strongly correlated when fitted from total strain alone, especially the elastic pair, and their standard errors can exceed the estimates. The fitted curve itself is well determined inside the tested strain range. Treat the constants as curve parameters, and read the standard errors before quoting any of them individually. Branch-wise linear fits from separated elastic and plastic strains remain the method of choice when stress amplitudes are available, this fit is for censored data and direct curve inference.
lcf.staircase
¶
Staircase (up-and-down) fatigue-limit analysis, Dixon-Mood method.
The staircase test estimates the mean and standard deviation of the fatigue strength at a fixed life: each specimen is tested one step above or below the previous level depending on whether the previous specimen survived. The Dixon-Mood estimator analyzes the counts of the less frequent event on the level grid (ADR-0015).
With the event counts n_i on level index i (0 at the lowest level
where the event occurred), A = sum(i*n_i), B = sum(i^2*n_i),
N = sum(n_i):
- mean = X0 + d*(A/N - 1/2) when the analysis uses failures,
+ 1/2for survivals, with d the step. - std = 1.62d((NB - A^2)/N^2 + 0.029) when the variability statistic
(N*B - A^2)/N^2is at least 0.3. Below that bound the estimate is unreliable and the 0.53d fallback is reported, flagged approximate.
References: Dixon and Mood, J. Amer. Statist. Assoc. 43 (1948) 109-126. ISO 12107:2012. Validated against the S34MnV worked example of Ekaputra, Dewa, Haryadi and Kim, Open Engineering 10 (2020) 394-400.
StaircaseResult
dataclass
¶
Dixon-Mood staircase estimate of the fatigue-strength distribution.
dixon_mood(stress_levels, failed, *, step=None)
¶
Dixon-Mood analysis of an up-and-down test sequence.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
stress_levels
|
sequence of float
|
Stress (or strain) level of each specimen, in test order. |
required |
failed
|
sequence of bool
|
True where the specimen failed before the target life, False for a survival (runout). |
required |
step
|
float
|
Step size d. If omitted it is inferred from the level sequence, and the consecutive levels must then differ by one constant step. |
None
|
lcf.rfl
¶
Random fatigue limit model, Pascual-Meeker normal-normal form.
The model: with stress amplitude s, the specimen fatigue limit gamma
varies unit to unit, V = log(gamma) ~ Normal(mu_gamma, sigma_gamma), and
given V the log life is
W = log(N) | V ~ Normal( beta0 + beta1 * log(s - gamma), sigma )
defined for s > gamma. A specimen whose fatigue limit is at or above the
test stress never fails. Runouts are right-censored observations. The
marginal likelihood integrates over V (Gauss-Legendre quadrature), and
the five parameters are found by maximum likelihood.
Validation status, stated plainly: the fitter reproduces the published Pascual and Meeker (Technometrics 41, 1999, 277-290) normal-normal fit of the laminate-panel dataset exactly, log-likelihood -86.221 and parameters matching their Table 1 to the digit (tests/test_rfl.py, data from the public SMRD.data R package). The marginal likelihood is also cross-checked against brute-force integration and the fitter recovers known parameters from simulated data.
RflFit
dataclass
¶
Maximum-likelihood estimates of the random fatigue limit model.
rfl_loglik(theta, stress, log_life, censored)
¶
Marginal log likelihood of the normal-normal RFL model.
theta is (beta0, beta1, log sigma, mu_gamma, log sigma_gamma).
log_life uses natural logs. Runouts contribute their survival
probability, including the probability that the fatigue limit is at or
above the stress level. Vectorized per unique stress level.
fit_rfl(stress, life, censored=None, *, life_is_log=False)
¶
Fit the random fatigue limit model by maximum likelihood.
stress are amplitudes (MPa or any consistent unit), life the
lives (cycles or reversals, be consistent), censored flags runouts.
Starting values come from a plain log-log regression plus a fatigue
limit slightly below the lowest stress, then Nelder-Mead maximizes the
marginal likelihood.
simulate_rfl(stress_levels, n_per_level, *, beta0, beta1, sigma, mu_gamma, sigma_gamma, censor_time, rng=None)
¶
Simulate an RFL test campaign, returning (stress, life, censored).
Specimens whose fatigue limit is at or above their stress level, and
failures beyond censor_time, are censored at censor_time.
Elevated temperature and evolution¶
lcf.hightemp
¶
Elevated-temperature fatigue: frequency effects and creep-fatigue.
Three capabilities (ADR-0010, research section 4):
- Frequency-modified Coffin-Manson, where the plastic strain-life coefficient
scales with cyclic frequency,
C_f = C_o * (f/f_ref)**(k-1). - Linear time-fraction creep-fatigue damage,
D = sum(n/N_f) + sum(t/t_r), with a bilinear creep-fatigue interaction (D-diagram) envelope check. - Temperature-dependent strain-life constants stored as a table and interpolated, linear in temperature for the exponents and the modulus, log-linear for the coefficients.
CreepFatigueResult
dataclass
¶
Linear time-fraction creep-fatigue damage summary.
frequency_modified_coefficient(eps_f_coeff, *, frequency, k, freq_ref=1.0)
¶
Frequency-modified Coffin-Manson coefficient C_f = C_o*(f/f_ref)**(k-1).
This is the Solomon and Engelmaier coefficient form, where frequency scales
the ductility coefficient. It is a common variant and is not literally
Coffin's original form, which folds frequency inside the life term. Reduces to
C_o at the reference frequency. The exponent k is material specific.
frequency_modified_plastic_strain(reversals, eps_f_coeff, c, *, frequency, k, freq_ref=1.0)
¶
Plastic strain amplitude from the frequency-modified Coffin-Manson law.
frequency_modified_reversals(plastic_strain_amp, eps_f_coeff, c, *, frequency, k, freq_ref=1.0)
¶
Invert the frequency-modified Coffin-Manson law for reversals to failure.
creep_fatigue_damage(cycle_counts, fatigue_lives, hold_times, rupture_times, *, envelope=1.0)
¶
Linear time-fraction (Robinson) plus Miner creep-fatigue damage.
D = sum(n_i/N_f,i) + sum(t_j/t_r,j). Failure when D reaches the
envelope value, 1.0 by default. The fatigue and creep terms are independent
lists, so a block may have any number of cycle levels and hold periods.
creep_fatigue_envelope_allowable(d_fatigue, knee_f, knee_c)
¶
Allowable creep damage on the bilinear D-diagram for a given fatigue damage.
The envelope runs from (1, 0) through the material knee (knee_f, knee_c) to
(0, 1). Returns the creep-damage value on that boundary at d_fatigue.
creep_fatigue_envelope_check(d_fatigue, d_creep, *, knee=(0.3, 0.3))
¶
Check a (D_fatigue, D_creep) point against the bilinear D-diagram envelope.
Returns the allowable creep damage at this fatigue damage, whether the point is safe (on or inside the envelope), and the margin (allowable minus actual).
interpolate_constants(table, temperature, *, log_coeffs=True)
¶
Interpolate temperature-dependent strain-life constants.
table is a mapping with a T sequence and any of E, sigma_f,
b, eps_f, c. Exponents and the modulus interpolate linearly in
temperature. Coefficients interpolate log-linearly when log_coeffs is
True. Temperatures outside the table clamp to the nearest end.
lcf.cyclic_evolution
¶
Cycle-dependent mean stress relaxation and ratcheting (ADR 0020).
Two phenomena that the stabilized-cycle analysis elsewhere in the toolkit does not model:
-
Mean stress relaxation, strain-controlled asymmetric cycling. A nonzero mean stress decays toward zero as plastic strain accumulates. The standard empirical form is a power law in cycle count,
sigma_m(N) = sigma_m1 * N ** b_r,
with sigma_m1 the first-cycle mean stress and b_r <= 0 a material
relaxation exponent, the slope of log(sigma_m) against log(N).
-
Ratcheting, stress-controlled asymmetric cycling. Plastic strain accumulates cycle by cycle in the direction of the mean stress. The accumulated ratcheting strain follows an empirical power law,
eps_r(N) = C * N ** p,
and its life interaction is a ductility-exhaustion penalty on the Coffin-Manson plastic term,
delta_eps_p / 2 = (eps_f' - eps_r) * (2 N_f) ** c.
Provenance and status. These forms were reconstructed from collaborator notes (Hugh Shortt, 2026-07-08) whose inline equations were lost in transfer, and they match the standard published forms cited below. They are labeled reconstructed and carry a note that the exact formulation is pending the collaborator's confirmation. Every function is validated by internal consistency and by fitter-recovery of known constants, there is no published worked-example golden.
References: Jhansale and Topper 1973 (ASTM STP 519), Morrow and Sinclair 1958 (ASTM STP 237) for relaxation. Xia, Kujawski and Ellyin 1996 (Int. J. Fatigue 18:335) and Kapoor 1994 (Fatigue Fract. Eng. Mater. Struct. 17:201) for ratcheting and its damage interaction.
mean_stress_relaxation(sigma_m1, N, b_r)
¶
Relaxed mean stress at cycle N: sigma_m1 * N ** b_r.
sigma_m1 is the first-cycle mean stress (MPa), b_r the relaxation
exponent (<= 0, more negative relaxes faster). N is a cycle count or
array of counts (>= 1). Returns the mean stress at each N.
fit_relaxation_exponent(cycles, mean_stresses)
¶
Fit the relaxation power law to measured (cycle, mean stress) data.
Returns sigma_m1 (the fitted first-cycle mean stress), b_r, the
coefficient of determination, and notes. Mean stresses must share one
sign, the log-log fit is on their magnitude.
ratcheting_strain(N, C, p)
¶
Accumulated ratcheting strain at cycle N: C * N ** p.
C is the ratcheting coefficient (strain at N=1), p the ratcheting
exponent (> 0). N is a cycle count or array (>= 1).
fit_ratcheting(cycles, ratchet_strains)
¶
Fit the ratcheting power law eps_r = C * N ** p to data.
Returns C, p, the coefficient of determination, and notes.
ratcheting_penalized_life(plastic_strain_amp, eps_r, *, eps_f, c, bracket=(1.0, 1000000000000.0))
¶
Reversals to failure with the ratcheting ductility-exhaustion penalty.
Solves plastic_strain_amp = (eps_f - eps_r) * (2 N_f) ** c for the
reversals, the Coffin-Manson plastic line with the fatigue ductility
reduced by the accumulated ratcheting strain eps_r. Returns the
reversals, the cycles, the penalized ductility, and notes.
Multiaxial¶
lcf.multiaxial
¶
Multiaxial fatigue, survey-only stub (ADR-0010, research section 5).
This module provides the critical-plane damage-parameter functions and a plane search interface, enough to evaluate a parameter once the plane quantities are known. It does NOT yet compute plane quantities from a full stress and strain tensor history with a rotating-plane search. That, with shear strain-life constants and tensor input, is the first Phase 3 item.
Parameters: - Fatemi-Socie, shear based, for shear-cracking ductile metals. - Brown-Miller, combined shear and normal strain on the maximum-shear plane. - Smith-Watson-Topper multiaxial, for tensile-cracking materials. - von Mises equivalent strain, for proportional-loading screening only.
fatemi_socie(shear_strain_amp, sigma_n_max, *, sigma_y, k=0.3)
¶
Fatemi-Socie parameter (dgamma_max/2)(1 + k*sigma_n_max/sigma_y).
shear_strain_amp is the maximum shear strain amplitude on the critical
plane, sigma_n_max the maximum normal stress on that plane. The normal
stress term captures extra hardening under non-proportional loading. The
material constant k defaults to 0.3, a common value for ductile metals,
and should be fitted when data allows.
brown_miller(shear_strain_amp, normal_strain_amp, *, S=1.0)
¶
Brown-Miller parameter dgamma_max/2 + S*deps_n on the max-shear plane.
swt_multiaxial(sigma_n_max, normal_strain_amp)
¶
Multiaxial SWT parameter sigma_n_max * deps_1/2 on the max-principal plane.
von_mises_equivalent_strain(eps_x, eps_y, eps_z, gamma_xy=0.0, gamma_yz=0.0, gamma_zx=0.0)
¶
von Mises equivalent strain for proportional-loading screening.
Incompressible form. For a uniaxial state with transverse strains -0.5*eps
this returns eps. Cannot represent non-proportional or mean-stress
effects, so use it only for proportional screening.
critical_plane_search(parameter_fn, *, angles=None)
¶
Search candidate plane angles for the one that maximizes a damage parameter.
parameter_fn maps a plane angle in degrees to the damage parameter on
that plane. Returns the critical angle, the maximum parameter, and the full
swept arrays. This is the plane-search interface, the per-angle plane
quantities are supplied by the caller until the Phase 3 tensor engine exists.
lcf.criticalplane
¶
Tensor critical-plane search (ADR-0018, P5).
Given strain (and optionally stress) tensor component histories sampled
over one cycle, scan candidate plane normals over a hemisphere grid,
resolve the per-plane quantities, and evaluate a critical-plane parameter
through the existing survey functions in :mod:lcf.multiaxial.
Conventions: strains are true strains, shear inputs and outputs are
ENGINEERING shear (gamma), stresses in MPa. The plane normal is
n = (sin(phi) cos(theta), sin(phi) sin(theta), cos(phi)) with theta
scanned over [0, 180) and phi over [0, 90] degrees. Per plane, the normal
strain amplitude is half the range of n . eps . n, the shear amplitude
is half the longest chord of the in-plane shear vector path (meaningful for
non-proportional paths too), and the maximum normal stress is the maximum
of n . sigma . n over the cycle.
Scope, stated plainly: amplitudes come from the given cycle's path. Per plane rainflow counting of long multiaxial histories is not implemented.
References: Fatemi and Socie 1988. Brown and Miller 1973. Socie and Marquis, Multiaxial Fatigue, SAE, 2000.
PlaneResult
dataclass
¶
Quantities and parameter value on one plane.
resolve_plane(eps, n)
¶
Resolve one plane: (engineering shear amp, normal strain amp, eps_n(t)).
eps is the (m, 3, 3) tensor history. The shear amplitude is half the
longest chord of the tangential (in-plane) strain-vector path times two,
which turns the tensor measure into engineering shear.
search_critical_plane_tensor(*, parameter, eps_xx, eps_yy, eps_zz, gamma_xy, gamma_yz, gamma_zx, sig_xx=None, sig_yy=None, sig_zz=None, tau_xy=None, tau_yz=None, tau_zx=None, sigma_y=None, k=0.3, S=1.0, grid_deg=10.0)
¶
Scan plane normals and return the plane maximizing the parameter.
parameter is fatemi_socie, brown_miller, or swt.
Fatemi-Socie and SWT need the stress history for the normal stress on
the plane, Brown-Miller works from strains alone.
Data, interchange, and provenance¶
lcf.datasets
¶
Bundled example datasets, published and citable.
One module holds the example data used by the README, the examples, and the graphical interface, so the numbers exist in exactly one place. The test suite keeps its own copy on purpose: a golden reference must stay independent of the code it validates.
This module also builds the seed open-data collection,
:func:seed_collection. The checked-in artifact
docs/data/seed_collection.json is exactly that output, a test guards
against drift. The seed is a schema-reference dataset of published, citable
strain-life data. It demonstrates the interchange formats, it is not yet a
database at publishable scale.
SAE1137_E = 208000.0
module-attribute
¶
Nominal elastic modulus for the SAE 1137 example (MPa).
sae1137_reduced()
¶
Published SAE 1137 per-test reduced strain-life data.
One row per test: half-life total strain amplitude (fraction), stress
amplitude (MPa), and reversals to failure 2N_f. Source:
:data:SAE1137_CITATION.
seed_collection()
¶
Build the seed open-data collection as a collection@1 document.
Six SAE 1137 strain-controlled tests as test records, Williams, Lee, Rilly 2003, plus three verified published constant sets as material documents. Every value is factual data re-tabulated from the cited source. The compilation, the selection, arrangement, and metadata, is licensed CC-BY-4.0. The record data keep their cited provenance.
lcf.interchange
¶
Versioned interchange of strain-life data (ADR-0017 P4, extended by ADR 0021).
Three document formats, each a small, versioned, human-diffable JSON object.
The formal JSON Schema artifacts live in docs/schemas/ and are generated
from the pydantic models here, a test guards against drift. The full
field-by-field specification is docs/INTERCHANGE.md.
lcf-strain-life/material@1: the four strain-life constants, the cyclic curve, the unit conventions, and a provenance block. Frozen since v0.1.lcf-strain-life/test-record@1: one strain-controlled fatigue test with ASTM E606-style metadata, the failure outcome, the half-life response, an optional per-cycle table, and a provenance block with the license basis.lcf-strain-life/collection@1: a dataset manifest that bundles material documents and test records with a compilation license and contributors.
Versioning policy: the version is an integer. Readers refuse unknown schemas, versions, and unit systems rather than guessing. Within a version, new optional fields may be added and readers accept unknown fields. Any breaking change bumps the version.
The pyLife adapter expresses the elastic (Basquin) line in pyLife's
WoehlerCurve conventions (k_1, ND, SD, TN, TS). It is
shape-compatible with pyLife's documented pandas conventions and the math
round-trips exactly, but it is not integration-tested against an installed
pyLife. A strain-life curve has no endurance limit, so the knee ND is a
representation choice recorded in the output.
MaterialUnits
¶
Bases: BaseModel
The fixed unit convention of material@1.
RecordUnits
¶
Bases: BaseModel
The fixed unit convention of test-record@1.
MaterialDoc
¶
Bases: _Block
lcf-strain-life/material@1, the constants document.
TestControl
¶
Bases: _Block
How the test was driven.
Specimen
¶
Bases: _Block
ASTM E606-style specimen description, all optional.
HalfLifeResponse
¶
Bases: _Block
Reduced stabilized response, by convention at half life.
PerCycleTable
¶
Bases: _Block
Optional per-cycle evolution table, the raw-data differentiator.
Failure
¶
Bases: _Block
The test outcome.
RecordProvenance
¶
Bases: _Block
Where the record came from and on what basis it is shared.
TestRecordDoc
¶
Bases: _Block
lcf-strain-life/test-record@1, one strain-controlled fatigue test.
CollectionDoc
¶
Bases: _Block
lcf-strain-life/collection@1, a dataset manifest.
export_material(*, name, E, sigma_f, b, eps_f, c, K_prime=None, n_prime=None, source=None, notes=None)
¶
Build the versioned material document from strain-life constants.
import_material(doc)
¶
Validate a material document and return the flat constants.
Returns a dict with name, E, sigma_f, b, eps_f,
c, and, when present, K_prime and n_prime. Refuses unknown
schemas, versions, and unit systems rather than guessing.
to_pylife_woehler(sigma_f, b, *, nd_cycles=1000000.0)
¶
Express the Basquin line in pyLife WoehlerCurve conventions.
k_1 = -1/b and SD is the Basquin stress amplitude at the knee
ND (in cycles). TN and TS are 1.0, meaning no scatter is
encoded. The knee is a representation choice, strain-life implies no
endurance limit.
to_py_fatigue_sn(sigma_f, b)
¶
Express the Basquin line in py-fatigue SNCurve conventions.
py-fatigue defines log10(N) = intercept - slope * log10(S). From
Basquin in reversals, slope = -1/b and
intercept = log10(0.5) + slope * log10(sigma_f) (the 0.5 converts
reversals to cycles). No endurance limit is encoded.
from_pylife_woehler(k_1, ND, SD)
¶
Recover Basquin constants from pyLife WoehlerCurve values.
export_test_record(*, record_id, material, reversals_to_failure, source, strain_amplitude=None, stress_amplitude=None, control_mode='strain', runout=False, criterion=None, condition=None, license=None, origin=None, notes=None, test=None, specimen=None, response=None, per_cycle=None)
¶
Build a validated test-record@1 document.
The common fields are keyword arguments. Less common control fields go in
test, which is merged over the keyword values. specimen,
response, and per_cycle are optional blocks passed as dicts.
source is required, a record without provenance is not accepted.
import_test_record(doc)
¶
Validate a test-record document, return the typed model.
Raises ValueError on any mismatch, never guesses. Use
.model_dump(by_alias=True) to get a plain dict back.
export_collection(*, name, license, description=None, created=None, doi=None, homepage=None, contributors=None, materials=None, records=None)
¶
Build a validated collection@1 document from member documents.
Every member document is validated. created is an ISO date string
supplied by the caller, nothing is auto-stamped, exports stay
reproducible.
import_collection(doc)
¶
Validate a collection document, return the typed model.
validate_document(doc)
¶
Validate any interchange document, returning a structured verdict.
Dispatches on the schema key across all three formats. Returns a dict
with valid, schema, version, kind, and a list of
human-readable errors. Never raises on invalid content and never
repairs a document.
json_schema(kind)
¶
Generate the JSON Schema for material, test-record, or
collection.
The checked-in artifacts in docs/schemas/ are exactly these outputs,
a test regenerates and compares them so they cannot drift.
lcf.citations
¶
Citation registry: the published source behind every method in the package.
The project promises to be upfront about its methods and sources. This module
is the single machine-readable place where each computational method maps to
its original publication or standard. Agents reach it through the
get_citations MCP tool and the lcf://citations resource, and the
human-readable equivalent is docs/PHYSICS_REVIEW.pdf.
Entries state the method, the citation, and where honesty requires it, a status note (for example that ASTM E739 was withdrawn, or that a method's validation is implementation-grade rather than benchmarked).
get_citations(topic=None)
¶
Return the citation registry, optionally filtered by a topic substring.
The filter matches case-insensitively against the key, the method name, and the citation text.