feat(cpp-object-oriented-modular-refactoring): step 18 - load-hierarchy
This commit is contained in:
@@ -9,6 +9,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include "fesa/constraints/essential_constraints.h"
|
||||
#include "fesa/loads/load.h"
|
||||
#include "fesa/model/source_target_resolver.h"
|
||||
|
||||
namespace fesa {
|
||||
@@ -25,33 +26,6 @@ Status LoadFailure(const std::string& code, const SourceLocation& location,
|
||||
{{Severity::kError, code, location, keyword, identity, message}});
|
||||
}
|
||||
|
||||
Result<std::vector<EntityIndex>> ResolveTarget(
|
||||
const SourceTargetResolver& resolver, const Domain& domain,
|
||||
const NodalLoad& load) {
|
||||
auto resolved = resolver.Resolve({SourceEntityKind::kNode, "", load.target});
|
||||
if (!resolved.HasValue()) {
|
||||
return Result<std::vector<EntityIndex>>::Failure(
|
||||
LoadFailure("invalid-load-target", load.location, "CLOAD", load.target,
|
||||
"The load target must resolve unambiguously to one node or "
|
||||
"one expanded node set."));
|
||||
}
|
||||
std::vector<EntityIndex> nodes;
|
||||
nodes.reserve(resolved.Value().size());
|
||||
std::vector<unsigned char> seen(domain.Nodes().size(), 0U);
|
||||
for (const auto& target : resolved.Value()) {
|
||||
const EntityIndex node = target.entity_index;
|
||||
if (node >= domain.Nodes().size() || seen[node] != 0U) {
|
||||
return Result<std::vector<EntityIndex>>::Failure(LoadFailure(
|
||||
"invalid-load-target", load.location, "CLOAD", load.target,
|
||||
"The expanded node set must contain unique in-range stable node "
|
||||
"identities."));
|
||||
}
|
||||
seen[node] = 1U;
|
||||
nodes.push_back(node);
|
||||
}
|
||||
return Result<std::vector<EntityIndex>>::Success(std::move(nodes));
|
||||
}
|
||||
|
||||
Status ValidateFiniteVector(const Vector& values,
|
||||
const SourceLocation& location,
|
||||
const std::string& identity) {
|
||||
@@ -125,6 +99,35 @@ Status ValidateShellMoments(const Domain& domain, const Vector& full_load) {
|
||||
|
||||
Result<Vector> LoadAssembler::AssembleFullNodalLoad(const AnalysisModel& model,
|
||||
const DofManager& dofs) {
|
||||
const auto& active_loads = model.ActiveLoads();
|
||||
const auto& owned_loads = model.Step().Loads();
|
||||
if (active_loads.size() != owned_loads.size()) {
|
||||
return Result<Vector>::Failure(LoadFailure(
|
||||
"invalid-load-order", model.Step().Location(), "CLOAD",
|
||||
model.Step().Name(),
|
||||
"The active load view must include every sole-step load once."));
|
||||
}
|
||||
|
||||
LoadView loads;
|
||||
loads.reserve(active_loads.size());
|
||||
for (std::size_t source_order = 0U; source_order < active_loads.size();
|
||||
++source_order) {
|
||||
const EntityIndex load_index = active_loads[source_order];
|
||||
if (static_cast<std::size_t>(load_index) != source_order ||
|
||||
load_index >= owned_loads.size()) {
|
||||
return Result<Vector>::Failure(LoadFailure(
|
||||
"invalid-load-order", model.Step().Location(), "CLOAD",
|
||||
std::to_string(source_order),
|
||||
"Active loads must retain complete stable source order."));
|
||||
}
|
||||
loads.push_back(owned_loads[load_index]);
|
||||
}
|
||||
return AssembleFullNodalLoad(model, dofs, loads);
|
||||
}
|
||||
|
||||
Result<Vector> LoadAssembler::AssembleFullNodalLoad(const AnalysisModel& model,
|
||||
const DofManager& dofs,
|
||||
const LoadView& loads) {
|
||||
const Domain& domain = model.GetDomain();
|
||||
if (domain.Nodes().size() >
|
||||
(std::numeric_limits<std::size_t>::max)() / kDofsPerNode) {
|
||||
@@ -165,52 +168,55 @@ Result<Vector> LoadAssembler::AssembleFullNodalLoad(const AnalysisModel& model,
|
||||
}
|
||||
}
|
||||
|
||||
const auto& active_loads = model.ActiveLoads();
|
||||
const auto& loads = model.Step().loads;
|
||||
if (active_loads.size() != loads.size()) {
|
||||
return Result<Vector>::Failure(LoadFailure(
|
||||
"invalid-load-order", model.Step().location, "CLOAD", model.Step().name,
|
||||
"The active load view must include every sole-step load once."));
|
||||
}
|
||||
const SourceTargetIndex target_index = SourceTargetIndex::FromDomain(domain);
|
||||
const SourceTargetResolver target_resolver{target_index};
|
||||
const LoadContext context{domain, dofs, target_resolver};
|
||||
|
||||
std::vector<std::vector<LoadContribution>> contributions_by_load;
|
||||
contributions_by_load.reserve(loads.size());
|
||||
for (std::size_t source_order = 0U; source_order < loads.size();
|
||||
++source_order) {
|
||||
auto contributions =
|
||||
loads[source_order].get().ComputeContributions(context);
|
||||
if (!contributions.HasValue()) {
|
||||
return Result<Vector>::Failure(contributions.GetStatus());
|
||||
}
|
||||
for (const auto& contribution : contributions.Value()) {
|
||||
if (contribution.source_order != source_order) {
|
||||
return Result<Vector>::Failure(LoadFailure(
|
||||
"invalid-load-order", model.Step().Location(), "LOAD_ASSEMBLER",
|
||||
std::to_string(contribution.source_order),
|
||||
"Every contribution must retain its supplying load source "
|
||||
"order."));
|
||||
}
|
||||
if (contribution.full_dof_index >= expected_full_count) {
|
||||
return Result<Vector>::Failure(LoadFailure(
|
||||
"invalid-load-index", model.Step().Location(), "LOAD_ASSEMBLER",
|
||||
std::to_string(contribution.full_dof_index),
|
||||
"A load contribution full-DOF index is outside the active "
|
||||
"model."));
|
||||
}
|
||||
if (!std::isfinite(contribution.value)) {
|
||||
return Result<Vector>::Failure(
|
||||
LoadFailure("nonfinite-load-value", model.Step().Location(),
|
||||
"LOAD_ASSEMBLER", std::to_string(source_order),
|
||||
"A load contribution value must be finite."));
|
||||
}
|
||||
}
|
||||
contributions_by_load.push_back(std::move(contributions.Value()));
|
||||
}
|
||||
|
||||
Vector full_load{expected_full_count};
|
||||
// Active load indices are required to be the original source order; this
|
||||
// loop is therefore also the fixed floating-point accumulation order.
|
||||
for (std::size_t source_order = 0U; source_order < active_loads.size();
|
||||
++source_order) {
|
||||
const EntityIndex load_index = active_loads[source_order];
|
||||
if (static_cast<std::size_t>(load_index) != source_order ||
|
||||
load_index >= loads.size()) {
|
||||
return Result<Vector>::Failure(LoadFailure(
|
||||
"invalid-load-order", model.Step().location, "CLOAD",
|
||||
std::to_string(source_order),
|
||||
"Active loads must retain complete stable source order."));
|
||||
}
|
||||
const auto& load = loads[load_index];
|
||||
if (load.dof < 1 || load.dof > static_cast<int>(kDofsPerNode)) {
|
||||
return Result<Vector>::Failure(LoadFailure(
|
||||
"invalid-load-dof", load.location, "CLOAD", load.target,
|
||||
"A nodal load component must be in the range 1 through 6."));
|
||||
}
|
||||
if (!std::isfinite(load.magnitude)) {
|
||||
return Result<Vector>::Failure(
|
||||
LoadFailure("nonfinite-load-value", load.location, "CLOAD",
|
||||
load.target, "A nodal load magnitude must be finite."));
|
||||
}
|
||||
|
||||
auto target = ResolveTarget(target_resolver, domain, load);
|
||||
if (!target.HasValue()) {
|
||||
return Result<Vector>::Failure(target.GetStatus());
|
||||
}
|
||||
const auto component = static_cast<DofComponent>(load.dof - 1);
|
||||
for (const EntityIndex node : target.Value()) {
|
||||
const std::size_t full_dof = dofs.FullDof(node, component);
|
||||
const double accumulated = full_load[full_dof] + load.magnitude;
|
||||
// Contribution validation completes before this fixed-order candidate
|
||||
// accumulation begins, so failures cannot expose a partial global vector.
|
||||
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)) {
|
||||
return Result<Vector>::Failure(LoadFailure(
|
||||
"nonfinite-load-accumulation", load.location, "CLOAD", load.target,
|
||||
"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;
|
||||
|
||||
Reference in New Issue
Block a user