feat(cpp-object-oriented-modular-refactoring): step 13 - element-definition-domain

This commit is contained in:
KOKO\Mimi
2026-08-16 09:10:38 +09:00
parent cf6fc6e1d9
commit 19ba02a6a4
26 changed files with 740 additions and 242 deletions
+26 -4
View File
@@ -37,11 +37,21 @@ const std::vector<EntityIndex>& AnalysisModel::ActiveElements() const noexcept {
return active_elements_;
}
const std::vector<EntityIndex>& AnalysisModel::ActiveBeamElements()
const noexcept {
return active_beam_elements_;
}
const std::vector<EntityIndex>& AnalysisModel::ActiveMaterials()
const noexcept {
return active_materials_;
}
const std::vector<EntityIndex>& AnalysisModel::ActiveProperties()
const noexcept {
return active_properties_;
}
const std::vector<EntityIndex>& AnalysisModel::ActiveSections() const noexcept {
return active_sections_;
}
@@ -56,13 +66,20 @@ const std::vector<EntityIndex>& AnalysisModel::ActiveLoads() const noexcept {
}
AnalysisModel::AnalysisModel(const Domain& domain) : domain_{&domain} {
std::vector<bool> reachable_materials(domain.Materials().size(), false);
std::vector<bool> reachable_sections(domain.Sections().size(), false);
std::vector<bool> reachable_materials(domain.Materials().Size(), false);
std::vector<bool> reachable_properties(domain.Properties().Size(), false);
std::vector<bool> reachable_sections(domain.Sections().Size(), false);
for (std::size_t index = 0U; index < domain.Elements().size(); ++index) {
for (std::size_t index = 0U; index < domain.Elements().Size(); ++index) {
const auto& element = domain.Elements()[index];
active_elements_.push_back(static_cast<EntityIndex>(index));
reachable_materials[element.material_index] = true;
reachable_materials[element.MaterialIndex()] = true;
reachable_properties[element.PropertyIndex()] = true;
}
for (std::size_t index = 0U; index < domain.BeamElements().Size(); ++index) {
const auto& element = domain.BeamElements()[index];
active_beam_elements_.push_back(static_cast<EntityIndex>(index));
reachable_sections[element.section_index] = true;
}
@@ -73,6 +90,11 @@ AnalysisModel::AnalysisModel(const Domain& domain) : domain_{&domain} {
active_materials_.push_back(static_cast<EntityIndex>(index));
}
}
for (std::size_t index = 0U; index < reachable_properties.size(); ++index) {
if (reachable_properties[index]) {
active_properties_.push_back(static_cast<EntityIndex>(index));
}
}
for (std::size_t index = 0U; index < reachable_sections.size(); ++index) {
if (reachable_sections[index]) {
active_sections_.push_back(static_cast<EntityIndex>(index));
+1 -1
View File
@@ -140,7 +140,7 @@ Status ValidateFiniteVector(const Vector& values,
}
Status ValidateShellMoments(const Domain& domain, const Vector& full_load) {
if (domain.ShellElements().empty()) {
if (domain.ShellElements().Empty()) {
return Status::Ok();
}
+29 -27
View File
@@ -51,19 +51,19 @@ Result<SparseMatrix> SparseAssembler::AssembleStiffness(
std::to_string(dofs.FullDofCount()),
"DofManager dimensions do not match the active model nodes.");
}
if (!model.ActiveElements().empty() && !domain.ShellElements().empty()) {
if (!model.ActiveBeamElements().empty() && !domain.ShellElements().Empty()) {
return AssemblyFailure(
"unsupported-mixed-element-model", {domain.SourcePath(), 0U},
"B33:FESA-MITC4",
"Sparse assembly does not support mixed beam and shell models.");
}
if (!domain.ShellElements().empty()) {
if (domain.ShellElements().size() >
if (!domain.ShellElements().Empty()) {
if (domain.ShellElements().Size() >
(std::numeric_limits<std::size_t>::max)() / kShellContributionCount) {
return AssemblyFailure(
"invalid-assembly-dimensions", {domain.SourcePath(), 0U},
std::to_string(domain.ShellElements().size()),
std::to_string(domain.ShellElements().Size()),
"Shell contribution storage exceeds the addressable range.");
}
@@ -88,12 +88,12 @@ Result<SparseMatrix> SparseAssembler::AssembleStiffness(
std::array<std::size_t, kShellElementDofCount> scatter;
};
std::vector<ShellInput> inputs;
inputs.reserve(domain.ShellElements().size());
inputs.reserve(domain.ShellElements().Size());
for (std::size_t element_order = 0U;
element_order < domain.ShellElements().size(); ++element_order) {
element_order < domain.ShellElements().Size(); ++element_order) {
const auto& element = domain.ShellElements()[element_order];
if (element.material_index >= domain.Materials().size() ||
element.section_index >= domain.ShellSections().size()) {
if (element.material_index >= domain.LinearElasticMaterials().Size() ||
element.section_index >= domain.ShellSections().Size()) {
return AssemblyFailure(
"invalid-assembly-element", element.location,
element.source_id.source_label_text,
@@ -102,7 +102,7 @@ Result<SparseMatrix> SparseAssembler::AssembleStiffness(
ShellInput input{};
input.section = &domain.ShellSections()[element.section_index];
input.material = &domain.Materials()[element.material_index];
input.material = &domain.LinearElasticMaterials()[element.material_index];
try {
input.scatter =
dofs.ShellElementScatter(static_cast<EntityIndex>(element_order));
@@ -191,28 +191,28 @@ Result<SparseMatrix> SparseAssembler::AssembleStiffness(
dofs.GetSparsePattern());
}
if (model.ActiveElements().size() >
if (model.ActiveBeamElements().size() >
(std::numeric_limits<std::size_t>::max)() / kBeamContributionCount) {
return AssemblyFailure(
"invalid-assembly-dimensions", {domain.SourcePath(), 0U},
std::to_string(model.ActiveElements().size()),
std::to_string(model.ActiveBeamElements().size()),
"Element contribution storage exceeds the addressable range.");
}
std::vector<std::array<std::size_t, kBeamElementDofCount>> scatters;
scatters.reserve(model.ActiveElements().size());
for (const EntityIndex element_index : model.ActiveElements()) {
if (element_index >= domain.Elements().size()) {
scatters.reserve(model.ActiveBeamElements().size());
for (const EntityIndex element_index : model.ActiveBeamElements()) {
if (element_index >= domain.BeamElements().Size()) {
return AssemblyFailure("invalid-assembly-element",
{domain.SourcePath(), 0U},
std::to_string(element_index),
"Active element index is outside the Domain.");
}
const auto& element = domain.Elements()[element_index];
const auto& element = domain.BeamElements()[element_index];
if (element.node_indices[0U] >= domain.Nodes().size() ||
element.node_indices[1U] >= domain.Nodes().size() ||
element.material_index >= domain.Materials().size() ||
element.section_index >= domain.Sections().size()) {
element.material_index >= domain.LinearElasticMaterials().Size() ||
element.section_index >= domain.Sections().Size()) {
return AssemblyFailure(
"invalid-assembly-element", element.location,
element.source_id.source_label_text,
@@ -247,18 +247,20 @@ Result<SparseMatrix> SparseAssembler::AssembleStiffness(
scatters.push_back(scatter);
}
std::vector<BeamElementBuffer> local_buffers(model.ActiveElements().size());
std::vector<BeamElementBuffer> local_buffers(
model.ActiveBeamElements().size());
std::vector<std::optional<Status>> local_failures(
model.ActiveElements().size());
model.ActiveBeamElements().size());
parallel_for.Execute(
model.ActiveElements().size(), [&](const std::size_t element_order) {
const EntityIndex element_index = model.ActiveElements()[element_order];
const auto& definition = domain.Elements()[element_index];
const auto beam =
EulerBeam3D::Create(domain.Nodes()[definition.node_indices[0U]],
domain.Nodes()[definition.node_indices[1U]],
domain.Sections()[definition.section_index],
domain.Materials()[definition.material_index]);
model.ActiveBeamElements().size(), [&](const std::size_t element_order) {
const EntityIndex element_index =
model.ActiveBeamElements()[element_order];
const auto& definition = domain.BeamElements()[element_index];
const auto beam = EulerBeam3D::Create(
domain.Nodes()[definition.node_indices[0U]],
domain.Nodes()[definition.node_indices[1U]],
domain.Sections()[definition.section_index],
domain.LinearElasticMaterials()[definition.material_index]);
if (!beam.HasValue()) {
local_failures[element_order] = beam.GetStatus();
return;
+43
View File
@@ -10,8 +10,51 @@
#include <vector>
#include "fesa/math/vector3.h"
#include "fesa/model/model_types.h"
namespace fesa {
EulerBeam3DDefinition::EulerBeam3DDefinition(
SourceEntityId source_id_value,
std::array<EntityIndex, 2> node_indices_value,
const EntityIndex material_index_value,
const EntityIndex section_index_value, SourceLocation location_value)
: source_id{std::move(source_id_value)},
node_indices{std::move(node_indices_value)},
material_index{material_index_value},
section_index{section_index_value},
location{std::move(location_value)},
node_indices_view_{node_indices.begin(), node_indices.end()} {}
ElementDefinitionKind EulerBeam3DDefinition::Kind() const noexcept {
return ElementDefinitionKind::kEulerBeam3D;
}
const SourceEntityId& EulerBeam3DDefinition::SourceId() const noexcept {
return source_id;
}
std::string_view EulerBeam3DDefinition::SourceElementType() const noexcept {
return "B33";
}
const std::vector<EntityIndex>& EulerBeam3DDefinition::NodeIndices()
const noexcept {
return node_indices_view_;
}
EntityIndex EulerBeam3DDefinition::MaterialIndex() const noexcept {
return material_index;
}
EntityIndex EulerBeam3DDefinition::PropertyIndex() const noexcept {
return section_index;
}
void EulerBeam3DDefinition::SynchronizeNodeIndices() {
node_indices_view_.assign(node_indices.begin(), node_indices.end());
}
namespace {
constexpr std::size_t kElementDofCount = 12U;
+52
View File
@@ -7,7 +7,59 @@
#include <string>
#include <utility>
#include "fesa/model/model_types.h"
namespace fesa {
Mitc4ShellDefinition::Mitc4ShellDefinition(
SourceEntityId source_id_value,
const ShellSourceElementType source_type_value,
std::array<EntityIndex, 4> node_indices_value,
const EntityIndex material_index_value,
const EntityIndex section_index_value, SourceLocation location_value)
: source_id{std::move(source_id_value)},
source_type{source_type_value},
node_indices{std::move(node_indices_value)},
material_index{material_index_value},
section_index{section_index_value},
location{std::move(location_value)},
property_index_{section_index_value},
node_indices_view_{node_indices.begin(), node_indices.end()} {}
ElementDefinitionKind Mitc4ShellDefinition::Kind() const noexcept {
return ElementDefinitionKind::kMitc4Shell;
}
const SourceEntityId& Mitc4ShellDefinition::SourceId() const noexcept {
return source_id;
}
std::string_view Mitc4ShellDefinition::SourceElementType() const noexcept {
return source_type == ShellSourceElementType::kS4 ? "S4" : "S4R";
}
const std::vector<EntityIndex>& Mitc4ShellDefinition::NodeIndices()
const noexcept {
return node_indices_view_;
}
EntityIndex Mitc4ShellDefinition::MaterialIndex() const noexcept {
return material_index;
}
EntityIndex Mitc4ShellDefinition::PropertyIndex() const noexcept {
return property_index_;
}
void Mitc4ShellDefinition::SetPropertyIndex(
const EntityIndex property_index) noexcept {
property_index_ = property_index;
}
void Mitc4ShellDefinition::SynchronizeNodeIndices() {
node_indices_view_.assign(node_indices.begin(), node_indices.end());
}
namespace {
constexpr std::size_t kNodeCount = 4U;
+6 -6
View File
@@ -120,9 +120,9 @@ Result<DofManager> DofManager::Create(const AnalysisModel& model) {
}
std::vector<std::array<std::size_t, 12>> element_scatters(
domain.Elements().size());
for (const EntityIndex element_index : model.ActiveElements()) {
const auto& element = domain.Elements().at(element_index);
domain.BeamElements().Size());
for (const EntityIndex element_index : model.ActiveBeamElements()) {
const auto& element = domain.BeamElements().At(element_index);
auto& scatter = element_scatters.at(element_index);
for (std::size_t endpoint = 0U; endpoint < element.node_indices.size();
++endpoint) {
@@ -135,9 +135,9 @@ Result<DofManager> DofManager::Create(const AnalysisModel& model) {
}
std::vector<std::array<std::size_t, 24>> shell_element_scatters(
domain.ShellElements().size());
domain.ShellElements().Size());
for (std::size_t element_index = 0U;
element_index < domain.ShellElements().size(); ++element_index) {
element_index < domain.ShellElements().Size(); ++element_index) {
const auto& element = domain.ShellElements()[element_index];
auto& scatter = shell_element_scatters[element_index];
for (std::size_t node_position = 0U;
@@ -150,7 +150,7 @@ Result<DofManager> DofManager::Create(const AnalysisModel& model) {
}
}
auto pattern = BuildSparsePattern(full_count, model.ActiveElements(),
auto pattern = BuildSparsePattern(full_count, model.ActiveBeamElements(),
element_scatters, shell_element_scatters);
return Result<DofManager>::Success(
DofManager{full_count, std::move(free_equations),
+53 -45
View File
@@ -230,7 +230,7 @@ struct WriterModelData {
};
bool IsShellDomain(const Domain& domain) noexcept {
return !domain.ShellElements().empty();
return !domain.ShellElements().Empty();
}
const char* ShellSourceTypeName(const ShellSourceElementType type) {
@@ -261,7 +261,7 @@ bool ComputeLocalAxes(const Domain& domain,
const EulerBeam3DDefinition& element, AxisSet& axes) {
if (element.node_indices[0U] >= domain.Nodes().size() ||
element.node_indices[1U] >= domain.Nodes().size() ||
element.section_index >= domain.Sections().size()) {
element.section_index >= domain.Sections().Size()) {
return false;
}
const auto& first = domain.Nodes()[element.node_indices[0U]].coordinates;
@@ -300,12 +300,14 @@ bool SizeProductFits(const std::size_t left, const std::size_t right,
Status ValidateShellWriterInput(const Domain& domain,
const AnalysisState& state) {
if (!domain.Elements().empty()) {
if (!domain.BeamElements().Empty()) {
return OutputFailure(
"invalid-result-identity",
"Schema v0 does not combine B33 and FESA-MITC4 element inventories.");
}
for (const auto& material : domain.Materials()) {
for (std::size_t index = 0U; index < domain.LinearElasticMaterials().Size();
++index) {
const auto& material = domain.LinearElasticMaterials()[index];
if (material.name.empty() || !IsValidUtf8(material.name) ||
!std::isfinite(material.youngs_modulus) ||
!std::isfinite(material.poisson_ratio) ||
@@ -315,32 +317,34 @@ Status ValidateShellWriterInput(const Domain& domain,
"finite constitutive data.");
}
}
for (const auto& section : domain.ShellSections()) {
for (std::size_t index = 0U; index < domain.ShellSections().Size(); ++index) {
const auto& section = domain.ShellSections()[index];
if (section.name.empty() || !IsValidUtf8(section.name) ||
section.material_index >= domain.Materials().size() ||
section.material_index >= domain.LinearElasticMaterials().Size() ||
!std::isfinite(section.thickness) || !(section.thickness > 0.0)) {
return OutputFailure("invalid-result-identity",
"Every shell section requires stable material "
"identity and positive finite thickness.");
}
}
for (const auto& element : domain.ShellElements()) {
for (std::size_t index = 0U; index < domain.ShellElements().Size(); ++index) {
const auto& element = domain.ShellElements()[index];
if ((element.source_type != ShellSourceElementType::kS4 &&
element.source_type != ShellSourceElementType::kS4r) ||
element.source_id.source_label <= 0 ||
element.source_id.source_label_text.empty() ||
!IsValidUtf8(element.source_id.instance_name) ||
!IsValidUtf8(element.source_id.source_label_text) ||
element.material_index >= domain.Materials().size() ||
element.section_index >= domain.ShellSections().size() ||
element.node_indices.size() != kShellNodeCount ||
element.material_index >= domain.LinearElasticMaterials().Size() ||
element.section_index >= domain.ShellSections().Size() ||
domain.ShellSections()[element.section_index].material_index !=
element.material_index) {
return OutputFailure("invalid-result-identity",
"Every shell element requires stable source, "
"material, and section identity.");
}
std::array<EntityIndex, kShellNodeCount> sorted_nodes =
element.node_indices;
auto sorted_nodes = element.node_indices;
std::sort(sorted_nodes.begin(), sorted_nodes.end());
if (sorted_nodes.back() >= domain.Nodes().size() ||
std::adjacent_find(sorted_nodes.begin(), sorted_nodes.end()) !=
@@ -368,7 +372,7 @@ Status ValidateShellWriterInput(const Domain& domain,
}
std::size_t expected_rows = 0U;
if (!SizeProductFits(domain.ShellElements().size(), kShellLocationCount,
if (!SizeProductFits(domain.ShellElements().Size(), kShellLocationCount,
expected_rows) ||
state.ShellResults().size() != expected_rows) {
return OutputFailure("invalid-result-rows",
@@ -487,15 +491,16 @@ Status ValidateWriterInput(const std::filesystem::path& output_path,
}
model_data.beam_local_axes.clear();
model_data.beam_local_axes.reserve(domain.Elements().size());
for (const EulerBeam3DDefinition& element : domain.Elements()) {
model_data.beam_local_axes.reserve(domain.BeamElements().Size());
for (std::size_t index = 0U; index < domain.BeamElements().Size(); ++index) {
const EulerBeam3DDefinition& element = domain.BeamElements()[index];
AxisSet axes{};
if (element.source_id.source_label <= 0 ||
element.source_id.source_label_text.empty() ||
!IsValidUtf8(element.source_id.instance_name) ||
!IsValidUtf8(element.source_id.source_label_text) ||
element.node_indices[0U] == element.node_indices[1U] ||
element.material_index >= domain.Materials().size() ||
element.material_index >= domain.LinearElasticMaterials().Size() ||
!ComputeLocalAxes(domain, element, axes)) {
return OutputFailure("invalid-result-identity",
"Every element requires valid source, connectivity, "
@@ -506,9 +511,9 @@ Status ValidateWriterInput(const std::filesystem::path& output_path,
std::size_t endpoint_count = 0U;
std::size_t gauss_count = 0U;
if (!SizeProductFits(domain.Elements().size(), kEndpointCount,
if (!SizeProductFits(domain.BeamElements().Size(), kEndpointCount,
endpoint_count) ||
!SizeProductFits(domain.Elements().size(), kGaussPointCount,
!SizeProductFits(domain.BeamElements().Size(), kGaussPointCount,
gauss_count) ||
state.EndpointResults().size() != endpoint_count ||
state.GaussResults().size() != gauss_count) {
@@ -522,7 +527,7 @@ Status ValidateWriterInput(const std::filesystem::path& output_path,
static_cast<EntityIndex>(row_index / kEndpointCount);
const int expected_endpoint = static_cast<int>(row_index % kEndpointCount);
const EndpointResultRow& row = state.EndpointResults()[row_index];
const auto& element = domain.Elements()[expected_element];
const auto& element = domain.BeamElements()[expected_element];
const auto& expected_node =
domain.Nodes()[element.node_indices[static_cast<std::size_t>(
expected_endpoint)]];
@@ -552,9 +557,9 @@ Status ValidateWriterInput(const std::filesystem::path& output_path,
}
std::size_t stress_index = 0U;
for (std::size_t element_index = 0U; element_index < domain.Elements().size();
++element_index) {
const auto& element = domain.Elements()[element_index];
for (std::size_t element_index = 0U;
element_index < domain.BeamElements().Size(); ++element_index) {
const auto& element = domain.BeamElements()[element_index];
const auto& section_points =
domain.Sections()[element.section_index].section_points;
for (std::size_t gauss = 0U; gauss < kGaussPointCount; ++gauss) {
@@ -973,9 +978,9 @@ void WriteNodes(const hid_t file, const Domain& domain) {
void WriteBeamElements(const hid_t file, const Domain& domain,
const std::vector<AxisSet>& axes) {
std::vector<ElementWriteRow> rows;
rows.reserve(domain.Elements().size());
for (std::size_t index = 0U; index < domain.Elements().size(); ++index) {
const auto& element = domain.Elements()[index];
rows.reserve(domain.BeamElements().Size());
for (std::size_t index = 0U; index < domain.BeamElements().Size(); ++index) {
const auto& element = domain.BeamElements()[index];
ElementWriteRow row{static_cast<std::uint64_t>(index),
element.source_id.instance_name.c_str(),
element.source_id.source_label_text.c_str(),
@@ -1047,8 +1052,8 @@ void WriteBeamElements(const hid_t file, const Domain& domain,
void WriteShellElements(const hid_t file, const Domain& domain) {
std::vector<ShellElementWriteRow> rows;
rows.reserve(domain.ShellElements().size());
for (std::size_t index = 0U; index < domain.ShellElements().size(); ++index) {
rows.reserve(domain.ShellElements().Size());
for (std::size_t index = 0U; index < domain.ShellElements().Size(); ++index) {
const auto& element = domain.ShellElements()[index];
rows.push_back({static_cast<std::uint64_t>(index),
element.source_id.instance_name.c_str(),
@@ -1127,9 +1132,10 @@ void WriteShellElements(const hid_t file, const Domain& domain) {
void WriteShellMaterials(const hid_t file, const Domain& domain) {
std::vector<ShellMaterialWriteRow> rows;
rows.reserve(domain.Materials().size());
for (std::size_t index = 0U; index < domain.Materials().size(); ++index) {
const auto& material = domain.Materials()[index];
rows.reserve(domain.LinearElasticMaterials().Size());
for (std::size_t index = 0U; index < domain.LinearElasticMaterials().Size();
++index) {
const auto& material = domain.LinearElasticMaterials()[index];
rows.push_back({static_cast<std::uint64_t>(index), material.name.c_str(),
material.youngs_modulus, material.poisson_ratio});
}
@@ -1171,13 +1177,14 @@ void WriteShellMaterials(const hid_t file, const Domain& domain) {
void WriteShellSections(const hid_t file, const Domain& domain) {
std::vector<std::string> source_files;
source_files.reserve(domain.ShellSections().size());
for (const auto& section : domain.ShellSections()) {
source_files.reserve(domain.ShellSections().Size());
for (std::size_t index = 0U; index < domain.ShellSections().Size(); ++index) {
const auto& section = domain.ShellSections()[index];
source_files.push_back(NormalizedPathString(section.location.file));
}
std::vector<ShellSectionWriteRow> rows;
rows.reserve(domain.ShellSections().size());
for (std::size_t index = 0U; index < domain.ShellSections().size(); ++index) {
rows.reserve(domain.ShellSections().Size());
for (std::size_t index = 0U; index < domain.ShellSections().Size(); ++index) {
const auto& section = domain.ShellSections()[index];
rows.push_back({static_cast<std::uint64_t>(index),
source_files[index].c_str(),
@@ -1423,7 +1430,7 @@ void WriteShellResultDatasets(const hid_t file, const Domain& domain,
}
const hsize_t element_count =
static_cast<hsize_t>(domain.ShellElements().size());
static_cast<hsize_t>(domain.ShellElements().Size());
const std::string root = std::string{kStepRoot} + "/element/shell";
const std::string frame_path = root + "/local_frame";
WriteDoubleDataset(file, frame_path, {element_count, 4U, 3U, 3U},
@@ -1578,10 +1585,10 @@ void WriteResultDatasets(const hid_t file, const Domain& domain,
}
const std::vector<hsize_t> endpoint_action_dimensions = {
static_cast<hsize_t>(domain.Elements().size()), kEndpointCount,
static_cast<hsize_t>(domain.BeamElements().Size()), kEndpointCount,
kEndActionComponentCount};
const std::vector<hsize_t> generalized_dimensions = {
static_cast<hsize_t>(domain.Elements().size()), kEndpointCount,
static_cast<hsize_t>(domain.BeamElements().Size()), kEndpointCount,
kGeneralizedComponentCount};
const auto end_actions =
FlattenEndpointValues(state.EndpointResults(), false);
@@ -2052,18 +2059,19 @@ void SelfCheckFile(const std::filesystem::path& path, const Domain& domain,
if (IsShellDomain(domain)) {
RequireCompoundDataset(
file.Get(), "/model/elements",
static_cast<hsize_t>(domain.ShellElements().size()),
static_cast<hsize_t>(domain.ShellElements().Size()),
{"internal_element_id", "instance_name", "source_label",
"source_element_type", "internal_formulation", "node_internal_ids",
"shell_section_internal_id", "material_internal_id"});
auto elements = OpenDatasetForCheck(file.Get(), "/model/elements");
RequireStringAttribute(elements.Get(), "formulation", "FESA-MITC4");
RequireCompoundDataset(file.Get(), "/model/shell/materials",
static_cast<hsize_t>(domain.Materials().size()),
{"internal_material_id", "name", "E", "nu"});
RequireCompoundDataset(
file.Get(), "/model/shell/materials",
static_cast<hsize_t>(domain.LinearElasticMaterials().Size()),
{"internal_material_id", "name", "E", "nu"});
RequireCompoundDataset(
file.Get(), "/model/shell/sections",
static_cast<hsize_t>(domain.ShellSections().size()),
static_cast<hsize_t>(domain.ShellSections().Size()),
{"internal_section_id", "source_file", "source_line", "source_elset",
"material_internal_id", "thickness"});
@@ -2114,7 +2122,7 @@ void SelfCheckFile(const std::filesystem::path& path, const Domain& domain,
"BOTTOM,MIDDLE,TOP");
const hsize_t element_count =
static_cast<hsize_t>(domain.ShellElements().size());
static_cast<hsize_t>(domain.ShellElements().Size());
const std::string shell_root = std::string{kStepRoot} + "/element/shell";
RequireDoubleDataset(file.Get(), (shell_root + "/local_frame").c_str(),
{element_count, 4U, 3U, 3U}, "X,Y,Z", "1,1,1",
@@ -2181,17 +2189,17 @@ void SelfCheckFile(const std::filesystem::path& path, const Domain& domain,
}
} else {
RequireCompoundDataset(file.Get(), "/model/elements",
static_cast<hsize_t>(domain.Elements().size()),
static_cast<hsize_t>(domain.BeamElements().Size()),
{"internal_element_id", "instance_name",
"source_label", "node_internal_ids", "local_axes"});
auto elements = OpenDatasetForCheck(file.Get(), "/model/elements");
RequireStringAttribute(elements.Get(), "formulation",
"B33-3D-Euler-Bernoulli");
const std::vector<hsize_t> end_dimensions = {
static_cast<hsize_t>(domain.Elements().size()), kEndpointCount,
static_cast<hsize_t>(domain.BeamElements().Size()), kEndpointCount,
kEndActionComponentCount};
const std::vector<hsize_t> generalized_dimensions = {
static_cast<hsize_t>(domain.Elements().size()), kGaussPointCount,
static_cast<hsize_t>(domain.BeamElements().Size()), kGaussPointCount,
kGeneralizedComponentCount};
RequireDoubleDataset(
file.Get(), "/steps/Step-1/frames/0/element/end_force_local",
+81 -13
View File
@@ -1,5 +1,7 @@
#include "fesa/model/domain.h"
#include <cstddef>
#include <memory>
#include <utility>
namespace fesa {
@@ -12,25 +14,43 @@ const std::vector<Node>& Domain::Nodes() const noexcept {
return definition_.nodes;
}
const std::vector<EulerBeam3DDefinition>& Domain::Elements() const noexcept {
return definition_.elements;
}
const std::vector<Mitc4ShellDefinition>& Domain::ShellElements()
const DomainCollectionView<ElementDefinition>& Domain::Elements()
const noexcept {
return definition_.shell_elements;
return elements_view_;
}
const std::vector<LinearElasticMaterial>& Domain::Materials() const noexcept {
return definition_.materials;
const DomainCollectionView<EulerBeam3DDefinition>& Domain::BeamElements()
const noexcept {
return beam_elements_view_;
}
const std::vector<GeneralBeamSection>& Domain::Sections() const noexcept {
return definition_.sections;
const DomainCollectionView<Mitc4ShellDefinition>& Domain::ShellElements()
const noexcept {
return shell_elements_view_;
}
const std::vector<ShellSection>& Domain::ShellSections() const noexcept {
return definition_.shell_sections;
const DomainCollectionView<Material>& Domain::Materials() const noexcept {
return materials_view_;
}
const DomainCollectionView<LinearElasticMaterial>&
Domain::LinearElasticMaterials() const noexcept {
return linear_materials_view_;
}
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()
@@ -63,6 +83,54 @@ const std::string& Domain::SourceContentIdentity() const noexcept {
}
Domain::Domain(ModelDefinition definition)
: definition_{std::move(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();
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();
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();
}
} // namespace fesa
+4 -14
View File
@@ -96,27 +96,17 @@ SourceTargetIndex SourceTargetIndex::FromDomain(const Domain& domain) {
}
}
for (std::size_t index = 0U; index < domain.Elements().size(); ++index) {
for (std::size_t index = 0U; index < domain.Elements().Size(); ++index) {
const auto& element = domain.Elements()[index];
entries.push_back({SourceEntityKind::kElement,
element.source_id.instance_name, "", element.source_id,
static_cast<EntityIndex>(index), declaration_order++});
}
for (std::size_t index = 0U; index < domain.ShellElements().size(); ++index) {
const auto& element = domain.ShellElements()[index];
entries.push_back({SourceEntityKind::kElement,
element.source_id.instance_name, "", element.source_id,
element.SourceId().instance_name, "", element.SourceId(),
static_cast<EntityIndex>(index), declaration_order++});
}
for (const auto& set : domain.ElementSets()) {
for (const EntityIndex element_index : set.element_indices) {
SourceEntityId source_id{set.instance_name.value_or(""), 0, ""};
if (domain.ShellElements().empty() &&
element_index < domain.Elements().size()) {
source_id = domain.Elements()[element_index].source_id;
} else if (domain.Elements().empty() &&
element_index < domain.ShellElements().size()) {
source_id = domain.ShellElements()[element_index].source_id;
if (element_index < domain.Elements().Size()) {
source_id = domain.Elements()[element_index].SourceId();
}
entries.push_back({SourceEntityKind::kElement,
set.instance_name.value_or(source_id.instance_name),
+34 -33
View File
@@ -216,8 +216,8 @@ Status ValidateRecoveryInputs(const AnalysisModel& model,
EntityIndex previous_element = 0U;
bool first_element = true;
for (const EntityIndex element : model.ActiveElements()) {
if (element >= domain.Elements().size() ||
for (const EntityIndex element : model.ActiveBeamElements()) {
if (element >= domain.BeamElements().Size() ||
(!first_element && element <= previous_element)) {
return RecoveryFailure(
"invalid-recovery-entity", {domain.SourcePath(), 0U},
@@ -226,11 +226,11 @@ Status ValidateRecoveryInputs(const AnalysisModel& model,
}
first_element = false;
previous_element = element;
const auto& definition = domain.Elements()[element];
const auto& definition = domain.BeamElements()[element];
if (definition.node_indices[0U] >= domain.Nodes().size() ||
definition.node_indices[1U] >= domain.Nodes().size() ||
definition.material_index >= domain.Materials().size() ||
definition.section_index >= domain.Sections().size()) {
definition.material_index >= domain.LinearElasticMaterials().Size() ||
definition.section_index >= domain.Sections().Size()) {
return RecoveryFailure(
"invalid-recovery-entity", definition.location,
definition.source_id.source_label_text,
@@ -263,13 +263,13 @@ Status ValidateRecoveryInputs(const AnalysisModel& model,
}
}
if (!model.ActiveElements().empty() && !domain.ShellElements().empty()) {
if (!model.ActiveBeamElements().empty() && !domain.ShellElements().Empty()) {
return RecoveryFailure(
"unsupported-mixed-element-model", {domain.SourcePath(), 0U},
"B33:FESA-MITC4",
"Result recovery does not support mixed beam and shell models.");
}
if (domain.ShellElements().size() >
if (domain.ShellElements().Size() >
static_cast<std::size_t>((std::numeric_limits<EntityIndex>::max)())) {
return RecoveryFailure("invalid-recovery-dimensions",
{domain.SourcePath(), 0U},
@@ -278,10 +278,10 @@ Status ValidateRecoveryInputs(const AnalysisModel& model,
"stable element identities.");
}
for (std::size_t element_order = 0U;
element_order < domain.ShellElements().size(); ++element_order) {
element_order < domain.ShellElements().Size(); ++element_order) {
const auto& definition = domain.ShellElements()[element_order];
if (definition.material_index >= domain.Materials().size() ||
definition.section_index >= domain.ShellSections().size()) {
if (definition.material_index >= domain.LinearElasticMaterials().Size() ||
definition.section_index >= domain.ShellSections().Size()) {
return RecoveryFailure("invalid-recovery-entity", definition.location,
definition.source_id.source_label_text,
"Active shell material and section references "
@@ -586,16 +586,16 @@ Status ResultRecovery::Recover(const AnalysisModel& model,
std::vector<EndpointResultRow> endpoint_rows;
std::vector<GaussResultRow> gauss_rows;
std::vector<StressS11Row> stress_rows;
endpoint_rows.reserve(model.ActiveElements().size() * 2U);
gauss_rows.reserve(model.ActiveElements().size() * 2U);
endpoint_rows.reserve(model.ActiveBeamElements().size() * 2U);
gauss_rows.reserve(model.ActiveBeamElements().size() * 2U);
const Domain& domain = model.GetDomain();
for (const EntityIndex element_index : model.ActiveElements()) {
const auto& definition = domain.Elements()[element_index];
auto beam =
EulerBeam3D::Create(domain.Nodes()[definition.node_indices[0U]],
domain.Nodes()[definition.node_indices[1U]],
domain.Sections()[definition.section_index],
domain.Materials()[definition.material_index]);
for (const EntityIndex element_index : model.ActiveBeamElements()) {
const auto& definition = domain.BeamElements()[element_index];
auto beam = EulerBeam3D::Create(
domain.Nodes()[definition.node_indices[0U]],
domain.Nodes()[definition.node_indices[1U]],
domain.Sections()[definition.section_index],
domain.LinearElasticMaterials()[definition.material_index]);
if (!beam.HasValue()) {
return beam.GetStatus();
}
@@ -649,8 +649,8 @@ Status ResultRecovery::Recover(const AnalysisModel& model,
ShellStateCandidate shell_candidate{};
std::vector<EntityIndex> expected_shell_elements;
if (!domain.ShellElements().empty()) {
if (domain.ShellElements().size() >
if (!domain.ShellElements().Empty()) {
if (domain.ShellElements().Size() >
(std::numeric_limits<std::size_t>::max)() / kShellLocationCount) {
return RecoveryFailure(
"invalid-recovery-dimensions", {domain.SourcePath(), 0U},
@@ -670,9 +670,9 @@ Status ResultRecovery::Recover(const AnalysisModel& model,
directors_by_node[frame.node_index] = frame.director;
}
shell_candidate.rows.reserve(domain.ShellElements().size() *
shell_candidate.rows.reserve(domain.ShellElements().Size() *
kShellLocationCount);
expected_shell_elements.reserve(domain.ShellElements().size());
expected_shell_elements.reserve(domain.ShellElements().Size());
constexpr std::array<ShellMidsurfaceLocation, kShellLocationCount>
locations{ShellMidsurfaceLocation::kGp1, ShellMidsurfaceLocation::kGp2,
ShellMidsurfaceLocation::kGp3, ShellMidsurfaceLocation::kGp4};
@@ -681,7 +681,7 @@ Status ResultRecovery::Recover(const AnalysisModel& model,
ShellSectionPosition::kTop};
constexpr std::array<double, 3> zeta{-1.0, 0.0, 1.0};
for (std::size_t element_order = 0U;
element_order < domain.ShellElements().size(); ++element_order) {
element_order < domain.ShellElements().Size(); ++element_order) {
const EntityIndex element_index = static_cast<EntityIndex>(element_order);
const auto& definition = domain.ShellElements()[element_order];
std::array<const Node*, 4> nodes{};
@@ -701,7 +701,7 @@ Status ResultRecovery::Recover(const AnalysisModel& model,
auto shell = Mitc4Shell::Create(
nodes, directors, domain.ShellSections()[definition.section_index],
domain.Materials()[definition.material_index]);
domain.LinearElasticMaterials()[definition.material_index]);
if (!shell.HasValue()) {
return shell.GetStatus();
}
@@ -788,9 +788,9 @@ ResultRecovery::NormalizeSectionResultantsToNodeStations(
"Node-station component tolerances must be finite and nonnegative.");
}
}
if (model.ActiveElements().size() >
if (model.ActiveBeamElements().size() >
(std::numeric_limits<std::size_t>::max)() / 2U ||
endpoint_rows.size() != model.ActiveElements().size() * 2U) {
endpoint_rows.size() != model.ActiveBeamElements().size() * 2U) {
return RecoveryResultFailure<std::vector<NodeStationResultRow>>(
"invalid-node-station-shape", {domain.SourcePath(), 0U},
std::to_string(endpoint_rows.size()),
@@ -799,15 +799,16 @@ ResultRecovery::NormalizeSectionResultantsToNodeStations(
std::vector<std::vector<const EndpointResultRow*>> rows_by_node(
domain.Nodes().size());
for (std::size_t order = 0U; order < model.ActiveElements().size(); ++order) {
const EntityIndex element_index = model.ActiveElements()[order];
if (element_index >= domain.Elements().size()) {
for (std::size_t order = 0U; order < model.ActiveBeamElements().size();
++order) {
const EntityIndex element_index = model.ActiveBeamElements()[order];
if (element_index >= domain.BeamElements().Size()) {
return RecoveryResultFailure<std::vector<NodeStationResultRow>>(
"invalid-node-station-entity", {domain.SourcePath(), 0U},
std::to_string(element_index),
"Every active station element must be a valid stable entity.");
}
const auto& definition = domain.Elements()[element_index];
const auto& definition = domain.BeamElements()[element_index];
for (std::size_t endpoint = 0U; endpoint < 2U; ++endpoint) {
const auto& row = endpoint_rows[order * 2U + endpoint];
const EntityIndex node_index = definition.node_indices[endpoint];
@@ -885,8 +886,8 @@ ResultRecovery::NormalizeSectionResultantsToNodeStations(
"Interior station collapse requires exactly two unloaded endpoints.");
}
const auto& first_element = domain.Elements()[incident[0U]->element];
const auto& second_element = domain.Elements()[incident[1U]->element];
const auto& first_element = domain.BeamElements()[incident[0U]->element];
const auto& second_element = domain.BeamElements()[incident[1U]->element];
const bool chain_orientation =
incident[0U]->endpoint != incident[1U]->endpoint &&
((incident[0U]->endpoint == 1 && incident[1U]->endpoint == 0) ||