feat(cpp-object-oriented-modular-refactoring): step 13 - element-definition-domain
This commit is contained in:
@@ -23,12 +23,18 @@ class AnalysisModel {
|
|||||||
/// @brief Returns the sole active static step.
|
/// @brief Returns the sole active static step.
|
||||||
const StaticStepDefinition& Step() const noexcept;
|
const StaticStepDefinition& Step() const noexcept;
|
||||||
|
|
||||||
/// @brief Returns active beam element indices in stable internal order.
|
/// @brief Returns active element-definition indices in stable Domain order.
|
||||||
const std::vector<EntityIndex>& ActiveElements() const noexcept;
|
const std::vector<EntityIndex>& ActiveElements() const noexcept;
|
||||||
|
|
||||||
|
/// @brief Returns active B33 indices in their concrete compatibility view.
|
||||||
|
const std::vector<EntityIndex>& ActiveBeamElements() const noexcept;
|
||||||
|
|
||||||
/// @brief Returns reachable material indices in stable internal order.
|
/// @brief Returns reachable material indices in stable internal order.
|
||||||
const std::vector<EntityIndex>& ActiveMaterials() const noexcept;
|
const std::vector<EntityIndex>& ActiveMaterials() const noexcept;
|
||||||
|
|
||||||
|
/// @brief Returns reachable property indices in stable internal order.
|
||||||
|
const std::vector<EntityIndex>& ActiveProperties() const noexcept;
|
||||||
|
|
||||||
/// @brief Returns reachable beam-section indices in stable internal order.
|
/// @brief Returns reachable beam-section indices in stable internal order.
|
||||||
const std::vector<EntityIndex>& ActiveSections() const noexcept;
|
const std::vector<EntityIndex>& ActiveSections() const noexcept;
|
||||||
|
|
||||||
@@ -44,7 +50,9 @@ class AnalysisModel {
|
|||||||
|
|
||||||
const Domain* domain_;
|
const Domain* domain_;
|
||||||
std::vector<EntityIndex> active_elements_;
|
std::vector<EntityIndex> active_elements_;
|
||||||
|
std::vector<EntityIndex> active_beam_elements_;
|
||||||
std::vector<EntityIndex> active_materials_;
|
std::vector<EntityIndex> active_materials_;
|
||||||
|
std::vector<EntityIndex> active_properties_;
|
||||||
std::vector<EntityIndex> active_sections_;
|
std::vector<EntityIndex> active_sections_;
|
||||||
std::vector<EntityIndex> active_boundary_conditions_;
|
std::vector<EntityIndex> active_boundary_conditions_;
|
||||||
std::vector<EntityIndex> active_loads_;
|
std::vector<EntityIndex> active_loads_;
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
#ifndef FESA_ELEMENTS_ELEMENT_DEFINITION_H_
|
||||||
|
#define FESA_ELEMENTS_ELEMENT_DEFINITION_H_
|
||||||
|
|
||||||
|
#include <string_view>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "fesa/core/source_identity.h"
|
||||||
|
|
||||||
|
namespace fesa {
|
||||||
|
|
||||||
|
/// @brief Identifies the supported semantic element-definition kinds.
|
||||||
|
enum class ElementDefinitionKind { kEulerBeam3D, kMitc4Shell };
|
||||||
|
|
||||||
|
/// @brief Provides immutable source identity and topology for one element.
|
||||||
|
/// @note Numerical stiffness, recovery, and equation ids are intentionally
|
||||||
|
/// excluded from this semantic interface.
|
||||||
|
class ElementDefinition {
|
||||||
|
public:
|
||||||
|
virtual ~ElementDefinition() = default;
|
||||||
|
|
||||||
|
/// @brief Returns the concrete semantic definition kind.
|
||||||
|
virtual ElementDefinitionKind Kind() const noexcept = 0;
|
||||||
|
|
||||||
|
/// @brief Returns the stable external source identity.
|
||||||
|
virtual const SourceEntityId& SourceId() const noexcept = 0;
|
||||||
|
|
||||||
|
/// @brief Returns the preserved source element type such as B33 or S4R.
|
||||||
|
virtual std::string_view SourceElementType() const noexcept = 0;
|
||||||
|
|
||||||
|
/// @brief Returns stable Domain node collection positions.
|
||||||
|
virtual const std::vector<EntityIndex>& NodeIndices() const noexcept = 0;
|
||||||
|
|
||||||
|
/// @brief Returns the stable Domain material collection position.
|
||||||
|
virtual EntityIndex MaterialIndex() const noexcept = 0;
|
||||||
|
|
||||||
|
/// @brief Returns the stable Domain property collection position.
|
||||||
|
virtual EntityIndex PropertyIndex() const noexcept = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace fesa
|
||||||
|
|
||||||
|
#endif // FESA_ELEMENTS_ELEMENT_DEFINITION_H_
|
||||||
@@ -4,15 +4,52 @@
|
|||||||
#include <array>
|
#include <array>
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "fesa/core/status.h"
|
#include "fesa/core/status.h"
|
||||||
|
#include "fesa/elements/element_definition.h"
|
||||||
|
#include "fesa/materials/isotropic_linear_elastic_material.h"
|
||||||
#include "fesa/math/matrix.h"
|
#include "fesa/math/matrix.h"
|
||||||
#include "fesa/math/vector.h"
|
#include "fesa/math/vector.h"
|
||||||
#include "fesa/model/model_types.h"
|
#include "fesa/properties/general_beam_section.h"
|
||||||
|
|
||||||
namespace fesa {
|
namespace fesa {
|
||||||
|
|
||||||
|
class Domain;
|
||||||
|
struct Node;
|
||||||
|
|
||||||
|
/// @brief Defines one two-node B33 semantic element.
|
||||||
|
class EulerBeam3DDefinition final : public ElementDefinition {
|
||||||
|
public:
|
||||||
|
/// @brief Constructs a parser-validated semantic definition.
|
||||||
|
EulerBeam3DDefinition(SourceEntityId source_id,
|
||||||
|
std::array<EntityIndex, 2> node_indices,
|
||||||
|
EntityIndex material_index, EntityIndex section_index,
|
||||||
|
SourceLocation location);
|
||||||
|
|
||||||
|
ElementDefinitionKind Kind() const noexcept override;
|
||||||
|
const SourceEntityId& SourceId() const noexcept override;
|
||||||
|
std::string_view SourceElementType() const noexcept override;
|
||||||
|
const std::vector<EntityIndex>& NodeIndices() const noexcept override;
|
||||||
|
EntityIndex MaterialIndex() const noexcept override;
|
||||||
|
EntityIndex PropertyIndex() const noexcept override;
|
||||||
|
|
||||||
|
SourceEntityId source_id;
|
||||||
|
std::array<EntityIndex, 2> node_indices;
|
||||||
|
EntityIndex material_index;
|
||||||
|
EntityIndex section_index;
|
||||||
|
SourceLocation location;
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class Domain;
|
||||||
|
|
||||||
|
/// @brief Synchronizes the base view after parser-candidate construction.
|
||||||
|
void SynchronizeNodeIndices();
|
||||||
|
|
||||||
|
std::vector<EntityIndex> node_indices_view_;
|
||||||
|
};
|
||||||
|
|
||||||
/// @brief Stores constant line-load components in the beam local frame.
|
/// @brief Stores constant line-load components in the beam local frame.
|
||||||
struct ConstantLocalLineLoad {
|
struct ConstantLocalLineLoad {
|
||||||
double px;
|
double px;
|
||||||
|
|||||||
@@ -3,15 +3,65 @@
|
|||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include "fesa/core/status.h"
|
#include "fesa/core/status.h"
|
||||||
|
#include "fesa/elements/element_definition.h"
|
||||||
|
#include "fesa/materials/isotropic_linear_elastic_material.h"
|
||||||
#include "fesa/math/matrix.h"
|
#include "fesa/math/matrix.h"
|
||||||
#include "fesa/math/vector.h"
|
#include "fesa/math/vector.h"
|
||||||
#include "fesa/math/vector3.h"
|
#include "fesa/math/vector3.h"
|
||||||
#include "fesa/model/model_types.h"
|
#include "fesa/properties/shell_section.h"
|
||||||
|
|
||||||
namespace fesa {
|
namespace fesa {
|
||||||
|
|
||||||
|
class Domain;
|
||||||
|
struct Node;
|
||||||
|
|
||||||
|
/// @brief Preserves the source shell type independently of formulation.
|
||||||
|
enum class ShellSourceElementType { kS4, kS4r };
|
||||||
|
|
||||||
|
/// @brief Names the internal shell formulation selected by S4 and S4R.
|
||||||
|
inline constexpr std::string_view kMitc4InternalFormulation{"FESA-MITC4"};
|
||||||
|
|
||||||
|
/// @brief Defines one four-node S4/S4R semantic element.
|
||||||
|
class Mitc4ShellDefinition final : public ElementDefinition {
|
||||||
|
public:
|
||||||
|
/// @brief Constructs a parser-validated semantic definition.
|
||||||
|
Mitc4ShellDefinition(SourceEntityId source_id,
|
||||||
|
ShellSourceElementType source_type,
|
||||||
|
std::array<EntityIndex, 4> node_indices,
|
||||||
|
EntityIndex material_index, EntityIndex section_index,
|
||||||
|
SourceLocation location);
|
||||||
|
|
||||||
|
ElementDefinitionKind Kind() const noexcept override;
|
||||||
|
const SourceEntityId& SourceId() const noexcept override;
|
||||||
|
std::string_view SourceElementType() const noexcept override;
|
||||||
|
const std::vector<EntityIndex>& NodeIndices() const noexcept override;
|
||||||
|
EntityIndex MaterialIndex() const noexcept override;
|
||||||
|
EntityIndex PropertyIndex() const noexcept override;
|
||||||
|
|
||||||
|
SourceEntityId source_id;
|
||||||
|
ShellSourceElementType source_type;
|
||||||
|
std::array<EntityIndex, 4> node_indices;
|
||||||
|
EntityIndex material_index;
|
||||||
|
EntityIndex section_index;
|
||||||
|
SourceLocation location;
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class Domain;
|
||||||
|
|
||||||
|
/// @brief Rebinds a shell-local section index to the unified property view.
|
||||||
|
void SetPropertyIndex(EntityIndex property_index) noexcept;
|
||||||
|
|
||||||
|
/// @brief Synchronizes the base view after parser-candidate construction.
|
||||||
|
void SynchronizeNodeIndices();
|
||||||
|
|
||||||
|
EntityIndex property_index_;
|
||||||
|
std::vector<EntityIndex> node_indices_view_;
|
||||||
|
};
|
||||||
|
|
||||||
/// @brief Stores bilinear shape values and natural-coordinate derivatives.
|
/// @brief Stores bilinear shape values and natural-coordinate derivatives.
|
||||||
struct Mitc4ShapeFunctions {
|
struct Mitc4ShapeFunctions {
|
||||||
std::array<double, 4> values;
|
std::array<double, 4> values;
|
||||||
|
|||||||
@@ -63,6 +63,9 @@ class IsotropicLinearElasticMaterial final : public Material {
|
|||||||
SourceEntityId source_id_;
|
SourceEntityId source_id_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// @brief Preserves the approved V0 material spelling for current consumers.
|
||||||
|
using LinearElasticMaterial = IsotropicLinearElasticMaterial;
|
||||||
|
|
||||||
} // namespace fesa
|
} // namespace fesa
|
||||||
|
|
||||||
#endif // FESA_MATERIALS_ISOTROPIC_LINEAR_ELASTIC_MATERIAL_H_
|
#endif // FESA_MATERIALS_ISOTROPIC_LINEAR_ELASTIC_MATERIAL_H_
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
#ifndef FESA_MODEL_DOMAIN_H_
|
#ifndef FESA_MODEL_DOMAIN_H_
|
||||||
#define FESA_MODEL_DOMAIN_H_
|
#define FESA_MODEL_DOMAIN_H_
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
@@ -10,6 +12,34 @@
|
|||||||
|
|
||||||
namespace fesa {
|
namespace fesa {
|
||||||
|
|
||||||
|
/// @brief Exposes immutable references without transferring Domain ownership.
|
||||||
|
/// @tparam T Base or concrete semantic type stored by the Domain.
|
||||||
|
template <class T>
|
||||||
|
class DomainCollectionView {
|
||||||
|
public:
|
||||||
|
/// @brief Returns the number of stable collection positions.
|
||||||
|
std::size_t Size() const noexcept { return entries_.size(); }
|
||||||
|
|
||||||
|
/// @brief Reports whether the collection has no entries.
|
||||||
|
bool Empty() const noexcept { return entries_.empty(); }
|
||||||
|
|
||||||
|
/// @brief Returns one immutable entry without bounds checking.
|
||||||
|
const T& operator[](const std::size_t index) const noexcept {
|
||||||
|
return *entries_[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Returns one immutable entry with bounds checking.
|
||||||
|
const T& At(const std::size_t index) const { return *entries_.at(index); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class Domain;
|
||||||
|
|
||||||
|
/// @brief Adds one reference while the owning Domain candidate is built.
|
||||||
|
void Add(const T& entry) { entries_.push_back(&entry); }
|
||||||
|
|
||||||
|
std::vector<const T*> entries_;
|
||||||
|
};
|
||||||
|
|
||||||
/// @brief Owns the complete immutable semantic model definition.
|
/// @brief Owns the complete immutable semantic model definition.
|
||||||
/// @note Collection positions remain stable internal indices after
|
/// @note Collection positions remain stable internal indices after
|
||||||
/// construction.
|
/// construction.
|
||||||
@@ -20,23 +50,40 @@ class Domain {
|
|||||||
/// @return A successful owning Domain.
|
/// @return A successful owning Domain.
|
||||||
static Result<Domain> Create(ModelDefinition definition);
|
static Result<Domain> Create(ModelDefinition definition);
|
||||||
|
|
||||||
|
Domain(const Domain&) = delete;
|
||||||
|
Domain& operator=(const Domain&) = delete;
|
||||||
|
Domain(Domain&&) noexcept = default;
|
||||||
|
Domain& operator=(Domain&&) noexcept = default;
|
||||||
|
|
||||||
/// @brief Returns nodes in stable declaration order.
|
/// @brief Returns nodes in stable declaration order.
|
||||||
const std::vector<Node>& Nodes() const noexcept;
|
const std::vector<Node>& Nodes() const noexcept;
|
||||||
|
|
||||||
/// @brief Returns Euler beam definitions in stable declaration order.
|
/// @brief Returns all element definitions in stable Domain index order.
|
||||||
const std::vector<EulerBeam3DDefinition>& Elements() const noexcept;
|
const DomainCollectionView<ElementDefinition>& Elements() const noexcept;
|
||||||
|
|
||||||
|
/// @brief Returns B33 definitions in their stable concrete order.
|
||||||
|
const DomainCollectionView<EulerBeam3DDefinition>& BeamElements()
|
||||||
|
const noexcept;
|
||||||
|
|
||||||
/// @brief Returns MITC4 shell definitions in stable declaration order.
|
/// @brief Returns MITC4 shell definitions in stable declaration order.
|
||||||
const std::vector<Mitc4ShellDefinition>& ShellElements() const noexcept;
|
const DomainCollectionView<Mitc4ShellDefinition>& ShellElements()
|
||||||
|
const noexcept;
|
||||||
|
|
||||||
/// @brief Returns materials in stable declaration order.
|
/// @brief Returns all materials in stable Domain index order.
|
||||||
const std::vector<LinearElasticMaterial>& Materials() const noexcept;
|
const DomainCollectionView<Material>& Materials() const noexcept;
|
||||||
|
|
||||||
|
/// @brief Returns current isotropic materials in stable concrete order.
|
||||||
|
const DomainCollectionView<LinearElasticMaterial>& LinearElasticMaterials()
|
||||||
|
const noexcept;
|
||||||
|
|
||||||
|
/// @brief Returns all properties in stable Domain index order.
|
||||||
|
const DomainCollectionView<ElementProperty>& Properties() const noexcept;
|
||||||
|
|
||||||
/// @brief Returns beam sections in stable declaration order.
|
/// @brief Returns beam sections in stable declaration order.
|
||||||
const std::vector<GeneralBeamSection>& Sections() const noexcept;
|
const DomainCollectionView<GeneralBeamSection>& Sections() const noexcept;
|
||||||
|
|
||||||
/// @brief Returns shell sections in stable declaration order.
|
/// @brief Returns shell sections in stable declaration order.
|
||||||
const std::vector<ShellSection>& ShellSections() const noexcept;
|
const DomainCollectionView<ShellSection>& ShellSections() const noexcept;
|
||||||
|
|
||||||
/// @brief Returns preprocessed shell-node frames in stable node order.
|
/// @brief Returns preprocessed shell-node frames in stable node order.
|
||||||
const std::vector<ShellNodeInitialFrame>& ShellNodeInitialFrames()
|
const std::vector<ShellNodeInitialFrame>& ShellNodeInitialFrames()
|
||||||
@@ -65,6 +112,17 @@ class Domain {
|
|||||||
explicit Domain(ModelDefinition definition);
|
explicit Domain(ModelDefinition definition);
|
||||||
|
|
||||||
ModelDefinition definition_;
|
ModelDefinition definition_;
|
||||||
|
std::vector<std::unique_ptr<ElementDefinition>> element_definitions_;
|
||||||
|
std::vector<std::unique_ptr<ElementProperty>> element_properties_;
|
||||||
|
std::vector<std::unique_ptr<Material>> materials_;
|
||||||
|
DomainCollectionView<ElementDefinition> elements_view_;
|
||||||
|
DomainCollectionView<EulerBeam3DDefinition> beam_elements_view_;
|
||||||
|
DomainCollectionView<Mitc4ShellDefinition> shell_elements_view_;
|
||||||
|
DomainCollectionView<ElementProperty> properties_view_;
|
||||||
|
DomainCollectionView<GeneralBeamSection> sections_view_;
|
||||||
|
DomainCollectionView<ShellSection> shell_sections_view_;
|
||||||
|
DomainCollectionView<Material> materials_view_;
|
||||||
|
DomainCollectionView<LinearElasticMaterial> linear_materials_view_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace fesa
|
} // namespace fesa
|
||||||
|
|||||||
@@ -11,6 +11,8 @@
|
|||||||
|
|
||||||
#include "fesa/core/diagnostic.h"
|
#include "fesa/core/diagnostic.h"
|
||||||
#include "fesa/core/source_identity.h"
|
#include "fesa/core/source_identity.h"
|
||||||
|
#include "fesa/elements/euler_beam_3d.h"
|
||||||
|
#include "fesa/elements/mitc4_shell.h"
|
||||||
#include "fesa/materials/isotropic_linear_elastic_material.h"
|
#include "fesa/materials/isotropic_linear_elastic_material.h"
|
||||||
#include "fesa/properties/general_beam_section.h"
|
#include "fesa/properties/general_beam_section.h"
|
||||||
#include "fesa/properties/shell_section.h"
|
#include "fesa/properties/shell_section.h"
|
||||||
@@ -24,26 +26,6 @@ struct Node {
|
|||||||
SourceLocation location;
|
SourceLocation location;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// @brief Keeps the current material spelling during the hierarchy migration.
|
|
||||||
using LinearElasticMaterial = IsotropicLinearElasticMaterial;
|
|
||||||
|
|
||||||
/// @brief Preserves the source shell element label independently of
|
|
||||||
/// formulation.
|
|
||||||
enum class ShellSourceElementType { kS4, kS4r };
|
|
||||||
|
|
||||||
/// @brief Names the single internal shell formulation selected by S4 and S4R.
|
|
||||||
inline constexpr std::string_view kMitc4InternalFormulation{"FESA-MITC4"};
|
|
||||||
|
|
||||||
/// @brief Defines one four-node shell with stable semantic references.
|
|
||||||
struct Mitc4ShellDefinition {
|
|
||||||
SourceEntityId source_id;
|
|
||||||
ShellSourceElementType source_type;
|
|
||||||
std::array<EntityIndex, 4> node_indices;
|
|
||||||
EntityIndex material_index;
|
|
||||||
EntityIndex section_index;
|
|
||||||
SourceLocation location;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// @brief Stores the deterministic initial director and tangent frame at a
|
/// @brief Stores the deterministic initial director and tangent frame at a
|
||||||
/// node.
|
/// node.
|
||||||
struct ShellNodeInitialFrame {
|
struct ShellNodeInitialFrame {
|
||||||
@@ -53,15 +35,6 @@ struct ShellNodeInitialFrame {
|
|||||||
std::array<double, 3> tangent_b;
|
std::array<double, 3> tangent_b;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// @brief Defines one two-node Euler beam with stable semantic references.
|
|
||||||
struct EulerBeam3DDefinition {
|
|
||||||
SourceEntityId source_id;
|
|
||||||
std::array<EntityIndex, 2> node_indices;
|
|
||||||
EntityIndex material_index;
|
|
||||||
EntityIndex section_index;
|
|
||||||
SourceLocation location;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// @brief Stores one prescribed nodal degree-of-freedom range.
|
/// @brief Stores one prescribed nodal degree-of-freedom range.
|
||||||
struct BoundaryCondition {
|
struct BoundaryCondition {
|
||||||
std::string target;
|
std::string target;
|
||||||
|
|||||||
@@ -37,11 +37,21 @@ const std::vector<EntityIndex>& AnalysisModel::ActiveElements() const noexcept {
|
|||||||
return active_elements_;
|
return active_elements_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const std::vector<EntityIndex>& AnalysisModel::ActiveBeamElements()
|
||||||
|
const noexcept {
|
||||||
|
return active_beam_elements_;
|
||||||
|
}
|
||||||
|
|
||||||
const std::vector<EntityIndex>& AnalysisModel::ActiveMaterials()
|
const std::vector<EntityIndex>& AnalysisModel::ActiveMaterials()
|
||||||
const noexcept {
|
const noexcept {
|
||||||
return active_materials_;
|
return active_materials_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const std::vector<EntityIndex>& AnalysisModel::ActiveProperties()
|
||||||
|
const noexcept {
|
||||||
|
return active_properties_;
|
||||||
|
}
|
||||||
|
|
||||||
const std::vector<EntityIndex>& AnalysisModel::ActiveSections() const noexcept {
|
const std::vector<EntityIndex>& AnalysisModel::ActiveSections() const noexcept {
|
||||||
return active_sections_;
|
return active_sections_;
|
||||||
}
|
}
|
||||||
@@ -56,13 +66,20 @@ const std::vector<EntityIndex>& AnalysisModel::ActiveLoads() const noexcept {
|
|||||||
}
|
}
|
||||||
|
|
||||||
AnalysisModel::AnalysisModel(const Domain& domain) : domain_{&domain} {
|
AnalysisModel::AnalysisModel(const Domain& domain) : domain_{&domain} {
|
||||||
std::vector<bool> reachable_materials(domain.Materials().size(), false);
|
std::vector<bool> reachable_materials(domain.Materials().Size(), false);
|
||||||
std::vector<bool> reachable_sections(domain.Sections().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];
|
const auto& element = domain.Elements()[index];
|
||||||
active_elements_.push_back(static_cast<EntityIndex>(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;
|
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));
|
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) {
|
for (std::size_t index = 0U; index < reachable_sections.size(); ++index) {
|
||||||
if (reachable_sections[index]) {
|
if (reachable_sections[index]) {
|
||||||
active_sections_.push_back(static_cast<EntityIndex>(index));
|
active_sections_.push_back(static_cast<EntityIndex>(index));
|
||||||
|
|||||||
@@ -140,7 +140,7 @@ Status ValidateFiniteVector(const Vector& values,
|
|||||||
}
|
}
|
||||||
|
|
||||||
Status ValidateShellMoments(const Domain& domain, const Vector& full_load) {
|
Status ValidateShellMoments(const Domain& domain, const Vector& full_load) {
|
||||||
if (domain.ShellElements().empty()) {
|
if (domain.ShellElements().Empty()) {
|
||||||
return Status::Ok();
|
return Status::Ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -51,19 +51,19 @@ Result<SparseMatrix> SparseAssembler::AssembleStiffness(
|
|||||||
std::to_string(dofs.FullDofCount()),
|
std::to_string(dofs.FullDofCount()),
|
||||||
"DofManager dimensions do not match the active model nodes.");
|
"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(
|
return AssemblyFailure(
|
||||||
"unsupported-mixed-element-model", {domain.SourcePath(), 0U},
|
"unsupported-mixed-element-model", {domain.SourcePath(), 0U},
|
||||||
"B33:FESA-MITC4",
|
"B33:FESA-MITC4",
|
||||||
"Sparse assembly does not support mixed beam and shell models.");
|
"Sparse assembly does not support mixed beam and shell models.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!domain.ShellElements().empty()) {
|
if (!domain.ShellElements().Empty()) {
|
||||||
if (domain.ShellElements().size() >
|
if (domain.ShellElements().Size() >
|
||||||
(std::numeric_limits<std::size_t>::max)() / kShellContributionCount) {
|
(std::numeric_limits<std::size_t>::max)() / kShellContributionCount) {
|
||||||
return AssemblyFailure(
|
return AssemblyFailure(
|
||||||
"invalid-assembly-dimensions", {domain.SourcePath(), 0U},
|
"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.");
|
"Shell contribution storage exceeds the addressable range.");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,12 +88,12 @@ Result<SparseMatrix> SparseAssembler::AssembleStiffness(
|
|||||||
std::array<std::size_t, kShellElementDofCount> scatter;
|
std::array<std::size_t, kShellElementDofCount> scatter;
|
||||||
};
|
};
|
||||||
std::vector<ShellInput> inputs;
|
std::vector<ShellInput> inputs;
|
||||||
inputs.reserve(domain.ShellElements().size());
|
inputs.reserve(domain.ShellElements().Size());
|
||||||
for (std::size_t element_order = 0U;
|
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];
|
const auto& element = domain.ShellElements()[element_order];
|
||||||
if (element.material_index >= domain.Materials().size() ||
|
if (element.material_index >= domain.LinearElasticMaterials().Size() ||
|
||||||
element.section_index >= domain.ShellSections().size()) {
|
element.section_index >= domain.ShellSections().Size()) {
|
||||||
return AssemblyFailure(
|
return AssemblyFailure(
|
||||||
"invalid-assembly-element", element.location,
|
"invalid-assembly-element", element.location,
|
||||||
element.source_id.source_label_text,
|
element.source_id.source_label_text,
|
||||||
@@ -102,7 +102,7 @@ Result<SparseMatrix> SparseAssembler::AssembleStiffness(
|
|||||||
|
|
||||||
ShellInput input{};
|
ShellInput input{};
|
||||||
input.section = &domain.ShellSections()[element.section_index];
|
input.section = &domain.ShellSections()[element.section_index];
|
||||||
input.material = &domain.Materials()[element.material_index];
|
input.material = &domain.LinearElasticMaterials()[element.material_index];
|
||||||
try {
|
try {
|
||||||
input.scatter =
|
input.scatter =
|
||||||
dofs.ShellElementScatter(static_cast<EntityIndex>(element_order));
|
dofs.ShellElementScatter(static_cast<EntityIndex>(element_order));
|
||||||
@@ -191,28 +191,28 @@ Result<SparseMatrix> SparseAssembler::AssembleStiffness(
|
|||||||
dofs.GetSparsePattern());
|
dofs.GetSparsePattern());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (model.ActiveElements().size() >
|
if (model.ActiveBeamElements().size() >
|
||||||
(std::numeric_limits<std::size_t>::max)() / kBeamContributionCount) {
|
(std::numeric_limits<std::size_t>::max)() / kBeamContributionCount) {
|
||||||
return AssemblyFailure(
|
return AssemblyFailure(
|
||||||
"invalid-assembly-dimensions", {domain.SourcePath(), 0U},
|
"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.");
|
"Element contribution storage exceeds the addressable range.");
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::array<std::size_t, kBeamElementDofCount>> scatters;
|
std::vector<std::array<std::size_t, kBeamElementDofCount>> scatters;
|
||||||
scatters.reserve(model.ActiveElements().size());
|
scatters.reserve(model.ActiveBeamElements().size());
|
||||||
for (const EntityIndex element_index : model.ActiveElements()) {
|
for (const EntityIndex element_index : model.ActiveBeamElements()) {
|
||||||
if (element_index >= domain.Elements().size()) {
|
if (element_index >= domain.BeamElements().Size()) {
|
||||||
return AssemblyFailure("invalid-assembly-element",
|
return AssemblyFailure("invalid-assembly-element",
|
||||||
{domain.SourcePath(), 0U},
|
{domain.SourcePath(), 0U},
|
||||||
std::to_string(element_index),
|
std::to_string(element_index),
|
||||||
"Active element index is outside the Domain.");
|
"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() ||
|
if (element.node_indices[0U] >= domain.Nodes().size() ||
|
||||||
element.node_indices[1U] >= domain.Nodes().size() ||
|
element.node_indices[1U] >= domain.Nodes().size() ||
|
||||||
element.material_index >= domain.Materials().size() ||
|
element.material_index >= domain.LinearElasticMaterials().Size() ||
|
||||||
element.section_index >= domain.Sections().size()) {
|
element.section_index >= domain.Sections().Size()) {
|
||||||
return AssemblyFailure(
|
return AssemblyFailure(
|
||||||
"invalid-assembly-element", element.location,
|
"invalid-assembly-element", element.location,
|
||||||
element.source_id.source_label_text,
|
element.source_id.source_label_text,
|
||||||
@@ -247,18 +247,20 @@ Result<SparseMatrix> SparseAssembler::AssembleStiffness(
|
|||||||
scatters.push_back(scatter);
|
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(
|
std::vector<std::optional<Status>> local_failures(
|
||||||
model.ActiveElements().size());
|
model.ActiveBeamElements().size());
|
||||||
parallel_for.Execute(
|
parallel_for.Execute(
|
||||||
model.ActiveElements().size(), [&](const std::size_t element_order) {
|
model.ActiveBeamElements().size(), [&](const std::size_t element_order) {
|
||||||
const EntityIndex element_index = model.ActiveElements()[element_order];
|
const EntityIndex element_index =
|
||||||
const auto& definition = domain.Elements()[element_index];
|
model.ActiveBeamElements()[element_order];
|
||||||
const auto beam =
|
const auto& definition = domain.BeamElements()[element_index];
|
||||||
EulerBeam3D::Create(domain.Nodes()[definition.node_indices[0U]],
|
const auto beam = EulerBeam3D::Create(
|
||||||
domain.Nodes()[definition.node_indices[1U]],
|
domain.Nodes()[definition.node_indices[0U]],
|
||||||
domain.Sections()[definition.section_index],
|
domain.Nodes()[definition.node_indices[1U]],
|
||||||
domain.Materials()[definition.material_index]);
|
domain.Sections()[definition.section_index],
|
||||||
|
domain.LinearElasticMaterials()[definition.material_index]);
|
||||||
if (!beam.HasValue()) {
|
if (!beam.HasValue()) {
|
||||||
local_failures[element_order] = beam.GetStatus();
|
local_failures[element_order] = beam.GetStatus();
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -10,8 +10,51 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "fesa/math/vector3.h"
|
#include "fesa/math/vector3.h"
|
||||||
|
#include "fesa/model/model_types.h"
|
||||||
|
|
||||||
namespace fesa {
|
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 {
|
namespace {
|
||||||
|
|
||||||
constexpr std::size_t kElementDofCount = 12U;
|
constexpr std::size_t kElementDofCount = 12U;
|
||||||
|
|||||||
@@ -7,7 +7,59 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
|
#include "fesa/model/model_types.h"
|
||||||
|
|
||||||
namespace fesa {
|
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 {
|
namespace {
|
||||||
|
|
||||||
constexpr std::size_t kNodeCount = 4U;
|
constexpr std::size_t kNodeCount = 4U;
|
||||||
|
|||||||
@@ -120,9 +120,9 @@ Result<DofManager> DofManager::Create(const AnalysisModel& model) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::array<std::size_t, 12>> element_scatters(
|
std::vector<std::array<std::size_t, 12>> element_scatters(
|
||||||
domain.Elements().size());
|
domain.BeamElements().Size());
|
||||||
for (const EntityIndex element_index : model.ActiveElements()) {
|
for (const EntityIndex element_index : model.ActiveBeamElements()) {
|
||||||
const auto& element = domain.Elements().at(element_index);
|
const auto& element = domain.BeamElements().At(element_index);
|
||||||
auto& scatter = element_scatters.at(element_index);
|
auto& scatter = element_scatters.at(element_index);
|
||||||
for (std::size_t endpoint = 0U; endpoint < element.node_indices.size();
|
for (std::size_t endpoint = 0U; endpoint < element.node_indices.size();
|
||||||
++endpoint) {
|
++endpoint) {
|
||||||
@@ -135,9 +135,9 @@ Result<DofManager> DofManager::Create(const AnalysisModel& model) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::array<std::size_t, 24>> shell_element_scatters(
|
std::vector<std::array<std::size_t, 24>> shell_element_scatters(
|
||||||
domain.ShellElements().size());
|
domain.ShellElements().Size());
|
||||||
for (std::size_t element_index = 0U;
|
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];
|
const auto& element = domain.ShellElements()[element_index];
|
||||||
auto& scatter = shell_element_scatters[element_index];
|
auto& scatter = shell_element_scatters[element_index];
|
||||||
for (std::size_t node_position = 0U;
|
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);
|
element_scatters, shell_element_scatters);
|
||||||
return Result<DofManager>::Success(
|
return Result<DofManager>::Success(
|
||||||
DofManager{full_count, std::move(free_equations),
|
DofManager{full_count, std::move(free_equations),
|
||||||
|
|||||||
@@ -230,7 +230,7 @@ struct WriterModelData {
|
|||||||
};
|
};
|
||||||
|
|
||||||
bool IsShellDomain(const Domain& domain) noexcept {
|
bool IsShellDomain(const Domain& domain) noexcept {
|
||||||
return !domain.ShellElements().empty();
|
return !domain.ShellElements().Empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* ShellSourceTypeName(const ShellSourceElementType type) {
|
const char* ShellSourceTypeName(const ShellSourceElementType type) {
|
||||||
@@ -261,7 +261,7 @@ bool ComputeLocalAxes(const Domain& domain,
|
|||||||
const EulerBeam3DDefinition& element, AxisSet& axes) {
|
const EulerBeam3DDefinition& element, AxisSet& axes) {
|
||||||
if (element.node_indices[0U] >= domain.Nodes().size() ||
|
if (element.node_indices[0U] >= domain.Nodes().size() ||
|
||||||
element.node_indices[1U] >= domain.Nodes().size() ||
|
element.node_indices[1U] >= domain.Nodes().size() ||
|
||||||
element.section_index >= domain.Sections().size()) {
|
element.section_index >= domain.Sections().Size()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const auto& first = domain.Nodes()[element.node_indices[0U]].coordinates;
|
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,
|
Status ValidateShellWriterInput(const Domain& domain,
|
||||||
const AnalysisState& state) {
|
const AnalysisState& state) {
|
||||||
if (!domain.Elements().empty()) {
|
if (!domain.BeamElements().Empty()) {
|
||||||
return OutputFailure(
|
return OutputFailure(
|
||||||
"invalid-result-identity",
|
"invalid-result-identity",
|
||||||
"Schema v0 does not combine B33 and FESA-MITC4 element inventories.");
|
"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) ||
|
if (material.name.empty() || !IsValidUtf8(material.name) ||
|
||||||
!std::isfinite(material.youngs_modulus) ||
|
!std::isfinite(material.youngs_modulus) ||
|
||||||
!std::isfinite(material.poisson_ratio) ||
|
!std::isfinite(material.poisson_ratio) ||
|
||||||
@@ -315,32 +317,34 @@ Status ValidateShellWriterInput(const Domain& domain,
|
|||||||
"finite constitutive data.");
|
"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) ||
|
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)) {
|
!std::isfinite(section.thickness) || !(section.thickness > 0.0)) {
|
||||||
return OutputFailure("invalid-result-identity",
|
return OutputFailure("invalid-result-identity",
|
||||||
"Every shell section requires stable material "
|
"Every shell section requires stable material "
|
||||||
"identity and positive finite thickness.");
|
"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 &&
|
if ((element.source_type != ShellSourceElementType::kS4 &&
|
||||||
element.source_type != ShellSourceElementType::kS4r) ||
|
element.source_type != ShellSourceElementType::kS4r) ||
|
||||||
element.source_id.source_label <= 0 ||
|
element.source_id.source_label <= 0 ||
|
||||||
element.source_id.source_label_text.empty() ||
|
element.source_id.source_label_text.empty() ||
|
||||||
!IsValidUtf8(element.source_id.instance_name) ||
|
!IsValidUtf8(element.source_id.instance_name) ||
|
||||||
!IsValidUtf8(element.source_id.source_label_text) ||
|
!IsValidUtf8(element.source_id.source_label_text) ||
|
||||||
element.material_index >= domain.Materials().size() ||
|
element.node_indices.size() != kShellNodeCount ||
|
||||||
element.section_index >= domain.ShellSections().size() ||
|
element.material_index >= domain.LinearElasticMaterials().Size() ||
|
||||||
|
element.section_index >= domain.ShellSections().Size() ||
|
||||||
domain.ShellSections()[element.section_index].material_index !=
|
domain.ShellSections()[element.section_index].material_index !=
|
||||||
element.material_index) {
|
element.material_index) {
|
||||||
return OutputFailure("invalid-result-identity",
|
return OutputFailure("invalid-result-identity",
|
||||||
"Every shell element requires stable source, "
|
"Every shell element requires stable source, "
|
||||||
"material, and section identity.");
|
"material, and section identity.");
|
||||||
}
|
}
|
||||||
std::array<EntityIndex, kShellNodeCount> sorted_nodes =
|
auto sorted_nodes = element.node_indices;
|
||||||
element.node_indices;
|
|
||||||
std::sort(sorted_nodes.begin(), sorted_nodes.end());
|
std::sort(sorted_nodes.begin(), sorted_nodes.end());
|
||||||
if (sorted_nodes.back() >= domain.Nodes().size() ||
|
if (sorted_nodes.back() >= domain.Nodes().size() ||
|
||||||
std::adjacent_find(sorted_nodes.begin(), sorted_nodes.end()) !=
|
std::adjacent_find(sorted_nodes.begin(), sorted_nodes.end()) !=
|
||||||
@@ -368,7 +372,7 @@ Status ValidateShellWriterInput(const Domain& domain,
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::size_t expected_rows = 0U;
|
std::size_t expected_rows = 0U;
|
||||||
if (!SizeProductFits(domain.ShellElements().size(), kShellLocationCount,
|
if (!SizeProductFits(domain.ShellElements().Size(), kShellLocationCount,
|
||||||
expected_rows) ||
|
expected_rows) ||
|
||||||
state.ShellResults().size() != expected_rows) {
|
state.ShellResults().size() != expected_rows) {
|
||||||
return OutputFailure("invalid-result-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.clear();
|
||||||
model_data.beam_local_axes.reserve(domain.Elements().size());
|
model_data.beam_local_axes.reserve(domain.BeamElements().Size());
|
||||||
for (const EulerBeam3DDefinition& element : domain.Elements()) {
|
for (std::size_t index = 0U; index < domain.BeamElements().Size(); ++index) {
|
||||||
|
const EulerBeam3DDefinition& element = domain.BeamElements()[index];
|
||||||
AxisSet axes{};
|
AxisSet axes{};
|
||||||
if (element.source_id.source_label <= 0 ||
|
if (element.source_id.source_label <= 0 ||
|
||||||
element.source_id.source_label_text.empty() ||
|
element.source_id.source_label_text.empty() ||
|
||||||
!IsValidUtf8(element.source_id.instance_name) ||
|
!IsValidUtf8(element.source_id.instance_name) ||
|
||||||
!IsValidUtf8(element.source_id.source_label_text) ||
|
!IsValidUtf8(element.source_id.source_label_text) ||
|
||||||
element.node_indices[0U] == element.node_indices[1U] ||
|
element.node_indices[0U] == element.node_indices[1U] ||
|
||||||
element.material_index >= domain.Materials().size() ||
|
element.material_index >= domain.LinearElasticMaterials().Size() ||
|
||||||
!ComputeLocalAxes(domain, element, axes)) {
|
!ComputeLocalAxes(domain, element, axes)) {
|
||||||
return OutputFailure("invalid-result-identity",
|
return OutputFailure("invalid-result-identity",
|
||||||
"Every element requires valid source, connectivity, "
|
"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 endpoint_count = 0U;
|
||||||
std::size_t gauss_count = 0U;
|
std::size_t gauss_count = 0U;
|
||||||
if (!SizeProductFits(domain.Elements().size(), kEndpointCount,
|
if (!SizeProductFits(domain.BeamElements().Size(), kEndpointCount,
|
||||||
endpoint_count) ||
|
endpoint_count) ||
|
||||||
!SizeProductFits(domain.Elements().size(), kGaussPointCount,
|
!SizeProductFits(domain.BeamElements().Size(), kGaussPointCount,
|
||||||
gauss_count) ||
|
gauss_count) ||
|
||||||
state.EndpointResults().size() != endpoint_count ||
|
state.EndpointResults().size() != endpoint_count ||
|
||||||
state.GaussResults().size() != gauss_count) {
|
state.GaussResults().size() != gauss_count) {
|
||||||
@@ -522,7 +527,7 @@ Status ValidateWriterInput(const std::filesystem::path& output_path,
|
|||||||
static_cast<EntityIndex>(row_index / kEndpointCount);
|
static_cast<EntityIndex>(row_index / kEndpointCount);
|
||||||
const int expected_endpoint = static_cast<int>(row_index % kEndpointCount);
|
const int expected_endpoint = static_cast<int>(row_index % kEndpointCount);
|
||||||
const EndpointResultRow& row = state.EndpointResults()[row_index];
|
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 =
|
const auto& expected_node =
|
||||||
domain.Nodes()[element.node_indices[static_cast<std::size_t>(
|
domain.Nodes()[element.node_indices[static_cast<std::size_t>(
|
||||||
expected_endpoint)]];
|
expected_endpoint)]];
|
||||||
@@ -552,9 +557,9 @@ Status ValidateWriterInput(const std::filesystem::path& output_path,
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::size_t stress_index = 0U;
|
std::size_t stress_index = 0U;
|
||||||
for (std::size_t element_index = 0U; element_index < domain.Elements().size();
|
for (std::size_t element_index = 0U;
|
||||||
++element_index) {
|
element_index < domain.BeamElements().Size(); ++element_index) {
|
||||||
const auto& element = domain.Elements()[element_index];
|
const auto& element = domain.BeamElements()[element_index];
|
||||||
const auto& section_points =
|
const auto& section_points =
|
||||||
domain.Sections()[element.section_index].section_points;
|
domain.Sections()[element.section_index].section_points;
|
||||||
for (std::size_t gauss = 0U; gauss < kGaussPointCount; ++gauss) {
|
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,
|
void WriteBeamElements(const hid_t file, const Domain& domain,
|
||||||
const std::vector<AxisSet>& axes) {
|
const std::vector<AxisSet>& axes) {
|
||||||
std::vector<ElementWriteRow> rows;
|
std::vector<ElementWriteRow> rows;
|
||||||
rows.reserve(domain.Elements().size());
|
rows.reserve(domain.BeamElements().Size());
|
||||||
for (std::size_t index = 0U; index < domain.Elements().size(); ++index) {
|
for (std::size_t index = 0U; index < domain.BeamElements().Size(); ++index) {
|
||||||
const auto& element = domain.Elements()[index];
|
const auto& element = domain.BeamElements()[index];
|
||||||
ElementWriteRow row{static_cast<std::uint64_t>(index),
|
ElementWriteRow row{static_cast<std::uint64_t>(index),
|
||||||
element.source_id.instance_name.c_str(),
|
element.source_id.instance_name.c_str(),
|
||||||
element.source_id.source_label_text.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) {
|
void WriteShellElements(const hid_t file, const Domain& domain) {
|
||||||
std::vector<ShellElementWriteRow> rows;
|
std::vector<ShellElementWriteRow> rows;
|
||||||
rows.reserve(domain.ShellElements().size());
|
rows.reserve(domain.ShellElements().Size());
|
||||||
for (std::size_t index = 0U; index < domain.ShellElements().size(); ++index) {
|
for (std::size_t index = 0U; index < domain.ShellElements().Size(); ++index) {
|
||||||
const auto& element = domain.ShellElements()[index];
|
const auto& element = domain.ShellElements()[index];
|
||||||
rows.push_back({static_cast<std::uint64_t>(index),
|
rows.push_back({static_cast<std::uint64_t>(index),
|
||||||
element.source_id.instance_name.c_str(),
|
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) {
|
void WriteShellMaterials(const hid_t file, const Domain& domain) {
|
||||||
std::vector<ShellMaterialWriteRow> rows;
|
std::vector<ShellMaterialWriteRow> rows;
|
||||||
rows.reserve(domain.Materials().size());
|
rows.reserve(domain.LinearElasticMaterials().Size());
|
||||||
for (std::size_t index = 0U; index < domain.Materials().size(); ++index) {
|
for (std::size_t index = 0U; index < domain.LinearElasticMaterials().Size();
|
||||||
const auto& material = domain.Materials()[index];
|
++index) {
|
||||||
|
const auto& material = domain.LinearElasticMaterials()[index];
|
||||||
rows.push_back({static_cast<std::uint64_t>(index), material.name.c_str(),
|
rows.push_back({static_cast<std::uint64_t>(index), material.name.c_str(),
|
||||||
material.youngs_modulus, material.poisson_ratio});
|
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) {
|
void WriteShellSections(const hid_t file, const Domain& domain) {
|
||||||
std::vector<std::string> source_files;
|
std::vector<std::string> source_files;
|
||||||
source_files.reserve(domain.ShellSections().size());
|
source_files.reserve(domain.ShellSections().Size());
|
||||||
for (const auto& section : domain.ShellSections()) {
|
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));
|
source_files.push_back(NormalizedPathString(section.location.file));
|
||||||
}
|
}
|
||||||
std::vector<ShellSectionWriteRow> rows;
|
std::vector<ShellSectionWriteRow> rows;
|
||||||
rows.reserve(domain.ShellSections().size());
|
rows.reserve(domain.ShellSections().Size());
|
||||||
for (std::size_t index = 0U; index < domain.ShellSections().size(); ++index) {
|
for (std::size_t index = 0U; index < domain.ShellSections().Size(); ++index) {
|
||||||
const auto& section = domain.ShellSections()[index];
|
const auto& section = domain.ShellSections()[index];
|
||||||
rows.push_back({static_cast<std::uint64_t>(index),
|
rows.push_back({static_cast<std::uint64_t>(index),
|
||||||
source_files[index].c_str(),
|
source_files[index].c_str(),
|
||||||
@@ -1423,7 +1430,7 @@ void WriteShellResultDatasets(const hid_t file, const Domain& domain,
|
|||||||
}
|
}
|
||||||
|
|
||||||
const hsize_t element_count =
|
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 root = std::string{kStepRoot} + "/element/shell";
|
||||||
const std::string frame_path = root + "/local_frame";
|
const std::string frame_path = root + "/local_frame";
|
||||||
WriteDoubleDataset(file, frame_path, {element_count, 4U, 3U, 3U},
|
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 = {
|
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};
|
kEndActionComponentCount};
|
||||||
const std::vector<hsize_t> generalized_dimensions = {
|
const std::vector<hsize_t> generalized_dimensions = {
|
||||||
static_cast<hsize_t>(domain.Elements().size()), kEndpointCount,
|
static_cast<hsize_t>(domain.BeamElements().Size()), kEndpointCount,
|
||||||
kGeneralizedComponentCount};
|
kGeneralizedComponentCount};
|
||||||
const auto end_actions =
|
const auto end_actions =
|
||||||
FlattenEndpointValues(state.EndpointResults(), false);
|
FlattenEndpointValues(state.EndpointResults(), false);
|
||||||
@@ -2052,18 +2059,19 @@ void SelfCheckFile(const std::filesystem::path& path, const Domain& domain,
|
|||||||
if (IsShellDomain(domain)) {
|
if (IsShellDomain(domain)) {
|
||||||
RequireCompoundDataset(
|
RequireCompoundDataset(
|
||||||
file.Get(), "/model/elements",
|
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",
|
{"internal_element_id", "instance_name", "source_label",
|
||||||
"source_element_type", "internal_formulation", "node_internal_ids",
|
"source_element_type", "internal_formulation", "node_internal_ids",
|
||||||
"shell_section_internal_id", "material_internal_id"});
|
"shell_section_internal_id", "material_internal_id"});
|
||||||
auto elements = OpenDatasetForCheck(file.Get(), "/model/elements");
|
auto elements = OpenDatasetForCheck(file.Get(), "/model/elements");
|
||||||
RequireStringAttribute(elements.Get(), "formulation", "FESA-MITC4");
|
RequireStringAttribute(elements.Get(), "formulation", "FESA-MITC4");
|
||||||
RequireCompoundDataset(file.Get(), "/model/shell/materials",
|
RequireCompoundDataset(
|
||||||
static_cast<hsize_t>(domain.Materials().size()),
|
file.Get(), "/model/shell/materials",
|
||||||
{"internal_material_id", "name", "E", "nu"});
|
static_cast<hsize_t>(domain.LinearElasticMaterials().Size()),
|
||||||
|
{"internal_material_id", "name", "E", "nu"});
|
||||||
RequireCompoundDataset(
|
RequireCompoundDataset(
|
||||||
file.Get(), "/model/shell/sections",
|
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",
|
{"internal_section_id", "source_file", "source_line", "source_elset",
|
||||||
"material_internal_id", "thickness"});
|
"material_internal_id", "thickness"});
|
||||||
|
|
||||||
@@ -2114,7 +2122,7 @@ void SelfCheckFile(const std::filesystem::path& path, const Domain& domain,
|
|||||||
"BOTTOM,MIDDLE,TOP");
|
"BOTTOM,MIDDLE,TOP");
|
||||||
|
|
||||||
const hsize_t element_count =
|
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";
|
const std::string shell_root = std::string{kStepRoot} + "/element/shell";
|
||||||
RequireDoubleDataset(file.Get(), (shell_root + "/local_frame").c_str(),
|
RequireDoubleDataset(file.Get(), (shell_root + "/local_frame").c_str(),
|
||||||
{element_count, 4U, 3U, 3U}, "X,Y,Z", "1,1,1",
|
{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 {
|
} else {
|
||||||
RequireCompoundDataset(file.Get(), "/model/elements",
|
RequireCompoundDataset(file.Get(), "/model/elements",
|
||||||
static_cast<hsize_t>(domain.Elements().size()),
|
static_cast<hsize_t>(domain.BeamElements().Size()),
|
||||||
{"internal_element_id", "instance_name",
|
{"internal_element_id", "instance_name",
|
||||||
"source_label", "node_internal_ids", "local_axes"});
|
"source_label", "node_internal_ids", "local_axes"});
|
||||||
auto elements = OpenDatasetForCheck(file.Get(), "/model/elements");
|
auto elements = OpenDatasetForCheck(file.Get(), "/model/elements");
|
||||||
RequireStringAttribute(elements.Get(), "formulation",
|
RequireStringAttribute(elements.Get(), "formulation",
|
||||||
"B33-3D-Euler-Bernoulli");
|
"B33-3D-Euler-Bernoulli");
|
||||||
const std::vector<hsize_t> end_dimensions = {
|
const std::vector<hsize_t> end_dimensions = {
|
||||||
static_cast<hsize_t>(domain.Elements().size()), kEndpointCount,
|
static_cast<hsize_t>(domain.BeamElements().Size()), kEndpointCount,
|
||||||
kEndActionComponentCount};
|
kEndActionComponentCount};
|
||||||
const std::vector<hsize_t> generalized_dimensions = {
|
const std::vector<hsize_t> generalized_dimensions = {
|
||||||
static_cast<hsize_t>(domain.Elements().size()), kGaussPointCount,
|
static_cast<hsize_t>(domain.BeamElements().Size()), kGaussPointCount,
|
||||||
kGeneralizedComponentCount};
|
kGeneralizedComponentCount};
|
||||||
RequireDoubleDataset(
|
RequireDoubleDataset(
|
||||||
file.Get(), "/steps/Step-1/frames/0/element/end_force_local",
|
file.Get(), "/steps/Step-1/frames/0/element/end_force_local",
|
||||||
|
|||||||
+81
-13
@@ -1,5 +1,7 @@
|
|||||||
#include "fesa/model/domain.h"
|
#include "fesa/model/domain.h"
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <memory>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
namespace fesa {
|
namespace fesa {
|
||||||
@@ -12,25 +14,43 @@ const std::vector<Node>& Domain::Nodes() const noexcept {
|
|||||||
return definition_.nodes;
|
return definition_.nodes;
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::vector<EulerBeam3DDefinition>& Domain::Elements() const noexcept {
|
const DomainCollectionView<ElementDefinition>& Domain::Elements()
|
||||||
return definition_.elements;
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::vector<Mitc4ShellDefinition>& Domain::ShellElements()
|
|
||||||
const noexcept {
|
const noexcept {
|
||||||
return definition_.shell_elements;
|
return elements_view_;
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::vector<LinearElasticMaterial>& Domain::Materials() const noexcept {
|
const DomainCollectionView<EulerBeam3DDefinition>& Domain::BeamElements()
|
||||||
return definition_.materials;
|
const noexcept {
|
||||||
|
return beam_elements_view_;
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::vector<GeneralBeamSection>& Domain::Sections() const noexcept {
|
const DomainCollectionView<Mitc4ShellDefinition>& Domain::ShellElements()
|
||||||
return definition_.sections;
|
const noexcept {
|
||||||
|
return shell_elements_view_;
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::vector<ShellSection>& Domain::ShellSections() const noexcept {
|
const DomainCollectionView<Material>& Domain::Materials() const noexcept {
|
||||||
return definition_.shell_sections;
|
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()
|
const std::vector<ShellNodeInitialFrame>& Domain::ShellNodeInitialFrames()
|
||||||
@@ -63,6 +83,54 @@ const std::string& Domain::SourceContentIdentity() const noexcept {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Domain::Domain(ModelDefinition definition)
|
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
|
} // namespace fesa
|
||||||
|
|||||||
@@ -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];
|
const auto& element = domain.Elements()[index];
|
||||||
entries.push_back({SourceEntityKind::kElement,
|
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 (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,
|
|
||||||
static_cast<EntityIndex>(index), declaration_order++});
|
static_cast<EntityIndex>(index), declaration_order++});
|
||||||
}
|
}
|
||||||
for (const auto& set : domain.ElementSets()) {
|
for (const auto& set : domain.ElementSets()) {
|
||||||
for (const EntityIndex element_index : set.element_indices) {
|
for (const EntityIndex element_index : set.element_indices) {
|
||||||
SourceEntityId source_id{set.instance_name.value_or(""), 0, ""};
|
SourceEntityId source_id{set.instance_name.value_or(""), 0, ""};
|
||||||
if (domain.ShellElements().empty() &&
|
if (element_index < domain.Elements().Size()) {
|
||||||
element_index < domain.Elements().size()) {
|
source_id = domain.Elements()[element_index].SourceId();
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
entries.push_back({SourceEntityKind::kElement,
|
entries.push_back({SourceEntityKind::kElement,
|
||||||
set.instance_name.value_or(source_id.instance_name),
|
set.instance_name.value_or(source_id.instance_name),
|
||||||
|
|||||||
@@ -216,8 +216,8 @@ Status ValidateRecoveryInputs(const AnalysisModel& model,
|
|||||||
|
|
||||||
EntityIndex previous_element = 0U;
|
EntityIndex previous_element = 0U;
|
||||||
bool first_element = true;
|
bool first_element = true;
|
||||||
for (const EntityIndex element : model.ActiveElements()) {
|
for (const EntityIndex element : model.ActiveBeamElements()) {
|
||||||
if (element >= domain.Elements().size() ||
|
if (element >= domain.BeamElements().Size() ||
|
||||||
(!first_element && element <= previous_element)) {
|
(!first_element && element <= previous_element)) {
|
||||||
return RecoveryFailure(
|
return RecoveryFailure(
|
||||||
"invalid-recovery-entity", {domain.SourcePath(), 0U},
|
"invalid-recovery-entity", {domain.SourcePath(), 0U},
|
||||||
@@ -226,11 +226,11 @@ Status ValidateRecoveryInputs(const AnalysisModel& model,
|
|||||||
}
|
}
|
||||||
first_element = false;
|
first_element = false;
|
||||||
previous_element = element;
|
previous_element = element;
|
||||||
const auto& definition = domain.Elements()[element];
|
const auto& definition = domain.BeamElements()[element];
|
||||||
if (definition.node_indices[0U] >= domain.Nodes().size() ||
|
if (definition.node_indices[0U] >= domain.Nodes().size() ||
|
||||||
definition.node_indices[1U] >= domain.Nodes().size() ||
|
definition.node_indices[1U] >= domain.Nodes().size() ||
|
||||||
definition.material_index >= domain.Materials().size() ||
|
definition.material_index >= domain.LinearElasticMaterials().Size() ||
|
||||||
definition.section_index >= domain.Sections().size()) {
|
definition.section_index >= domain.Sections().Size()) {
|
||||||
return RecoveryFailure(
|
return RecoveryFailure(
|
||||||
"invalid-recovery-entity", definition.location,
|
"invalid-recovery-entity", definition.location,
|
||||||
definition.source_id.source_label_text,
|
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(
|
return RecoveryFailure(
|
||||||
"unsupported-mixed-element-model", {domain.SourcePath(), 0U},
|
"unsupported-mixed-element-model", {domain.SourcePath(), 0U},
|
||||||
"B33:FESA-MITC4",
|
"B33:FESA-MITC4",
|
||||||
"Result recovery does not support mixed beam and shell models.");
|
"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)())) {
|
static_cast<std::size_t>((std::numeric_limits<EntityIndex>::max)())) {
|
||||||
return RecoveryFailure("invalid-recovery-dimensions",
|
return RecoveryFailure("invalid-recovery-dimensions",
|
||||||
{domain.SourcePath(), 0U},
|
{domain.SourcePath(), 0U},
|
||||||
@@ -278,10 +278,10 @@ Status ValidateRecoveryInputs(const AnalysisModel& model,
|
|||||||
"stable element identities.");
|
"stable element identities.");
|
||||||
}
|
}
|
||||||
for (std::size_t element_order = 0U;
|
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];
|
const auto& definition = domain.ShellElements()[element_order];
|
||||||
if (definition.material_index >= domain.Materials().size() ||
|
if (definition.material_index >= domain.LinearElasticMaterials().Size() ||
|
||||||
definition.section_index >= domain.ShellSections().size()) {
|
definition.section_index >= domain.ShellSections().Size()) {
|
||||||
return RecoveryFailure("invalid-recovery-entity", definition.location,
|
return RecoveryFailure("invalid-recovery-entity", definition.location,
|
||||||
definition.source_id.source_label_text,
|
definition.source_id.source_label_text,
|
||||||
"Active shell material and section references "
|
"Active shell material and section references "
|
||||||
@@ -586,16 +586,16 @@ Status ResultRecovery::Recover(const AnalysisModel& model,
|
|||||||
std::vector<EndpointResultRow> endpoint_rows;
|
std::vector<EndpointResultRow> endpoint_rows;
|
||||||
std::vector<GaussResultRow> gauss_rows;
|
std::vector<GaussResultRow> gauss_rows;
|
||||||
std::vector<StressS11Row> stress_rows;
|
std::vector<StressS11Row> stress_rows;
|
||||||
endpoint_rows.reserve(model.ActiveElements().size() * 2U);
|
endpoint_rows.reserve(model.ActiveBeamElements().size() * 2U);
|
||||||
gauss_rows.reserve(model.ActiveElements().size() * 2U);
|
gauss_rows.reserve(model.ActiveBeamElements().size() * 2U);
|
||||||
const Domain& domain = model.GetDomain();
|
const Domain& domain = model.GetDomain();
|
||||||
for (const EntityIndex element_index : model.ActiveElements()) {
|
for (const EntityIndex element_index : model.ActiveBeamElements()) {
|
||||||
const auto& definition = domain.Elements()[element_index];
|
const auto& definition = domain.BeamElements()[element_index];
|
||||||
auto beam =
|
auto beam = EulerBeam3D::Create(
|
||||||
EulerBeam3D::Create(domain.Nodes()[definition.node_indices[0U]],
|
domain.Nodes()[definition.node_indices[0U]],
|
||||||
domain.Nodes()[definition.node_indices[1U]],
|
domain.Nodes()[definition.node_indices[1U]],
|
||||||
domain.Sections()[definition.section_index],
|
domain.Sections()[definition.section_index],
|
||||||
domain.Materials()[definition.material_index]);
|
domain.LinearElasticMaterials()[definition.material_index]);
|
||||||
if (!beam.HasValue()) {
|
if (!beam.HasValue()) {
|
||||||
return beam.GetStatus();
|
return beam.GetStatus();
|
||||||
}
|
}
|
||||||
@@ -649,8 +649,8 @@ Status ResultRecovery::Recover(const AnalysisModel& model,
|
|||||||
|
|
||||||
ShellStateCandidate shell_candidate{};
|
ShellStateCandidate shell_candidate{};
|
||||||
std::vector<EntityIndex> expected_shell_elements;
|
std::vector<EntityIndex> expected_shell_elements;
|
||||||
if (!domain.ShellElements().empty()) {
|
if (!domain.ShellElements().Empty()) {
|
||||||
if (domain.ShellElements().size() >
|
if (domain.ShellElements().Size() >
|
||||||
(std::numeric_limits<std::size_t>::max)() / kShellLocationCount) {
|
(std::numeric_limits<std::size_t>::max)() / kShellLocationCount) {
|
||||||
return RecoveryFailure(
|
return RecoveryFailure(
|
||||||
"invalid-recovery-dimensions", {domain.SourcePath(), 0U},
|
"invalid-recovery-dimensions", {domain.SourcePath(), 0U},
|
||||||
@@ -670,9 +670,9 @@ Status ResultRecovery::Recover(const AnalysisModel& model,
|
|||||||
directors_by_node[frame.node_index] = frame.director;
|
directors_by_node[frame.node_index] = frame.director;
|
||||||
}
|
}
|
||||||
|
|
||||||
shell_candidate.rows.reserve(domain.ShellElements().size() *
|
shell_candidate.rows.reserve(domain.ShellElements().Size() *
|
||||||
kShellLocationCount);
|
kShellLocationCount);
|
||||||
expected_shell_elements.reserve(domain.ShellElements().size());
|
expected_shell_elements.reserve(domain.ShellElements().Size());
|
||||||
constexpr std::array<ShellMidsurfaceLocation, kShellLocationCount>
|
constexpr std::array<ShellMidsurfaceLocation, kShellLocationCount>
|
||||||
locations{ShellMidsurfaceLocation::kGp1, ShellMidsurfaceLocation::kGp2,
|
locations{ShellMidsurfaceLocation::kGp1, ShellMidsurfaceLocation::kGp2,
|
||||||
ShellMidsurfaceLocation::kGp3, ShellMidsurfaceLocation::kGp4};
|
ShellMidsurfaceLocation::kGp3, ShellMidsurfaceLocation::kGp4};
|
||||||
@@ -681,7 +681,7 @@ Status ResultRecovery::Recover(const AnalysisModel& model,
|
|||||||
ShellSectionPosition::kTop};
|
ShellSectionPosition::kTop};
|
||||||
constexpr std::array<double, 3> zeta{-1.0, 0.0, 1.0};
|
constexpr std::array<double, 3> zeta{-1.0, 0.0, 1.0};
|
||||||
for (std::size_t element_order = 0U;
|
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 EntityIndex element_index = static_cast<EntityIndex>(element_order);
|
||||||
const auto& definition = domain.ShellElements()[element_order];
|
const auto& definition = domain.ShellElements()[element_order];
|
||||||
std::array<const Node*, 4> nodes{};
|
std::array<const Node*, 4> nodes{};
|
||||||
@@ -701,7 +701,7 @@ Status ResultRecovery::Recover(const AnalysisModel& model,
|
|||||||
|
|
||||||
auto shell = Mitc4Shell::Create(
|
auto shell = Mitc4Shell::Create(
|
||||||
nodes, directors, domain.ShellSections()[definition.section_index],
|
nodes, directors, domain.ShellSections()[definition.section_index],
|
||||||
domain.Materials()[definition.material_index]);
|
domain.LinearElasticMaterials()[definition.material_index]);
|
||||||
if (!shell.HasValue()) {
|
if (!shell.HasValue()) {
|
||||||
return shell.GetStatus();
|
return shell.GetStatus();
|
||||||
}
|
}
|
||||||
@@ -788,9 +788,9 @@ ResultRecovery::NormalizeSectionResultantsToNodeStations(
|
|||||||
"Node-station component tolerances must be finite and nonnegative.");
|
"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 ||
|
(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>>(
|
return RecoveryResultFailure<std::vector<NodeStationResultRow>>(
|
||||||
"invalid-node-station-shape", {domain.SourcePath(), 0U},
|
"invalid-node-station-shape", {domain.SourcePath(), 0U},
|
||||||
std::to_string(endpoint_rows.size()),
|
std::to_string(endpoint_rows.size()),
|
||||||
@@ -799,15 +799,16 @@ ResultRecovery::NormalizeSectionResultantsToNodeStations(
|
|||||||
|
|
||||||
std::vector<std::vector<const EndpointResultRow*>> rows_by_node(
|
std::vector<std::vector<const EndpointResultRow*>> rows_by_node(
|
||||||
domain.Nodes().size());
|
domain.Nodes().size());
|
||||||
for (std::size_t order = 0U; order < model.ActiveElements().size(); ++order) {
|
for (std::size_t order = 0U; order < model.ActiveBeamElements().size();
|
||||||
const EntityIndex element_index = model.ActiveElements()[order];
|
++order) {
|
||||||
if (element_index >= domain.Elements().size()) {
|
const EntityIndex element_index = model.ActiveBeamElements()[order];
|
||||||
|
if (element_index >= domain.BeamElements().Size()) {
|
||||||
return RecoveryResultFailure<std::vector<NodeStationResultRow>>(
|
return RecoveryResultFailure<std::vector<NodeStationResultRow>>(
|
||||||
"invalid-node-station-entity", {domain.SourcePath(), 0U},
|
"invalid-node-station-entity", {domain.SourcePath(), 0U},
|
||||||
std::to_string(element_index),
|
std::to_string(element_index),
|
||||||
"Every active station element must be a valid stable entity.");
|
"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) {
|
for (std::size_t endpoint = 0U; endpoint < 2U; ++endpoint) {
|
||||||
const auto& row = endpoint_rows[order * 2U + endpoint];
|
const auto& row = endpoint_rows[order * 2U + endpoint];
|
||||||
const EntityIndex node_index = definition.node_indices[endpoint];
|
const EntityIndex node_index = definition.node_indices[endpoint];
|
||||||
@@ -885,8 +886,8 @@ ResultRecovery::NormalizeSectionResultantsToNodeStations(
|
|||||||
"Interior station collapse requires exactly two unloaded endpoints.");
|
"Interior station collapse requires exactly two unloaded endpoints.");
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto& first_element = domain.Elements()[incident[0U]->element];
|
const auto& first_element = domain.BeamElements()[incident[0U]->element];
|
||||||
const auto& second_element = domain.Elements()[incident[1U]->element];
|
const auto& second_element = domain.BeamElements()[incident[1U]->element];
|
||||||
const bool chain_orientation =
|
const bool chain_orientation =
|
||||||
incident[0U]->endpoint != incident[1U]->endpoint &&
|
incident[0U]->endpoint != incident[1U]->endpoint &&
|
||||||
((incident[0U]->endpoint == 1 && incident[1U]->endpoint == 0) ||
|
((incident[0U]->endpoint == 1 && incident[1U]->endpoint == 0) ||
|
||||||
|
|||||||
@@ -307,7 +307,7 @@ class CapturingResultsWriter final : public fesa::ResultsWriter {
|
|||||||
const std::vector<fesa::Diagnostic>& diagnostics) override {
|
const std::vector<fesa::Diagnostic>& diagnostics) override {
|
||||||
output_path_ = output_path;
|
output_path_ = output_path;
|
||||||
node_count_ = domain.Nodes().size();
|
node_count_ = domain.Nodes().size();
|
||||||
shell_element_count_ = domain.ShellElements().size();
|
shell_element_count_ = domain.ShellElements().Size();
|
||||||
state_ = std::make_unique<fesa::AnalysisState>(state);
|
state_ = std::make_unique<fesa::AnalysisState>(state);
|
||||||
diagnostics_ = diagnostics;
|
diagnostics_ = diagnostics;
|
||||||
return fesa::Status::Ok();
|
return fesa::Status::Ok();
|
||||||
|
|||||||
@@ -815,7 +815,7 @@ HdfProjection ReadHdfProjection(const std::filesystem::path& results,
|
|||||||
projection.nodes = ReadNodeRows(file.Get());
|
projection.nodes = ReadNodeRows(file.Get());
|
||||||
projection.elements = ReadElementRows(file.Get());
|
projection.elements = ReadElementRows(file.Get());
|
||||||
if (projection.nodes.size() != domain.Nodes().size() ||
|
if (projection.nodes.size() != domain.Nodes().size() ||
|
||||||
projection.elements.size() != domain.Elements().size()) {
|
projection.elements.size() != domain.BeamElements().Size()) {
|
||||||
Fail("schema-mismatch",
|
Fail("schema-mismatch",
|
||||||
"HDF5 model identity counts do not match the input.");
|
"HDF5 model identity counts do not match the input.");
|
||||||
}
|
}
|
||||||
@@ -834,7 +834,7 @@ HdfProjection ReadHdfProjection(const std::filesystem::path& results,
|
|||||||
for (std::size_t element = 0U; element < projection.elements.size();
|
for (std::size_t element = 0U; element < projection.elements.size();
|
||||||
++element) {
|
++element) {
|
||||||
const auto& actual = projection.elements[element];
|
const auto& actual = projection.elements[element];
|
||||||
const auto& expected = domain.Elements()[element];
|
const auto& expected = domain.BeamElements()[element];
|
||||||
if (actual.internal_element_id != element ||
|
if (actual.internal_element_id != element ||
|
||||||
actual.instance_name != expected.source_id.instance_name ||
|
actual.instance_name != expected.source_id.instance_name ||
|
||||||
actual.source_element_label != expected.source_id.source_label ||
|
actual.source_element_label != expected.source_id.source_label ||
|
||||||
|
|||||||
@@ -77,6 +77,19 @@ fesa::ModelDefinition MakeDefinition() {
|
|||||||
return definition;
|
return definition;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fesa::ModelDefinition MakeMixedDefinition() {
|
||||||
|
auto definition = MakeDefinition();
|
||||||
|
const auto source = definition.source_path;
|
||||||
|
definition.shell_sections = {{"ShellSection", 0.01, 1U, {source, 35U}}};
|
||||||
|
definition.shell_elements = {{{"Shell-1", 5, "5"},
|
||||||
|
fesa::ShellSourceElementType::kS4,
|
||||||
|
{0U, 1U, 2U, 3U},
|
||||||
|
1U,
|
||||||
|
0U,
|
||||||
|
{source, 45U}}};
|
||||||
|
return definition;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
TEST(AnalysisModel, ClassifiesActiveEntitiesInStableOrder) {
|
TEST(AnalysisModel, ClassifiesActiveEntitiesInStableOrder) {
|
||||||
@@ -102,9 +115,9 @@ TEST(AnalysisModel, ReferencesWithoutCopyingOrMutatingDomain) {
|
|||||||
auto domain_result = fesa::Domain::Create(MakeDefinition());
|
auto domain_result = fesa::Domain::Create(MakeDefinition());
|
||||||
ASSERT_TRUE(domain_result.HasValue());
|
ASSERT_TRUE(domain_result.HasValue());
|
||||||
const fesa::Domain& domain = domain_result.Value();
|
const fesa::Domain& domain = domain_result.Value();
|
||||||
const auto* const element_address = domain.Elements().data();
|
const auto* const element_address = &domain.Elements()[0U];
|
||||||
const auto* const material_address = domain.Materials().data();
|
const auto* const material_address = &domain.Materials()[0U];
|
||||||
const auto* const section_address = domain.Sections().data();
|
const auto* const section_address = &domain.Sections()[0U];
|
||||||
const std::string step_name = domain.Steps()[0].name;
|
const std::string step_name = domain.Steps()[0].name;
|
||||||
const double first_load_magnitude = domain.Steps()[0].loads[0].magnitude;
|
const double first_load_magnitude = domain.Steps()[0].loads[0].magnitude;
|
||||||
|
|
||||||
@@ -114,9 +127,9 @@ TEST(AnalysisModel, ReferencesWithoutCopyingOrMutatingDomain) {
|
|||||||
|
|
||||||
EXPECT_EQ(&model.GetDomain(), &domain);
|
EXPECT_EQ(&model.GetDomain(), &domain);
|
||||||
EXPECT_EQ(&model.Step(), &domain.Steps()[0]);
|
EXPECT_EQ(&model.Step(), &domain.Steps()[0]);
|
||||||
EXPECT_EQ(model.GetDomain().Elements().data(), element_address);
|
EXPECT_EQ(&model.GetDomain().Elements()[0U], element_address);
|
||||||
EXPECT_EQ(model.GetDomain().Materials().data(), material_address);
|
EXPECT_EQ(&model.GetDomain().Materials()[0U], material_address);
|
||||||
EXPECT_EQ(model.GetDomain().Sections().data(), section_address);
|
EXPECT_EQ(&model.GetDomain().Sections()[0U], section_address);
|
||||||
EXPECT_EQ(&model.GetDomain().Elements()[model.ActiveElements()[1]],
|
EXPECT_EQ(&model.GetDomain().Elements()[model.ActiveElements()[1]],
|
||||||
&domain.Elements()[1]);
|
&domain.Elements()[1]);
|
||||||
EXPECT_EQ(&model.GetDomain().Materials()[model.ActiveMaterials()[2]],
|
EXPECT_EQ(&model.GetDomain().Materials()[model.ActiveMaterials()[2]],
|
||||||
@@ -127,6 +140,31 @@ TEST(AnalysisModel, ReferencesWithoutCopyingOrMutatingDomain) {
|
|||||||
EXPECT_DOUBLE_EQ(domain.Steps()[0].loads[0].magnitude, first_load_magnitude);
|
EXPECT_DOUBLE_EQ(domain.Steps()[0].loads[0].magnitude, first_load_magnitude);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// C-MODEL-002
|
||||||
|
TEST(AnalysisModel, KeepsNonOwningMixedDefinitionAndPropertyIndices) {
|
||||||
|
auto domain_result = fesa::Domain::Create(MakeMixedDefinition());
|
||||||
|
ASSERT_TRUE(domain_result.HasValue());
|
||||||
|
const fesa::Domain& domain = domain_result.Value();
|
||||||
|
|
||||||
|
auto model_result = fesa::AnalysisModel::Create(domain);
|
||||||
|
ASSERT_TRUE(model_result.HasValue());
|
||||||
|
const fesa::AnalysisModel& model = model_result.Value();
|
||||||
|
|
||||||
|
EXPECT_EQ(&model.GetDomain(), &domain);
|
||||||
|
EXPECT_EQ(model.ActiveElements(),
|
||||||
|
(std::vector<fesa::EntityIndex>{0U, 1U, 2U, 3U, 4U}));
|
||||||
|
EXPECT_EQ(model.ActiveMaterials(),
|
||||||
|
(std::vector<fesa::EntityIndex>{0U, 1U, 2U}));
|
||||||
|
EXPECT_EQ(model.ActiveProperties(),
|
||||||
|
(std::vector<fesa::EntityIndex>{0U, 1U, 2U, 4U}));
|
||||||
|
EXPECT_EQ(&model.GetDomain().Elements()[model.ActiveElements()[4U]],
|
||||||
|
&domain.Elements()[4U]);
|
||||||
|
EXPECT_EQ(&model.GetDomain().Materials()[model.ActiveMaterials()[1U]],
|
||||||
|
&domain.Materials()[1U]);
|
||||||
|
EXPECT_EQ(&model.GetDomain().Properties()[model.ActiveProperties()[3U]],
|
||||||
|
&domain.Properties()[4U]);
|
||||||
|
}
|
||||||
|
|
||||||
TEST(AnalysisModel, RejectsMissingOrMultipleStep) {
|
TEST(AnalysisModel, RejectsMissingOrMultipleStep) {
|
||||||
auto missing_definition = MakeDefinition();
|
auto missing_definition = MakeDefinition();
|
||||||
missing_definition.steps.clear();
|
missing_definition.steps.clear();
|
||||||
|
|||||||
@@ -103,7 +103,7 @@ fesa::ModelDefinition MakeShellDefinition(
|
|||||||
|
|
||||||
fesa::Result<fesa::Mitc4Stiffness> DirectShellStiffness(
|
fesa::Result<fesa::Mitc4Stiffness> DirectShellStiffness(
|
||||||
const fesa::Domain& domain, const fesa::EntityIndex element_index) {
|
const fesa::Domain& domain, const fesa::EntityIndex element_index) {
|
||||||
const auto& definition = domain.ShellElements().at(element_index);
|
const auto& definition = domain.ShellElements().At(element_index);
|
||||||
std::array<const fesa::Node*, 4> nodes{};
|
std::array<const fesa::Node*, 4> nodes{};
|
||||||
std::array<std::array<double, 3>, 4> directors{};
|
std::array<std::array<double, 3>, 4> directors{};
|
||||||
for (std::size_t node = 0U; node < definition.node_indices.size(); ++node) {
|
for (std::size_t node = 0U; node < definition.node_indices.size(); ++node) {
|
||||||
@@ -112,8 +112,8 @@ fesa::Result<fesa::Mitc4Stiffness> DirectShellStiffness(
|
|||||||
directors[node] = domain.ShellNodeInitialFrames().at(node_index).director;
|
directors[node] = domain.ShellNodeInitialFrames().at(node_index).director;
|
||||||
}
|
}
|
||||||
auto shell = fesa::Mitc4Shell::Create(
|
auto shell = fesa::Mitc4Shell::Create(
|
||||||
nodes, directors, domain.ShellSections().at(definition.section_index),
|
nodes, directors, domain.ShellSections().At(definition.section_index),
|
||||||
domain.Materials().at(definition.material_index));
|
domain.LinearElasticMaterials().At(definition.material_index));
|
||||||
if (!shell.HasValue()) {
|
if (!shell.HasValue()) {
|
||||||
return fesa::Result<fesa::Mitc4Stiffness>::Failure(shell.GetStatus());
|
return fesa::Result<fesa::Mitc4Stiffness>::Failure(shell.GetStatus());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "fesa/math/vector3.h"
|
#include "fesa/math/vector3.h"
|
||||||
|
#include "fesa/model/model_types.h"
|
||||||
|
|
||||||
namespace fesa {
|
namespace fesa {
|
||||||
namespace {
|
namespace {
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "fesa/math/vector3.h"
|
#include "fesa/math/vector3.h"
|
||||||
|
#include "fesa/model/model_types.h"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include "fesa/elements/element_definition.h"
|
||||||
#include "fesa/io/abaqus/input_reader.h"
|
#include "fesa/io/abaqus/input_reader.h"
|
||||||
#include "fesa/math/vector3.h"
|
#include "fesa/math/vector3.h"
|
||||||
|
|
||||||
@@ -242,6 +243,7 @@ RootAssembly, 6, -12.5
|
|||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
// C-DUP-002
|
// C-DUP-002
|
||||||
|
// C-MODEL-002
|
||||||
TEST(InpDomainMapping, MapsEverySupportedKeywordAndLegacyDeck) {
|
TEST(InpDomainMapping, MapsEverySupportedKeywordAndLegacyDeck) {
|
||||||
auto result = MapText("supported-inventory", SupportedInventoryDeck(true));
|
auto result = MapText("supported-inventory", SupportedInventoryDeck(true));
|
||||||
ASSERT_TRUE(result.HasValue());
|
ASSERT_TRUE(result.HasValue());
|
||||||
@@ -254,17 +256,23 @@ TEST(InpDomainMapping, MapsEverySupportedKeywordAndLegacyDeck) {
|
|||||||
EXPECT_EQ(domain.Nodes()[1].source_id.source_label_text, "0002");
|
EXPECT_EQ(domain.Nodes()[1].source_id.source_label_text, "0002");
|
||||||
EXPECT_DOUBLE_EQ(domain.Nodes()[1].coordinates[0], 2.0);
|
EXPECT_DOUBLE_EQ(domain.Nodes()[1].coordinates[0], 2.0);
|
||||||
|
|
||||||
ASSERT_EQ(domain.Elements().size(), 1U);
|
ASSERT_EQ(domain.Elements().Size(), 1U);
|
||||||
EXPECT_EQ(domain.Elements()[0].source_id.source_label_text, "0007");
|
const fesa::ElementDefinition& element = domain.Elements()[0U];
|
||||||
EXPECT_EQ(domain.Elements()[0].node_indices[0], 0U);
|
EXPECT_EQ(element.Kind(), fesa::ElementDefinitionKind::kEulerBeam3D);
|
||||||
EXPECT_EQ(domain.Elements()[0].node_indices[1], 1U);
|
EXPECT_EQ(element.SourceId().source_label_text, "0007");
|
||||||
|
EXPECT_EQ(element.SourceElementType(), "B33");
|
||||||
|
EXPECT_EQ(element.NodeIndices(), (std::vector<fesa::EntityIndex>{0U, 1U}));
|
||||||
|
EXPECT_EQ(&element, &domain.BeamElements()[0U]);
|
||||||
|
|
||||||
ASSERT_EQ(domain.Materials().size(), 1U);
|
ASSERT_EQ(domain.Materials().Size(), 1U);
|
||||||
EXPECT_EQ(domain.Materials()[0].name, "Steel");
|
EXPECT_EQ(domain.Materials()[0U].Kind(),
|
||||||
EXPECT_DOUBLE_EQ(domain.Materials()[0].youngs_modulus, 210000.0);
|
fesa::MaterialKind::kIsotropicLinearElastic);
|
||||||
EXPECT_DOUBLE_EQ(domain.Materials()[0].poisson_ratio, 0.75);
|
EXPECT_EQ(domain.LinearElasticMaterials()[0U].name, "Steel");
|
||||||
|
EXPECT_DOUBLE_EQ(domain.LinearElasticMaterials()[0U].youngs_modulus,
|
||||||
|
210000.0);
|
||||||
|
EXPECT_DOUBLE_EQ(domain.LinearElasticMaterials()[0U].poisson_ratio, 0.75);
|
||||||
|
|
||||||
ASSERT_EQ(domain.Sections().size(), 1U);
|
ASSERT_EQ(domain.Sections().Size(), 1U);
|
||||||
const auto& section = domain.Sections()[0];
|
const auto& section = domain.Sections()[0];
|
||||||
EXPECT_DOUBLE_EQ(section.area, 2.0);
|
EXPECT_DOUBLE_EQ(section.area, 2.0);
|
||||||
EXPECT_DOUBLE_EQ(section.i11, 3.0);
|
EXPECT_DOUBLE_EQ(section.i11, 3.0);
|
||||||
@@ -274,8 +282,8 @@ TEST(InpDomainMapping, MapsEverySupportedKeywordAndLegacyDeck) {
|
|||||||
EXPECT_EQ(section.first_axis, (std::array<double, 3>{0.0, 1.0, 0.0}));
|
EXPECT_EQ(section.first_axis, (std::array<double, 3>{0.0, 1.0, 0.0}));
|
||||||
EXPECT_EQ(section.section_points,
|
EXPECT_EQ(section.section_points,
|
||||||
(std::vector<std::array<double, 2>>{{-0.5, 0.25}, {0.5, -0.25}}));
|
(std::vector<std::array<double, 2>>{{-0.5, 0.25}, {0.5, -0.25}}));
|
||||||
EXPECT_EQ(domain.Elements()[0].material_index, 0U);
|
EXPECT_EQ(domain.BeamElements()[0U].material_index, 0U);
|
||||||
EXPECT_EQ(domain.Elements()[0].section_index, 0U);
|
EXPECT_EQ(domain.BeamElements()[0U].section_index, 0U);
|
||||||
|
|
||||||
ASSERT_EQ(domain.Steps().size(), 1U);
|
ASSERT_EQ(domain.Steps().size(), 1U);
|
||||||
const auto& step = domain.Steps()[0];
|
const auto& step = domain.Steps()[0];
|
||||||
@@ -305,9 +313,9 @@ TEST(InpDomainMapping, MapsEverySupportedKeywordAndLegacyDeck) {
|
|||||||
auto legacy = fesa::AbaqusDomainMapper{}.Map(parsed_legacy.Value());
|
auto legacy = fesa::AbaqusDomainMapper{}.Map(parsed_legacy.Value());
|
||||||
ASSERT_TRUE(legacy.HasValue());
|
ASSERT_TRUE(legacy.HasValue());
|
||||||
EXPECT_EQ(legacy.Value().Nodes().size(), 11U);
|
EXPECT_EQ(legacy.Value().Nodes().size(), 11U);
|
||||||
EXPECT_EQ(legacy.Value().Elements().size(), 10U);
|
EXPECT_EQ(legacy.Value().Elements().Size(), 10U);
|
||||||
EXPECT_EQ(legacy.Value().Materials().size(), 1U);
|
EXPECT_EQ(legacy.Value().Materials().Size(), 1U);
|
||||||
EXPECT_EQ(legacy.Value().Sections().size(), 1U);
|
EXPECT_EQ(legacy.Value().Sections().Size(), 1U);
|
||||||
EXPECT_EQ(legacy.Value().Steps().size(), 1U);
|
EXPECT_EQ(legacy.Value().Steps().size(), 1U);
|
||||||
EXPECT_EQ(legacy.Value().Warnings().size(), 7U);
|
EXPECT_EQ(legacy.Value().Warnings().size(), 7U);
|
||||||
EXPECT_EQ(ReadExactBytes(legacy_path), bytes_before);
|
EXPECT_EQ(ReadExactBytes(legacy_path), bytes_before);
|
||||||
@@ -367,12 +375,12 @@ OnlySecond, 2, 5.
|
|||||||
EXPECT_EQ(domain.Nodes()[3].source_id.instance_name, "Second");
|
EXPECT_EQ(domain.Nodes()[3].source_id.instance_name, "Second");
|
||||||
EXPECT_EQ(domain.Nodes()[3].source_id.source_label, 20);
|
EXPECT_EQ(domain.Nodes()[3].source_id.source_label, 20);
|
||||||
|
|
||||||
ASSERT_EQ(domain.Elements().size(), 2U);
|
ASSERT_EQ(domain.BeamElements().Size(), 2U);
|
||||||
EXPECT_EQ(domain.Elements()[0].source_id.instance_name, "First");
|
EXPECT_EQ(domain.BeamElements()[0].source_id.instance_name, "First");
|
||||||
EXPECT_EQ(domain.Elements()[0].node_indices,
|
EXPECT_EQ(domain.BeamElements()[0].node_indices,
|
||||||
(std::array<fesa::EntityIndex, 2>{0U, 1U}));
|
(std::array<fesa::EntityIndex, 2>{0U, 1U}));
|
||||||
EXPECT_EQ(domain.Elements()[1].source_id.instance_name, "Second");
|
EXPECT_EQ(domain.BeamElements()[1].source_id.instance_name, "Second");
|
||||||
EXPECT_EQ(domain.Elements()[1].node_indices,
|
EXPECT_EQ(domain.BeamElements()[1].node_indices,
|
||||||
(std::array<fesa::EntityIndex, 2>{2U, 3U}));
|
(std::array<fesa::EntityIndex, 2>{2U, 3U}));
|
||||||
|
|
||||||
ASSERT_EQ(domain.NodeSets().size(), 3U);
|
ASSERT_EQ(domain.NodeSets().size(), 3U);
|
||||||
@@ -525,12 +533,12 @@ TEST(InpDomainMapping, NoOpAllowlistWarnsWithoutSemanticEffect) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
EXPECT_EQ(with_no_ops.Value().Nodes().size(), plain.Value().Nodes().size());
|
EXPECT_EQ(with_no_ops.Value().Nodes().size(), plain.Value().Nodes().size());
|
||||||
EXPECT_EQ(with_no_ops.Value().Elements().size(),
|
EXPECT_EQ(with_no_ops.Value().Elements().Size(),
|
||||||
plain.Value().Elements().size());
|
plain.Value().Elements().Size());
|
||||||
EXPECT_EQ(with_no_ops.Value().Materials().size(),
|
EXPECT_EQ(with_no_ops.Value().Materials().Size(),
|
||||||
plain.Value().Materials().size());
|
plain.Value().Materials().Size());
|
||||||
EXPECT_EQ(with_no_ops.Value().Sections().size(),
|
EXPECT_EQ(with_no_ops.Value().Sections().Size(),
|
||||||
plain.Value().Sections().size());
|
plain.Value().Sections().Size());
|
||||||
EXPECT_EQ(with_no_ops.Value().NodeSets().size(),
|
EXPECT_EQ(with_no_ops.Value().NodeSets().size(),
|
||||||
plain.Value().NodeSets().size());
|
plain.Value().NodeSets().size());
|
||||||
EXPECT_EQ(with_no_ops.Value().ElementSets().size(),
|
EXPECT_EQ(with_no_ops.Value().ElementSets().size(),
|
||||||
@@ -543,13 +551,20 @@ TEST(InpDomainMapping, NoOpAllowlistWarnsWithoutSemanticEffect) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// MITC4-MAP-001
|
// MITC4-MAP-001
|
||||||
|
// C-MODEL-002
|
||||||
TEST(InpDomainMapping, MapsS4AndS4rThroughOneMitc4Identity) {
|
TEST(InpDomainMapping, MapsS4AndS4rThroughOneMitc4Identity) {
|
||||||
auto result = MapText("mitc4-map-001", ShellDeck());
|
auto result = MapText("mitc4-map-001", ShellDeck());
|
||||||
|
|
||||||
ASSERT_TRUE(result.HasValue());
|
ASSERT_TRUE(result.HasValue());
|
||||||
const fesa::Domain& domain = result.Value();
|
const fesa::Domain& domain = result.Value();
|
||||||
EXPECT_TRUE(domain.Elements().empty());
|
EXPECT_TRUE(domain.BeamElements().Empty());
|
||||||
ASSERT_EQ(domain.ShellElements().size(), 4U);
|
ASSERT_EQ(domain.Elements().Size(), 4U);
|
||||||
|
EXPECT_EQ(domain.Elements()[0U].Kind(),
|
||||||
|
fesa::ElementDefinitionKind::kMitc4Shell);
|
||||||
|
EXPECT_EQ(domain.Elements()[0U].SourceElementType(), "S4");
|
||||||
|
EXPECT_EQ(domain.Elements()[1U].SourceElementType(), "S4R");
|
||||||
|
EXPECT_EQ(&domain.Elements()[0U], &domain.ShellElements()[0U]);
|
||||||
|
ASSERT_EQ(domain.ShellElements().Size(), 4U);
|
||||||
EXPECT_EQ(domain.ShellElements()[0].source_id.instance_name, "First");
|
EXPECT_EQ(domain.ShellElements()[0].source_id.instance_name, "First");
|
||||||
EXPECT_EQ(domain.ShellElements()[0].source_id.source_label_text, "0010");
|
EXPECT_EQ(domain.ShellElements()[0].source_id.source_label_text, "0010");
|
||||||
EXPECT_EQ(domain.ShellElements()[0].source_type,
|
EXPECT_EQ(domain.ShellElements()[0].source_type,
|
||||||
@@ -573,7 +588,7 @@ TEST(InpDomainMapping, MapsS4AndS4rThroughOneMitc4Identity) {
|
|||||||
EXPECT_EQ(domain.ShellElements()[3].source_id.instance_name, "Second");
|
EXPECT_EQ(domain.ShellElements()[3].source_id.instance_name, "Second");
|
||||||
EXPECT_EQ(fesa::kMitc4InternalFormulation, std::string_view{"FESA-MITC4"});
|
EXPECT_EQ(fesa::kMitc4InternalFormulation, std::string_view{"FESA-MITC4"});
|
||||||
|
|
||||||
ASSERT_EQ(domain.ShellSections().size(), 2U);
|
ASSERT_EQ(domain.ShellSections().Size(), 2U);
|
||||||
EXPECT_EQ(domain.ShellSections()[0].name, "ShellS4");
|
EXPECT_EQ(domain.ShellSections()[0].name, "ShellS4");
|
||||||
EXPECT_DOUBLE_EQ(domain.ShellSections()[0].thickness, 0.1);
|
EXPECT_DOUBLE_EQ(domain.ShellSections()[0].thickness, 0.1);
|
||||||
EXPECT_EQ(domain.ShellSections()[0].material_index, 0U);
|
EXPECT_EQ(domain.ShellSections()[0].material_index, 0U);
|
||||||
|
|||||||
@@ -8,6 +8,8 @@
|
|||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include "fesa/elements/element_definition.h"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
fesa::ModelDefinition MakeOwnedDefinition() {
|
fesa::ModelDefinition MakeOwnedDefinition() {
|
||||||
@@ -63,6 +65,90 @@ fesa::ModelDefinition MakeOwnedDefinition() {
|
|||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
// C-MODEL-002
|
||||||
|
TEST(ElementDefinition, DomainOwnsMixedDefinitionsInStableBaseOrder) {
|
||||||
|
static_assert(std::has_virtual_destructor_v<fesa::ElementDefinition>);
|
||||||
|
static_assert(!std::is_copy_constructible_v<fesa::Domain>);
|
||||||
|
static_assert(!std::is_copy_assignable_v<fesa::Domain>);
|
||||||
|
static_assert(std::is_move_constructible_v<fesa::Domain>);
|
||||||
|
static_assert(std::is_move_assignable_v<fesa::Domain>);
|
||||||
|
|
||||||
|
fesa::ModelDefinition definition{};
|
||||||
|
definition.source_path = "models/mixed-owned.inp";
|
||||||
|
definition.source_content_identity = "fnv1a64:mixed";
|
||||||
|
definition.materials = {
|
||||||
|
{"Steel", 210.0e9, 0.3, {definition.source_path, 30U}}};
|
||||||
|
definition.sections = {{"BeamSection",
|
||||||
|
0.02,
|
||||||
|
1.0e-5,
|
||||||
|
0.0,
|
||||||
|
2.0e-5,
|
||||||
|
5.0e-6,
|
||||||
|
{0.0, 1.0, 0.0},
|
||||||
|
{},
|
||||||
|
{definition.source_path, 40U}}};
|
||||||
|
definition.shell_sections = {
|
||||||
|
{"ShellSection", 0.01, 0U, {definition.source_path, 41U}}};
|
||||||
|
definition.elements = {
|
||||||
|
{{"Beam-1", 7, "0007"}, {0U, 1U}, 0U, 0U, {definition.source_path, 20U}}};
|
||||||
|
definition.shell_elements = {{{"Shell-1", 9, "0009"},
|
||||||
|
fesa::ShellSourceElementType::kS4r,
|
||||||
|
{2U, 3U, 4U, 5U},
|
||||||
|
0U,
|
||||||
|
0U,
|
||||||
|
{definition.source_path, 21U}}};
|
||||||
|
|
||||||
|
auto result = fesa::Domain::Create(definition);
|
||||||
|
ASSERT_TRUE(result.HasValue());
|
||||||
|
fesa::Domain domain = std::move(result.Value());
|
||||||
|
|
||||||
|
static_assert(std::is_same_v<
|
||||||
|
decltype(std::declval<const fesa::Domain&>().Elements()[0U]),
|
||||||
|
const fesa::ElementDefinition&>);
|
||||||
|
static_assert(std::is_same_v<
|
||||||
|
decltype(std::declval<const fesa::Domain&>().Materials()[0U]),
|
||||||
|
const fesa::Material&>);
|
||||||
|
static_assert(std::is_same_v<
|
||||||
|
decltype(std::declval<const fesa::Domain&>().Properties()[0U]),
|
||||||
|
const fesa::ElementProperty&>);
|
||||||
|
|
||||||
|
ASSERT_EQ(domain.Elements().Size(), 2U);
|
||||||
|
const fesa::ElementDefinition& beam = domain.Elements()[0U];
|
||||||
|
EXPECT_EQ(beam.Kind(), fesa::ElementDefinitionKind::kEulerBeam3D);
|
||||||
|
EXPECT_EQ(beam.SourceId().source_label_text, "0007");
|
||||||
|
EXPECT_EQ(beam.SourceElementType(), "B33");
|
||||||
|
EXPECT_EQ(beam.NodeIndices(), (std::vector<fesa::EntityIndex>{0U, 1U}));
|
||||||
|
EXPECT_EQ(beam.MaterialIndex(), fesa::EntityIndex{0U});
|
||||||
|
EXPECT_EQ(beam.PropertyIndex(), fesa::EntityIndex{0U});
|
||||||
|
|
||||||
|
const fesa::ElementDefinition& shell = domain.Elements()[1U];
|
||||||
|
EXPECT_EQ(shell.Kind(), fesa::ElementDefinitionKind::kMitc4Shell);
|
||||||
|
EXPECT_EQ(shell.SourceId().source_label_text, "0009");
|
||||||
|
EXPECT_EQ(shell.SourceElementType(), "S4R");
|
||||||
|
EXPECT_EQ(shell.NodeIndices(),
|
||||||
|
(std::vector<fesa::EntityIndex>{2U, 3U, 4U, 5U}));
|
||||||
|
EXPECT_EQ(shell.MaterialIndex(), fesa::EntityIndex{0U});
|
||||||
|
EXPECT_EQ(shell.PropertyIndex(), fesa::EntityIndex{1U});
|
||||||
|
|
||||||
|
ASSERT_EQ(domain.BeamElements().Size(), 1U);
|
||||||
|
ASSERT_EQ(domain.ShellElements().Size(), 1U);
|
||||||
|
EXPECT_EQ(&domain.BeamElements()[0U], &beam);
|
||||||
|
EXPECT_EQ(&domain.ShellElements()[0U], &shell);
|
||||||
|
|
||||||
|
ASSERT_EQ(domain.Materials().Size(), 1U);
|
||||||
|
EXPECT_EQ(domain.Materials()[0U].Kind(),
|
||||||
|
fesa::MaterialKind::kIsotropicLinearElastic);
|
||||||
|
EXPECT_EQ(&domain.LinearElasticMaterials()[0U], &domain.Materials()[0U]);
|
||||||
|
|
||||||
|
ASSERT_EQ(domain.Properties().Size(), 2U);
|
||||||
|
EXPECT_EQ(domain.Properties()[0U].Kind(),
|
||||||
|
fesa::ElementPropertyKind::kGeneralBeamSection);
|
||||||
|
EXPECT_EQ(domain.Properties()[1U].Kind(),
|
||||||
|
fesa::ElementPropertyKind::kShellSection);
|
||||||
|
EXPECT_EQ(&domain.Sections()[0U], &domain.Properties()[0U]);
|
||||||
|
EXPECT_EQ(&domain.ShellSections()[0U], &domain.Properties()[1U]);
|
||||||
|
}
|
||||||
|
|
||||||
TEST(DomainModel, ImmutableOwnershipPreservesStableOrder) {
|
TEST(DomainModel, ImmutableOwnershipPreservesStableOrder) {
|
||||||
auto definition = MakeOwnedDefinition();
|
auto definition = MakeOwnedDefinition();
|
||||||
auto result = fesa::Domain::Create(definition);
|
auto result = fesa::Domain::Create(definition);
|
||||||
@@ -81,9 +167,9 @@ TEST(DomainModel, ImmutableOwnershipPreservesStableOrder) {
|
|||||||
static_assert(
|
static_assert(
|
||||||
std::is_same_v<decltype(std::declval<const fesa::Domain&>().Nodes()),
|
std::is_same_v<decltype(std::declval<const fesa::Domain&>().Nodes()),
|
||||||
const std::vector<fesa::Node>&>);
|
const std::vector<fesa::Node>&>);
|
||||||
static_assert(
|
static_assert(std::is_same_v<
|
||||||
std::is_same_v<decltype(std::declval<const fesa::Domain&>().Elements()),
|
decltype(std::declval<const fesa::Domain&>().Elements()),
|
||||||
const std::vector<fesa::EulerBeam3DDefinition>&>);
|
const fesa::DomainCollectionView<fesa::ElementDefinition>&>);
|
||||||
|
|
||||||
EXPECT_EQ(domain.SourcePath(), std::filesystem::path{"models/owned.inp"});
|
EXPECT_EQ(domain.SourcePath(), std::filesystem::path{"models/owned.inp"});
|
||||||
EXPECT_EQ(domain.SourceContentIdentity(), "fnv1a64:fedcba9876543210");
|
EXPECT_EQ(domain.SourceContentIdentity(), "fnv1a64:fedcba9876543210");
|
||||||
@@ -94,11 +180,11 @@ TEST(DomainModel, ImmutableOwnershipPreservesStableOrder) {
|
|||||||
EXPECT_EQ(domain.Nodes()[1].source_id.source_label, 10);
|
EXPECT_EQ(domain.Nodes()[1].source_id.source_label, 10);
|
||||||
EXPECT_EQ(domain.Nodes().data(), first_node_address);
|
EXPECT_EQ(domain.Nodes().data(), first_node_address);
|
||||||
|
|
||||||
ASSERT_EQ(domain.Elements().size(), 1U);
|
ASSERT_EQ(domain.BeamElements().Size(), 1U);
|
||||||
EXPECT_EQ(domain.Elements()[0].node_indices[0], 1U);
|
EXPECT_EQ(domain.BeamElements()[0].node_indices[0], 1U);
|
||||||
EXPECT_EQ(domain.Elements()[0].node_indices[1], 0U);
|
EXPECT_EQ(domain.BeamElements()[0].node_indices[1], 0U);
|
||||||
ASSERT_EQ(domain.Materials().size(), 1U);
|
ASSERT_EQ(domain.Materials().Size(), 1U);
|
||||||
ASSERT_EQ(domain.Sections().size(), 1U);
|
ASSERT_EQ(domain.Sections().Size(), 1U);
|
||||||
EXPECT_DOUBLE_EQ(domain.Sections()[0].section_points[0][0], -0.1);
|
EXPECT_DOUBLE_EQ(domain.Sections()[0].section_points[0][0], -0.1);
|
||||||
ASSERT_EQ(domain.NodeSets().size(), 1U);
|
ASSERT_EQ(domain.NodeSets().size(), 1U);
|
||||||
EXPECT_EQ(domain.NodeSets()[0].node_indices[0], 0U);
|
EXPECT_EQ(domain.NodeSets()[0].node_indices[0], 0U);
|
||||||
@@ -189,13 +275,13 @@ TEST(DomainModel, MultipleIdentityInstancesDoNotMerge) {
|
|||||||
EXPECT_NE(domain.Nodes()[0].source_id.instance_name,
|
EXPECT_NE(domain.Nodes()[0].source_id.instance_name,
|
||||||
domain.Nodes()[2].source_id.instance_name);
|
domain.Nodes()[2].source_id.instance_name);
|
||||||
|
|
||||||
ASSERT_EQ(domain.Elements().size(), 2U);
|
ASSERT_EQ(domain.BeamElements().Size(), 2U);
|
||||||
EXPECT_EQ(domain.Elements()[0].source_id.source_label, 1);
|
EXPECT_EQ(domain.BeamElements()[0].source_id.source_label, 1);
|
||||||
EXPECT_EQ(domain.Elements()[1].source_id.source_label, 1);
|
EXPECT_EQ(domain.BeamElements()[1].source_id.source_label, 1);
|
||||||
EXPECT_EQ(domain.Elements()[0].source_id.instance_name, "Instance-A");
|
EXPECT_EQ(domain.BeamElements()[0].source_id.instance_name, "Instance-A");
|
||||||
EXPECT_EQ(domain.Elements()[1].source_id.instance_name, "Instance-B");
|
EXPECT_EQ(domain.BeamElements()[1].source_id.instance_name, "Instance-B");
|
||||||
EXPECT_EQ(domain.Elements()[0].node_indices[0], 0U);
|
EXPECT_EQ(domain.BeamElements()[0].node_indices[0], 0U);
|
||||||
EXPECT_EQ(domain.Elements()[1].node_indices[0], 2U);
|
EXPECT_EQ(domain.BeamElements()[1].node_indices[0], 2U);
|
||||||
}
|
}
|
||||||
|
|
||||||
// MITC4-MODEL-002
|
// MITC4-MODEL-002
|
||||||
@@ -235,16 +321,16 @@ TEST(DomainModel, ShellOwnershipPreservesResolvedAssignmentsAndOptionalFrames) {
|
|||||||
const fesa::Domain& domain = result.Value();
|
const fesa::Domain& domain = result.Value();
|
||||||
static_assert(std::is_same_v<
|
static_assert(std::is_same_v<
|
||||||
decltype(std::declval<const fesa::Domain&>().ShellElements()),
|
decltype(std::declval<const fesa::Domain&>().ShellElements()),
|
||||||
const std::vector<fesa::Mitc4ShellDefinition>&>);
|
const fesa::DomainCollectionView<fesa::Mitc4ShellDefinition>&>);
|
||||||
static_assert(std::is_same_v<
|
static_assert(std::is_same_v<
|
||||||
decltype(std::declval<const fesa::Domain&>().ShellSections()),
|
decltype(std::declval<const fesa::Domain&>().ShellSections()),
|
||||||
const std::vector<fesa::ShellSection>&>);
|
const fesa::DomainCollectionView<fesa::ShellSection>&>);
|
||||||
static_assert(
|
static_assert(
|
||||||
std::is_same_v<decltype(std::declval<const fesa::Domain&>()
|
std::is_same_v<decltype(std::declval<const fesa::Domain&>()
|
||||||
.ShellNodeInitialFrames()),
|
.ShellNodeInitialFrames()),
|
||||||
const std::vector<fesa::ShellNodeInitialFrame>&>);
|
const std::vector<fesa::ShellNodeInitialFrame>&>);
|
||||||
|
|
||||||
ASSERT_EQ(domain.ShellElements().size(), 2U);
|
ASSERT_EQ(domain.ShellElements().Size(), 2U);
|
||||||
EXPECT_EQ(domain.ShellElements()[0].source_id.source_label, 20);
|
EXPECT_EQ(domain.ShellElements()[0].source_id.source_label, 20);
|
||||||
EXPECT_EQ(domain.ShellElements()[0].source_id.source_label_text, "0020");
|
EXPECT_EQ(domain.ShellElements()[0].source_id.source_label_text, "0020");
|
||||||
EXPECT_EQ(domain.ShellElements()[1].source_id.source_label, 10);
|
EXPECT_EQ(domain.ShellElements()[1].source_id.source_label, 10);
|
||||||
@@ -256,7 +342,7 @@ TEST(DomainModel, ShellOwnershipPreservesResolvedAssignmentsAndOptionalFrames) {
|
|||||||
EXPECT_EQ(domain.ShellElements()[0].material_index, 0U);
|
EXPECT_EQ(domain.ShellElements()[0].material_index, 0U);
|
||||||
EXPECT_EQ(domain.ShellElements()[0].section_index, 0U);
|
EXPECT_EQ(domain.ShellElements()[0].section_index, 0U);
|
||||||
|
|
||||||
ASSERT_EQ(domain.ShellSections().size(), 2U);
|
ASSERT_EQ(domain.ShellSections().Size(), 2U);
|
||||||
EXPECT_EQ(domain.ShellSections()[0].name, "Section-B");
|
EXPECT_EQ(domain.ShellSections()[0].name, "Section-B");
|
||||||
EXPECT_DOUBLE_EQ(domain.ShellSections()[0].thickness, 0.02);
|
EXPECT_DOUBLE_EQ(domain.ShellSections()[0].thickness, 0.02);
|
||||||
EXPECT_EQ(domain.ShellSections()[0].material_index, 0U);
|
EXPECT_EQ(domain.ShellSections()[0].material_index, 0U);
|
||||||
|
|||||||
@@ -318,7 +318,7 @@ void ExpectScaledNear(const double actual, const double expected,
|
|||||||
std::vector<fesa::EndpointResultRow> MakeStationRows(
|
std::vector<fesa::EndpointResultRow> MakeStationRows(
|
||||||
const RecoveryFixture& fixture) {
|
const RecoveryFixture& fixture) {
|
||||||
const auto& nodes = fixture.domain->Nodes();
|
const auto& nodes = fixture.domain->Nodes();
|
||||||
const auto& elements = fixture.domain->Elements();
|
const auto& elements = fixture.domain->BeamElements();
|
||||||
return {{0U,
|
return {{0U,
|
||||||
0,
|
0,
|
||||||
nodes[elements[0U].node_indices[0U]].source_id,
|
nodes[elements[0U].node_indices[0U]].source_id,
|
||||||
|
|||||||
Reference in New Issue
Block a user