feat: map fixed-step HHT shell dynamics input
This commit is contained in:
@@ -147,11 +147,13 @@ struct RawStep {
|
||||
std::string name_raw;
|
||||
std::string name_canonical;
|
||||
bool has_static{false};
|
||||
std::optional<DynamicStepDefinition> dynamic;
|
||||
bool perturbation_explicit{false};
|
||||
bool nlgeom_explicit{false};
|
||||
StaticProcedureKind procedure_kind{
|
||||
StaticProcedureKind::kLinearSmallDisplacement};
|
||||
bool nlgeom{false};
|
||||
std::uint32_t maximum_increments{0U};
|
||||
std::array<double, 4> static_values{};
|
||||
std::optional<FrequencyStepDefinition> frequency;
|
||||
std::vector<PrescribedDisplacementDefinition> boundaries;
|
||||
@@ -481,6 +483,25 @@ class DomainMappingContext::Impl {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ParseDynamicDouble(const std::string& text, double& value,
|
||||
const SourceLocation& location,
|
||||
const std::string& keyword) {
|
||||
if (text.empty()) {
|
||||
return InputFailure("invalid-dynamic-data", location, keyword, text,
|
||||
"Dynamic data fields 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-dynamic-data", location, keyword, text,
|
||||
"Dynamic data fields must be finite.");
|
||||
}
|
||||
value = parsed;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ParseModelDouble(const std::string& text, double& value,
|
||||
const SourceLocation& location,
|
||||
const std::string& keyword,
|
||||
@@ -1683,7 +1704,12 @@ class DomainMappingContext::Impl {
|
||||
void ParseBoundary(
|
||||
const KeywordBlock& block,
|
||||
std::vector<PrescribedDisplacementDefinition>& destination) {
|
||||
if (!ValidateParameters(block, {}) || block.data.empty()) {
|
||||
const bool dynamic_input = std::any_of(
|
||||
input_.blocks.begin(), input_.blocks.end(),
|
||||
[](const auto& candidate) { return candidate.canonical_name == "DYNAMIC"; });
|
||||
if (!ValidateParameters(block, {}, dynamic_input
|
||||
? "unsupported-dynamic-boundary"
|
||||
: "invalid-keyword-parameter") || block.data.empty()) {
|
||||
if (!failure_) {
|
||||
InputFailure("invalid-data-arity", block.location, block.canonical_name,
|
||||
"", "BOUNDARY requires one or more data rows.");
|
||||
@@ -1729,6 +1755,12 @@ class DomainMappingContext::Impl {
|
||||
}
|
||||
|
||||
void ParseCload(const KeywordBlock& block, std::vector<NodalLoad>& loads) {
|
||||
if (step_.dynamic && !block.parameters.empty()) {
|
||||
InputFailure("unsupported-dynamic-load", block.location,
|
||||
block.canonical_name, "",
|
||||
"Dynamic CLOAD does not support keyword parameters.");
|
||||
return;
|
||||
}
|
||||
if (!ValidateParameters(block, {}) || block.data.empty()) {
|
||||
if (!failure_) {
|
||||
InputFailure("invalid-data-arity", block.location, block.canonical_name,
|
||||
@@ -1755,6 +1787,12 @@ class DomainMappingContext::Impl {
|
||||
row.fields[0], "CLOAD DOF must be in 1..6.");
|
||||
return;
|
||||
}
|
||||
if (step_.dynamic && dof > 3) {
|
||||
InputFailure("unsupported-dynamic-load", row.location,
|
||||
block.canonical_name, row.fields[1],
|
||||
"Dynamic CLOAD supports global translational DOF 1 through 3 only.");
|
||||
return;
|
||||
}
|
||||
loads.push_back(
|
||||
{row.fields[0], static_cast<int>(dof), magnitude, row.location});
|
||||
}
|
||||
@@ -1803,7 +1841,7 @@ class DomainMappingContext::Impl {
|
||||
|
||||
void ParseStepStart(const KeywordBlock& block) {
|
||||
if (step_seen_) {
|
||||
if (step_.frequency) {
|
||||
if (step_.frequency || step_.dynamic) {
|
||||
InputFailure(
|
||||
"unsupported-analysis-procedure", block.location,
|
||||
block.canonical_name, "",
|
||||
@@ -1832,7 +1870,7 @@ class DomainMappingContext::Impl {
|
||||
"material definitions.");
|
||||
return;
|
||||
}
|
||||
if (!ValidateParameters(block, {"NAME", "NLGEOM", "PERTURBATION"}) ||
|
||||
if (!ValidateParameters(block, {"NAME", "NLGEOM", "PERTURBATION", "INC"}) ||
|
||||
!RequireNoData(block)) {
|
||||
return;
|
||||
}
|
||||
@@ -1877,6 +1915,29 @@ class DomainMappingContext::Impl {
|
||||
"STEP NAME must not be empty when it is present.");
|
||||
return;
|
||||
}
|
||||
const auto* increment_limit = Parameter(block, "INC");
|
||||
std::uint32_t maximum_increments = 100U;
|
||||
if (increment_limit != nullptr) {
|
||||
if (!increment_limit->value || increment_limit->value->empty()) {
|
||||
InputFailure("invalid-dynamic-data", block.location,
|
||||
block.canonical_name, "INC",
|
||||
"INC requires a positive uint32 value.");
|
||||
return;
|
||||
}
|
||||
std::int64_t parsed_limit = 0;
|
||||
if (!ParseInteger(*increment_limit->value, parsed_limit, block.location,
|
||||
block.canonical_name, true) ||
|
||||
parsed_limit >
|
||||
static_cast<std::int64_t>(std::numeric_limits<std::uint32_t>::max())) {
|
||||
if (!failure_) {
|
||||
InputFailure("invalid-dynamic-data", block.location,
|
||||
block.canonical_name, "INC",
|
||||
"INC requires a positive uint32 value.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
maximum_increments = static_cast<std::uint32_t>(parsed_limit);
|
||||
}
|
||||
step_seen_ = true;
|
||||
in_step_ = true;
|
||||
step_ = {};
|
||||
@@ -1887,6 +1948,7 @@ class DomainMappingContext::Impl {
|
||||
step_.nlgeom = nlgeom;
|
||||
step_.nlgeom_explicit = nlgeom_parameter != nullptr;
|
||||
step_.perturbation_explicit = perturbation != nullptr;
|
||||
step_.maximum_increments = maximum_increments;
|
||||
step_load_seen_ = false;
|
||||
step_no_op_seen_ = false;
|
||||
}
|
||||
@@ -1897,7 +1959,7 @@ class DomainMappingContext::Impl {
|
||||
if (!ValidateParameters(block, {}) || !RequireNoData(block)) {
|
||||
return;
|
||||
}
|
||||
if (!step_.has_static && !step_.frequency) {
|
||||
if (!step_.has_static && !step_.frequency && !step_.dynamic) {
|
||||
InvalidKeywordLocation(
|
||||
block, "The sole step requires one supported procedure keyword.");
|
||||
return;
|
||||
@@ -1930,9 +1992,19 @@ class DomainMappingContext::Impl {
|
||||
return;
|
||||
}
|
||||
ParseFrequency(block);
|
||||
} else if (block.canonical_name == "DYNAMIC") {
|
||||
active_output_ = false;
|
||||
if (step_.has_static || step_.frequency || step_.dynamic ||
|
||||
!step_.boundaries.empty() || !step_.loads.empty() ||
|
||||
!step_.surface_pressure_loads.empty() || step_no_op_seen_) {
|
||||
InvalidKeywordLocation(
|
||||
block, "DYNAMIC must be the first procedure in the sole step.");
|
||||
return;
|
||||
}
|
||||
ParseDynamic(block);
|
||||
} else if (block.canonical_name == "BOUNDARY") {
|
||||
active_output_ = false;
|
||||
if ((!step_.has_static && !step_.frequency) || step_load_seen_ ||
|
||||
if ((!step_.has_static && !step_.frequency && !step_.dynamic) || step_load_seen_ ||
|
||||
step_no_op_seen_) {
|
||||
InvalidKeywordLocation(
|
||||
block,
|
||||
@@ -1940,13 +2012,22 @@ class DomainMappingContext::Impl {
|
||||
"and no-op requests.");
|
||||
return;
|
||||
}
|
||||
if (step_.dynamic && !block.parameters.empty()) {
|
||||
InputFailure("unsupported-dynamic-boundary", block.location,
|
||||
block.canonical_name, "",
|
||||
"Dynamic BOUNDARY does not support keyword parameters.");
|
||||
return;
|
||||
}
|
||||
ParseBoundary(block, step_.boundaries);
|
||||
if (!failure_ && step_.frequency) {
|
||||
ValidateEigenBoundaries(step_.boundaries);
|
||||
}
|
||||
if (!failure_ && step_.dynamic) {
|
||||
ValidateDynamicBoundaries(step_.boundaries);
|
||||
}
|
||||
} else if (block.canonical_name == "CLOAD") {
|
||||
active_output_ = false;
|
||||
if (!step_.has_static || step_no_op_seen_) {
|
||||
if ((!step_.has_static && !step_.dynamic) || step_no_op_seen_) {
|
||||
InvalidKeywordLocation(
|
||||
block,
|
||||
"CLOAD follows STATIC and boundaries and precedes no-op requests.");
|
||||
@@ -1959,7 +2040,7 @@ class DomainMappingContext::Impl {
|
||||
step_load_seen_ = !failure_;
|
||||
} else if (block.canonical_name == "DSLOAD") {
|
||||
active_output_ = false;
|
||||
if (!step_.has_static || step_no_op_seen_) {
|
||||
if (!step_.has_static || step_.dynamic || step_no_op_seen_) {
|
||||
InvalidKeywordLocation(block,
|
||||
"DSLOAD follows STATIC and boundaries and "
|
||||
"precedes no-op requests.");
|
||||
@@ -1969,7 +2050,7 @@ class DomainMappingContext::Impl {
|
||||
step_load_seen_ = !failure_;
|
||||
} else if (block.canonical_name == "RESTART") {
|
||||
active_output_ = false;
|
||||
if (!step_.has_static && !step_.frequency) {
|
||||
if (!step_.has_static && !step_.frequency && !step_.dynamic) {
|
||||
InvalidKeywordLocation(
|
||||
block, "Step no-op requests follow STATIC, boundaries, and loads.");
|
||||
return;
|
||||
@@ -1984,7 +2065,7 @@ class DomainMappingContext::Impl {
|
||||
if (!failure_) {
|
||||
InputFailure("unsupported-keyword", block.location,
|
||||
block.canonical_name, "",
|
||||
"Frequency RESTART accepts only WRITE, FREQUENCY=0.");
|
||||
"Dynamic and frequency RESTART accept only WRITE, FREQUENCY=0.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -1993,7 +2074,7 @@ class DomainMappingContext::Impl {
|
||||
AddIgnoredWarning(block);
|
||||
}
|
||||
} else if (block.canonical_name == "OUTPUT") {
|
||||
if (!step_.has_static && !step_.frequency) {
|
||||
if (!step_.has_static && !step_.frequency && !step_.dynamic) {
|
||||
InvalidKeywordLocation(
|
||||
block, "Step no-op requests follow STATIC, boundaries, and loads.");
|
||||
return;
|
||||
@@ -2160,6 +2241,86 @@ class DomainMappingContext::Impl {
|
||||
}
|
||||
}
|
||||
|
||||
void ValidateDynamicBoundaries(
|
||||
const std::vector<PrescribedDisplacementDefinition>& boundaries) {
|
||||
for (const auto& boundary : boundaries) {
|
||||
if (boundary.value != 0.0) {
|
||||
InputFailure("unsupported-dynamic-boundary", boundary.location,
|
||||
"BOUNDARY", boundary.target,
|
||||
"Dynamic boundaries must be homogeneous and constant.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ParseDynamic(const KeywordBlock& block) {
|
||||
if (step_.perturbation_explicit || !step_.nlgeom ||
|
||||
model_element_family_ != ElementFamily::kShell ||
|
||||
!ValidateParameters(block, {"DIRECT"}, "unsupported-dynamic-parameter")) {
|
||||
if (!failure_) {
|
||||
InputFailure("unsupported-analysis-procedure", block.location,
|
||||
block.canonical_name, "",
|
||||
"DYNAMIC requires the approved nonlinear shell configuration.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
const auto* direct = Parameter(block, "DIRECT");
|
||||
if (direct == nullptr || direct->value || block.data.size() != 1U ||
|
||||
block.data[0].fields.size() < 2U ||
|
||||
std::any_of(block.data[0].fields.begin() + 2,
|
||||
block.data[0].fields.end(),
|
||||
[](const auto& field) { return !field.empty(); })) {
|
||||
InputFailure("invalid-dynamic-data", block.location, block.canonical_name,
|
||||
"", "DYNAMIC,DIRECT requires one row with increment and time period.");
|
||||
return;
|
||||
}
|
||||
DynamicStepDefinition dynamic{step_.name_raw,
|
||||
step_.name_canonical,
|
||||
step_.location,
|
||||
block.location,
|
||||
block.data[0].location,
|
||||
0.0,
|
||||
0.0,
|
||||
0U,
|
||||
step_.maximum_increments,
|
||||
-0.05,
|
||||
0.275625,
|
||||
0.55,
|
||||
{},
|
||||
{}};
|
||||
if (!ParseDynamicDouble(block.data[0].fields[0], dynamic.initial_increment,
|
||||
block.data[0].location, block.canonical_name) ||
|
||||
!ParseDynamicDouble(block.data[0].fields[1], dynamic.time_period,
|
||||
block.data[0].location, block.canonical_name) ||
|
||||
!(dynamic.initial_increment > 0.0) || !(dynamic.time_period > 0.0)) {
|
||||
if (!failure_) {
|
||||
InputFailure("invalid-dynamic-data", block.data[0].location,
|
||||
block.canonical_name, "",
|
||||
"Dynamic increment and time period must be finite and positive.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
const double count = dynamic.time_period / dynamic.initial_increment;
|
||||
const double rounded_count = std::round(count);
|
||||
if (!std::isfinite(count) || rounded_count < 1.0 ||
|
||||
rounded_count >
|
||||
static_cast<double>(std::numeric_limits<std::uint32_t>::max()) ||
|
||||
dynamic.initial_increment * rounded_count != dynamic.time_period) {
|
||||
InputFailure("invalid-dynamic-schedule", block.data[0].location,
|
||||
block.canonical_name, "",
|
||||
"DYNAMIC,DIRECT requires an exact positive integer number of increments.");
|
||||
return;
|
||||
}
|
||||
dynamic.increment_count = static_cast<std::uint32_t>(rounded_count);
|
||||
if (dynamic.increment_count > dynamic.maximum_increments) {
|
||||
InputFailure("invalid-dynamic-schedule", block.data[0].location,
|
||||
block.canonical_name, "",
|
||||
"DYNAMIC,DIRECT increment count cannot exceed STEP INC.");
|
||||
return;
|
||||
}
|
||||
step_.dynamic = std::move(dynamic);
|
||||
}
|
||||
|
||||
void ParseStatic(const KeywordBlock& block) {
|
||||
if (step_.perturbation_explicit || step_.frequency) {
|
||||
InputFailure("unsupported-analysis-procedure", block.location,
|
||||
@@ -2214,7 +2375,10 @@ class DomainMappingContext::Impl {
|
||||
}
|
||||
|
||||
void ParseOutputRoot(const KeywordBlock& block) {
|
||||
if (!ValidateParameters(block, {"FIELD", "HISTORY", "VARIABLE"})) {
|
||||
if (!ValidateParameters(
|
||||
block, step_.dynamic
|
||||
? std::vector<std::string_view>{"FIELD", "HISTORY", "VARIABLE", "FREQUENCY"}
|
||||
: std::vector<std::string_view>{"FIELD", "HISTORY", "VARIABLE"})) {
|
||||
return;
|
||||
}
|
||||
const auto* field = Parameter(block, "FIELD");
|
||||
@@ -2237,7 +2401,22 @@ class DomainMappingContext::Impl {
|
||||
"Nonlinear OUTPUT requires VARIABLE=PRESELECT.");
|
||||
return;
|
||||
}
|
||||
if (step_.frequency &&
|
||||
if (step_.dynamic) {
|
||||
const auto* frequency = Parameter(block, "FREQUENCY");
|
||||
if (!ValidateParameters(block, {"FIELD", "HISTORY", "VARIABLE", "FREQUENCY"}) ||
|
||||
variable == nullptr || !variable->value ||
|
||||
!AsciiCaseInsensitiveEquals(*variable->value, "PRESELECT") ||
|
||||
(frequency != nullptr &&
|
||||
(!frequency->value || *frequency->value != "1")) ||
|
||||
!RequireNoData(block)) {
|
||||
if (!failure_) {
|
||||
InputFailure("unsupported-keyword", block.location,
|
||||
block.canonical_name, "",
|
||||
"Dynamic OUTPUT accepts only VARIABLE=PRESELECT, FREQUENCY=1.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
} else if (step_.frequency &&
|
||||
((variable != nullptr &&
|
||||
(!variable->value ||
|
||||
!AsciiCaseInsensitiveEquals(*variable->value, "PRESELECT"))) ||
|
||||
@@ -2288,7 +2467,9 @@ class DomainMappingContext::Impl {
|
||||
void AddIgnoredWarning(const KeywordBlock& block) {
|
||||
// One warning per allowlisted keyword keeps no-op provenance stable;
|
||||
// subordinate variable rows remain attached to that keyword record.
|
||||
definition_.warnings.push_back({Severity::kWarning, "ignored-input-keyword",
|
||||
definition_.warnings.push_back({Severity::kWarning,
|
||||
step_.dynamic ? "ignored-output-request"
|
||||
: "ignored-input-keyword",
|
||||
block.location, block.canonical_name, "",
|
||||
"The allowlisted Abaqus keyword is "
|
||||
"ignored without semantic effect."});
|
||||
@@ -2359,7 +2540,7 @@ class DomainMappingContext::Impl {
|
||||
void ValidateCardinality() {
|
||||
if (parts_.empty() || !assembly_seen_ || instances_.empty() ||
|
||||
materials_.empty() || !step_seen_ ||
|
||||
(!step_.has_static && !step_.frequency)) {
|
||||
(!step_.has_static && !step_.frequency && !step_.dynamic)) {
|
||||
const auto location = input_.blocks.empty()
|
||||
? SourceLocation{input_.source_path, 0U}
|
||||
: input_.blocks.back().location;
|
||||
@@ -2410,7 +2591,7 @@ class DomainMappingContext::Impl {
|
||||
}
|
||||
|
||||
void FinalizeDensities() {
|
||||
if (!step_.frequency) {
|
||||
if (!step_.frequency && !step_.dynamic) {
|
||||
if (std::any_of(materials_.begin(), materials_.end(),
|
||||
[](const RawMaterial& material) {
|
||||
return material.density.has_value();
|
||||
@@ -2420,20 +2601,33 @@ class DomainMappingContext::Impl {
|
||||
}
|
||||
return;
|
||||
}
|
||||
ValidateFrequencyPreprint();
|
||||
if (failure_) {
|
||||
return;
|
||||
if (step_.frequency) {
|
||||
ValidateFrequencyPreprint();
|
||||
if (failure_) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (step_.dynamic) {
|
||||
ValidateDynamicPreprint();
|
||||
if (failure_) {
|
||||
return;
|
||||
}
|
||||
for (auto& warning : definition_.warnings) {
|
||||
if (warning.keyword == "PREPRINT") {
|
||||
warning.code = "ignored-output-request";
|
||||
}
|
||||
}
|
||||
}
|
||||
if (model_element_family_ != ElementFamily::kShell) {
|
||||
InputFailure("unsupported-analysis-procedure", step_.location,
|
||||
"FREQUENCY", step_.name_raw,
|
||||
"FREQUENCY requires the approved shell model family.");
|
||||
step_.frequency ? "FREQUENCY" : "DYNAMIC", step_.name_raw,
|
||||
"The procedure requires the approved shell model family.");
|
||||
return;
|
||||
}
|
||||
if (!element_surfaces_.empty()) {
|
||||
InputFailure("unsupported-keyword", element_surfaces_.front().location,
|
||||
"SURFACE", element_surfaces_.front().name,
|
||||
"Frequency models do not support element surfaces.");
|
||||
"The selected procedure does not support element surfaces.");
|
||||
return;
|
||||
}
|
||||
for (std::size_t index = 0U; index < materials_.size(); ++index) {
|
||||
@@ -2449,7 +2643,7 @@ class DomainMappingContext::Impl {
|
||||
if (active) {
|
||||
ModelFailure("missing-material-density", material.location,
|
||||
"MATERIAL", material.name,
|
||||
"Each active frequency material requires DENSITY.");
|
||||
"Each active dynamic or frequency material requires DENSITY.");
|
||||
return;
|
||||
}
|
||||
continue;
|
||||
@@ -2492,6 +2686,32 @@ class DomainMappingContext::Impl {
|
||||
}
|
||||
}
|
||||
|
||||
void ValidateDynamicPreprint() {
|
||||
for (const auto& block : input_.blocks) {
|
||||
if (block.canonical_name != "PREPRINT") {
|
||||
continue;
|
||||
}
|
||||
if (!ValidateParameters(block, {"ECHO", "MODEL", "HISTORY", "CONTACT"}) ||
|
||||
block.parameters.size() != 4U) {
|
||||
if (!failure_) {
|
||||
InputFailure("unsupported-keyword", block.location,
|
||||
block.canonical_name, "",
|
||||
"DYNAMIC requires PREPRINT with ECHO, MODEL, HISTORY, and CONTACT parameters.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
for (const auto& parameter : block.parameters) {
|
||||
if (!parameter.value ||
|
||||
!AsciiCaseInsensitiveEquals(*parameter.value, "NO")) {
|
||||
InputFailure("unsupported-keyword", block.location,
|
||||
block.canonical_name, parameter.name,
|
||||
"DYNAMIC requires PREPRINT parameters to equal NO.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FinalizePartsAndSections() {
|
||||
part_section_assignments_.resize(parts_.size());
|
||||
part_shell_section_assignments_.resize(parts_.size());
|
||||
@@ -3213,6 +3433,12 @@ class DomainMappingContext::Impl {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (step_.dynamic) {
|
||||
ValidateDynamicBoundaries(boundaries);
|
||||
if (failure_) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
const SourceTargetIndex target_index = BuildSourceTargetIndex();
|
||||
const SourceTargetResolver target_resolver{target_index};
|
||||
|
||||
@@ -3250,6 +3476,12 @@ class DomainMappingContext::Impl {
|
||||
definition_.frequency_step = std::move(step_.frequency);
|
||||
return;
|
||||
}
|
||||
if (step_.dynamic) {
|
||||
step_.dynamic->boundaries = std::move(boundaries);
|
||||
step_.dynamic->loads = step_.loads;
|
||||
definition_.dynamic_step = std::move(step_.dynamic);
|
||||
return;
|
||||
}
|
||||
std::vector<SurfacePressureLoadDefinition> surface_pressure_loads;
|
||||
surface_pressure_loads.reserve(step_.surface_pressure_loads.size());
|
||||
for (const auto& load : step_.surface_pressure_loads) {
|
||||
|
||||
@@ -69,6 +69,43 @@ StepDefinition::StepDefinition(FrequencyStepDefinition definition)
|
||||
}
|
||||
}
|
||||
|
||||
StepDefinition::StepDefinition(DynamicStepDefinition definition)
|
||||
: name_{definition.source_step_name_raw},
|
||||
procedure_kind_{StaticProcedureKind::kNonlinearFullNewton},
|
||||
nlgeom_{true},
|
||||
initial_increment_{definition.initial_increment},
|
||||
time_period_{definition.time_period},
|
||||
minimum_increment_{definition.initial_increment},
|
||||
maximum_increment_{definition.initial_increment},
|
||||
location_{definition.step_location},
|
||||
dynamic_{std::move(definition)} {
|
||||
for (const auto& boundary : dynamic_->boundaries) {
|
||||
for (int source_dof = boundary.first_dof; source_dof <= boundary.last_dof;
|
||||
++source_dof) {
|
||||
const std::size_t source_order = boundary_conditions_.size();
|
||||
auto owned = std::make_unique<PrescribedDisplacementBoundaryCondition>(
|
||||
SourceTargetQuery{SourceEntityKind::kNode, "", boundary.target},
|
||||
static_cast<DofComponent>(source_dof - 1), boundary.value,
|
||||
source_order, boundary.location);
|
||||
boundary_conditions_view_.push_back(std::cref(*owned));
|
||||
prescribed_displacements_view_.Add(*owned);
|
||||
boundary_conditions_.push_back(std::move(owned));
|
||||
}
|
||||
}
|
||||
loads_.reserve(dynamic_->loads.size());
|
||||
loads_view_.reserve(dynamic_->loads.size());
|
||||
for (std::size_t source_order = 0U; source_order < dynamic_->loads.size();
|
||||
++source_order) {
|
||||
auto& load = dynamic_->loads[source_order];
|
||||
auto owned = std::make_unique<ConcentratedNodalLoad>(
|
||||
SourceTargetQuery{SourceEntityKind::kNode, "", load.target}, load.dof,
|
||||
load.magnitude, source_order, std::move(load.location));
|
||||
loads_view_.push_back(std::cref(*owned));
|
||||
concentrated_loads_view_.Add(*owned);
|
||||
loads_.push_back(std::move(owned));
|
||||
}
|
||||
}
|
||||
|
||||
const std::string& StepDefinition::Name() const noexcept { return name_; }
|
||||
|
||||
const BoundaryConditionView& StepDefinition::BoundaryConditions()
|
||||
@@ -98,17 +135,24 @@ StaticProcedureKind StepDefinition::ProcedureKind() const noexcept {
|
||||
}
|
||||
|
||||
bool StepDefinition::IsNonlinearStatic() const noexcept {
|
||||
return procedure_kind_ == StaticProcedureKind::kNonlinearFullNewton;
|
||||
return !dynamic_ &&
|
||||
procedure_kind_ == StaticProcedureKind::kNonlinearFullNewton;
|
||||
}
|
||||
|
||||
bool StepDefinition::IsFrequency() const noexcept {
|
||||
return frequency_.has_value();
|
||||
}
|
||||
|
||||
bool StepDefinition::IsDynamic() const noexcept { return dynamic_.has_value(); }
|
||||
|
||||
const FrequencyStepDefinition* StepDefinition::Frequency() const noexcept {
|
||||
return frequency_ ? &*frequency_ : nullptr;
|
||||
}
|
||||
|
||||
const DynamicStepDefinition* StepDefinition::Dynamic() const noexcept {
|
||||
return dynamic_ ? &*dynamic_ : nullptr;
|
||||
}
|
||||
|
||||
bool StepDefinition::UsesNonlinearGeometry() const noexcept { return nlgeom_; }
|
||||
|
||||
double StepDefinition::InitialIncrement() const noexcept {
|
||||
@@ -207,6 +251,10 @@ const FrequencyStepDefinition* Domain::Frequency() const noexcept {
|
||||
return steps_view_.Size() == 1U ? steps_view_[0U].Frequency() : nullptr;
|
||||
}
|
||||
|
||||
const DynamicStepDefinition* Domain::Dynamic() const noexcept {
|
||||
return steps_view_.Size() == 1U ? steps_view_[0U].Dynamic() : nullptr;
|
||||
}
|
||||
|
||||
const std::vector<Diagnostic>& Domain::Warnings() const noexcept {
|
||||
return definition_.warnings;
|
||||
}
|
||||
@@ -270,7 +318,8 @@ Domain::Domain(ModelDefinition definition)
|
||||
definition_.shell_elements.clear();
|
||||
|
||||
step_definitions_.reserve(definition_.steps.size() +
|
||||
(definition_.frequency_step ? 1U : 0U));
|
||||
(definition_.frequency_step ? 1U : 0U) +
|
||||
(definition_.dynamic_step ? 1U : 0U));
|
||||
for (auto& step : definition_.steps) {
|
||||
auto owned =
|
||||
std::unique_ptr<StepDefinition>(new StepDefinition(std::move(step)));
|
||||
@@ -284,6 +333,12 @@ Domain::Domain(ModelDefinition definition)
|
||||
steps_view_.Add(*owned);
|
||||
step_definitions_.push_back(std::move(owned));
|
||||
}
|
||||
if (definition_.dynamic_step) {
|
||||
auto owned = std::unique_ptr<StepDefinition>(
|
||||
new StepDefinition(std::move(*definition_.dynamic_step)));
|
||||
steps_view_.Add(*owned);
|
||||
step_definitions_.push_back(std::move(owned));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace fesa
|
||||
|
||||
Reference in New Issue
Block a user