The library produces phase IDs (0 = matrix, 1..N = inclusion indices)
but no mechanism to attach material properties per phase. Downstream
FFT/FE solvers need this association — currently the user has to
hand-edit material cards after generation, which defeats the
JSON-driven reproducibility story.
Proposed fix
A new materials JSON section + a phase_assignment block:
{
"materials": {
"matrix": { "type": "linear_elastic", "E": 3.5e9, "nu": 0.35 },
"fibre": { "type": "linear_elastic", "E": 240e9, "nu": 0.25 },
"void": { "type": "void" }
},
"phase_assignment": {
"matrix": "matrix",
"all_inclusions": "fibre"
},
...
"postprocess": [
{ "type": "voxel", "nx": 64, "ny": 64, "nz": 64,
"output_path": "rve.vox",
"emit_material_grid": true,
"material_grid_path": "rve.mat" }
]
}
Voxel writer extension: when emit_material_grid is true, emit a
parallel grid where each cell carries the material ID resolved from
the phase ID via phase_assignment.
Material types (initial set)
linear_elastic — E, nu (or K, G); converts to stiffness
tensor on demand.
orthotropic — 9 stiffness components (or compliance form).
plastic_J2 — yield stress + hardening modulus.
void — zero stiffness; used for porosity studies.
Each material is a registered type via the existing registry pattern
(new material_registry_t). Schema-driven ctor takes a
parameter_handler_t and the standard parameters() static returns
the schema with policy hints.
Phase-assignment forms
"all_inclusions": "fibre" — every shape gets the named material.
"by_shape_type": { "circle": "fibre1", "rectangle": "fibre2" } —
per-shape-type assignment.
"by_index": { "0": "matrix", "1-": "fibre" } — explicit
per-phase-id mapping with range syntax.
Output extensions
voxel_writer / vtk_legacy_writer gain optional
emit_material_grid + material_grid_path fields. Phase grid
unchanged; an additional file/dataset carries material IDs.
gmsh_geo_writer emits Physical Surface ("matrix") = { ... };
directives so gmsh's mesh carries material tags directly.
- New post-process
damask_material_config — emits a material.config
file in DAMASK's format for direct FFT-homogenization input.
Acceptance
Implementation sketch
namespace rvegen {
template <typename T>
class material_base {
public:
virtual ~material_base() = default;
virtual stiffness_tensor<T> stiffness() const = 0;
virtual std::string_view kind() const = 0;
};
template <typename T>
class linear_elastic final : public material_base<T> {
public:
T E, nu;
// ... ctors, parameters(), stiffness() implementation
};
template <typename T = double>
using material_registry_t = numsim_core::object_registry<
material_base<T>, parameter_controller_t, parameter_handler_t const>;
}
phase_assignment is a small struct holding unordered_map<size_t, shared_ptr<material_base<T>>>. The CLI builds it from the JSON
section after constructing materials. Each post-process that wants
material info gets a const reference.
Out of scope
- Plastic / damage / nonlinear material models — deferred to
follow-ups; the registry pattern accommodates them when needed.
- Per-orientation material variants (for FOD or polycrystal cases) —
belongs with #112 / #114.
- Material parameter calibration tooling.
Branch with full draft + design notes: feature/per-phase-materials (docs/issues/113-*.md).
The library produces phase IDs (0 = matrix, 1..N = inclusion indices)
but no mechanism to attach material properties per phase. Downstream
FFT/FE solvers need this association — currently the user has to
hand-edit material cards after generation, which defeats the
JSON-driven reproducibility story.
Proposed fix
A new
materialsJSON section + aphase_assignmentblock:{ "materials": { "matrix": { "type": "linear_elastic", "E": 3.5e9, "nu": 0.35 }, "fibre": { "type": "linear_elastic", "E": 240e9, "nu": 0.25 }, "void": { "type": "void" } }, "phase_assignment": { "matrix": "matrix", "all_inclusions": "fibre" }, ... "postprocess": [ { "type": "voxel", "nx": 64, "ny": 64, "nz": 64, "output_path": "rve.vox", "emit_material_grid": true, "material_grid_path": "rve.mat" } ] }Voxel writer extension: when
emit_material_gridis true, emit aparallel grid where each cell carries the material ID resolved from
the phase ID via
phase_assignment.Material types (initial set)
linear_elastic—E,nu(orK,G); converts to stiffnesstensor on demand.
orthotropic— 9 stiffness components (or compliance form).plastic_J2— yield stress + hardening modulus.void— zero stiffness; used for porosity studies.Each material is a registered type via the existing registry pattern
(new
material_registry_t). Schema-driven ctor takes aparameter_handler_tand the standardparameters()static returnsthe schema with policy hints.
Phase-assignment forms
"all_inclusions": "fibre"— every shape gets the named material."by_shape_type": { "circle": "fibre1", "rectangle": "fibre2" }—per-shape-type assignment.
"by_index": { "0": "matrix", "1-": "fibre" }— explicitper-phase-id mapping with range syntax.
Output extensions
voxel_writer/vtk_legacy_writergain optionalemit_material_grid+material_grid_pathfields. Phase gridunchanged; an additional file/dataset carries material IDs.
gmsh_geo_writeremitsPhysical Surface ("matrix") = { ... };directives so gmsh's mesh carries material tags directly.
damask_material_config— emits amaterial.configfile in DAMASK's format for direct FFT-homogenization input.
Acceptance
material_registry_t+material_base<T>+ at leastlinear_elasticandvoidtypes registered.phase_assignmentresolution works for the three forms above.Physical Surfaceper material.damask_material_configpost-process works on thecircles_in_unit_box.jsonexample.Implementation sketch
phase_assignmentis a small struct holdingunordered_map<size_t, shared_ptr<material_base<T>>>. The CLI builds it from the JSONsection after constructing materials. Each post-process that wants
material info gets a const reference.
Out of scope
follow-ups; the registry pattern accommodates them when needed.
belongs with #112 / #114.
Branch with full draft + design notes:
feature/per-phase-materials(docs/issues/113-*.md).