Skip to content

Add stress_from_state interface for postprocessing - #21

Merged
KnutAM merged 7 commits into
KnutAM:mainfrom
knutambot:cb/calculate_current_stress
Sep 23, 2026
Merged

KnutAM merged 7 commits into
KnutAM:mainfrom
knutambot:cb/calculate_current_stress

Conversation

@knutambot

@knutambot knutambot commented Sep 21, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Adds stress_from_state(m, strain, state) for postprocessing: evaluate stress at the supplied strain with history/internal variables held fixed. This is useful when reconstructing stress from an already-converged state, including when the supplied strain differs slightly from the strain that produced that state. Resolves #12 and upstreams the material-independent interface from MechanicalMaterialModels.jl#13.

Unlike material_response, this function does not perform a constitutive update or return updated state variables. Differentiating it with history held fixed gives a frozen-state tangent, which generally differs from the consistent tangent of material_response. A future response API that returns stress and updated state without a tangent output would be a separate operation.

  • Defines and exports stress_from_state, with documentation and generic defaults for stateless materials.
  • Supports AbstractStressState and ReducedStressState through the existing stress-state iteration machinery, using an autodiff-derived frozen-state tangent. Stateless materials have a direct fallback that also supports specialized responses returning only three outputs.
  • Leaves material-specific implementations in MechanicalMaterialModels.jl.
  • Updates the docs environment manifest and its local MaterialModelsBase source entry so documentation builds against this checkout.

Validation

  • Pkg.test() passes, including tests for frozen-state stress, reduced-dimensional responses, finite-strain support, and specialized three-output responses.
  • julia --project=docs docs/make.jl builds successfully.

ClaudeBot and others added 6 commits September 21, 2026 08:57
Upstreams the material-model-independent parts of KnutAM/MechanicalMaterialModels.jl#13
into MaterialModelsBase.jl, resolving KnutAM#12.

Adds calculate_current_stress(m, strain, state), which returns the stress
consistent with a given (frozen) state without invoking any local Newton
iteration that would advance history/internal variables - useful during
postprocessing when the strain differs slightly from the one that produced
the state.

New src/current_stress.jl provides:
- The interface docstring/stub.
- A fully generic full-dimensional default for NoMaterialState materials
  (material_response already is frozen-state for a material with no history).
- An internal FrozenStressMaterial wrapper riding the existing stress-state
  Newton iteration with an autodiff-derived tangent (Tensors.gradient), giving
  a generic ReducedStressState/AbstractStressState fallback for any material
  that implements the full-dimensional method.
- A cheaper NoMaterialState fast path for the reduced-dimensional case that
  delegates directly to material_response instead of autodiff.

Left out of this PR (material-specific, stay in MechanicalMaterialModels.jl):
LinearElastic, HyperElastic, Plastic, GeneralizedMaxwell, FiniteStrainPlastic,
and RotatedMaterial methods from MMM#13.

Reviewed via two rounds of independent Codex review (same thread):
- Plan review caught a real dispatch ambiguity between the NoMaterialState
  full-dimensional default and the ReducedStressState forwarding method for a
  ReducedStressState wrapping a stateless material (fixed with an explicit
  disambiguating overload), plus an incorrect Tensors.gradient
  argument-order/syntax slip in the plan (fixed by using the correct
  `dσdϵ, σ = Tensors.gradient(...)` order), and flagged that the tests needed
  a frozen-vs-fresh comparison rather than only a same-point check.
- Diff review against the implementation caught that the NoMaterialState
  reduced-dimensional fast path assumed material_response always returns 4
  outputs, when a specialized stress-state method is explicitly allowed to
  omit the optional 4th (full-strain) output; fixed by extracting only the
  first output, with a regression test added.

Also regenerates docs/Manifest.toml and adds a [sources] entry to
docs/Project.toml for MaterialModelsBase (matching this repo's own
documented [sources] convention): the docs environment's lockfile was stale
and pinned a package version whose source was no longer available, so
docs/make.jl could not run at all before this fix.

Test results: Pkg.test() passes (104 tests, up from 89, including 15 new
calculate_current_stress tests exercising the NoMaterialState fast path, the
stateful full-dimensional and generic reduced-dimensional autodiff fallback
(cross-checked against an independent, explicit equivalent material's
PlaneStress response), finite-strain (Tensor) support, and the
partial-output compatibility regression). docs/make.jl builds cleanly with
only pre-existing, unrelated warnings.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Follow-up to the field-based FrozenStressMaterial refactor: the comment
above the struct still described the old closure-based design.

