161 lines
6.2 KiB
C++
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
|