Connect MITC4 drilling evidence across analysis procedures
This commit is contained in:
@@ -18,6 +18,9 @@
|
||||
#include "fesa/analysis/prepared_analysis.h"
|
||||
#include "fesa/assembly/parallel_for.h"
|
||||
#include "fesa/assembly/sparse_assembler.h"
|
||||
#include "fesa/elements/element_factory.h"
|
||||
#include "fesa/results/result_recovery.h"
|
||||
#include "elements/mitc4_drilling_internal.h"
|
||||
#include "fesa/fem/dof_manager.h"
|
||||
#include "fesa/model/domain.h"
|
||||
#include "fesa/model/model_types.h"
|
||||
@@ -132,6 +135,8 @@ TEST(LinearFrequencyProcedureExecutor,
|
||||
fesa::LinearFrequencyProcedureExecutor{}.Execute(domain.Value());
|
||||
ASSERT_TRUE(result.HasValue()) << StatusText(result.GetStatus());
|
||||
const auto& state = result.Value();
|
||||
ASSERT_TRUE(state.drilling_mode);
|
||||
EXPECT_EQ(*state.drilling_mode, fesa::Mitc4DrillingMode::kDirectSpring);
|
||||
ASSERT_EQ(state.modes.size(), 1U);
|
||||
EXPECT_EQ(state.mass_null_dimension, 0U);
|
||||
EXPECT_EQ(state.finite_dimension, 1U);
|
||||
@@ -189,10 +194,10 @@ TEST(LinearFrequencyProcedureExecutor,
|
||||
}
|
||||
}
|
||||
|
||||
// E2-C09 is a pencil oracle. The public static contract conflict is recorded
|
||||
// separately; direct covariance does not certify spatial moment equilibrium.
|
||||
// E2-C09 keeps raw spatial imbalance visible while the direct-spring gate
|
||||
// subtracts only the separately recovered artificial internal moment.
|
||||
TEST(LinearFrequencyProcedureExecutor,
|
||||
PartialBoundaryStaticRejectsUnbalancedDrillingMoment) {
|
||||
PartialBoundaryStaticSeparatesArtificialMomentAndPreservesLegacyGate) {
|
||||
const auto domain = PanelDomain(false, false, true);
|
||||
ASSERT_TRUE(domain.HasValue());
|
||||
const fesa::SerialParallelFor serial;
|
||||
@@ -200,9 +205,32 @@ TEST(LinearFrequencyProcedureExecutor,
|
||||
const auto result =
|
||||
fesa::LinearStaticProcedureExecutor{serial, solver}.Execute(
|
||||
domain.Value());
|
||||
ASSERT_FALSE(result.HasValue());
|
||||
EXPECT_NE(StatusText(result.GetStatus())
|
||||
.find("global-equilibrium-tolerance-failure"),
|
||||
ASSERT_TRUE(result.HasValue()) << StatusText(result.GetStatus());
|
||||
const auto& state = result.Value().state;
|
||||
ASSERT_TRUE(state.Mitc4Drilling());
|
||||
EXPECT_NE(state.Equilibrium()[5U], 0.0);
|
||||
for (std::size_t c = 0U; c < 3U; ++c) {
|
||||
EXPECT_NEAR(state.Equilibrium()[3U + c], state.Mitc4Drilling()->drilling_moment[c], 1e-14);
|
||||
EXPECT_NEAR(state.Mitc4Drilling()->equilibrium_with_drilling[3U + c], 0.0, 1e-14);
|
||||
}
|
||||
auto model = fesa::AnalysisModel::Create(domain.Value());
|
||||
ASSERT_TRUE(model.HasValue());
|
||||
auto dofs = fesa::DofManager::Create(model.Value());
|
||||
ASSERT_TRUE(dofs.HasValue());
|
||||
auto element = fesa::ElementFactory{}.Create(domain.Value().Elements()[0U], domain.Value());
|
||||
ASSERT_TRUE(element.HasValue());
|
||||
auto* shell = dynamic_cast<fesa::Mitc4Shell*>(element.Value().get());
|
||||
ASSERT_NE(shell, nullptr);
|
||||
*shell = fesa::mitc4_drilling_internal::TestAccess::WithMode(
|
||||
*shell, fesa::Mitc4DrillingMode::kLegacyCorotatedTwist);
|
||||
fesa::ElementView view{std::cref(*shell)};
|
||||
auto stiffness = fesa::SparseAssembler::Assemble(view, dofs.Value(), serial);
|
||||
ASSERT_TRUE(stiffness.HasValue());
|
||||
auto legacy_state = fesa::AnalysisState::Create(dofs.Value(), {"Step-1", 0U});
|
||||
const auto legacy = fesa::ResultRecovery::Recover(model.Value(), view, dofs.Value(),
|
||||
stiffness.Value(), state.Displacement(), state.ExternalForce(), legacy_state);
|
||||
ASSERT_FALSE(legacy.IsOk());
|
||||
EXPECT_NE(StatusText(legacy).find("global-equilibrium-tolerance-failure"),
|
||||
std::string::npos);
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,13 @@
|
||||
#include <vector>
|
||||
|
||||
#include "fesa/elements/finite_rotation.h"
|
||||
#include "fesa/analysis/prepared_analysis.h"
|
||||
#include "fesa/analysis/nonlinear_static_procedure.h"
|
||||
#include "fesa/analysis/newton_iteration_log.h"
|
||||
#include "fesa/assembly/parallel_for.h"
|
||||
#include "fesa/elements/element_factory.h"
|
||||
#include "fesa/elements/mitc4_shell.h"
|
||||
#include "fesa/solvers/linear/mkl_pardiso_tangent_solver.h"
|
||||
#include "fesa/math/vector3.h"
|
||||
|
||||
namespace {
|
||||
@@ -343,3 +350,57 @@ TEST(NonlinearStaticProcedure,
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST(Mitc4DrillingEvidence, NonlinearAndPressureFramesRetainSignedTorqueOracle) {
|
||||
for (const bool pressure : {false, true}) {
|
||||
SCOPED_TRACE(pressure);
|
||||
TempDirectory directory{"drilling"};
|
||||
auto deck = AllConstrainedNonlinearShellDeck();
|
||||
const auto boundary = deck.find("All, 1, 6");
|
||||
ASSERT_NE(boundary, std::string::npos);
|
||||
deck.replace(boundary, std::string{"All, 1, 6"}.size(), "All, 1, 5\nAll, 6, 6, 0.125");
|
||||
if (pressure) {
|
||||
const auto assembly_end = deck.find("*End Assembly");
|
||||
deck.insert(assembly_end, "*Surface, TYPE=ELEMENT, NAME=Pressure\nShell-1.1, SPOS\n");
|
||||
deck.insert(deck.find("*End Step"), "*Dsload\nPressure, P, 4.0\n");
|
||||
}
|
||||
const auto path = directory.Path() / "drilling.inp";
|
||||
WriteText(path, deck);
|
||||
auto prepared = fesa::PrepareAnalysisInput(path);
|
||||
ASSERT_TRUE(prepared.HasValue());
|
||||
const auto& domain = prepared.Value().domain;
|
||||
auto element = fesa::ElementFactory{}.Create(domain.Elements()[0U], domain);
|
||||
ASSERT_TRUE(element.HasValue());
|
||||
const auto* shell = dynamic_cast<const fesa::Mitc4Shell*>(element.Value().get());
|
||||
ASSERT_NE(shell, nullptr);
|
||||
const auto stiffness = shell->Stiffness();
|
||||
ASSERT_TRUE(stiffness.HasValue());
|
||||
double trace = 0.0;
|
||||
for (std::size_t node = 0U; node < 4U; ++node)
|
||||
for (std::size_t c = 3U; c < 5U; ++c)
|
||||
trace += stiffness.Value().physical_local20(5U * node + c, 5U * node + c);
|
||||
const double kd = 1e-3 * trace / 8.0;
|
||||
fesa::SerialParallelFor serial;
|
||||
fesa::MklPardisoTangentSolver tangent;
|
||||
fesa::NewtonIterationLogWriter log;
|
||||
ASSERT_TRUE(log.Open(directory.Path() / "drilling.log").IsOk());
|
||||
auto result = fesa::NonlinearStaticProcedureExecutor{serial, tangent}.Execute(domain, log);
|
||||
ASSERT_TRUE(log.Close().IsOk());
|
||||
ASSERT_TRUE(result.HasValue()) << (result.GetStatus().Diagnostics().empty() ? "" : result.GetStatus().Diagnostics()[0U].message);
|
||||
ASSERT_EQ(result.Value().state.AcceptedFrames().size(), 10U);
|
||||
for (const auto& frame : result.Value().state.AcceptedFrames()) {
|
||||
ASSERT_TRUE(frame.mitc4_drilling);
|
||||
EXPECT_EQ(frame.mitc4_drilling->mode, fesa::Mitc4DrillingMode::kDirectSpring);
|
||||
const double torque = 4.0 * kd * 0.125 * frame.load_factor;
|
||||
EXPECT_NEAR(frame.global_equilibrium[5U], torque, 1e-13);
|
||||
EXPECT_NEAR(frame.mitc4_drilling->drilling_moment[2U], torque, 1e-13);
|
||||
for (const double value : frame.mitc4_drilling->equilibrium_with_drilling)
|
||||
EXPECT_NEAR(value, 0.0, 1e-13);
|
||||
EXPECT_NEAR(frame.physical_strain_energy, 0.0, 1e-20);
|
||||
if (pressure) EXPECT_NE(frame.external_force[2U], 0.0);
|
||||
}
|
||||
ASSERT_TRUE(result.Value().state.CommittedState().mitc4_drilling);
|
||||
EXPECT_EQ(result.Value().state.CommittedState().mitc4_drilling->drilling_moment,
|
||||
result.Value().state.AcceptedFrames().back().mitc4_drilling->drilling_moment);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user