Files
FESADev/src/fesa/fem/dof_manager.cpp
T

231 lines
8.2 KiB
C++

#include "fesa/fem/dof_manager.hpp"
#include <algorithm>
#include <charconv>
#include <stdexcept>
#include <string>
#include <system_error>
#include <utility>
namespace fesa {
namespace {
constexpr std::size_t dofsPerNode = 6U;
char asciiLower(char value) {
if (value >= 'A' && value <= 'Z') {
return static_cast<char>(value + ('a' - 'A'));
}
return value;
}
bool equalName(const std::string& left, const std::string& right) {
return left.size() == right.size() &&
std::equal(
left.begin(), left.end(), right.begin(),
[](char leftValue, char rightValue) {
return asciiLower(leftValue) == asciiLower(rightValue);
});
}
bool tryPositiveInteger(const std::string& text, std::int64_t& value) {
const char* const first = text.data();
const char* const last = first + text.size();
const auto parsed = std::from_chars(first, last, value);
return parsed.ec == std::errc{} && parsed.ptr == last && value > 0;
}
std::vector<EntityIndex> expandBoundaryTarget(
const Domain& domain, const BoundaryCondition& boundary) {
for (const auto& set : domain.nodeSets()) {
if (equalName(set.name, boundary.target)) {
return set.nodeIndices;
}
}
std::int64_t sourceLabel = 0;
if (tryPositiveInteger(boundary.target, sourceLabel)) {
for (std::size_t node = 0U; node < domain.nodes().size(); ++node) {
if (domain.nodes()[node].sourceId.sourceLabel == sourceLabel) {
return {static_cast<EntityIndex>(node)};
}
}
}
return {};
}
SparsePattern buildSparsePattern(
std::size_t fullDofCount,
const std::vector<EntityIndex>& activeElements,
const std::vector<std::array<std::size_t, 12>>& elementScatters) {
std::vector<std::vector<std::size_t>> columnsByRow(fullDofCount);
for (const EntityIndex element : activeElements) {
const auto& scatter = elementScatters.at(element);
for (const std::size_t row : scatter) {
auto& columns = columnsByRow[row];
columns.insert(columns.end(), scatter.begin(), scatter.end());
}
}
SparsePattern pattern;
pattern.rowOffsets.reserve(fullDofCount + 1U);
pattern.rowOffsets.push_back(0U);
for (auto& columns : columnsByRow) {
// Stable CSR structure is independent of element traversal duplicates.
std::sort(columns.begin(), columns.end());
columns.erase(std::unique(columns.begin(), columns.end()), columns.end());
pattern.columnIndices.insert(
pattern.columnIndices.end(), columns.begin(), columns.end());
pattern.rowOffsets.push_back(pattern.columnIndices.size());
}
return pattern;
}
} // namespace
Result<DofManager> DofManager::create(const AnalysisModel& model) {
const Domain& domain = model.domain();
const std::size_t fullCount = domain.nodes().size() * dofsPerNode;
std::vector<std::optional<double>> prescribedByFullDof(fullCount);
for (const EntityIndex boundaryIndex : model.activeBoundaryConditions()) {
const auto& boundary = model.step().boundaries.at(boundaryIndex);
const auto target = expandBoundaryTarget(domain, boundary);
for (const EntityIndex node : target) {
for (int component = boundary.firstDof;
component <= boundary.lastDof;
++component) {
const std::size_t fullDof =
static_cast<std::size_t>(node) * dofsPerNode +
static_cast<std::size_t>(component - 1);
auto& prescribed = prescribedByFullDof[fullDof];
if (prescribed && *prescribed != boundary.value) {
return Result<DofManager>::failure(Status::failure(
FailureCategory::input,
{{Severity::error,
"conflicting-boundary-condition",
boundary.location,
"BOUNDARY",
boundary.target,
"Expanded boundary rows prescribe different values to one node/DOF."}}));
}
prescribed = boundary.value;
}
}
}
std::vector<std::size_t> freeDofs;
std::vector<std::size_t> constrainedDofs;
std::vector<double> constrainedValues;
std::vector<std::optional<std::size_t>> freeEquations(fullCount);
freeDofs.reserve(fullCount);
constrainedDofs.reserve(fullCount);
constrainedValues.reserve(fullCount);
// A full-DOF scan fixes free equations, constrained DOFs, and dc in the
// same stable order regardless of boundary declaration overlap.
for (std::size_t fullDof = 0U; fullDof < fullCount; ++fullDof) {
if (prescribedByFullDof[fullDof]) {
constrainedDofs.push_back(fullDof);
constrainedValues.push_back(*prescribedByFullDof[fullDof]);
} else {
freeEquations[fullDof] = freeDofs.size();
freeDofs.push_back(fullDof);
}
}
Vector prescribedValues{constrainedValues.size()};
for (std::size_t index = 0U; index < constrainedValues.size(); ++index) {
prescribedValues[index] = constrainedValues[index];
}
std::vector<std::array<std::size_t, 12>> elementScatters(
domain.elements().size());
for (const EntityIndex elementIndex : model.activeElements()) {
const auto& element = domain.elements().at(elementIndex);
auto& scatter = elementScatters.at(elementIndex);
for (std::size_t endpoint = 0U; endpoint < element.nodeIndices.size(); ++endpoint) {
const std::size_t node = element.nodeIndices[endpoint];
for (std::size_t component = 0U; component < dofsPerNode; ++component) {
scatter[endpoint * dofsPerNode + component] =
node * dofsPerNode + component;
}
}
}
auto pattern = buildSparsePattern(
fullCount, model.activeElements(), elementScatters);
return Result<DofManager>::success(DofManager{
fullCount,
std::move(freeEquations),
std::move(elementScatters),
std::move(freeDofs),
std::move(constrainedDofs),
std::move(prescribedValues),
std::move(pattern)});
}
std::size_t DofManager::fullDofCount() const noexcept {
return fullDofCount_;
}
std::size_t DofManager::freeDofCount() const noexcept {
return freeDofs_.size();
}
std::size_t DofManager::constrainedDofCount() const noexcept {
return constrainedDofs_.size();
}
std::size_t DofManager::fullDof(
EntityIndex node, DofComponent component) const {
const std::size_t componentIndex = static_cast<std::size_t>(component);
if (node >= fullDofCount_ / dofsPerNode || componentIndex >= dofsPerNode) {
throw std::out_of_range{"Node or DOF component is out of range."};
}
return static_cast<std::size_t>(node) * dofsPerNode + componentIndex;
}
std::optional<std::size_t> DofManager::freeEquation(
std::size_t fullDof) const {
return freeEquations_.at(fullDof);
}
const std::array<std::size_t, 12>& DofManager::elementScatter(
EntityIndex element) const {
return elementScatters_.at(element);
}
const std::vector<std::size_t>& DofManager::freeDofs() const noexcept {
return freeDofs_;
}
const std::vector<std::size_t>& DofManager::constrainedDofs() const noexcept {
return constrainedDofs_;
}
const Vector& DofManager::prescribedValues() const noexcept {
return prescribedValues_;
}
const SparsePattern& DofManager::sparsePattern() const noexcept {
return sparsePattern_;
}
DofManager::DofManager(
std::size_t fullDofCount,
std::vector<std::optional<std::size_t>> freeEquations,
std::vector<std::array<std::size_t, 12>> elementScatters,
std::vector<std::size_t> freeDofs,
std::vector<std::size_t> constrainedDofs,
Vector prescribedValues,
SparsePattern sparsePattern)
: fullDofCount_{fullDofCount},
freeEquations_{std::move(freeEquations)},
elementScatters_{std::move(elementScatters)},
freeDofs_{std::move(freeDofs)},
constrainedDofs_{std::move(constrainedDofs)},
prescribedValues_{std::move(prescribedValues)},
sparsePattern_{std::move(sparsePattern)} {}
} // namespace fesa