Integrate Refactor solver sources and dual MSVC CMake builds
This commit is contained in:
@@ -1,9 +1,20 @@
|
||||
#include "fesa/analysis/analysis.h"
|
||||
#include "fesa/analysis/analysis_procedure.h"
|
||||
#include "fesa/analysis/prepared_analysis.h"
|
||||
#include "analysis/equilibrium_algorithm.h"
|
||||
#include "analysis/hht_reduced_integrator.h"
|
||||
#include "analysis/hht_reduced_procedure.h"
|
||||
#include "analysis/solid_hht_integrator.h"
|
||||
#include "analysis/solid_hht_core.h"
|
||||
#include "analysis/thermal_transient_algebra.h"
|
||||
#include "fesa/solvers/linear/tangent_solver.h"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <filesystem>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
@@ -65,3 +76,555 @@ TEST(Analysis, PropagatesStatusAndDestroysThroughMinimalAnalysis) {
|
||||
analysis.reset();
|
||||
EXPECT_TRUE(destroyed);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
using ProcedureSignature = fesa::Result<fesa::PreparedAnalysisOutcome> (
|
||||
fesa::AnalysisProcedure::*)(const fesa::Domain &);
|
||||
static_assert(std::has_virtual_destructor_v<fesa::AnalysisProcedure>);
|
||||
static_assert(std::is_abstract_v<fesa::AnalysisProcedure>);
|
||||
static_assert(std::is_same_v<decltype(&fesa::AnalysisProcedure::Execute),
|
||||
ProcedureSignature>);
|
||||
|
||||
class ObservedProcedure final : public fesa::AnalysisProcedure {
|
||||
public:
|
||||
ObservedProcedure(const fesa::Domain *&observed, bool &destroyed)
|
||||
: observed_(observed), destroyed_(destroyed) {}
|
||||
~ObservedProcedure() override { destroyed_ = true; }
|
||||
fesa::Result<fesa::PreparedAnalysisOutcome>
|
||||
Execute(const fesa::Domain &domain) override {
|
||||
observed_ = &domain;
|
||||
return fesa::Result<fesa::PreparedAnalysisOutcome>::Failure(
|
||||
fesa::Status::Failure(fesa::FailureCategory::kSolver,
|
||||
{{fesa::Severity::kError,
|
||||
"procedure-original-failure",
|
||||
{},
|
||||
"ORIGINAL",
|
||||
"source-identity",
|
||||
"Original failure message."}}));
|
||||
}
|
||||
|
||||
private:
|
||||
const fesa::Domain *&observed_;
|
||||
bool &destroyed_;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST(AnalysisProcedure, PreparedConsumerPreservesDomainFailureAndLifetime) {
|
||||
const auto domain_result = fesa::Domain::Create({});
|
||||
ASSERT_TRUE(domain_result.HasValue());
|
||||
const auto &domain = domain_result.Value();
|
||||
const fesa::Domain *observed = nullptr;
|
||||
bool destroyed = false;
|
||||
std::unique_ptr<fesa::AnalysisProcedure> procedure =
|
||||
std::make_unique<ObservedProcedure>(observed, destroyed);
|
||||
auto outcome = fesa::ExecutePreparedAnalysis(domain, *procedure);
|
||||
EXPECT_EQ(observed, &domain);
|
||||
ASSERT_FALSE(outcome.HasValue());
|
||||
EXPECT_EQ(outcome.GetStatus().Category(), fesa::FailureCategory::kSolver);
|
||||
ASSERT_EQ(outcome.GetStatus().Diagnostics().size(), 1U);
|
||||
const auto &diagnostic = outcome.GetStatus().Diagnostics()[0];
|
||||
EXPECT_EQ(diagnostic.code, "procedure-original-failure");
|
||||
EXPECT_EQ(diagnostic.keyword, "ORIGINAL");
|
||||
EXPECT_EQ(diagnostic.entity_identity, "source-identity");
|
||||
EXPECT_EQ(diagnostic.message, "Original failure message.");
|
||||
EXPECT_FALSE(destroyed);
|
||||
procedure.reset();
|
||||
EXPECT_TRUE(destroyed);
|
||||
}
|
||||
|
||||
TEST(EquilibriumAlgorithm, RetainsMoveOnlyCandidateUntilProcedureTakesIt) {
|
||||
static_assert(std::has_virtual_destructor_v<fesa::EquilibriumAlgorithm>);
|
||||
static_assert(std::is_abstract_v<fesa::EquilibriumAlgorithm>);
|
||||
using Candidate = std::unique_ptr<int>;
|
||||
using EvaluationResult = fesa::Result<std::optional<Candidate>>;
|
||||
std::vector<std::uint32_t> observed;
|
||||
fesa::EquilibriumStep<Candidate, 1U> step(
|
||||
[&](std::uint32_t iteration) {
|
||||
observed.push_back(iteration);
|
||||
if (iteration == 3U)
|
||||
return EvaluationResult::Success(std::make_unique<int>(42));
|
||||
return EvaluationResult::Success(std::nullopt);
|
||||
}, 100U);
|
||||
fesa::NewtonStepAlgorithm algorithm;
|
||||
fesa::EquilibriumAlgorithm& base = algorithm;
|
||||
EXPECT_FALSE(step.HasCandidate());
|
||||
ASSERT_TRUE(step.Solve(base).IsOk());
|
||||
EXPECT_EQ(observed, (std::vector<std::uint32_t>{1U, 2U, 3U}));
|
||||
ASSERT_TRUE(step.HasCandidate());
|
||||
Candidate accepted = step.TakeCandidate();
|
||||
ASSERT_NE(accepted, nullptr);
|
||||
EXPECT_EQ(*accepted, 42);
|
||||
}
|
||||
|
||||
TEST(EquilibriumAlgorithm, FirstFailedEvaluationLeavesNoAcceptedCandidate) {
|
||||
using EvaluationResult = fesa::Result<std::optional<int>>;
|
||||
std::vector<std::uint32_t> observed;
|
||||
fesa::EquilibriumStep<int, 1U> step(
|
||||
[&](std::uint32_t iteration) {
|
||||
observed.push_back(iteration);
|
||||
return EvaluationResult::Failure(fesa::Status::Failure(
|
||||
fesa::FailureCategory::kSolver,
|
||||
{{fesa::Severity::kError, "first-evaluation-failure", {},
|
||||
"STATIC", "original-owner", "Original evaluation failure."}}));
|
||||
}, 100U);
|
||||
fesa::NewtonStepAlgorithm algorithm;
|
||||
fesa::EquilibriumAlgorithm& base = algorithm;
|
||||
const auto status = step.Solve(base);
|
||||
EXPECT_EQ(observed, (std::vector<std::uint32_t>{1U}));
|
||||
EXPECT_FALSE(step.HasCandidate());
|
||||
ASSERT_FALSE(status.IsOk());
|
||||
ASSERT_EQ(status.Diagnostics().size(), 1U);
|
||||
EXPECT_EQ(status.Diagnostics()[0].code, "first-evaluation-failure");
|
||||
EXPECT_EQ(status.Diagnostics()[0].entity_identity, "original-owner");
|
||||
EXPECT_EQ(status.Diagnostics()[0].message, "Original evaluation failure.");
|
||||
}
|
||||
|
||||
namespace {
|
||||
// These helpers route supplied values only. They never call a concrete time
|
||||
// integrator, Domain/assembly/time-grid builder, analysis or numerical solver.
|
||||
fesa::Status InjectionFailure() {
|
||||
return fesa::Status::Failure(fesa::FailureCategory::kSolver,
|
||||
{{fesa::Severity::kError, "injected-policy-failure", {}, "MOCK",
|
||||
"caller-owned", "Original injected failure."}});
|
||||
}
|
||||
void ExpectInjectionFailure(const fesa::Status& status) {
|
||||
ASSERT_FALSE(status.IsOk());
|
||||
EXPECT_EQ(status.Category(), fesa::FailureCategory::kSolver);
|
||||
ASSERT_EQ(status.Diagnostics().size(), 1U);
|
||||
const auto& diagnostic = status.Diagnostics()[0];
|
||||
EXPECT_EQ(diagnostic.code, "injected-policy-failure");
|
||||
EXPECT_EQ(diagnostic.keyword, "MOCK");
|
||||
EXPECT_EQ(diagnostic.entity_identity, "caller-owned");
|
||||
EXPECT_EQ(diagnostic.message, "Original injected failure.");
|
||||
}
|
||||
void ExpectValueVector(const fesa::Vector& value, double expected) {
|
||||
ASSERT_EQ(value.Size(), 2U);
|
||||
for (std::size_t i = 0; i < value.Size(); ++i)
|
||||
EXPECT_DOUBLE_EQ(value[i], expected);
|
||||
}
|
||||
class NeverCalledLinear final : public fesa::LinearSolver {
|
||||
public:
|
||||
fesa::Status Factorize(const fesa::SparseMatrix&) override {
|
||||
ADD_FAILURE() << "Mock dispatch must not factorize.";
|
||||
return InjectionFailure();
|
||||
}
|
||||
fesa::Status Solve(const fesa::Vector&, fesa::Vector&) const override {
|
||||
ADD_FAILURE() << "Mock dispatch must not solve.";
|
||||
return InjectionFailure();
|
||||
}
|
||||
};
|
||||
class NeverCalledTangent final : public fesa::TangentSolver {
|
||||
public:
|
||||
fesa::Status FactorizeFresh(const fesa::SparseMatrix&) override {
|
||||
ADD_FAILURE() << "Mock dispatch must not factorize.";
|
||||
return InjectionFailure();
|
||||
}
|
||||
fesa::Status SolveCorrection(const fesa::Vector&, fesa::Vector&) const override {
|
||||
ADD_FAILURE() << "Mock dispatch must not solve.";
|
||||
return InjectionFailure();
|
||||
}
|
||||
};
|
||||
class SelectedOrderAlgorithm final : public fesa::EquilibriumAlgorithm {
|
||||
public:
|
||||
explicit SelectedOrderAlgorithm(bool& destroyed) : destroyed_(destroyed) {}
|
||||
~SelectedOrderAlgorithm() override { destroyed_ = true; }
|
||||
bool fail = false;
|
||||
fesa::Status SolveCurrentStep(std::uint32_t first, std::uint32_t last,
|
||||
const Evaluation& evaluate) override {
|
||||
EXPECT_EQ(first, 0U);
|
||||
EXPECT_EQ(last, 3U);
|
||||
if (fail) return InjectionFailure();
|
||||
auto first_result = evaluate(first);
|
||||
if (!first_result.HasValue()) return first_result.GetStatus();
|
||||
EXPECT_FALSE(first_result.Value());
|
||||
auto last_result = evaluate(last);
|
||||
if (!last_result.HasValue()) return last_result.GetStatus();
|
||||
EXPECT_TRUE(last_result.Value());
|
||||
return fesa::Status::Ok();
|
||||
}
|
||||
private:
|
||||
bool& destroyed_;
|
||||
};
|
||||
namespace shell_value = fesa::hht_reduced_internal;
|
||||
class ValueShellIntegrator final : public shell_value::ShellHhtIntegrator {
|
||||
public:
|
||||
ValueShellIntegrator(bool& destroyed, std::vector<std::string>& events)
|
||||
: destroyed_(destroyed), events_(events) {}
|
||||
~ValueShellIntegrator() override { destroyed_ = true; }
|
||||
std::function<shell_value::ReducedShellPredictor(
|
||||
const shell_value::ReducedHistory&, double, const fesa::HhtParameters&)> predict;
|
||||
std::function<void(const shell_value::ReducedShellRateRequest&,
|
||||
fesa::Vector&, fesa::Vector&)> rates;
|
||||
std::function<fesa::Result<shell_value::ReconstructedRates>(
|
||||
const shell_value::FixedChart&, const shell_value::EmbeddingRoot&,
|
||||
const fesa::Vector&, const fesa::Vector&, fesa::TangentSolver&)> reconstruct;
|
||||
shell_value::ReducedShellPredictor Predict(
|
||||
const shell_value::ReducedHistory& history, double h,
|
||||
const fesa::HhtParameters& parameters) const override {
|
||||
events_.push_back("predict");
|
||||
return predict(history, h, parameters);
|
||||
}
|
||||
void EvaluateRates(const shell_value::ReducedShellRateRequest& request,
|
||||
fesa::Vector& velocity, fesa::Vector& acceleration) const override {
|
||||
events_.push_back("rates");
|
||||
rates(request, velocity, acceleration);
|
||||
}
|
||||
fesa::Result<shell_value::ReconstructedRates> ReconstructPhysicalRates(
|
||||
const shell_value::FixedChart& chart, const shell_value::EmbeddingRoot& root,
|
||||
const fesa::Vector& velocity, const fesa::Vector& acceleration,
|
||||
fesa::TangentSolver& solver) const override {
|
||||
events_.push_back("reconstruct");
|
||||
return reconstruct(chart, root, velocity, acceleration, solver);
|
||||
}
|
||||
private:
|
||||
bool& destroyed_;
|
||||
std::vector<std::string>& events_;
|
||||
};
|
||||
class ValueSolidIntegrator final : public fesa::detail::SolidHhtIntegrator {
|
||||
public:
|
||||
ValueSolidIntegrator(bool& destroyed, std::vector<std::string>& events)
|
||||
: destroyed_(destroyed), events_(events) {}
|
||||
~ValueSolidIntegrator() override { destroyed_ = true; }
|
||||
std::function<fesa::Result<fesa::detail::SolidHhtPairedPredictor>(
|
||||
const fesa::SolidDynamicAcceptedFrameState&, double,
|
||||
const fesa::HhtParameters&)> predict;
|
||||
std::function<fesa::Result<fesa::detail::SolidHhtPairedRates>(
|
||||
const fesa::Vector&, const fesa::Vector&,
|
||||
const fesa::detail::SolidHhtPairedPredictor&, const fesa::Vector&,
|
||||
const fesa::Vector&, double, const fesa::HhtParameters&)> rates;
|
||||
fesa::Result<fesa::detail::SolidHhtPairedPredictor> Predict(
|
||||
const fesa::SolidDynamicAcceptedFrameState& old, double h,
|
||||
const fesa::HhtParameters& parameters) const override {
|
||||
events_.push_back("predict");
|
||||
return predict(old, h, parameters);
|
||||
}
|
||||
fesa::Result<fesa::detail::SolidHhtPairedRates> EvaluateRates(
|
||||
const fesa::Vector& high, const fesa::Vector& low,
|
||||
const fesa::detail::SolidHhtPairedPredictor& predictor,
|
||||
const fesa::Vector& old_acceleration,
|
||||
const fesa::Vector& old_acceleration_low, double h,
|
||||
const fesa::HhtParameters& parameters) const override {
|
||||
events_.push_back("rates");
|
||||
return rates(high, low, predictor, old_acceleration, old_acceleration_low,
|
||||
h, parameters);
|
||||
}
|
||||
private:
|
||||
bool& destroyed_;
|
||||
std::vector<std::string>& events_;
|
||||
};
|
||||
namespace heat_value = fesa::thermal_transient_internal;
|
||||
class ValueHeatIntegrator final : public heat_value::HeatStepIntegrator {
|
||||
public:
|
||||
ValueHeatIntegrator(bool& destroyed, std::vector<std::string>& events)
|
||||
: destroyed_(destroyed), events_(events) {}
|
||||
~ValueHeatIntegrator() override { destroyed_ = true; }
|
||||
std::function<fesa::Result<heat_value::ThermalStepCandidate>(
|
||||
const heat_value::ThermalSystem&, const heat_value::ThermalPartition&,
|
||||
const fesa::Vector&, double, fesa::LinearSolver&,
|
||||
heat_value::ThermalFactorization&)> solve;
|
||||
fesa::Result<heat_value::ThermalStepCandidate> SolveIncrement(
|
||||
const heat_value::ThermalSystem& system,
|
||||
const heat_value::ThermalPartition& partition,
|
||||
const fesa::Vector& old, double delta, fesa::LinearSolver& solver,
|
||||
heat_value::ThermalFactorization& cache) const override {
|
||||
events_.push_back("solve");
|
||||
return solve(system, partition, old, delta, solver, cache);
|
||||
}
|
||||
private:
|
||||
bool& destroyed_;
|
||||
std::vector<std::string>& events_;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
TEST(EquilibriumAlgorithm, InjectsAlternativeOrderAndPreservesTypedPendingOnFailure) {
|
||||
bool destroyed = false;
|
||||
auto owned = std::make_unique<SelectedOrderAlgorithm>(destroyed);
|
||||
auto* selected = owned.get();
|
||||
std::unique_ptr<fesa::EquilibriumAlgorithm> policy = std::move(owned);
|
||||
std::vector<std::uint32_t> observed;
|
||||
using Candidate = std::unique_ptr<int>;
|
||||
using EvaluationResult = fesa::Result<std::optional<Candidate>>;
|
||||
fesa::EquilibriumStep<Candidate, 0U> step(
|
||||
[&](std::uint32_t iteration) {
|
||||
observed.push_back(iteration);
|
||||
return iteration == 3U
|
||||
? EvaluationResult::Success(std::make_unique<int>(42))
|
||||
: EvaluationResult::Success(std::nullopt);
|
||||
}, 3U);
|
||||
ASSERT_TRUE(step.Solve(*policy).IsOk());
|
||||
EXPECT_EQ(observed, (std::vector<std::uint32_t>{0U, 3U}));
|
||||
selected->fail = true;
|
||||
ExpectInjectionFailure(step.Solve(*policy));
|
||||
EXPECT_EQ(observed, (std::vector<std::uint32_t>{0U, 3U}));
|
||||
ASSERT_TRUE(step.HasCandidate());
|
||||
EXPECT_EQ(*step.TakeCandidate(), 42);
|
||||
EXPECT_FALSE(destroyed);
|
||||
policy.reset();
|
||||
EXPECT_TRUE(destroyed);
|
||||
// The default algorithm still evaluates zero through the inclusive maximum.
|
||||
std::vector<std::uint32_t> zero_observed;
|
||||
fesa::NewtonStepAlgorithm zero_algorithm;
|
||||
fesa::EquilibriumStep<int, 0U> zero_step([&](std::uint32_t iteration) {
|
||||
zero_observed.push_back(iteration);
|
||||
return iteration == 2U ? fesa::Result<std::optional<int>>::Success(7)
|
||||
: fesa::Result<std::optional<int>>::Success(std::nullopt);
|
||||
}, 2U);
|
||||
ASSERT_TRUE(zero_step.Solve(zero_algorithm).IsOk());
|
||||
EXPECT_EQ(zero_observed, (std::vector<std::uint32_t>{0U, 1U, 2U}));
|
||||
ASSERT_TRUE(zero_step.HasCandidate());
|
||||
EXPECT_EQ(zero_step.TakeCandidate(), 7);
|
||||
}
|
||||
|
||||
TEST(ProcedureIntegratorInjection, ShellValuesUseProductionStepAndPreserveHistory) {
|
||||
bool destroyed = false;
|
||||
std::vector<std::string> events;
|
||||
auto owned = std::make_unique<ValueShellIntegrator>(destroyed, events);
|
||||
auto* fake = owned.get();
|
||||
std::unique_ptr<shell_value::ShellHhtIntegrator> integrator = std::move(owned);
|
||||
shell_value::ReducedHistory old{fesa::Vector(2, 1), fesa::Vector(2, 2),
|
||||
fesa::Vector(2, 3), fesa::Vector(2, 4), fesa::Vector(2, 5)};
|
||||
shell_value::FixedChart chart;
|
||||
shell_value::EmbeddingRoot root;
|
||||
fesa::Vector algorithmic(2, 6), retained_v(2, 7), retained_a(2, 8);
|
||||
const fesa::HhtParameters parameters{11, 12, 13};
|
||||
NeverCalledTangent solver;
|
||||
bool fail_reconstruction = false, fail_commit = false;
|
||||
fake->predict = [&](const auto& history, double h, const auto& p) {
|
||||
EXPECT_EQ(&history, &old); EXPECT_EQ(&p, ¶meters); EXPECT_DOUBLE_EQ(h, 17);
|
||||
return shell_value::ReducedShellPredictor{fesa::Vector(2, 21), fesa::Vector(2, 22),
|
||||
fesa::Vector(2, 23), fesa::Vector(2, 24), fesa::Vector(2, 25), fesa::Vector(2, 26)};
|
||||
};
|
||||
fake->rates = [&](const auto& request, auto& velocity, auto& acceleration) {
|
||||
EXPECT_EQ(&request.history, &old); EXPECT_EQ(&request.chart, &chart);
|
||||
EXPECT_EQ(&request.trial_algorithmic, &algorithmic);
|
||||
EXPECT_EQ(&request.retained_velocity, &retained_v);
|
||||
EXPECT_EQ(&request.retained_acceleration, &retained_a);
|
||||
EXPECT_EQ(&request.parameters, ¶meters); EXPECT_DOUBLE_EQ(request.h, 17);
|
||||
EXPECT_FALSE(request.copy_translation_rates); ExpectValueVector(request.vpp, 22);
|
||||
EXPECT_NE(velocity.Data(), acceleration.Data());
|
||||
EXPECT_NE(velocity.Data(), request.vpp.Data());
|
||||
EXPECT_NE(acceleration.Data(), request.trial_algorithmic.Data());
|
||||
ExpectValueVector(velocity, 0); ExpectValueVector(acceleration, 0);
|
||||
for (std::size_t i = 0; i < 2; ++i) { velocity[i] = 31; acceleration[i] = 32; }
|
||||
};
|
||||
fake->reconstruct = [&](const auto& c, const auto& r, const auto& v,
|
||||
const auto& a, auto& backend) {
|
||||
EXPECT_EQ(&c, &chart); EXPECT_EQ(&r, &root); EXPECT_EQ(&backend, &solver);
|
||||
ExpectValueVector(v, 31); ExpectValueVector(a, 32);
|
||||
if (fail_reconstruction)
|
||||
return fesa::Result<shell_value::ReconstructedRates>::Failure(InjectionFailure());
|
||||
return fesa::Result<shell_value::ReconstructedRates>::Success({
|
||||
fesa::Vector(2, 41), fesa::Vector(2, 42), fesa::Vector(2, 43), fesa::Vector(2, 44)});
|
||||
};
|
||||
std::vector<shell_value::ReconstructedRates> history;
|
||||
const auto evaluate = [&](const auto& selected, auto predictor) {
|
||||
ExpectValueVector(predictor.pp, 21); ExpectValueVector(predictor.vpp, 22);
|
||||
ExpectValueVector(predictor.coordinate_low, 23);
|
||||
ExpectValueVector(predictor.increment_trace, 24);
|
||||
ExpectValueVector(predictor.increment_trace_compensation, 25);
|
||||
ExpectValueVector(predictor.increment_trace_absolute_sum, 26);
|
||||
shell_value::ReducedShellTrialRates rates{fesa::Vector(2), fesa::Vector(2)};
|
||||
auto* v_data = rates.velocity.Data(); auto* a_data = rates.acceleration.Data();
|
||||
selected.EvaluateRates({chart, old, algorithmic, predictor.vpp, false,
|
||||
retained_v, retained_a, 17, parameters}, rates.velocity, rates.acceleration);
|
||||
EXPECT_EQ(v_data, rates.velocity.Data()); EXPECT_EQ(a_data, rates.acceleration.Data());
|
||||
return selected.ReconstructPhysicalRates(chart, root, rates.velocity,
|
||||
rates.acceleration, solver);
|
||||
};
|
||||
const auto commit = [&](shell_value::ReconstructedRates&& pending) {
|
||||
events.push_back("commit");
|
||||
ExpectValueVector(pending.velocity, 41); ExpectValueVector(pending.acceleration, 42);
|
||||
ExpectValueVector(pending.z_velocity, 43); ExpectValueVector(pending.z_acceleration, 44);
|
||||
if (fail_commit) return InjectionFailure();
|
||||
history.push_back(std::move(pending));
|
||||
return fesa::Status::Ok();
|
||||
};
|
||||
ASSERT_TRUE(shell_value::RunReducedHhtStep(*integrator, old, 17, parameters,
|
||||
evaluate, commit).IsOk());
|
||||
EXPECT_EQ(events, (std::vector<std::string>{"predict", "rates", "reconstruct", "commit"}));
|
||||
ASSERT_EQ(history.size(), 1U);
|
||||
events.clear(); fail_reconstruction = true;
|
||||
ExpectInjectionFailure(shell_value::RunReducedHhtStep(*integrator, old, 17,
|
||||
parameters, evaluate, commit));
|
||||
EXPECT_EQ(events, (std::vector<std::string>{"predict", "rates", "reconstruct"}));
|
||||
EXPECT_EQ(history.size(), 1U);
|
||||
events.clear(); fail_reconstruction = false; fail_commit = true;
|
||||
ExpectInjectionFailure(shell_value::RunReducedHhtStep(*integrator, old, 17,
|
||||
parameters, evaluate, commit));
|
||||
EXPECT_EQ(events, (std::vector<std::string>{"predict", "rates", "reconstruct", "commit"}));
|
||||
EXPECT_EQ(history.size(), 1U);
|
||||
ExpectValueVector(old.p, 1); ExpectValueVector(old.velocity, 2);
|
||||
ExpectValueVector(old.acceleration, 3); ExpectValueVector(old.algorithmic_acceleration, 4);
|
||||
ExpectValueVector(old.z, 5); ExpectValueVector(history[0].velocity, 41);
|
||||
EXPECT_FALSE(destroyed); integrator.reset(); EXPECT_TRUE(destroyed);
|
||||
}
|
||||
|
||||
TEST(ProcedureIntegratorInjection, SolidPairedValuesUseProductionStepAndPreserveHistory) {
|
||||
bool destroyed = false;
|
||||
std::vector<std::string> events;
|
||||
auto owned = std::make_unique<ValueSolidIntegrator>(destroyed, events);
|
||||
auto* fake = owned.get();
|
||||
std::unique_ptr<fesa::detail::SolidHhtIntegrator> integrator = std::move(owned);
|
||||
fesa::SolidDynamicAcceptedFrameState old;
|
||||
old.displacement = fesa::Vector(2, 1); old.low.displacement = fesa::Vector(2, 2);
|
||||
old.velocity = fesa::Vector(2, 3); old.low.velocity = fesa::Vector(2, 4);
|
||||
old.acceleration = fesa::Vector(2, 5); old.low.acceleration = fesa::Vector(2, 6);
|
||||
old.algorithmic_acceleration = fesa::Vector(2, 7);
|
||||
old.low.algorithmic_acceleration = fesa::Vector(2, 8);
|
||||
const fesa::HhtParameters parameters{11, 12, 13};
|
||||
bool fail_predict = false, fail_rates = false, fail_commit = false;
|
||||
fake->predict = [&](const auto& frame, double h, const auto& p) {
|
||||
EXPECT_EQ(&frame, &old); EXPECT_EQ(&p, ¶meters); EXPECT_DOUBLE_EQ(h, 17);
|
||||
if (fail_predict)
|
||||
return fesa::Result<fesa::detail::SolidHhtPairedPredictor>::Failure(InjectionFailure());
|
||||
return fesa::Result<fesa::detail::SolidHhtPairedPredictor>::Success({
|
||||
fesa::Vector(2, 21), fesa::Vector(2, 22), fesa::Vector(2, 23), fesa::Vector(2, 24)});
|
||||
};
|
||||
fake->rates = [&](const auto& high, const auto& low, const auto& predictor,
|
||||
const auto& old_a, const auto& old_a_low, double h, const auto& p) {
|
||||
ExpectValueVector(high, 1); ExpectValueVector(low, 2);
|
||||
ExpectValueVector(predictor.displacement, 21); ExpectValueVector(predictor.displacement_low, 22);
|
||||
ExpectValueVector(predictor.velocity, 23); ExpectValueVector(predictor.velocity_low, 24);
|
||||
EXPECT_EQ(&old_a, &old.acceleration); EXPECT_EQ(&old_a_low, &old.low.acceleration);
|
||||
EXPECT_EQ(&p, ¶meters); EXPECT_DOUBLE_EQ(h, 17);
|
||||
if (fail_rates)
|
||||
return fesa::Result<fesa::detail::SolidHhtPairedRates>::Failure(InjectionFailure());
|
||||
return fesa::Result<fesa::detail::SolidHhtPairedRates>::Success({
|
||||
fesa::Vector(2, 31), fesa::Vector(2, 32), fesa::Vector(2, 33),
|
||||
fesa::Vector(2, 34), fesa::Vector(2, 35), fesa::Vector(2, 36)});
|
||||
};
|
||||
std::vector<fesa::SolidDynamicAcceptedFrameState> history;
|
||||
const auto failure = [&](const fesa::Status& status) {
|
||||
events.push_back("predict-failure"); return status;
|
||||
};
|
||||
const auto evaluate = [&](const auto& selected, const auto& predictor) {
|
||||
auto pending = old;
|
||||
const auto status = fesa::detail::SetSolidHhtRates(selected, old, predictor,
|
||||
17, parameters, &pending);
|
||||
if (!status.IsOk()) {
|
||||
ExpectValueVector(pending.velocity, 3); ExpectValueVector(pending.low.velocity, 4);
|
||||
ExpectValueVector(pending.acceleration, 5); ExpectValueVector(pending.low.acceleration, 6);
|
||||
ExpectValueVector(pending.algorithmic_acceleration, 7);
|
||||
ExpectValueVector(pending.low.algorithmic_acceleration, 8);
|
||||
return fesa::Result<fesa::SolidDynamicAcceptedFrameState>::Failure(status);
|
||||
}
|
||||
return fesa::Result<fesa::SolidDynamicAcceptedFrameState>::Success(std::move(pending));
|
||||
};
|
||||
const auto commit = [&](fesa::SolidDynamicAcceptedFrameState&& pending) {
|
||||
events.push_back("commit");
|
||||
ExpectValueVector(pending.displacement, 1); ExpectValueVector(pending.low.displacement, 2);
|
||||
ExpectValueVector(pending.velocity, 31); ExpectValueVector(pending.low.velocity, 32);
|
||||
ExpectValueVector(pending.acceleration, 33); ExpectValueVector(pending.low.acceleration, 34);
|
||||
ExpectValueVector(pending.algorithmic_acceleration, 35);
|
||||
ExpectValueVector(pending.low.algorithmic_acceleration, 36);
|
||||
if (fail_commit) return InjectionFailure();
|
||||
history.push_back(std::move(pending)); return fesa::Status::Ok();
|
||||
};
|
||||
ASSERT_TRUE(fesa::detail::RunSolidHhtStep(*integrator, old, 17, parameters,
|
||||
failure, evaluate, commit).IsOk());
|
||||
EXPECT_EQ(events, (std::vector<std::string>{"predict", "rates", "commit"}));
|
||||
ASSERT_EQ(history.size(), 1U);
|
||||
events.clear(); fail_predict = true;
|
||||
ExpectInjectionFailure(fesa::detail::RunSolidHhtStep(*integrator, old, 17,
|
||||
parameters, failure, evaluate, commit));
|
||||
EXPECT_EQ(events, (std::vector<std::string>{"predict", "predict-failure"}));
|
||||
EXPECT_EQ(history.size(), 1U);
|
||||
events.clear(); fail_predict = false; fail_rates = true;
|
||||
ExpectInjectionFailure(fesa::detail::RunSolidHhtStep(*integrator, old, 17,
|
||||
parameters, failure, evaluate, commit));
|
||||
EXPECT_EQ(events, (std::vector<std::string>{"predict", "rates"}));
|
||||
EXPECT_EQ(history.size(), 1U);
|
||||
events.clear(); fail_rates = false; fail_commit = true;
|
||||
ExpectInjectionFailure(fesa::detail::RunSolidHhtStep(*integrator, old, 17,
|
||||
parameters, failure, evaluate, commit));
|
||||
EXPECT_EQ(events, (std::vector<std::string>{"predict", "rates", "commit"}));
|
||||
EXPECT_EQ(history.size(), 1U);
|
||||
ExpectValueVector(old.displacement, 1); ExpectValueVector(old.low.displacement, 2);
|
||||
ExpectValueVector(old.velocity, 3); ExpectValueVector(old.low.velocity, 4);
|
||||
ExpectValueVector(old.acceleration, 5); ExpectValueVector(old.low.acceleration, 6);
|
||||
ExpectValueVector(old.algorithmic_acceleration, 7); ExpectValueVector(old.low.algorithmic_acceleration, 8);
|
||||
ExpectValueVector(history[0].low.algorithmic_acceleration, 36);
|
||||
EXPECT_FALSE(destroyed); integrator.reset(); EXPECT_TRUE(destroyed);
|
||||
}
|
||||
|
||||
TEST(ProcedureIntegratorInjection, HeatValuesUseProductionRecoveryCommitAndPreserveHistory) {
|
||||
bool destroyed = false;
|
||||
std::vector<std::string> events;
|
||||
auto owned = std::make_unique<ValueHeatIntegrator>(destroyed, events);
|
||||
auto* fake = owned.get();
|
||||
std::unique_ptr<heat_value::HeatStepIntegrator> integrator = std::move(owned);
|
||||
auto matrix = fesa::SparseMatrix::FromCoo(
|
||||
2, 2, {}, fesa::SparsePattern{{0, 0, 0}, {}});
|
||||
ASSERT_TRUE(matrix.HasValue());
|
||||
const auto& k = matrix.Value();
|
||||
const fesa::SparseMatrix c{k};
|
||||
const fesa::Vector q(2, 2), old(2, 1);
|
||||
const heat_value::ThermalSystem system{k, c, q};
|
||||
const heat_value::ThermalPartition partition{{0, 1}, {}, fesa::Vector(0)};
|
||||
heat_value::ThermalFactorization cache;
|
||||
NeverCalledLinear solver;
|
||||
bool fail_solve = false, fail_recovery = false, fail_commit = false;
|
||||
fake->solve = [&](const auto& supplied, const auto& p, const auto& previous,
|
||||
double delta, auto& backend, auto& workspace) {
|
||||
EXPECT_EQ(&supplied, &system); EXPECT_EQ(&p, &partition); EXPECT_EQ(&previous, &old);
|
||||
EXPECT_EQ(&backend, &solver); EXPECT_EQ(&workspace, &cache); EXPECT_DOUBLE_EQ(delta, 17);
|
||||
EXPECT_EQ(&supplied.k, &k); EXPECT_EQ(&supplied.c, &c); EXPECT_EQ(&supplied.q, &q);
|
||||
EXPECT_EQ(supplied.k_error, nullptr); EXPECT_EQ(supplied.c_error, nullptr);
|
||||
EXPECT_EQ(supplied.q_error, nullptr); EXPECT_EQ(p.free, (std::vector<std::size_t>{0, 1}));
|
||||
EXPECT_TRUE(p.constrained.empty()); EXPECT_EQ(p.prescribed.Size(), 0U);
|
||||
if (fail_solve)
|
||||
return fesa::Result<heat_value::ThermalStepCandidate>::Failure(InjectionFailure());
|
||||
heat_value::ThermalStepCandidate pending{fesa::Vector(2, 21), fesa::Vector(2, 22),
|
||||
fesa::Vector(2, 23), fesa::Vector(2, 24),
|
||||
{31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, true, 42, 43}};
|
||||
return fesa::Result<heat_value::ThermalStepCandidate>::Success(std::move(pending));
|
||||
};
|
||||
const heat_value::ThermalRecovery recovery = [&](const fesa::Vector& temperature) {
|
||||
events.push_back("recovery"); ExpectValueVector(temperature, 21);
|
||||
if (fail_recovery)
|
||||
return fesa::Result<std::vector<fesa::ThermalElementFlux>>::Failure(InjectionFailure());
|
||||
std::vector<fesa::ThermalElementFlux> flux(1);
|
||||
for (auto& value : flux[0]) value = fesa::Vector3{51, 52, 53};
|
||||
return fesa::Result<std::vector<fesa::ThermalElementFlux>>::Success(std::move(flux));
|
||||
};
|
||||
std::vector<heat_value::ThermalStepCandidate> history;
|
||||
const auto commit = [&](heat_value::ThermalStepCandidate&& pending,
|
||||
std::vector<fesa::ThermalElementFlux>&& flux) {
|
||||
events.push_back("commit");
|
||||
ExpectValueVector(pending.temperature, 21); ExpectValueVector(pending.residual, 22);
|
||||
ExpectValueVector(pending.reaction, 23); ExpectValueVector(pending.certified_residual, 24);
|
||||
const auto& e = pending.evidence;
|
||||
EXPECT_DOUBLE_EQ(e.beta_cert, 31); EXPECT_DOUBLE_EQ(e.beta_limit, 32);
|
||||
EXPECT_DOUBLE_EQ(e.free_residual_cert, 33); EXPECT_DOUBLE_EQ(e.full_free_residual_cert, 34);
|
||||
EXPECT_DOUBLE_EQ(e.full_beta_cert, 35); EXPECT_DOUBLE_EQ(e.operator_construction_error, 36);
|
||||
EXPECT_DOUBLE_EQ(e.rhs_construction_error, 37); EXPECT_DOUBLE_EQ(e.construction_identity_bound, 38);
|
||||
EXPECT_DOUBLE_EQ(e.energy_change, 39); EXPECT_DOUBLE_EQ(e.energy_defect, 40);
|
||||
EXPECT_DOUBLE_EQ(e.energy_bound, 41); EXPECT_TRUE(e.dissipation_checked);
|
||||
EXPECT_DOUBLE_EQ(e.dissipation_defect, 42); EXPECT_DOUBLE_EQ(e.dissipation_bound, 43);
|
||||
EXPECT_EQ(flux.size(), 1U);
|
||||
for (const auto& value : flux[0]) EXPECT_EQ(value.Components(), (std::array<double, 3>{51, 52, 53}));
|
||||
if (fail_commit) return InjectionFailure();
|
||||
history.push_back(std::move(pending)); return fesa::Status::Ok();
|
||||
};
|
||||
ASSERT_TRUE(heat_value::RunThermalStep(*integrator, system, partition, old,
|
||||
17, solver, cache, recovery, commit).IsOk());
|
||||
EXPECT_EQ(events, (std::vector<std::string>{"solve", "recovery", "commit"}));
|
||||
ASSERT_EQ(history.size(), 1U);
|
||||
events.clear(); fail_solve = true;
|
||||
ExpectInjectionFailure(heat_value::RunThermalStep(*integrator, system, partition,
|
||||
old, 17, solver, cache, recovery, commit));
|
||||
EXPECT_EQ(events, (std::vector<std::string>{"solve"})); EXPECT_EQ(history.size(), 1U);
|
||||
events.clear(); fail_solve = false; fail_recovery = true;
|
||||
ExpectInjectionFailure(heat_value::RunThermalStep(*integrator, system, partition,
|
||||
old, 17, solver, cache, recovery, commit));
|
||||
EXPECT_EQ(events, (std::vector<std::string>{"solve", "recovery"})); EXPECT_EQ(history.size(), 1U);
|
||||
events.clear(); fail_recovery = false; fail_commit = true;
|
||||
ExpectInjectionFailure(heat_value::RunThermalStep(*integrator, system, partition,
|
||||
old, 17, solver, cache, recovery, commit));
|
||||
EXPECT_EQ(events, (std::vector<std::string>{"solve", "recovery", "commit"}));
|
||||
EXPECT_EQ(history.size(), 1U); ExpectValueVector(old, 1);
|
||||
ExpectValueVector(history[0].temperature, 21); EXPECT_FALSE(cache.matrix.has_value());
|
||||
EXPECT_FALSE(destroyed); integrator.reset(); EXPECT_TRUE(destroyed);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user