feat(solid): add Hex8 input and three-DOF semantics

This commit is contained in:
KOKO\Mimi
2026-10-09 01:12:30 +09:00
parent df0388ae2f
commit 9acf18db4b
16 changed files with 1468 additions and 98 deletions
+39 -12
View File
@@ -116,8 +116,12 @@ Result<DofManager> DofManager::Create(const AnalysisModel& model) {
"An active element index is outside the Domain."));
}
const auto& definition = domain.Elements()[element_index];
layouts.push_back({definition.SourceId(), definition.NodeIndices(),
FullNodeComponents()});
layouts.push_back(
{definition.SourceId(), definition.NodeIndices(),
domain.IsSolid()
? std::vector<DofComponent>{DofComponent::kUx, DofComponent::kUy,
DofComponent::kUz}
: FullNodeComponents()});
}
DofManager dofs;
@@ -305,6 +309,19 @@ Status DofManager::BuildLayouts(const AnalysisModel& analysis_model,
++source_order) {
const EntityIndex definition_index =
analysis_model.ActiveElements()[source_order];
if (domain.IsSolid()) {
const std::vector<DofComponent> translations{
DofComponent::kUx, DofComponent::kUy, DofComponent::kUz};
if (layouts[source_order].components_per_node != translations ||
definition_index >= domain.Elements().Size() ||
layouts[source_order].node_indices !=
domain.Elements()[definition_index].NodeIndices()) {
return DofFailure(
"invalid-solid-element-layout", {domain.SourcePath(), 0U},
std::to_string(source_order),
"Solid layouts require source-ordered eight nodes and UX/UY/UZ.");
}
}
if (definition_index >= domain.Elements().Size() ||
!SameSourceIdentity(layouts[source_order].source_id,
domain.Elements()[definition_index].SourceId())) {
@@ -318,10 +335,13 @@ Status DofManager::BuildLayouts(const AnalysisModel& analysis_model,
const SourceTargetIndex target_index = SourceTargetIndex::FromDomain(domain);
const SourceTargetResolver target_resolver{target_index};
const std::size_t full_count = domain.Nodes().size() * kDofsPerNode;
const std::size_t component_count = domain.IsSolid() ? 3U : kDofsPerNode;
const std::size_t full_count = domain.Nodes().size() * component_count;
DofManager full_dof_map;
full_dof_map.full_dof_count_ = full_count;
full_dof_map.profile_ =
domain.IsSolid() ? DofProfile::kSolidStructural : DofProfile::kStructural;
const BoundaryConditionContext context{domain, full_dof_map, target_resolver};
std::vector<std::vector<ConstraintDefinition>> definitions_by_boundary;
definitions_by_boundary.reserve(boundaries.size());
@@ -417,6 +437,7 @@ Status DofManager::BuildLayouts(const AnalysisModel& analysis_model,
std::move(constrained_dofs),
std::move(prescribed_values),
{}};
candidate.profile_ = full_dof_map.profile_;
candidate.element_scatters_.reserve(layouts.size());
for (const auto& layout : layouts) {
auto scatter = candidate.ElementScatter(layout);
@@ -441,6 +462,10 @@ std::size_t DofManager::FullDofCount() const noexcept {
}
DofProfile DofManager::Profile() const noexcept { return profile_; }
std::size_t DofManager::ComponentsPerNode() const noexcept {
if (profile_ == DofProfile::kThermal) return kThermalDofsPerNode;
return profile_ == DofProfile::kSolidStructural ? 3U : kDofsPerNode;
}
std::size_t DofManager::FreeDofCount() const noexcept {
return free_dofs_.size();
@@ -452,15 +477,16 @@ std::size_t DofManager::ConstrainedDofCount() const noexcept {
std::size_t DofManager::FullDof(const EntityIndex node,
const DofComponent component) const {
if (profile_ != DofProfile::kStructural) {
throw std::logic_error{"Structural components are invalid for thermal DOFs."};
if (profile_ == DofProfile::kThermal) {
throw std::logic_error{
"Structural components are invalid for thermal DOFs."};
}
const std::size_t component_index = static_cast<std::size_t>(component);
if (node >= full_dof_count_ / kDofsPerNode ||
component_index >= kDofsPerNode) {
if (node >= full_dof_count_ / ComponentsPerNode() ||
component_index >= ComponentsPerNode()) {
throw std::out_of_range{"Node or DOF component is out of range."};
}
return static_cast<std::size_t>(node) * kDofsPerNode + component_index;
return static_cast<std::size_t>(node) * ComponentsPerNode() + component_index;
}
std::size_t DofManager::FullThermalDof(
@@ -481,7 +507,7 @@ std::optional<std::size_t> DofManager::FreeEquation(
Result<std::vector<std::size_t>> DofManager::ElementScatter(
const ElementDofLayout& layout) const {
if (profile_ != DofProfile::kStructural) {
if (profile_ == DofProfile::kThermal) {
return Result<std::vector<std::size_t>>::Failure(DofFailure(
"invalid-dof-profile", {}, layout.source_id.source_label_text,
"Structural layouts are invalid for a thermal mapping."));
@@ -501,8 +527,8 @@ Result<std::vector<std::size_t>> DofManager::ElementScatter(
for (const EntityIndex node : layout.node_indices) {
for (const DofComponent component : layout.components_per_node) {
const std::size_t component_index = static_cast<std::size_t>(component);
if (node >= full_dof_count_ / kDofsPerNode ||
component_index >= kDofsPerNode) {
if (node >= full_dof_count_ / ComponentsPerNode() ||
component_index >= ComponentsPerNode()) {
return Result<std::vector<std::size_t>>::Failure(DofFailure(
"invalid-element-dof-layout", {},
layout.source_id.source_label_text,
@@ -510,7 +536,8 @@ Result<std::vector<std::size_t>> DofManager::ElementScatter(
"DOF range."));
}
const std::size_t full_dof =
static_cast<std::size_t>(node) * kDofsPerNode + component_index;
static_cast<std::size_t>(node) * ComponentsPerNode() +
component_index;
if (std::find(scatter.begin(), scatter.end(), full_dof) !=
scatter.end()) {
return Result<std::vector<std::size_t>>::Failure(DofFailure(