refactor: extract math solver boundary
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
#include "fesa/Boundary/Boundary.hpp"
|
||||
#include "fesa/Core/Types.hpp"
|
||||
#include "fesa/Load/Load.hpp"
|
||||
#include "fesa/Math/Vector.hpp"
|
||||
#include "fesa/Property/Property.hpp"
|
||||
#include "fesa/Util/String.hpp"
|
||||
|
||||
@@ -13,12 +14,6 @@
|
||||
|
||||
namespace fesa {
|
||||
|
||||
struct Vec3 {
|
||||
Real x = 0.0;
|
||||
Real y = 0.0;
|
||||
Real z = 0.0;
|
||||
};
|
||||
|
||||
struct Node {
|
||||
GlobalId id = 0;
|
||||
Vec3 coordinates;
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
#pragma once
|
||||
|
||||
#include "fesa/Core/Types.hpp"
|
||||
|
||||
#include <cstddef>
|
||||
#include <vector>
|
||||
|
||||
namespace fesa {
|
||||
|
||||
class DenseMatrix {
|
||||
public:
|
||||
DenseMatrix() = default;
|
||||
DenseMatrix(LocalIndex rows, LocalIndex cols) : rows_(rows), cols_(cols), values_(static_cast<std::size_t>(rows * cols), 0.0) {}
|
||||
|
||||
LocalIndex rows() const {
|
||||
return rows_;
|
||||
}
|
||||
|
||||
LocalIndex cols() const {
|
||||
return cols_;
|
||||
}
|
||||
|
||||
Real& operator()(LocalIndex row, LocalIndex col) {
|
||||
return values_[static_cast<std::size_t>(row * cols_ + col)];
|
||||
}
|
||||
|
||||
Real operator()(LocalIndex row, LocalIndex col) const {
|
||||
return values_[static_cast<std::size_t>(row * cols_ + col)];
|
||||
}
|
||||
|
||||
void add(LocalIndex row, LocalIndex col, Real value) {
|
||||
(*this)(row, col) += value;
|
||||
}
|
||||
|
||||
std::vector<Real> multiply(const std::vector<Real>& x) const {
|
||||
std::vector<Real> y(static_cast<std::size_t>(rows_), 0.0);
|
||||
for (LocalIndex i = 0; i < rows_; ++i) {
|
||||
Real sum = 0.0;
|
||||
for (LocalIndex j = 0; j < cols_; ++j) {
|
||||
sum += (*this)(i, j) * x[static_cast<std::size_t>(j)];
|
||||
}
|
||||
y[static_cast<std::size_t>(i)] = sum;
|
||||
}
|
||||
return y;
|
||||
}
|
||||
|
||||
private:
|
||||
LocalIndex rows_ = 0;
|
||||
LocalIndex cols_ = 0;
|
||||
std::vector<Real> values_;
|
||||
};
|
||||
|
||||
} // namespace fesa
|
||||
@@ -0,0 +1,81 @@
|
||||
#pragma once
|
||||
|
||||
#include "fesa/Math/DenseMatrix.hpp"
|
||||
#include "fesa/Util/Diagnostics.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cstddef>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace fesa {
|
||||
|
||||
struct SolveResult {
|
||||
std::vector<Real> x;
|
||||
std::vector<Diagnostic> diagnostics;
|
||||
|
||||
bool ok() const {
|
||||
return !hasError(diagnostics);
|
||||
}
|
||||
};
|
||||
|
||||
class LinearSolver {
|
||||
public:
|
||||
virtual ~LinearSolver() = default;
|
||||
virtual SolveResult solve(DenseMatrix a, std::vector<Real> b) const = 0;
|
||||
};
|
||||
|
||||
class GaussianEliminationSolver final : public LinearSolver {
|
||||
public:
|
||||
SolveResult solve(DenseMatrix a, std::vector<Real> b) const override {
|
||||
const LocalIndex n = a.rows();
|
||||
SolveResult result;
|
||||
if (a.rows() != a.cols() || static_cast<LocalIndex>(b.size()) != n) {
|
||||
result.diagnostics.push_back(makeDiagnostic(Severity::Error, "FESA-SOLVER-SIZE", "Linear system size mismatch", "solver"));
|
||||
return result;
|
||||
}
|
||||
for (LocalIndex col = 0; col < n; ++col) {
|
||||
LocalIndex pivot = col;
|
||||
Real pivot_abs = std::fabs(a(col, col));
|
||||
for (LocalIndex row = col + 1; row < n; ++row) {
|
||||
const Real candidate = std::fabs(a(row, col));
|
||||
if (candidate > pivot_abs) {
|
||||
pivot_abs = candidate;
|
||||
pivot = row;
|
||||
}
|
||||
}
|
||||
if (pivot_abs < 1.0e-12) {
|
||||
result.diagnostics.push_back(makeDiagnostic(Severity::Error, "FESA-SINGULAR-SOLVER",
|
||||
"Reduced system is singular or ill-conditioned", "solver"));
|
||||
return result;
|
||||
}
|
||||
if (pivot != col) {
|
||||
for (LocalIndex j = col; j < n; ++j) {
|
||||
std::swap(a(col, j), a(pivot, j));
|
||||
}
|
||||
std::swap(b[static_cast<std::size_t>(col)], b[static_cast<std::size_t>(pivot)]);
|
||||
}
|
||||
const Real diag = a(col, col);
|
||||
for (LocalIndex row = col + 1; row < n; ++row) {
|
||||
const Real factor = a(row, col) / diag;
|
||||
a(row, col) = 0.0;
|
||||
for (LocalIndex j = col + 1; j < n; ++j) {
|
||||
a(row, j) -= factor * a(col, j);
|
||||
}
|
||||
b[static_cast<std::size_t>(row)] -= factor * b[static_cast<std::size_t>(col)];
|
||||
}
|
||||
}
|
||||
result.x.assign(static_cast<std::size_t>(n), 0.0);
|
||||
for (LocalIndex i = n; i-- > 0;) {
|
||||
Real sum = b[static_cast<std::size_t>(i)];
|
||||
for (LocalIndex j = i + 1; j < n; ++j) {
|
||||
sum -= a(i, j) * result.x[static_cast<std::size_t>(j)];
|
||||
}
|
||||
result.x[static_cast<std::size_t>(i)] = sum / a(i, i);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace fesa
|
||||
@@ -1,5 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include "fesa/Math/DenseMatrix.hpp"
|
||||
#include "fesa/Math/LinearSolver.hpp"
|
||||
#include "fesa/Math/SparsePattern.hpp"
|
||||
#include "fesa/Math/Vector.hpp"
|
||||
#include "fesa/ModuleInfo.hpp"
|
||||
|
||||
namespace fesa::module {
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#include "fesa/Core/Types.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
|
||||
namespace fesa {
|
||||
|
||||
struct SparsePatternEntry {
|
||||
EquationId row = 0;
|
||||
EquationId col = 0;
|
||||
};
|
||||
|
||||
struct SparsePattern {
|
||||
EquationId equation_count = 0;
|
||||
std::vector<SparsePatternEntry> entries;
|
||||
|
||||
SparseIndex nonzeroCount() const {
|
||||
return static_cast<SparseIndex>(entries.size());
|
||||
}
|
||||
|
||||
bool contains(EquationId row, EquationId col) const {
|
||||
return std::any_of(entries.begin(), entries.end(), [&](const SparsePatternEntry& entry) {
|
||||
return entry.row == row && entry.col == col;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace fesa
|
||||
@@ -0,0 +1,66 @@
|
||||
#pragma once
|
||||
|
||||
#include "fesa/Core/Types.hpp"
|
||||
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
#include <optional>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace fesa {
|
||||
|
||||
struct Vec3 {
|
||||
Real x = 0.0;
|
||||
Real y = 0.0;
|
||||
Real z = 0.0;
|
||||
};
|
||||
|
||||
inline Vec3 operator+(const Vec3& a, const Vec3& b) {
|
||||
return {a.x + b.x, a.y + b.y, a.z + b.z};
|
||||
}
|
||||
|
||||
inline Vec3 operator-(const Vec3& a, const Vec3& b) {
|
||||
return {a.x - b.x, a.y - b.y, a.z - b.z};
|
||||
}
|
||||
|
||||
inline Vec3 operator*(Real scalar, const Vec3& value) {
|
||||
return {scalar * value.x, scalar * value.y, scalar * value.z};
|
||||
}
|
||||
|
||||
inline Real dot(const Vec3& a, const Vec3& b) {
|
||||
return a.x * b.x + a.y * b.y + a.z * b.z;
|
||||
}
|
||||
|
||||
inline Vec3 cross(const Vec3& a, const Vec3& b) {
|
||||
return {a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x};
|
||||
}
|
||||
|
||||
inline Real norm(const Vec3& value) {
|
||||
return std::sqrt(dot(value, value));
|
||||
}
|
||||
|
||||
inline bool isFinite(Real value) {
|
||||
return std::isfinite(value);
|
||||
}
|
||||
|
||||
inline bool isFinite(const Vec3& value) {
|
||||
return isFinite(value.x) && isFinite(value.y) && isFinite(value.z);
|
||||
}
|
||||
|
||||
inline std::optional<Vec3> normalizedIfValid(const Vec3& value, Real tolerance = 1.0e-12) {
|
||||
const Real length = norm(value);
|
||||
if (!isFinite(length) || length <= tolerance) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return (1.0 / length) * value;
|
||||
}
|
||||
|
||||
inline Vec3 normalized(const Vec3& value) {
|
||||
const Real length = norm(value);
|
||||
if (length <= std::numeric_limits<Real>::epsilon()) {
|
||||
throw std::runtime_error("zero-length vector");
|
||||
}
|
||||
return (1.0 / length) * value;
|
||||
}
|
||||
|
||||
} // namespace fesa
|
||||
+1
-178
@@ -3,6 +3,7 @@
|
||||
#include "fesa/Boundary/Boundary.hpp"
|
||||
#include "fesa/Core/Core.hpp"
|
||||
#include "fesa/Load/Load.hpp"
|
||||
#include "fesa/Math/Math.hpp"
|
||||
#include "fesa/ModuleInfo.hpp"
|
||||
#include "fesa/Property/Property.hpp"
|
||||
#include "fesa/Util/Util.hpp"
|
||||
@@ -27,54 +28,6 @@
|
||||
|
||||
namespace fesa {
|
||||
|
||||
inline Vec3 operator+(const Vec3& a, const Vec3& b) {
|
||||
return {a.x + b.x, a.y + b.y, a.z + b.z};
|
||||
}
|
||||
|
||||
inline Vec3 operator-(const Vec3& a, const Vec3& b) {
|
||||
return {a.x - b.x, a.y - b.y, a.z - b.z};
|
||||
}
|
||||
|
||||
inline Vec3 operator*(Real scalar, const Vec3& value) {
|
||||
return {scalar * value.x, scalar * value.y, scalar * value.z};
|
||||
}
|
||||
|
||||
inline Real dot(const Vec3& a, const Vec3& b) {
|
||||
return a.x * b.x + a.y * b.y + a.z * b.z;
|
||||
}
|
||||
|
||||
inline Vec3 cross(const Vec3& a, const Vec3& b) {
|
||||
return {a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x};
|
||||
}
|
||||
|
||||
inline Real norm(const Vec3& value) {
|
||||
return std::sqrt(dot(value, value));
|
||||
}
|
||||
|
||||
inline bool isFinite(Real value) {
|
||||
return std::isfinite(value);
|
||||
}
|
||||
|
||||
inline bool isFinite(const Vec3& value) {
|
||||
return isFinite(value.x) && isFinite(value.y) && isFinite(value.z);
|
||||
}
|
||||
|
||||
inline std::optional<Vec3> normalizedIfValid(const Vec3& value, Real tolerance = 1.0e-12) {
|
||||
const Real length = norm(value);
|
||||
if (!isFinite(length) || length <= tolerance) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return (1.0 / length) * value;
|
||||
}
|
||||
|
||||
inline Vec3 normalized(const Vec3& value) {
|
||||
const Real length = norm(value);
|
||||
if (length <= std::numeric_limits<Real>::epsilon()) {
|
||||
throw std::runtime_error("zero-length vector");
|
||||
}
|
||||
return (1.0 / length) * value;
|
||||
}
|
||||
|
||||
struct KeywordLine {
|
||||
std::string name;
|
||||
std::map<std::string, std::string> parameters;
|
||||
@@ -518,26 +471,6 @@ class AbaqusInputParser {
|
||||
}
|
||||
};
|
||||
|
||||
struct SparsePatternEntry {
|
||||
EquationId row = 0;
|
||||
EquationId col = 0;
|
||||
};
|
||||
|
||||
struct SparsePattern {
|
||||
EquationId equation_count = 0;
|
||||
std::vector<SparsePatternEntry> entries;
|
||||
|
||||
SparseIndex nonzeroCount() const {
|
||||
return static_cast<SparseIndex>(entries.size());
|
||||
}
|
||||
|
||||
bool contains(EquationId row, EquationId col) const {
|
||||
return std::any_of(entries.begin(), entries.end(), [&](const SparsePatternEntry& entry) {
|
||||
return entry.row == row && entry.col == col;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
inline SparsePattern buildReducedSparsePattern(const Domain& domain, const DofManager& dofs) {
|
||||
SparsePattern pattern;
|
||||
pattern.equation_count = dofs.freeDofCount();
|
||||
@@ -564,49 +497,6 @@ inline SparsePattern buildReducedSparsePattern(const Domain& domain, const DofMa
|
||||
return pattern;
|
||||
}
|
||||
|
||||
class DenseMatrix {
|
||||
public:
|
||||
DenseMatrix() = default;
|
||||
DenseMatrix(LocalIndex rows, LocalIndex cols) : rows_(rows), cols_(cols), values_(static_cast<std::size_t>(rows * cols), 0.0) {}
|
||||
|
||||
LocalIndex rows() const {
|
||||
return rows_;
|
||||
}
|
||||
|
||||
LocalIndex cols() const {
|
||||
return cols_;
|
||||
}
|
||||
|
||||
Real& operator()(LocalIndex row, LocalIndex col) {
|
||||
return values_[static_cast<std::size_t>(row * cols_ + col)];
|
||||
}
|
||||
|
||||
Real operator()(LocalIndex row, LocalIndex col) const {
|
||||
return values_[static_cast<std::size_t>(row * cols_ + col)];
|
||||
}
|
||||
|
||||
void add(LocalIndex row, LocalIndex col, Real value) {
|
||||
(*this)(row, col) += value;
|
||||
}
|
||||
|
||||
std::vector<Real> multiply(const std::vector<Real>& x) const {
|
||||
std::vector<Real> y(static_cast<std::size_t>(rows_), 0.0);
|
||||
for (LocalIndex i = 0; i < rows_; ++i) {
|
||||
Real sum = 0.0;
|
||||
for (LocalIndex j = 0; j < cols_; ++j) {
|
||||
sum += (*this)(i, j) * x[static_cast<std::size_t>(j)];
|
||||
}
|
||||
y[static_cast<std::size_t>(i)] = sum;
|
||||
}
|
||||
return y;
|
||||
}
|
||||
|
||||
private:
|
||||
LocalIndex rows_ = 0;
|
||||
LocalIndex cols_ = 0;
|
||||
std::vector<Real> values_;
|
||||
};
|
||||
|
||||
inline std::vector<Real> recoverFullReaction(const DenseMatrix& k_full, const std::vector<Real>& u_full, const std::vector<Real>& f_full) {
|
||||
if (k_full.rows() != k_full.cols() || static_cast<LocalIndex>(u_full.size()) != k_full.cols() ||
|
||||
static_cast<LocalIndex>(f_full.size()) != k_full.rows()) {
|
||||
@@ -619,73 +509,6 @@ inline std::vector<Real> recoverFullReaction(const DenseMatrix& k_full, const st
|
||||
return reaction;
|
||||
}
|
||||
|
||||
struct SolveResult {
|
||||
std::vector<Real> x;
|
||||
std::vector<Diagnostic> diagnostics;
|
||||
|
||||
bool ok() const {
|
||||
return !hasError(diagnostics);
|
||||
}
|
||||
};
|
||||
|
||||
class LinearSolver {
|
||||
public:
|
||||
virtual ~LinearSolver() = default;
|
||||
virtual SolveResult solve(DenseMatrix a, std::vector<Real> b) const = 0;
|
||||
};
|
||||
|
||||
class GaussianEliminationSolver final : public LinearSolver {
|
||||
public:
|
||||
SolveResult solve(DenseMatrix a, std::vector<Real> b) const override {
|
||||
const LocalIndex n = a.rows();
|
||||
SolveResult result;
|
||||
if (a.rows() != a.cols() || static_cast<LocalIndex>(b.size()) != n) {
|
||||
result.diagnostics.push_back(makeDiagnostic(Severity::Error, "FESA-SOLVER-SIZE", "Linear system size mismatch", "solver"));
|
||||
return result;
|
||||
}
|
||||
for (LocalIndex col = 0; col < n; ++col) {
|
||||
LocalIndex pivot = col;
|
||||
Real pivot_abs = std::fabs(a(col, col));
|
||||
for (LocalIndex row = col + 1; row < n; ++row) {
|
||||
const Real candidate = std::fabs(a(row, col));
|
||||
if (candidate > pivot_abs) {
|
||||
pivot_abs = candidate;
|
||||
pivot = row;
|
||||
}
|
||||
}
|
||||
if (pivot_abs < 1.0e-12) {
|
||||
result.diagnostics.push_back(makeDiagnostic(Severity::Error, "FESA-SINGULAR-SOLVER",
|
||||
"Reduced system is singular or ill-conditioned", "solver"));
|
||||
return result;
|
||||
}
|
||||
if (pivot != col) {
|
||||
for (LocalIndex j = col; j < n; ++j) {
|
||||
std::swap(a(col, j), a(pivot, j));
|
||||
}
|
||||
std::swap(b[static_cast<std::size_t>(col)], b[static_cast<std::size_t>(pivot)]);
|
||||
}
|
||||
const Real diag = a(col, col);
|
||||
for (LocalIndex row = col + 1; row < n; ++row) {
|
||||
const Real factor = a(row, col) / diag;
|
||||
a(row, col) = 0.0;
|
||||
for (LocalIndex j = col + 1; j < n; ++j) {
|
||||
a(row, j) -= factor * a(col, j);
|
||||
}
|
||||
b[static_cast<std::size_t>(row)] -= factor * b[static_cast<std::size_t>(col)];
|
||||
}
|
||||
}
|
||||
result.x.assign(static_cast<std::size_t>(n), 0.0);
|
||||
for (LocalIndex i = n; i-- > 0;) {
|
||||
Real sum = b[static_cast<std::size_t>(i)];
|
||||
for (LocalIndex j = i + 1; j < n; ++j) {
|
||||
sum -= a(i, j) * result.x[static_cast<std::size_t>(j)];
|
||||
}
|
||||
result.x[static_cast<std::size_t>(i)] = sum / a(i, i);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
struct ShapeData {
|
||||
std::array<Real, 4> n{};
|
||||
std::array<Real, 4> dr{};
|
||||
|
||||
Reference in New Issue
Block a user