feat(surface): major work on implementing surface constraints in a presciption agnostic manner
This commit is contained in:
174
experiments/rigid_motion_null_space.cpp
Normal file
174
experiments/rigid_motion_null_space.cpp
Normal file
@@ -0,0 +1,174 @@
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#include <mfem.hpp>
|
||||
#include <mpi.h>
|
||||
|
||||
import experiment;
|
||||
import experiment.stellar_null_space;
|
||||
import mean_field;
|
||||
import test_helpers;
|
||||
|
||||
namespace {
|
||||
[[nodiscard]] double relative_difference(
|
||||
const mfem::Vector &computed,
|
||||
const mfem::Vector &reference,
|
||||
const MPI_Comm communicator
|
||||
) {
|
||||
mfem::Vector difference(computed);
|
||||
difference -= reference;
|
||||
const double scale = std::max(
|
||||
{experiment::null_space::global_norm(computed, communicator),
|
||||
experiment::null_space::global_norm(reference, communicator), std::numeric_limits<double>::epsilon()}
|
||||
);
|
||||
return experiment::null_space::global_norm(difference, communicator) / scale;
|
||||
}
|
||||
|
||||
void add_block_metrics(
|
||||
std::map<
|
||||
std::string,
|
||||
double> &metrics,
|
||||
const std::string &prefix,
|
||||
const std::array<
|
||||
double,
|
||||
6> &norms
|
||||
) {
|
||||
for (std::size_t block = 0; block < norms.size(); ++block) {
|
||||
metrics.emplace(prefix + experiment::null_space::residualBlockNames[block] + "_norm", norms[block]);
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
TEST_CASE(
|
||||
"Rigid Motion Responses Of The Stellar Equilibrium Jacobian",
|
||||
"[null_space][rigid_motion]"
|
||||
) {
|
||||
mean_field::utils::Args args = test_utils::setup_args();
|
||||
args.p.rtol = 1.0e-12;
|
||||
args.p.atol = std::min(args.p.atol, 1.0e-14);
|
||||
args.p.max_iters = std::max(args.p.max_iters, 2000);
|
||||
|
||||
experiment::null_space::N3Equilibrium fixture(std::move(args));
|
||||
const MPI_Comm communicator = fixture.fem().mesh->GetComm();
|
||||
int rank = 0;
|
||||
MPI_Comm_rank(communicator, &rank);
|
||||
|
||||
const auto modes = experiment::null_space::make_rigid_modes(fixture);
|
||||
constexpr std::array<double, 2> rotationFractions{0.0, 0.5};
|
||||
constexpr std::array<double, 2> finiteDifferenceSteps{1.0e-4, 1.0e-6};
|
||||
const int totalCases = static_cast<int>(rotationFractions.size() * modes.size());
|
||||
int completedCases = 0;
|
||||
|
||||
for (const double rotationFraction : rotationFractions) {
|
||||
const mean_field::physics::RigidRotation rotation = fixture.rotation(rotationFraction);
|
||||
fixture.prepare(fixture.state(), rotation);
|
||||
|
||||
mfem::Vector constrainedResidual;
|
||||
fixture.stellar_operator().BuildResidual(constrainedResidual);
|
||||
const mfem::Vector unpinnedResidual = fixture.unpinned_residual();
|
||||
|
||||
REQUIRE(constrainedResidual.Size() == unpinnedResidual.Size());
|
||||
REQUIRE(std::isfinite(experiment::null_space::global_norm(constrainedResidual, communicator)));
|
||||
REQUIRE(std::isfinite(experiment::null_space::global_norm(unpinnedResidual, communicator)));
|
||||
|
||||
for (const experiment::null_space::RigidMode &mode : modes) {
|
||||
experiment::null_space::report_progress(
|
||||
communicator, "probing " + mode.name + " at rotation fraction " + std::to_string(rotationFraction) +
|
||||
" (" + std::to_string(completedCases + 1) + "/" + std::to_string(totalCases) + ")"
|
||||
);
|
||||
|
||||
fixture.prepare(fixture.state(), rotation);
|
||||
const mfem::Vector unpinnedAction = fixture.unpinned_jacobian_action(mode.direction);
|
||||
mfem::Vector constrainedAction;
|
||||
fixture.stellar_operator().Mult(mode.direction, constrainedAction);
|
||||
|
||||
mfem::Vector centeringContribution(constrainedAction);
|
||||
centeringContribution -= unpinnedAction;
|
||||
|
||||
const double inputNorm = experiment::null_space::global_norm(mode.direction, communicator);
|
||||
const double unpinnedNorm = experiment::null_space::global_norm(unpinnedAction, communicator);
|
||||
const double constrainedNorm = experiment::null_space::global_norm(constrainedAction, communicator);
|
||||
|
||||
REQUIRE(inputNorm > 0.0);
|
||||
REQUIRE(std::isfinite(unpinnedNorm));
|
||||
REQUIRE(std::isfinite(constrainedNorm));
|
||||
|
||||
std::map<std::string, double> metrics{
|
||||
{"input_algebraic_norm", inputNorm},
|
||||
{"unpinned_action_norm", unpinnedNorm},
|
||||
{"unpinned_action_per_input_norm", unpinnedNorm / inputNorm},
|
||||
{"constrained_action_norm", constrainedNorm},
|
||||
{"constrained_action_per_input_norm", constrainedNorm / inputNorm},
|
||||
{"centering_contribution_norm",
|
||||
experiment::null_space::global_norm(centeringContribution, communicator)},
|
||||
{"unpinned_base_residual_norm", experiment::null_space::global_norm(unpinnedResidual, communicator)},
|
||||
{"constrained_base_residual_norm",
|
||||
experiment::null_space::global_norm(constrainedResidual, communicator)}
|
||||
};
|
||||
|
||||
add_block_metrics(
|
||||
metrics, "unpinned_",
|
||||
experiment::null_space::residual_block_norms(
|
||||
unpinnedAction, fixture.stellar_operator().GetLayout(), communicator
|
||||
)
|
||||
);
|
||||
add_block_metrics(
|
||||
metrics, "constrained_",
|
||||
experiment::null_space::residual_block_norms(
|
||||
constrainedAction, fixture.stellar_operator().GetLayout(), communicator
|
||||
)
|
||||
);
|
||||
|
||||
for (const double step : finiteDifferenceSteps) {
|
||||
mfem::Vector plusState(fixture.state());
|
||||
plusState.Add(step, mode.direction);
|
||||
fixture.prepare(plusState, rotation);
|
||||
const mfem::Vector plusResidual = fixture.unpinned_residual();
|
||||
|
||||
mfem::Vector minusState(fixture.state());
|
||||
minusState.Add(-step, mode.direction);
|
||||
fixture.prepare(minusState, rotation);
|
||||
const mfem::Vector minusResidual = fixture.unpinned_residual();
|
||||
|
||||
mfem::Vector finiteDifference(plusResidual);
|
||||
finiteDifference -= minusResidual;
|
||||
finiteDifference /= 2.0 * step;
|
||||
|
||||
const std::string stepName = step == finiteDifferenceSteps.front() ? "1e-4" : "1e-6";
|
||||
metrics.emplace(
|
||||
"finite_difference_relative_error_" + stepName,
|
||||
relative_difference(unpinnedAction, finiteDifference, communicator)
|
||||
);
|
||||
}
|
||||
|
||||
fixture.prepare(fixture.state(), rotation);
|
||||
|
||||
if (rank == 0) {
|
||||
experiment::record_experiment_result(
|
||||
"stellar_rigid_motion_null_space", mode.name,
|
||||
{{"mode_kind",
|
||||
mode.kind == experiment::null_space::RigidModeKind::translation ? "translation" : "rotation"},
|
||||
{"axis", std::to_string(mode.axis)},
|
||||
{"rotation_fraction_of_keplerian", std::to_string(rotationFraction)},
|
||||
{"mesh_file", test_utils::setup_args().mesh_file},
|
||||
{"local_state_dofs", std::to_string(fixture.stellar_operator().Width())}},
|
||||
std::move(metrics)
|
||||
);
|
||||
}
|
||||
|
||||
++completedCases;
|
||||
experiment::null_space::report_progress(
|
||||
communicator,
|
||||
"completed " + std::to_string(completedCases) + "/" + std::to_string(totalCases) + " rigid-mode cases"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
experiment::null_space::report_progress(communicator, "rigid-motion probe complete; writing CSV output");
|
||||
}
|
||||
Reference in New Issue
Block a user