Skip to content

Calibrated scalar strain-life assessment

constitutive.CoffinMansonBasquinCurve describes the combined elastic and plastic strain-amplitude relation

eps_a = (sigma_f / E) (2 N)^b + eps_f (2 N)^c

N is full cycles to failure; 2 N is reversals. The strain contributions are added, not the lives. Strain is dimensionless, not percent. The stress coefficient and Young's modulus must use the same declared unit. The COMSOL theory description documents this conventional relation and the importance of the selected strain measure and mean stress. AgentFEM's initial scope is narrower: one signed scalar total mechanical strain component, not a multiaxial criterion.

from agentfem import constitutive

# Synthetic values for demonstrating the interface, NOT design data.
curve = constitutive.CoffinMansonBasquinCurve(
    young=200_000.0,
    fatigue_strength_coefficient=1_000.0,
    fatigue_strength_exponent=-0.1,
    fatigue_ductility_coefficient=0.5,
    fatigue_ductility_exponent=-0.6,
    calibrated_cycles=(1.0, 1.0e8),
    stress_unit="MPa",
    source="synthetic example; not material calibration",
)
assessment = constitutive.assess_strain_history(
    [-0.01, 0.01, -0.01],
    curve,
    strain_measure="mechanical_total",
    source="signed axial mechanical strain at the selected material point",
    mean_stress_correction="none",
)
print(assessment.as_dict())

Declaring the calibration domain

The constructor above declares a cycle-count domain. For strain-controlled data whose reviewed applicability is an amplitude interval, use CoffinMansonBasquinCurve.from_strain_domain(...) with the same coefficients, unit and source, replacing calibrated_cycles with calibrated_strain_amplitudes=(minimum_amplitude, maximum_amplitude). These are total mechanical strain amplitudes in dimensionless units.

This explicit alternative derives the corresponding cycle endpoints from the curve. Observed life extrema need not equal fitted endpoint predictions: experimental scatter is not itself an extrapolation boundary. The declared amplitude interval must still be justified by the material data; the software does not infer or widen it. Output records distinguish the declared amplitude domain from model-implied cycle bounds. Changing coefficients requires rebuilding this curve so its derived bounds remain consistent.

Input and failure contract

  • Supply a signed mechanical strain history with enough accepted samples to resolve its reversals. Sparse visualization frames are not automatically a sufficient fatigue history. Thermal strain must already be excluded.
  • Do not use accumulated PEEQ/CEEQ or a positive strain norm as a substitute for a reversing signed component. The caller declares the measure; the software cannot recover a missing tensor or infer physical semantics from unlabeled numbers.
  • An explicit calibration domain and provenance are mandatory. A counted amplitude outside those bounds raises an error; there is no hidden clipping, extrapolation, or endurance-limit inference.
  • The caller must explicitly select mean_stress_correction="none". Means remain visible in the counted cycles. This is not evidence that mean-stress effects are negligible. Morrow/SWT require their own paired stress/strain contract and are not implemented here.
  • The existing scalar rainflow algorithm is reused. Remaining excursions count as half cycles. Miner damage is a postprocessing assumption, not constitutive damage. A constant sampled history returns no counted damage; strict JSON reports repeated_history_life: null, not a claim of infinite service life.

Composition and evidence

For a reviewed isothermal tensile rupture table, use assessments.TabulatedRuptureCurve instead of writing an interpolation callback:

from agentfem import assessments

# Synthetic illustration, not P92 or design data.
rupture = assessments.TabulatedRuptureCurve(
    stresses=(40.0, 100.0), rupture_times=(2500.0, 1000.0),
    temperature=800.0, stress_unit="MPa", time_unit="s",
    stress_measure="signed_axial", material_condition="synthetic",
    failure_definition="synthetic rupture", source="analytic t=100000/sigma",
)
print(rupture(60.0, 800.0))

Supply this as rupture_time= to assessments.creep_damage_from_result, with the same units and stress measure and rupture_source=rupture.source. The assessment retains the complete curve. Interpolation is linear in log stress and log life; temperature is fixed in kelvin. Stress outside the table or a different temperature is rejected. Tables must already be reviewed into a monotone curve: repeated tests and scatter are not silently averaged. This does not fit a creep mechanism, interpolate between temperatures or supply an engineering allowable. Existing user-owned rupture callables remain valid.

Pass the strain-life assessment explicitly as fatigue= to assessments.creep_fatigue, together with source-identified creep time fractions and the intended interaction diagram. assessments.creep_damage_from_result provides time-resolved creep fractions for declared, nonoverlapping dwell intervals; its stress/temperature/time units and rupture source are explicit. Do not replace a resolved relaxation history by its maximum when evaluating the actual time integral. The existing creep_fatigue_from_result convenience function remains stress-life based; it does not silently reinterpret its history as strain. The reference linear interaction is transparent, not a design-code acceptance curve. A combined assessment does not feed damage back into equilibrium, and does not prove absence of double-counting when the supplied strain-life calibration already includes dwell effects.

For a nonlinear rupture relation, compare quadrature_subdivisions=1, 2, 4 in creep_damage_from_result to check integration sensitivity on the same piecewise-linear stress/temperature histories. This explicitly refines every union-grid interval, preserves original knots, and records the subdivision count. It does not add physical samples or reduce the reported source gaps; maximum_source_interval still checks the original histories. The supported integer range is 1–1024 per interval. Numerical time resolution must permit the requested subdivision. No automatic error certificate is inferred.

The current tests cover equal-exponent closed-form inversion, round trips, stress-unit invariance, full/half-cycle conventions, calibration rejection, source retention and composition. They do not validate a material's fitted parameters, nonisothermal life, multiaxial damage or component service life.

The verification ladder adds structural relaxation, a classical cyclic hysteresis reference and a traceable standards and experimental-data map. Interaction acceptance requires diagram-domain coverage even when the fatigue damage is zero; it is not regulatory approval.