feat(surface): major work on implementing surface constraints in a presciption agnostic manner
This commit is contained in:
273
experiments/gravity_completed_rigid_motion.cpp
Normal file
273
experiments/gravity_completed_rigid_motion.cpp
Normal file
@@ -0,0 +1,273 @@
|
||||
#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 {
|
||||
class GravityUnknownJacobian final : public mfem::Operator {
|
||||
public:
|
||||
explicit GravityUnknownJacobian(
|
||||
const mean_field::operators::PreparedStellarEquilibriumOperator &stellarOperator
|
||||
)
|
||||
: mfem::Operator(
|
||||
stellarOperator.GetLayout().size(experiment::null_space::gravityGradientValue) +
|
||||
stellarOperator.GetLayout().size(experiment::null_space::gravityPotentialValue)
|
||||
),
|
||||
m_stellarOperator(stellarOperator),
|
||||
m_gravityGradientSize(stellarOperator.GetLayout().size(experiment::null_space::gravityGradientValue)) {
|
||||
MFEM_VERIFY(Width() == Height(), "The reduced gravity Jacobian must be square.");
|
||||
}
|
||||
|
||||
void Mult(
|
||||
const mfem::Vector &gravityDirection,
|
||||
mfem::Vector &gravityAction
|
||||
) const override {
|
||||
MFEM_VERIFY(gravityDirection.Size() == Width(), "The reduced gravity direction has the wrong size.");
|
||||
|
||||
const mfem::Vector gravityGradientDirection(
|
||||
const_cast<mfem::real_t *>(gravityDirection.GetData()), m_gravityGradientSize
|
||||
);
|
||||
const mfem::Vector gravityPotentialDirection(
|
||||
const_cast<mfem::real_t *>(gravityDirection.GetData()) + m_gravityGradientSize,
|
||||
Width() - m_gravityGradientSize
|
||||
);
|
||||
|
||||
m_stellarOperator.GetGravityOperator().ApplyGravityUnknowns(
|
||||
gravityGradientDirection,
|
||||
gravityPotentialDirection,
|
||||
m_stellarOperator.GetGravityContext().GetGeometryContext(),
|
||||
gravityAction
|
||||
);
|
||||
}
|
||||
|
||||
[[nodiscard]] int gravity_gradient_size() const noexcept {
|
||||
return m_gravityGradientSize;
|
||||
}
|
||||
|
||||
private:
|
||||
const mean_field::operators::PreparedStellarEquilibriumOperator &m_stellarOperator;
|
||||
int m_gravityGradientSize;
|
||||
};
|
||||
|
||||
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]);
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] mfem::Vector gravity_residual_blocks(
|
||||
const mfem::Vector &completeAction,
|
||||
const mean_field::operators::StellarEquilibriumLayout &layout
|
||||
) {
|
||||
const mfem::Vector gradient = experiment::null_space::const_residual_view(
|
||||
completeAction, layout, experiment::null_space::gravityGradientResidual
|
||||
);
|
||||
const mfem::Vector potential = experiment::null_space::const_residual_view(
|
||||
completeAction, layout, experiment::null_space::gravityPotentialResidual
|
||||
);
|
||||
|
||||
mfem::Vector result(gradient.Size() + potential.Size());
|
||||
mfem::Vector(result.GetData(), gradient.Size()) = gradient;
|
||||
mfem::Vector(result.GetData() + gradient.Size(), potential.Size()) = potential;
|
||||
return result;
|
||||
}
|
||||
|
||||
void assign_gravity_completion(
|
||||
mfem::Vector &completeDirection,
|
||||
const mean_field::operators::StellarEquilibriumLayout &layout,
|
||||
const mfem::Vector &gravityCompletion,
|
||||
const int gravityGradientSize
|
||||
) {
|
||||
const mfem::Vector gravityGradient(
|
||||
const_cast<mfem::real_t *>(gravityCompletion.GetData()), gravityGradientSize
|
||||
);
|
||||
const mfem::Vector gravityPotential(
|
||||
const_cast<mfem::real_t *>(gravityCompletion.GetData()) + gravityGradientSize,
|
||||
gravityCompletion.Size() - gravityGradientSize
|
||||
);
|
||||
experiment::null_space::assign_value_block(
|
||||
completeDirection, layout, experiment::null_space::gravityGradientValue, gravityGradient
|
||||
);
|
||||
experiment::null_space::assign_value_block(
|
||||
completeDirection, layout, experiment::null_space::gravityPotentialValue, gravityPotential
|
||||
);
|
||||
}
|
||||
|
||||
void apply_centering_rows(
|
||||
const mean_field::operators::PreparedStellarEquilibriumOperator &stellarOperator,
|
||||
const mfem::Vector &direction,
|
||||
mfem::Vector &action
|
||||
) {
|
||||
const auto &layout = stellarOperator.GetLayout();
|
||||
const mfem::Vector displacementDirection =
|
||||
experiment::null_space::const_value_view(direction, layout, experiment::null_space::displacementValue);
|
||||
mfem::Vector displacementAction =
|
||||
experiment::null_space::residual_view(action, layout, experiment::null_space::displacementResidual);
|
||||
stellarOperator.GetCenteringConstraintOperator().ApplyJacobianRows(displacementDirection, displacementAction);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
TEST_CASE(
|
||||
"Gravity-Completed Rigid Motion Responses Of The Stellar Equilibrium Jacobian",
|
||||
"[null_space][gravity_completed]"
|
||||
) {
|
||||
mean_field::utils::Args args = test_utils::setup_args();
|
||||
args.p.rtol = 1.0e-11;
|
||||
args.p.atol = std::min(args.p.atol, 1.0e-13);
|
||||
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};
|
||||
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);
|
||||
|
||||
GravityUnknownJacobian gravityUnknownJacobian(fixture.stellar_operator());
|
||||
mean_field::operators::ReducedGravityFieldPreconditioner gravityPreconditioner(
|
||||
fixture.fem(), fixture.stellar_operator().GetGravityContext().GetGeometryContext()
|
||||
);
|
||||
|
||||
mfem::MINRESSolver gravitySolver(communicator);
|
||||
gravitySolver.SetOperator(gravityUnknownJacobian);
|
||||
gravitySolver.SetPreconditioner(gravityPreconditioner);
|
||||
gravitySolver.SetRelTol(1.0e-11);
|
||||
gravitySolver.SetAbsTol(1.0e-13);
|
||||
gravitySolver.SetMaxIter(2000);
|
||||
gravitySolver.SetPrintLevel(1);
|
||||
|
||||
for (const experiment::null_space::RigidMode &mode : modes) {
|
||||
experiment::null_space::report_progress(
|
||||
communicator, "solving the gravity completion for " + mode.name + " at rotation fraction " +
|
||||
std::to_string(rotationFraction) + " (" + std::to_string(completedCases + 1) + "/" +
|
||||
std::to_string(totalCases) + ")"
|
||||
);
|
||||
|
||||
const mfem::Vector displacementOnlyAction = fixture.unpinned_jacobian_action(mode.direction);
|
||||
mfem::Vector gravityRightHandSide =
|
||||
gravity_residual_blocks(displacementOnlyAction, fixture.stellar_operator().GetLayout());
|
||||
gravityRightHandSide *= -1.0;
|
||||
|
||||
mfem::Vector gravityCompletion(gravityUnknownJacobian.Width());
|
||||
gravityCompletion = 0.0;
|
||||
gravitySolver.Mult(gravityRightHandSide, gravityCompletion);
|
||||
|
||||
REQUIRE(gravitySolver.GetConverged());
|
||||
|
||||
mfem::Vector gravitySolveAction;
|
||||
gravityUnknownJacobian.Mult(gravityCompletion, gravitySolveAction);
|
||||
mfem::Vector gravitySolveResidual(gravitySolveAction);
|
||||
gravitySolveResidual -= gravityRightHandSide;
|
||||
|
||||
const double gravityRightHandSideNorm =
|
||||
experiment::null_space::global_norm(gravityRightHandSide, communicator);
|
||||
const double gravitySolveResidualNorm =
|
||||
experiment::null_space::global_norm(gravitySolveResidual, communicator);
|
||||
const double gravitySolveRelativeResidual =
|
||||
gravitySolveResidualNorm / std::max(gravityRightHandSideNorm, std::numeric_limits<double>::epsilon());
|
||||
|
||||
REQUIRE(std::isfinite(gravitySolveRelativeResidual));
|
||||
|
||||
mfem::Vector completedDirection(mode.direction);
|
||||
assign_gravity_completion(
|
||||
completedDirection, fixture.stellar_operator().GetLayout(), gravityCompletion,
|
||||
gravityUnknownJacobian.gravity_gradient_size()
|
||||
);
|
||||
|
||||
const mfem::Vector completedUnpinnedAction = fixture.unpinned_jacobian_action(completedDirection);
|
||||
mfem::Vector completedConstrainedAction(completedUnpinnedAction);
|
||||
apply_centering_rows(fixture.stellar_operator(), completedDirection, completedConstrainedAction);
|
||||
|
||||
mfem::Vector centeringContribution(completedConstrainedAction);
|
||||
centeringContribution -= completedUnpinnedAction;
|
||||
|
||||
std::map<std::string, double> metrics{
|
||||
{"displacement_only_input_norm", experiment::null_space::global_norm(mode.direction, communicator)},
|
||||
{"gravity_completion_norm", experiment::null_space::global_norm(gravityCompletion, communicator)},
|
||||
{"completed_input_norm", experiment::null_space::global_norm(completedDirection, communicator)},
|
||||
{"displacement_only_action_norm",
|
||||
experiment::null_space::global_norm(displacementOnlyAction, communicator)},
|
||||
{"gravity_completed_unpinned_action_norm",
|
||||
experiment::null_space::global_norm(completedUnpinnedAction, communicator)},
|
||||
{"gravity_completed_constrained_action_norm",
|
||||
experiment::null_space::global_norm(completedConstrainedAction, communicator)},
|
||||
{"centering_contribution_norm",
|
||||
experiment::null_space::global_norm(centeringContribution, communicator)},
|
||||
{"gravity_solve_rhs_norm", gravityRightHandSideNorm},
|
||||
{"gravity_solve_residual_norm", gravitySolveResidualNorm},
|
||||
{"gravity_solve_relative_residual", gravitySolveRelativeResidual},
|
||||
{"gravity_solve_iterations", static_cast<double>(gravitySolver.GetNumIterations())},
|
||||
{"gravity_solve_final_norm", gravitySolver.GetFinalNorm()}
|
||||
};
|
||||
|
||||
add_block_metrics(
|
||||
metrics, "displacement_only_",
|
||||
experiment::null_space::residual_block_norms(
|
||||
displacementOnlyAction, fixture.stellar_operator().GetLayout(), communicator
|
||||
)
|
||||
);
|
||||
add_block_metrics(
|
||||
metrics, "gravity_completed_unpinned_",
|
||||
experiment::null_space::residual_block_norms(
|
||||
completedUnpinnedAction, fixture.stellar_operator().GetLayout(), communicator
|
||||
)
|
||||
);
|
||||
add_block_metrics(
|
||||
metrics, "gravity_completed_constrained_",
|
||||
experiment::null_space::residual_block_norms(
|
||||
completedConstrainedAction, fixture.stellar_operator().GetLayout(), communicator
|
||||
)
|
||||
);
|
||||
|
||||
if (rank == 0) {
|
||||
experiment::record_experiment_result(
|
||||
"gravity_completed_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) +
|
||||
" gravity-completed rigid-mode cases"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
experiment::null_space::report_progress(
|
||||
communicator, "gravity-completed rigid-motion probe complete; writing CSV output"
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user