Skip to content

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:Test from arrays (time, strain, force).
  • :func:from_dataframe: build a :class:Test from 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 (time/strain/force). Example: {"Time (s)": "time", "Axial Strain": "strain", "Axial Force": "force"}.

None
**read_csv_kwargs

Passed through to :func:pandas.read_csv (e.g. sep, skiprows, comment) to handle machine-specific header blocks.

{}

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 2N_f.

required
stress_amp array - like

Per-test total strain amplitude, half-life stress amplitude, and reversals to failure 2N_f.

required
reversals array - like

Per-test total strain amplitude, half-life stress amplitude, and reversals to failure 2N_f.

required
E float

Young's modulus (MPa).

required
plastic_strain_amp array - like

If omitted, computed as Δε_t/2 − Δσ/(2E) (the standard default).

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 σ'_f, b, ε'_f, c with a nonlinear fit of the combined total-strain curve, seeded by the linear fits (ADR-0005).

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 σ_ar that produces the same life. All models reduce to σa when σm = 0. Walker with γ = 0.5 reduces 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 none, morrow, swt, walker.

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 k continues below the knee, the conservative elementary variant.
  • haibach: the line continues with the flatter fictitious slope 2k - 1 below 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/2 for survivals, with d the step.
  • std = 1.62d((NB - A^2)/N^2 + 0.029) when the variability statistic (N*B - A^2)/N^2 is 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):

  1. Frequency-modified Coffin-Manson, where the plastic strain-life coefficient scales with cyclic frequency, C_f = C_o * (f/f_ref)**(k-1).
  2. 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.
  3. 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.

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.