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
@@ -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());