feat(cpp-object-oriented-modular-refactoring): step 11 - source-target-resolver
This commit is contained in:
@@ -1,17 +1,15 @@
|
||||
#include "fesa/assembly/load_assembler.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <charconv>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <system_error>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "fesa/constraints/essential_constraints.h"
|
||||
#include "fesa/model/source_target_resolver.h"
|
||||
|
||||
namespace fesa {
|
||||
namespace {
|
||||
@@ -27,28 +25,6 @@ Status LoadFailure(const std::string& code, const SourceLocation& location,
|
||||
{{Severity::kError, code, location, keyword, identity, message}});
|
||||
}
|
||||
|
||||
char AsciiLower(const 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(),
|
||||
[](const char left_value, const char right_value) {
|
||||
return AsciiLower(left_value) == AsciiLower(right_value);
|
||||
});
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/// @brief Checks that equation-space indices preserve stable full-DOF order.
|
||||
bool IsStrictlyIncreasing(const std::vector<std::size_t>& values) {
|
||||
return std::adjacent_find(
|
||||
@@ -122,52 +98,31 @@ Status ValidateDofOrder(const DofManager& dofs,
|
||||
return Status::Ok();
|
||||
}
|
||||
|
||||
Result<std::vector<EntityIndex>> ResolveTarget(const Domain& domain,
|
||||
const NodalLoad& load) {
|
||||
std::vector<const NodeSet*> matching_sets;
|
||||
for (const auto& set : domain.NodeSets()) {
|
||||
if (EqualName(set.name, load.target)) {
|
||||
matching_sets.push_back(&set);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<EntityIndex> matching_nodes;
|
||||
std::int64_t label = 0;
|
||||
if (TryPositiveInteger(load.target, label)) {
|
||||
for (std::size_t index = 0U; index < domain.Nodes().size(); ++index) {
|
||||
if (domain.Nodes()[index].source_id.source_label == label) {
|
||||
matching_nodes.push_back(static_cast<EntityIndex>(index));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (matching_sets.size() > 1U || matching_nodes.size() > 1U ||
|
||||
(!matching_sets.empty() && !matching_nodes.empty())) {
|
||||
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."));
|
||||
}
|
||||
if (!matching_sets.empty()) {
|
||||
const auto& nodes = matching_sets.front()->node_indices;
|
||||
std::vector<unsigned char> seen(domain.Nodes().size(), 0U);
|
||||
for (const EntityIndex node : nodes) {
|
||||
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;
|
||||
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."));
|
||||
}
|
||||
return Result<std::vector<EntityIndex>>::Success(nodes);
|
||||
seen[node] = 1U;
|
||||
nodes.push_back(node);
|
||||
}
|
||||
if (!matching_nodes.empty()) {
|
||||
return Result<std::vector<EntityIndex>>::Success(std::move(matching_nodes));
|
||||
}
|
||||
return Result<std::vector<EntityIndex>>::Failure(LoadFailure(
|
||||
"invalid-load-target", load.location, "CLOAD", load.target,
|
||||
"The load target must resolve to one semantic node or node set."));
|
||||
return Result<std::vector<EntityIndex>>::Success(std::move(nodes));
|
||||
}
|
||||
|
||||
Status ValidateFiniteVector(const Vector& values,
|
||||
@@ -285,6 +240,8 @@ Result<Vector> LoadAssembler::AssembleFullNodalLoad(const AnalysisModel& model,
|
||||
"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};
|
||||
|
||||
Vector full_load{expected_full_count};
|
||||
// Active load indices are required to be the original source order; this
|
||||
@@ -311,7 +268,7 @@ Result<Vector> LoadAssembler::AssembleFullNodalLoad(const AnalysisModel& model,
|
||||
load.target, "A nodal load magnitude must be finite."));
|
||||
}
|
||||
|
||||
auto target = ResolveTarget(domain, load);
|
||||
auto target = ResolveTarget(target_resolver, domain, load);
|
||||
if (!target.HasValue()) {
|
||||
return Result<Vector>::Failure(target.GetStatus());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user