Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions src/targets/moose_material.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,21 @@ auto emit_source(ConstitutiveModel const &model, std::string const &app_name,
}
os << "\n";

// issue #10: the tmech::adaptor boundary assumes RankTwoTensor /
// RankFourTensor store contiguous row-major Real with no members beyond
// the data — an assumption about MOOSE internals that could silently
// break on a MOOSE upgrade. Emit it as a static_assert into the .C so a
// layout change becomes a compile error in the consumer's build, not a
// silent miscompute at the quadrature points.
os << "// Layout preconditions for the tmech::adaptor boundary — see\n";
os << "// numsim-codegen issue #10.\n";
os << "static_assert(sizeof(RankTwoTensor) == 9 * sizeof(Real),\n";
os << " \"numsim-codegen assumes RankTwoTensor is contiguous "
"3x3 Real storage\");\n";
os << "static_assert(sizeof(RankFourTensor) == 81 * sizeof(Real),\n";
os << " \"numsim-codegen assumes RankFourTensor is contiguous "
"3x3x3x3 Real storage\");\n\n";

// Embed the Layer 2 compute function as an anonymous-namespace inline
// function at file scope. Keeps the implementation self-contained in
// a single .C file without leaking the helper into other TUs.
Expand Down Expand Up @@ -444,6 +459,35 @@ auto emit_source(ConstitutiveModel const &model, std::string const &app_name,

auto MooseMaterialTarget::emit(ConstitutiveModel const &model) const
-> std::vector<EmittedFile> {
// issue #7: MOOSE's RankTwoTensor/RankFourTensor are ALWAYS 3D
// (LIBMESH_DIM = 3). A dim != 3 recipe would emit tmech::adaptor reads
// with dim-2 strides over 3x3 storage — a silent miscompute, not a
// compile error. Reject loudly; plane-strain/axisymmetric models must
// be authored as dim-3 recipes (embed the 2D state in a 3x3 tensor),
// matching how MOOSE's own tensor-mechanics materials treat 2D.
auto reject_non_3d = [&](std::size_t dim, std::string const &what,
std::string const &name) {
if (dim != 3) {
throw std::runtime_error(
"MooseMaterialTarget: " + what + " '" + name + "' has dim " +
std::to_string(dim) +
", but MOOSE RankTwoTensor/RankFourTensor storage is always 3D "
"(LIBMESH_DIM = 3); emitting would silently miscompute at the "
"adaptor boundary. Author the recipe with dim = 3 (embed "
"plane-strain/axisymmetric states in a 3x3 tensor).");
}
};
for (auto const &i : model.inputs()) {
if (i.kind == SymbolDecl::Kind::Tensor) {
reject_non_3d(i.dim, "tensor input", i.name);
}
}
for (auto const &o : model.outputs()) {
if (o.kind == OutputDecl::Kind::Tensor) {
reject_non_3d(o.dim, "tensor output", o.name);
}
}

// Stateful symbols would need old/new MaterialProperty pair handling
// that the MOOSE backend doesn't implement yet. Fail loudly rather
// than silently emit a regular read.
Expand Down
19 changes: 14 additions & 5 deletions tests/AlgorithmicTangentTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -187,11 +187,20 @@ TEST(AlgorithmicTangent, TangentDimFollowsStrainDimNotHardcoded3) {
}
}
EXPECT_TRUE(found);
auto const source = MooseMaterialTarget{}.emit(m).at(1).contents;
EXPECT_NE(source.find("tmech::adaptor<double, 2, 4, tmech::full<2>> "
"Jacobian_mult_ad"),
std::string::npos)
<< source;
// issue #7: the MOOSE half of this test previously asserted a dim-2
// adaptor over Jacobian_mult — but RankTwoTensor/RankFourTensor are
// unconditionally 3D (LIBMESH_DIM = 3), so a full<2> adaptor over that
// storage reads the wrong elements: the "propagated" dim made the
// miscompute deterministic, not correct. Dim propagation stays pinned
// above at the canonical_arguments level (the Layer-2 / standalone path
// genuinely supports dim 2); the MOOSE backend must now REJECT dim != 3.
try {
[[maybe_unused]] auto const discarded = MooseMaterialTarget{}.emit(m);
ADD_FAILURE() << "expected MooseMaterialTarget to reject the dim-2 recipe";
} catch (std::runtime_error const &e) {
EXPECT_NE(std::string(e.what()).find("LIBMESH_DIM"), std::string::npos)
<< e.what();
}
}

// Round-2 review (coverage): the tangent through the MOOSE backend ALONGSIDE a
Expand Down
34 changes: 34 additions & 0 deletions tests/MooseTargetTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,40 @@ TEST(MooseTarget, SpectralSourceIncludesRuntimeHeader) {
}

// A non-spectral material must NOT drag in the spectral header.
// issue #7: RankTwoTensor is always 3D; a dim-2 recipe must be rejected
// loudly instead of silently miscomputing through the adaptor boundary.
TEST(MooseTarget, RejectsNonThreeDimensionalTensors) {
ConstitutiveModel m("PlaneStrain2D");
auto mu = m.add_parameter("mu", 0.5, "Shear modulus");
auto eps = m.add_tensor_input("eps", 2, 2, roles::Strain);
m.add_output("stress", 2 * mu * eps, roles::Stress);
MooseMaterialTarget target;
try {
[[maybe_unused]] auto const discarded = target.emit(m);
ADD_FAILURE() << "expected emit() to reject the dim-2 tensor input";
} catch (std::runtime_error const &e) {
std::string const msg = e.what();
EXPECT_NE(msg.find("dim 2"), std::string::npos) << msg;
EXPECT_NE(msg.find("LIBMESH_DIM"), std::string::npos) << msg;
}
}

// issue #10: the storage-layout assumption behind the tmech::adaptor
// boundary must be a compile-time check in the consumer's build.
TEST(MooseTarget, SourceEmitsLayoutStaticAsserts) {
MooseMaterialTarget target;
auto files = target.emit(build_linear_elastic_shear());
auto const &source = files[1].contents;
EXPECT_NE(source.find("static_assert(sizeof(RankTwoTensor) == 9 * "
"sizeof(Real)"),
std::string::npos)
<< source;
EXPECT_NE(source.find("static_assert(sizeof(RankFourTensor) == 81 * "
"sizeof(Real)"),
std::string::npos)
<< source;
}

TEST(MooseTarget, NonSpectralSourceOmitsRuntimeHeader) {
MooseMaterialTarget target;
auto files = target.emit(build_linear_elastic_shear());
Expand Down
Loading