Files
FESADev/src/fesa/loads/concentrated_nodal_load.cpp
T

161 lines
6.2 KiB
C++

#include "fesa/loads/concentrated_nodal_load.h"
#include <cmath>
#include <stdexcept>
#include <string>
#include <utility>
#include <vector>
#include "fesa/fem/dof_manager.h"
#include "fesa/model/domain.h"
namespace fesa {
namespace {
constexpr std::size_t kDofsPerNode = 6U;
Status LoadFailure(const std::string& code, const SourceLocation& location,
const std::string& identity, const std::string& message) {
return Status::Failure(
FailureCategory::kModel,
{{Severity::kError, code, location, "CLOAD", identity, message}});
}
} // namespace
ConcentratedNodalLoad::ConcentratedNodalLoad(
SourceTargetQuery target, std::array<double, 6> global_components,
const std::size_t source_order)
: target_{std::move(target)},
global_components_{global_components},
source_order_{source_order} {}
ConcentratedNodalLoad::ConcentratedNodalLoad(
SourceTargetQuery target, const int source_dof, const double magnitude,
const std::size_t source_order, SourceLocation location,
std::optional<EntityIndex> amplitude_index, std::string amplitude_name_raw,
SourceLocation amplitude_location)
: target_{std::move(target)},
source_order_{source_order},
location_{std::move(location)},
source_dof_{source_dof},
amplitude_index_{amplitude_index},
amplitude_name_raw_{std::move(amplitude_name_raw)},
amplitude_location_{std::move(amplitude_location)} {
if (source_dof >= 1 && source_dof <= static_cast<int>(kDofsPerNode)) {
global_components_[static_cast<std::size_t>(source_dof - 1)] = magnitude;
}
}
Result<std::vector<LoadContribution>>
ConcentratedNodalLoad::ComputeContributions(const LoadContext& context) const {
const std::string& identity = target_.target_name_or_label;
const std::size_t components = context.dof_manager.ComponentsPerNode();
if (context.dof_manager.Profile() == DofProfile::kThermal ||
(source_dof_ != 0 && source_dof_ > static_cast<int>(components))) {
return Result<std::vector<LoadContribution>>::Failure(LoadFailure(
"invalid-load-dof", location_, identity,
"The source load component is inactive for this physical profile."));
}
for (std::size_t component = components; component < kDofsPerNode;
++component) {
if (global_components_[component] != 0.0) {
return Result<std::vector<LoadContribution>>::Failure(
LoadFailure("invalid-load-dof", location_, identity,
"A Solid load cannot contain rotational components."));
}
}
double amplitude = 1.0;
if (amplitude_index_) {
if (!context.domain.IsSolid() || context.domain.Dynamic() == nullptr ||
*amplitude_index_ >= context.domain.Amplitudes().size() ||
!std::isfinite(context.step_time) || context.step_time < 0.0) {
return Result<std::vector<LoadContribution>>::Failure(
LoadFailure("invalid-load-amplitude", location_, identity,
"Amplitude requires a valid Solid dynamic endpoint."));
}
amplitude = context.domain.Amplitudes()[*amplitude_index_].Evaluate(
context.step_time);
}
if (source_dof_ != 0 &&
(source_dof_ < 1 || source_dof_ > static_cast<int>(kDofsPerNode))) {
return Result<std::vector<LoadContribution>>::Failure(LoadFailure(
"invalid-load-dof", location_, identity,
"A nodal load component must be in the range 1 through 6."));
}
for (const double component : global_components_) {
if (!std::isfinite(component)) {
return Result<std::vector<LoadContribution>>::Failure(
LoadFailure("nonfinite-load-value", location_, identity,
"A nodal load magnitude must be finite."));
}
}
if (target_.entity_kind != SourceEntityKind::kNode) {
return Result<std::vector<LoadContribution>>::Failure(
LoadFailure("invalid-load-target", location_, identity,
"A concentrated nodal load requires a node target."));
}
auto resolved = context.target_resolver.Resolve(target_);
if (!resolved.HasValue()) {
return Result<std::vector<LoadContribution>>::Failure(LoadFailure(
"invalid-load-target", location_, identity,
"The load target must resolve unambiguously to one node or one "
"expanded node set."));
}
std::vector<unsigned char> seen(context.domain.Nodes().size(), 0U);
std::vector<LoadContribution> contributions;
contributions.reserve(resolved.Value().size() * components);
for (const auto& target : resolved.Value()) {
const EntityIndex node = target.entity_index;
if (node >= context.domain.Nodes().size() || seen[node] != 0U) {
return Result<std::vector<LoadContribution>>::Failure(LoadFailure(
"invalid-load-target", location_, identity,
"The expanded node set must contain unique in-range stable node "
"identities."));
}
seen[node] = 1U;
for (std::size_t component = 0U; component < components; ++component) {
const double value = global_components_[component] * amplitude;
if (!std::isfinite(value)) {
return Result<std::vector<LoadContribution>>::Failure(
LoadFailure("nonfinite-load-value", location_, identity,
"Sampled load magnitude must remain finite."));
}
try {
contributions.push_back(
{source_order_,
context.dof_manager.FullDof(node,
static_cast<DofComponent>(component)),
value});
} catch (const std::out_of_range&) {
return Result<std::vector<LoadContribution>>::Failure(LoadFailure(
"invalid-load-target", location_, identity,
"The resolved load target must have all six full DOFs."));
}
}
}
return Result<std::vector<LoadContribution>>::Success(
std::move(contributions));
}
const SourceTargetQuery& ConcentratedNodalLoad::Target() const noexcept {
return target_;
}
const std::array<double, 6>& ConcentratedNodalLoad::GlobalComponents()
const noexcept {
return global_components_;
}
std::size_t ConcentratedNodalLoad::SourceOrder() const noexcept {
return source_order_;
}
const SourceLocation& ConcentratedNodalLoad::Location() const noexcept {
return location_;
}
} // namespace fesa