#include #include #include #include #include #include #include #include #include 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(gravityDirection.GetData()), m_gravityGradientSize ); const mfem::Vector gravityPotentialDirection( const_cast(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(gravityCompletion.GetData()), gravityGradientSize ); const mfem::Vector gravityPotential( const_cast(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 ); } } // namespace TEST_CASE( "Gravity-Completed Reduced Surface Mode Responses Of The Stellar Equilibrium Jacobian", "[null_space][surface_modes][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_surface_modes(fixture); constexpr std::array rotationFractions{0.0, 0.5}; const int totalCases = static_cast(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::SurfaceMode &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 surfaceOnlyAction = fixture.jacobian_action(mode.direction); mfem::Vector gravityRightHandSide = gravity_residual_blocks(surfaceOnlyAction, 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::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 completedAction = fixture.jacobian_action(completedDirection); std::map metrics{ {"surface_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)}, {"surface_only_action_norm", experiment::null_space::global_norm(surfaceOnlyAction, communicator)}, {"gravity_completed_action_norm", experiment::null_space::global_norm(completedAction, communicator)}, {"gravity_solve_rhs_norm", gravityRightHandSideNorm}, {"gravity_solve_residual_norm", gravitySolveResidualNorm}, {"gravity_solve_relative_residual", gravitySolveRelativeResidual}, {"gravity_solve_iterations", static_cast(gravitySolver.GetNumIterations())}, {"gravity_solve_final_norm", gravitySolver.GetFinalNorm()} }; add_block_metrics( metrics, "surface_only_", experiment::null_space::residual_block_norms( surfaceOnlyAction, fixture.stellar_operator().GetLayout(), communicator ) ); add_block_metrics( metrics, "gravity_completed_", experiment::null_space::residual_block_norms( completedAction, fixture.stellar_operator().GetLayout(), communicator ) ); if (rank == 0) { experiment::record_experiment_result( "gravity_completed_reduced_surface_modes", mode.name, {{"mode_kind", experiment::null_space::surface_mode_kind_name(mode.kind)}, {"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 reduced surface-mode cases" ); } } experiment::null_space::report_progress( communicator, "gravity-completed reduced surface-mode probe complete; writing CSV output" ); }