feat(solid): add Hex8 input and three-DOF semantics
This commit is contained in:
@@ -13,17 +13,21 @@ namespace fesa {
|
||||
class LoadAssembler {
|
||||
public:
|
||||
/// @brief Accumulates all active CLOAD rows in full-DOF space.
|
||||
/// @param step_time Actual finite nonnegative dynamic endpoint for amplitude.
|
||||
/// @return A finite full load vector or a structured model failure.
|
||||
static Result<Vector> AssembleFullNodalLoad(const AnalysisModel& model,
|
||||
const DofManager& dofs);
|
||||
const DofManager& dofs,
|
||||
double step_time = 0.0);
|
||||
|
||||
/// @brief Accumulates explicitly supplied loads in their view order.
|
||||
/// @param loads Non-owning loads whose contribution source orders must match
|
||||
/// their view positions.
|
||||
/// @param step_time Actual finite nonnegative dynamic endpoint for amplitude.
|
||||
/// @return A candidate committed only after all contributions validate.
|
||||
static Result<Vector> AssembleFullNodalLoad(const AnalysisModel& model,
|
||||
const DofManager& dofs,
|
||||
const LoadView& loads);
|
||||
const LoadView& loads,
|
||||
double step_time = 0.0);
|
||||
|
||||
/// @brief Forms Ff-Kfc*dc in stable free/constrained order.
|
||||
/// @note This operation neither factorizes nor invokes a solver.
|
||||
|
||||
@@ -9,7 +9,12 @@
|
||||
namespace fesa {
|
||||
|
||||
/// @brief Identifies the supported semantic element-definition kinds.
|
||||
enum class ElementDefinitionKind { kEulerBeam3D, kMitc4Shell, kDs4Thermal };
|
||||
enum class ElementDefinitionKind {
|
||||
kEulerBeam3D,
|
||||
kMitc4Shell,
|
||||
kDs4Thermal,
|
||||
kHex8
|
||||
};
|
||||
|
||||
/// @brief Provides immutable source identity and topology for one element.
|
||||
/// @note Numerical stiffness, recovery, and equation ids are intentionally
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
#ifndef FESA_ELEMENTS_HEX8_DEFINITION_H_
|
||||
#define FESA_ELEMENTS_HEX8_DEFINITION_H_
|
||||
|
||||
#include <array>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "fesa/elements/element_definition.h"
|
||||
|
||||
namespace fesa {
|
||||
/// @brief Owns source-safe eight-node Solid topology without numerical state.
|
||||
class Hex8Definition final : public ElementDefinition {
|
||||
public:
|
||||
/// @brief Constructs a semantic candidate in source connectivity order.
|
||||
Hex8Definition(SourceEntityId source, std::array<EntityIndex, 8> nodes,
|
||||
EntityIndex material, EntityIndex section,
|
||||
SourceLocation source_location)
|
||||
: source_id{std::move(source)},
|
||||
node_indices{nodes},
|
||||
material_index{material},
|
||||
section_index{section},
|
||||
location{std::move(source_location)},
|
||||
property_index_{section},
|
||||
node_indices_view_{nodes.begin(), nodes.end()} {}
|
||||
ElementDefinitionKind Kind() const noexcept override {
|
||||
return ElementDefinitionKind::kHex8;
|
||||
}
|
||||
const SourceEntityId& SourceId() const noexcept override { return source_id; }
|
||||
std::string_view SourceElementType() const noexcept override {
|
||||
return "C3D8";
|
||||
}
|
||||
const std::vector<EntityIndex>& NodeIndices() const noexcept override {
|
||||
return node_indices_view_;
|
||||
}
|
||||
EntityIndex MaterialIndex() const noexcept override { return material_index; }
|
||||
EntityIndex PropertyIndex() const noexcept override {
|
||||
return property_index_;
|
||||
}
|
||||
|
||||
SourceEntityId source_id;
|
||||
std::array<EntityIndex, 8> node_indices;
|
||||
EntityIndex material_index;
|
||||
EntityIndex section_index;
|
||||
SourceLocation location;
|
||||
|
||||
private:
|
||||
friend class Domain;
|
||||
/// @brief Synchronizes candidate topology with the immutable base view.
|
||||
void SynchronizeNodeIndices() {
|
||||
node_indices_view_.assign(node_indices.begin(), node_indices.end());
|
||||
}
|
||||
/// @brief Binds the local Solid section to the unified property collection.
|
||||
void SetPropertyIndex(EntityIndex index) noexcept { property_index_ = index; }
|
||||
EntityIndex property_index_;
|
||||
std::vector<EntityIndex> node_indices_view_;
|
||||
};
|
||||
} // namespace fesa
|
||||
#endif // FESA_ELEMENTS_HEX8_DEFINITION_H_
|
||||
@@ -16,8 +16,8 @@ namespace fesa {
|
||||
|
||||
class DofManagerTestAccess;
|
||||
|
||||
/// @brief Distinguishes structural six-component and thermal five-station maps.
|
||||
enum class DofProfile { kStructural, kThermal };
|
||||
/// @brief Distinguishes six-component, Solid translation, and thermal maps.
|
||||
enum class DofProfile { kStructural, kThermal, kSolidStructural };
|
||||
|
||||
/// @brief Stores the stable full-space CSR pattern.
|
||||
struct SparsePattern {
|
||||
@@ -59,6 +59,8 @@ class DofManager {
|
||||
|
||||
/// @brief Reports which physical component profile owns this mapping.
|
||||
DofProfile Profile() const noexcept;
|
||||
/// @brief Returns the physical component count for the active profile.
|
||||
std::size_t ComponentsPerNode() const noexcept;
|
||||
|
||||
/// @brief Returns the full node-by-component DOF count.
|
||||
std::size_t FullDofCount() const noexcept;
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
#include "fesa/core/diagnostic.h"
|
||||
#include "fesa/loads/load.h"
|
||||
@@ -21,7 +23,10 @@ class ConcentratedNodalLoad final : public Load {
|
||||
/// @brief Creates one parsed CLOAD component while preserving diagnostics.
|
||||
ConcentratedNodalLoad(SourceTargetQuery target, int source_dof,
|
||||
double magnitude, std::size_t source_order,
|
||||
SourceLocation location);
|
||||
SourceLocation location,
|
||||
std::optional<EntityIndex> amplitude_index = {},
|
||||
std::string amplitude_name_raw = {},
|
||||
SourceLocation amplitude_location = {});
|
||||
|
||||
/// @brief Computes target-major, component-minor full-DOF contributions.
|
||||
Result<std::vector<LoadContribution>> ComputeContributions(
|
||||
@@ -39,12 +44,28 @@ class ConcentratedNodalLoad final : public Load {
|
||||
/// @brief Returns the source location used by structured diagnostics.
|
||||
const SourceLocation& Location() const noexcept;
|
||||
|
||||
/// @brief Returns a model-level amplitude index when one was specified.
|
||||
std::optional<EntityIndex> AmplitudeIndex() const noexcept {
|
||||
return amplitude_index_;
|
||||
}
|
||||
/// @brief Returns preserved CLOAD amplitude option spelling.
|
||||
const std::string& AmplitudeName() const noexcept {
|
||||
return amplitude_name_raw_;
|
||||
}
|
||||
/// @brief Returns the source location of the CLOAD amplitude option.
|
||||
const SourceLocation& AmplitudeLocation() const noexcept {
|
||||
return amplitude_location_;
|
||||
}
|
||||
|
||||
private:
|
||||
SourceTargetQuery target_;
|
||||
std::array<double, 6> global_components_{};
|
||||
std::size_t source_order_;
|
||||
SourceLocation location_{};
|
||||
int source_dof_{0};
|
||||
std::optional<EntityIndex> amplitude_index_;
|
||||
std::string amplitude_name_raw_;
|
||||
SourceLocation amplitude_location_;
|
||||
};
|
||||
|
||||
} // namespace fesa
|
||||
|
||||
@@ -26,6 +26,8 @@ struct LoadContext {
|
||||
const Domain& domain;
|
||||
const DofManager& dof_manager;
|
||||
const SourceTargetResolver& target_resolver;
|
||||
/// @brief Supplies the actual dynamic endpoint time for amplitude sampling.
|
||||
double step_time{0.0};
|
||||
};
|
||||
|
||||
/// @brief Produces local ordered load contributions without global mutation.
|
||||
|
||||
@@ -112,6 +112,13 @@ class StepDefinition {
|
||||
/// @brief Returns the static maximum-increment provenance value.
|
||||
double MaximumIncrement() const noexcept;
|
||||
|
||||
/// @brief Reports the source STATIC DIRECT fixed-schedule selection.
|
||||
bool IsDirectStatic() const noexcept;
|
||||
/// @brief Returns the fixed Solid static increment count, or zero otherwise.
|
||||
std::uint32_t IncrementCount() const noexcept;
|
||||
/// @brief Returns the source static INC limit.
|
||||
std::uint32_t MaximumIncrements() const noexcept;
|
||||
|
||||
/// @brief Returns the source location of the step declaration.
|
||||
const SourceLocation& Location() const noexcept;
|
||||
|
||||
@@ -145,6 +152,9 @@ class StepDefinition {
|
||||
SourceLocation location_;
|
||||
std::optional<FrequencyStepDefinition> frequency_;
|
||||
std::optional<DynamicStepDefinition> dynamic_;
|
||||
bool direct_static_{false};
|
||||
std::uint32_t increment_count_{0U};
|
||||
std::uint32_t maximum_increments_{100U};
|
||||
};
|
||||
|
||||
/// @brief Owns the complete immutable semantic model definition.
|
||||
@@ -176,6 +186,15 @@ class Domain {
|
||||
const DomainCollectionView<Mitc4ShellDefinition>& ShellElements()
|
||||
const noexcept;
|
||||
|
||||
/// @brief Returns C3D8 definitions in stable source order.
|
||||
const DomainCollectionView<Hex8Definition>& SolidElements() const noexcept;
|
||||
/// @brief Returns homogeneous Solid section assignments.
|
||||
const DomainCollectionView<SolidSection>& SolidSections() const noexcept;
|
||||
/// @brief Reports whether this model has exclusively Solid elements.
|
||||
bool IsSolid() const noexcept;
|
||||
/// @brief Returns immutable model-level amplitude curves in source order.
|
||||
const std::vector<TabularAmplitudeDefinition>& Amplitudes() const noexcept;
|
||||
|
||||
/// @brief Returns all materials in stable Domain index order.
|
||||
const DomainCollectionView<Material>& Materials() const noexcept;
|
||||
|
||||
@@ -262,6 +281,8 @@ class Domain {
|
||||
DomainCollectionView<ElementDefinition> elements_view_;
|
||||
DomainCollectionView<EulerBeam3DDefinition> beam_elements_view_;
|
||||
DomainCollectionView<Mitc4ShellDefinition> shell_elements_view_;
|
||||
DomainCollectionView<Hex8Definition> solid_elements_view_;
|
||||
DomainCollectionView<SolidSection> solid_sections_view_;
|
||||
DomainCollectionView<ElementProperty> properties_view_;
|
||||
DomainCollectionView<GeneralBeamSection> sections_view_;
|
||||
DomainCollectionView<ShellSection> shell_sections_view_;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#ifndef FESA_MODEL_MODEL_TYPES_H_
|
||||
#define FESA_MODEL_MODEL_TYPES_H_
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <filesystem>
|
||||
@@ -14,11 +15,13 @@
|
||||
#include "fesa/core/source_identity.h"
|
||||
#include "fesa/elements/ds4_thermal.h"
|
||||
#include "fesa/elements/euler_beam_3d.h"
|
||||
#include "fesa/elements/hex8_definition.h"
|
||||
#include "fesa/elements/mitc4_shell.h"
|
||||
#include "fesa/materials/isotropic_linear_elastic_material.h"
|
||||
#include "fesa/materials/isotropic_thermal_material.h"
|
||||
#include "fesa/properties/general_beam_section.h"
|
||||
#include "fesa/properties/shell_section.h"
|
||||
#include "fesa/properties/solid_section.h"
|
||||
|
||||
namespace fesa {
|
||||
|
||||
@@ -47,12 +50,37 @@ struct PrescribedDisplacementDefinition {
|
||||
SourceLocation location;
|
||||
};
|
||||
|
||||
/// @brief Owns a validated TABULAR STEP TIME RELATIVE load curve.
|
||||
struct TabularAmplitudeDefinition {
|
||||
std::string name_raw;
|
||||
std::string name_canonical;
|
||||
SourceLocation location;
|
||||
std::vector<std::array<double, 2>> points;
|
||||
|
||||
/// @brief Evaluates exact knots, linear interiors, and endpoint holds.
|
||||
/// @pre Time is finite; points are finite, nonempty, strictly increasing.
|
||||
double Evaluate(double time) const {
|
||||
if (time <= points.front()[0]) return points.front()[1];
|
||||
if (time >= points.back()[0]) return points.back()[1];
|
||||
const auto upper = std::lower_bound(
|
||||
points.begin(), points.end(), time,
|
||||
[](const auto& point, double t) { return point[0] < t; });
|
||||
if ((*upper)[0] == time) return (*upper)[1];
|
||||
const auto& lower = *(upper - 1);
|
||||
const double weight = (time - lower[0]) / ((*upper)[0] - lower[0]);
|
||||
return (1.0 - weight) * lower[1] + weight * (*upper)[1];
|
||||
}
|
||||
};
|
||||
|
||||
/// @brief Stores one concentrated nodal load component.
|
||||
struct NodalLoad {
|
||||
std::string target;
|
||||
int dof;
|
||||
double magnitude;
|
||||
SourceLocation location;
|
||||
std::optional<EntityIndex> amplitude_index;
|
||||
std::string amplitude_name_raw;
|
||||
SourceLocation amplitude_location;
|
||||
};
|
||||
|
||||
/// @brief Identifies the selected exterior shell surface side.
|
||||
@@ -169,6 +197,9 @@ struct StaticStepDefinition {
|
||||
std::vector<PrescribedDisplacementDefinition> boundaries;
|
||||
std::vector<NodalLoad> loads;
|
||||
std::vector<SurfacePressureLoadDefinition> surface_pressure_loads;
|
||||
bool direct{false};
|
||||
std::uint32_t increment_count{0U};
|
||||
std::uint32_t maximum_increments{100U};
|
||||
StaticProcedureKind procedure_kind;
|
||||
bool nlgeom;
|
||||
double initial_increment;
|
||||
@@ -226,6 +257,7 @@ struct DynamicStepDefinition {
|
||||
double gamma{0.55};
|
||||
std::vector<PrescribedDisplacementDefinition> boundaries;
|
||||
std::vector<NodalLoad> loads;
|
||||
bool nlgeom{true};
|
||||
};
|
||||
|
||||
/// @brief Stores homogeneous isotropic thermal material source data.
|
||||
@@ -374,10 +406,13 @@ struct ModelDefinition {
|
||||
std::vector<Node> nodes;
|
||||
std::vector<EulerBeam3DDefinition> elements;
|
||||
std::vector<Mitc4ShellDefinition> shell_elements;
|
||||
std::vector<Hex8Definition> solid_elements;
|
||||
std::vector<LinearElasticMaterial> materials;
|
||||
std::vector<ThermalMaterialDefinition> thermal_materials;
|
||||
std::vector<GeneralBeamSection> sections;
|
||||
std::vector<ShellSection> shell_sections;
|
||||
std::vector<SolidSection> solid_sections;
|
||||
std::vector<TabularAmplitudeDefinition> amplitudes;
|
||||
std::vector<ThermalShellSectionDefinition> thermal_sections;
|
||||
std::vector<Ds4ThermalElementDefinition> thermal_elements;
|
||||
std::vector<ShellNodeInitialFrame> shell_node_initial_frames;
|
||||
|
||||
@@ -6,7 +6,11 @@
|
||||
namespace fesa {
|
||||
|
||||
/// @brief Identifies the supported concrete element-property semantics.
|
||||
enum class ElementPropertyKind { kGeneralBeamSection, kShellSection };
|
||||
enum class ElementPropertyKind {
|
||||
kGeneralBeamSection,
|
||||
kShellSection,
|
||||
kSolidSection
|
||||
};
|
||||
|
||||
/// @brief Provides stable identity for a Domain-owned element property.
|
||||
class ElementProperty {
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
#ifndef FESA_PROPERTIES_SOLID_SECTION_H_
|
||||
#define FESA_PROPERTIES_SOLID_SECTION_H_
|
||||
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "fesa/properties/element_property.h"
|
||||
|
||||
namespace fesa {
|
||||
/// @brief Owns a homogeneous Solid material assignment without thickness.
|
||||
class SolidSection final : public ElementProperty {
|
||||
public:
|
||||
/// @brief Constructs a parser-validated ELSET and material assignment.
|
||||
SolidSection(SourceEntityId source, std::string name,
|
||||
EntityIndex material_index, SourceLocation location)
|
||||
: source_id_{std::move(source)},
|
||||
name_{std::move(name)},
|
||||
material_index_{material_index},
|
||||
location_{std::move(location)} {}
|
||||
ElementPropertyKind Kind() const noexcept override {
|
||||
return ElementPropertyKind::kSolidSection;
|
||||
}
|
||||
const SourceEntityId& SourceId() const noexcept override {
|
||||
return source_id_;
|
||||
}
|
||||
const SourceLocation& Location() const noexcept override { return location_; }
|
||||
/// @brief Returns the source ELSET spelling.
|
||||
const std::string& Name() const noexcept { return name_; }
|
||||
/// @brief Returns the stable Domain material index.
|
||||
EntityIndex MaterialIndex() const noexcept { return material_index_; }
|
||||
|
||||
private:
|
||||
SourceEntityId source_id_;
|
||||
std::string name_;
|
||||
EntityIndex material_index_;
|
||||
SourceLocation location_;
|
||||
};
|
||||
} // namespace fesa
|
||||
#endif // FESA_PROPERTIES_SOLID_SECTION_H_
|
||||
Reference in New Issue
Block a user