feat(nonlinear-static-newton-raphson): step 1 - nonlinear-static-domain-contract

This commit is contained in:
KOKO\Mimi
2026-08-24 13:17:17 +09:00
parent e7c6932d88
commit c00fb79b8f
28 changed files with 814 additions and 275 deletions
+112 -28
View File
@@ -107,6 +107,9 @@ struct RawMaterial {
struct RawStep {
SourceLocation location;
bool has_static{false};
StaticProcedureKind procedure_kind{
StaticProcedureKind::kLinearSmallDisplacement};
bool nlgeom{false};
std::array<double, 4> static_values{};
std::vector<PrescribedDisplacementDefinition> boundaries;
std::vector<NodalLoad> loads;
@@ -343,6 +346,27 @@ class DomainMappingContext::Impl {
return true;
}
bool ParseNonlinearStaticDouble(const std::string& text, double& value,
const SourceLocation& location,
const std::string& keyword) {
if (text.empty()) {
return InputFailure("invalid-nonlinear-static-data", location, keyword,
text,
"A nonlinear STATIC field cannot be empty.");
}
char* end = nullptr;
errno = 0;
const double parsed = std::strtod(text.c_str(), &end);
if (errno == ERANGE || end != text.c_str() + text.size() ||
!std::isfinite(parsed)) {
return InputFailure("invalid-nonlinear-static-data", location, keyword,
text,
"All four nonlinear STATIC fields must be finite.");
}
value = parsed;
return true;
}
bool ParseModelDouble(const std::string& text, double& value,
const SourceLocation& location,
const std::string& keyword,
@@ -1367,9 +1391,20 @@ class DomainMappingContext::Impl {
void ParseStepStart(const KeywordBlock& block) {
if (step_seen_) {
InputFailure("unsupported-multiple-step", block.location,
block.canonical_name, "",
"V0 accepts exactly one analysis step.");
const auto* nlgeom = Parameter(block, "NLGEOM");
const bool nonlinear_second_step =
nlgeom != nullptr && nlgeom->value &&
AsciiCaseInsensitiveEquals(*nlgeom->value, "YES");
if (step_.nlgeom || nonlinear_second_step) {
InputFailure("unsupported-analysis-procedure", block.location,
block.canonical_name, "",
"The nonlinear static subset accepts exactly one "
"procedure step.");
} else {
InputFailure("unsupported-multiple-step", block.location,
block.canonical_name, "",
"V0 accepts exactly one analysis step.");
}
return;
}
if (!assembly_seen_ || materials_.empty()) {
@@ -1382,30 +1417,37 @@ class DomainMappingContext::Impl {
!RequireNoData(block)) {
return;
}
const auto* nlgeom = Parameter(block, "NLGEOM");
if (nlgeom != nullptr) {
if (!nlgeom->value || nlgeom->value->empty()) {
StaticProcedureKind procedure_kind =
StaticProcedureKind::kLinearSmallDisplacement;
bool nlgeom = false;
const auto* nlgeom_parameter = Parameter(block, "NLGEOM");
if (nlgeom_parameter != nullptr) {
if (!nlgeom_parameter->value || nlgeom_parameter->value->empty()) {
InputFailure("invalid-keyword-parameter", block.location,
block.canonical_name, "NLGEOM",
"NLGEOM requires NO in the approved subset.");
"NLGEOM requires YES or NO in the approved subset.");
return;
}
if (!AsciiCaseInsensitiveEquals(*nlgeom->value, "NO")) {
if (model_element_family_ == ElementFamily::kShell) {
InputFailure("unsupported-nonlinear-geometry", block.location,
block.canonical_name, *nlgeom->value,
"Only absent NLGEOM or NLGEOM=NO is supported.");
} else {
ModelFailure("unsupported-nonlinear-geometry", block.location,
block.canonical_name, *nlgeom->value,
"Only absent NLGEOM or NLGEOM=NO is supported.");
if (AsciiCaseInsensitiveEquals(*nlgeom_parameter->value, "YES")) {
if (model_element_family_ != ElementFamily::kShell) {
InputFailure("unsupported-analysis-procedure", block.location,
block.canonical_name, *nlgeom_parameter->value,
"NLGEOM=YES requires an S4 or S4R shell model.");
return;
}
procedure_kind = StaticProcedureKind::kNonlinearFullNewton;
nlgeom = true;
} else if (!AsciiCaseInsensitiveEquals(*nlgeom_parameter->value, "NO")) {
InputFailure("unsupported-analysis-procedure", block.location,
block.canonical_name, *nlgeom_parameter->value,
"NLGEOM must select YES or NO.");
return;
}
}
step_seen_ = true;
in_step_ = true;
step_ = RawStep{block.location, false, {}, {}, {}};
step_ = RawStep{block.location, false, procedure_kind, nlgeom, {}, {},
{}};
step_load_seen_ = false;
step_no_op_seen_ = false;
}
@@ -1485,6 +1527,14 @@ class DomainMappingContext::Impl {
ParseOutputChild(block);
} else {
active_output_ = false;
if (step_.procedure_kind ==
StaticProcedureKind::kNonlinearFullNewton &&
!step_.has_static) {
InputFailure("unsupported-analysis-procedure", block.location,
block.canonical_name, "",
"The nonlinear step supports only the STATIC procedure.");
return;
}
RejectUnknown(block);
}
}
@@ -1493,41 +1543,75 @@ class DomainMappingContext::Impl {
if (step_.has_static || !ValidateParameters(block, {}) ||
block.data.size() != 1U || block.data[0].fields.size() != 4U) {
if (!failure_) {
InputFailure("invalid-static-data", block.location,
InputFailure(step_.nlgeom ? "invalid-nonlinear-static-data"
: "invalid-static-data",
block.location,
block.canonical_name, "",
"STATIC requires exactly one row of four values.");
}
return;
}
for (std::size_t field = 0U; field < 4U; ++field) {
if (!ParseDouble(block.data[0].fields[field], step_.static_values[field],
block.data[0].location, block.canonical_name)) {
const std::string& text = block.data[0].fields[field];
const bool parsed = step_.nlgeom
? ParseNonlinearStaticDouble(
text, step_.static_values[field],
block.data[0].location,
block.canonical_name)
: ParseDouble(text, step_.static_values[field],
block.data[0].location,
block.canonical_name);
if (!parsed) {
return;
}
if (step_.static_values[field] <= 0.0) {
InputFailure("invalid-static-data", block.data[0].location,
InputFailure(step_.nlgeom ? "invalid-nonlinear-static-data"
: "invalid-static-data",
block.data[0].location,
block.canonical_name, "",
"All four STATIC fields must be positive.");
return;
}
}
if (step_.static_values[2] > step_.static_values[3]) {
InputFailure("invalid-static-data", block.data[0].location,
InputFailure(step_.nlgeom ? "invalid-nonlinear-static-data"
: "invalid-static-data",
block.data[0].location,
block.canonical_name, "",
"STATIC minimum increment cannot exceed maximum increment.");
return;
}
if (step_.nlgeom && step_.static_values[1] != 1.0) {
InputFailure("invalid-nonlinear-static-data", block.data[0].location,
block.canonical_name, "",
"The nonlinear STATIC time period must be exactly 1.0.");
return;
}
step_.has_static = true;
}
void ParseOutputRoot(const KeywordBlock& block) {
if (!ValidateParameters(block, {"FIELD", "HISTORY", "VARIABLE"})) {
return;
}
const auto* field = Parameter(block, "FIELD");
const auto* history = Parameter(block, "HISTORY");
const auto* variable = Parameter(block, "VARIABLE");
if ((field == nullptr) == (history == nullptr) ||
(field != nullptr && field->value) ||
(history != nullptr && history->value)) {
InputFailure("unsupported-keyword", block.location, block.canonical_name,
"", "OUTPUT must select exactly FIELD or HISTORY.");
InputFailure(step_.nlgeom ? "invalid-keyword-parameter"
: "unsupported-keyword",
block.location, block.canonical_name, "",
"OUTPUT must select exactly FIELD or HISTORY.");
return;
}
if (step_.nlgeom &&
(variable == nullptr || !variable->value ||
!AsciiCaseInsensitiveEquals(*variable->value, "PRESELECT"))) {
InputFailure("invalid-keyword-parameter", block.location,
block.canonical_name, "VARIABLE",
"Nonlinear OUTPUT requires VARIABLE=PRESELECT.");
return;
}
active_output_ = true;
@@ -2176,10 +2260,10 @@ class DomainMappingContext::Impl {
// Static time-control values are provenance only: V0 creates exactly
// the canonical final frame 0 and performs no increment loop here.
definition_.steps.push_back({"Step-1", std::move(boundaries), step_.loads,
step_.static_values[0], step_.static_values[1],
step_.static_values[2], step_.static_values[3],
step_.location});
definition_.steps.push_back(
{"Step-1", std::move(boundaries), step_.loads, step_.procedure_kind,
step_.nlgeom, step_.static_values[0], step_.static_values[1],
step_.static_values[2], step_.static_values[3], step_.location});
}
const ParsedInput& input_;
+10
View File
@@ -9,6 +9,8 @@ namespace fesa {
StepDefinition::StepDefinition(StaticStepDefinition definition)
: name_{std::move(definition.name)},
procedure_kind_{definition.procedure_kind},
nlgeom_{definition.nlgeom},
initial_increment_{definition.initial_increment},
time_period_{definition.time_period},
minimum_increment_{definition.minimum_increment},
@@ -60,6 +62,14 @@ StepDefinition::ConcentratedLoads() const noexcept {
return concentrated_loads_view_;
}
StaticProcedureKind StepDefinition::ProcedureKind() const noexcept {
return procedure_kind_;
}
bool StepDefinition::IsNonlinearStatic() const noexcept {
return procedure_kind_ == StaticProcedureKind::kNonlinearFullNewton;
}
double StepDefinition::InitialIncrement() const noexcept {
return initial_increment_;
}