feat(nonlinear-static-newton-raphson): step 1 - nonlinear-static-domain-contract
This commit is contained in:
@@ -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_;
|
||||
|
||||
@@ -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_;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user