feat(newton): first newton solver implementation
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
#include <concepts>
|
||||
#include <numbers>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <mfem.hpp>
|
||||
@@ -8,6 +10,16 @@ import mean_field;
|
||||
import test_helpers;
|
||||
|
||||
namespace {
|
||||
struct UserApiZeroMetric final { };
|
||||
|
||||
[[nodiscard]] mean_field::solver::nonlinear::MetricEvaluation getMetric(
|
||||
const UserApiZeroMetric &,
|
||||
const mfem::Vector &,
|
||||
MPI_Comm
|
||||
) {
|
||||
return {.residualNorm = 0.0, .merit = 0.0};
|
||||
}
|
||||
|
||||
[[nodiscard]] mean_field::fem::FEM makeFiniteElements() {
|
||||
const mean_field::utils::Args arguments = test_utils::setup_args();
|
||||
return mean_field::fem::setup_fem(arguments.mesh_file, arguments, 0);
|
||||
@@ -18,9 +30,7 @@ namespace {
|
||||
Problem &problem,
|
||||
const mfem::Vector &state,
|
||||
const mean_field::operators::StellarEquilibriumDependencies &dependencies
|
||||
) {
|
||||
problem.Prepare(state, dependencies);
|
||||
};
|
||||
) { problem.Prepare(state, dependencies); };
|
||||
|
||||
template <typename Problem>
|
||||
concept PreparesWithPrescribedRotation = requires(
|
||||
@@ -28,9 +38,7 @@ namespace {
|
||||
const mfem::Vector &state,
|
||||
const mean_field::operators::StellarEquilibriumDependencies &dependencies,
|
||||
const mean_field::physics::RigidRotation &rotation
|
||||
) {
|
||||
problem.Prepare(state, dependencies, rotation);
|
||||
};
|
||||
) { problem.Prepare(state, dependencies, rotation); };
|
||||
} // namespace
|
||||
|
||||
TEST_CASE(
|
||||
@@ -47,7 +55,7 @@ TEST_CASE(
|
||||
integral::FixedTotalMass({.Mtotal = dimensions::MassValue{1.0}})
|
||||
);
|
||||
|
||||
auto problem = equilibrium::discretize(model, finiteElements);
|
||||
auto problem = equilibrium::discretize(model, std::move(finiteElements));
|
||||
auto preconditioner = preconditioning::makePreconditioner(problem);
|
||||
const auto &gravity = preconditioner.structureComponent().gravityComponent();
|
||||
|
||||
@@ -62,6 +70,52 @@ TEST_CASE(
|
||||
CHECK(gravity.potentialSchurBackend().application.cycles == 3);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Complete User API Builds A Context And Evaluates A Borrowing Newton Solver",
|
||||
"[user-api][solver][context][report]"
|
||||
) {
|
||||
using namespace mean_field;
|
||||
|
||||
auto finiteElements = makeFiniteElements();
|
||||
REQUIRE(finiteElements.okay());
|
||||
|
||||
constexpr double radius = utils::RADIUS;
|
||||
const double polytropicConstant = 2.0 * utils::G * radius * radius / std::numbers::pi_v<double>;
|
||||
const double centralDensity = std::numbers::pi_v<double> * utils::MASS / (4.0 * radius * radius * radius);
|
||||
auto stellarModel = model::StellarModel(
|
||||
eos::Polytrope({.n = 1.0, .K = polytropicConstant}),
|
||||
surface::Isobaric({.Psurf = dimensions::PressureValue{0.0}}),
|
||||
integral::FixedTotalMass({.Mtotal = dimensions::MassValue{utils::MASS}}),
|
||||
constraint::FixedCentralDensity({.RhoC = dimensions::DensityValue{centralDensity}})
|
||||
);
|
||||
auto discretization = equilibrium::makeStellarDiscretization(
|
||||
std::move(finiteElements), normalization::PhysicalRieszDiagonal{dimensions::LengthValue{radius}, utils::G}
|
||||
);
|
||||
auto context = solver::makeContext(
|
||||
std::move(stellarModel), std::move(discretization), preconditioning::makePreconditioner(),
|
||||
solver::linear::FGMRES({.restartLength = 20, .printLevel = -1})
|
||||
);
|
||||
|
||||
int beforeCalls = 0;
|
||||
int afterCalls = 0;
|
||||
auto observer = solver::nonlinear::makeObserver(
|
||||
[&beforeCalls](const solver::nonlinear::BeforeIteration &) { ++beforeCalls; },
|
||||
[&afterCalls](const solver::nonlinear::AfterIteration &) { ++afterCalls; }
|
||||
);
|
||||
auto newton = solver::nonlinear::Newton(solver::nonlinear::NewtonOptions{}, UserApiZeroMetric{});
|
||||
auto equilibriumSolver = solver::make(context, newton, observer);
|
||||
auto report = equilibriumSolver.evaluate();
|
||||
|
||||
REQUIRE(report.converged());
|
||||
CHECK(report.completedNonlinearIterations() == 0);
|
||||
CHECK(beforeCalls == 0);
|
||||
CHECK(afterCalls == 0);
|
||||
auto structure = report.structureView();
|
||||
REQUIRE(structure.valid());
|
||||
CHECK_FALSE(structure.state().empty());
|
||||
CHECK(context.hasActiveSolver());
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Fixed Angular Momentum User API Generates Rotation And Its Composable Solver Border",
|
||||
"[user-api][fixed-angular-momentum][type]"
|
||||
@@ -72,18 +126,14 @@ TEST_CASE(
|
||||
REQUIRE(finiteElements.okay());
|
||||
|
||||
auto model = model::StellarModel(
|
||||
eos::Polytrope({.n = 1.0, .K = 0.25}),
|
||||
surface::Isobaric({.Psurf = dimensions::PressureValue{0.0}}),
|
||||
eos::Polytrope({.n = 1.0, .K = 0.25}), surface::Isobaric({.Psurf = dimensions::PressureValue{0.0}}),
|
||||
integral::FixedTotalMass({.Mtotal = dimensions::MassValue{1.0}}),
|
||||
integral::FixedAngularMomentum({
|
||||
.Jtotal = dimensions::AngularMomentumValue{0.2},
|
||||
.axis = {0.0, 0.0, 2.0}
|
||||
})
|
||||
integral::FixedAngularMomentum({.Jtotal = dimensions::AngularMomentumValue{0.2}, .axis = {0.0, 0.0, 2.0}})
|
||||
);
|
||||
|
||||
auto problem = equilibrium::discretize(model, finiteElements);
|
||||
auto preconditioner = preconditioning::makePreconditioner(problem);
|
||||
using Problem = std::remove_cvref_t<decltype(problem)>;
|
||||
auto problem = equilibrium::discretize(model, std::move(finiteElements));
|
||||
auto preconditioner = preconditioning::makePreconditioner(problem);
|
||||
using Problem = std::remove_cvref_t<decltype(problem)>;
|
||||
using Preconditioner = std::remove_cvref_t<decltype(preconditioner)>;
|
||||
|
||||
STATIC_CHECK(Problem::hasFixedAngularMomentum);
|
||||
@@ -99,8 +149,9 @@ TEST_CASE(
|
||||
CHECK(problem.StateSize() == problem.EquationSize());
|
||||
CHECK(problem.StateSize() == problem.GetPhysicalOperator().Width() + 1);
|
||||
REQUIRE(problem.GetManifest().constraints().size() == 3);
|
||||
CHECK(problem.GetManifest().template specification<models::FixedAngularMomentum>().stableId ==
|
||||
"FixedAngularMomentum");
|
||||
CHECK(
|
||||
problem.GetManifest().template specification<models::FixedAngularMomentum>().stableId == "FixedAngularMomentum"
|
||||
);
|
||||
CHECK(problem.GetManifest().valueBlocks().back().symbol == "Omega");
|
||||
CHECK(problem.GetManifest().residualBlocks().back().symbol == "R_J");
|
||||
}
|
||||
@@ -119,7 +170,7 @@ TEST_CASE(
|
||||
integral::FixedTotalMass({.Mtotal = dimensions::MassValue{1.0}}),
|
||||
constraint::FixedCentralDensity({.RhoC = dimensions::DensityValue{1.0}})
|
||||
);
|
||||
auto problem = equilibrium::discretize(model, finiteElements);
|
||||
auto problem = equilibrium::discretize(model, std::move(finiteElements));
|
||||
|
||||
auto material = preconditioning::materialSurfaceBlock(problem);
|
||||
auto gravity = preconditioning::GravityFieldBlock(
|
||||
@@ -151,7 +202,7 @@ TEST_CASE(
|
||||
integral::FixedTotalMass({.Mtotal = dimensions::MassValue{1.0}}),
|
||||
constraint::FixedCentralDensity({.RhoC = dimensions::DensityValue{1.0}})
|
||||
);
|
||||
auto problem = equilibrium::discretize(model, finiteElements);
|
||||
auto problem = equilibrium::discretize(model, std::move(finiteElements));
|
||||
|
||||
auto material = preconditioning::materialSurfaceBlock(
|
||||
problem, preconditioning::backend::Diagonal{}, preconditioning::backend::Diagonal{},
|
||||
|
||||
Reference in New Issue
Block a user