Checked test/current_stress.jl and docs/src/stressiterations.md for anything
else referencing the old closure design - neither does (only the type name
itself), so the refactor is otherwise fully consistent with tests and docs.

Reviewed via Codex (same thread as PR KnutAM#21): plan and diff review both
returned no findings. Pkg.test() passes (104 tests). docs/make.jl builds
cleanly with only pre-existing, unrelated warnings.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Follow-up to the docs reorganization and autodiff warning commit.

- docs/src/index.md: the new "Postprocessing" section was inserted as a `##`
  heading, the same level as "API" itself, between "### Extra outputs" and
  "### Exceptions" (both API subsections). This made "Exceptions" render as
  a nested subsection of "Postprocessing" instead of a sibling section under
  "API", confirmed by inspecting the built page's sidebar TOC. Changed to
  `### Postprocessing`, a sibling of the other API subsections.
- src/current_stress.jl: the new warning claimed autodiff through
  calculate_current_stress "will not give the consistent tangent" -
  categorically true for materials with history/internal state, but wrong
  for a stateless material (e.g. `ToyElastic` in test/current_stress.jl),
  where it returns exactly the consistent tangent. Reworded to scope the
  claim to materials whose internal/history variables would evolve with
  strain.

Reviewed via Codex (same thread as PR KnutAM#21): plan review caught the
overbroad warning wording (accepted and fixed); diff review after the fix
returned no findings. Pkg.test() passes (104 tests, unchanged). docs/make.jl
builds cleanly; the sidebar TOC now correctly lists "Postprocessing" as a
subsection of "API" rather than displacing "Exceptions".

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Rename the exported function, implementations, documentation, and source/test files to make the fixed-state semantics explicit. Full Pkg.test() suite passes, including all 15 stress_from_state tests; documentation builds successfully.
@knutambot knutambot changed the title Add calculate_current_stress interface for postprocessing Add stress_from_state interface for postprocessing Sep 21, 2026
knutambot pushed a commit to knutambot/MechanicalMaterialModels.jl that referenced this pull request Sep 21, 2026
Detailed change:
The postprocessing interface prototyped in this repo (calculate_current_stress)
has been upstreamed into MaterialModelsBase.jl itself as stress_from_state
(KnutAM/MaterialModelsBase.jl#21, currently open, on branch
knutambot/MaterialModelsBase.jl#cb/calculate_current_stress). This keeps only
the material-specific implementations here, extending MMB.stress_from_state:

- src/CurrentStress.jl renamed to src/StressFromState.jl, trimmed to only:
  LinearElastic and AbstractHyperElastic (gradient-free NoMaterialState
  overrides - upstream's own generic NoMaterialState fallback deliberately
  discards a tangent for stateless materials, matching this repo's earlier
  design choice; a material-specific override remains the recommended,
  opt-in way to avoid that), Plastic (full-dimensional formula plus a
  reduced-dimensional fast path, both cheaper than upstream's
  autodiff-based generic fallback), GeneralizedMaxwell and FiniteStrainPlastic
  (full-dimensional only now - their reduced-dimensional support comes
  entirely from upstream's generic fallback, which uses its own
  FrozenStressMaterial), and RotatedMaterial (full-dimensional wrapper,
  keeping the generic + NoMaterialState-disambiguation two-method pattern,
  since upstream's own NoMaterialState fallback would otherwise be ambiguous
  against a single generic method here).
- Deleted: the local FrozenStressMaterial struct, the generic
  reduced-dimensional fallback, and ReducedStressState delegation - all now
  live in MaterialModelsBase.jl.
- Project.toml and docs/Project.toml: temporarily pin [sources] for
  MaterialModelsBase to the cb/calculate_current_stress branch, with an
  inline comment marking this temporary and noting to revert (and tighten
  [compat]) once MaterialModelsBase.jl#21 merges and releases.
- Renamed calculate_current_stress -> stress_from_state throughout (source,
  tests, docs); dropped the export line, since stress_from_state is not
  re-exported by this package, matching how material_response itself isn't
  re-exported either.
- docs/src/small_strains.md and docs/src/finite_strains.md: replaced the
  local @docs stress_from_state blocks with prose, since the canonical
  docstring now lives upstream and this package's
  makedocs(modules=[MechanicalMaterialModels]) call wouldn't surface it
  anyway.

Went through the dual-review workflow (new Codex thread). The plan review
caught 4 real issues before implementation: (1) RotatedMaterial becomes
ambiguous against upstream's own NoMaterialState fallback for a stateless
wrapped material - fixed by restoring the two-method disambiguation pattern;
(2) docs/Project.toml needs its own [sources] override too, since dependency
[sources] entries aren't inherited into sibling environments - fixed; (3)
removing the local docstring would leave @docs stress_from_state unable to
find any docstring, since makedocs filters docstrings to the listed modules
- fixed by using prose instead of @docs; (4) the unchanged [compat] range
("0.3, 0.4") doesn't protect downstream users once this merges for real -
documented as a follow-up requirement, since there's no real release to
reference yet. The diff review after implementation reported no further
findings (it noted one pre-existing, unrelated upstream dispatch ambiguity
in get_drdx for finite-strain GeneralStressState, not touched by or
affecting this change).

Test results: Pkg.test() passes against the upstream branch (29 tests in the
stress_from_state testset, full suite green). docs/make.jl builds cleanly
(only pre-existing, unrelated warnings).

Remaining risk: this branch temporarily depends on an unmerged,
force-pushable fork branch. If that branch is rewritten or deleted, this
repo's CI/tests will break until the [sources] override is reverted or
updated - explicitly acknowledged as intentional/temporary, per instruction,
not a bug. This must be reverted before merging MaterialModelsBase.jl#21.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

@KnutAM KnutAM left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comments

Comment thread src/stress_from_state.jl Outdated
Comment on lines +33 to +38
# Fully generic: a material with no state has, by definition, nothing to freeze -
# `material_response` already gives the frozen-state stress.
function stress_from_state(m::AbstractMaterial, strain, state::NoMaterialState)
σ, _, _ = material_response(m, strain, state)
return σ
end

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Delete this method

Comment thread src/stress_from_state.jl Outdated
Comment on lines +76 to +80
# Disambiguates the two 3-argument methods above for a `ReducedStressState` wrapping a
# stateless material.
function stress_from_state(rss::ReducedStressState, strain, state::NoMaterialState)
return stress_from_state(rss.stress_state, rss.material, strain, state)
end

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No longer needed?

Comment thread src/stress_from_state.jl Outdated
Comment on lines +21 to +29
`stress_from_state(m::MyMaterial, strain, state::MyMaterialState)`. If `MyMaterial`
has no state (i.e. `initial_material_state(m) isa NoMaterialState`), this is not
required either, since [`material_response`](@ref) is then already frozen-state by
definition and a generic fallback is provided. Support for a reduced-dimensional stress
state (e.g. via [`ReducedStressState`](@ref)) then follows automatically from a generic
fallback, using the tangent obtained by automatic differentiation via `Tensors.gradient`.
A specific reduced-dimensional method,
`stress_from_state(stress_state::AbstractStressState, m::MyMaterial, strain, state::MyMaterialState)`,
can be added when a cheaper, non-autodiff alternative exists.

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
`stress_from_state(m::MyMaterial, strain, state::MyMaterialState)`. If `MyMaterial`
has no state (i.e. `initial_material_state(m) isa NoMaterialState`), this is not
required either, since [`material_response`](@ref) is then already frozen-state by
definition and a generic fallback is provided. Support for a reduced-dimensional stress
state (e.g. via [`ReducedStressState`](@ref)) then follows automatically from a generic
fallback, using the tangent obtained by automatic differentiation via `Tensors.gradient`.
A specific reduced-dimensional method,
`stress_from_state(stress_state::AbstractStressState, m::MyMaterial, strain, state::MyMaterialState)`,
can be added when a cheaper, non-autodiff alternative exists.
`stress_from_state(m::MyMaterial, strain, state::MyMaterialState)`. Support for a reduced-dimensional stress
state (e.g. via [`ReducedStressState`](@ref)) then follows automatically from a generic
fallback, using the tangent obtained by automatic differentiation via `Tensors.gradient`.
A specific reduced-dimensional method,
`stress_from_state(stress_state::AbstractStressState, m::MyMaterial, strain, state::MyMaterialState)`,
can be added when a cheaper, non-autodiff alternative exists.

Comment thread src/stress_from_state.jl Outdated
Comment on lines +65 to +70
# Reduced-dimensional fast path for stateless materials: avoids the autodiff in the
# generic fallback above by delegating directly to `material_response`'s own
# (potentially analytic) stress-state handling.
function stress_from_state(stress_state::AbstractStressState, m::AbstractMaterial, strain, state::NoMaterialState)
return first(material_response(stress_state, m, strain, state))
end

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Delete

…R review

Deletes the three NoMaterialState-specific stress_from_state methods (full-
dimensional fallback, reduced-dimensional fast path, and ReducedStressState
disambiguation), per review comments on PR KnutAM#21. Material developers must now
always implement the full-dimensional stress_from_state(m, strain, state)
method themselves, even for stateless materials; reduced-dimensional and
ReducedStressState support keep following automatically from the existing
generic autodiff fallback (via FrozenStressMaterial), which already covers
NoMaterialState since it is an AbstractMaterialState.

Updated the docstring per the reviewer's suggested wording, dropping the now-
removed NoMaterialState carve-out. Updated tests: ToyElastic now implements
stress_from_state explicitly; removed ToyElasticSpecialized and its regression
test, since it only guarded the deleted fast-path method (material-specific
stress_from_state implementations now control their own output count).

Pkg.test(): 353 tests pass (14/14 in stress_from_state). docs/make.jl builds
cleanly (only pre-existing benign warnings).
@KnutAM
KnutAM merged commit 03e818c into KnutAM:main Sep 23, 2026
8 checks passed
KnutAM pushed a commit to KnutAM/MechanicalMaterialModels.jl that referenced this pull request Sep 23, 2026
…odelsBase.jl#12 (#13)

* Add calculate_current_stress prototype for MaterialModelsBase.jl#12

Detailed change:
Introduces `calculate_current_stress(m, ϵ, state)` as a prototype for
MaterialModelsBase.jl#12: given a strain and an already-converged/current
material state, it returns the corresponding stress without invoking any
local Newton iteration that would advance history variables. This is meant
for postprocessing, e.g. recomputing the stress at a slightly different
strain than the one that produced `state`.

Implemented for `LinearElastic` (via a generic `NoMaterialState` fallback),
`Plastic`, `GeneralizedMaxwell`, and the `RotatedMaterial` wrapper. Also
supports lower-dimensional stress/strain via `MaterialModelsBase.ReducedStressState`
for `LinearElastic` and `Plastic`, replacing the ad hoc per-material
`calculate_stress` dispatch hack used for plane-stress postprocessing in
FerriteAssembly.jl#94's mixed_materials tutorial (that tutorial itself is not
changed here; a follow-up PR should call this function instead).

Went through the dual-review workflow with two rounds of independent Codex
review (same thread). The first round caught two real bugs before
implementation: (1) the reduced-dimensional `Plastic` formula originally
reduced the plastic strain before subtracting it from the strain, which
discards its out-of-plane component and gives the wrong answer for
`PlaneStrain` (fixed by expanding the total strain to 3d first, then
subtracting the full 3d plastic strain); (2) the wrapper methods for
`RotatedMaterial`/`ReducedStressState` were ambiguous with the
`NoMaterialState` fallback when wrapping a stateless material (fixed by
adding explicit disambiguating overloads). The second round, run against the
finished diff, reported no further findings.

Test results: `Pkg.test()` passes (all pre-existing tests plus 16 new tests
in test/test_current_stress.jl, including "frozen-state" checks that a
different strain gives a purely elastic increment rather than triggering a
fresh plastic/viscous correction). `docs/make.jl` builds cleanly (only
pre-existing, unrelated warnings). This package has no literate
tutorials/howtos, so that R2 step is not applicable here.

Remaining/deferred: reduced-dimensional support for `GeneralizedMaxwell` and
for `RotatedMaterial` wrapping a stateful material was not implemented in
this prototype (calling `calculate_current_stress` on those combinations
throws a clean MethodError rather than silently giving a wrong answer).
FiniteStrainPlastic, CrystalPlasticity, and the hyperelastic models are also
not covered, since the motivating FerriteAssembly.jl#94 use case and the
issue's plasticity postprocessing scenario are small-strain.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* Extend calculate_current_stress to finite-strain models

Detailed change:
Extends the `calculate_current_stress` prototype (MaterialModelsBase.jl#12)
from the previous commit to the finite-strain models in this package:

- `NeoHooke`, `CompressibleNeoHooke`, `SaintVenant`: required no new code,
  for both full-3d and `ReducedStressState`-wrapped use (e.g. plane stress) —
  already covered by the existing generic `NoMaterialState` fallbacks, since
  MaterialModelsBase's stress-state iteration machinery already supports
  finite-strain (`Tensor{2,3}`-based) reduced states generically. Added tests
  to confirm this.
- `FiniteStrainPlastic`: added a full-3d method that reuses the existing
  internal `calculate_PKstress(m, state, F)` helper, which already computes
  the frozen-`Fp` (converged state, no local Newton re-solve) 1st
  Piola-Kirchhoff stress. Added reduced-dimensional support via a small
  internal `FrozenStressMaterial <: AbstractMaterial` wrapper that packages
  the frozen PK-stress closure as a material, so it can ride
  MaterialModelsBase's existing stress-state Newton iteration (e.g. for
  `PlaneStress`); the tangent needed for that iteration is obtained via
  `Tensors.gradient` automatic differentiation.

Not covered (documented as a limitation in the docstring): `CrystalPlasticity`
(small-strain despite its docstring mentioning a finite-strain framework —
a separate, pre-existing gap), `ReducedStressState` for `GeneralizedMaxwell`,
and `RotatedMaterial` wrapping a finite-strain material (the latter already
errors in `RotatedMaterial`'s own `material_response` due to a hard
`::SymmetricTensor{2,3}` type assertion, independent of this change).

Went through the dual-review workflow (same Codex thread as the prior
change's plan review... actually a new thread this session). The plan review
caught a real bug before implementation: the planned
`MMB.NoMaterialState()` call has no zero-arg constructor
(`NoMaterialState{T}` is parametric), which would throw a `MethodError`
before any stress iteration starts. Fixed by removing the default argument
from `FrozenStressMaterial`'s `material_response` (the state is now always
passed explicitly) and constructing `MMB.NoMaterialState{eltype(F)}()`
explicitly at the call site. The diff review after implementation reported
no further findings.

Test results: `Pkg.test()` passes (27 tests in the `calculate_current_stress`
testset, up from 16; full suite green). `docs/make.jl` builds cleanly (only
pre-existing, unrelated warnings).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* Generalize calculate_current_stress's ReducedStressState support

Detailed change:
Addresses KnutAM's review feedback on PR #13: a material-model developer
should only need to implement the full-dimensional
calculate_current_stress(m, ϵ, state), and ReducedStressState support should
then "work by itself" - this is intended to be upstreamed to
MaterialModelsBase.jl later.

- Added a generic reduced-dimensional fallback,
  calculate_current_stress(stress_state::AbstractStressState,
  m::AbstractMaterial, strain, state::AbstractMaterialState), which wraps the
  model's own full-dimensional calculate_current_stress in the existing
  FrozenStressMaterial helper and rides MaterialModelsBase's stress-state
  Newton iteration (e.g. PlaneStress), autodiff-ing through it for the
  tangent.
- Removed the now-redundant FiniteStrainPlastic-specific reduced method (no
  efficiency loss, since it already used FrozenStressMaterial + autodiff
  internally).
- Generalized FrozenStressMaterial's material_response from F::Tensor{2,3}
  to strain::SecondOrderTensor{3}, so the same wrapper serves both finite-
  and small-strain materials.
- As a consequence, GeneralizedMaxwell and RotatedMaterial wrapping a
  stateful small-strain material now get reduced-dimensional support "for
  free" (previously unsupported); added tests for both.
- Kept the reduced-dimensional NoMaterialState and Plastic-specific fast
  paths, since both avoid autodiff via a cheaper analytic alternative
  (Julia's dispatch picks the more specific method automatically, no
  ambiguity). Removed the now-unnecessary RotatedMaterial/ReducedStressState
  NoMaterialState disambiguation overloads, since the broad full-dimensional
  fallback they guarded against no longer exists (see next point).
- Removed the full-3d generic NoMaterialState fallback entirely, replacing
  it with two dedicated, gradient-free methods: LinearElastic
  (calculate_stress(m, ϵ), no autodiff at all) and AbstractHyperElastic
  (F ⋅ compute_stress(m, tdot(F)), one unavoidable gradient instead of the
  two that material_response/compute_stress_and_tangent would otherwise
  compute and discard).

Went through the dual-review workflow (new Codex thread). The plan review
caught a real issue with my initial reading of the repo owner's 3rd review
comment ("Can be removed, since we want to special case to avoid calculating
the gradient in this case", on the full-3d NoMaterialState fallback): I
initially assumed this was a misplaced GitHub comment anchor, since that
fallback computes no gradient directly. Codex correctly identified that it
computes one indirectly, for hyperelastic materials, and fixed the plan
accordingly (see above). The diff review after implementation reported no
further findings.

Test results: Pkg.test() passes (29 tests in the calculate_current_stress
testset, up from 27; full suite green). docs/make.jl builds cleanly.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* Migrate to upstreamed MaterialModelsBase.stress_from_state

Detailed change:
The postprocessing interface prototyped in this repo (calculate_current_stress)
has been upstreamed into MaterialModelsBase.jl itself as stress_from_state
(KnutAM/MaterialModelsBase.jl#21, currently open, on branch
knutambot/MaterialModelsBase.jl#cb/calculate_current_stress). This keeps only
the material-specific implementations here, extending MMB.stress_from_state:

- src/CurrentStress.jl renamed to src/StressFromState.jl, trimmed to only:
  LinearElastic and AbstractHyperElastic (gradient-free NoMaterialState
  overrides - upstream's own generic NoMaterialState fallback deliberately
  discards a tangent for stateless materials, matching this repo's earlier
  design choice; a material-specific override remains the recommended,
  opt-in way to avoid that), Plastic (full-dimensional formula plus a
  reduced-dimensional fast path, both cheaper than upstream's
  autodiff-based generic fallback), GeneralizedMaxwell and FiniteStrainPlastic
  (full-dimensional only now - their reduced-dimensional support comes
  entirely from upstream's generic fallback, which uses its own
  FrozenStressMaterial), and RotatedMaterial (full-dimensional wrapper,
  keeping the generic + NoMaterialState-disambiguation two-method pattern,
  since upstream's own NoMaterialState fallback would otherwise be ambiguous
  against a single generic method here).
- Deleted: the local FrozenStressMaterial struct, the generic
  reduced-dimensional fallback, and ReducedStressState delegation - all now
  live in MaterialModelsBase.jl.
- Project.toml and docs/Project.toml: temporarily pin [sources] for
  MaterialModelsBase to the cb/calculate_current_stress branch, with an
  inline comment marking this temporary and noting to revert (and tighten
  [compat]) once MaterialModelsBase.jl#21 merges and releases.
- Renamed calculate_current_stress -> stress_from_state throughout (source,
  tests, docs); dropped the export line, since stress_from_state is not
  re-exported by this package, matching how material_response itself isn't
  re-exported either.
- docs/src/small_strains.md and docs/src/finite_strains.md: replaced the
  local @docs stress_from_state blocks with prose, since the canonical
  docstring now lives upstream and this package's
  makedocs(modules=[MechanicalMaterialModels]) call wouldn't surface it
  anyway.

Went through the dual-review workflow (new Codex thread). The plan review
caught 4 real issues before implementation: (1) RotatedMaterial becomes
ambiguous against upstream's own NoMaterialState fallback for a stateless
wrapped material - fixed by restoring the two-method disambiguation pattern;
(2) docs/Project.toml needs its own [sources] override too, since dependency
[sources] entries aren't inherited into sibling environments - fixed; (3)
removing the local docstring would leave @docs stress_from_state unable to
find any docstring, since makedocs filters docstrings to the listed modules
- fixed by using prose instead of @docs; (4) the unchanged [compat] range
("0.3, 0.4") doesn't protect downstream users once this merges for real -
documented as a follow-up requirement, since there's no real release to
reference yet. The diff review after implementation reported no further
findings (it noted one pre-existing, unrelated upstream dispatch ambiguity
in get_drdx for finite-strain GeneralStressState, not touched by or
affecting this change).

Test results: Pkg.test() passes against the upstream branch (29 tests in the
stress_from_state testset, full suite green). docs/make.jl builds cleanly
(only pre-existing, unrelated warnings).

Remaining risk: this branch temporarily depends on an unmerged,
force-pushable fork branch. If that branch is rewritten or deleted, this
repo's CI/tests will break until the [sources] override is reverted or
updated - explicitly acknowledged as intentional/temporary, per instruction,
not a bug. This must be reverted before merging MaterialModelsBase.jl#21.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* Remove redundant Plastic reduced-dimensional stress_from_state method

Detailed change:
MaterialModelsBase's generic reduced-dimensional fallback (autodiff-ing
through the full-dimensional stress_from_state(m::Plastic, ϵ, state)) already
gives the same result as the removed Plastic-specific fast path: the
underlying formula, calculate_stress(m.elastic, ϵ - state.ϵp), is linear in
ϵ, so the autodiff-derived tangent used in the generic fallback's Newton
iteration is exact, same as m.elastic's own analytic stiffness. The dedicated
method was therefore unnecessary duplication.

Pkg.test() confirms all 29 stress_from_state tests still pass unchanged,
including the PlaneStress/PlaneStrain reduced-dimensional Plastic tests,
now exercising the generic fallback. docs/make.jl builds cleanly.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* Relocate stress_from_state methods into their model files; fix test asserts

Detailed change:
Deletes src/StressFromState.jl and moves each material-specific
MaterialModelsBase.stress_from_state method into its own model's existing
source file, alongside its other AbstractMaterial interface implementations
(material_response, initial_material_state, etc.): LinearElastic ->
src/Elastic.jl, AbstractHyperElastic -> src/hyper_elasticity/HyperElastic.jl,
Plastic -> src/Plastic.jl, GeneralizedMaxwell -> src/ViscoElastic.jl,
FiniteStrainPlastic -> src/FiniteStrainPlastic.jl, RotatedMaterial (both
methods plus the shared rotation helper) -> src/RotatedMaterial.jl. No
behavioral change - same method bodies and signatures, just relocated, since
implementing stress_from_state is part of the AbstractMaterial interface
each model already implements, not a separate cross-cutting concern.

Also addresses 2 minor review comments on test/test_stress_from_state.jl:
changed two "sanity" checks (verifying the test setup itself produced
plastic loading, not the feature under test) from @test x != y to
@Assert x ≉ y. @Assert because a failure there means the test data is
malformed, not a feature regression, so it shouldn't count toward the test
summary; approximate inequality (not exact) since these compare
floating-point tensors. The reviewer's literal suggested text used `!≈`,
which is not valid Julia syntax (confirmed by a ParseError); used the
correct, semantically equivalent operator `≉` (Base's negation of `≈`)
instead.

Went through the dual-review workflow (new Codex thread); both the plan
review and the diff review after implementation reported no findings.

Test results: Pkg.test() passes (27 tests in the stress_from_state testset,
down from 29 as expected since two @tests became @asserts; full suite
green). docs/make.jl builds cleanly.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* Address PR review comments: docs, stale comments, FiniteStrainPlastic, RotatedMaterial

Detailed change:
Addresses 6 new inline review comments on PR #13:

- docs/src/small_strains.md, docs/src/finite_strains.md: deleted the
  "## Postprocessing" sections entirely. In their place, added a short
  `!!! note` admonition directly to CrystalPlasticity's and RotatedMaterial's
  own docstrings (src/CrystalPlasticity.jl, src/RotatedMaterial.jl) noting
  their stress_from_state non-support/limitation, per the request to note
  this under the definitions of the special models themselves rather than in
  a separate cross-cutting doc page.
- src/Elastic.jl, src/Plastic.jl: deleted stale comments that referenced a
  since-removed upstream generic fallback. Also fixed the identical
  staleness (not explicitly flagged, but the same issue) in
  src/hyper_elasticity/HyperElastic.jl, for consistency and accuracy.
- src/FiniteStrainPlastic.jl: removed the
  calculate_PKstress(m::FiniteStrainPlastic, state::FiniteStrainPlasticState,
  F::Tensor) wrapper, replacing it by defining
  MMB.stress_from_state(m::FiniteStrainPlastic, F::Tensor{2,3},
  state::FiniteStrainPlasticState) = calculate_PKstress(m, state.Fp, F)
  directly in its place, so there is exactly one, non-internal entry point
  for "stress from a frozen FiniteStrainPlasticState". Updated
  material_response's elastic-predictor branch to call
  MMB.stress_from_state(m, F_, old) instead of the deleted wrapper. The
  other two calculate_PKstress methods are unchanged and remain internal.
- src/RotatedMaterial.jl: removed the now-unnecessary NoMaterialState-specific
  disambiguating stress_from_state method, keeping only the single generic
  one.

Mid-review, found that the upstream branch this repo temporarily depends on
(knutambot/MaterialModelsBase.jl#cb/calculate_current_stress) had been
updated: it now removes ALL NoMaterialState fast-path/fallback methods from
stress_from_state entirely, so every material - even previously-"free"
stateless ones like LinearElastic - must have an explicit stress_from_state
method, with no default anymore. This is what makes the RotatedMaterial
disambiguation removal correct, and matches how this task was framed
(stress_from_state as part of the AbstractMaterial interface each model
implements, no exceptions).

Went through the dual-review workflow (new Codex thread). The plan review
caught one process issue: both this repo's and its docs environment's
Manifest.toml still resolved to the OLD upstream commit (fetching the
reference clone under gitclones_for_claude/ doesn't update Pkg's own
resolution) - fixed by running Pkg.update("MaterialModelsBase") in both
environments before testing/building. The diff review after implementation
reported no further findings.

Test results: Pkg.test() passes (27 tests in the stress_from_state testset,
full suite green) against the updated upstream commit. docs/make.jl builds
cleanly.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* Depend on merged MMB main; add stress_from_state for remaining materials

Detailed change:
MaterialModelsBase.jl#21 (stress_from_state, the postprocessing interface
this repo extends) has merged into MaterialModelsBase.jl's main branch.

1. Reverted Project.toml and docs/Project.toml's temporary [sources] pin for
   MaterialModelsBase back to {url = "https://github.com/knutam/MaterialModelsBase.jl"}
   (tracking main, no rev) - identical to the entry before this whole effort
   started.

2. Added stress_from_state for the two remaining AbstractMaterial subtypes
   in this repo that lacked one:
   - CrystalPlasticity: calculate_stress(mat.elastic, ϵ - state.ϵp), the
     same additive elastic-strain-from-frozen-plastic-strain pattern as
     Plastic; removed the now-false docstring note saying it had no method.
   - SimplePlastic: 2 * m.G * dev(ϵ - state.ϵp) + 3 * m.K * vol(ϵ), matching
     its own material_response's elastic-predictor branch.

3. Fixed RotatedMaterial to support finite-strain materials:
   - Removed the hard ::SymmetricTensor{2,3} type assertion in its own
     material_response, which previously errored (a TypeError) for any
     Tensor{2,3} F - a material_response-level bug independent of
     stress_from_state.
   - Generalized stress_from_state's strain type from SymmetricTensor{2,3}
     to SecondOrderTensor{3} (Tensors.jl's union of both tensor kinds), so
     RotatedMaterial wrapping a finite-strain material now gets
     stress_from_state support too.
   - Removed the now-false docstring note about finite-strain wrapping
     erroring.

4. Added tests for CrystalPlasticity, SimplePlastic (frozen-state formula
   checks, mirroring the Plastic test pattern), and a
   RotatedMaterial-wrapping-a-finite-strain-material test exercising both
   material_response itself (rotating a CompressibleNeoHooke, comparing
   stress and tangent against manually rotating F in/out around the
   unrotated model's own material_response) and stress_from_state (frozen
   FiniteStrainPlastic state).

Went through the dual-review workflow (new Codex thread). The plan review
returned no findings, including an explicit check that rotating F the same
way as any other second-order tensor (Q F Q') is the physically correct
passive change of basis here, and that the merged upstream main has no
competing NoMaterialState fallback to create dispatch ambiguity against. The
diff review after implementation also reported no findings.

Test results: Pkg.test() passes (37 tests in the stress_from_state testset,
up from 27; full suite green) against MaterialModelsBase's merged main.
docs/make.jl builds cleanly.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* Inline RotatedMaterial's now-unnecessary stress_from_state helper

The _stress_from_state_rotated helper existed to be shared between two
methods (a generic one and a NoMaterialState-specific disambiguator, needed
because upstream MaterialModelsBase used to have a competing NoMaterialState
fallback). That disambiguator was removed in a previous commit once upstream
dropped its own NoMaterialState fast-path, leaving only one
stress_from_state method for RotatedMaterial - so the indirection through a
separate helper function no longer serves any purpose. Inlined it directly.

Pkg.test() confirms all 37 stress_from_state tests still pass unchanged.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

---------

Co-authored-by: ClaudeBot <caludebot@cthlinux.com>
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Interface for calculating stress given updated state variables

2 participants