feat(cpp-object-oriented-modular-refactoring): step 14 - runtime-element-factory
This commit is contained in:
@@ -13,6 +13,7 @@ add_library(
|
||||
core/ascii.cpp
|
||||
core/diagnostic.cpp
|
||||
core/status.cpp
|
||||
elements/element_factory.cpp
|
||||
elements/euler_beam_3d.cpp
|
||||
elements/mitc4_shell.cpp
|
||||
fem/dof_manager.cpp
|
||||
|
||||
@@ -0,0 +1,240 @@
|
||||
#include "fesa/elements/element_factory.h"
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "fesa/elements/euler_beam_3d.h"
|
||||
#include "fesa/elements/mitc4_shell.h"
|
||||
#include "fesa/materials/material.h"
|
||||
#include "fesa/model/domain.h"
|
||||
#include "fesa/properties/element_property.h"
|
||||
|
||||
namespace fesa {
|
||||
namespace {
|
||||
|
||||
/// @brief Creates one deterministic factory failure for a semantic element.
|
||||
Result<std::unique_ptr<Element>> FactoryFailure(
|
||||
const ElementDefinition& definition, const Domain& domain, std::string code,
|
||||
std::string message) {
|
||||
return Result<std::unique_ptr<Element>>::Failure(Status::Failure(
|
||||
FailureCategory::kModel, {{Severity::kError,
|
||||
std::move(code),
|
||||
{domain.SourcePath(), 0U},
|
||||
"*ELEMENT",
|
||||
definition.SourceId().source_label_text,
|
||||
std::move(message)}}));
|
||||
}
|
||||
|
||||
/// @brief Returns the shared stable six-component node ordering.
|
||||
std::vector<DofComponent> FullNodeComponents() {
|
||||
return {DofComponent::kUx, DofComponent::kUy, DofComponent::kUz,
|
||||
DofComponent::kUrx, DofComponent::kUry, DofComponent::kUrz};
|
||||
}
|
||||
|
||||
/// @brief Finds one element's unified stable Domain collection position.
|
||||
std::optional<EntityIndex> FindElementIndex(const ElementDefinition& definition,
|
||||
const Domain& domain) {
|
||||
for (std::size_t index = 0U; index < domain.Elements().Size(); ++index) {
|
||||
if (&domain.Elements()[index] == &definition) {
|
||||
return static_cast<EntityIndex>(index);
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
/// @brief Finds a Domain-owned B33 definition without performing a cast.
|
||||
std::optional<EntityIndex> FindBeamIndex(const ElementDefinition& definition,
|
||||
const Domain& domain) {
|
||||
for (std::size_t index = 0U; index < domain.BeamElements().Size(); ++index) {
|
||||
const ElementDefinition* candidate = &domain.BeamElements()[index];
|
||||
if (candidate == &definition) {
|
||||
return static_cast<EntityIndex>(index);
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
/// @brief Finds a Domain-owned MITC4 definition without performing a cast.
|
||||
std::optional<EntityIndex> FindShellIndex(const ElementDefinition& definition,
|
||||
const Domain& domain) {
|
||||
for (std::size_t index = 0U; index < domain.ShellElements().Size(); ++index) {
|
||||
const ElementDefinition* candidate = &domain.ShellElements()[index];
|
||||
if (candidate == &definition) {
|
||||
return static_cast<EntityIndex>(index);
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
/// @brief Verifies every topology entry before indexing Domain nodes.
|
||||
bool HasValidNodes(const ElementDefinition& definition, const Domain& domain,
|
||||
const std::size_t expected_count) {
|
||||
if (definition.NodeIndices().size() != expected_count) {
|
||||
return false;
|
||||
}
|
||||
for (const EntityIndex node : definition.NodeIndices()) {
|
||||
if (node >= domain.Nodes().size()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Result<std::unique_ptr<Element>> ElementFactory::CreateBeam(
|
||||
const ElementDefinition& definition, const Domain& domain) const {
|
||||
const auto element_index = FindElementIndex(definition, domain);
|
||||
const auto beam_index = FindBeamIndex(definition, domain);
|
||||
if (!element_index.has_value() || !beam_index.has_value()) {
|
||||
return FactoryFailure(
|
||||
definition, domain, "invalid-element-definition",
|
||||
"The B33 definition must be owned by the supplied Domain.");
|
||||
}
|
||||
// The concrete access is valid only after the kind and Domain-owned
|
||||
// collection identity checks performed by Create and FindBeamIndex.
|
||||
const auto& beam_definition =
|
||||
static_cast<const EulerBeam3DDefinition&>(definition);
|
||||
if (!HasValidNodes(definition, domain, 2U)) {
|
||||
return FactoryFailure(definition, domain, "invalid-element-topology",
|
||||
"B33 requires two valid Domain node indices.");
|
||||
}
|
||||
if (definition.PropertyIndex() >= domain.Properties().Size() ||
|
||||
definition.MaterialIndex() >= domain.Materials().Size()) {
|
||||
return FactoryFailure(
|
||||
definition, domain, "invalid-element-assignment",
|
||||
"B33 property and material indices must resolve in the Domain.");
|
||||
}
|
||||
const ElementProperty& property =
|
||||
domain.Properties()[definition.PropertyIndex()];
|
||||
const Material& material = domain.Materials()[definition.MaterialIndex()];
|
||||
if (property.Kind() != ElementPropertyKind::kGeneralBeamSection ||
|
||||
material.Kind() != MaterialKind::kIsotropicLinearElastic) {
|
||||
return FactoryFailure(
|
||||
definition, domain, "incompatible-element-assignment",
|
||||
"B33 requires a general beam section and isotropic elastic material.");
|
||||
}
|
||||
// These casts are centralized after explicit discriminator validation.
|
||||
const auto& section = static_cast<const GeneralBeamSection&>(property);
|
||||
const auto& elastic =
|
||||
static_cast<const IsotropicLinearElasticMaterial&>(material);
|
||||
auto candidate = EulerBeam3D::Create(
|
||||
domain.Nodes()[definition.NodeIndices()[0U]],
|
||||
domain.Nodes()[definition.NodeIndices()[1U]], section, elastic);
|
||||
if (!candidate.HasValue()) {
|
||||
return Result<std::unique_ptr<Element>>::Failure(candidate.GetStatus());
|
||||
}
|
||||
|
||||
ElementDofLayout layout{definition.SourceId(), definition.NodeIndices(),
|
||||
FullNodeComponents()};
|
||||
std::vector<SourceEntityId> node_source_ids;
|
||||
node_source_ids.reserve(2U);
|
||||
for (const EntityIndex node : definition.NodeIndices()) {
|
||||
node_source_ids.push_back(domain.Nodes()[node].source_id);
|
||||
}
|
||||
auto runtime = std::make_unique<EulerBeam3D>(std::move(candidate.Value()));
|
||||
runtime->BindRuntime(std::move(layout), *element_index,
|
||||
std::move(node_source_ids), beam_definition.location);
|
||||
std::unique_ptr<Element> result = std::move(runtime);
|
||||
return Result<std::unique_ptr<Element>>::Success(std::move(result));
|
||||
}
|
||||
|
||||
Result<std::unique_ptr<Element>> ElementFactory::CreateShell(
|
||||
const ElementDefinition& definition, const Domain& domain) const {
|
||||
const auto element_index = FindElementIndex(definition, domain);
|
||||
const auto shell_index = FindShellIndex(definition, domain);
|
||||
if (!element_index.has_value() || !shell_index.has_value()) {
|
||||
return FactoryFailure(
|
||||
definition, domain, "invalid-element-definition",
|
||||
"The MITC4 definition must be owned by the supplied Domain.");
|
||||
}
|
||||
// The concrete access is valid only after the kind and Domain-owned
|
||||
// collection identity checks performed by Create and FindShellIndex.
|
||||
const auto& shell_definition =
|
||||
static_cast<const Mitc4ShellDefinition&>(definition);
|
||||
if (!HasValidNodes(definition, domain, 4U)) {
|
||||
return FactoryFailure(definition, domain, "invalid-element-topology",
|
||||
"MITC4 requires four valid Domain node indices.");
|
||||
}
|
||||
if (definition.PropertyIndex() >= domain.Properties().Size() ||
|
||||
definition.MaterialIndex() >= domain.Materials().Size()) {
|
||||
return FactoryFailure(
|
||||
definition, domain, "invalid-element-assignment",
|
||||
"MITC4 property and material indices must resolve in the Domain.");
|
||||
}
|
||||
const ElementProperty& property =
|
||||
domain.Properties()[definition.PropertyIndex()];
|
||||
const Material& material = domain.Materials()[definition.MaterialIndex()];
|
||||
if (property.Kind() != ElementPropertyKind::kShellSection ||
|
||||
material.Kind() != MaterialKind::kIsotropicLinearElastic) {
|
||||
return FactoryFailure(
|
||||
definition, domain, "incompatible-element-assignment",
|
||||
"MITC4 requires a shell section and isotropic elastic material.");
|
||||
}
|
||||
const auto& section = static_cast<const ShellSection&>(property);
|
||||
if (section.MaterialIndex() != definition.MaterialIndex()) {
|
||||
return FactoryFailure(
|
||||
definition, domain, "incompatible-element-assignment",
|
||||
"MITC4 definition and shell section must reference one material.");
|
||||
}
|
||||
// These casts are centralized after explicit discriminator validation.
|
||||
const auto& elastic =
|
||||
static_cast<const IsotropicLinearElasticMaterial&>(material);
|
||||
|
||||
std::vector<const ShellNodeInitialFrame*> frame_by_node(domain.Nodes().size(),
|
||||
nullptr);
|
||||
for (const auto& frame : domain.ShellNodeInitialFrames()) {
|
||||
if (frame.node_index >= frame_by_node.size() ||
|
||||
frame_by_node[frame.node_index] != nullptr) {
|
||||
return FactoryFailure(
|
||||
definition, domain, "invalid-shell-frame",
|
||||
"Shell initial frames must map uniquely to valid Domain nodes.");
|
||||
}
|
||||
frame_by_node[frame.node_index] = &frame;
|
||||
}
|
||||
|
||||
std::array<const Node*, 4> nodes{};
|
||||
std::array<std::array<double, 3>, 4> directors{};
|
||||
for (std::size_t position = 0U; position < nodes.size(); ++position) {
|
||||
const EntityIndex node = definition.NodeIndices()[position];
|
||||
const ShellNodeInitialFrame* frame = frame_by_node[node];
|
||||
if (frame == nullptr) {
|
||||
return FactoryFailure(
|
||||
definition, domain, "missing-shell-frame",
|
||||
"MITC4 requires one initial frame for every element node.");
|
||||
}
|
||||
nodes[position] = &domain.Nodes()[node];
|
||||
directors[position] = frame->director;
|
||||
}
|
||||
|
||||
auto candidate = Mitc4Shell::Create(nodes, directors, section, elastic);
|
||||
if (!candidate.HasValue()) {
|
||||
return Result<std::unique_ptr<Element>>::Failure(candidate.GetStatus());
|
||||
}
|
||||
ElementDofLayout layout{definition.SourceId(), definition.NodeIndices(),
|
||||
FullNodeComponents()};
|
||||
auto runtime = std::make_unique<Mitc4Shell>(std::move(candidate.Value()));
|
||||
runtime->BindRuntime(std::move(layout), *element_index,
|
||||
shell_definition.location);
|
||||
std::unique_ptr<Element> result = std::move(runtime);
|
||||
return Result<std::unique_ptr<Element>>::Success(std::move(result));
|
||||
}
|
||||
|
||||
Result<std::unique_ptr<Element>> ElementFactory::Create(
|
||||
const ElementDefinition& definition, const Domain& domain) const {
|
||||
switch (definition.Kind()) {
|
||||
case ElementDefinitionKind::kEulerBeam3D:
|
||||
return CreateBeam(definition, domain);
|
||||
case ElementDefinitionKind::kMitc4Shell:
|
||||
return CreateShell(definition, domain);
|
||||
}
|
||||
return FactoryFailure(definition, domain, "unsupported-element-definition",
|
||||
"The element definition kind is not supported.");
|
||||
}
|
||||
|
||||
} // namespace fesa
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <limits>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
@@ -77,6 +78,29 @@ Result<EulerBeam3D> ModelFailure(const std::string& code,
|
||||
{{Severity::kError, code, location, "*ELEMENT", identity, message}}));
|
||||
}
|
||||
|
||||
/// @brief Creates a structured failure at the runtime element boundary.
|
||||
template <class T>
|
||||
Result<T> RuntimeFailure(const SourceLocation& location,
|
||||
const SourceEntityId& source_id,
|
||||
const std::string& message) {
|
||||
return Result<T>::Failure(
|
||||
Status::Failure(FailureCategory::kModel,
|
||||
{{Severity::kError, "invalid-runtime-element", location,
|
||||
"*ELEMENT", source_id.source_label_text, message}}));
|
||||
}
|
||||
|
||||
/// @brief Reports whether every fixed-size recovery component is finite.
|
||||
template <class T, std::size_t Size>
|
||||
bool IsFinite(const std::array<T, Size>& values) {
|
||||
return std::all_of(values.begin(), values.end(), [](const auto& value) {
|
||||
if constexpr (std::is_arithmetic_v<T>) {
|
||||
return std::isfinite(value);
|
||||
} else {
|
||||
return IsFinite(value);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// @brief Repeats the local-axis rotation in stable nodal translation and
|
||||
/// rotation blocks.
|
||||
Matrix Transformation(const std::array<double, 9>& rotation) {
|
||||
@@ -428,7 +452,7 @@ Vector EulerBeam3D::LocalEquivalentLoad(
|
||||
return equivalent;
|
||||
}
|
||||
|
||||
BeamRecovery EulerBeam3D::Recover(
|
||||
BeamRecovery EulerBeam3D::RecoverBeam(
|
||||
const Vector& global_element_displacement) const {
|
||||
const Matrix transform = Transformation(rotation_);
|
||||
const Vector local_displacement =
|
||||
@@ -484,6 +508,105 @@ BeamRecovery EulerBeam3D::Recover(
|
||||
return recovery;
|
||||
}
|
||||
|
||||
const ElementDofLayout& EulerBeam3D::DofLayout() const noexcept {
|
||||
return dof_layout_;
|
||||
}
|
||||
|
||||
Result<ElementStiffnessContribution> EulerBeam3D::ComputeStiffness() const {
|
||||
if (dof_layout_.node_indices.size() != 2U ||
|
||||
dof_layout_.components_per_node.size() != 6U) {
|
||||
return RuntimeFailure<ElementStiffnessContribution>(
|
||||
runtime_location_, dof_layout_.source_id,
|
||||
"B33 runtime stiffness requires a bound two-node six-DOF layout.");
|
||||
}
|
||||
try {
|
||||
return Result<ElementStiffnessContribution>::Success(
|
||||
{dof_layout_, GlobalStiffness()});
|
||||
} catch (const std::exception& error) {
|
||||
return RuntimeFailure<ElementStiffnessContribution>(
|
||||
runtime_location_, dof_layout_.source_id, error.what());
|
||||
}
|
||||
}
|
||||
|
||||
Result<ElementResultBundle> EulerBeam3D::Recover(
|
||||
const Vector& element_displacement) const {
|
||||
if (dof_layout_.node_indices.size() != 2U ||
|
||||
dof_layout_.components_per_node.size() != 6U ||
|
||||
node_source_ids_.size() != 2U ||
|
||||
element_displacement.Size() != kElementDofCount) {
|
||||
return RuntimeFailure<ElementResultBundle>(
|
||||
runtime_location_, dof_layout_.source_id,
|
||||
"B33 runtime recovery requires a bound two-node layout and 12 DOFs.");
|
||||
}
|
||||
for (std::size_t dof = 0U; dof < element_displacement.Size(); ++dof) {
|
||||
if (!std::isfinite(element_displacement[dof])) {
|
||||
return RuntimeFailure<ElementResultBundle>(
|
||||
runtime_location_, dof_layout_.source_id,
|
||||
"B33 runtime recovery displacement must be finite.");
|
||||
}
|
||||
}
|
||||
|
||||
BeamRecovery recovered{};
|
||||
try {
|
||||
recovered = RecoverBeam(element_displacement);
|
||||
} catch (const std::exception& error) {
|
||||
return RuntimeFailure<ElementResultBundle>(
|
||||
runtime_location_, dof_layout_.source_id, error.what());
|
||||
}
|
||||
|
||||
BeamElementResultRows rows{};
|
||||
rows.endpoint_rows.reserve(2U);
|
||||
rows.gauss_rows.reserve(2U);
|
||||
rows.stress_rows.reserve(recovered.stress_points.size());
|
||||
for (std::size_t endpoint = 0U; endpoint < 2U; ++endpoint) {
|
||||
if (!IsFinite(recovered.equilibrium_end_actions[endpoint]) ||
|
||||
!IsFinite(recovered.endpoint_section_resultants[endpoint])) {
|
||||
return RuntimeFailure<ElementResultBundle>(
|
||||
runtime_location_, dof_layout_.source_id,
|
||||
"B33 endpoint recovery values must be finite.");
|
||||
}
|
||||
rows.endpoint_rows.push_back(
|
||||
{element_index_, static_cast<int>(endpoint), node_source_ids_[endpoint],
|
||||
recovered.equilibrium_end_actions[endpoint],
|
||||
recovered.endpoint_section_resultants[endpoint]});
|
||||
}
|
||||
for (std::size_t gauss = 0U; gauss < 2U; ++gauss) {
|
||||
if (!IsFinite(recovered.gauss_generalized_strains[gauss]) ||
|
||||
!IsFinite(recovered.gauss_generalized_resultants[gauss])) {
|
||||
return RuntimeFailure<ElementResultBundle>(
|
||||
runtime_location_, dof_layout_.source_id,
|
||||
"B33 Gauss recovery values must be finite.");
|
||||
}
|
||||
rows.gauss_rows.push_back({element_index_, static_cast<int>(gauss + 1U),
|
||||
recovered.gauss_generalized_strains[gauss],
|
||||
recovered.gauss_generalized_resultants[gauss]});
|
||||
}
|
||||
for (const auto& point : recovered.stress_points) {
|
||||
if ((point.gauss_point != 1 && point.gauss_point != 2) ||
|
||||
!std::isfinite(point.x1) || !std::isfinite(point.x2) ||
|
||||
!std::isfinite(point.s11)) {
|
||||
return RuntimeFailure<ElementResultBundle>(
|
||||
runtime_location_, dof_layout_.source_id,
|
||||
"B33 stress recovery identity and values must be finite.");
|
||||
}
|
||||
rows.stress_rows.push_back({element_index_, point.gauss_point,
|
||||
point.section_point, point.x1, point.x2,
|
||||
point.s11, point.source});
|
||||
}
|
||||
return Result<ElementResultBundle>::Success(
|
||||
{dof_layout_.source_id, std::move(rows)});
|
||||
}
|
||||
|
||||
void EulerBeam3D::BindRuntime(ElementDofLayout layout,
|
||||
const EntityIndex element_index,
|
||||
std::vector<SourceEntityId> node_source_ids,
|
||||
SourceLocation location) {
|
||||
dof_layout_ = std::move(layout);
|
||||
element_index_ = element_index;
|
||||
node_source_ids_ = std::move(node_source_ids);
|
||||
runtime_location_ = std::move(location);
|
||||
}
|
||||
|
||||
EulerBeam3D::EulerBeam3D(double length, double youngs_modulus,
|
||||
double shear_modulus, double area, double iy,
|
||||
double iz, double torsional_constant,
|
||||
|
||||
@@ -288,6 +288,16 @@ Result<Mitc4PhysicalRecovery> RecoveryFailure(const SourceLocation& location,
|
||||
"*ELEMENT", identity, std::move(message)}}));
|
||||
}
|
||||
|
||||
/// @brief Creates a structured failure at the runtime element boundary.
|
||||
template <class T>
|
||||
Result<T> RuntimeFailure(const SourceLocation& location,
|
||||
const SourceEntityId& source_id, std::string message) {
|
||||
return Result<T>::Failure(Status::Failure(
|
||||
FailureCategory::kModel,
|
||||
{{Severity::kError, "invalid-runtime-element", location, "*ELEMENT",
|
||||
source_id.source_label_text, std::move(message)}}));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Result<Mitc4Shell> Mitc4Shell::Create(
|
||||
@@ -794,6 +804,95 @@ Result<Mitc4PhysicalRecovery> Mitc4Shell::RecoverPhysical(
|
||||
return Result<Mitc4PhysicalRecovery>::Success(std::move(recovery));
|
||||
}
|
||||
|
||||
const ElementDofLayout& Mitc4Shell::DofLayout() const noexcept {
|
||||
return dof_layout_;
|
||||
}
|
||||
|
||||
Result<ElementStiffnessContribution> Mitc4Shell::ComputeStiffness() const {
|
||||
if (dof_layout_.node_indices.size() != kNodeCount ||
|
||||
dof_layout_.components_per_node.size() != kGlobalDofsPerNode) {
|
||||
return RuntimeFailure<ElementStiffnessContribution>(
|
||||
runtime_location_, dof_layout_.source_id,
|
||||
"MITC4 runtime stiffness requires a bound four-node six-DOF layout.");
|
||||
}
|
||||
try {
|
||||
auto stiffness = Stiffness();
|
||||
if (!stiffness.HasValue()) {
|
||||
return Result<ElementStiffnessContribution>::Failure(
|
||||
stiffness.GetStatus());
|
||||
}
|
||||
return Result<ElementStiffnessContribution>::Success(
|
||||
{dof_layout_, std::move(stiffness.Value().stabilized_global24)});
|
||||
} catch (const std::exception& error) {
|
||||
return RuntimeFailure<ElementStiffnessContribution>(
|
||||
runtime_location_, dof_layout_.source_id, error.what());
|
||||
}
|
||||
}
|
||||
|
||||
Result<ElementResultBundle> Mitc4Shell::Recover(
|
||||
const Vector& element_displacement) const {
|
||||
if (dof_layout_.node_indices.size() != kNodeCount ||
|
||||
dof_layout_.components_per_node.size() != kGlobalDofsPerNode ||
|
||||
element_displacement.Size() != kGlobalDofCount) {
|
||||
return RuntimeFailure<ElementResultBundle>(
|
||||
runtime_location_, dof_layout_.source_id,
|
||||
"MITC4 runtime recovery requires a bound four-node layout and 24 "
|
||||
"DOFs.");
|
||||
}
|
||||
|
||||
Result<Mitc4PhysicalRecovery> recovered = [&]() {
|
||||
try {
|
||||
return RecoverPhysical(element_displacement);
|
||||
} catch (const std::exception& error) {
|
||||
return RuntimeFailure<Mitc4PhysicalRecovery>(
|
||||
runtime_location_, dof_layout_.source_id, error.what());
|
||||
}
|
||||
}();
|
||||
if (!recovered.HasValue()) {
|
||||
return Result<ElementResultBundle>::Failure(recovered.GetStatus());
|
||||
}
|
||||
|
||||
constexpr std::array<ShellMidsurfaceLocation, kNodeCount> locations{
|
||||
ShellMidsurfaceLocation::kGp1, ShellMidsurfaceLocation::kGp2,
|
||||
ShellMidsurfaceLocation::kGp3, ShellMidsurfaceLocation::kGp4};
|
||||
constexpr std::array<ShellSectionPosition, 3> positions{
|
||||
ShellSectionPosition::kBottom, ShellSectionPosition::kMiddle,
|
||||
ShellSectionPosition::kTop};
|
||||
constexpr std::array<double, 3> zeta{-1.0, 0.0, 1.0};
|
||||
|
||||
ShellElementResultRows rows{};
|
||||
rows.rows.reserve(kNodeCount);
|
||||
rows.physical_strain_energy = recovered.Value().strain_energy;
|
||||
for (std::size_t point = 0U; point < recovered.Value().points.size();
|
||||
++point) {
|
||||
const auto& physical_point = recovered.Value().points[point];
|
||||
ShellResultRow row{};
|
||||
row.element = element_index_;
|
||||
row.location = locations[point];
|
||||
row.natural_coordinates = physical_point.natural_coordinates;
|
||||
row.local_frame = {physical_point.local_frame.e1,
|
||||
physical_point.local_frame.e2,
|
||||
physical_point.local_frame.e3};
|
||||
row.generalized_strain = physical_point.generalized_strain;
|
||||
row.section_resultant = physical_point.section_resultant;
|
||||
for (std::size_t position = 0U; position < positions.size(); ++position) {
|
||||
row.stress[position] = {positions[position], zeta[position],
|
||||
physical_point.in_plane_stress[position]};
|
||||
}
|
||||
rows.rows.push_back(std::move(row));
|
||||
}
|
||||
return Result<ElementResultBundle>::Success(
|
||||
{dof_layout_.source_id, std::move(rows)});
|
||||
}
|
||||
|
||||
void Mitc4Shell::BindRuntime(ElementDofLayout layout,
|
||||
const EntityIndex element_index,
|
||||
SourceLocation location) {
|
||||
dof_layout_ = std::move(layout);
|
||||
element_index_ = element_index;
|
||||
runtime_location_ = std::move(location);
|
||||
}
|
||||
|
||||
Mitc4Shell::Mitc4Shell(std::array<Vector3, 4> coordinates,
|
||||
std::array<Vector3, 4> directors,
|
||||
std::array<Vector3, 4> tangent_a,
|
||||
|
||||
@@ -607,7 +607,8 @@ Status ResultRecovery::Recover(const AnalysisModel& model,
|
||||
element_displacement[local_dof] =
|
||||
state.Displacement()[scatter[local_dof]];
|
||||
}
|
||||
const BeamRecovery recovered = beam.Value().Recover(element_displacement);
|
||||
const BeamRecovery recovered =
|
||||
beam.Value().RecoverBeam(element_displacement);
|
||||
for (std::size_t endpoint = 0U; endpoint < 2U; ++endpoint) {
|
||||
if (!IsFinite(recovered.equilibrium_end_actions[endpoint]) ||
|
||||
!IsFinite(recovered.endpoint_section_resultants[endpoint])) {
|
||||
|
||||
Reference in New Issue
Block a user