perf(hex8): defer optional paired tangent until a valid linear trial
This commit is contained in:
@@ -3,10 +3,13 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <cmath>
|
||||
#include <cfenv>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <future>
|
||||
#include <limits>
|
||||
#include <random>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "fesa/materials/isotropic_linear_elastic_material.h"
|
||||
@@ -93,6 +96,112 @@ fesa::Hex8Element UnitBrick(double offset = 0, double nu = 0) {
|
||||
return fesa::Hex8Element::Create(0, layout, coordinates, material, 2).Value();
|
||||
}
|
||||
|
||||
void ExpectSamePairResponse(const fesa::Hex8HighLowResponse& actual,
|
||||
const fesa::Hex8HighLowResponse& expected) {
|
||||
EXPECT_EQ(PairBits(actual.high.strain_energy),
|
||||
PairBits(expected.high.strain_energy));
|
||||
EXPECT_EQ(PairBits(actual.strain_energy_low),
|
||||
PairBits(expected.strain_energy_low));
|
||||
ASSERT_EQ(actual.high.tangent.Rows(), 24U);
|
||||
ASSERT_EQ(actual.tangent_low.Rows(), 24U);
|
||||
for (std::size_t i = 0; i < 24; ++i) {
|
||||
EXPECT_EQ(PairBits(actual.high.internal_force[i]),
|
||||
PairBits(expected.high.internal_force[i]));
|
||||
EXPECT_EQ(PairBits(actual.internal_force_low[i]),
|
||||
PairBits(expected.internal_force_low[i]));
|
||||
for (std::size_t j = 0; j < 24; ++j) {
|
||||
EXPECT_EQ(PairBits(actual.high.tangent(i, j)),
|
||||
PairBits(expected.high.tangent(i, j)));
|
||||
EXPECT_EQ(PairBits(actual.tangent_low(i, j)),
|
||||
PairBits(expected.tangent_low(i, j)));
|
||||
}
|
||||
}
|
||||
for (std::size_t point = 0; point < 8; ++point) {
|
||||
EXPECT_EQ(actual.high.points[point].integration_point,
|
||||
expected.high.points[point].integration_point);
|
||||
for (std::size_t i = 0; i < 6; ++i) {
|
||||
EXPECT_EQ(PairBits(actual.high.points[point].cauchy_stress[i]),
|
||||
PairBits(expected.high.points[point].cauchy_stress[i]));
|
||||
EXPECT_EQ(PairBits(actual.high.points[point].strain[i]),
|
||||
PairBits(expected.high.points[point].strain[i]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Hex8HighLow, ConcurrentFirstLinearTrialsAcrossCopiesAndMoveKeepBits) {
|
||||
const auto element = UnitBrick();
|
||||
auto copied = element;
|
||||
const auto moved = std::move(copied);
|
||||
fesa::Vector high(24), low_a(24), low_b(24);
|
||||
for (std::size_t i = 0; i < 24; ++i) high[i] = 1;
|
||||
for (auto a : {1, 2, 5, 6}) {
|
||||
low_a[3U * a] = std::ldexp(1.0, -60);
|
||||
low_b[3U * a] = -std::ldexp(1.0, -59);
|
||||
}
|
||||
const auto oracle = UnitBrick();
|
||||
const auto expected_a = oracle.EvaluateLinearHighLow(high, low_a);
|
||||
const auto expected_b = oracle.EvaluateLinearHighLow(high, low_b);
|
||||
ASSERT_TRUE(expected_a.HasValue());
|
||||
ASSERT_TRUE(expected_b.HasValue());
|
||||
std::promise<void> start;
|
||||
const auto signal = start.get_future().share();
|
||||
std::array<std::future<fesa::Result<fesa::Hex8HighLowResponse>>, 4> calls;
|
||||
for (std::size_t i = 0; i < calls.size(); ++i)
|
||||
calls[i] = std::async(std::launch::async, [&, i] {
|
||||
signal.wait();
|
||||
return (i % 2U == 0U ? element : moved)
|
||||
.EvaluateLinearHighLow(high, i < 2U ? low_a : low_b);
|
||||
});
|
||||
start.set_value();
|
||||
for (std::size_t i = 0; i < calls.size(); ++i) {
|
||||
const auto response = calls[i].get();
|
||||
ASSERT_TRUE(response.HasValue());
|
||||
ExpectSamePairResponse(response.Value(),
|
||||
i < 2U ? expected_a.Value() : expected_b.Value());
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Hex8HighLow, FirstLinearPreparationRecoversAfterTrialAndEnvironmentFailure) {
|
||||
ASSERT_TRUE(fesa::math_internal::ArithmeticEnvironmentSupported());
|
||||
const auto element = UnitBrick();
|
||||
fesa::Vector high(24), low(24);
|
||||
EXPECT_FALSE(element.EvaluateLinearHighLow(fesa::Vector(23), low).HasValue());
|
||||
high[0] = std::numeric_limits<double>::infinity();
|
||||
EXPECT_FALSE(element.EvaluateLinearHighLow(high, low).HasValue());
|
||||
high[0] = 0;
|
||||
struct RestoreEnvironment {
|
||||
int rounding = std::fegetround();
|
||||
#if defined(_M_X64) || defined(__SSE2__)
|
||||
unsigned csr = _mm_getcsr();
|
||||
#endif
|
||||
~RestoreEnvironment() {
|
||||
std::fesetround(rounding);
|
||||
#if defined(_M_X64) || defined(__SSE2__)
|
||||
_mm_setcsr(csr);
|
||||
#endif
|
||||
}
|
||||
} restore;
|
||||
ASSERT_EQ(std::fesetround(FE_DOWNWARD), 0);
|
||||
const auto bad_environment = element.EvaluateLinearHighLow(high, low);
|
||||
ASSERT_FALSE(bad_environment.HasValue());
|
||||
EXPECT_EQ(bad_environment.GetStatus().Diagnostics()[0].message,
|
||||
"Hex8 high+low requires nearest rounding and gradual underflow.");
|
||||
ASSERT_EQ(std::fesetround(FE_TONEAREST), 0);
|
||||
#if defined(_M_X64) || defined(__SSE2__)
|
||||
_mm_setcsr(restore.csr | 0x8040U);
|
||||
EXPECT_FALSE(element.EvaluateLinearHighLow(high, low).HasValue());
|
||||
_mm_setcsr(restore.csr);
|
||||
#endif
|
||||
const auto recovered = element.EvaluateLinearHighLow(high, low);
|
||||
ASSERT_TRUE(recovered.HasValue());
|
||||
high[3] = -2;
|
||||
EXPECT_FALSE(element.EvaluateLinearHighLow(high, low).HasValue());
|
||||
high[3] = 0;
|
||||
const auto after_failure = element.EvaluateLinearHighLow(high, low);
|
||||
ASSERT_TRUE(after_failure.HasValue());
|
||||
ExpectSamePairResponse(after_failure.Value(), recovered.Value());
|
||||
}
|
||||
|
||||
TEST(Hex8HighLow, LowOnlyAffineStrainSurvivesUniformHighTranslation) {
|
||||
auto brick = UnitBrick();
|
||||
fesa::Vector high(24), low(24);
|
||||
|
||||
Reference in New Issue
Block a user