fix(equation-and-linear-solve): address review findings
This commit is contained in:
@@ -47,7 +47,13 @@ SymmetricCsr build_sparsity_pattern(
|
||||
const DofManager& dofs) {
|
||||
using Coordinate = std::pair<std::size_t, std::size_t>;
|
||||
std::vector<Coordinate> coordinates;
|
||||
coordinates.reserve(domain.beam_elements().size() * 78);
|
||||
coordinates.reserve(
|
||||
dofs.full_dof_count() +
|
||||
domain.beam_elements().size() * 78);
|
||||
|
||||
for (std::size_t full = 0; full < dofs.full_dof_count(); ++full) {
|
||||
coordinates.emplace_back(full, full);
|
||||
}
|
||||
|
||||
for (const BeamElement& element : domain.beam_elements()) {
|
||||
const std::array<std::size_t, 12> full_dofs =
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include <fesa/constraints/essential_bc.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
@@ -33,6 +34,16 @@ std::optional<std::string> validate_system(
|
||||
if (matrix.column_indices.size() != matrix.values.size()) {
|
||||
return "CSR column and value counts must match.";
|
||||
}
|
||||
if (!std::ranges::all_of(
|
||||
matrix.values, [](const double value) {
|
||||
return std::isfinite(value);
|
||||
}) ||
|
||||
!std::ranges::all_of(
|
||||
system.force, [](const double value) {
|
||||
return std::isfinite(value);
|
||||
})) {
|
||||
return "Matrix and force values must be finite.";
|
||||
}
|
||||
|
||||
std::int32_t previous_offset = 0;
|
||||
for (const std::int32_t offset : matrix.row_offsets) {
|
||||
@@ -92,8 +103,7 @@ std::int32_t csr_index(const std::size_t value) {
|
||||
|
||||
ConstraintResult eliminate_essential_bcs(
|
||||
const EquationSystem& original,
|
||||
const DofManager& dofs,
|
||||
const std::span<const PrescribedDof> prescribed) {
|
||||
const DofManager& dofs) {
|
||||
ConstraintResult result;
|
||||
if (const auto error = validate_system(original);
|
||||
error.has_value()) {
|
||||
@@ -109,91 +119,31 @@ ConstraintResult eliminate_essential_bcs(
|
||||
}
|
||||
|
||||
const std::size_t full_count = original.stiffness.order;
|
||||
std::vector<bool> constrained(full_count, false);
|
||||
std::vector<double> prescribed_full(full_count, 0.0);
|
||||
for (const PrescribedDof& value : prescribed) {
|
||||
if (value.dof < 1 || value.dof > 6 ||
|
||||
!std::isfinite(value.value)) {
|
||||
result.diagnostics.push_back(equation_error(
|
||||
"equation.invalid_prescribed_dof",
|
||||
"Prescribed DOF data must contain a DOF in [1, 6] "
|
||||
"and a finite value."));
|
||||
continue;
|
||||
}
|
||||
|
||||
const DofAddress address{
|
||||
value.node,
|
||||
static_cast<NodeDof>(value.dof - std::uint8_t{1}),
|
||||
};
|
||||
std::size_t full = 0;
|
||||
std::optional<double> owned_value;
|
||||
try {
|
||||
full = dofs.full_dof(address);
|
||||
owned_value = dofs.prescribed_value(address);
|
||||
} catch (const std::out_of_range&) {
|
||||
result.diagnostics.push_back(equation_error(
|
||||
"equation.invalid_prescribed_dof",
|
||||
"Prescribed DOF references a node absent from the "
|
||||
"DofManager."));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (constrained[full]) {
|
||||
result.diagnostics.push_back(equation_error(
|
||||
"equation.conflicting_prescribed_dof",
|
||||
"A full DOF has more than one prescribed value."));
|
||||
continue;
|
||||
}
|
||||
constrained[full] = true;
|
||||
|
||||
if (!owned_value.has_value() ||
|
||||
*owned_value != value.value) {
|
||||
result.diagnostics.push_back(equation_error(
|
||||
"equation.prescribed_dof_mismatch",
|
||||
"Prescribed DOFs must match the state owned by the "
|
||||
"DofManager."));
|
||||
continue;
|
||||
}
|
||||
prescribed_full[full] = *owned_value;
|
||||
}
|
||||
|
||||
const std::size_t constrained_count =
|
||||
full_count - dofs.free_equation_count();
|
||||
if (prescribed.size() != constrained_count) {
|
||||
result.diagnostics.push_back(equation_error(
|
||||
"equation.prescribed_dof_mismatch",
|
||||
"Prescribed DOF count must match the DofManager "
|
||||
"constraint state."));
|
||||
}
|
||||
if (!result.diagnostics.empty()) {
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<std::size_t> full_to_free(full_count, full_count);
|
||||
std::vector<std::size_t> free_to_full;
|
||||
free_to_full.reserve(dofs.free_equation_count());
|
||||
std::vector<double> prescribed_full(full_count, 0.0);
|
||||
for (std::size_t full = 0; full < full_count; ++full) {
|
||||
if (!constrained[full]) {
|
||||
full_to_free[full] = free_to_full.size();
|
||||
free_to_full.push_back(full);
|
||||
const std::optional<std::size_t> equation =
|
||||
dofs.equation(full);
|
||||
if (equation.has_value()) {
|
||||
full_to_free[full] = *equation;
|
||||
} else {
|
||||
prescribed_full[full] =
|
||||
dofs.prescribed_value(full).value();
|
||||
}
|
||||
}
|
||||
if (free_to_full.size() != dofs.free_equation_count()) {
|
||||
result.diagnostics.push_back(equation_error(
|
||||
"equation.prescribed_dof_mismatch",
|
||||
"Prescribed DOFs do not identify the DofManager constraints."));
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<double> reduced_force(
|
||||
free_to_full.size(), 0.0);
|
||||
for (std::size_t free = 0; free < free_to_full.size(); ++free) {
|
||||
reduced_force[free] = original.force[free_to_full[free]];
|
||||
dofs.free_equation_count(), 0.0);
|
||||
for (std::size_t full = 0; full < full_count; ++full) {
|
||||
if (full_to_free[full] != full_count) {
|
||||
reduced_force[full_to_free[full]] = original.force[full];
|
||||
}
|
||||
}
|
||||
|
||||
SymmetricCsr reduced{
|
||||
free_to_full.size(),
|
||||
std::vector<std::int32_t>(free_to_full.size() + 1, 0),
|
||||
dofs.free_equation_count(),
|
||||
std::vector<std::int32_t>(
|
||||
dofs.free_equation_count() + 1, 0),
|
||||
{},
|
||||
{},
|
||||
};
|
||||
@@ -212,12 +162,12 @@ ConstraintResult eliminate_essential_bcs(
|
||||
original.stiffness.column_indices[entry]);
|
||||
const double stiffness = original.stiffness.values[entry];
|
||||
|
||||
if (constrained[full_row]) {
|
||||
if (!constrained[full_column]) {
|
||||
if (full_to_free[full_row] == full_count) {
|
||||
if (full_to_free[full_column] != full_count) {
|
||||
reduced_force[full_to_free[full_column]] -=
|
||||
stiffness * prescribed_full[full_row];
|
||||
}
|
||||
} else if (constrained[full_column]) {
|
||||
} else if (full_to_free[full_column] == full_count) {
|
||||
reduced_force[full_to_free[full_row]] -=
|
||||
stiffness * prescribed_full[full_column];
|
||||
} else {
|
||||
@@ -226,17 +176,24 @@ ConstraintResult eliminate_essential_bcs(
|
||||
reduced.values.push_back(stiffness);
|
||||
}
|
||||
}
|
||||
if (!constrained[full_row]) {
|
||||
if (full_to_free[full_row] != full_count) {
|
||||
reduced.row_offsets[full_to_free[full_row] + 1] =
|
||||
csr_index(reduced.column_indices.size());
|
||||
}
|
||||
}
|
||||
if (!std::ranges::all_of(
|
||||
reduced_force, [](const double value) {
|
||||
return std::isfinite(value);
|
||||
})) {
|
||||
result.diagnostics.push_back(equation_error(
|
||||
"equation.nonfinite_result",
|
||||
"Essential BC elimination produced a nonfinite force."));
|
||||
return result;
|
||||
}
|
||||
|
||||
result.reduced_system = ReducedSystem{
|
||||
std::move(reduced),
|
||||
std::move(reduced_force),
|
||||
std::move(free_to_full),
|
||||
std::move(prescribed_full),
|
||||
};
|
||||
return result;
|
||||
}
|
||||
@@ -252,6 +209,13 @@ std::vector<double> recover_reaction(
|
||||
throw std::invalid_argument{
|
||||
"Full displacement size must equal the matrix order."};
|
||||
}
|
||||
if (!std::ranges::all_of(
|
||||
full_displacement, [](const double value) {
|
||||
return std::isfinite(value);
|
||||
})) {
|
||||
throw std::invalid_argument{
|
||||
"Full displacement values must be finite."};
|
||||
}
|
||||
|
||||
std::vector<double> reaction = original.force;
|
||||
for (double& value : reaction) {
|
||||
@@ -277,6 +241,13 @@ std::vector<double> recover_reaction(
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!std::ranges::all_of(
|
||||
reaction, [](const double value) {
|
||||
return std::isfinite(value);
|
||||
})) {
|
||||
throw std::invalid_argument{
|
||||
"Reaction recovery produced a nonfinite value."};
|
||||
}
|
||||
return reaction;
|
||||
}
|
||||
|
||||
|
||||
@@ -60,16 +60,25 @@ std::size_t DofManager::free_equation_count() const noexcept {
|
||||
|
||||
std::optional<std::size_t> DofManager::equation(
|
||||
const DofAddress address) const {
|
||||
return equations_[full_dof(address)];
|
||||
return equation(full_dof(address));
|
||||
}
|
||||
|
||||
std::optional<std::size_t> DofManager::equation(
|
||||
const std::size_t full_dof) const {
|
||||
return equations_.at(full_dof);
|
||||
}
|
||||
|
||||
std::optional<double> DofManager::prescribed_value(
|
||||
const DofAddress address) const {
|
||||
const std::size_t full = full_dof(address);
|
||||
if (equations_[full].has_value()) {
|
||||
return prescribed_value(full_dof(address));
|
||||
}
|
||||
|
||||
std::optional<double> DofManager::prescribed_value(
|
||||
const std::size_t full_dof) const {
|
||||
if (equations_.at(full_dof).has_value()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return prescribed_values_[full];
|
||||
return prescribed_values_.at(full_dof);
|
||||
}
|
||||
|
||||
std::array<std::size_t, 12> DofManager::element_full_dofs(
|
||||
|
||||
@@ -106,9 +106,14 @@ public:
|
||||
PardisoSession& operator=(const PardisoSession&) = delete;
|
||||
|
||||
~PardisoSession() noexcept {
|
||||
static_cast<void>(release());
|
||||
}
|
||||
|
||||
MKL_INT release() noexcept {
|
||||
if (!active_) {
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
active_ = false;
|
||||
|
||||
constexpr MKL_INT release_all = -1;
|
||||
MKL_INT error = 0;
|
||||
@@ -129,6 +134,7 @@ public:
|
||||
right_hand_side_->data(),
|
||||
solution_->data(),
|
||||
&error);
|
||||
return error;
|
||||
}
|
||||
|
||||
MKL_INT execute(
|
||||
@@ -260,37 +266,45 @@ LinearSolveResult PardisoLinearSolver::solve(
|
||||
PardisoSession session;
|
||||
|
||||
constexpr MKL_INT analyze = 11;
|
||||
if (const MKL_INT error =
|
||||
session.execute(
|
||||
analyze, matrix, right_hand_side, solution);
|
||||
error != 0) {
|
||||
MKL_INT error = session.execute(
|
||||
analyze, matrix, right_hand_side, solution);
|
||||
if (error != 0) {
|
||||
result.diagnostics.push_back(solver_error(
|
||||
"solver.analysis_failed",
|
||||
pardiso_failure("analysis", error)));
|
||||
return result;
|
||||
}
|
||||
|
||||
constexpr MKL_INT factorize = 22;
|
||||
if (const MKL_INT error =
|
||||
session.execute(
|
||||
factorize, matrix, right_hand_side, solution);
|
||||
error != 0) {
|
||||
result.diagnostics.push_back(solver_error(
|
||||
"solver.factorization_failed",
|
||||
pardiso_failure("factorization", error)));
|
||||
return result;
|
||||
if (error == 0) {
|
||||
constexpr MKL_INT factorize = 22;
|
||||
error = session.execute(
|
||||
factorize, matrix, right_hand_side, solution);
|
||||
if (error != 0) {
|
||||
result.diagnostics.push_back(solver_error(
|
||||
"solver.factorization_failed",
|
||||
pardiso_failure("factorization", error)));
|
||||
}
|
||||
}
|
||||
|
||||
constexpr MKL_INT solve_system = 33;
|
||||
if (const MKL_INT error =
|
||||
session.execute(
|
||||
solve_system, matrix, right_hand_side, solution);
|
||||
error != 0) {
|
||||
result.diagnostics.push_back(solver_error(
|
||||
"solver.solve_failed",
|
||||
pardiso_failure("solve", error)));
|
||||
return result;
|
||||
if (error == 0) {
|
||||
constexpr MKL_INT solve_system = 33;
|
||||
error = session.execute(
|
||||
solve_system, matrix, right_hand_side, solution);
|
||||
if (error != 0) {
|
||||
result.diagnostics.push_back(solver_error(
|
||||
"solver.solve_failed",
|
||||
pardiso_failure("solve", error)));
|
||||
}
|
||||
}
|
||||
|
||||
if (const MKL_INT release_error = session.release();
|
||||
release_error != 0) {
|
||||
result.diagnostics.push_back(solver_error(
|
||||
"solver.release_failed",
|
||||
pardiso_failure("release", release_error)));
|
||||
}
|
||||
}
|
||||
if (!result.diagnostics.empty()) {
|
||||
return result;
|
||||
}
|
||||
if (!std::ranges::all_of(solution, [](const double value) {
|
||||
return std::isfinite(value);
|
||||
|
||||
Reference in New Issue
Block a user