Connect MITC4 drilling evidence across analysis procedures

This commit is contained in:
KOKO\Mimi
2026-10-20 09:21:39 +09:00
parent 294ffbf8a1
commit c6188b3ad7
30 changed files with 763 additions and 28 deletions
+16
View File
@@ -1,5 +1,7 @@
#include "fesa/analysis/analysis_state.h"
#include "results/mitc4_drilling_evidence.h"
#include <algorithm>
#include <array>
#include <cmath>
@@ -167,9 +169,18 @@ Status AnalysisState::CommitShellResults(
"Shell energy, equilibrium, and normalized metrics must be finite.");
}
if (!results_internal::ValidDrillingEvidence(candidate.mitc4_drilling,
candidate.equilibrium) ||
(mitc4_drilling_ && !results_internal::SameDrillingInventory(
mitc4_drilling_, candidate.mitc4_drilling)))
return ShellCandidateFailure("invalid-shell-drilling-evidence",
identity_.step_name,
"Drilling evidence must be finite, consistent, and retain its mode.");
shell_results_ = std::move(candidate.rows);
physical_strain_energy_ = candidate.physical_strain_energy;
equilibrium_ = candidate.equilibrium;
mitc4_drilling_ = candidate.mitc4_drilling;
verification_metrics_ = candidate.verification_metrics;
return Status::Ok();
}
@@ -187,6 +198,11 @@ const std::array<double, 6>& AnalysisState::Equilibrium() const noexcept {
return equilibrium_;
}
const std::optional<Mitc4DrillingEvidence>& AnalysisState::Mitc4Drilling()
const noexcept {
return mitc4_drilling_;
}
const std::array<double, 3>& AnalysisState::VerificationMetrics()
const noexcept {
return verification_metrics_;
@@ -1,5 +1,7 @@
#include "fesa/analysis/dynamic_analysis_state.h"
#include "results/mitc4_drilling_evidence.h"
#include <cmath>
#include <limits>
#include <stdexcept>
@@ -273,6 +275,12 @@ Status DynamicAnalysisState::CommitAcceptedFrame(
return Failure(FailureCategory::kSolver,
"Frame physical evidence is nonfinite or has invalid size.");
}
if (!results_internal::ValidDrillingEvidence(frame.mitc4_drilling,
frame.global_equilibrium) ||
(!accepted_frames_.empty() && !results_internal::SameDrillingInventory(
frame.mitc4_drilling, accepted_frames_.back().mitc4_drilling)))
return Failure(FailureCategory::kSolver,
"Accepted drilling evidence must be finite, consistent, and retain its mode.");
for (const std::size_t dof : constrained_dofs_) {
if (frame.generalized_coordinates[dof] != 0.0 ||
frame.velocity[dof] != 0.0 || frame.acceleration[dof] != 0.0 ||
+14 -1
View File
@@ -64,6 +64,16 @@ bool Within(const double value, const double scale, const double tolerance) {
Status Validate(const Domain& domain, const DofManager& dofs,
const DynamicAssembly& assembly, const double length) {
const std::size_t count = dofs.FullDofCount();
if (assembly.frame.mitc4_drilling &&
assembly.frame.mitc4_drilling->mode == Mitc4DrillingMode::kDirectSpring) {
if (assembly.hidden_constraint.Size() != assembly.rotations.size())
return Failure(domain, "dynamic-invalid-algebra-state",
"Canonical hidden values must cover every node.");
for (std::size_t i = 0U; i < assembly.hidden_constraint.Size(); ++i)
if (!std::isfinite(assembly.hidden_constraint[i]))
return Failure(domain, "dynamic-invalid-algebra-state",
"Canonical hidden values must remain finite.");
}
if (!std::isfinite(length) || !(length > 0.0) || count % 6U != 0U ||
assembly.rotations.size() != count / 6U) {
return Failure(domain, "dynamic-invalid-algebra-state",
@@ -547,6 +557,8 @@ Status HiddenEvidence(const Domain& domain, const DynamicAssembly& assembly,
const SparseMatrix& gradient,
DynamicAcceptedFrameState* frame,
const bool require_acceleration) {
const bool direct = frame->mitc4_drilling &&
frame->mitc4_drilling->mode == Mitc4DrillingMode::kDirectSpring;
Vector psi{nodes.size()};
Vector psi_scale{nodes.size()};
Vector velocity_defect{nodes.size()};
@@ -575,9 +587,10 @@ Status HiddenEvidence(const Domain& domain, const DynamicAssembly& assembly,
const double g =
frame->internal_force[full] - frame->external_force[full];
const double n = rotation.drill_null[c];
psi[row] += n * g;
if (!direct) psi[row] += n * g;
psi_scale[row] += std::abs(n) * frame->residual_scale[full];
}
if (direct) psi[row] = assembly.hidden_constraint[node];
velocity_defect[row] += velocity_correction;
AddCompensated(assembly.hidden_second[node], &acceleration_defect[row],
&acceleration_correction);
+42 -2
View File
@@ -18,6 +18,7 @@
#include "fesa/elements/finite_rotation.h"
#include "fesa/loads/load.h"
#include "fesa/model/source_target_resolver.h"
#include "results/mitc4_drilling_evidence.h"
namespace fesa::hht_internal {
@@ -498,6 +499,10 @@ Result<DynamicAssembly> EvaluateAssembly(
internal[e] = std::move(f.Value());
inertia[e] = std::move(m.Value());
});
if (shells.empty())
return Result<DynamicAssembly>::Failure(Failure("HHT requires a shell kernel."));
frame.mitc4_drilling =
Mitc4DrillingEvidence{shells.front().internal_kernel->DrillingMode()};
frame.inertia_force = Vector(count);
frame.internal_force = Vector(count);
frame.residual = Vector(count);
@@ -515,6 +520,7 @@ Result<DynamicAssembly> EvaluateAssembly(
std::vector<std::map<std::size_t, double>>
constraint_gradients(domain.Nodes().size());
std::vector<bool> external_done(domain.Nodes().size(), false);
Vector hidden_constraint(directional_third ? domain.Nodes().size() : 0U);
Vector third(count);
Vector third_scale(count);
std::array<std::vector<CooContribution>, 6> entries;
@@ -532,9 +538,31 @@ Result<DynamicAssembly> EvaluateAssembly(
return Result<DynamicAssembly>::Failure(*failures[e]);
const auto& f = *internal[e];
const auto& m = *inertia[e];
if (shells[e].internal_kernel->DrillingMode() != frame.mitc4_drilling->mode ||
f.drilling_force.Size() != 24U)
return Result<DynamicAssembly>::Failure(Failure("Invalid actual drilling mode or force."));
for (std::size_t node = 0U; node < 4U; ++node) {
const auto local = 6U * node;
const auto global = shells[e].scatter[local];
const Vector3 force = Triple(f.drilling_force, local);
const auto spatial = SpatialMomentSO3(
Triple(frame.generalized_coordinates, global + 3U),
Triple(f.drilling_force, local + 3U));
if (!spatial.HasValue())
return Result<DynamicAssembly>::Failure(spatial.GetStatus());
const Vector3 position = Vector3(domain.Nodes()[global / 6U].coordinates) +
Triple(frame.generalized_coordinates, global);
const Vector3 moment = position.Cross(force) + spatial.Value();
for (std::size_t c = 0U; c < 3U; ++c)
frame.mitc4_drilling->drilling_moment[c] += moment[c];
}
for (const double value : frame.mitc4_drilling->drilling_moment)
if (!std::isfinite(value))
return Result<DynamicAssembly>::Failure(Failure("Drilling spatial moment overflowed."));
if (directional_third) {
for (std::size_t local = 0U; local < 4U; ++local) {
const auto node = shells[e].scatter[6U * local] / 6U;
hidden_constraint[node] += f.drilling_constraint[local];
auto& gradient = constraint_gradients[node];
auto& hessian = constraint_hessians[node];
for (std::size_t i = 0U; i < 24U; ++i)
@@ -555,6 +583,7 @@ Result<DynamicAssembly> EvaluateAssembly(
Triple(frame.external_force, first), {}, {});
if (!external.HasValue())
return Result<DynamicAssembly>::Failure(external.GetStatus());
hidden_constraint[node] += external.Value().value;
for (std::size_t i = 0U; i < 3U; ++i) {
gradient[first + i] += external.Value().gradient[i];
for (std::size_t j = 0U; j < 3U; ++j)
@@ -740,7 +769,8 @@ Result<DynamicAssembly> EvaluateAssembly(
std::move(third), std::move(third_scale), std::move(rotations),
momentum_scale, angular_scale, std::move(hidden_second),
std::move(hidden_second_scale), std::move(hidden_gradient),
std::move(trial_constraints), std::move(field_flux)});
std::move(trial_constraints), std::move(field_flux),
std::move(hidden_constraint)});
}
Status CompleteEquilibrium(const Domain& domain, const DofManager& dofs,
@@ -776,10 +806,20 @@ Status CompleteEquilibrium(const Domain& domain, const DofManager& dofs,
frame->global_equilibrium[c] = force_error[c];
frame->global_equilibrium[3U + c] = moment_error[c];
}
Vector3 acceptance_moment = moment_error;
if (frame->mitc4_drilling) {
results_internal::CompleteDrillingEvidence(frame->global_equilibrium,
&*frame->mitc4_drilling);
if (!results_internal::ValidDrillingEvidence(frame->mitc4_drilling,
frame->global_equilibrium))
return Failure("Invalid dynamic drilling equilibrium evidence.");
if (frame->mitc4_drilling->mode == Mitc4DrillingMode::kDirectSpring)
acceptance_moment = moment_error - Vector3(frame->mitc4_drilling->drilling_moment);
}
if (!force_error.IsFinite() || !moment_error.IsFinite() ||
!std::isfinite(force_scale) || !std::isfinite(moment_scale) ||
Ratio(force_error.Norm(), force_scale) > 1e-8 ||
Ratio(moment_error.Norm(), moment_scale) > 1e-8)
Ratio(acceptance_moment.Norm(), moment_scale) > 1e-8)
return Failure("Dynamic physical force or moment balance exceeds 1e-8.");
return Status::Ok();
}
+3
View File
@@ -97,6 +97,9 @@ struct DynamicAssembly {
std::optional<SparseMatrix> hidden_gradient;
std::optional<TrialConstraintData> trial_constraints;
std::array<std::vector<hht_field_convergence::FluxOccurrence>, 2> field_flux;
// Source-ordered scalar primitive, including the external term exactly once.
// Empty when directional primitives were not evaluated; never a zero fallback.
Vector hidden_constraint{0U};
};
/// @brief Builds checked shell evaluators in stable source order.
+2 -1
View File
@@ -82,7 +82,8 @@ const hht_internal::DirectionalShellConstraintData* MatchingConstraints(
nonlinear_mitc4_internal::ConfigurationSnapshot::Read(*shell.internal_kernel);
if (record.configuration.source.file != configuration.source.file ||
record.configuration.source.line != configuration.source.line ||
record.configuration.identity != configuration.identity)
record.configuration.identity != configuration.identity ||
record.configuration.drilling_mode != configuration.drilling_mode)
return nullptr;
for (std::size_t i = 0U; i < configuration.values.size(); ++i)
if (!std::isfinite(record.configuration.values[i]) ||
@@ -217,6 +217,13 @@ Result<EigenAnalysisState> ExecuteCandidate(const Domain& domain,
if (!backend.HasValue())
return Result<EigenAnalysisState>::Failure(backend.GetStatus());
EigenAnalysisState state(std::move(backend.Value()));
for (const auto& reference : references) {
if (state.drilling_mode && *state.drilling_mode != reference->DrillingMode())
return Result<EigenAnalysisState>::Failure(Failure(
"invalid-shell-drilling-evidence", "mode",
"All frequency kernels must retain one actual drilling mode."));
state.drilling_mode = reference->DrillingMode();
}
state.full_dof_count = dofs.FullDofCount();
state.free_dof_count = reduction.Value().free_count;
state.mass_null_dimension = reduction.Value().null_count;
@@ -12,6 +12,7 @@
#include "fesa/elements/element_definition.h"
#include "math/finite_values.h"
#include "results/shell_result_recovery.h"
#include "results/mitc4_drilling_evidence.h"
#include "analysis/shell_state_inventory.h"
namespace fesa {
@@ -110,6 +111,13 @@ Status ValidateAcceptedFrame(const AcceptedFrameState& frame,
"Accepted frame physical result inventory is invalid.");
}
if (!results_internal::ValidDrillingEvidence(frame.mitc4_drilling,
frame.global_equilibrium) ||
(accepted_frame_count != 0U && !results_internal::SameDrillingInventory(
frame.mitc4_drilling, committed.mitc4_drilling)))
return StateFailure(FailureCategory::kSolver, "invalid-shell-drilling-evidence", {},
"Accepted drilling evidence must be finite and retain its mode.");
const double gauss = 1.0 / std::sqrt(3.0);
const std::array<ShellMidsurfaceLocation, kShellLocationsPerElement>
expected_locations{ShellMidsurfaceLocation::kGp1,
@@ -243,7 +251,7 @@ Status NonlinearAnalysisState::CommitAcceptedFrame(
frame.reaction,
frame.shell_results,
frame.physical_strain_energy,
frame.global_equilibrium};
frame.global_equilibrium, frame.mitc4_drilling};
std::vector<AcceptedFrameState> accepted_candidate = accepted_frames_;
accepted_candidate.push_back(frame);
@@ -33,6 +33,7 @@
#include "assembly/ordered_contribution_internal.h"
#include "math/finite_values.h"
#include "elements/shell_kernel_values.h"
#include "results/mitc4_drilling_evidence.h"
namespace fesa {
namespace {
@@ -57,6 +58,7 @@ struct InternalAssembly {
SparseMatrix tangent;
std::vector<ShellResultRow> shell_rows;
double physical_strain_energy;
Mitc4DrillingEvidence mitc4_drilling;
};
Status ProcedureFailure(const FailureCategory category, const std::string& code,
@@ -175,6 +177,10 @@ Result<NonlinearStateBaselineInput> BuildBaseline(const Domain& domain) {
Result<std::vector<RuntimeShell>> BuildRuntimeShells(const Domain& domain,
const DofManager& dofs) {
std::vector<RuntimeShell> shells;
if (domain.Elements().Empty())
return Result<std::vector<RuntimeShell>>::Failure(ProcedureFailure(
FailureCategory::kModel, "unsupported-nonlinear-element", {}, "ELEMENT",
{}, "The nonlinear static procedure requires a MITC4 shell element."));
shells.reserve(domain.Elements().Size());
for (std::size_t index = 0U; index < domain.Elements().Size(); ++index) {
const auto* definition =
@@ -320,7 +326,7 @@ NonlinearMitc4TrialDofs ExtractElementTrial(
Result<InternalAssembly> AssembleInternal(
const std::vector<RuntimeShell>& shells, const DofManager& dofs,
const Vector& coordinates, const ParallelFor& parallel_for) {
const Domain& domain, const Vector& coordinates, const ParallelFor& parallel_for) {
std::vector<std::optional<NonlinearMitc4InternalEquilibrium>> local(
shells.size());
std::vector<std::optional<Status>> failures(shells.size());
@@ -341,6 +347,7 @@ Result<InternalAssembly> AssembleInternal(
std::vector<ShellResultRow> rows;
rows.reserve(shells.size() * 4U);
double energy = 0.0;
Mitc4DrillingEvidence drilling{shells.front().kernel->DrillingMode()};
for (std::size_t index = 0U; index < shells.size(); ++index) {
if (failures[index].has_value()) {
return Result<InternalAssembly>::Failure(*failures[index]);
@@ -354,6 +361,28 @@ Result<InternalAssembly> AssembleInternal(
"NONLINEAR_STATIC_PROCEDURE", std::to_string(index),
"A nonlinear shell internal residual or tangent is nonfinite."));
}
if (shells[index].kernel->DrillingMode() != drilling.mode ||
local[index]->drilling_force.Size() != kShellDofCount ||
!IsFinite(local[index]->drilling_force))
return Result<InternalAssembly>::Failure(ProcedureFailure(
FailureCategory::kSolver, "invalid-shell-drilling-evidence", {},
"NONLINEAR_STATIC_PROCEDURE", {}, "Drilling mode or force is invalid."));
const auto element_trial = ExtractElementTrial(shells[index], coordinates);
for (std::size_t node = 0U; node < 4U; ++node) {
const auto first = 6U * node;
const auto global_node = shells[index].scatter[first] / 6U;
const auto& values = local[index]->drilling_force;
const Vector3 drilling_force{values[first], values[first + 1U], values[first + 2U]};
const auto spatial = SpatialMomentSO3(element_trial.rotation_vector[node],
{values[first + 3U], values[first + 4U], values[first + 5U]});
if (!spatial.HasValue())
return Result<InternalAssembly>::Failure(spatial.GetStatus());
const Vector3 position = Vector3(domain.Nodes()[global_node].coordinates) +
element_trial.displacement[node];
const Vector3 moment = position.Cross(drilling_force) + spatial.Value();
for (std::size_t c = 0U; c < 3U; ++c)
drilling.drilling_moment[c] += moment[c];
}
for (std::size_t row = 0U; row < kShellDofCount; ++row) {
force[shells[index].scatter[row]] += local[index]->internal_residual[row];
assembly_internal::AppendLocalMatrixRow(
@@ -373,7 +402,8 @@ Result<InternalAssembly> AssembleInternal(
}
energy += local[index]->physical_strain_energy;
}
if (!IsFinite(force) || !std::isfinite(energy)) {
if (!IsFinite(force) || !std::isfinite(energy) ||
!IsFinite(drilling.drilling_moment)) {
return Result<InternalAssembly>::Failure(ProcedureFailure(
FailureCategory::kSolver, "nonfinite-newton-data", {},
"NONLINEAR_STATIC_PROCEDURE", {},
@@ -387,7 +417,7 @@ Result<InternalAssembly> AssembleInternal(
return Result<InternalAssembly>::Failure(tangent.GetStatus());
}
return Result<InternalAssembly>::Success(
{std::move(force), std::move(tangent.Value()), std::move(rows), energy});
{std::move(force), std::move(tangent.Value()), std::move(rows), energy, drilling});
}
Result<Vector> Difference(const Vector& left, const Vector& right) {
@@ -699,7 +729,7 @@ AcceptedFrameState AcceptedFrame(const std::uint32_t frame_index,
trial.reaction,
trial.shell_results,
trial.physical_strain_energy,
trial.global_equilibrium};
trial.global_equilibrium, trial.mitc4_drilling};
}
} // namespace
@@ -807,7 +837,7 @@ Result<NonlinearStaticExecutionResult> ExecuteNonlinearStaticCore(
NewtonIterationStage::kKinematicsEvaluated;
Result<InternalAssembly> internal = AssembleInternal(
shells.Value(), dofs.Value(),
shells.Value(), dofs.Value(), domain,
trial.Value().generalized_coordinates, parallel_for);
if (!internal.HasValue()) {
return Result<std::optional<ShellStepCandidate>>::Failure(
@@ -879,6 +909,9 @@ Result<NonlinearStaticExecutionResult> ExecuteNonlinearStaticCore(
NewtonIterationStage::kExternalAssembled));
}
trial.Value().global_equilibrium = global_equilibrium.Value();
trial.Value().mitc4_drilling = internal.Value().mitc4_drilling;
results_internal::CompleteDrillingEvidence(
trial.Value().global_equilibrium, &*trial.Value().mitc4_drilling);
Result<PartitionedStiffness> partition =
constraints.Partition(tangent.Value(), dofs.Value());
@@ -95,6 +95,10 @@ Result<ThermalShellRecovery> Ds4ThermalAdapter::Recover(
Mitc4ReferenceAdapter::Mitc4ReferenceAdapter(const Mitc4Shell& kernel)
: kernel_{kernel} {}
Mitc4DrillingMode Mitc4ReferenceAdapter::DrillingMode() const noexcept {
return kernel_.DrillingMode();
}
const ElementDofLayout& Mitc4ReferenceAdapter::DofLayout() const noexcept {
return kernel_.DofLayout();
}
+16
View File
@@ -958,6 +958,22 @@ Result<ElementResultBundle> Mitc4Shell::Recover(
ShellElementResultRows rows{};
rows.rows.reserve(kNodeCount);
rows.physical_strain_energy = recovered.Value().strain_energy;
const auto stiffness = Stiffness();
if (!stiffness.HasValue())
return Result<ElementResultBundle>::Failure(stiffness.GetStatus());
const Vector drilling_force =
stiffness.Value().drilling_global24.Multiply(element_displacement);
rows.drilling_mode = DrillingMode();
Vector3 drilling_moment;
for (std::size_t node = 0U; node < kNodeCount; ++node) {
const std::size_t first = 6U * node;
const Vector3 force{drilling_force[first], drilling_force[first + 1U],
drilling_force[first + 2U]};
const Vector3 moment{drilling_force[first + 3U], drilling_force[first + 4U],
drilling_force[first + 5U]};
drilling_moment = drilling_moment + coordinates_[node].Cross(force) + moment;
}
rows.drilling_moment = drilling_moment.Components();
for (std::size_t point = 0U; point < recovered.Value().points.size();
++point) {
const auto& physical_point = recovered.Value().points[point];
+2 -1
View File
@@ -48,7 +48,8 @@ Status CommitRecoveryCandidate(RecoveryCandidate candidate,
!candidate.shell.rows.empty() ||
candidate.shell.physical_strain_energy != 0.0 ||
candidate.shell.equilibrium != zero_equilibrium ||
candidate.shell.verification_metrics != zero_metrics;
candidate.shell.verification_metrics != zero_metrics ||
candidate.shell.mitc4_drilling.has_value();
const bool has_complete_shell_inventory =
!candidate.expected_shell_elements.empty() &&
!candidate.shell.rows.empty();
@@ -10,6 +10,7 @@
#include <vector>
#include "fesa/math/vector3.h"
#include "results/mitc4_drilling_evidence.h"
namespace fesa::results_internal {
namespace {
@@ -243,11 +244,24 @@ Status RecoverShellGlobalEvidence(const Domain& domain, const DofManager& dofs,
candidate.shell.equilibrium[component] = force_balance[component];
candidate.shell.equilibrium[3U + component] = moment_balance[component];
}
Vector3 acceptance_moment = moment_balance;
if (candidate.shell.mitc4_drilling) {
CompleteDrillingEvidence(candidate.shell.equilibrium,
&*candidate.shell.mitc4_drilling);
if (!ValidDrillingEvidence(candidate.shell.mitc4_drilling,
candidate.shell.equilibrium))
return RecoveryFailure("invalid-shell-drilling-evidence",
{domain.SourcePath(), 0U}, "global-equilibrium",
"Shell drilling diagnostics must be finite and consistent.");
if (candidate.shell.mitc4_drilling->mode == Mitc4DrillingMode::kDirectSpring)
acceptance_moment = moment_balance -
Vector3(candidate.shell.mitc4_drilling->drilling_moment);
}
const double force_metric =
NormalizedBalance(force_balance.Components(),
(std::max)(applied_force_scale, reaction_force_scale));
const double moment_metric = NormalizedBalance(
moment_balance.Components(),
acceptance_moment.Components(),
(std::max)(applied_moment_scale, reaction_moment_scale));
candidate.shell.verification_metrics = {global.normalized_free_residual,
force_metric, moment_metric};
@@ -0,0 +1,54 @@
#ifndef FESA_RESULTS_MITC4_DRILLING_EVIDENCE_H_
#define FESA_RESULTS_MITC4_DRILLING_EVIDENCE_H_
#include <array>
#include <cmath>
#include <cstddef>
#include <optional>
#include "fesa/results/result_records.h"
namespace fesa::results_internal {
inline bool ValidDrillingMode(const Mitc4DrillingMode mode) {
return mode == Mitc4DrillingMode::kDirectSpring ||
mode == Mitc4DrillingMode::kLegacyCorotatedTwist;
}
/// @brief Validates exact raw-minus-internal diagnostics without a tolerance.
inline bool ValidDrillingEvidence(
const std::optional<Mitc4DrillingEvidence>& evidence,
const std::array<double, 6>& raw) {
if (!evidence) return true; // Older manually constructed fixtures.
if (!ValidDrillingMode(evidence->mode)) return false;
for (std::size_t i = 0U; i < 6U; ++i) {
const double expected = i < 3U ? raw[i]
: raw[i] - evidence->drilling_moment[i - 3U];
if (!std::isfinite(raw[i]) || !std::isfinite(expected) ||
!std::isfinite(evidence->equilibrium_with_drilling[i]) ||
evidence->equilibrium_with_drilling[i] != expected)
return false;
}
for (const double value : evidence->drilling_moment)
if (!std::isfinite(value)) return false;
return true;
}
inline bool SameDrillingInventory(
const std::optional<Mitc4DrillingEvidence>& left,
const std::optional<Mitc4DrillingEvidence>& right) {
return left.has_value() == right.has_value() &&
(!left || left->mode == right->mode);
}
/// @brief Keeps force balance raw and subtracts positive artificial moment once.
inline void CompleteDrillingEvidence(
const std::array<double, 6>& raw, Mitc4DrillingEvidence* evidence) {
evidence->equilibrium_with_drilling = raw;
for (std::size_t i = 0U; i < 3U; ++i)
evidence->equilibrium_with_drilling[3U + i] -= evidence->drilling_moment[i];
}
} // namespace fesa::results_internal
#endif // FESA_RESULTS_MITC4_DRILLING_EVIDENCE_H_
@@ -4,6 +4,8 @@
#include <cmath>
#include <cstddef>
#include "results/mitc4_drilling_evidence.h"
namespace fesa::results_internal {
bool IsFiniteShellRow(const ShellResultRow& row) {
@@ -29,6 +31,27 @@ Status ShellResultRecovery(const SourceLocation& location,
const EntityIndex expected_element,
const ShellElementResultRows& rows,
RecoveryCandidate& candidate) {
if ((rows.drilling_mode && !ValidDrillingMode(*rows.drilling_mode)) ||
!IsFiniteArray(rows.drilling_moment) ||
(candidate.has_shell_results &&
(candidate.shell.mitc4_drilling.has_value() != rows.drilling_mode.has_value() ||
(rows.drilling_mode &&
candidate.shell.mitc4_drilling->mode != *rows.drilling_mode)))) {
return RecoveryFailure("invalid-shell-drilling-evidence", location,
source_id.source_label_text,
"Shell recovery requires one finite actual drilling mode.");
}
if (rows.drilling_mode) {
if (!candidate.shell.mitc4_drilling)
candidate.shell.mitc4_drilling = Mitc4DrillingEvidence{*rows.drilling_mode};
for (std::size_t i = 0U; i < 3U; ++i) {
candidate.shell.mitc4_drilling->drilling_moment[i] += rows.drilling_moment[i];
if (!std::isfinite(candidate.shell.mitc4_drilling->drilling_moment[i]))
return RecoveryFailure("nonfinite-recovery-value", location,
source_id.source_label_text,
"Source-ordered drilling moment overflowed.");
}
}
const double accumulated_energy =
candidate.shell.physical_strain_energy + rows.physical_strain_energy;
if (!std::isfinite(rows.physical_strain_energy) ||