Skip to content

Latest commit

 

History

History
201 lines (159 loc) · 7.27 KB

File metadata and controls

201 lines (159 loc) · 7.27 KB

Materials

The material is explicit and typed. There is no integer constitutive selector and no shared bag of parameters: every law names its own arguments, and every argument carries its units.

Composable instantaneous materials

An InstantaneousMaterial can contain an elastic law, a viscous law, or both:

from pyimr import CarreauYasuda, Gent, InstantaneousMaterial

material = InstantaneousMaterial(
    elastic=Gent(shear_modulus_pa=2500.0, extensibility=250.0),
    viscous=CarreauYasuda(
        zero_shear_viscosity_pa_s=0.5,
        infinite_shear_viscosity_pa_s=0.02,
        time_constant_s=20e-6,
        transition_exponent=2.0,
        power_index=0.45,
    ),
)

Elastic: NeoHookean, MooneyRivlin, Yeoh, Fung, Gent, ArrudaBoyce, Ogden.

Generalized-Newtonian: Newtonian, PowerLaw, CarreauYasuda, Cross, PowellEyring, ModifiedPowellEyring, HerschelBulkley, Bingham.

Carreau is CarreauYasuda(transition_exponent=2); simplified Cross is Cross(transition_exponent=1).

Ogden

Ogden takes matched tuples and is the only elastic law here that depends on the principal stretches rather than on I1 alone, so exponents may be negative or fractional:

material = Ogden(shear_moduli_pa=(1800.0, 600.0, -300.0), exponents=(1.3, 4.0, -2.0))

A single term with exponents=(2.0,) is neo-Hookean, and reduces to it exactly rather than asymptotically. The small-strain shear modulus is sum(shear_moduli_pa * exponents) / 2, which must be positive; individual moduli may be negative, as above.

Why there is no BlatzKo

The standard Blatz-Ko strain energy is distinguished by its dependence on I3, and this solver assumes an incompressible spherical deformation with stretches (l^-2, l, l), so I3 = 1 identically. In that limit Blatz-Ko is MooneyRivlin(c10=0.0, c01=mu/2) -- verified equal to machine precision -- so a separate class would be an alias, not a new capability. A genuinely compressible Blatz-Ko needs the incompressibility assumption relaxed throughout the radial dynamics.

Powell-Eyring

The pair uses the standard laws, eta_inf + (eta_0 - eta_inf) * asinh(x)/x and its log1p(x)/x variant with x = lambda*|gdot|. Both reduce exactly to Newtonian(eta_0) as lambda -> 0. IMRv2's f_viscosity.m instead uses sinh(x)/x^nc, which is shear-thickening and diverges exponentially, and a log(1+x)/x^nc variant with no finite zero-shear limit unless nc == 1; neither was copied. See upstream.md.

Domain limits

PowerLaw, HerschelBulkley and Bingham require a positive regularization_rate_per_s. The latter two use a smooth yield-stress regularization so the implicit radial equations retain a finite tangent at zero strain rate.

Gent lock-up is a material-domain error: if a trajectory reaches $I_1-3\ge J_m$, the solve raises SimulationError rather than continuing with nonphysical stress.

Prepared Gauss-Legendre rules evaluate the finite-interval stress integrals. The solver evaluates the stress-rate terms and acceleration coefficient analytically, including the viscosity tangent -- no finite-difference derivative is used inside the radial dynamics. The specialized NeoHookeanKelvinVoigt path and the equivalent composable material agree to solver tolerance.

Closed-form memory

The finite-dimensional hot paths are Zener, QuadraticZener, OldroydB and LinearMaxwell:

material = Zener(
    shear_modulus_pa=2500.0,
    viscosity_pa_s=0.1,
    relaxation_time_s=40e-6,
    retardation_time_s=8e-6,
)

Beyond one relaxation time

A single relaxation time is a strong assumption for a crosslinked biopolymer, and the one-mode residual on the gelatin records is correlated at lag one rather than white. Two laws enrich it in different directions, and both reduce to QuadraticZener exactly rather than approximately -- which is what their tests assert, since a reduction that merely held closely would leave room for an error of its own size.

TwoModeQuadraticZener adds a second Maxwell arm: two timescales, with second_share splitting both the elastic target and the viscous forcing between them. At second_share=0 the second memory is driven by nothing, decays from zero, and the law is QuadraticZener.

material = TwoModeQuadraticZener(
    shear_modulus_pa=204.3,
    viscosity_pa_s=0.047,
    relaxation_time_s=2.0e-7,
    retardation_time_s=0.0,
    stiffening=5.3,
    second_relaxation_time_s=2.0e-6,
    second_share=0.3,
)

CarreauZener keeps one arm and lets its timescale move. A Maxwell arm is a spring and a dashpot in series, so lambda = eta/G; if the dashpot obeys Carreau rather than Newton, the relaxation time falls as the medium is sheared harder, and a collapse spans decades of shear rate. Only the dashpot thins -- the elastic target and the solvent term are the one-mode law's. At power_index=1 the thinning factor is exactly one at every shear rate, so the reduction is bit-identical.

material = CarreauZener(
    shear_modulus_pa=204.3,
    viscosity_pa_s=0.047,
    relaxation_time_s=2.0e-7,
    retardation_time_s=0.0,
    stiffening=5.3,
    thinning_time_s=2.0e-6,
    power_index=0.6,
)

Both are comparison candidates, in EXTENDED_MODELS rather than STANDARD_MODELS: at six free parameters the grid quadrature in pyimr.selection costs count**6, so they are scored by candidate_log_evidence instead. See selection.

Distributed nonlinear memory

Giesekus and LinearPTT evolve radial and hoop stress on a prepared, wall-clustered Lagrangian grid:

material = Giesekus(
    viscosity_pa_s=0.1,
    relaxation_time_s=40e-6,
    retardation_time_s=8e-6,
    mobility=0.2,
)

result.stress_state contains radial stress followed by hoop stress on result.stress_reference_radius_ratio.

The constitutive equations at each material point are ordinary differential equations -- there are no spatial derivatives -- so the only spatial approximation is the quadrature for the stress integral. quadrature="gauss" (the default, 240 points) places the material points at Gauss-Legendre nodes and converges spectrally: about five orders of magnitude more accurate than the trapezoid grid it replaced, and cheaper. The table is in discretization.md.

At zero mobility or zero extensibility these converge to the analytic Oldroyd-B solution. Use OldroydB directly when that closure applies -- it is substantially smaller and faster.

Collapse-state shooting

Memory materials can initialize from a resolved equilibrium-to-maximum-radius precursor rather than an assumed unstressed state:

from pyimr import CollapseInitialization

config = SimulationConfig(
    R0=225e-6, Req=37.5e-6,
    material=Zener(2500.0, 0.1, 40e-6, 8e-6),
    collapse=CollapseInitialization(),
)
problem = prepare(config)
problem.collapse_stats

Preparation brackets the precursor velocity, shoots to R/R0 == 1, and retains the complete memory state at the maximum. CollapseStats records the root, achieved maximum, integration work, and immutable stress state. Sensitivities differentiate the event time and shooting root implicitly. Oldroyd-B and distributed Giesekus/PTT states use the same mechanism; the Zener precursor retains the upstream IMRv2 formulation.

Back to the README