fix: validate thermal dof layouts

This commit is contained in:
KOKO\Mimi
2026-09-29 19:05:39 +09:00
parent e71f3ad9ac
commit 17a2355159
2 changed files with 187 additions and 34 deletions
+45 -21
View File
@@ -53,7 +53,9 @@ std::vector<DofComponent> FullNodeComponents() {
bool IsThermalSurfaceDof(const int dof) { return dof == 11 || dof == 15; }
std::size_t ThermalComponentIndex(const ThermalDofComponent component) {
return static_cast<std::size_t>(static_cast<int>(component) - 11);
const int source_dof = static_cast<int>(component);
if (source_dof < 11 || source_dof > 15) return kThermalDofsPerNode;
return static_cast<std::size_t>(source_dof - 11);
}
void AppendScatter(std::vector<std::vector<std::size_t>>& columns_by_row,
@@ -157,8 +159,9 @@ Result<DofManager> DofManager::CreateThermal(const AnalysisModel& model) {
Status DofManager::Build(const AnalysisModel& analysis_model,
const ElementView& elements) {
if (analysis_model.IsThermal()) {
return DofFailure("invalid-dof-profile", {analysis_model.GetDomain().SourcePath(), 0U},
"structural", "Structural element layouts cannot build a thermal mapping.");
return DofFailure(
"invalid-dof-profile", {analysis_model.GetDomain().SourcePath(), 0U},
"structural", "Structural element layouts cannot build a thermal mapping.");
}
return Build(analysis_model, elements,
analysis_model.Step().BoundaryConditions());
@@ -169,8 +172,9 @@ Status DofManager::Build(const AnalysisModel& analysis_model,
const BoundaryConditionView& boundaries) {
std::vector<ElementDofLayout> layouts;
if (analysis_model.IsThermal()) {
return DofFailure("invalid-dof-profile", {analysis_model.GetDomain().SourcePath(), 0U},
"structural", "Structural element layouts cannot build a thermal mapping.");
return DofFailure(
"invalid-dof-profile", {analysis_model.GetDomain().SourcePath(), 0U},
"structural", "Structural element layouts cannot build a thermal mapping.");
}
layouts.reserve(elements.size());
for (const auto& element : elements) {
@@ -183,13 +187,16 @@ Status DofManager::BuildThermal(
const AnalysisModel& analysis_model,
const std::vector<ThermalElementDofLayout>& layouts) {
if (!analysis_model.IsThermal() || analysis_model.ThermalStep() == nullptr) {
return DofFailure("invalid-dof-profile", {analysis_model.GetDomain().SourcePath(), 0U},
"thermal", "Thermal layouts require a thermal AnalysisModel.");
return DofFailure(
"invalid-dof-profile", {analysis_model.GetDomain().SourcePath(), 0U},
"thermal", "Thermal layouts require a thermal AnalysisModel.");
}
const Domain& domain = analysis_model.GetDomain();
if (layouts.size() != analysis_model.ActiveThermalElements().size()) {
return DofFailure("invalid-element-layout-order", {domain.SourcePath(), 0U},
std::to_string(layouts.size()), "Thermal layouts must match the active DS4 inventory.");
return DofFailure(
"invalid-element-layout-order", {domain.SourcePath(), 0U},
std::to_string(layouts.size()),
"Thermal layouts must match the active DS4 inventory.");
}
DofManager candidate;
candidate.profile_ = DofProfile::kThermal;
@@ -198,8 +205,9 @@ Status DofManager::BuildThermal(
++row) {
const EntityIndex node = analysis_model.ActiveThermalNodes()[row];
if (node >= candidate.thermal_node_rows_.size()) {
return DofFailure("invalid-thermal-node", {domain.SourcePath(), 0U},
std::to_string(node), "Active thermal node is outside Domain nodes.");
return DofFailure(
"invalid-thermal-node", {domain.SourcePath(), 0U},
std::to_string(node), "Active thermal node is outside Domain nodes.");
}
candidate.thermal_node_rows_[node] = row;
}
@@ -209,8 +217,9 @@ Status DofManager::BuildThermal(
for (std::size_t index = 0U; index < layouts.size(); ++index) {
const auto& definition = domain.ThermalElements()[index];
if (!SameSourceIdentity(layouts[index].source_id, definition.SourceId())) {
return DofFailure("invalid-element-layout-order", {domain.SourcePath(), 0U},
std::to_string(index), "Thermal layout identity changed source order.");
return DofFailure(
"invalid-element-layout-order", {domain.SourcePath(), 0U},
std::to_string(index), "Thermal layout identity changed source order.");
}
for (std::size_t node = 0U; node < layouts[index].node_indices.size();
++node) {
@@ -225,9 +234,10 @@ Status DofManager::BuildThermal(
candidate.element_scatters_.push_back(scatter.Value());
}
std::map<std::size_t, double> effective;
const auto apply = [&candidate, &effective](
const std::vector<ThermalBoundaryConditionDefinition>& boundaries,
const bool overrides) -> Status {
const auto apply =
[&candidate, &effective](
const std::vector<ThermalBoundaryConditionDefinition>& boundaries,
const bool overrides) -> Status {
for (const auto& boundary : boundaries) {
if (!IsThermalSurfaceDof(boundary.dof) || !std::isfinite(boundary.value)) {
return DofFailure("invalid-thermal-boundary", boundary.location, "BOUNDARY",
@@ -521,6 +531,15 @@ Result<std::vector<std::size_t>> DofManager::ThermalElementScatter(
"Thermal layouts require a thermal mapping."));
}
std::array<bool, kThermalDofsPerNode> components{};
const std::array<ThermalDofComponent, kThermalDofsPerNode> canonical{
{ThermalDofComponent::kBottom, ThermalDofComponent::kLower,
ThermalDofComponent::kMiddle, ThermalDofComponent::kUpper,
ThermalDofComponent::kTop}};
if (layout.components != canonical) {
return Result<std::vector<std::size_t>>::Failure(DofFailure(
"invalid-thermal-element-layout", {}, layout.source_id.source_label_text,
"Thermal layouts must preserve canonical bottom-to-top stations."));
}
for (const auto component : layout.components) {
const std::size_t index = ThermalComponentIndex(component);
if (index >= components.size() || components[index]) {
@@ -543,11 +562,16 @@ Result<std::vector<std::size_t>> DofManager::ThermalElementScatter(
}
}
}
if (std::adjacent_find(layout.node_indices.begin(), layout.node_indices.end()) !=
layout.node_indices.end()) {
return Result<std::vector<std::size_t>>::Failure(DofFailure(
"duplicate-element-dof", {}, layout.source_id.source_label_text,
"A thermal layout must not repeat a node."));
for (std::size_t index = 0U; index < layout.node_indices.size(); ++index) {
if (std::find(layout.node_indices.begin(),
layout.node_indices.begin() +
static_cast<std::ptrdiff_t>(index),
layout.node_indices[index]) !=
layout.node_indices.begin() + static_cast<std::ptrdiff_t>(index)) {
return Result<std::vector<std::size_t>>::Failure(DofFailure(
"duplicate-element-dof", {}, layout.source_id.source_label_text,
"A thermal layout must not repeat a node."));
}
}
return Result<std::vector<std::size_t>>::Success(std::move(scatter));
}