Files
FESADev/src/fesa/model/domain.cpp
T

798 lines
32 KiB
C++

#include "fesa/model/domain.h"
#include <algorithm>
#include <array>
#include <cmath>
#include <cstddef>
#include <functional>
#include <map>
#include <memory>
#include <set>
#include <string>
#include <utility>
#include "../elements/thermal_shell_geometry.h"
namespace fesa {
namespace {
Status InvalidThermalDefinition(const SourceLocation& location,
const std::string& identity,
const std::string& message) {
return Status::Failure(
FailureCategory::kModel,
{{Severity::kError, "invalid-thermal-definition", location,
"THERMAL", identity, message}});
}
bool IsThermalDof(const int dof) { return dof == 11 || dof == 15; }
Status ValidateThermalTargets(
const std::vector<ThermalBoundaryConditionDefinition>& boundaries,
const std::vector<bool>& active_nodes, const char* identity) {
std::map<std::pair<EntityIndex, int>, double> values;
for (const auto& boundary : boundaries) {
if (boundary.node_index >= active_nodes.size() ||
!active_nodes[boundary.node_index] || !IsThermalDof(boundary.dof) ||
!std::isfinite(boundary.value)) {
return InvalidThermalDefinition(boundary.location, identity,
"Thermal target is invalid or inactive.");
}
const auto inserted = values.emplace(
std::make_pair(boundary.node_index, boundary.dof), boundary.value);
if (!inserted.second && inserted.first->second != boundary.value) {
return InvalidThermalDefinition(
boundary.location, identity,
"Conflicting thermal boundary values target one node and DOF.");
}
}
return Status::Ok();
}
Status ValidateThermalDefinition(const ModelDefinition& definition) {
const bool has_structural = !definition.elements.empty() ||
!definition.shell_elements.empty() ||
!definition.solid_elements.empty();
const bool has_thermal_data =
!definition.thermal_materials.empty() ||
!definition.thermal_sections.empty() ||
definition.thermal_step.has_value() ||
!definition.thermal_initial_conditions.empty() ||
!definition.thermal_model_boundaries.empty();
if (has_structural && !definition.thermal_elements.empty()) {
return InvalidThermalDefinition({}, "DS4",
"Structural and DS4 elements cannot mix.");
}
if (definition.thermal_elements.empty()) {
if (has_thermal_data) {
return InvalidThermalDefinition({}, "DS4",
"Thermal definitions require a DS4 element.");
}
return Status::Ok();
}
if (!definition.thermal_step) {
return InvalidThermalDefinition({}, "DS4",
"A DS4 model requires one thermal step.");
}
std::map<std::string, bool> material_identities;
for (const auto& material : definition.thermal_materials) {
const std::string identity = material.source_id.instance_name + ":" +
material.source_id.source_label_text;
if (!material_identities.emplace(identity, true).second) {
return InvalidThermalDefinition(material.location, identity,
"Thermal material identity is duplicated.");
}
const auto candidate = IsotropicThermalMaterial::Create(
material.source_id, material.name, material.conductivity,
material.density, material.specific_heat, material.location);
if (!candidate.HasValue()) return candidate.GetStatus();
}
std::map<std::string, bool> section_identities;
for (const auto& section : definition.thermal_sections) {
const std::string identity = section.source_id.instance_name + ":" +
section.source_id.source_label_text;
if (section.source_id.source_label_text.empty() || section.name.empty() ||
!std::isfinite(section.thickness) || !(section.thickness > 0.0) ||
section.material_index >= definition.thermal_materials.size() ||
!section_identities.emplace(identity, true).second) {
return InvalidThermalDefinition(section.location, section.name,
"Thermal section is invalid.");
}
}
std::vector<bool> active_nodes(definition.nodes.size(), false);
std::map<std::string, bool> identities;
for (const auto& element : definition.thermal_elements) {
const std::string identity = element.source_id.instance_name + ":" +
element.source_id.source_label_text;
if (element.source_id.source_label_text.empty() ||
!identities.emplace(identity, true).second ||
element.section_index >= definition.thermal_sections.size()) {
return InvalidThermalDefinition(element.location, identity,
"DS4 section assignment is invalid.");
}
for (const EntityIndex node : element.node_indices) {
if (node >= active_nodes.size()) {
return InvalidThermalDefinition(element.location, identity,
"DS4 node target is invalid.");
}
active_nodes[node] = true;
}
}
const Status geometry_status = ValidateThermalShellMesh(definition);
if (!geometry_status.IsOk()) return geometry_status;
std::map<EntityIndex, std::array<double, 5>> initial_values;
for (const auto& initial : definition.thermal_initial_conditions) {
if (initial.node_index >= active_nodes.size() ||
!active_nodes[initial.node_index]) {
return InvalidThermalDefinition(initial.location, "INITIAL CONDITIONS",
"Thermal initial target is inactive.");
}
for (const double value : initial.station_values) {
if (!std::isfinite(value) || value != initial.station_values[0U]) {
return InvalidThermalDefinition(
initial.location, "INITIAL CONDITIONS",
"Initial temperatures must be finite and equal at five stations.");
}
}
const auto inserted = initial_values.emplace(initial.node_index,
initial.station_values);
if (!inserted.second && inserted.first->second != initial.station_values) {
return InvalidThermalDefinition(initial.location, "INITIAL CONDITIONS",
"Conflicting initial temperatures target "
"one node.");
}
}
Status model_status = ValidateThermalTargets(
definition.thermal_model_boundaries, active_nodes, "BOUNDARY");
if (!model_status.IsOk()) return model_status;
const auto& step = *definition.thermal_step;
if (!std::isfinite(step.nominal_increment) || !(step.nominal_increment > 0.0) ||
!std::isfinite(step.time_period) || !(step.time_period > 0.0) ||
step.maximum_increments == 0U || step.nlgeom) {
return InvalidThermalDefinition(step.location, step.name,
"Thermal step controls are invalid.");
}
Status step_status =
ValidateThermalTargets(step.boundaries, active_nodes, "STEP BOUNDARY");
if (!step_status.IsOk()) return step_status;
for (const auto& load : step.loads) {
if (load.node_index >= active_nodes.size() || !active_nodes[load.node_index] ||
!IsThermalDof(load.dof) || !std::isfinite(load.power)) {
return InvalidThermalDefinition(load.location, "CFLUX",
"Thermal flux target is invalid or inactive.");
}
}
return Status::Ok();
}
/// @brief Checks Solid semantic candidates before immutable ownership starts.
Status ValidateSolidDefinition(ModelDefinition& definition) {
const auto fail = [](const SourceLocation& location,
const std::string& identity,
const std::string& message) {
return Status::Failure(FailureCategory::kModel,
{{Severity::kError, "invalid-solid-definition",
location, "SOLID", identity, message}});
};
if (definition.solid_elements.empty()) {
if (!definition.solid_sections.empty() || !definition.amplitudes.empty())
return fail({}, "C3D8", "Solid definitions require a C3D8 model.");
return Status::Ok();
}
if (!definition.elements.empty() || !definition.shell_elements.empty() ||
!definition.thermal_elements.empty() || definition.thermal_step ||
!definition.sections.empty() || !definition.shell_sections.empty() ||
!definition.element_surfaces.empty()) {
return fail({}, "C3D8",
"Solid models cannot mix element or section families.");
}
if (definition.steps.size() + (definition.frequency_step ? 1U : 0U) +
(definition.dynamic_step ? 1U : 0U) !=
1U) {
return fail({}, "STEP", "Solid models require exactly one procedure.");
}
std::vector<bool> active(definition.nodes.size(), false);
std::set<std::pair<std::string, std::int64_t>> node_ids;
for (const auto& node : definition.nodes) {
if (!node_ids
.emplace(node.source_id.instance_name, node.source_id.source_label)
.second)
return fail(node.location, node.source_id.source_label_text,
"Node identity is duplicated.");
for (const double coordinate : node.coordinates)
if (!std::isfinite(coordinate))
return fail(node.location, node.source_id.source_label_text,
"Solid coordinates must be finite.");
}
std::set<std::pair<std::string, std::int64_t>> element_ids;
std::set<EntityIndex> active_materials;
for (const auto& element : definition.solid_elements) {
if (!element_ids
.emplace(element.source_id.instance_name,
element.source_id.source_label)
.second ||
element.section_index >= definition.solid_sections.size() ||
element.material_index >= definition.materials.size()) {
return fail(
element.location, element.source_id.source_label_text,
"Solid identity, section, and material must resolve uniquely.");
}
if (definition.solid_sections[element.section_index].MaterialIndex() !=
element.material_index)
return fail(element.location, element.source_id.source_label_text,
"Solid material assignment conflicts.");
active_materials.insert(element.material_index);
std::set<EntityIndex> distinct;
for (const auto node : element.node_indices) {
if (node >= active.size() || !distinct.insert(node).second)
return fail(element.location, element.source_id.source_label_text,
"Solid connectivity requires eight distinct active nodes.");
active[node] = true;
}
}
for (std::size_t node = 0U; node < active.size(); ++node)
if (!active[node])
return fail(definition.nodes[node].location,
definition.nodes[node].source_id.source_label_text,
"Solid nodes must be connected.");
for (const auto& section : definition.solid_sections)
if (section.MaterialIndex() >= definition.materials.size() ||
section.Name().empty())
return fail(section.Location(), section.Name(),
"Solid material assignment is invalid.");
for (const auto index : active_materials) {
const auto& material = definition.materials[index];
const double young = material.YoungsModulus();
const double nu = material.PoissonsRatio();
const double mu = young / (2.0 * (1.0 + nu));
const double lambda = young * nu / ((1.0 + nu) * (1.0 - 2.0 * nu));
if (!std::isfinite(young) || !(young > 0.0) || !std::isfinite(nu) ||
!(nu > -1.0 && nu < 0.5) || !std::isfinite(mu) ||
!std::isfinite(lambda))
return fail(material.Location(), material.Name(),
"Solid isotropic constants are invalid.");
}
std::set<EntityIndex> density_materials;
for (const auto& density : definition.densities) {
if (density.material_index >= definition.materials.size() ||
!std::isfinite(density.rho) || !(density.rho > 0.0) ||
!density_materials.insert(density.material_index).second)
return fail(density.location, "DENSITY",
"Solid density must be finite positive and unique.");
}
if (definition.dynamic_step || definition.frequency_step)
for (const auto material : active_materials)
if (density_materials.count(material) == 0U)
return fail({}, "DENSITY",
"Solid frequency and dynamic materials require density.");
std::set<std::string> amplitude_names;
for (const auto& amplitude : definition.amplitudes) {
if (!definition.dynamic_step || amplitude.name_raw.empty() ||
amplitude.name_canonical.empty() ||
!amplitude_names.insert(amplitude.name_canonical).second ||
amplitude.points.empty())
return fail(amplitude.location, amplitude.name_raw,
"Solid amplitude identity is invalid.");
double previous = -1.0;
for (const auto& point : amplitude.points) {
if (!std::isfinite(point[0]) || !std::isfinite(point[1]) ||
point[0] < 0.0 || !(point[0] > previous))
return fail(amplitude.location, amplitude.name_raw,
"Solid amplitude pairs are invalid.");
previous = point[0];
}
}
const auto validate_boundaries = [&fail](const auto& boundaries,
bool homogeneous) {
for (const auto& boundary : boundaries) {
if (boundary.first_dof < 1 || boundary.last_dof < boundary.first_dof ||
boundary.last_dof > 3 || !std::isfinite(boundary.value) ||
(homogeneous && boundary.value != 0.0))
return fail(boundary.location, boundary.target,
"Solid physical boundaries require DOF1..3.");
}
return Status::Ok();
};
const auto validate_loads = [&fail, &definition](const auto& loads,
bool dynamic) {
for (const auto& load : loads) {
if (load.dof < 1 || load.dof > 3 || !std::isfinite(load.magnitude) ||
(load.amplitude_index &&
(!dynamic || *load.amplitude_index >= definition.amplitudes.size())))
return fail(load.location, load.target,
"Solid load component or amplitude is invalid.");
}
return Status::Ok();
};
if (definition.frequency_step)
return validate_boundaries(definition.frequency_step->boundaries, true);
if (definition.dynamic_step) {
const auto& dynamic = *definition.dynamic_step;
if (!std::isfinite(dynamic.initial_increment) ||
!(dynamic.initial_increment > 0.0) ||
!std::isfinite(dynamic.time_period) || !(dynamic.time_period > 0.0) ||
dynamic.increment_count == 0U ||
dynamic.increment_count > dynamic.maximum_increments ||
dynamic.initial_increment *
static_cast<double>(dynamic.increment_count) !=
dynamic.time_period ||
dynamic.alpha != -0.05 || dynamic.beta != 0.275625 ||
dynamic.gamma != 0.55)
return fail(dynamic.data_location, dynamic.source_step_name_raw,
"Solid dynamic controls are invalid.");
const auto status = validate_boundaries(dynamic.boundaries, true);
if (!status.IsOk()) return status;
return validate_loads(dynamic.loads, true);
}
const auto& step = definition.steps[0];
if (step.direct &&
(!std::isfinite(step.initial_increment) ||
!(step.initial_increment > 0.0) || !std::isfinite(step.time_period) ||
!(step.time_period > 0.0) || step.increment_count == 0U ||
step.increment_count > step.maximum_increments ||
step.initial_increment * static_cast<double>(step.increment_count) !=
step.time_period))
return fail(step.location, step.name,
"Solid static fixed schedule is invalid.");
const auto status = validate_boundaries(step.boundaries, false);
if (!status.IsOk()) return status;
return validate_loads(step.loads, false);
}
} // namespace
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},
maximum_increment_{definition.maximum_increment},
location_{std::move(definition.location)} {
for (auto& boundary : definition.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(definition.loads.size());
loads_view_.reserve(definition.loads.size());
for (std::size_t source_order = 0U; source_order < definition.loads.size();
++source_order) {
auto& load = definition.loads[source_order];
auto owned = std::make_unique<ConcentratedNodalLoad>(
SourceTargetQuery{SourceEntityKind::kNode, "", load.target}, load.dof,
load.magnitude, source_order, std::move(load.location),
load.amplitude_index, std::move(load.amplitude_name_raw),
std::move(load.amplitude_location));
loads_view_.push_back(std::cref(*owned));
concentrated_loads_view_.Add(*owned);
loads_.push_back(std::move(owned));
}
surface_pressure_loads_ = std::move(definition.surface_pressure_loads);
direct_static_ = definition.direct;
increment_count_ = definition.increment_count;
maximum_increments_ = definition.maximum_increments;
}
StepDefinition::StepDefinition(FrequencyStepDefinition definition)
: name_{definition.source_step_name_raw},
procedure_kind_{StaticProcedureKind::kLinearSmallDisplacement},
nlgeom_{false},
initial_increment_{0.0},
time_period_{0.0},
minimum_increment_{0.0},
maximum_increment_{0.0},
location_{definition.step_location},
frequency_{std::move(definition)} {
for (const auto& boundary : frequency_->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));
}
}
}
StepDefinition::StepDefinition(DynamicStepDefinition definition)
: name_{definition.source_step_name_raw},
procedure_kind_{definition.nlgeom
? StaticProcedureKind::kNonlinearFullNewton
: StaticProcedureKind::kLinearSmallDisplacement},
nlgeom_{definition.nlgeom},
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),
load.amplitude_index, std::move(load.amplitude_name_raw),
std::move(load.amplitude_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()
const noexcept {
return boundary_conditions_view_;
}
const DomainCollectionView<PrescribedDisplacementBoundaryCondition>&
StepDefinition::PrescribedDisplacements() const noexcept {
return prescribed_displacements_view_;
}
const LoadView& StepDefinition::Loads() const noexcept { return loads_view_; }
const DomainCollectionView<ConcentratedNodalLoad>&
StepDefinition::ConcentratedLoads() const noexcept {
return concentrated_loads_view_;
}
const std::vector<SurfacePressureLoadDefinition>&
StepDefinition::SurfacePressureLoads() const noexcept {
return surface_pressure_loads_;
}
StaticProcedureKind StepDefinition::ProcedureKind() const noexcept {
return procedure_kind_;
}
bool StepDefinition::IsNonlinearStatic() const noexcept {
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 {
return initial_increment_;
}
double StepDefinition::TimePeriod() const noexcept { return time_period_; }
double StepDefinition::MinimumIncrement() const noexcept {
return minimum_increment_;
}
double StepDefinition::MaximumIncrement() const noexcept {
return maximum_increment_;
}
bool StepDefinition::IsDirectStatic() const noexcept { return direct_static_; }
std::uint32_t StepDefinition::IncrementCount() const noexcept {
return increment_count_;
}
std::uint32_t StepDefinition::MaximumIncrements() const noexcept {
return maximum_increments_;
}
const SourceLocation& StepDefinition::Location() const noexcept {
return location_;
}
Result<Domain> Domain::Create(ModelDefinition definition) {
const Status solid_status = ValidateSolidDefinition(definition);
if (!solid_status.IsOk()) return Result<Domain>::Failure(solid_status);
const Status thermal_status = ValidateThermalDefinition(definition);
if (!thermal_status.IsOk()) return Result<Domain>::Failure(thermal_status);
return Result<Domain>::Success(Domain{std::move(definition)});
}
const std::vector<Node>& Domain::Nodes() const noexcept {
return definition_.nodes;
}
const DomainCollectionView<ElementDefinition>& Domain::Elements()
const noexcept {
return elements_view_;
}
const DomainCollectionView<EulerBeam3DDefinition>& Domain::BeamElements()
const noexcept {
return beam_elements_view_;
}
const DomainCollectionView<Mitc4ShellDefinition>& Domain::ShellElements()
const noexcept {
return shell_elements_view_;
}
const DomainCollectionView<Hex8Definition>& Domain::SolidElements()
const noexcept {
return solid_elements_view_;
}
const DomainCollectionView<SolidSection>& Domain::SolidSections()
const noexcept {
return solid_sections_view_;
}
bool Domain::IsSolid() const noexcept {
return !solid_elements_view_.Empty() &&
solid_elements_view_.Size() == elements_view_.Size();
}
const std::vector<TabularAmplitudeDefinition>& Domain::Amplitudes()
const noexcept {
return definition_.amplitudes;
}
const DomainCollectionView<Material>& Domain::Materials() const noexcept {
return materials_view_;
}
const DomainCollectionView<LinearElasticMaterial>&
Domain::LinearElasticMaterials() const noexcept {
return linear_materials_view_;
}
const DomainCollectionView<IsotropicThermalMaterial>& Domain::ThermalMaterials()
const noexcept {
return thermal_materials_view_;
}
const DomainCollectionView<Ds4ThermalDefinition>& Domain::ThermalElements()
const noexcept {
return thermal_elements_view_;
}
const std::vector<ThermalShellSectionDefinition>& Domain::ThermalSections()
const noexcept {
return definition_.thermal_sections;
}
const std::vector<ThermalInitialConditionDefinition>&
Domain::ThermalInitialConditions() const noexcept {
return definition_.thermal_initial_conditions;
}
const std::vector<ThermalBoundaryConditionDefinition>&
Domain::ThermalModelBoundaries() const noexcept {
return definition_.thermal_model_boundaries;
}
const ThermalStepDefinition* Domain::ThermalStep() const noexcept {
return definition_.thermal_step ? &*definition_.thermal_step : nullptr;
}
const DomainCollectionView<ElementProperty>& Domain::Properties()
const noexcept {
return properties_view_;
}
const DomainCollectionView<GeneralBeamSection>& Domain::Sections()
const noexcept {
return sections_view_;
}
const DomainCollectionView<ShellSection>& Domain::ShellSections()
const noexcept {
return shell_sections_view_;
}
const std::vector<ShellNodeInitialFrame>& Domain::ShellNodeInitialFrames()
const noexcept {
return definition_.shell_node_initial_frames;
}
const std::vector<NodeSet>& Domain::NodeSets() const noexcept {
return definition_.node_sets;
}
const std::vector<ElementSet>& Domain::ElementSets() const noexcept {
return definition_.element_sets;
}
const std::vector<ElementSurfaceDefinition>& Domain::ElementSurfaces()
const noexcept {
return definition_.element_surfaces;
}
const DomainCollectionView<StepDefinition>& Domain::Steps() const noexcept {
return steps_view_;
}
const std::vector<MaterialDensityDefinition>& Domain::Densities()
const noexcept {
return definition_.densities;
}
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;
}
const std::filesystem::path& Domain::SourcePath() const noexcept {
return definition_.source_path;
}
const std::string& Domain::SourceContentIdentity() const noexcept {
return definition_.source_content_identity;
}
Domain::Domain(ModelDefinition definition)
: definition_{std::move(definition)} {
materials_.reserve(definition_.materials.size());
for (auto& material : definition_.materials) {
auto owned = std::make_unique<LinearElasticMaterial>(std::move(material));
materials_view_.Add(*owned);
linear_materials_view_.Add(*owned);
materials_.push_back(std::move(owned));
}
definition_.materials.clear();
const std::size_t structural_material_count = materials_.size();
materials_.reserve(materials_.size() + definition_.thermal_materials.size());
for (const auto& material : definition_.thermal_materials) {
const auto candidate = IsotropicThermalMaterial::Create(
material.source_id, material.name, material.conductivity,
material.density, material.specific_heat, material.location);
auto owned = std::make_unique<IsotropicThermalMaterial>(
std::move(candidate.Value()));
materials_view_.Add(*owned);
thermal_materials_view_.Add(*owned);
materials_.push_back(std::move(owned));
}
definition_.thermal_materials.clear();
element_properties_.reserve(definition_.sections.size() +
definition_.shell_sections.size());
for (auto& section : definition_.sections) {
auto owned = std::make_unique<GeneralBeamSection>(std::move(section));
properties_view_.Add(*owned);
sections_view_.Add(*owned);
element_properties_.push_back(std::move(owned));
}
const std::size_t beam_property_count = element_properties_.size();
definition_.sections.clear();
for (auto& section : definition_.shell_sections) {
auto owned = std::make_unique<ShellSection>(std::move(section));
properties_view_.Add(*owned);
shell_sections_view_.Add(*owned);
element_properties_.push_back(std::move(owned));
}
definition_.shell_sections.clear();
const std::size_t solid_property_offset = element_properties_.size();
for (auto& section : definition_.solid_sections) {
auto owned = std::make_unique<SolidSection>(std::move(section));
properties_view_.Add(*owned);
solid_sections_view_.Add(*owned);
element_properties_.push_back(std::move(owned));
}
definition_.solid_sections.clear();
element_definitions_.reserve(definition_.elements.size() +
definition_.shell_elements.size());
for (auto& element : definition_.elements) {
element.SynchronizeNodeIndices();
auto owned = std::make_unique<EulerBeam3DDefinition>(std::move(element));
elements_view_.Add(*owned);
beam_elements_view_.Add(*owned);
element_definitions_.push_back(std::move(owned));
}
definition_.elements.clear();
for (auto& element : definition_.shell_elements) {
element.SynchronizeNodeIndices();
element.SetPropertyIndex(
static_cast<EntityIndex>(beam_property_count + element.section_index));
auto owned = std::make_unique<Mitc4ShellDefinition>(std::move(element));
elements_view_.Add(*owned);
shell_elements_view_.Add(*owned);
element_definitions_.push_back(std::move(owned));
}
definition_.shell_elements.clear();
for (auto& element : definition_.solid_elements) {
element.SynchronizeNodeIndices();
element.SetPropertyIndex(static_cast<EntityIndex>(solid_property_offset +
element.section_index));
auto owned = std::make_unique<Hex8Definition>(std::move(element));
elements_view_.Add(*owned);
solid_elements_view_.Add(*owned);
element_definitions_.push_back(std::move(owned));
}
definition_.solid_elements.clear();
for (auto& element : definition_.thermal_elements) {
const EntityIndex material_index = static_cast<EntityIndex>(
structural_material_count +
definition_.thermal_sections[element.section_index].material_index);
auto owned = std::make_unique<Ds4ThermalDefinition>(
std::move(element.source_id), element.node_indices, element.section_index,
std::move(element.location));
owned->BindMaterialIndex(material_index);
elements_view_.Add(*owned);
thermal_elements_view_.Add(*owned);
element_definitions_.push_back(std::move(owned));
}
definition_.thermal_elements.clear();
if (definition_.thermal_step) {
for (std::size_t index = 0U;
index < definition_.thermal_step->loads.size(); ++index) {
definition_.thermal_step->loads[index].source_order = index;
}
}
step_definitions_.reserve(definition_.steps.size() +
(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)));
steps_view_.Add(*owned);
step_definitions_.push_back(std::move(owned));
}
definition_.steps.clear();
if (definition_.frequency_step) {
auto owned = std::unique_ptr<StepDefinition>(
new StepDefinition(std::move(*definition_.frequency_step)));
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