Summary
When required parameters are missing, input_parameter_controller::accept() throws an exception carrying only a count, not the names:
missing 1 required parameter(s)
The names are known at the throw site — they are collected in missing and printed to stdout — but they never reach the exception. A caller that catches the error, or runs anywhere stdout is not visible, is told that something is missing but not what.
Where
include/numsim-core/input_parameter_controller.h, in accept() (step 4):
// 4. Report all missing required parameters at once
if (!missing.empty()) {
std::println(" missing required parameters:");
for (const auto& key : missing)
std::println(" - {}", key);
throw std::invalid_argument(
"missing " + std::to_string(missing.size()) + " required parameter(s)");
}
Why it matters
Two paths in this header report the same condition, and they disagree. The single-parameter check already does the right thing:
void check(ParameterHandler &input) const final override {
if (!input.contains(this->m_para.name())) {
throw std::invalid_argument("Parameter " + this->m_para.name() +
" is missing!");
}
}
So the quality of the diagnostic depends on which validation path happens to run. The batch path in accept() is the one a document- or deck-driven flow goes through, which is exactly the case where the user did not write the C++ and has no other way to find out which key is wrong.
Concretely, in numsim-materials a JSON material model that omits one cone parameter of a Drucker-Prager material fails with:
missing 1 required parameter(s)
for a document that named eta, beta, K_bulk, G, sigma_0, hardening_source, strain_source and solver_source — eight candidates, one of them wrong, and the error narrows it to none of them. We have a test pinning this behaviour rather than the desirable one (MaterialRegistry.ADruckerPragerDocumentMissingEtaFailsLoudly), with a comment pointing here.
Printing to stdout does not cover the gap:
- a library writing diagnostics to stdout is not usable from a host that owns that stream — an Abaqus UMAT, a Python binding, a GUI;
- the print and the throw can be separated by arbitrary output from other materials being constructed;
- a caller that catches and re-throws with context (as
numsim-materials' UMAT boundary does, mapping failures to a solver return code) has nothing to add the names to.
Suggested fix
Interpolate the names that are already collected:
if (!missing.empty()) {
std::string names;
for (const auto& key : missing)
names += (names.empty() ? "" : ", ") + std::string(key);
throw std::invalid_argument(
"missing " + std::to_string(missing.size()) +
" required parameter(s): " + names);
}
Two things worth deciding at the same time:
- Whether the
std::println calls in accept() (missing parameters, and the using default: '{}' lines above them) belong in a library at all, or should move behind a caller-supplied sink.
- Whether the two messages should be unified, so
"Parameter eta is missing!" and the batch form read the same way.
Happy to send a PR for the message change if the shape above is what you want.
Summary
When required parameters are missing,
input_parameter_controller::accept()throws an exception carrying only a count, not the names:The names are known at the throw site — they are collected in
missingand printed to stdout — but they never reach the exception. A caller that catches the error, or runs anywhere stdout is not visible, is told that something is missing but not what.Where
include/numsim-core/input_parameter_controller.h, inaccept()(step 4):Why it matters
Two paths in this header report the same condition, and they disagree. The single-parameter check already does the right thing:
So the quality of the diagnostic depends on which validation path happens to run. The batch path in
accept()is the one a document- or deck-driven flow goes through, which is exactly the case where the user did not write the C++ and has no other way to find out which key is wrong.Concretely, in
numsim-materialsa JSON material model that omits one cone parameter of a Drucker-Prager material fails with:for a document that named
eta,beta,K_bulk,G,sigma_0,hardening_source,strain_sourceandsolver_source— eight candidates, one of them wrong, and the error narrows it to none of them. We have a test pinning this behaviour rather than the desirable one (MaterialRegistry.ADruckerPragerDocumentMissingEtaFailsLoudly), with a comment pointing here.Printing to stdout does not cover the gap:
numsim-materials' UMAT boundary does, mapping failures to a solver return code) has nothing to add the names to.Suggested fix
Interpolate the names that are already collected:
Two things worth deciding at the same time:
std::printlncalls inaccept()(missing parameters, and theusing default: '{}'lines above them) belong in a library at all, or should move behind a caller-supplied sink."Parameter eta is missing!"and the batch form read the same way.Happy to send a PR for the message change if the shape above is what you want.