Repository navigation
Conversation
core/tensor_kind.h grows stress_like into kinded<T, voigt, leg...> with the aliases for reference strains, two-point and material tensors, vectors and moduli, and rotated() acting on spatial legs only. statev_map builds every kinded entry with one builder: 3/9 full slots for voigt::none, 6 otherwise, a rotate step only when a leg is spatial, and describe() marks per kind. Plain tensor2 history keeps its engineering, spatial meaning.
The value of a history stays a plain tmech tensor; how a host stores and
rotates it is chosen when the history is declared.
- core/history_kind.h replaces core/tensor_kind.h: leg, storage
{engineering, plain, full}, history_kind {store, legs} with valid_for,
rotates, rotated (Q = R on a spatial leg, I on a material one) and tags;
the named kinds in history_kinds and default_history_kind<V>.
- add_history_output<T>(name, kind, initial): an invalid kind throws
logic_error at declaration; the material records the kind and
history_kind_of() returns it.
- statev_map entries are slot objects (scalar, tensor_slot<1|2>, rank 4);
it casts by value type only and takes the kind from the owning material,
else default_history_kind. STATEV layout and describe() are unchanged.
- core/stress_like.h removed; the #91 probe declares
history_kinds::stress_like.
|
Rework in c149e55. As the maintainer asked, the history kind is now given when the history is declared, instead of being carried by the value type. The description above has been updated to match.
|
Replace the history kind (legs x storage) by codecs: plain structs that say how one value type sits in a flat buffer and how a rotation acts on it. add_history records a history_slot (concept/model erasure) with the given codec, or default_codec<T>; statev_map only loops over the slots, without dynamic_cast or kind enum. - core/history_codec.h: scalar, strain, stress, reference_strain, reference_tensor, two_point, vector, reference_vector, moduli; the history_codec_for concept; default_codec; require_symmetric and require_minor_symmetric moved from statev_map. - core/history_slot.h: the type-erased handle. - core/voigt_buffer.h: the buffer conversions moved from umat/tensor_conversion.h, which re-exports them. - material_interface/material_base: add_history and add_history_output overloads taking a codec; history_slot_of(). - core/history_kind.h removed; the #91 probe uses codecs::stress. - tests/test_history_codecs.cpp replaces test_history_kinds.cpp.
|
Round 2: history codecs, type-erased at declaration (43f9ed7). The kind design of c149e55 is replaced. Where a history is declared, its value type and its codec (how it is stored in STATEV and how DROT acts on it) are captured once in a
|
j2_kinematic_plasticity::back_stress and incremental_linear_elasticity::stress are stress-like and now use codecs::stress: STATEV holds their shear as is instead of doubled. external_deformation_gradient_source::deformation_gradient uses codecs::two_point: all nine components, rotated as R F. Every other shipped history already takes the right default codec.
|
a6c4764 sets explicit codecs on the shipped materials' histories. I checked every history declared in
STATEV change for the first two. Their shear slots now hold β₁₂ and σ₁₂ instead of twice those values. Widths and offsets are the same, but old restart files of these two materials read back with doubled shear. All other shipped histories already get the right default codec: the plastic strains and phase strains use There is one new test per material. Each was red before its port. Gate: 1510/1510 tests, 0 warnings. CI: 4/4 green. Follow-up: the history unknowns of |
Implements #216 (part of epic #221). No other PR has to merge first.
What and why
On main,
statev_mapknew three history shapes: a scalar, a symmetric rank-2 tensor stored like a strain (plusstress_likefrom #91), and a minor-symmetric rank-4 tensor. It rotated every tensor entry under DROT. Reference-configuration history (Cp, Fp), non-symmetric tensors (Fe), vectors and symmetric non-strains (back stress, conductivity) had no correct storage.This PR adds history codecs. When a history is declared, both its value type and the way a host stores and rotates it are known. A type-erased handle records them once, at that point. After that,
statev_maponly loops over the handles. It needs nodynamic_cast, no switch and no kind enum, and scalars, rank-4 tensors and user types all take the same path.core/history_codec.h(new, host-neutral)A codec is a plain struct that says how one value type sits in a flat buffer and how a rotation R acts on it. It has
width,read(v, buffer)andwrite(v, buffer, who), plus an optionaltagfordescribe()and an optionalrotate(v, R). A codec withoutrotateis never rotated.describe()tagscalarstrain[engineering shear]stress[stress]reference_strain[engineering shear] [reference]reference_tensor[full] [reference]two_point[full] [two-point]vector[vector]reference_vector[vector] [reference]modulistrain : reference_strain,two_point : reference_tensorandvector : reference_vector: each spatial codec is its reference codec plusrotate().stressalso covers spatial moduli such as a conductivity.history_codec_for<C, V, T>is the concept: C stores a V in a T buffer (width,read,write). A codec that does not fit the value is a compile error, e.g.reference_strainon a vector.default_codec<V>is the codec a history gets when it is declared without one:scalarfor an arithmetic type,vectorfortensor<T,3,1>,strainfortensor<T,3,2>,modulifortensor<T,3,4>. For any other type it isvoid.require_symmetricandrequire_minor_symmetricmoved here fromstatev_map, with the same messages. They name the history (owner::name).require_minor_symmetricnow reusesis_minor_symmetric.core/history_slot.h(new): the type-erased handlehistory_slot<T>uses the usual concept/model erasure. It holds aunique_ptrto amodel<V, Codec>, which keeps a reference to thehistory_property<V>. It provides:width();unpack(), which sets the old and the new side;pack(), which writes the new side;rotate(R), which turns both sides and does nothing when the codec has norotate;rotates()andtag().Declaration
material_interface::add_history<T>(name, data)records a slot withdefault_codec<T>if that codec fits the context'svalue_type.add_history<T>(name, data, Codec), is constrained byhistory_codec_forand records the given codec instead.material_base::add_history_output<T>(name, Codec, initial = T{})sits next to the existing(name, initial)overload and has the same constraint.history_slot_of(property)returns the recorded slot, or null.add_history(checked with grep).Example:
add_history_output<tensor2>("back_stress", codecs::stress{}).umat/statev_map.h{owner, name, offset, slot*}.add_entryasks the owning material for the slot.fatal_error, which names the property: "has a type that cannot be stored in STATEV — declare it with a codec (core/history_codec.h)". Two cases lead here: a type with no default codec that was declared without one, or aninthistory in adoublebuffer.unpack,pack,rotate_history,has_rotatable_history,describe,nstatvandcheck_nstatvkeep their public behaviour.describe()strings are the same as on main. Two shipped materials now declarecodecs::stress(see Shipped materials), so their shear slots change.Layering:
core/voigt_buffer.h(new, host-neutral)These functions moved from
umat/tensor_conversion.hintonumsim::materials, unchanged apart from the namespace:canonical_widthand the shear-convention notes;strain_*_buffer,stress_*_bufferandtangent_*_buffer;is_minor_symmetric;full_*_buffer, which is new in this PR.tensor_conversion.hincludes the new header and re-exports these names intoumatwithusingdeclarations. Every existing spelling (umat::strain_to_buffer, unqualified uses insideumat) compiles unchanged.Shipped materials
Every history declared in
include/was checked. Three of them need a codec other than their type's default, and now declare it:j2_kinematic_plasticityback_stresscodecs::stressincremental_linear_elasticitystresscodecs::stressexternal_deformation_gradient_sourcedeformation_gradientcodecs::two_pointEverything else already gets the right codec by default:
j2_plasticity,j2_rk_plasticity,j2_kinematic_plasticity,drucker_prager_plasticity), the phase strains ofmean_field_strainandexternal_strain_source::strainusestrain;scalar. That covers the equivalent plastic strains,kappa, the curing and RK states and the scalar sources.STATEV change for restart files. For the first two materials, the shear slots (4–6 of the entry) now hold β₁₂ and σ₁₂ instead of twice those values. Widths, offsets and rotation are unchanged. Only the stored shear values differ, so an old restart file of these two materials reads back with doubled shear stress. The deformation gradient source is excluded from STATEV under a host, so no host layout changes for it.
docs/plasticity.mdand thej2_kinematic_plasticityheader no longer say "until #91 lands".Open, outside this PR: the history unknowns of
system(#229) take the default codec,strainfor a tensor. A stress-like unknown such as a back stress would need acodecfield onsystem_unknown. Whichever of #224 and #229 merges second should add that field.Changes since the previous revision (c149e55) and why
The maintainer found the kind design still too complex. It had legs × storage combinations,
valid_for,rotates,used,to_string,history_kind_rankanddefault_history_kind, a slot class hierarchy insidestatev_map, and a four-waydynamic_cast.core/history_kind.hand every use of it;history_kind_ofand the slot hierarchy instatev_map, with all itsdynamic_casts;logic_error. A codec that does not fit is now a compile error.material_moduliandspatial_modulikinds.stresscovers spatial moduli, and a reference-frame modulus can wait for a model that needs one.core/stress_like.hstays removed. The STATEV: stress-like tensor history without the engineering-shear factor (#63) #91 probe now declaresadd_history_output<tensor2>("back_stress", codecs::stress{}).Decisions and deviations from the brief
core/elastic_constants.h. The codecs call the existingnumsim::materials::rotate(rank 2: R·a·Rᵀ; rank 4: written with tmechotimesu/dcontract) instead of movingumat::rotateto core. Moving it would have put a second, overlappingrotateoverload set intonumsim::materials.umat::rotatestays where it was. The rank-4 result is the same rotation that main applies, andStatevMap.RotatesRankFourHistorypasses unchanged.history_codec_for<Codec, V, T>. A user value such as a struct has no scalar type to infer the buffer from. The extra parameter also makesinthistory fail thescalarcodec for adoublebuffer.history_slottakeswhofrom the property (traits().id), not as a constructor argument, so the name has one source.unordered_mapkeyed by the property. This lets the codec overload simply replace the default slot. Pointers to the map's values stay valid.material_interface::add_history(the default-codec record), next to lines those PRs also change.tagis optional likerotate, soscalar,moduliand a user codec can leave it out.Tests
tests/test_history_codecs.cppreplacestest_history_kinds.cpp. It has 9 tests and compile-time checks:static_assert:history_codec_for<strain, tensor2>holds;reference_strainon a vector andscalaron anintdo not fit;default_codecisscalarfor value_type,vectorfor tensor1,strainfor tensor2 andmodulifor tensor4, andvoidforstd::string.describe()for all nine codecs in one probe.strain,stress,vector,two_point(R·F) andmoduliare rotated;reference_strain,reference_tensor,reference_vectorandscalarcome back unchanged.reference_tensorandtwo_pointkeep all 9 non-symmetric components through pack and unpack, and a vector keeps all 3 (raw slots checked).stressstores D₁₂ next tostrainandreference_strain, which store 2ε₁₂.has_rotatable_history()is false when the only history is reference and scalar.reference_strainhistory through a plaintensor2input.statev_map, with its own tag and rotation.inthistory (no codec for adoublebuffer) throws thestatev_mapfatal_error, which names it and mentions the codec.One test per ported material, through
statev_map:J2Kinematic.BackStressIsStoredAsAStress: the entry is[stress], and slot 4 holds β₁₂.IncrementalElasticity.StressIsStoredAsAStress: the entry is[stress], and slot 4 holds σ₁₂.ExternalDeformationGradientSource.IsStoredAsATwoPointTensor: 9 slots,[full] [two-point], and a sheared F round-trips.Each failed before its port. The first two showed
[engineering shear]; the third showed0..5 … [engineering shear]instead of 9 slots.All existing tests pass unchanged:
test_statev_map.cpp, the UMAT, CalculiX and evaluator tests, and #91'sStressLikeHistoryKeepsItsShearComponentsForTheHostafter the one-line migration.Gate
GCC 14 gate at -O1, including the ASan/UBSan test targets: BUILD=0, 1510/1510 tests passed, 0 warnings (a6c4764).