perf(jacobian-action): major updates to jacobian action application by removing redudant quadrature work. ~5x increase in speed
This commit is contained in:
164
tests/operators/prepared_central_density.cpp
Normal file
164
tests/operators/prepared_central_density.cpp
Normal file
@@ -0,0 +1,164 @@
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <concepts>
|
||||
#include <limits>
|
||||
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <mfem.hpp>
|
||||
|
||||
import mean_field;
|
||||
import test_helpers;
|
||||
|
||||
namespace {
|
||||
[[nodiscard]] mean_field::field::FieldPointDofMap make_center_map() {
|
||||
mfem::Array<int> centerDof(1);
|
||||
centerDof[0] = 2;
|
||||
return {5, centerDof};
|
||||
}
|
||||
|
||||
[[nodiscard]] double relative_error(
|
||||
const double actual,
|
||||
const double expected
|
||||
) {
|
||||
return std::abs(actual - expected) / std::max({1.0, std::abs(actual), std::abs(expected)});
|
||||
}
|
||||
} // namespace
|
||||
|
||||
TEST_CASE(
|
||||
"Fixed Central Density Compiles A Carrier Phase Row And Solver Border",
|
||||
tags::model_specification_type_contract
|
||||
) {
|
||||
using namespace mean_field;
|
||||
using Request = models::CentralDensityLayoutRequest;
|
||||
using Form = utils::blocks::central_density_bordered_stellar_equilibrium_form;
|
||||
|
||||
STATIC_CHECK(models::ConstraintLayoutRequestType<Request>);
|
||||
STATIC_CHECK(models::CompiledConstraint<models::CompiledFixedCentralDensity>);
|
||||
STATIC_CHECK(Request::rowInjection == models::ConstraintRowInjection::solver_border);
|
||||
STATIC_CHECK(Request::valueArity == 1);
|
||||
STATIC_CHECK(Request::residualArity == 1);
|
||||
STATIC_CHECK(Request::valueBlock<Form>().index == Form::value_block_count - 1);
|
||||
STATIC_CHECK(Request::residualBlock<Form>().index == Form::residual_block_count - 1);
|
||||
STATIC_CHECK(std::same_as<typename models::CompiledFixedCentralDensity::CarrierField, field::Enthalpy>);
|
||||
STATIC_CHECK(std::same_as<typename models::CompiledFixedCentralDensity::BorderField, field::CentralDensityBorder>);
|
||||
|
||||
const eos::Polytrope equationOfState{3.0, 0.25};
|
||||
const models::CompiledFixedCentralDensity compiled =
|
||||
models::compileConstraint(models::FixedCentralDensity{eos::DensityValue{8.0}}, equationOfState);
|
||||
|
||||
CHECK(compiled.targetDensity() == eos::DensityValue{8.0});
|
||||
CHECK(compiled.targetEnthalpy() == eos::SpecificEnthalpyValue{2.0});
|
||||
CHECK(compiled.densityFromEnthalpy(eos::SpecificEnthalpyValue{2.5}) == eos::DensityValue{15.625});
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Prepared Central Density Phase Has Exact Residual Jacobian And Transpose Actions",
|
||||
tags::central_density_phase_unit
|
||||
) {
|
||||
using namespace mean_field;
|
||||
|
||||
const eos::Polytrope equationOfState{3.0, 0.25};
|
||||
const models::CompiledFixedCentralDensity compiled =
|
||||
models::compileConstraint(models::FixedCentralDensity{eos::DensityValue{8.0}}, equationOfState);
|
||||
operators::PreparedCentralDensityConstraint phase(make_center_map(), MPI_COMM_SELF);
|
||||
|
||||
mfem::Vector enthalpy(5);
|
||||
enthalpy = 0.0;
|
||||
enthalpy(2) = 2.5;
|
||||
const operators::CentralDensityDependencies dependencies{.enthalpy = {.identity = 17, .revision = 1}};
|
||||
|
||||
const operators::PreparedCentralDensityReport initial = phase.Prepare(compiled, enthalpy, 0.3, dependencies);
|
||||
CHECK(initial.refreshedCentralEnthalpy);
|
||||
CHECK(initial.refreshedBorder);
|
||||
CHECK(initial.assembledResidual);
|
||||
|
||||
mfem::Vector carrierResidual(5);
|
||||
mfem::Vector phaseResidual(1);
|
||||
carrierResidual = 1.0;
|
||||
phaseResidual = 0.0;
|
||||
phase.AddResidual(carrierResidual, phaseResidual);
|
||||
CHECK(carrierResidual(2) == 1.3);
|
||||
CHECK(phaseResidual(0) == 0.5);
|
||||
|
||||
mfem::Vector enthalpyVariation(5);
|
||||
enthalpyVariation = 0.0;
|
||||
enthalpyVariation(2) = -0.4;
|
||||
constexpr double borderVariation = 0.7;
|
||||
|
||||
mfem::Vector carrierAction(5);
|
||||
mfem::Vector phaseAction(1);
|
||||
carrierAction = 0.0;
|
||||
phaseAction = 0.0;
|
||||
phase.ApplyJacobian(
|
||||
{.enthalpyVariation = enthalpyVariation, .borderVariation = borderVariation},
|
||||
{.enthalpyAction = carrierAction, .phaseAction = phaseAction}
|
||||
);
|
||||
CHECK(carrierAction(2) == borderVariation);
|
||||
CHECK(phaseAction(0) == enthalpyVariation(2));
|
||||
|
||||
// The phase residual is affine, so a larger centered-difference step
|
||||
// reduces cancellation without introducing truncation error.
|
||||
constexpr double epsilon = 1.0e-3;
|
||||
mfem::Vector plusEnthalpy(enthalpy);
|
||||
mfem::Vector minusEnthalpy(enthalpy);
|
||||
plusEnthalpy.Add(epsilon, enthalpyVariation);
|
||||
minusEnthalpy.Add(-epsilon, enthalpyVariation);
|
||||
|
||||
auto plusDependencies = dependencies;
|
||||
++plusDependencies.enthalpy.revision;
|
||||
phase.Prepare(compiled, plusEnthalpy, 0.3 + epsilon * borderVariation, plusDependencies);
|
||||
mfem::Vector plusCarrier(5);
|
||||
mfem::Vector plusPhase(1);
|
||||
plusCarrier = 0.0;
|
||||
plusPhase = 0.0;
|
||||
phase.AddResidual(plusCarrier, plusPhase);
|
||||
|
||||
auto minusDependencies = plusDependencies;
|
||||
++minusDependencies.enthalpy.revision;
|
||||
phase.Prepare(compiled, minusEnthalpy, 0.3 - epsilon * borderVariation, minusDependencies);
|
||||
mfem::Vector minusCarrier(5);
|
||||
mfem::Vector minusPhase(1);
|
||||
minusCarrier = 0.0;
|
||||
minusPhase = 0.0;
|
||||
phase.AddResidual(minusCarrier, minusPhase);
|
||||
|
||||
plusCarrier -= minusCarrier;
|
||||
plusCarrier /= 2.0 * epsilon;
|
||||
const double phaseDifference = (plusPhase(0) - minusPhase(0)) / (2.0 * epsilon);
|
||||
plusCarrier -= carrierAction;
|
||||
CHECK(plusCarrier.Norml2() < 1.0e-10);
|
||||
const double phaseDifferenceError = relative_error(phaseDifference, phaseAction(0));
|
||||
INFO("Central-density phase action = " << phaseAction(0));
|
||||
INFO("Central-density centered difference = " << phaseDifference);
|
||||
INFO("Central-density centered-difference error = " << phaseDifferenceError);
|
||||
CHECK(phaseDifferenceError < 1.0e-10);
|
||||
|
||||
auto restoredDependencies = minusDependencies;
|
||||
++restoredDependencies.enthalpy.revision;
|
||||
phase.Prepare(compiled, enthalpy, 0.3, restoredDependencies);
|
||||
mfem::Vector carrierDual(5);
|
||||
carrierDual = 0.0;
|
||||
carrierDual(2) = -0.8;
|
||||
constexpr double phaseDual = 1.1;
|
||||
mfem::Vector enthalpyDual(5);
|
||||
mfem::Vector borderDual(1);
|
||||
enthalpyDual = 0.0;
|
||||
borderDual = 0.0;
|
||||
phase.ApplyJacobianTranspose(
|
||||
{.enthalpyResidualDual = carrierDual, .phaseResidualDual = phaseDual},
|
||||
{.enthalpyDual = enthalpyDual, .borderDual = borderDual}
|
||||
);
|
||||
|
||||
const double forwardPairing = carrierAction * carrierDual + phaseAction(0) * phaseDual;
|
||||
const double transposePairing = enthalpyVariation * enthalpyDual + borderVariation * borderDual(0);
|
||||
CHECK(relative_error(transposePairing, forwardPairing) < 8.0 * std::numeric_limits<double>::epsilon());
|
||||
|
||||
const operators::CentralDensityConstraintReport report = phase.GetConstraintReport();
|
||||
CHECK(report.targetDensity == 8.0);
|
||||
CHECK(report.achievedDensity == 15.625);
|
||||
CHECK(report.targetEnthalpy == 2.0);
|
||||
CHECK(report.achievedEnthalpy == 2.5);
|
||||
CHECK(report.enthalpyResidual == 0.5);
|
||||
CHECK(report.scaledResidual == 0.25);
|
||||
}
|
||||
Reference in New Issue
Block a user