Integrate Refactor solver sources and dual MSVC CMake builds
This commit is contained in:
@@ -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_
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user