Add native spherical harmonic surface coordinates to coupled equilibrium
This commit is contained in:
@@ -32,6 +32,7 @@ import mean_field;
|
||||
#include "plateau_polynomial_certificate.hpp"
|
||||
#include "shape_field_weak_diagnostics.hpp"
|
||||
#include "reduced_surface_diagnostics.hpp"
|
||||
#include "spherical_harmonic_surface_diagnostics.hpp"
|
||||
|
||||
using namespace mean_field;
|
||||
namespace {
|
||||
@@ -44,7 +45,8 @@ struct Options {
|
||||
bool preconditionerProbe = false, nonlinearStepProbe = false;
|
||||
bool weakShapeProbe = false, weakShapeFinal = false;
|
||||
bool reducedSurfaceProbe = false, reducedSurfaceAlgebraCheck = false;
|
||||
int surfaceDegree = 4;
|
||||
int surfaceDegree = 4, surfaceMaximumOrder = -1;
|
||||
std::string surfacePrescription = "nodal";
|
||||
bool coupledTrialProbe = false, coupledSeedControl = false;
|
||||
bool dampedCoupledProbe = false;
|
||||
std::string coupledSearch = "near";
|
||||
@@ -122,6 +124,10 @@ Options Parse(int argc, char **argv) {
|
||||
o.reducedSurfaceProbe = true;
|
||||
else if (a == "--reduced-surface-algebra-check")
|
||||
o.reducedSurfaceAlgebraCheck = true;
|
||||
else if (a == "--surface-prescription")
|
||||
o.surfacePrescription = v();
|
||||
else if (a == "--surface-maximum-order")
|
||||
o.surfaceMaximumOrder = std::stoi(v());
|
||||
else if (a == "--surface-degree")
|
||||
o.surfaceDegree = std::stoi(v());
|
||||
else if (a == "--weak-shape-probe")
|
||||
@@ -186,6 +192,23 @@ Options Parse(int argc, char **argv) {
|
||||
}
|
||||
if (o.output.empty() || o.n < 1 || o.n >= 5 || o.maxNewton < 0 || o.maxLinear < 1 || o.restart < 1)
|
||||
throw std::invalid_argument("Invalid options");
|
||||
if (o.surfacePrescription != "nodal" && o.surfacePrescription != "harmonics")
|
||||
throw std::invalid_argument("Surface prescriptions are nodal or harmonics");
|
||||
if (o.surfacePrescription == "harmonics") {
|
||||
deformation::SphericalHarmonicSurfaceOptions surfaceOptions{.maximumDegree = o.surfaceDegree};
|
||||
if (o.surfaceMaximumOrder != -1) surfaceOptions.maximumAzimuthalOrder = o.surfaceMaximumOrder;
|
||||
surfaceOptions.validate();
|
||||
// Older frozen probes interpret snapshots as nodal radial values. A
|
||||
// size match is insufficient to establish modal snapshot provenance.
|
||||
if (o.reducedSurfaceProbe || o.reducedSurfaceAlgebraCheck || o.weakShapeProbe || o.weakShapeFinal ||
|
||||
o.probes || o.pressureProbe || o.shapeCurvature || o.surfaceTrace || o.strategyProbe ||
|
||||
o.positivityProbe || o.positivityCertificate || o.preconditionerProbe || o.nonlinearStepProbe ||
|
||||
o.coupledTrialProbe || o.dampedCoupledProbe || !o.probeState.empty() || !o.probeDirection.empty() ||
|
||||
!o.replayCandidate.empty() || o.strategy != "baseline")
|
||||
throw std::invalid_argument("Native harmonic diagnostics currently support live baseline Newton trajectories only");
|
||||
} else if (o.surfaceMaximumOrder != -1) {
|
||||
throw std::invalid_argument("--surface-maximum-order requires --surface-prescription harmonics");
|
||||
}
|
||||
if (o.reducedSurfaceProbe || o.reducedSurfaceAlgebraCheck) {
|
||||
if ((o.reducedSurfaceProbe && o.reducedSurfaceAlgebraCheck) || o.maxNewton != 0 ||
|
||||
o.strategy != "baseline" || o.probes || o.pressureProbe || o.shapeCurvature ||
|
||||
@@ -323,6 +346,15 @@ void SaveVector(const std::filesystem::path &p, const mfem::Vector &v) {
|
||||
v.Print(s, 1);
|
||||
}
|
||||
mfem::Vector LoadVector(const std::filesystem::path &path, int expectedSize) {
|
||||
// Legacy nodal probes may share a total vector size with another shape
|
||||
// space. Honor known coordinate provenance before reading its numbers.
|
||||
for (const auto &directory : {path.parent_path(), path.parent_path().parent_path()}) {
|
||||
std::ifstream metadata(directory / "metadata.txt");
|
||||
std::string line;
|
||||
while (std::getline(metadata, line))
|
||||
if (line == "surface_prescription=harmonics")
|
||||
throw std::invalid_argument("Legacy nodal probes cannot load a harmonic snapshot: " + path.string());
|
||||
}
|
||||
std::ifstream input(path);
|
||||
int count = -1;
|
||||
input >> count;
|
||||
@@ -560,6 +592,8 @@ int RunContext(Context &context, const Options &o, const Scales &scales) {
|
||||
<< "\ngravity_iterations=" << o.gravityIterations << "\ngravity_restart=" << o.gravityRestart
|
||||
<< "\npreconditioner_probe=" << o.preconditionerProbe
|
||||
<< "\nnonlinear_step_probe=" << o.nonlinearStepProbe
|
||||
<< "\nsurface_prescription=" << o.surfacePrescription
|
||||
<< "\nsurface_maximum_order=" << o.surfaceMaximumOrder
|
||||
<< "\nreduced_surface_probe=" << o.reducedSurfaceProbe << "\nsurface_degree=" << o.surfaceDegree
|
||||
<< "\nweak_shape_probe=" << o.weakShapeProbe << "\nweak_shape_final=" << o.weakShapeFinal
|
||||
<< "\ndamped_coupled_probe=" << o.dampedCoupledProbe
|
||||
@@ -762,6 +796,11 @@ int RunContext(Context &context, const Options &o, const Scales &scales) {
|
||||
experiment::polytrope_validation::PhysicalState physical(s, f);
|
||||
auto out = File(o.output / (label + "_physical.csv"));
|
||||
out << "quadrature,metric,value\n";
|
||||
if (o.surfacePrescription == "harmonics") {
|
||||
deformation::SphericalHarmonicSurfaceOptions surfaceOptions{.maximumDegree = o.surfaceDegree};
|
||||
if (o.surfaceMaximumOrder != -1) surfaceOptions.maximumAzimuthalOrder = o.surfaceMaximumOrder;
|
||||
experiment::plateau_diagnostics::RecordHarmonicSurface(s, f, surfaceOptions, o.output, label);
|
||||
}
|
||||
for (int q : {14, 18}) {
|
||||
auto metrics = experiment::plateau_diagnostics::MeasurePhysical(physical, o.n, scales.K, q);
|
||||
metrics["normalized_bordered_residual"] = physical.normalizedBorderedResidualNorm;
|
||||
@@ -1065,9 +1104,11 @@ int RunContext(Context &context, const Options &o, const Scales &scales) {
|
||||
});
|
||||
return converged ? 0 : 3;
|
||||
}
|
||||
template <class Model> int Setup(Model model, fem::FEM f, const Options &o, const Scales &scales) {
|
||||
template <class Model, class Surface>
|
||||
int SetupWithSurface(Model model, fem::FEM f, Surface surface, const Options &o, const Scales &scales) {
|
||||
auto d = equilibrium::makeStellarDiscretization(
|
||||
std::move(f), normalization::PhysicalRieszDiagonal{dimensions::LengthValue{1.}, utils::G});
|
||||
std::move(f), normalization::PhysicalRieszDiagonal{dimensions::LengthValue{1.}, utils::G},
|
||||
std::move(surface));
|
||||
const auto withRecipe = [&](auto recipe) {
|
||||
using Recipe = decltype(recipe);
|
||||
using Problem = equilibrium::StellarEquilibriumProblem<Model, decltype(d)>;
|
||||
@@ -1095,6 +1136,18 @@ template <class Model> int Setup(Model model, fem::FEM f, const Options &o, cons
|
||||
{.correctionSweeps = o.schurCorrectionSweeps, .relaxation = o.schurRelaxation}});
|
||||
throw std::logic_error("An unsupported Newton preconditioner bypassed option validation.");
|
||||
}
|
||||
template <class Model> int Setup(Model model, fem::FEM f, const Options &o, const Scales &scales) {
|
||||
if (o.surfacePrescription == "harmonics") {
|
||||
deformation::SphericalHarmonicSurfaceOptions options{.maximumDegree = o.surfaceDegree};
|
||||
if (o.surfaceMaximumOrder != -1) options.maximumAzimuthalOrder = o.surfaceMaximumOrder;
|
||||
return SetupWithSurface(std::move(model), std::move(f),
|
||||
deformation::SphericalHarmonicSurface{options}, o, scales);
|
||||
}
|
||||
mfem::Vector center(3);
|
||||
center = 0.0;
|
||||
return SetupWithSurface(std::move(model), std::move(f),
|
||||
deformation::NodalRadialSurface{std::move(center)}, o, scales);
|
||||
}
|
||||
int Run(const Options &o) {
|
||||
const auto scales = Derive(o.n);
|
||||
std::cout << "n=" << o.n << " K=" << scales.K << " rho_c=" << scales.rho << " J=" << o.angularMomentum << std::endl;
|
||||
|
||||
Reference in New Issue
Block a user