feat(field-support): added field support system, mid migration

currently the barotope and the pressure force operator are migrated to the new support system
This commit is contained in:
2026-08-23 10:13:53 -04:00
parent dc912fd15e
commit 0f3ca8050b
137 changed files with 29975 additions and 16389 deletions

View File

@@ -47,13 +47,9 @@ namespace mean_field::fem {
int mpiSize = 1;
MPI_Comm_size(MPI_COMM_WORLD, &mpiSize);
const std::unique_ptr<int[]> meshPartitioning(
fem.smesh.mesh->GeneratePartitioning(mpiSize, 1)
);
const std::unique_ptr<int[]> meshPartitioning(fem.smesh.mesh->GeneratePartitioning(mpiSize, 1));
fem.mesh = std::make_unique<mfem::ParMesh>(
MPI_COMM_WORLD, *fem.smesh.mesh, meshPartitioning.get(), 1
);
fem.mesh = std::make_unique<mfem::ParMesh>(MPI_COMM_WORLD, *fem.smesh.mesh, meshPartitioning.get(), 1);
fem.mesh->EnsureNodes();
@@ -73,11 +69,9 @@ namespace mean_field::fem {
throw std::runtime_error("Values for exterior coordinate not set.");
}
const mfem::FiniteElementSpace &serialCoordinateSpace =
*fem.smesh.exterior_coordinate->space;
const mfem::FiniteElementSpace &serialCoordinateSpace = *fem.smesh.exterior_coordinate->space;
const mfem::GridFunction &serialCoordinate =
*fem.smesh.exterior_coordinate->values;
const mfem::GridFunction &serialCoordinate = *fem.smesh.exterior_coordinate->values;
if (serialCoordinate.FESpace() != &serialCoordinateSpace) {
throw std::runtime_error(
@@ -94,9 +88,7 @@ namespace mean_field::fem {
}
if (serialCoordinateSpace.GetVDim() != 1) {
throw std::runtime_error(
"Exterior coordinate must be a scalar field."
);
throw std::runtime_error("Exterior coordinate must be a scalar field.");
}
if (serialCoordinate.Size() != serialCoordinateSpace.GetVSize()) {
@@ -106,50 +98,37 @@ namespace mean_field::fem {
);
}
const int compactificationOrder =
serialCoordinateSpace.GetMaxElementOrder();
const int compactificationOrder = serialCoordinateSpace.GetMaxElementOrder();
const int dimension = fem.mesh->Dimension();
const int dimension = fem.mesh->Dimension();
fem.compactificationFec = std::make_unique<mfem::H1_FECollection>(
compactificationOrder, dimension
);
fem.compactificationFec = std::make_unique<mfem::H1_FECollection>(compactificationOrder, dimension);
fem.compactificationFes = std::make_unique<mfem::ParFiniteElementSpace>(
fem.mesh.get(), fem.compactificationFec.get()
);
fem.compactificationFes =
std::make_unique<mfem::ParFiniteElementSpace>(fem.mesh.get(), fem.compactificationFec.get());
mfem::ParGridFunction distributedCoordinate(
fem.mesh.get(), &serialCoordinate, meshPartitioning.get()
);
mfem::ParGridFunction distributedCoordinate(fem.mesh.get(), &serialCoordinate, meshPartitioning.get());
if (distributedCoordinate.Size() !=
fem.compactificationFes->GetVSize()) {
if (distributedCoordinate.Size() != fem.compactificationFes->GetVSize()) {
throw std::runtime_error(
"Distributed exterior coordinate does not match the "
"constructed parallel finite-element space."
);
}
fem.compactificationCoordinate =
std::make_unique<mfem::ParGridFunction>(
fem.compactificationFes.get()
);
fem.compactificationCoordinate = std::make_unique<mfem::ParGridFunction>(fem.compactificationFes.get());
*fem.compactificationCoordinate = distributedCoordinate;
double localMinimum = std::numeric_limits<double>::infinity();
double localMinimum = std::numeric_limits<double>::infinity();
double localMaximum = -std::numeric_limits<double>::infinity();
double localMaximum = -std::numeric_limits<double>::infinity();
for (int index = 0; index < fem.compactificationCoordinate->Size();
++index) {
for (int index = 0; index < fem.compactificationCoordinate->Size(); ++index) {
const double value = (*fem.compactificationCoordinate)(index);
if (!std::isfinite(value)) {
throw std::runtime_error(
"Exterior coordinate contains a non-finite value."
);
throw std::runtime_error("Exterior coordinate contains a non-finite value.");
}
localMinimum = std::min(localMinimum, value);
@@ -160,20 +139,13 @@ namespace mean_field::fem {
double globalMinimum = 0.0;
double globalMaximum = 0.0;
MPI_Allreduce(
&localMinimum, &globalMinimum, 1, MPI_DOUBLE, MPI_MIN,
MPI_COMM_WORLD
);
MPI_Allreduce(&localMinimum, &globalMinimum, 1, MPI_DOUBLE, MPI_MIN, MPI_COMM_WORLD);
MPI_Allreduce(
&localMaximum, &globalMaximum, 1, MPI_DOUBLE, MPI_MAX,
MPI_COMM_WORLD
);
MPI_Allreduce(&localMaximum, &globalMaximum, 1, MPI_DOUBLE, MPI_MAX, MPI_COMM_WORLD);
constexpr double coordinateTolerance = 1.0e-12;
if (globalMinimum < -coordinateTolerance ||
globalMaximum > 1.0 + coordinateTolerance) {
if (globalMinimum < -coordinateTolerance || globalMaximum > 1.0 + coordinateTolerance) {
throw std::runtime_error(
"Exterior coordinate lies outside the expected "
"interval [0, 1]."
@@ -188,59 +160,45 @@ namespace mean_field::fem {
// Gravity potential: scalar L2
// ---------------------------------------------------------------------
fem.gravityPotentialFec =
GravityField::make_fec<GravityPotential>(dimension);
fem.gravityPotentialFec = GravityField::make_fec<GravityPotential>(dimension);
fem.gravityPotentialFes = GravityField::make_fespace<GravityPotential>(
*fem.mesh, *fem.gravityPotentialFec
);
fem.gravityPotentialFes = GravityField::make_fespace<GravityPotential>(*fem.mesh, *fem.gravityPotentialFec);
// ---------------------------------------------------------------------
// Gravity flux: H(div)/RT. Basis choices are encoded by field.mfem.
// ---------------------------------------------------------------------
fem.gravityFluxFec = GravityField::make_fec<GravityFlux>(dimension);
fem.gravityFluxFec = GravityField::make_fec<GravityFlux>(dimension);
fem.gravityFluxFes = GravityField::make_fespace<GravityFlux>(
*fem.mesh, *fem.gravityFluxFec
);
fem.gravityFluxFes = GravityField::make_fespace<GravityFlux>(*fem.mesh, *fem.gravityFluxFec);
// ---------------------------------------------------------------------
// Displacement: vector H1. Ordering is encoded by field.mfem.
// ---------------------------------------------------------------------
fem.displacementFec =
DisplacementField::make_fec<DisplacementVector>(dimension);
fem.displacementFec = DisplacementField::make_fec<DisplacementVector>(dimension);
fem.displacementFes =
DisplacementField::make_fespace<DisplacementVector>(
*fem.mesh, *fem.displacementFec
);
fem.displacementFes = DisplacementField::make_fespace<DisplacementVector>(*fem.mesh, *fem.displacementFec);
fem.displacement =
std::make_unique<mfem::ParGridFunction>(fem.displacementFes.get());
fem.displacement = std::make_unique<mfem::ParGridFunction>(fem.displacementFes.get());
*fem.displacement = 0.0;
*fem.displacement = 0.0;
// ---------------------------------------------------------------------
// Density: scalar discontinuous L2
// ---------------------------------------------------------------------
fem.densityFec = DensityField::make_fec<DensityScalar>(dimension);
fem.densityFec = DensityField::make_fec<DensityScalar>(dimension);
fem.densityFes = DensityField::make_fespace<DensityScalar>(
*fem.mesh, *fem.densityFec
);
fem.densityFes = DensityField::make_fespace<DensityScalar>(*fem.mesh, *fem.densityFec);
// ---------------------------------------------------------------------
// Specific enthalpy: scalar continuous H1
// ---------------------------------------------------------------------
fem.enthalpyFec = EnthalpyField::make_fec<EnthalpyScalar>(dimension);
fem.enthalpyFec = EnthalpyField::make_fec<EnthalpyScalar>(dimension);
fem.enthalpyFes = EnthalpyField::make_fespace<EnthalpyScalar>(
*fem.mesh, *fem.enthalpyFec
);
fem.enthalpyFes = EnthalpyField::make_fespace<EnthalpyScalar>(*fem.mesh, *fem.enthalpyFec);
// =====================================================================
// Section 4: Domain mapping
@@ -248,21 +206,16 @@ namespace mean_field::fem {
auto [stellarRadiusReference, infinityRadiusReference] =
utils::discover_bounds(fem.mesh.get(), 3)
.or_else(
[](const boundary::BoundsError &)
-> std::expected<
boundary::Bounds, boundary::BoundsError> {
throw std::runtime_error(
"Unable to determine vacuum-domain reference "
"boundaries."
);
}
)
.or_else([](const boundary::BoundsError &) -> std::expected<boundary::Bounds, boundary::BoundsError> {
throw std::runtime_error(
"Unable to determine vacuum-domain reference "
"boundaries."
);
})
.value();
fem.mapping = std::make_unique<mapping::DomainMapper>(
*fem.displacement, stellarRadiusReference, infinityRadiusReference
);
fem.mapping =
std::make_unique<mapping::DomainMapper>(*fem.displacement, stellarRadiusReference, infinityRadiusReference);
// =====================================================================
// Section 5: Block offsets
@@ -278,17 +231,14 @@ namespace mean_field::fem {
fem.blockTrueOffsets[1] = fem.displacementFes->GetTrueVSize();
fem.blockTrueOffsets[2] =
fem.blockTrueOffsets[1] + fem.densityFes->GetTrueVSize();
fem.blockTrueOffsets[2] = fem.blockTrueOffsets[1] + fem.densityFes->GetTrueVSize();
fem.gravityBlockTrueOffsets.SetSize(3);
fem.gravityBlockTrueOffsets[0] = 0;
fem.gravityBlockTrueOffsets[1] = fem.gravityFluxFes->GetTrueVSize();
fem.gravityBlockTrueOffsets[2] =
fem.gravityBlockTrueOffsets[1] +
fem.gravityPotentialFes->GetTrueVSize();
fem.gravityBlockTrueOffsets[2] = fem.gravityBlockTrueOffsets[1] + fem.gravityPotentialFes->GetTrueVSize();
// =====================================================================
// Section 6: Multipole data
@@ -306,10 +256,7 @@ namespace mean_field::fem {
fem.essentialDisplacementTdofs.SetSize(0);
populate_element_mask(
fem.mesh.get(), utils::DOMAINS::STELLAR,
fem.gravityContext.stellar_mask
);
populate_element_mask(fem.mesh.get(), utils::DOMAINS::STELLAR, fem.gravityContext.stellar_mask);
const int boundaryAttributeCount = fem.mesh->bdr_attributes.Max();
@@ -317,21 +264,18 @@ namespace mean_field::fem {
fem.boundaryContext.stellar_bounds.SetSize(boundaryAttributeCount);
fem.boundaryContext.inf_bounds = 0;
fem.boundaryContext.stellar_bounds = 0;
fem.boundaryContext.inf_bounds = 0;
fem.boundaryContext.stellar_bounds = 0;
fem.boundaryContext.inf_bounds
[static_cast<int>(boundary::Boundaries::INF_SURFACE) - 1] = 1;
fem.boundaryContext.inf_bounds[static_cast<int>(boundary::Boundaries::INF_SURFACE) - 1] = 1;
fem.boundaryContext.stellar_bounds
[static_cast<int>(boundary::Boundaries::STELLAR_SURFACE) - 1] = 1;
fem.boundaryContext.stellar_bounds[static_cast<int>(boundary::Boundaries::STELLAR_SURFACE) - 1] = 1;
// =====================================================================
// Section 8: Gravity solver context
// =====================================================================
fem.gravityContext.minres =
std::make_unique<mfem::MINRESSolver>(fem.mesh->GetComm());
fem.gravityContext.minres = std::make_unique<mfem::MINRESSolver>(fem.mesh->GetComm());
fem.gravityContext.minres->SetRelTol(1.0e-12);
fem.gravityContext.minres->SetAbsTol(1.0e-12);
@@ -343,13 +287,9 @@ namespace mean_field::fem {
fem.gravityContext.prec_Phi->SetPrintLevel(0);
fem.gravityContext.block_prec =
std::make_unique<mfem::BlockDiagonalPreconditioner>(
fem.gravityBlockTrueOffsets
);
std::make_unique<mfem::BlockDiagonalPreconditioner>(fem.gravityBlockTrueOffsets);
fem.gravityContext.minres->SetPreconditioner(
*fem.gravityContext.block_prec
);
fem.gravityContext.minres->SetPreconditioner(*fem.gravityContext.block_prec);
// =====================================================================
// Section 9: Vacuum true-DOF masks
@@ -358,202 +298,115 @@ namespace mean_field::fem {
{
mfem::Array<int> vacuumMask;
utils::populate_element_mask(
fem.mesh.get(), utils::DOMAINS::VACUUM, vacuumMask
);
utils::populate_element_mask(fem.mesh.get(), utils::DOMAINS::VACUUM, vacuumMask);
utils::populate_domain_tdofs(
fem.displacementFes.get(), vacuumMask,
fem.vacuumDisplacementTdofs
);
utils::populate_domain_tdofs(fem.displacementFes.get(), vacuumMask, fem.vacuumDisplacementTdofs);
utils::populate_domain_tdofs(
fem.densityFes.get(), vacuumMask, fem.vacuumDensityTdofs
);
utils::populate_domain_tdofs(fem.densityFes.get(), vacuumMask, fem.vacuumDensityTdofs);
utils::populate_domain_tdofs(
fem.enthalpyFes.get(), vacuumMask, fem.vacuumEnthalpyTdofs
);
utils::populate_domain_tdofs(fem.enthalpyFes.get(), vacuumMask, fem.vacuumEnthalpyTdofs);
}
// =====================================================================
// Section 10: Quadrature policy
// =====================================================================
const quadrature::QuadratureOptions &quadratureOptions =
args.quadrature;
const quadrature::QuadratureOptions &quadratureOptions = args.quadrature;
if (quadratureOptions.validation.reject_negative_boosts &&
quadratureOptions.global_boost < 0) {
throw std::invalid_argument(
"Global quadrature boost cannot be negative."
);
if (quadratureOptions.validation.reject_negative_boosts && quadratureOptions.global_boost < 0) {
throw std::invalid_argument("Global quadrature boost cannot be negative.");
}
quadrature::RuleSet quadratureRuleSet = quadrature::make_rule_set(
quadratureOptions.mode, quadratureOptions.global_boost
);
quadrature::RuleSet quadratureRuleSet =
quadrature::make_rule_set(quadratureOptions.mode, quadratureOptions.global_boost);
if (quadratureOptions.fallback_fixed_order.has_value()) {
if (*quadratureOptions.fallback_fixed_order < 0) {
throw std::invalid_argument(
"Fallback quadrature order cannot be negative."
);
throw std::invalid_argument("Fallback quadrature order cannot be negative.");
}
quadratureRuleSet.fallback.fixed_order =
quadratureOptions.fallback_fixed_order;
quadratureRuleSet.fallback.fixed_order = quadratureOptions.fallback_fixed_order;
}
auto apply_quadrature_options =
[&quadratureOptions](
quadrature::RuleControl &ruleControl,
const quadrature::QuadratureTermOptions &termOptions
) {
if (termOptions.fixed_order.has_value() &&
*termOptions.fixed_order < 0) {
throw std::invalid_argument(
"Fixed quadrature order cannot be negative."
);
}
auto apply_quadrature_options = [&quadratureOptions](
quadrature::RuleControl &ruleControl,
const quadrature::QuadratureTermOptions &termOptions
) {
if (termOptions.fixed_order.has_value() && *termOptions.fixed_order < 0) {
throw std::invalid_argument("Fixed quadrature order cannot be negative.");
}
if (quadratureOptions.validation.reject_negative_boosts &&
termOptions.additional_boost < 0) {
throw std::invalid_argument(
"Term quadrature boost cannot be negative."
);
}
if (quadratureOptions.validation.reject_negative_boosts && termOptions.additional_boost < 0) {
throw std::invalid_argument("Term quadrature boost cannot be negative.");
}
ruleControl.boost += termOptions.additional_boost;
ruleControl.boost += termOptions.additional_boost;
if (termOptions.fixed_order.has_value()) {
ruleControl.fixed_order = termOptions.fixed_order;
}
};
if (termOptions.fixed_order.has_value()) {
ruleControl.fixed_order = termOptions.fixed_order;
}
};
apply_quadrature_options(
quadratureRuleSet.gravity_hdiv_mass,
quadratureOptions.gravity_hdiv_mass
);
apply_quadrature_options(quadratureRuleSet.gravity_hdiv_mass, quadratureOptions.gravity_hdiv_mass);
apply_quadrature_options(
quadratureRuleSet.gravity_divergence,
quadratureOptions.gravity_divergence
);
apply_quadrature_options(quadratureRuleSet.gravity_divergence, quadratureOptions.gravity_divergence);
apply_quadrature_options(
quadratureRuleSet.gravity_source, quadratureOptions.gravity_source
);
apply_quadrature_options(quadratureRuleSet.gravity_source, quadratureOptions.gravity_source);
apply_quadrature_options(
quadratureRuleSet.gravity_boundary,
quadratureOptions.gravity_boundary
);
apply_quadrature_options(quadratureRuleSet.gravity_force, quadratureOptions.gravity_force);
apply_quadrature_options(
quadratureRuleSet.centrifugal, quadratureOptions.centrifugal
);
apply_quadrature_options(quadratureRuleSet.gravity_boundary, quadratureOptions.gravity_boundary);
apply_quadrature_options(
quadratureRuleSet.density_projection,
quadratureOptions.density_projection
);
apply_quadrature_options(quadratureRuleSet.centrifugal, quadratureOptions.centrifugal);
apply_quadrature_options(
quadratureRuleSet.eos_closure, quadratureOptions.eos_closure
);
apply_quadrature_options(quadratureRuleSet.density_projection, quadratureOptions.density_projection);
apply_quadrature_options(
quadratureRuleSet.hydrostatic_equilibrium,
quadratureOptions.hydrostatic_equilibrium
);
apply_quadrature_options(quadratureRuleSet.eos_closure, quadratureOptions.eos_closure);
apply_quadrature_options(
quadratureRuleSet.isobaric_surface,
quadratureOptions.isobaric_surface
);
apply_quadrature_options(quadratureRuleSet.hydrostatic_equilibrium, quadratureOptions.hydrostatic_equilibrium);
apply_quadrature_options(
quadratureRuleSet.mesh_extension, quadratureOptions.mesh_extension
);
apply_quadrature_options(quadratureRuleSet.isobaric_surface, quadratureOptions.isobaric_surface);
apply_quadrature_options(
quadratureRuleSet.mass_conservation,
quadratureOptions.mass_conservation
);
apply_quadrature_options(quadratureRuleSet.mesh_extension, quadratureOptions.mesh_extension);
apply_quadrature_options(
quadratureRuleSet.mass_normalization,
quadratureOptions.mass_normalization
);
apply_quadrature_options(quadratureRuleSet.mass_conservation, quadratureOptions.mass_conservation);
apply_quadrature_options(
quadratureRuleSet.center_of_mass, quadratureOptions.center_of_mass
);
apply_quadrature_options(quadratureRuleSet.mass_normalization, quadratureOptions.mass_normalization);
apply_quadrature_options(
quadratureRuleSet.quadrupole, quadratureOptions.quadrupole
);
apply_quadrature_options(quadratureRuleSet.center_of_mass, quadratureOptions.center_of_mass);
apply_quadrature_options(
quadratureRuleSet.gravitational_energy,
quadratureOptions.gravitational_energy
);
apply_quadrature_options(quadratureRuleSet.quadrupole, quadratureOptions.quadrupole);
apply_quadrature_options(
quadratureRuleSet.pressure_integral,
quadratureOptions.pressure_integral
);
apply_quadrature_options(quadratureRuleSet.gravitational_energy, quadratureOptions.gravitational_energy);
apply_quadrature_options(
quadratureRuleSet.pressure_force, quadratureOptions.pressure_force
);
apply_quadrature_options(quadratureRuleSet.pressure_integral, quadratureOptions.pressure_integral);
apply_quadrature_options(
quadratureRuleSet.virial, quadratureOptions.virial
);
apply_quadrature_options(quadratureRuleSet.pressure_force, quadratureOptions.pressure_force);
apply_quadrature_options(
quadratureRuleSet.error_norm, quadratureOptions.error_norm
);
apply_quadrature_options(quadratureRuleSet.virial, quadratureOptions.virial);
apply_quadrature_options(
quadratureRuleSet.roles.discretization,
quadratureOptions.roles.discretization
);
apply_quadrature_options(quadratureRuleSet.error_norm, quadratureOptions.error_norm);
apply_quadrature_options(
quadratureRuleSet.roles.preconditioner,
quadratureOptions.roles.preconditioner
);
apply_quadrature_options(quadratureRuleSet.roles.discretization, quadratureOptions.roles.discretization);
apply_quadrature_options(
quadratureRuleSet.roles.diagnostic,
quadratureOptions.roles.diagnostic
);
apply_quadrature_options(quadratureRuleSet.roles.preconditioner, quadratureOptions.roles.preconditioner);
apply_quadrature_options(
quadratureRuleSet.roles.projection,
quadratureOptions.roles.projection
);
apply_quadrature_options(quadratureRuleSet.roles.diagnostic, quadratureOptions.roles.diagnostic);
fem.quadratureFactory = std::make_unique<quadrature::RuleFactory>(
quadrature::Policy(std::move(quadratureRuleSet))
);
apply_quadrature_options(quadratureRuleSet.roles.projection, quadratureOptions.roles.projection);
fem.quadratureFactory =
std::make_unique<quadrature::RuleFactory>(quadrature::Policy(std::move(quadratureRuleSet)));
// =====================================================================
// Section 11: Stateless domain mapper
// =====================================================================
auto exteriorDomain = std::make_unique<
const mapping::compactification::KelvinCompactification>(
args.kelvin_options
);
auto exteriorDomain =
std::make_unique<const mapping::compactification::KelvinCompactification>(args.kelvin_options);
fem.domainMapperStateless =
std::make_unique<mapping::DomainMapperStateless>(
args.domain_mapper_options, std::move(exteriorDomain)
);
std::make_unique<mapping::DomainMapperStateless>(args.domain_mapper_options, std::move(exteriorDomain));
return fem;
}