Integrate Refactor solver sources and dual MSVC CMake builds

This commit is contained in:
KOKO\Mimi
2026-10-18 02:10:51 +09:00
parent 7ee32e5837
commit 12d51480de
218 changed files with 17842 additions and 9689 deletions
+6 -113
View File
@@ -11,7 +11,8 @@
#include "fesa/constraints/essential_constraint_policy.h"
#include "fesa/loads/load.h"
#include "fesa/model/source_target_resolver.h"
#include "math/dense_blas_internal.h"
#include "assembly/ordered_contribution_internal.h"
namespace fesa {
namespace {
@@ -27,20 +28,6 @@ Status LoadFailure(const std::string& code, const SourceLocation& location,
{{Severity::kError, code, location, keyword, identity, message}});
}
Status ValidateFiniteVector(const Vector& values,
const SourceLocation& location,
const std::string& identity) {
for (std::size_t index = 0U; index < values.Size(); ++index) {
if (!std::isfinite(values[index])) {
return LoadFailure("nonfinite-load-value", location, "LOAD_ASSEMBLER",
identity + ":" + std::to_string(index),
"Load and prescribed displacement vectors must "
"contain finite values.");
}
}
return Status::Ok();
}
Status ValidateShellMoments(const Domain& domain, const Vector& full_load) {
if (domain.ShellElements().Empty()) {
return Status::Ok();
@@ -224,14 +211,13 @@ Result<Vector> LoadAssembler::AssembleFullNodalLoad(const AnalysisModel& model,
for (const auto& contributions : contributions_by_load) {
for (const auto& contribution : contributions) {
const std::size_t full_dof = contribution.full_dof_index;
const double accumulated = full_load[full_dof] + contribution.value;
if (!std::isfinite(accumulated)) {
if (!assembly_internal::AccumulateFiniteLoad(
full_load, full_dof, contribution.value)) {
return Result<Vector>::Failure(LoadFailure(
"nonfinite-load-accumulation", model.Step().Location(),
"LOAD_ASSEMBLER", std::to_string(full_dof),
"Source-order load accumulation produced a nonfinite value."));
}
full_load[full_dof] = accumulated;
}
}
const Status shell_moment_status = ValidateShellMoments(domain, full_load);
@@ -245,101 +231,8 @@ Result<Vector> LoadAssembler::EffectiveFreeRhs(const Vector& full_load,
const SparseMatrix& kfc,
const Vector& prescribed_values,
const DofManager& dofs) {
const SourceLocation location{{}, 0U};
if (dofs.FullDofCount() != full_load.Size()) {
return Result<Vector>::Failure(
LoadFailure("invalid-load-dimensions", location, "LOAD_ASSEMBLER",
std::to_string(dofs.FullDofCount()),
"The DofManager full dimension must match the full load."));
}
const Status dof_status = dofs.ValidateInvariants();
if (!dof_status.IsOk()) {
return Result<Vector>::Failure(dof_status);
}
if (kfc.Rows() != dofs.FreeDofCount() ||
kfc.Columns() != dofs.ConstrainedDofCount() ||
prescribed_values.Size() != dofs.ConstrainedDofCount()) {
return Result<Vector>::Failure(LoadFailure(
"invalid-load-dimensions", location, "LOAD_ASSEMBLER",
std::to_string(kfc.Rows()) + "x" + std::to_string(kfc.Columns()),
"Kfc rows/columns and prescribed values must match free/constrained "
"order."));
}
const Status matrix_status = kfc.Validate();
if (!matrix_status.IsOk()) {
return Result<Vector>::Failure(matrix_status);
}
const Status load_status =
ValidateFiniteVector(full_load, location, "full-load");
if (!load_status.IsOk()) {
return Result<Vector>::Failure(load_status);
}
const Status prescribed_status =
ValidateFiniteVector(prescribed_values, location, "prescribed-values");
if (!prescribed_status.IsOk()) {
return Result<Vector>::Failure(prescribed_status);
}
Vector entry_coefficients{kfc.Values().size()};
Vector entry_prescribed{kfc.Values().size()};
for (std::size_t position = 0U; position < kfc.Values().size(); ++position) {
entry_coefficients[position] = kfc.Values()[position];
entry_prescribed[position] =
prescribed_values[kfc.ColumnIndices()[position]];
}
Vector entry_products{0U};
try {
entry_products = dense_blas_internal::MultiplyDiagonal(entry_coefficients,
entry_prescribed);
} catch (const std::length_error&) {
return Result<Vector>::Failure(LoadFailure(
"load-backend-dimension-overflow", location, "LOAD_ASSEMBLER", "Kfc",
"Prescribed displacement products exceed the backend integer range."));
} catch (const std::invalid_argument&) {
return Result<Vector>::Failure(LoadFailure(
"invalid-load-product", location, "LOAD_ASSEMBLER", "Kfc",
"Prescribed displacement product inputs must be finite and compatible."));
}
// MKL computes independent entries; ordered CSR reduction below retains
// cancellation and the first product/row-sum overflow diagnostic.
Vector correction{kfc.Rows()};
for (std::size_t row = 0U; row < kfc.Rows(); ++row) {
double sum = 0.0;
for (std::size_t position = kfc.RowOffsets()[row];
position < kfc.RowOffsets()[row + 1U]; ++position) {
const double product = entry_products[position];
if (!std::isfinite(product)) {
return Result<Vector>::Failure(LoadFailure(
"nonfinite-load-accumulation", location, "LOAD_ASSEMBLER",
std::to_string(row),
"Kfc times prescribed displacement produced a nonfinite product."));
}
sum += product;
if (!std::isfinite(sum)) {
return Result<Vector>::Failure(LoadFailure(
"nonfinite-load-accumulation", location, "LOAD_ASSEMBLER",
std::to_string(row),
"Kfc times prescribed displacement produced a nonfinite row sum."));
}
}
correction[row] = sum;
}
Vector rhs = EssentialConstraintPolicy{}.GatherFree(full_load, dofs);
// The constrained vector is already in DofManager order, so this is the
// approved elimination equation rhs = Ff - Kfc*dc without reordering dc.
for (std::size_t row = 0U; row < rhs.Size(); ++row) {
const double value = rhs[row] - correction[row];
if (!std::isfinite(value)) {
return Result<Vector>::Failure(
LoadFailure("nonfinite-load-accumulation", location, "LOAD_ASSEMBLER",
std::to_string(row),
"Effective RHS subtraction produced a nonfinite value."));
}
rhs[row] = value;
}
return Result<Vector>::Success(std::move(rhs));
return EssentialConstraintPolicy{}.EffectiveFreeRhs(
full_load, kfc, prescribed_values, dofs);
}
} // namespace fesa
@@ -0,0 +1,58 @@
#ifndef FESA_ASSEMBLY_ORDERED_CONTRIBUTION_INTERNAL_H_
#define FESA_ASSEMBLY_ORDERED_CONTRIBUTION_INTERNAL_H_
#include <cmath>
#include <cstddef>
#include <vector>
#include "fesa/math/matrix.h"
#include "fesa/math/sparse_matrix.h"
#include "fesa/math/vector.h"
namespace fesa::assembly_internal {
/// @brief Appends one validated local entry without changing its stable order.
/// @note Caller owns shape/scatter validation and diagnostic selection. Retains
/// every structural zero; never writes shared global storage from a worker.
template <class Scatter>
void AppendLocalMatrixEntry(const Scatter& scatter, std::size_t row,
std::size_t column, double value,
std::size_t source_order,
std::vector<CooContribution>& contributions) {
contributions.push_back({scatter[row], scatter[column], value, source_order,
row * scatter.size() + column});
}
/// @brief Appends a validated matrix row in the original local-column order.
template <class Scatter>
void AppendLocalMatrixRow(const Matrix& values, const Scatter& scatter,
std::size_t row, std::size_t source_order,
std::vector<CooContribution>& contributions) {
for (std::size_t column = 0U; column < scatter.size(); ++column) {
AppendLocalMatrixEntry(scatter, row, column, values(row, column),
source_order, contributions);
}
}
/// @brief Appends a validated matrix in fixed local row/column order.
template <class Scatter>
void AppendLocalMatrix(const Matrix& values, const Scatter& scatter,
std::size_t source_order,
std::vector<CooContribution>& contributions) {
for (std::size_t row = 0U; row < scatter.size(); ++row) {
AppendLocalMatrixRow(values, scatter, row, source_order, contributions);
}
}
/// @brief Adds one full-space load entry, committing only a finite candidate.
/// @note Caller validates the index and keeps source-order error provenance.
inline bool AccumulateFiniteLoad(Vector& full, std::size_t index, double value) {
const double accumulated = full[index] + value;
if (!std::isfinite(value) || !std::isfinite(accumulated)) return false;
full[index] = accumulated;
return true;
}
} // namespace fesa::assembly_internal
#endif // FESA_ASSEMBLY_ORDERED_CONTRIBUTION_INTERNAL_H_
+5 -19
View File
@@ -15,6 +15,8 @@
#include "fesa/fem/dof_manager.h"
#include "fesa/model/domain.h"
#include "assembly/ordered_contribution_internal.h"
namespace fesa {
namespace {
@@ -26,15 +28,8 @@ Status AssemblyFailure(const std::string& code, const SourceLocation& location,
{{Severity::kError, code, location, "*ELEMENT", identity, message}});
}
bool SameSourceIdentity(const SourceEntityId& left,
const SourceEntityId& right) {
return left.instance_name == right.instance_name &&
left.source_label == right.source_label &&
left.source_label_text == right.source_label_text;
}
bool SameLayout(const ElementDofLayout& left, const ElementDofLayout& right) {
return SameSourceIdentity(left.source_id, right.source_id) &&
return SourceIdentityEquals(left.source_id, right.source_id) &&
left.node_indices == right.node_indices &&
left.components_per_node == right.components_per_node;
}
@@ -80,17 +75,8 @@ Result<SparseMatrix> SparseAssembler::Assemble(
auto& buffer = local_buffers[element_order];
buffer.reserve(local_dof_count * local_dof_count);
for (std::size_t local_row = 0U; local_row < local_dof_count; ++local_row) {
for (std::size_t local_column = 0U; local_column < local_dof_count;
++local_column) {
const std::size_t local_order =
local_row * local_dof_count + local_column;
buffer.push_back({scatter.Value()[local_row],
scatter.Value()[local_column],
contribution.values(local_row, local_column),
element_order, local_order});
}
}
assembly_internal::AppendLocalMatrix(
contribution.values, scatter.Value(), element_order, buffer);
});
for (std::size_t element_order = 0U; element_order < local_failures.size();
@@ -13,6 +13,8 @@
#include "fesa/elements/element_definition.h"
#include "fesa/model/domain.h"
#include "assembly/ordered_contribution_internal.h"
namespace fesa {
namespace {
@@ -250,14 +252,13 @@ Result<SurfacePressureAssemblyCandidate> AssembleSurfacePressure(
for (std::size_t local_row = 0U; local_row < kFacetDofCount; ++local_row) {
const std::size_t full_row = task.scatter[local_row];
const double force = contribution.force[local_row];
const double accumulated = full_force[full_row] + force;
if (!std::isfinite(force) || !std::isfinite(accumulated)) {
if (!assembly_internal::AccumulateFiniteLoad(full_force, full_row,
force)) {
return Result<SurfacePressureAssemblyCandidate>::Failure(Failure(
FailureCategory::kSolver, task.input.location,
"nonfinite-pressure-accumulation", task.input.entity_identity,
"Stable pressure force reduction produced a nonfinite value."));
}
full_force[full_row] = accumulated;
for (std::size_t local_column = 0U; local_column < kFacetDofCount;
++local_column) {
const double tangent = contribution.external_tangent[
@@ -268,12 +269,9 @@ Result<SurfacePressureAssemblyCandidate> AssembleSurfacePressure(
"nonfinite-pressure-tangent", task.input.entity_identity,
"A pressure local tangent contains a nonfinite value."));
}
tangent_contributions.push_back(
{full_row,
task.scatter[local_column],
tangent,
task_index,
local_row * kFacetDofCount + local_column});
assembly_internal::AppendLocalMatrixEntry(
task.scatter, local_row, local_column, tangent, task_index,
tangent_contributions);
}
}
}
+27 -10
View File
@@ -5,6 +5,7 @@
#include <cmath>
#include <cstddef>
#include <limits>
#include <memory>
#include <optional>
#include <stdexcept>
#include <string>
@@ -13,6 +14,8 @@
#include "fesa/elements/thermal_element_layout.h"
#include "assembly/ordered_contribution_internal.h"
namespace fesa {
namespace {
@@ -92,6 +95,7 @@ struct SumInterval {
struct LocalContribution {
std::optional<ThermalShell> kernel;
std::unique_ptr<ThermalElement> element;
std::array<std::size_t, 20> scatter{};
std::vector<CooContribution> conductivity;
std::vector<CooContribution> capacity;
@@ -105,8 +109,8 @@ Result<SparseMatrix> AssembleError(const SparseMatrix& matrix,
bool capacity) {
std::vector<SumInterval> sums(matrix.Values().size());
for (const auto& contribution : local) {
const Matrix& error = capacity ? contribution.kernel->CapacityError()
: contribution.kernel->ConductivityError();
const Matrix& error = capacity ? contribution.element->CapacityError()
: contribution.element->ConductivityError();
const auto& values =
capacity ? contribution.capacity : contribution.conductivity;
for (const auto& value : values) {
@@ -215,16 +219,21 @@ Result<ThermalAssembly> AssembleThermal(const AnalysisModel& model,
std::copy(scatter.Value().begin(), scatter.Value().end(),
output.scatter.begin());
output.kernel = std::move(kernel.Value());
// local is fully sized before workers run; this optional cannot move
// until every local matrix/error consumer has finished.
output.element = std::make_unique<Ds4ThermalAdapter>(*output.kernel);
output.conductivity.reserve(400);
output.capacity.reserve(400);
const Matrix& conductivity_matrix = output.element->Conductivity();
const Matrix& capacity_matrix = output.element->Capacity();
for (std::size_t i = 0; i < 20; ++i)
for (std::size_t j = 0; j < 20; ++j) {
output.conductivity.push_back({output.scatter[i], output.scatter[j],
output.kernel->Conductivity()(i, j),
index, 20 * i + j});
output.capacity.push_back({output.scatter[i], output.scatter[j],
output.kernel->Capacity()(i, j), index,
20 * i + j});
assembly_internal::AppendLocalMatrixEntry(
output.scatter, i, j, conductivity_matrix(i, j), index,
output.conductivity);
assembly_internal::AppendLocalMatrixEntry(
output.scatter, i, j, capacity_matrix(i, j), index,
output.capacity);
}
});
std::vector<CooContribution> conductivity;
@@ -310,7 +319,11 @@ ThermalAssembly::ThermalAssembly(
input_error_{std::move(input_error)},
element_order_{std::move(element_order)},
kernels_{std::move(kernels)},
scatters_{std::move(scatters)} {}
scatters_{std::move(scatters)} {
elements_.reserve(kernels_.size());
for (const auto& kernel : kernels_)
elements_.push_back(std::make_unique<Ds4ThermalAdapter>(kernel));
}
const SparseMatrix& ThermalAssembly::Conductivity() const noexcept {
return conductivity_;
}
@@ -330,6 +343,10 @@ const Vector& ThermalAssembly::HeatInputError() const noexcept {
const std::vector<EntityIndex>& ThermalAssembly::ElementOrder() const noexcept {
return element_order_;
}
const std::vector<std::unique_ptr<ThermalElement>>&
ThermalAssembly::Elements() const noexcept {
return elements_;
}
const std::vector<ThermalShell>& ThermalAssembly::Kernels() const noexcept {
return kernels_;
}
@@ -358,7 +375,7 @@ Result<std::vector<ThermalElementFlux>> ThermalAssembly::RecoverFlux(
std::array<double, 20> local{};
for (std::size_t p = 0; p < 20; ++p)
local[p] = temperature[scatters_[index][p]];
const auto result = kernels_[index].Recover(local);
const auto result = elements_[index]->Recover(local);
if (!result.HasValue()) {
status[index] = result.GetStatus();
return;