완성된 HHT trial의 constraint 자료 중복 평가 제거
This commit is contained in:
@@ -5,6 +5,8 @@
|
||||
#include <cmath>
|
||||
#include <cfenv>
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <limits>
|
||||
@@ -19,6 +21,7 @@
|
||||
#endif
|
||||
|
||||
#include "analysis/hht_reduced_internal.h"
|
||||
#include "math/dense_blas_internal.h"
|
||||
#include "fesa/analysis/analysis_model.h"
|
||||
#include "fesa/analysis/hht_time_integration.h"
|
||||
#include "fesa/assembly/parallel_for.h"
|
||||
@@ -26,6 +29,71 @@
|
||||
|
||||
namespace {
|
||||
|
||||
class RestoreTrialEnvironment {
|
||||
public:
|
||||
RestoreTrialEnvironment()
|
||||
: rounding_(std::fegetround()),
|
||||
dynamic_(mkl_get_dynamic()),
|
||||
tls_(mkl_set_num_threads_local(0)) {
|
||||
#if defined(_M_X64)
|
||||
mxcsr_ = _mm_getcsr();
|
||||
_mm_setcsr((_mm_getcsr() | 0x1F80U) & ~0x8040U);
|
||||
#endif
|
||||
static_cast<void>(std::fesetround(FE_TONEAREST));
|
||||
mkl_set_dynamic(0);
|
||||
}
|
||||
~RestoreTrialEnvironment() {
|
||||
mkl_set_dynamic(dynamic_);
|
||||
static_cast<void>(mkl_set_num_threads_local(tls_));
|
||||
static_cast<void>(std::fesetround(rounding_));
|
||||
#if defined(_M_X64)
|
||||
_mm_setcsr(mxcsr_);
|
||||
#endif
|
||||
}
|
||||
|
||||
private:
|
||||
int rounding_;
|
||||
int dynamic_;
|
||||
int tls_;
|
||||
#if defined(_M_X64)
|
||||
unsigned int mxcsr_;
|
||||
#endif
|
||||
};
|
||||
|
||||
void ExpectSameBits(const double left, const double right) {
|
||||
EXPECT_EQ(std::memcmp(&left, &right, sizeof(double)), 0);
|
||||
}
|
||||
|
||||
void ExpectSameOperators(
|
||||
const fesa::hht_reduced_internal::ConstraintOperators& left,
|
||||
const fesa::hht_reduced_internal::ConstraintOperators& right) {
|
||||
ASSERT_EQ(left.rows.size(), right.rows.size());
|
||||
for (std::size_t row = 0U; row < left.rows.size(); ++row) {
|
||||
const auto& a = left.rows[row];
|
||||
const auto& b = right.rows[row];
|
||||
ExpectSameBits(a.value, b.value);
|
||||
ExpectSameBits(a.value_scale, b.value_scale);
|
||||
ExpectSameBits(a.time_first, b.time_first);
|
||||
ExpectSameBits(a.time_second, b.time_second);
|
||||
for (const auto& pair : {std::make_pair(&a.gradient, &b.gradient),
|
||||
std::make_pair(&a.time_gradient, &b.time_gradient),
|
||||
std::make_pair(&a.third_directional_gradient,
|
||||
&b.third_directional_gradient)}) {
|
||||
ASSERT_EQ(pair.first->size(), pair.second->size());
|
||||
for (std::size_t i = 0U; i < pair.first->size(); ++i) {
|
||||
EXPECT_EQ((*pair.first)[i].dof, (*pair.second)[i].dof);
|
||||
ExpectSameBits((*pair.first)[i].value, (*pair.second)[i].value);
|
||||
}
|
||||
}
|
||||
ASSERT_EQ(a.hessian.size(), b.hessian.size());
|
||||
for (std::size_t i = 0U; i < a.hessian.size(); ++i) {
|
||||
EXPECT_EQ(a.hessian[i].row, b.hessian[i].row);
|
||||
EXPECT_EQ(a.hessian[i].column, b.hessian[i].column);
|
||||
ExpectSameBits(a.hessian[i].value, b.hessian[i].value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct Fixture {
|
||||
std::unique_ptr<fesa::Domain> domain;
|
||||
fesa::DofManager dofs;
|
||||
@@ -394,6 +462,480 @@ TEST(HhtReducedChart, InvertibleGraphDoesNotCertifySelectorTransversality) {
|
||||
assembly, chart.Value(), solver).HasValue());
|
||||
}
|
||||
|
||||
TEST(HhtReducedOperators,
|
||||
CompleteTrialConstraintDataMatchesFreshAndOwnsCopies) {
|
||||
RestoreTrialEnvironment restore;
|
||||
const auto fixture = MakeFixture(false, true);
|
||||
const auto shells =
|
||||
fesa::hht_internal::BuildRuntimeShells(*fixture.domain, fixture.dofs);
|
||||
ASSERT_TRUE(shells.HasValue());
|
||||
const fesa::Vector zero(fixture.dofs.FullDofCount());
|
||||
auto q = zero;
|
||||
auto v = zero;
|
||||
auto force = zero;
|
||||
auto rate = zero;
|
||||
auto acceleration = zero;
|
||||
for (std::size_t i = 0U; i < zero.Size(); ++i) {
|
||||
q[i] = 0.001 * static_cast<double>((i % 5U) + 1U);
|
||||
v[i] = 0.002 * static_cast<double>((i % 7U) + 1U);
|
||||
if (i % 6U >= 3U) {
|
||||
force[i] = 0.03;
|
||||
rate[i] = -0.02;
|
||||
acceleration[i] = 0.01;
|
||||
}
|
||||
}
|
||||
auto assembled = Assemble(fixture, shells.Value(), q, v, zero, force, true);
|
||||
ASSERT_TRUE(assembled.HasValue());
|
||||
ASSERT_TRUE(assembled.Value().trial_constraints.has_value());
|
||||
ASSERT_EQ(assembled.Value().trial_constraints->shells.size(),
|
||||
shells.Value().size());
|
||||
const auto chart = fesa::hht_reduced_internal::BuildFixedChart(
|
||||
fixture.dofs, assembled.Value().rotations);
|
||||
ASSERT_TRUE(chart.HasValue());
|
||||
for (std::size_t e = 0U; e < shells.Value().size(); ++e) {
|
||||
const auto& saved = assembled.Value().trial_constraints->shells[e];
|
||||
ASSERT_TRUE(saved.directional_internal);
|
||||
fesa::NonlinearMitc4TrialDofs trial{};
|
||||
fesa::Vector direction{24U};
|
||||
for (std::size_t node = 0U; node < 4U; ++node) {
|
||||
const auto first = 6U * node;
|
||||
trial.displacement[node] =
|
||||
fesa::Vector3{saved.q[first], saved.q[first + 1U], saved.q[first + 2U]};
|
||||
trial.rotation_vector[node] = fesa::Vector3{
|
||||
saved.q[first + 3U], saved.q[first + 4U], saved.q[first + 5U]};
|
||||
}
|
||||
for (std::size_t i = 0U; i < 24U; ++i) direction[i] = saved.v[i];
|
||||
const auto response = fesa::EvaluateInternalResidualAndTangent(
|
||||
shells.Value()[e].internal, trial, &direction);
|
||||
ASSERT_TRUE(response.HasValue());
|
||||
for (std::size_t node = 0U; node < 4U; ++node) {
|
||||
ExpectSameBits(saved.drilling_constraint[node],
|
||||
response.Value().drilling_constraint[node]);
|
||||
for (std::size_t i = 0U; i < 24U; ++i) {
|
||||
ExpectSameBits(saved.drilling_constraint_gradient(node, i),
|
||||
response.Value().drilling_constraint_gradient(node, i));
|
||||
ExpectSameBits(
|
||||
saved.drilling_constraint_third_directional_gradient[node][i],
|
||||
response.Value()
|
||||
.drilling_constraint_third_directional_gradient[node][i]);
|
||||
for (std::size_t j = 0U; j < 24U; ++j)
|
||||
ExpectSameBits(
|
||||
saved.drilling_constraint_hessian[node](i, j),
|
||||
response.Value().drilling_constraint_hessian[node](i, j));
|
||||
}
|
||||
}
|
||||
}
|
||||
const auto evaluate = [&](const fesa::hht_internal::DynamicAssembly& value) {
|
||||
return fesa::hht_reduced_internal::EvaluateConstraintOperators(
|
||||
*fixture.domain, fixture.dofs, shells.Value(), value, chart.Value(),
|
||||
rate, acceleration);
|
||||
};
|
||||
auto fresh = assembled.Value();
|
||||
fresh.trial_constraints.reset();
|
||||
const auto reference = evaluate(fresh);
|
||||
const auto reused = evaluate(assembled.Value());
|
||||
ASSERT_TRUE(reference.HasValue());
|
||||
ASSERT_TRUE(reused.HasValue());
|
||||
ExpectSameOperators(reused.Value(), reference.Value());
|
||||
auto copy = assembled.Value();
|
||||
copy.trial_constraints->shells[0U].drilling_constraint[0U] += 7.0;
|
||||
// Test-only finite marker proves the selected route actually reads its owner.
|
||||
const auto marker = evaluate(copy);
|
||||
ASSERT_TRUE(marker.HasValue());
|
||||
EXPECT_NE(marker.Value().rows[0U].value, reused.Value().rows[0U].value);
|
||||
EXPECT_NE(
|
||||
copy.trial_constraints->shells[0U].drilling_constraint[0U],
|
||||
assembled.Value().trial_constraints->shells[0U].drilling_constraint[0U]);
|
||||
auto moved = std::move(assembled.Value());
|
||||
const auto moved_result = evaluate(moved);
|
||||
ASSERT_TRUE(moved_result.HasValue());
|
||||
ExpectSameOperators(moved_result.Value(), reference.Value());
|
||||
fesa::DynamicAcceptedFrameState frame{};
|
||||
frame.generalized_coordinates = q;
|
||||
frame.velocity = v;
|
||||
frame.acceleration = zero;
|
||||
frame.algorithmic_acceleration = zero;
|
||||
frame.external_force = force;
|
||||
const auto parameters = fesa::MakeHhtParameters(-0.05);
|
||||
ASSERT_TRUE(parameters.HasValue());
|
||||
fesa::TbbParallelFor parallel;
|
||||
const auto tbb = fesa::hht_internal::EvaluateAssembly(
|
||||
*fixture.domain, fixture.dofs, shells.Value(), frame, zero, 0.1,
|
||||
parameters.Value(), parallel, true);
|
||||
ASSERT_TRUE(tbb.HasValue());
|
||||
const auto tbb_result = evaluate(tbb.Value());
|
||||
ASSERT_TRUE(tbb_result.HasValue());
|
||||
ExpectSameOperators(tbb_result.Value(), reference.Value());
|
||||
}
|
||||
|
||||
TEST(HhtReducedOperators,
|
||||
ChangedTrialConstraintContextFallsBackInOriginalPosition) {
|
||||
RestoreTrialEnvironment restore;
|
||||
const auto fixture = MakeFixture(false);
|
||||
const auto shells =
|
||||
fesa::hht_internal::BuildRuntimeShells(*fixture.domain, fixture.dofs);
|
||||
ASSERT_TRUE(shells.HasValue());
|
||||
const fesa::Vector zero(fixture.dofs.FullDofCount());
|
||||
const auto assembled =
|
||||
Assemble(fixture, shells.Value(), zero, zero, zero, zero, true);
|
||||
ASSERT_TRUE(assembled.HasValue());
|
||||
ASSERT_TRUE(assembled.Value().trial_constraints.has_value());
|
||||
ASSERT_TRUE(
|
||||
assembled.Value().trial_constraints->shells[0U].directional_internal);
|
||||
const auto chart = fesa::hht_reduced_internal::BuildFixedChart(
|
||||
fixture.dofs, assembled.Value().rotations);
|
||||
ASSERT_TRUE(chart.HasValue());
|
||||
const auto evaluate = [&](const fesa::hht_internal::DynamicAssembly& value) {
|
||||
return fesa::hht_reduced_internal::EvaluateConstraintOperators(
|
||||
*fixture.domain, fixture.dofs, shells.Value(), value, chart.Value(),
|
||||
zero, zero);
|
||||
};
|
||||
using Assembly = fesa::hht_internal::DynamicAssembly;
|
||||
std::vector<std::function<void(Assembly&)>> changes{
|
||||
[](Assembly& a) { a.frame.generalized_coordinates[2U] = 0.001; },
|
||||
[](Assembly& a) { a.frame.velocity[3U] = 0.003; },
|
||||
[](Assembly& a) { a.frame.time = 0.1; },
|
||||
[](Assembly& a) { ++a.frame.frame_index; },
|
||||
[](Assembly& a) { ++a.frame.increment_index; },
|
||||
[](Assembly& a) { ++a.frame.iteration_count; },
|
||||
[](Assembly& a) { a.frame.is_initial = true; },
|
||||
[](Assembly& a) { a.trial_constraints->source_path = "different.inp"; },
|
||||
[](Assembly& a) { ++a.trial_constraints->shells[0U].element_index; },
|
||||
[](Assembly& a) { ++a.trial_constraints->shells[0U].scatter[0U]; },
|
||||
[](Assembly& a) {
|
||||
a.trial_constraints->shells[0U].directional_internal = false;
|
||||
},
|
||||
[](Assembly& a) {
|
||||
a.trial_constraints->shells[0U].node_source_ids[0U].instance_name +=
|
||||
"x";
|
||||
},
|
||||
[](Assembly& a) {
|
||||
++a.trial_constraints->shells[0U].node_source_ids[0U].source_label;
|
||||
},
|
||||
[](Assembly& a) {
|
||||
a.trial_constraints->shells[0U].node_source_ids[0U].source_label_text +=
|
||||
"x";
|
||||
},
|
||||
[](Assembly& a) {
|
||||
++a.trial_constraints->shells[0U].configuration.source.line;
|
||||
},
|
||||
[](Assembly& a) {
|
||||
a.trial_constraints->shells[0U].configuration.source.file =
|
||||
"different.inp";
|
||||
},
|
||||
[](Assembly& a) {
|
||||
a.trial_constraints->shells[0U].configuration.identity += "x";
|
||||
},
|
||||
[](Assembly& a) { a.trial_constraints->shells[0U].q[0U] = -0.0; },
|
||||
[](Assembly& a) { a.trial_constraints->shells[0U].v[0U] = -0.0; },
|
||||
[](Assembly& a) {
|
||||
a.trial_constraints->shells[0U].drilling_constraint_gradient =
|
||||
fesa::Matrix{0U, 0U};
|
||||
},
|
||||
[](Assembly& a) {
|
||||
a.trial_constraints->shells[0U].drilling_constraint_hessian[2U] =
|
||||
fesa::Matrix{0U, 0U};
|
||||
},
|
||||
[](Assembly& a) {
|
||||
a.trial_constraints->shells[0U]
|
||||
.drilling_constraint_third_directional_gradient[1U] =
|
||||
fesa::Vector{0U};
|
||||
},
|
||||
[](Assembly& a) {
|
||||
a.trial_constraints->shells[0U].drilling_constraint_hessian[0U](
|
||||
0U, 0U) = std::numeric_limits<double>::infinity();
|
||||
},
|
||||
[](Assembly& a) {
|
||||
a.trial_constraints->shells[0U]
|
||||
.evaluation_context.dense_policy.blas_max_threads = 2;
|
||||
},
|
||||
[](Assembly& a) {
|
||||
a.trial_constraints->shells[0U]
|
||||
.evaluation_context.dense_policy.dynamic = 1;
|
||||
},
|
||||
[](Assembly& a) {
|
||||
a.trial_constraints->shells[0U]
|
||||
.evaluation_context.dense_policy.dynamic = -1;
|
||||
},
|
||||
[](Assembly& a) {
|
||||
a.trial_constraints->shells[0U].evaluation_context.mxcsr_control ^=
|
||||
0x8000U;
|
||||
},
|
||||
[](Assembly& a) {
|
||||
a.trial_constraints->shells[0U].evaluation_context.supported = false;
|
||||
}};
|
||||
for (std::size_t i = 0U; i < 64U; ++i)
|
||||
changes.push_back([i](Assembly& a) {
|
||||
a.trial_constraints->shells[0U].configuration.values[i] += 0.125;
|
||||
});
|
||||
for (const double invalid : {std::numeric_limits<double>::quiet_NaN(),
|
||||
std::numeric_limits<double>::infinity(),
|
||||
-std::numeric_limits<double>::infinity()})
|
||||
changes.push_back([invalid](Assembly& a) {
|
||||
a.trial_constraints->shells[0U].configuration.values[60U] = invalid;
|
||||
});
|
||||
for (std::size_t i = 0U; i < changes.size(); ++i) {
|
||||
SCOPED_TRACE(i);
|
||||
auto candidate = assembled.Value();
|
||||
candidate.trial_constraints->shells[0U].drilling_constraint[0U] = 17.0;
|
||||
changes[i](candidate);
|
||||
auto fresh = candidate;
|
||||
fresh.trial_constraints.reset();
|
||||
const auto reference = evaluate(fresh);
|
||||
const auto actual = evaluate(candidate);
|
||||
ASSERT_TRUE(reference.HasValue());
|
||||
ASSERT_TRUE(actual.HasValue());
|
||||
ExpectSameOperators(actual.Value(), reference.Value());
|
||||
}
|
||||
for (std::size_t kind = 0U; kind < 2U; ++kind) {
|
||||
auto candidate = assembled.Value();
|
||||
if (kind == 0U) candidate.trial_constraints->shells.clear();
|
||||
if (kind == 1U) candidate.trial_constraints.reset();
|
||||
auto fresh = assembled.Value();
|
||||
fresh.trial_constraints.reset();
|
||||
const auto reference = evaluate(fresh);
|
||||
const auto actual = evaluate(candidate);
|
||||
ASSERT_TRUE(reference.HasValue());
|
||||
ASSERT_TRUE(actual.HasValue());
|
||||
ExpectSameOperators(actual.Value(), reference.Value());
|
||||
}
|
||||
// Change the actual consumer environment, preserving the original fallback.
|
||||
for (std::size_t mode = 0U; mode < 5U; ++mode) {
|
||||
SCOPED_TRACE(mode);
|
||||
auto candidate = assembled.Value();
|
||||
candidate.trial_constraints->shells[0U].drilling_constraint[0U] = 17.0;
|
||||
auto fresh = candidate;
|
||||
fresh.trial_constraints.reset();
|
||||
#if defined(_M_X64)
|
||||
const auto csr = _mm_getcsr();
|
||||
if (mode == 0U) _mm_setcsr(csr ^ 0x8000U);
|
||||
if (mode == 1U) _mm_setcsr(csr ^ 0x0040U);
|
||||
#endif
|
||||
const int rounding = std::fegetround();
|
||||
if (mode == 2U) static_cast<void>(std::fesetround(FE_DOWNWARD));
|
||||
if (mode == 3U) static_cast<void>(mkl_set_num_threads_local(2));
|
||||
if (mode == 4U) mkl_set_dynamic(1);
|
||||
const auto reference = evaluate(fresh);
|
||||
const auto actual = evaluate(candidate);
|
||||
if (mode == 4U) mkl_set_dynamic(0);
|
||||
if (mode == 3U) EXPECT_EQ(mkl_set_num_threads_local(0), 2);
|
||||
static_cast<void>(std::fesetround(rounding));
|
||||
#if defined(_M_X64)
|
||||
_mm_setcsr(csr);
|
||||
#endif
|
||||
ASSERT_TRUE(reference.HasValue());
|
||||
ASSERT_TRUE(actual.HasValue());
|
||||
ExpectSameOperators(actual.Value(), reference.Value());
|
||||
}
|
||||
auto failed_trial = assembled.Value();
|
||||
failed_trial.frame.generalized_coordinates[0U] =
|
||||
std::numeric_limits<double>::infinity();
|
||||
auto fresh_failure = failed_trial;
|
||||
fresh_failure.trial_constraints.reset();
|
||||
const auto failed = evaluate(failed_trial);
|
||||
const auto original = evaluate(fresh_failure);
|
||||
ASSERT_FALSE(failed.HasValue());
|
||||
ASSERT_FALSE(original.HasValue());
|
||||
ASSERT_EQ(failed.GetStatus().Diagnostics().size(),
|
||||
original.GetStatus().Diagnostics().size());
|
||||
EXPECT_EQ(failed.GetStatus().Diagnostics()[0U].code,
|
||||
original.GetStatus().Diagnostics()[0U].code);
|
||||
EXPECT_EQ(failed.GetStatus().Diagnostics()[0U].message,
|
||||
original.GetStatus().Diagnostics()[0U].message);
|
||||
const auto rerun = evaluate(assembled.Value());
|
||||
auto fresh = assembled.Value();
|
||||
fresh.trial_constraints.reset();
|
||||
const auto reference = evaluate(fresh);
|
||||
ASSERT_TRUE(rerun.HasValue());
|
||||
ASSERT_TRUE(reference.HasValue());
|
||||
ExpectSameOperators(rerun.Value(), reference.Value());
|
||||
}
|
||||
|
||||
TEST(HhtReducedOperators, TrialContextControlsAndInitialFalseFailureLifetime) {
|
||||
RestoreTrialEnvironment restore;
|
||||
const auto context = fesa::hht_internal::ReadTrialEvaluationContext();
|
||||
ASSERT_TRUE(fesa::hht_internal::ReusableTrialContext(context, context));
|
||||
for (const unsigned int bit : {0x0040U, 0x8000U, 0x2000U, 0x0080U}) {
|
||||
auto changed = context;
|
||||
changed.mxcsr_control ^= bit;
|
||||
EXPECT_FALSE(fesa::hht_internal::ReusableTrialContext(context, changed));
|
||||
}
|
||||
auto unmasked = context;
|
||||
unmasked.mxcsr_control &= ~0x0080U;
|
||||
EXPECT_FALSE(fesa::hht_internal::ReusableTrialContext(unmasked, unmasked));
|
||||
static_cast<void>(mkl_set_num_threads_local(2));
|
||||
const auto positive = fesa::hht_internal::ReadTrialEvaluationContext();
|
||||
EXPECT_FALSE(fesa::hht_internal::ReusableTrialContext(positive, positive));
|
||||
EXPECT_EQ(mkl_set_num_threads_local(0), 2);
|
||||
mkl_set_dynamic(1);
|
||||
const auto dynamic = fesa::hht_internal::ReadTrialEvaluationContext();
|
||||
EXPECT_FALSE(fesa::hht_internal::ReusableTrialContext(dynamic, dynamic));
|
||||
mkl_set_dynamic(0);
|
||||
const auto fixture = MakeFixture(false);
|
||||
const auto shells =
|
||||
fesa::hht_internal::BuildRuntimeShells(*fixture.domain, fixture.dofs);
|
||||
ASSERT_TRUE(shells.HasValue());
|
||||
const fesa::Vector zero(fixture.dofs.FullDofCount());
|
||||
const auto position =
|
||||
Assemble(fixture, shells.Value(), zero, zero, zero, zero, false);
|
||||
ASSERT_TRUE(position.HasValue());
|
||||
EXPECT_FALSE(position.Value().trial_constraints.has_value());
|
||||
mkl_set_dynamic(1);
|
||||
const auto dynamic_trial =
|
||||
Assemble(fixture, shells.Value(), zero, zero, zero, zero, true);
|
||||
mkl_set_dynamic(0);
|
||||
ASSERT_TRUE(dynamic_trial.HasValue());
|
||||
ASSERT_TRUE(dynamic_trial.Value().trial_constraints.has_value());
|
||||
EXPECT_FALSE(
|
||||
dynamic_trial.Value().trial_constraints->shells[0U].directional_internal);
|
||||
static_cast<void>(mkl_set_num_threads_local(2));
|
||||
const auto positive_trial =
|
||||
Assemble(fixture, shells.Value(), zero, zero, zero, zero, true);
|
||||
EXPECT_EQ(mkl_set_num_threads_local(0), 2);
|
||||
ASSERT_TRUE(positive_trial.HasValue());
|
||||
ASSERT_TRUE(positive_trial.Value().trial_constraints.has_value());
|
||||
EXPECT_FALSE(positive_trial.Value()
|
||||
.trial_constraints->shells[0U]
|
||||
.directional_internal);
|
||||
fesa::DynamicAcceptedFrameState frame{};
|
||||
frame.is_initial = true;
|
||||
for (auto* vector :
|
||||
{&frame.generalized_coordinates, &frame.velocity, &frame.acceleration,
|
||||
&frame.algorithmic_acceleration, &frame.external_force})
|
||||
*vector = zero;
|
||||
fesa::SerialParallelFor parallel;
|
||||
const auto parameters = fesa::MakeHhtParameters(-0.05);
|
||||
ASSERT_TRUE(parameters.HasValue());
|
||||
const auto initial = fesa::hht_internal::EvaluateAssembly(
|
||||
*fixture.domain, fixture.dofs, shells.Value(), frame, zero, 0.1,
|
||||
parameters.Value(), parallel, true);
|
||||
ASSERT_TRUE(initial.HasValue());
|
||||
EXPECT_FALSE(initial.Value().trial_constraints.has_value());
|
||||
auto invalid = zero;
|
||||
invalid[0U] = std::numeric_limits<double>::quiet_NaN();
|
||||
const auto failure =
|
||||
Assemble(fixture, shells.Value(), invalid, zero, zero, zero, true);
|
||||
EXPECT_FALSE(failure.HasValue());
|
||||
const auto current =
|
||||
Assemble(fixture, shells.Value(), zero, zero, zero, zero, true);
|
||||
const auto next =
|
||||
Assemble(fixture, shells.Value(), zero, zero, zero, zero, true);
|
||||
ASSERT_TRUE(current.HasValue());
|
||||
ASSERT_TRUE(next.HasValue());
|
||||
ASSERT_EQ(current.Value().trial_constraints->shells.size(),
|
||||
shells.Value().size());
|
||||
ASSERT_EQ(next.Value().trial_constraints->shells.size(),
|
||||
shells.Value().size());
|
||||
EXPECT_NE(¤t.Value().trial_constraints->shells[0U],
|
||||
&next.Value().trial_constraints->shells[0U]);
|
||||
}
|
||||
|
||||
TEST(HhtReducedOperators, NonfiniteMatchingTrialKeysPreserveOriginalFailure) {
|
||||
RestoreTrialEnvironment restore;
|
||||
const auto fixture = MakeFixture(false);
|
||||
const auto shells =
|
||||
fesa::hht_internal::BuildRuntimeShells(*fixture.domain, fixture.dofs);
|
||||
ASSERT_TRUE(shells.HasValue());
|
||||
const fesa::Vector zero(fixture.dofs.FullDofCount());
|
||||
const auto assembled =
|
||||
Assemble(fixture, shells.Value(), zero, zero, zero, zero, true);
|
||||
ASSERT_TRUE(assembled.HasValue());
|
||||
ASSERT_TRUE(assembled.Value().trial_constraints.has_value());
|
||||
ASSERT_TRUE(
|
||||
assembled.Value().trial_constraints->shells[0U].directional_internal);
|
||||
const auto chart = fesa::hht_reduced_internal::BuildFixedChart(
|
||||
fixture.dofs, assembled.Value().rotations);
|
||||
ASSERT_TRUE(chart.HasValue());
|
||||
const auto evaluate = [&](const fesa::hht_internal::DynamicAssembly& value) {
|
||||
return fesa::hht_reduced_internal::EvaluateConstraintOperators(
|
||||
*fixture.domain, fixture.dofs, shells.Value(), value, chart.Value(),
|
||||
zero, zero);
|
||||
};
|
||||
const std::array<double, 3> invalid{std::numeric_limits<double>::quiet_NaN(),
|
||||
std::numeric_limits<double>::infinity(),
|
||||
-std::numeric_limits<double>::infinity()};
|
||||
for (std::size_t channel = 0U; channel < 2U; ++channel)
|
||||
for (std::size_t i = 0U; i < invalid.size(); ++i) {
|
||||
SCOPED_TRACE(channel);
|
||||
SCOPED_TRACE(i);
|
||||
auto candidate = assembled.Value();
|
||||
auto& saved = candidate.trial_constraints->shells[0U];
|
||||
if (channel == 0U) {
|
||||
candidate.frame.generalized_coordinates[0U] = invalid[i];
|
||||
saved.q[0U] = invalid[i];
|
||||
} else {
|
||||
candidate.frame.velocity[0U] = invalid[i];
|
||||
saved.v[0U] = invalid[i];
|
||||
}
|
||||
auto fresh = candidate;
|
||||
fresh.trial_constraints.reset();
|
||||
const auto original = evaluate(fresh);
|
||||
const auto actual = evaluate(candidate);
|
||||
ASSERT_FALSE(original.HasValue());
|
||||
ASSERT_FALSE(actual.HasValue());
|
||||
EXPECT_EQ(actual.GetStatus().Category(), original.GetStatus().Category());
|
||||
const auto& a = actual.GetStatus().Diagnostics();
|
||||
const auto& b = original.GetStatus().Diagnostics();
|
||||
ASSERT_EQ(a.size(), b.size());
|
||||
for (std::size_t j = 0U; j < a.size(); ++j) {
|
||||
EXPECT_EQ(a[j].severity, b[j].severity);
|
||||
EXPECT_EQ(a[j].code, b[j].code);
|
||||
EXPECT_EQ(a[j].location.file, b[j].location.file);
|
||||
EXPECT_EQ(a[j].location.line, b[j].location.line);
|
||||
EXPECT_EQ(a[j].keyword, b[j].keyword);
|
||||
EXPECT_EQ(a[j].entity_identity, b[j].entity_identity);
|
||||
EXPECT_EQ(a[j].message, b[j].message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(HhtReducedOperators, NonfiniteMatchingTimeKeyMissPreservesFreshSuccess) {
|
||||
RestoreTrialEnvironment restore;
|
||||
const auto fixture = MakeFixture(false);
|
||||
const auto shells =
|
||||
fesa::hht_internal::BuildRuntimeShells(*fixture.domain, fixture.dofs);
|
||||
ASSERT_TRUE(shells.HasValue());
|
||||
const fesa::Vector zero(fixture.dofs.FullDofCount());
|
||||
const auto assembled =
|
||||
Assemble(fixture, shells.Value(), zero, zero, zero, zero, true);
|
||||
ASSERT_TRUE(assembled.HasValue());
|
||||
ASSERT_TRUE(assembled.Value().trial_constraints.has_value());
|
||||
const auto chart = fesa::hht_reduced_internal::BuildFixedChart(
|
||||
fixture.dofs, assembled.Value().rotations);
|
||||
ASSERT_TRUE(chart.HasValue());
|
||||
const auto evaluate = [&](const fesa::hht_internal::DynamicAssembly& value) {
|
||||
return fesa::hht_reduced_internal::EvaluateConstraintOperators(
|
||||
*fixture.domain, fixture.dofs, shells.Value(), value, chart.Value(),
|
||||
zero, zero);
|
||||
};
|
||||
const auto parameters = fesa::MakeHhtParameters(-0.05);
|
||||
ASSERT_TRUE(parameters.HasValue());
|
||||
fesa::SerialParallelFor parallel;
|
||||
for (const double bad_time : {std::numeric_limits<double>::quiet_NaN(),
|
||||
std::numeric_limits<double>::infinity(),
|
||||
-std::numeric_limits<double>::infinity()}) {
|
||||
auto candidate = assembled.Value();
|
||||
candidate.frame.time = bad_time;
|
||||
candidate.trial_constraints->time = bad_time;
|
||||
candidate.trial_constraints->shells[0U].drilling_constraint[0U] = 17.0;
|
||||
auto fresh = candidate;
|
||||
fresh.trial_constraints.reset();
|
||||
const auto reference = evaluate(fresh);
|
||||
const auto actual = evaluate(candidate);
|
||||
ASSERT_TRUE(reference.HasValue());
|
||||
ASSERT_TRUE(actual.HasValue());
|
||||
ExpectSameOperators(actual.Value(), reference.Value());
|
||||
const auto producer = fesa::hht_internal::EvaluateAssembly(
|
||||
*fixture.domain, fixture.dofs, shells.Value(), candidate.frame, zero,
|
||||
0.1, parameters.Value(), parallel, true);
|
||||
ASSERT_TRUE(producer.HasValue());
|
||||
EXPECT_FALSE(producer.Value().trial_constraints.has_value());
|
||||
const auto producer_result = evaluate(producer.Value());
|
||||
ASSERT_TRUE(producer_result.HasValue());
|
||||
ExpectSameOperators(producer_result.Value(), reference.Value());
|
||||
}
|
||||
}
|
||||
|
||||
TEST(HhtReducedOperators, ScatterShellDerivativesByConstraintRow) {
|
||||
const auto fixture = MakeFixture(false);
|
||||
const auto shells = fesa::hht_internal::BuildRuntimeShells(*fixture.domain,
|
||||
|
||||
@@ -11,6 +11,39 @@
|
||||
namespace fesa::dense_blas_internal {
|
||||
namespace {
|
||||
|
||||
TEST(DenseBlasInternal, PolicyQueryPreservesDefaultPositiveAndNestedTls) {
|
||||
const int previous = mkl_set_num_threads_local(0);
|
||||
struct Restore {
|
||||
int previous;
|
||||
~Restore() { static_cast<void>(mkl_set_num_threads_local(previous)); }
|
||||
} restore{previous};
|
||||
const int global = mkl_get_max_threads();
|
||||
const int domain = mkl_domain_get_max_threads(MKL_DOMAIN_BLAS);
|
||||
const int dynamic = mkl_get_dynamic();
|
||||
for (int repeat = 0; repeat < 2; ++repeat) {
|
||||
const auto policy = ReadScopedLocalDensePolicy();
|
||||
EXPECT_EQ(policy.blas_max_threads, 1);
|
||||
EXPECT_EQ(policy.dynamic, dynamic);
|
||||
EXPECT_EQ(mkl_set_num_threads_local(0), 0);
|
||||
EXPECT_EQ(mkl_get_max_threads(), global);
|
||||
EXPECT_EQ(mkl_domain_get_max_threads(MKL_DOMAIN_BLAS), domain);
|
||||
EXPECT_EQ(mkl_get_dynamic(), dynamic);
|
||||
}
|
||||
static_cast<void>(mkl_set_num_threads_local(2));
|
||||
EXPECT_EQ(ReadScopedLocalDensePolicy().blas_max_threads, 2);
|
||||
EXPECT_EQ(mkl_set_num_threads_local(2), 2);
|
||||
static_cast<void>(mkl_set_num_threads_local(0));
|
||||
try {
|
||||
const ScopedLocalDenseThreads outer{};
|
||||
EXPECT_EQ(ReadScopedLocalDensePolicy().blas_max_threads, 1);
|
||||
EXPECT_EQ(mkl_set_num_threads_local(1), 1);
|
||||
throw std::runtime_error{"policy-query-unwind"};
|
||||
} catch (const std::runtime_error&) {
|
||||
}
|
||||
EXPECT_EQ(mkl_set_num_threads_local(0), 0);
|
||||
EXPECT_EQ(mkl_get_dynamic(), dynamic);
|
||||
}
|
||||
|
||||
TEST(DenseBlasInternal,
|
||||
OuterProductUsesRectangularStorageAndPreservesRejection) {
|
||||
const std::array<double, 2U> left{2.0, -3.0};
|
||||
|
||||
Reference in New Issue
Block a user