feat(FieldDofMap): Completed FieldDofMap migration

also removed legacy BarotropicPolytrope implementation
This commit is contained in:
2026-08-29 08:56:36 -04:00
parent 177ae8b38a
commit 36adfa1174
104 changed files with 26967 additions and 26916 deletions

View File

@@ -6,6 +6,32 @@ module mean_field;
import :mapping.coefficients;
namespace {
using DomainSchema = mean_field::utils::domain::CoreEnvelopeVacuumDomainSchema;
mfem::Array<int> make_domain_marker(
const mfem::Mesh &mesh,
const mean_field::utils::DOMAINS domain
) {
switch (domain) {
case mean_field::utils::DOMAINS::CORE:
return mean_field::utils::domain::make_attribute_marker<
mean_field::utils::domain::Core, DomainSchema>(mesh);
case mean_field::utils::DOMAINS::ENVELOPE:
return mean_field::utils::domain::make_attribute_marker<
mean_field::utils::domain::Envelope, DomainSchema>(mesh);
case mean_field::utils::DOMAINS::ALL:
return mean_field::utils::domain::make_attribute_marker<
mean_field::utils::domain::All, DomainSchema>(mesh);
case mean_field::utils::DOMAINS::STELLAR:
return mean_field::utils::domain::make_attribute_marker<
mean_field::utils::domain::Stellar, DomainSchema>(mesh);
case mean_field::utils::DOMAINS::VACUUM:
return mean_field::utils::domain::make_attribute_marker<
mean_field::utils::domain::Vacuum, DomainSchema>(mesh);
}
MFEM_ABORT("Unsupported integration domain.");
}
template <typename FormT>
const mfem::IntegrationRule &get_density_rule(
const mean_field::fem::FEM &fem,
@@ -36,14 +62,16 @@ namespace mean_field::analysis {
mfem::LinearForm lf(fem.densityFes.get());
mfem::GridFunctionCoefficient gf_c(&gf);
double local_integral;
mfem::Array<int> elem_markers;
populate_element_mask(fem.mesh.get(), domain, elem_markers);
mfem::Array<int> elem_markers = make_domain_marker(*fem.mesh, domain);
const mfem::ElementTransformation &representative_transformation = *fem.mesh->GetElementTransformation(0);
const mfem::IntegrationRule &integration_rule =
get_density_rule<field::Density::Form::MassConservation>(fem, representative_transformation, {}, domain);
if (fem.has_mapping() && coord_space == mapping::COORDINATE_SPACE::PHYSICAL) {
mapping::MappedScalarCoefficient mapped_gf_c(*fem.mapping, gf_c);
mapping::MappedScalarCoefficient mapped_gf_c(
*fem.domainMapperStateless, *fem.displacement,
*fem.compactificationCoordinate, gf_c
);
// ReSharper disable once CppDFAMemoryLeak // Disabled because MFEM
// takes ownership so memory is not leaked
@@ -78,12 +106,17 @@ namespace mean_field::analysis {
const mfem::GridFunction &rho
) {
const int dim = fem.mesh->Dimension();
mapping::GridFunctionMappingEvaluator mapping_evaluator(
*fem.domainMapperStateless, *fem.displacement,
*fem.compactificationCoordinate
);
mfem::Vector local_com(dim);
local_com = 0.0;
double local_mass = 0.0;
for (int i = 0; i < fem.mesh->GetNE(); ++i) {
if (fem.mesh->GetAttribute(i) == 3)
if (!DomainSchema::template attribute_belongs_to<utils::domain::Stellar>(
fem.mesh->GetAttribute(i)))
continue;
mfem::ElementTransformation *trans = fem.mesh->GetElementTransformation(i);
const mfem::IntegrationRule &ir = get_density_rule<field::Density::Form::CenterOfMass>(
@@ -94,18 +127,16 @@ namespace mean_field::analysis {
const mfem::IntegrationPoint &ip = ir.IntPoint(j);
trans->SetIntPoint(&ip);
double weight = trans->Weight() * ip.weight;
if (fem.has_mapping()) {
weight *= fem.mapping->ComputeDetJ(*trans, ip);
}
mapping::VolumeMappingContext mapping_context;
MFEM_VERIFY(
mapping_evaluator.EvaluateVolume(*trans, ip, mapping_context) ==
mapping::MappingStatus::valid,
"Center-of-mass integration encountered an invalid mapping."
);
const double weight = mapping_context.quadrature.weight;
double rho_val = rho.GetValue(i, ip);
mfem::Vector phys_point(dim);
if (fem.has_mapping()) {
fem.mapping->GetPhysicalPoint(*trans, ip, phys_point);
} else {
trans->Transform(ip, phys_point);
}
const mfem::Vector &phys_point = mapping_context.mapping.physical_position;
const double mass_term = rho_val * weight;
local_mass += mass_term;
@@ -151,7 +182,10 @@ namespace mean_field::analysis {
std::unique_ptr<mfem::Coefficient> s2_coeff;
if (fem.has_mapping()) {
s2_coeff = std::make_unique<mapping::PhysicalPositionFunctionCoefficient>(*fem.mapping, s2_func);
s2_coeff = std::make_unique<mapping::PhysicalPositionFunctionCoefficient>(
*fem.domainMapperStateless, *fem.displacement,
*fem.compactificationCoordinate, s2_func
);
} else {
s2_coeff = std::make_unique<mfem::FunctionCoefficient>(s2_func);
}
@@ -164,12 +198,15 @@ namespace mean_field::analysis {
const mfem::IntegrationRule &integration_rule = get_density_rule<field::Density::Form::Quadrupole>(
fem, representative_transformation, std::array<int, 1>{2}, utils::DOMAINS::STELLAR
);
mfem::Array<int> stellar_markers;
populate_element_mask(fem.mesh.get(), utils::DOMAINS::STELLAR, stellar_markers);
mfem::Array<int> stellar_markers =
utils::domain::make_attribute_marker<utils::domain::Stellar, DomainSchema>(*fem.mesh);
double local_I = 0.0;
if (fem.has_mapping()) {
mapping::MappedScalarCoefficient mapped_integrand(*fem.mapping, I_integrand);
mapping::MappedScalarCoefficient mapped_integrand(
*fem.domainMapperStateless, *fem.displacement,
*fem.compactificationCoordinate, I_integrand
);
auto *integrator = new mfem::DomainLFIntegrator(mapped_integrand);
integrator->SetIntRule(&integration_rule);
I_lf.AddDomainIntegrator(integrator, stellar_markers);
@@ -201,23 +238,21 @@ namespace mean_field::analysis {
}
double local_volume = 0.0;
mapping::GridFunctionMappingEvaluator mapping_evaluator(
*fem.domainMapperStateless, *fem.displacement,
*fem.compactificationCoordinate
);
for (int e = 0; e < mesh.GetNE(); ++e) {
const int attr = mesh.GetAttribute(e);
switch (domain) {
case utils::DOMAINS::ALL:
break;
case utils::DOMAINS::STELLAR:
if (attr == 3)
continue;
break;
case utils::DOMAINS::VACUUM:
if (attr != 3)
continue;
break;
default:
MFEM_ABORT("Unsupported domain type for volume computation.");
}
const bool selected =
domain == utils::DOMAINS::ALL ||
(domain == utils::DOMAINS::STELLAR &&
DomainSchema::template attribute_belongs_to<utils::domain::Stellar>(attr)) ||
(domain == utils::DOMAINS::VACUUM &&
DomainSchema::template attribute_belongs_to<utils::domain::Vacuum>(attr));
if (!selected)
continue;
mfem::ElementTransformation *T = mesh.GetElementTransformation(e);
const mfem::IntegrationRule &ir =
get_density_rule<field::Density::Form::MassConservation>(fem, *T, {}, domain);
@@ -229,7 +264,13 @@ namespace mean_field::analysis {
double dV = ip.weight * T->Weight();
if (physical) {
dV *= std::fabs(fem.mapping->ComputeDetJ(*T, ip));
mapping::VolumeMappingContext context;
MFEM_VERIFY(
mapping_evaluator.EvaluateVolume(*T, ip, context) ==
mapping::MappingStatus::valid,
"Mesh-volume integration encountered an invalid mapping."
);
dV = context.quadrature.weight;
}
local_volume += dV;

View File

@@ -21,393 +21,361 @@ import :utils.misc;
import :utils.user;
namespace mean_field::fem {
FEM setup_fem(
const std::string &filename,
const utils::Args &args,
const int extraRefine
) {
FEM fem;
FEM setup_fem(const std::string &filename, const utils::Args &args,
const int extraRefine) {
FEM fem;
using GravityPotential = field::Gravity::Potential;
using GravityFlux = field::Gravity::Flux;
using DisplacementVector = field::Displacement::Vector;
using DensityScalar = field::Density::Scalar;
using EnthalpyScalar = field::Enthalpy::Scalar;
using GravityPotential = field::Gravity::Potential;
using GravityFlux = field::Gravity::Flux;
using DisplacementVector = field::Displacement::Vector;
using DensityScalar = field::Density::Scalar;
using EnthalpyScalar = field::Enthalpy::Scalar;
using DomainSchema = utils::domain::CoreEnvelopeVacuumDomainSchema;
// =====================================================================
// Section 1: Mesh construction
// =====================================================================
// =====================================================================
// Section 1: Mesh construction
// =====================================================================
fem.smesh = stroid::IO::LoadStroidMesh(filename).value();
fem.smesh = stroid::IO::LoadStroidMesh(filename).value();
if (extraRefine > 0) {
stroid::refinement::UniformRefinement(fem.smesh, extraRefine);
}
if (extraRefine > 0) {
stroid::refinement::UniformRefinement(fem.smesh, extraRefine);
}
int mpiSize = 1;
MPI_Comm_size(MPI_COMM_WORLD, &mpiSize);
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();
fem.mesh->EnsureNodes();
// =====================================================================
// Section 2: Exterior compactification coordinate
// =====================================================================
// =====================================================================
// Section 2: Exterior compactification coordinate
// =====================================================================
if (fem.smesh.exterior_coordinate == nullptr) {
throw std::runtime_error("Exterior coordinate not set.");
}
if (fem.smesh.exterior_coordinate == nullptr) {
throw std::runtime_error("Exterior coordinate not set.");
}
if (fem.smesh.exterior_coordinate->space == nullptr) {
throw std::runtime_error("Space for exterior coordinate not set.");
}
if (fem.smesh.exterior_coordinate->space == nullptr) {
throw std::runtime_error("Space for exterior coordinate not set.");
}
if (fem.smesh.exterior_coordinate->values == nullptr) {
throw std::runtime_error("Values for exterior coordinate not set.");
}
if (fem.smesh.exterior_coordinate->values == nullptr) {
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(
"Exterior coordinate values are not associated with the "
"supplied finite-element space."
);
}
if (serialCoordinate.FESpace() != &serialCoordinateSpace) {
throw std::runtime_error(
"Exterior coordinate values are not associated with the "
"supplied finite-element space.");
}
if (serialCoordinateSpace.GetMesh() != fem.smesh.mesh.get()) {
throw std::runtime_error(
"Exterior coordinate space is not associated with the "
"loaded STROID mesh."
);
}
if (serialCoordinateSpace.GetMesh() != fem.smesh.mesh.get()) {
throw std::runtime_error(
"Exterior coordinate space is not associated with the "
"loaded STROID mesh.");
}
if (serialCoordinateSpace.GetVDim() != 1) {
throw std::runtime_error("Exterior coordinate must be a scalar field.");
}
if (serialCoordinateSpace.GetVDim() != 1) {
throw std::runtime_error("Exterior coordinate must be a scalar field.");
}
if (serialCoordinate.Size() != serialCoordinateSpace.GetVSize()) {
throw std::runtime_error(
"Exterior coordinate value count does not match its "
"finite-element space."
);
}
if (serialCoordinate.Size() != serialCoordinateSpace.GetVSize()) {
throw std::runtime_error(
"Exterior coordinate value count does not match its "
"finite-element space.");
}
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()) {
throw std::runtime_error(
"Distributed exterior coordinate does not match the "
"constructed parallel finite-element space."
);
}
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;
*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) {
const double value = (*fem.compactificationCoordinate)(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.");
}
localMinimum = std::min(localMinimum, value);
localMaximum = std::max(localMaximum, value);
}
double globalMinimum = 0.0;
double globalMaximum = 0.0;
MPI_Allreduce(&localMinimum, &globalMinimum, 1, MPI_DOUBLE, MPI_MIN, 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) {
throw std::runtime_error(
"Exterior coordinate lies outside the expected "
"interval [0, 1]."
);
}
// =====================================================================
// Section 3: Compile-time field realization
// =====================================================================
// ---------------------------------------------------------------------
// Gravity potential: scalar L2
// ---------------------------------------------------------------------
fem.gravityPotentialFec = GravityField::make_fec<GravityPotential>(dimension);
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.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.displacementFes = DisplacementField::make_fespace<DisplacementVector>(*fem.mesh, *fem.displacementFec);
fem.displacement = std::make_unique<mfem::ParGridFunction>(fem.displacementFes.get());
*fem.displacement = 0.0;
// ---------------------------------------------------------------------
// Density: scalar discontinuous L2
// ---------------------------------------------------------------------
fem.densityFec = DensityField::make_fec<DensityScalar>(dimension);
fem.densityFes = DensityField::make_fespace<DensityScalar>(*fem.mesh, *fem.densityFec);
// ---------------------------------------------------------------------
// Specific enthalpy: scalar continuous H1
// ---------------------------------------------------------------------
fem.enthalpyFec = EnthalpyField::make_fec<EnthalpyScalar>(dimension);
fem.enthalpyFes = EnthalpyField::make_fespace<EnthalpyScalar>(*fem.mesh, *fem.enthalpyFec);
// =====================================================================
// Section 4: Domain mapping
// =====================================================================
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."
);
})
.value();
fem.mapping =
std::make_unique<mapping::DomainMapper>(*fem.displacement, stellarRadiusReference, infinityRadiusReference);
// =====================================================================
// Section 5: Block offsets
//
// Legacy layouts only. New coupled operators use :utils.blocks forms.
//
// Main system: [Displacement | Density]
// Gravity system: [Flux | Potential]
// =====================================================================
fem.blockTrueOffsets.SetSize(3);
fem.blockTrueOffsets[0] = 0;
fem.blockTrueOffsets[1] = fem.displacementFes->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();
// =====================================================================
// Section 6: Multipole data
// =====================================================================
fem.com.SetSize(dimension);
fem.com = 0.0;
fem.Q.SetSize(dimension, dimension);
fem.Q = 0.0;
// =====================================================================
// Section 7: Essential boundaries and domain masks
// =====================================================================
fem.essentialDisplacementTdofs.SetSize(0);
populate_element_mask(fem.mesh.get(), utils::DOMAINS::STELLAR, fem.gravityContext.stellar_mask);
const int boundaryAttributeCount = fem.mesh->bdr_attributes.Max();
fem.boundaryContext.inf_bounds.SetSize(boundaryAttributeCount);
fem.boundaryContext.stellar_bounds.SetSize(boundaryAttributeCount);
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.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->SetRelTol(1.0e-12);
fem.gravityContext.minres->SetAbsTol(1.0e-12);
fem.gravityContext.minres->SetMaxIter(1000);
fem.gravityContext.minres->SetPrintLevel(0);
fem.gravityContext.prec_Phi = std::make_unique<mfem::HypreBoomerAMG>();
fem.gravityContext.prec_Phi->SetPrintLevel(0);
fem.gravityContext.block_prec =
std::make_unique<mfem::BlockDiagonalPreconditioner>(fem.gravityBlockTrueOffsets);
fem.gravityContext.minres->SetPreconditioner(*fem.gravityContext.block_prec);
// =====================================================================
// Section 9: Vacuum true-DOF masks
// =====================================================================
{
mfem::Array<int> 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.densityFes.get(), vacuumMask, fem.vacuumDensityTdofs);
utils::populate_domain_tdofs(fem.enthalpyFes.get(), vacuumMask, fem.vacuumEnthalpyTdofs);
}
// =====================================================================
// Section 10: Quadrature policy
// =====================================================================
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.");
}
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.");
}
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.");
}
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;
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_divergence, quadratureOptions.gravity_divergence);
apply_quadrature_options(quadratureRuleSet.gravity_source, quadratureOptions.gravity_source);
apply_quadrature_options(quadratureRuleSet.gravity_force, quadratureOptions.gravity_force);
apply_quadrature_options(quadratureRuleSet.gravity_boundary, quadratureOptions.gravity_boundary);
apply_quadrature_options(quadratureRuleSet.centrifugal, quadratureOptions.centrifugal);
apply_quadrature_options(quadratureRuleSet.density_projection, quadratureOptions.density_projection);
apply_quadrature_options(quadratureRuleSet.eos_closure, quadratureOptions.eos_closure);
apply_quadrature_options(quadratureRuleSet.hydrostatic_equilibrium, quadratureOptions.hydrostatic_equilibrium);
apply_quadrature_options(quadratureRuleSet.isobaric_surface, quadratureOptions.isobaric_surface);
apply_quadrature_options(quadratureRuleSet.mesh_extension, quadratureOptions.mesh_extension);
apply_quadrature_options(quadratureRuleSet.mass_conservation, quadratureOptions.mass_conservation);
apply_quadrature_options(quadratureRuleSet.mass_normalization, quadratureOptions.mass_normalization);
apply_quadrature_options(quadratureRuleSet.center_of_mass, quadratureOptions.center_of_mass);
apply_quadrature_options(quadratureRuleSet.quadrupole, quadratureOptions.quadrupole);
apply_quadrature_options(quadratureRuleSet.gravitational_energy, quadratureOptions.gravitational_energy);
apply_quadrature_options(quadratureRuleSet.pressure_integral, quadratureOptions.pressure_integral);
apply_quadrature_options(quadratureRuleSet.pressure_force, quadratureOptions.pressure_force);
apply_quadrature_options(quadratureRuleSet.virial, quadratureOptions.virial);
apply_quadrature_options(quadratureRuleSet.error_norm, quadratureOptions.error_norm);
apply_quadrature_options(quadratureRuleSet.roles.discretization, quadratureOptions.roles.discretization);
apply_quadrature_options(quadratureRuleSet.roles.preconditioner, quadratureOptions.roles.preconditioner);
apply_quadrature_options(quadratureRuleSet.roles.diagnostic, quadratureOptions.roles.diagnostic);
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);
fem.domainMapperStateless =
std::make_unique<mapping::DomainMapperStateless>(args.domain_mapper_options, std::move(exteriorDomain));
return fem;
if (!std::isfinite(value)) {
throw std::runtime_error(
"Exterior coordinate contains a non-finite value.");
}
} // namespace mean_field::fem
localMinimum = std::min(localMinimum, value);
localMaximum = std::max(localMaximum, value);
}
double globalMinimum = 0.0;
double globalMaximum = 0.0;
MPI_Allreduce(&localMinimum, &globalMinimum, 1, MPI_DOUBLE, MPI_MIN,
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) {
throw std::runtime_error("Exterior coordinate lies outside the expected "
"interval [0, 1].");
}
// =====================================================================
// Section 3: Compile-time field realization
// =====================================================================
// ---------------------------------------------------------------------
// Gravity potential: scalar L2
// ---------------------------------------------------------------------
fem.gravityPotentialFec = GravityField::make_fec<GravityPotential>(dimension);
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.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.displacementFes = DisplacementField::make_fespace<DisplacementVector>(
*fem.mesh, *fem.displacementFec);
fem.displacement =
std::make_unique<mfem::ParGridFunction>(fem.displacementFes.get());
*fem.displacement = 0.0;
// ---------------------------------------------------------------------
// Density: scalar discontinuous L2
// ---------------------------------------------------------------------
fem.densityFec = DensityField::make_fec<DensityScalar>(dimension);
fem.densityFes =
DensityField::make_fespace<DensityScalar>(*fem.mesh, *fem.densityFec);
// ---------------------------------------------------------------------
// Specific enthalpy: scalar continuous H1
// ---------------------------------------------------------------------
fem.enthalpyFec = EnthalpyField::make_fec<EnthalpyScalar>(dimension);
fem.enthalpyFes =
EnthalpyField::make_fespace<EnthalpyScalar>(*fem.mesh, *fem.enthalpyFec);
// =====================================================================
// Section 4: Multipole data
// =====================================================================
fem.com.SetSize(dimension);
fem.com = 0.0;
fem.Q.SetSize(dimension, dimension);
fem.Q = 0.0;
// =====================================================================
// Section 5: Boundary markers
// =====================================================================
const int boundaryAttributeCount = fem.mesh->bdr_attributes.Max();
fem.boundaryContext.inf_bounds.SetSize(boundaryAttributeCount);
fem.boundaryContext.stellar_bounds.SetSize(boundaryAttributeCount);
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
.stellar_bounds[static_cast<int>(boundary::Boundaries::STELLAR_SURFACE) -
1] = 1;
// =====================================================================
// Section 7: Quadrature policy
// =====================================================================
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.");
}
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.");
}
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.");
}
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;
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_divergence,
quadratureOptions.gravity_divergence);
apply_quadrature_options(quadratureRuleSet.gravity_source,
quadratureOptions.gravity_source);
apply_quadrature_options(quadratureRuleSet.gravity_force,
quadratureOptions.gravity_force);
apply_quadrature_options(quadratureRuleSet.gravity_boundary,
quadratureOptions.gravity_boundary);
apply_quadrature_options(quadratureRuleSet.centrifugal,
quadratureOptions.centrifugal);
apply_quadrature_options(quadratureRuleSet.density_projection,
quadratureOptions.density_projection);
apply_quadrature_options(quadratureRuleSet.eos_closure,
quadratureOptions.eos_closure);
apply_quadrature_options(quadratureRuleSet.hydrostatic_equilibrium,
quadratureOptions.hydrostatic_equilibrium);
apply_quadrature_options(quadratureRuleSet.isobaric_surface,
quadratureOptions.isobaric_surface);
apply_quadrature_options(quadratureRuleSet.mesh_extension,
quadratureOptions.mesh_extension);
apply_quadrature_options(quadratureRuleSet.mass_conservation,
quadratureOptions.mass_conservation);
apply_quadrature_options(quadratureRuleSet.mass_normalization,
quadratureOptions.mass_normalization);
apply_quadrature_options(quadratureRuleSet.center_of_mass,
quadratureOptions.center_of_mass);
apply_quadrature_options(quadratureRuleSet.quadrupole,
quadratureOptions.quadrupole);
apply_quadrature_options(quadratureRuleSet.gravitational_energy,
quadratureOptions.gravitational_energy);
apply_quadrature_options(quadratureRuleSet.pressure_integral,
quadratureOptions.pressure_integral);
apply_quadrature_options(quadratureRuleSet.pressure_force,
quadratureOptions.pressure_force);
apply_quadrature_options(quadratureRuleSet.virial, quadratureOptions.virial);
apply_quadrature_options(quadratureRuleSet.error_norm,
quadratureOptions.error_norm);
apply_quadrature_options(quadratureRuleSet.roles.discretization,
quadratureOptions.roles.discretization);
apply_quadrature_options(quadratureRuleSet.roles.preconditioner,
quadratureOptions.roles.preconditioner);
apply_quadrature_options(quadratureRuleSet.roles.diagnostic,
quadratureOptions.roles.diagnostic);
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);
MFEM_VERIFY(
args.domain_mapper_options.vacuum_element_attribute ==
DomainSchema::template material_attribute<utils::domain::Vacuum>(),
"The domain-mapper compactification attribute must match the vacuum "
"material registered by the "
"production domain schema.");
fem.domainMapperStateless = std::make_unique<mapping::DomainMapper>(
args.domain_mapper_options, std::move(exteriorDomain));
return fem;
}
} // namespace mean_field::fem

View File

@@ -4,7 +4,12 @@ module;
module mean_field;
namespace mean_field::integrators {
AdvectionIntegrator::AdvectionIntegrator(const mapping::DomainMapper &map) : m_map(map) {
AdvectionIntegrator::AdvectionIntegrator(
const mapping::DomainMapper &mapper,
const mfem::GridFunction &displacement,
const mfem::GridFunction &compactification_coordinate
)
: m_mapping(mapper, displacement, compactification_coordinate) {
}
void AdvectionIntegrator::AssembleElementVector(
@@ -13,6 +18,8 @@ namespace mean_field::integrators {
const mfem::Array<const mfem::Vector *> &elfun,
const mfem::Array<mfem::Vector *> &elvec
) {
m_mapping.InvalidateCache();
if (utils::is_vacuum(Tr, elvec)) {
return;
}
@@ -44,7 +51,7 @@ namespace mean_field::integrators {
const mfem::IntegrationPoint &ip = ir->IntPoint(q);
Tr.SetIntPoint(&ip);
auto [J_inv, detJ, weight] = m_map.GetQuadratureContext(Tr, ip);
auto [J_inv, detJ, weight] = m_mapping.GetQuadratureContext(Tr, ip);
fe_v->CalcShape(ip, shape_v);
fe_v->CalcDShape(ip, dshape_v_ref);
@@ -93,6 +100,8 @@ namespace mean_field::integrators {
const mfem::Array<const mfem::Vector *> &elfun,
const mfem::Array2D<mfem::DenseMatrix *> &elmats
) {
m_mapping.InvalidateCache();
const mfem::FiniteElement *fe_v = el[0];
const mfem::FiniteElement *fe_rho = el[1];
@@ -120,7 +129,7 @@ namespace mean_field::integrators {
const mfem::IntegrationPoint &ip = ir->IntPoint(q);
Tr.SetIntPoint(&ip);
auto [J_inv, detJ, weight] = m_map.GetQuadratureContext(Tr, ip);
auto [J_inv, detJ, weight] = m_mapping.GetQuadratureContext(Tr, ip);
fe_v->CalcShape(ip, shape_v);
fe_v->CalcDShape(ip, dshape_v_ref);
@@ -208,4 +217,4 @@ namespace mean_field::integrators {
}
}
}
} // namespace mean_field::integrators
} // namespace mean_field::integrators

View File

@@ -4,10 +4,12 @@ module mean_field;
namespace mean_field::integrators {
CentrifugalForceIntegrator::CentrifugalForceIntegrator(
const mapping::DomainMapper &map,
const mapping::DomainMapper &mapper,
const mfem::GridFunction &displacement,
const mfem::GridFunction &compactification_coordinate,
const mfem::Vector &omega
)
: m_map(map),
: m_mapping(mapper, displacement, compactification_coordinate),
m_omega(3) {
MFEM_ASSERT(omega.Size() == 3, "Omega vector must be 3D");
m_omega = omega;
@@ -28,6 +30,8 @@ namespace mean_field::integrators {
const mfem::Array<const mfem::Vector *> &elfun,
const mfem::Array<mfem::Vector *> &elvec
) {
m_mapping.InvalidateCache();
if (utils::is_vacuum(Tr, elvec)) {
return;
}
@@ -64,12 +68,12 @@ namespace mean_field::integrators {
const mfem::IntegrationPoint &ip = ir->IntPoint(q);
Tr.SetIntPoint(&ip);
auto [J_inv, detJ, weight] = m_map.GetQuadratureContext(Tr, ip);
auto [J_inv, detJ, weight] = m_mapping.GetQuadratureContext(Tr, ip);
fe_v->CalcShape(ip, shape_v);
fe_rho->CalcShape(ip, shape_rho);
m_map.GetPhysicalPoint(Tr, ip, x_phys);
m_mapping.GetPhysicalPoint(Tr, ip, x_phys);
// ω x r
a(0) = m_omega(1) * x_phys(2) - m_omega(2) * x_phys(1);
@@ -100,6 +104,8 @@ namespace mean_field::integrators {
const mfem::Array<const mfem::Vector *> &elfun,
const mfem::Array2D<mfem::DenseMatrix *> &elmats
) {
m_mapping.InvalidateCache();
if (utils::is_vacuum(Tr, elmats)) {
return;
}
@@ -134,12 +140,12 @@ namespace mean_field::integrators {
const mfem::IntegrationPoint &ip = ir->IntPoint(q);
Tr.SetIntPoint(&ip);
auto [J_inv, detJ, weight] = m_map.GetQuadratureContext(Tr, ip);
auto [J_inv, detJ, weight] = m_mapping.GetQuadratureContext(Tr, ip);
fe_v->CalcShape(ip, shape_v);
fe_rho->CalcShape(ip, shape_rho);
m_map.GetPhysicalPoint(Tr, ip, x_phys);
m_mapping.GetPhysicalPoint(Tr, ip, x_phys);
// ω x r
a(0) = m_omega(1) * x_phys(2) - m_omega(2) * x_phys(1);
@@ -162,4 +168,4 @@ namespace mean_field::integrators {
}
}
}
} // namespace mean_field::integrators
} // namespace mean_field::integrators

View File

@@ -5,10 +5,12 @@ module mean_field;
namespace mean_field::integrators {
CoriolisIntegrator::CoriolisIntegrator(
const mapping::DomainMapper &map,
const mapping::DomainMapper &mapper,
const mfem::GridFunction &displacement,
const mfem::GridFunction &compactification_coordinate,
const mfem::Vector &omega
)
: m_map(map),
: m_mapping(mapper, displacement, compactification_coordinate),
m_omega(omega) {
m_omega_mat.SetSize(3, 3);
m_omega_mat = 0.0;
@@ -26,6 +28,8 @@ namespace mean_field::integrators {
const mfem::Array<const mfem::Vector *> &elfun,
const mfem::Array<mfem::Vector *> &elvec
) {
m_mapping.InvalidateCache();
if (utils::is_vacuum(Tr, elvec)) {
return;
}
@@ -55,7 +59,7 @@ namespace mean_field::integrators {
const mfem::IntegrationPoint &ip = ir->IntPoint(q);
Tr.SetIntPoint(&ip);
auto [J_inv, detJ, weight] = m_map.GetQuadratureContext(Tr, ip);
auto [J_inv, detJ, weight] = m_mapping.GetQuadratureContext(Tr, ip);
fe_v->CalcShape(ip, shape_v);
fe_rho->CalcShape(ip, shape_rho);
@@ -89,6 +93,7 @@ namespace mean_field::integrators {
const mfem::Array<const mfem::Vector *> &elfun,
const mfem::Array2D<mfem::DenseMatrix *> &elmats
) {
m_mapping.InvalidateCache();
const mfem::FiniteElement *fe_v = el[0];
const mfem::FiniteElement *fe_rho = el[1];
@@ -115,7 +120,7 @@ namespace mean_field::integrators {
const mfem::IntegrationPoint &ip = ir->IntPoint(q);
Tr.SetIntPoint(&ip);
auto [J_inv, detJ, weight] = m_map.GetQuadratureContext(Tr, ip);
auto [J_inv, detJ, weight] = m_mapping.GetQuadratureContext(Tr, ip);
fe_v->CalcShape(ip, shape_v);
fe_rho->CalcShape(ip, shape_rho);
@@ -163,4 +168,4 @@ namespace mean_field::integrators {
}
}
}
} // namespace mean_field::integrators
} // namespace mean_field::integrators

View File

@@ -14,10 +14,12 @@ namespace {
namespace mean_field::integrators {
GravityMomentumIntegrator::GravityMomentumIntegrator(
const mapping::DomainMapper &map,
const mapping::DomainMapper &mapper,
const mfem::GridFunction &displacement,
const mfem::GridFunction &compactification_coordinate,
const GravityForceJacobianMode jacobian_mode
)
: m_map(map),
: m_mapping(mapper, displacement, compactification_coordinate),
m_jacobian_mode(jacobian_mode) {
}
@@ -39,6 +41,8 @@ namespace mean_field::integrators {
const mfem::Array<const mfem::Vector *> &elfun,
const mfem::Array<mfem::Vector *> &elvec
) {
m_mapping.InvalidateCache();
if (utils::is_vacuum(Tr, elvec)) {
return;
}
@@ -123,7 +127,7 @@ namespace mean_field::integrators {
const mfem::IntegrationPoint &integration_point = integration_rule.IntPoint(q);
Tr.SetIntPoint(&integration_point);
const mapping::VolumeQuadratureContext context = m_map.GetQuadratureContext(Tr, integration_point);
const mapping::VolumeQuadratureContext context = m_mapping.GetQuadratureContext(Tr, integration_point);
velocity_element->CalcShape(integration_point, velocity_shape);
density_element->CalcShape(integration_point, density_shape);
@@ -152,6 +156,8 @@ namespace mean_field::integrators {
const mfem::Array<const mfem::Vector *> &elfun,
const mfem::Array2D<mfem::DenseMatrix *> &elmats
) {
m_mapping.InvalidateCache();
if (utils::is_vacuum(Tr, elmats)) {
return;
}
@@ -189,8 +195,8 @@ namespace mean_field::integrators {
MFEM_ABORT(
"Exact GravityForceIntegrator geometry Jacobian is unavailable "
"until "
"DomainMapper linearization is "
"implemented."
"the stateless mapping variation is wired into this legacy "
"integrator."
);
}
@@ -240,7 +246,7 @@ namespace mean_field::integrators {
const mfem::IntegrationPoint &integration_point = integration_rule.IntPoint(q);
Tr.SetIntPoint(&integration_point);
const mapping::VolumeQuadratureContext context = m_map.GetQuadratureContext(Tr, integration_point);
const mapping::VolumeQuadratureContext context = m_mapping.GetQuadratureContext(Tr, integration_point);
velocity_element->CalcShape(integration_point, velocity_shape);
density_element->CalcShape(integration_point, density_shape);
@@ -286,4 +292,4 @@ namespace mean_field::integrators {
}
}
}
} // namespace mean_field::integrators
} // namespace mean_field::integrators

View File

@@ -4,7 +4,12 @@ module;
module mean_field;
namespace mean_field::integrators {
ContinuityVolumeIntegrator::ContinuityVolumeIntegrator(const mapping::DomainMapper &map) : m_map(map) { };
ContinuityVolumeIntegrator::ContinuityVolumeIntegrator(
const mapping::DomainMapper &mapper,
const mfem::GridFunction &displacement,
const mfem::GridFunction &compactification_coordinate
)
: m_mapping(mapper, displacement, compactification_coordinate) { };
void ContinuityVolumeIntegrator::AssembleElementVector(
const mfem::Array<const mfem::FiniteElement *> &el,
@@ -12,6 +17,8 @@ namespace mean_field::integrators {
const mfem::Array<const mfem::Vector *> &elfun,
const mfem::Array<mfem::Vector *> &elvec
) {
m_mapping.InvalidateCache();
if (utils::is_vacuum(Tr, elvec)) {
return;
}
@@ -46,7 +53,7 @@ namespace mean_field::integrators {
const mfem::IntegrationPoint &ip = ir->IntPoint(q);
Tr.SetIntPoint(&ip);
auto [J_inv, detJ, weight] = m_map.GetQuadratureContext(Tr, ip);
auto [J_inv, detJ, weight] = m_mapping.GetQuadratureContext(Tr, ip);
fe_v->CalcShape(ip, shape_v);
fe_rho->CalcShape(ip, shape_rho);
@@ -82,6 +89,7 @@ namespace mean_field::integrators {
const mfem::Array<const mfem::Vector *> &elfun,
const mfem::Array2D<mfem::DenseMatrix *> &elmats
) {
m_mapping.InvalidateCache();
const mfem::FiniteElement *fe_v = el[0];
const mfem::FiniteElement *fe_rho = el[1];
@@ -115,7 +123,7 @@ namespace mean_field::integrators {
const mfem::IntegrationPoint &ip = ir->IntPoint(q);
Tr.SetIntPoint(&ip);
auto [J_inv, detJ, weight] = m_map.GetQuadratureContext(Tr, ip);
auto [J_inv, detJ, weight] = m_mapping.GetQuadratureContext(Tr, ip);
fe_v->CalcShape(ip, shape_v);
fe_rho->CalcShape(ip, shape_rho);
@@ -161,7 +169,12 @@ namespace mean_field::integrators {
}
}
ContinuityFaceIntegrator::ContinuityFaceIntegrator(const mapping::DomainMapper &map) : m_map(map) {
ContinuityFaceIntegrator::ContinuityFaceIntegrator(
const mapping::DomainMapper &mapper,
const mfem::GridFunction &displacement,
const mfem::GridFunction &compactification_coordinate
)
: m_mapping(mapper, displacement, compactification_coordinate) {
}
void ContinuityFaceIntegrator::AssembleFaceVector(
@@ -171,6 +184,8 @@ namespace mean_field::integrators {
const mfem::Array<const mfem::Vector *> &elfun,
const mfem::Array<mfem::Vector *> &elvect
) {
m_mapping.InvalidateCache();
const mfem::FiniteElement *fe_v_minus = el1[0];
const mfem::FiniteElement *fe_v_plus = el2[0];
@@ -195,9 +210,9 @@ namespace mean_field::integrators {
const int attr_minus = Tr.Elem1->Attribute;
const int attr_plus = (Tr.Elem2 != nullptr) ? Tr.Elem2->Attribute : -1;
constexpr int VACUUM_ATTR = 3;
if (attr_minus == VACUUM_ATTR || attr_plus == VACUUM_ATTR) {
using DomainSchema = utils::domain::CoreEnvelopeVacuumDomainSchema;
if (DomainSchema::template attribute_belongs_to<utils::domain::Vacuum>(attr_minus) ||
DomainSchema::template attribute_belongs_to<utils::domain::Vacuum>(attr_plus)) {
return; // No flux contribution for vacuum faces
}
@@ -228,7 +243,7 @@ namespace mean_field::integrators {
const mfem::IntegrationPoint &ip_minus = Tr.GetElement1IntPoint();
const mfem::IntegrationPoint &ip_plus = Tr.GetElement2IntPoint();
auto [n_unit, ds, v_dot_n_scale] = m_map.GetFaceQuadratureContext(Tr, face_ip);
auto [n_unit, ds, v_dot_n_scale] = m_mapping.GetFaceQuadratureContext(Tr, face_ip);
fe_v_minus->CalcShape(ip_minus, shape_v_minus);
fe_rho_minus->CalcShape(ip_minus, shape_rho_minus);
@@ -281,6 +296,8 @@ namespace mean_field::integrators {
const mfem::Array<const mfem::Vector *> &elfun,
const mfem::Array2D<mfem::DenseMatrix *> &elmats
) {
m_mapping.InvalidateCache();
const mfem::FiniteElement *fe_v_minus = el1[0];
const mfem::FiniteElement *fe_v_plus = el2[0];
const mfem::FiniteElement *fe_rho_minus = el1[1];
@@ -330,7 +347,7 @@ namespace mean_field::integrators {
const mfem::IntegrationPoint &ip_minus = Tr.GetElement1IntPoint();
const mfem::IntegrationPoint &ip_plus = Tr.GetElement2IntPoint();
auto [n_unit, ds, v_dot_n_scale] = m_map.GetFaceQuadratureContext(Tr, face_ip);
auto [n_unit, ds, v_dot_n_scale] = m_mapping.GetFaceQuadratureContext(Tr, face_ip);
fe_v_minus->CalcShape(ip_minus, shape_v_minus);
fe_rho_minus->CalcShape(ip_minus, shape_rho_minus);
@@ -399,10 +416,11 @@ namespace mean_field::integrators {
}
bool ContinuityFaceIntegrator::skip_face(const mfem::FaceElementTransformations &Tr) {
constexpr int VACUUM_ATTR = 3;
const int attr_minus = Tr.Elem1->Attribute;
const int attr_plus = (Tr.Elem2 != nullptr) ? Tr.Elem2->Attribute : -1;
if (attr_minus == VACUUM_ATTR || attr_plus == VACUUM_ATTR) {
using DomainSchema = utils::domain::CoreEnvelopeVacuumDomainSchema;
if (DomainSchema::template attribute_belongs_to<utils::domain::Vacuum>(attr_minus) ||
DomainSchema::template attribute_belongs_to<utils::domain::Vacuum>(attr_plus)) {
return true; // No flux contribution for vacuum faces
}
if (Tr.Elem2 == nullptr) {

View File

@@ -4,11 +4,13 @@ module mean_field;
namespace mean_field::integrators {
ViscosityIntegrator::ViscosityIntegrator(
const mapping::DomainMapper &map,
const mapping::DomainMapper &mapper,
const mfem::GridFunction &displacement,
const mfem::GridFunction &compactification_coordinate,
const double mu,
const int quad_boost
)
: m_map(map),
: m_mapping(mapper, displacement, compactification_coordinate),
m_mu(mu),
m_quad_boost(quad_boost) {
}
@@ -23,6 +25,8 @@ namespace mean_field::integrators {
const mfem::Array<const mfem::Vector *> &elfun,
const mfem::Array<mfem::Vector *> &elvec
) {
m_mapping.InvalidateCache();
if (utils::is_vacuum(Tr, elvec)) {
return;
}
@@ -56,7 +60,7 @@ namespace mean_field::integrators {
const mfem::IntegrationPoint &ip = ir->IntPoint(q);
Tr.SetIntPoint(&ip);
auto [J_inv, detJ, weight] = m_map.GetQuadratureContext(Tr, ip);
auto [J_inv, detJ, weight] = m_mapping.GetQuadratureContext(Tr, ip);
fe_v->CalcDShape(ip, dshape_v_ref);
mfem::Mult(dshape_v_ref, J_inv, dshape_v_phys);
@@ -102,6 +106,8 @@ namespace mean_field::integrators {
const mfem::Array<const mfem::Vector *> &elfun,
const mfem::Array2D<mfem::DenseMatrix *> &elmats
) {
m_mapping.InvalidateCache();
const mfem::FiniteElement *fe_v = el[0];
const mfem::FiniteElement *fe_rho = el[1];
@@ -130,7 +136,7 @@ namespace mean_field::integrators {
const mfem::IntegrationPoint &ip = ir->IntPoint(q);
Tr.SetIntPoint(&ip);
auto [J_inv, detJ, weight] = m_map.GetQuadratureContext(Tr, ip);
auto [J_inv, detJ, weight] = m_mapping.GetQuadratureContext(Tr, ip);
fe_v->CalcDShape(ip, dshape_v_ref);
mfem::Mult(dshape_v_ref, J_inv, dshape_v_phys);
@@ -164,4 +170,4 @@ namespace mean_field::integrators {
}
}
} // namespace mean_field::integrators
} // namespace mean_field::integrators

View File

@@ -9,11 +9,13 @@ namespace mean_field::mapping {
/// MappedScalarCoefficient ///
//////////////////////////////
MappedScalarCoefficient::MappedScalarCoefficient(
const DomainMapper &map,
const DomainMapper &mapper,
const mfem::GridFunction &displacement,
const mfem::GridFunction &compactification_coordinate,
Coefficient &coeff,
const COORDINATE_SPACE coord_space
)
: m_map(map),
: m_mapping(mapper, displacement, compactification_coordinate),
m_coeff(coeff),
m_coord_space(coord_space) { };
@@ -27,8 +29,12 @@ namespace mean_field::mapping {
switch (m_coord_space) {
case COORDINATE_SPACE::PHYSICAL: {
f_val = eval_at_point(m_coeff, T, ip);
const double detJ = m_map.ComputeDetJ(T, ip);
return f_val * fabs(detJ);
VolumeMappingContext context;
MFEM_VERIFY(
m_mapping.EvaluateVolume(T, ip, context) == MappingStatus::valid,
"Mapped scalar coefficient encountered an invalid mapping."
);
return f_val * std::abs(context.mapping.mapping_determinant);
}
case COORDINATE_SPACE::REFERENCE: {
f_val = m_coeff.Eval(T, ip);
@@ -50,21 +56,25 @@ namespace mean_field::mapping {
//////////////////////////////////
MappedDiffusionCoefficient::MappedDiffusionCoefficient(
const DomainMapper &map,
const DomainMapper &mapper,
const mfem::GridFunction &displacement,
const mfem::GridFunction &compactification_coordinate,
mfem::Coefficient &sigma,
const int dim
)
: MatrixCoefficient(dim),
m_map(map),
m_mapping(mapper, displacement, compactification_coordinate),
m_scalar(&sigma),
m_tensor(nullptr) { };
MappedDiffusionCoefficient::MappedDiffusionCoefficient(
const DomainMapper &map,
const DomainMapper &mapper,
const mfem::GridFunction &displacement,
const mfem::GridFunction &compactification_coordinate,
MatrixCoefficient &sigma
)
: MatrixCoefficient(sigma.GetHeight()),
m_map(map),
m_mapping(mapper, displacement, compactification_coordinate),
m_scalar(nullptr),
m_tensor(&sigma) { };
@@ -76,10 +86,13 @@ namespace mean_field::mapping {
const int dim = height;
T.SetIntPoint(&ip);
mfem::DenseMatrix J(dim, dim), JInv(dim, dim);
m_map.ComputeJacobian(T, J);
const double detJ = J.Det();
mfem::CalcInverse(J, JInv);
VolumeMappingContext context;
MFEM_VERIFY(
m_mapping.EvaluateVolume(T, ip, context) == MappingStatus::valid,
"Mapped diffusion coefficient encountered an invalid mapping."
);
const mfem::DenseMatrix &JInv = context.mapping.inverse_mapping_jacobian;
const double detJ = context.mapping.mapping_determinant;
if (m_scalar) {
const double sig_val = m_scalar->Eval(T, ip);
@@ -101,11 +114,13 @@ namespace mean_field::mapping {
/// MappedVectorCoefficient ///
///////////////////////////////
MappedVectorCoefficient::MappedVectorCoefficient(
const DomainMapper &map,
const DomainMapper &mapper,
const mfem::GridFunction &displacement,
const mfem::GridFunction &compactification_coordinate,
VectorCoefficient &coeff
)
: VectorCoefficient(coeff.GetVDim()),
m_map(map),
m_mapping(mapper, displacement, compactification_coordinate),
m_coeff(coeff) { };
void MappedVectorCoefficient::Eval(
@@ -116,9 +131,13 @@ namespace mean_field::mapping {
const int dim = vdim;
T.SetIntPoint(&ip);
mfem::DenseMatrix JInv(dim, dim);
m_map.ComputeInverseJacobian(T, JInv);
double detJ = m_map.ComputeDetJ(T, ip);
VolumeMappingContext context;
MFEM_VERIFY(
m_mapping.EvaluateVolume(T, ip, context) == MappingStatus::valid,
"Mapped vector coefficient encountered an invalid mapping."
);
const mfem::DenseMatrix &JInv = context.mapping.inverse_mapping_jacobian;
const double detJ = context.mapping.mapping_determinant;
mfem::Vector C_phys(dim);
m_coeff.Eval(C_phys, T, ip);
@@ -132,28 +151,35 @@ namespace mean_field::mapping {
/// PhysicalPositionFunctionCoefficient ///
///////////////////////////////////////////
PhysicalPositionFunctionCoefficient::PhysicalPositionFunctionCoefficient(
const DomainMapper &map,
const DomainMapper &mapper,
const mfem::GridFunction &displacement,
const mfem::GridFunction &compactification_coordinate,
Func f // std::function<double(const mfem::Vector&)>
)
: m_f(std::move(f)),
m_map(map) { };
m_mapping(mapper, displacement, compactification_coordinate) { };
double PhysicalPositionFunctionCoefficient::Eval(
mfem::ElementTransformation &T,
const mfem::IntegrationPoint &ip
) {
T.SetIntPoint(&ip);
mfem::Vector x;
m_map.GetPhysicalPoint(T, ip, x);
return m_f(x);
MappingPointContext context;
MFEM_VERIFY(
m_mapping.EvaluatePoint(T, ip, context) == MappingStatus::valid,
"Physical-position coefficient encountered an invalid mapping."
);
return m_f(context.physical_position);
}
MappedHDivMassCoefficient::MappedHDivMassCoefficient(
const DomainMapper &map,
const DomainMapper &mapper,
const mfem::GridFunction &displacement,
const mfem::GridFunction &compactification_coordinate,
const int dim
)
: MatrixCoefficient(dim),
m_map(map) {
m_mapping(mapper, displacement, compactification_coordinate) {
}
void MappedHDivMassCoefficient::Eval(
@@ -163,10 +189,13 @@ namespace mean_field::mapping {
) {
transformation.SetIntPoint(&integration_point);
mfem::DenseMatrix map_jacobian(height, height);
m_map.ComputeJacobian(transformation, map_jacobian);
const double map_determinant = map_jacobian.Det();
VolumeMappingContext context;
MFEM_VERIFY(
m_mapping.EvaluateVolume(transformation, integration_point, context) == MappingStatus::valid,
"Mapped H(div) coefficient encountered an invalid mapping."
);
const mfem::DenseMatrix &map_jacobian = context.mapping.mapping_jacobian;
const double map_determinant = context.mapping.mapping_determinant;
MFEM_VERIFY(map_determinant > 0.0, "Domain mapping has a non-positive Jacobian determinant.");

File diff suppressed because it is too large Load Diff

View File

@@ -1,770 +0,0 @@
module;
#include <cmath>
#include <memory>
#include <mfem.hpp>
#include <stdexcept>
#include <utility>
module mean_field;
import :mapping.types;
import :mapping.compactification;
import :utils.user;
namespace {
bool vector_is_finite(const mfem::Vector &vector) {
for (int i = 0; i < vector.Size(); ++i) {
if (!std::isfinite(vector(i)))
return false;
}
return true;
}
bool matrix_is_finite(const mfem::DenseMatrix &matrix) {
for (int i = 0; i < matrix.Height(); ++i) {
for (int j = 0; j < matrix.Width(); ++j) {
if (!std::isfinite(matrix(i, j)))
return false;
}
}
return true;
}
} // namespace
namespace mean_field::mapping {
ElementCompactificationData::ElementCompactificationData(
const mfem::FiniteElement &element,
const mfem::Vector &dofs
)
: m_element(&element),
m_dofs(dofs) {
if (element.GetRangeType() != mfem::FiniteElement::SCALAR) {
throw std::invalid_argument("Compactification coordinate requires a scalar finite element.");
}
if (element.GetMapType() != mfem::FiniteElement::VALUE) {
throw std::invalid_argument(
"Compactification coordinate requires a value-mapped scalar "
"finite "
"element."
);
}
if (element.GetDerivType() != mfem::FiniteElement::GRAD) {
throw std::invalid_argument(
"Compactification coordinate finite element must provide a "
"gradient."
);
}
if (element.GetDof() <= 0) {
throw std::invalid_argument(
"Compactification coordinate finite element has no degrees of "
"freedom."
);
}
if (dofs.Size() != element.GetDof()) {
throw std::invalid_argument(
"Compactification coordinate DOF count does not match its "
"finite "
"element."
);
}
}
const mfem::FiniteElement &ElementCompactificationData::GetElement() const noexcept {
return *m_element;
}
const mfem::Vector &ElementCompactificationData::GetDofs() const noexcept {
return m_dofs;
}
int ElementCompactificationData::GetDofCount() const noexcept {
return m_dofs.Size();
}
ElementDisplacementData::ElementDisplacementData(
const mfem::FiniteElement &element,
const mfem::Vector &displacement_dofs,
const mfem::Ordering::Type ordering
)
: m_element(&element),
m_dimension(0),
m_ordering(ordering) {
const int dof_count = element.GetDof();
if (dof_count <= 0)
throw std::invalid_argument(
"The displacement element must have at least one degree of "
"freedom."
);
if (displacement_dofs.Size() <= 0 || displacement_dofs.Size() % dof_count != 0) {
throw std::invalid_argument(
"The displacement vector size must be a positive multiple of "
"the "
"element degree-of-freedom count."
);
}
m_dimension = displacement_dofs.Size() / dof_count;
m_dof_matrix.SetSize(dof_count, m_dimension);
if (ordering == mfem::Ordering::byNODES) {
for (int component = 0; component < m_dimension; ++component) {
for (int i = 0; i < dof_count; ++i) {
m_dof_matrix(i, component) = displacement_dofs(i + component * dof_count);
}
}
} else if (ordering == mfem::Ordering::byVDIM) {
for (int i = 0; i < dof_count; ++i) {
for (int component = 0; component < m_dimension; ++component) {
m_dof_matrix(i, component) = displacement_dofs(component + i * m_dimension);
}
}
} else {
throw std::invalid_argument("Unsupported MFEM displacement ordering.");
}
}
const mfem::FiniteElement &ElementDisplacementData::GetElement() const noexcept {
return *m_element;
}
const mfem::DenseMatrix &ElementDisplacementData::GetDofMatrix() const noexcept {
return m_dof_matrix;
}
int ElementDisplacementData::GetDimension() const noexcept {
return m_dimension;
}
int ElementDisplacementData::GetDofCount() const noexcept {
return m_element->GetDof();
}
mfem::Ordering::Type ElementDisplacementData::GetOrdering() const noexcept {
return m_ordering;
}
ElementDisplacementData ElementDisplacementDataFromElementVDofs(
const mfem::FiniteElement &element,
const mfem::Vector &displacement_dofs
) {
return ElementDisplacementData(element, displacement_dofs, mfem::Ordering::byNODES);
}
DomainMapperStateless::Workspace::Workspace(const int dimension) {
SetDimension(dimension);
}
void DomainMapperStateless::Workspace::SetDimension(const int dimension) {
if (dimension <= 0) {
throw std::invalid_argument("Domain mapping workspace dimension must be positive.");
}
m_dimension = dimension;
m_field_value.SetSize(dimension);
m_field_jacobian.SetSize(dimension, dimension);
m_compactification_point.coordinate = 0.0;
m_compactification_point.coordinate_gradient.SetSize(dimension);
m_reference_normal.SetSize(dimension);
m_mapped_normal.SetSize(dimension);
m_full_element_jacobian.SetSize(dimension, dimension);
m_vector_temp.SetSize(dimension);
m_matrix_temp_1.SetSize(dimension, dimension);
m_matrix_temp_2.SetSize(dimension, dimension);
m_exterior_result.physical_position.SetSize(dimension);
m_exterior_result.mapping_jacobian.SetSize(dimension, dimension);
m_exterior_variation.physical_position_variation.SetSize(dimension);
m_exterior_variation.mapping_jacobian_variation.SetSize(dimension, dimension);
}
int DomainMapperStateless::Workspace::GetDimension() const noexcept {
return m_dimension;
}
DomainMapperStateless::DomainMapperStateless(
const utils::DomainMapperStatelessOptions options,
std::unique_ptr<const compactification::ExteriorDomainMap> exterior_map
)
: m_options(options),
m_exterior_map(std::move(exterior_map)) {
if (m_options.dimension <= 0)
throw std::invalid_argument("The domain-mapping dimension must be positive.");
if (m_options.vacuum_element_attribute <= 0)
throw std::invalid_argument("The vacuum element attribute must be positive.");
if (!m_exterior_map)
throw std::invalid_argument("DomainMapperStateless requires an exterior-domain mapping.");
}
bool
DomainMapperStateless::IsCompactifiedElement(const mfem::ElementTransformation &transformation) const noexcept {
return transformation.Attribute == m_options.vacuum_element_attribute;
}
int DomainMapperStateless::GetDimension() const noexcept {
return m_options.dimension;
}
int DomainMapperStateless::GetVacuumElementAttribute() const noexcept {
return m_options.vacuum_element_attribute;
}
const compactification::ExteriorDomainMap &DomainMapperStateless::GetExteriorMap() const noexcept {
return *m_exterior_map;
}
void DomainMapperStateless::ValidateElementData(const ElementMappingData &element_data) const {
const ElementDisplacementData &displacement = element_data.displacement;
const ElementCompactificationData &compactification = element_data.compactification;
if (displacement.GetDimension() != m_options.dimension) {
throw std::invalid_argument(
"Displacement field dimension does not match the domain mapper "
"dimension."
);
}
if (displacement.GetElement().GetDim() != m_options.dimension) {
throw std::invalid_argument(
"Displacement finite element dimension does not match the "
"domain "
"mapper dimension."
);
}
if (compactification.GetElement().GetDim() != m_options.dimension) {
throw std::invalid_argument(
"Compactification finite element dimension does not match the "
"domain "
"mapper dimension."
);
}
if (displacement.GetElement().GetGeomType() != compactification.GetElement().GetGeomType()) {
throw std::invalid_argument(
"Displacement and compactification finite elements have "
"different "
"geometries."
);
}
if (compactification.GetElement().GetRangeType() != mfem::FiniteElement::SCALAR) {
throw std::invalid_argument("Compactification coordinate requires a scalar finite element.");
}
if (compactification.GetElement().GetMapType() != mfem::FiniteElement::VALUE) {
throw std::invalid_argument(
"Compactification coordinate requires a value-mapped finite "
"element."
);
}
if (compactification.GetElement().GetDerivType() != mfem::FiniteElement::GRAD) {
throw std::invalid_argument(
"Compactification coordinate finite element does not provide a "
"gradient."
);
}
if (compactification.GetDofCount() != compactification.GetElement().GetDof()) {
throw std::invalid_argument(
"Compactification coordinate DOF count does not match its "
"finite "
"element."
);
}
}
MappingStatus DomainMapperStateless::EvaluateCompactificationCoordinate(
const ElementCompactificationData &compactification,
mfem::ElementTransformation &transformation,
const mfem::IntegrationPoint &integration_point,
Workspace &workspace,
CompactificationPointData &point_data
) const {
const mfem::FiniteElement &element = compactification.GetElement();
const mfem::Vector &dofs = compactification.GetDofs();
const int dof_count = element.GetDof();
if (workspace.GetDimension() != m_options.dimension || transformation.GetSpaceDim() != m_options.dimension ||
element.GetDim() != m_options.dimension) {
return MappingStatus::invalid_dimension;
}
if (dofs.Size() != dof_count) {
return MappingStatus::invalid_dimension;
}
for (int i = 0; i < dofs.Size(); ++i) {
if (!std::isfinite(dofs(i)))
return MappingStatus::non_finite_input;
}
transformation.SetIntPoint(&integration_point);
workspace.m_compactification_shape.SetSize(dof_count);
workspace.m_compactification_dshape.SetSize(dof_count, m_options.dimension);
element.CalcShape(integration_point, workspace.m_compactification_shape);
element.CalcPhysDShape(transformation, workspace.m_compactification_dshape);
point_data.coordinate = dofs * workspace.m_compactification_shape;
point_data.coordinate_gradient.SetSize(m_options.dimension);
workspace.m_compactification_dshape.MultTranspose(dofs, point_data.coordinate_gradient);
if (!std::isfinite(point_data.coordinate)) {
return MappingStatus::non_finite_result;
}
for (int d = 0; d < point_data.coordinate_gradient.Size(); ++d) {
if (!std::isfinite(point_data.coordinate_gradient(d)))
return MappingStatus::non_finite_result;
}
return MappingStatus::valid;
}
void DomainMapperStateless::EvaluateField(
const ElementDisplacementData &field,
mfem::ElementTransformation &transformation,
const mfem::IntegrationPoint &integration_point,
Workspace &workspace,
mfem::Vector &value,
mfem::DenseMatrix &jacobian
) const {
transformation.SetIntPoint(&integration_point);
const mfem::FiniteElement &element = field.GetElement();
const mfem::DenseMatrix &dof_matrix = field.GetDofMatrix();
workspace.m_shape.SetSize(element.GetDof());
workspace.m_mesh_dshape.SetSize(element.GetDof(), m_options.dimension);
element.CalcShape(integration_point, workspace.m_shape);
element.CalcPhysDShape(transformation, workspace.m_mesh_dshape);
value.SetSize(m_options.dimension);
dof_matrix.MultTranspose(workspace.m_shape, value);
jacobian.SetSize(m_options.dimension, m_options.dimension);
mfem::MultAtB(dof_matrix, workspace.m_mesh_dshape, jacobian);
}
MappingStatus DomainMapperStateless::EvaluatePoint(
const ElementMappingData &element_data,
mfem::ElementTransformation &transformation,
const mfem::IntegrationPoint &integration_point,
Workspace &workspace,
MappingPointContext &context
) const {
ValidateElementData(element_data);
if (workspace.GetDimension() != m_options.dimension)
throw std::invalid_argument("The mapping workspace has the wrong dimension.");
if (transformation.GetSpaceDim() != m_options.dimension)
throw std::invalid_argument("The element transformation has the wrong spatial dimension.");
if (transformation.GetGeometryType() != element_data.displacement.GetElement().GetGeomType())
throw std::invalid_argument(
"The element transformation geometry does not match the "
"supplied "
"element data."
);
transformation.SetIntPoint(&integration_point);
context.reference_position.SetSize(m_options.dimension);
transformation.Transform(integration_point, context.reference_position);
EvaluateField(
element_data.displacement, transformation, integration_point, workspace, workspace.m_field_value,
workspace.m_field_jacobian
);
if (!vector_is_finite(context.reference_position) || !vector_is_finite(workspace.m_field_value) ||
!matrix_is_finite(workspace.m_field_jacobian)) {
return MappingStatus::non_finite_input;
}
context.displaced_position.SetSize(m_options.dimension);
context.displaced_position = context.reference_position;
context.displaced_position += workspace.m_field_value;
context.displacement_jacobian.SetSize(m_options.dimension, m_options.dimension);
context.displacement_jacobian = workspace.m_field_jacobian;
for (int i = 0; i < m_options.dimension; ++i)
context.displacement_jacobian(i, i) += 1.0;
context.compactified = IsCompactifiedElement(transformation);
if (context.compactified) {
const MappingStatus coordinate_status = EvaluateCompactificationCoordinate(
element_data.compactification, transformation, integration_point, workspace,
workspace.m_compactification_point
);
if (coordinate_status != MappingStatus::valid)
return coordinate_status;
const compactification::ExteriorMapInput exterior_input{
.reference_position = context.reference_position,
.displaced_position = context.displaced_position,
.displacement_jacobian = context.displacement_jacobian,
.compactification_coordinate = workspace.m_compactification_point.coordinate,
.compactification_coordinate_gradient = workspace.m_compactification_point.coordinate_gradient
};
const MappingStatus exterior_status = m_exterior_map->Evaluate(exterior_input, workspace.m_exterior_result);
if (exterior_status != MappingStatus::valid)
return exterior_status;
context.physical_position = workspace.m_exterior_result.physical_position;
context.mapping_jacobian = workspace.m_exterior_result.mapping_jacobian;
} else {
context.physical_position = context.displaced_position;
context.mapping_jacobian = context.displacement_jacobian;
}
if (!vector_is_finite(context.physical_position) || !matrix_is_finite(context.mapping_jacobian))
return MappingStatus::non_finite_result;
context.mapping_determinant = context.mapping_jacobian.Det();
if (!std::isfinite(context.mapping_determinant))
return MappingStatus::non_finite_result;
if (context.mapping_determinant <= 0.0)
return MappingStatus::non_positive_determinant;
context.inverse_mapping_jacobian.SetSize(m_options.dimension, m_options.dimension);
mfem::CalcInverse(context.mapping_jacobian, context.inverse_mapping_jacobian);
if (!matrix_is_finite(context.inverse_mapping_jacobian))
return MappingStatus::non_finite_result;
return MappingStatus::valid;
}
MappingStatus DomainMapperStateless::EvaluateVolume(
const ElementMappingData &element_data,
mfem::ElementTransformation &transformation,
const mfem::IntegrationPoint &integration_point,
Workspace &workspace,
VolumeMappingContext &context
) const {
const MappingStatus point_status =
EvaluatePoint(element_data, transformation, integration_point, workspace, context.mapping);
if (point_status != MappingStatus::valid)
return point_status;
transformation.SetIntPoint(&integration_point);
mfem::Mult(context.mapping.mapping_jacobian, transformation.Jacobian(), workspace.m_full_element_jacobian);
context.quadrature.J_inv.SetSize(m_options.dimension, m_options.dimension);
mfem::CalcInverse(workspace.m_full_element_jacobian, context.quadrature.J_inv);
context.quadrature.detJ = context.mapping.mapping_determinant;
context.quadrature.weight =
integration_point.weight * transformation.Weight() * context.mapping.mapping_determinant;
if (!matrix_is_finite(context.quadrature.J_inv) || !std::isfinite(context.quadrature.weight))
return MappingStatus::non_finite_result;
if (context.quadrature.weight <= 0.0)
return MappingStatus::non_positive_determinant;
return MappingStatus::valid;
}
mfem::ElementTransformation &DomainMapperStateless::SelectFaceElementTransformation(
mfem::FaceElementTransformations &transformation,
const FaceElementSide side
) {
if (side == FaceElementSide::element_1) {
MFEM_VERIFY(transformation.Elem1 != nullptr, "The face does not have an element-1 transformation.");
return *transformation.Elem1;
}
MFEM_VERIFY(transformation.Elem2 != nullptr, "The face does not have an element-2 transformation.");
return *transformation.Elem2;
}
const mfem::IntegrationPoint &DomainMapperStateless::SelectFaceElementIntegrationPoint(
mfem::FaceElementTransformations &transformation,
const FaceElementSide side
) {
mfem::ElementTransformation &element_transformation = SelectFaceElementTransformation(transformation, side);
return element_transformation.GetIntPoint();
}
MappingStatus DomainMapperStateless::EvaluateFace(
const ElementMappingData &element_data,
mfem::FaceElementTransformations &transformation,
const FaceElementSide side,
const mfem::IntegrationPoint &integration_point,
Workspace &workspace,
FaceMappingContext &context
) const {
transformation.SetAllIntPoints(&integration_point);
mfem::ElementTransformation &element_transformation = SelectFaceElementTransformation(transformation, side);
const mfem::IntegrationPoint &element_integration_point =
SelectFaceElementIntegrationPoint(transformation, side);
const MappingStatus point_status =
EvaluatePoint(element_data, element_transformation, element_integration_point, workspace, context.mapping);
if (point_status != MappingStatus::valid)
return point_status;
workspace.m_reference_normal.SetSize(m_options.dimension);
mfem::CalcOrtho(transformation.Jacobian(), workspace.m_reference_normal);
if (side == FaceElementSide::element_2)
workspace.m_reference_normal *= -1.0;
const double reference_normal_magnitude = workspace.m_reference_normal.Norml2();
if (!std::isfinite(reference_normal_magnitude) || reference_normal_magnitude <= 0.0)
return MappingStatus::non_finite_result;
context.reference_normal.SetSize(m_options.dimension);
context.reference_normal = workspace.m_reference_normal;
context.reference_normal /= reference_normal_magnitude;
context.mapping.inverse_mapping_jacobian.MultTranspose(workspace.m_reference_normal, workspace.m_mapped_normal);
workspace.m_mapped_normal *= context.mapping.mapping_determinant;
const double mapped_normal_magnitude = workspace.m_mapped_normal.Norml2();
if (!std::isfinite(mapped_normal_magnitude) || mapped_normal_magnitude <= 0.0)
return MappingStatus::non_finite_result;
context.quadrature.normal.SetSize(m_options.dimension);
context.quadrature.normal = workspace.m_mapped_normal;
context.quadrature.normal /= mapped_normal_magnitude;
context.reference_surface_weight = integration_point.weight * reference_normal_magnitude;
context.physical_surface_weight = integration_point.weight * mapped_normal_magnitude;
context.quadrature.ds = context.reference_surface_weight;
context.quadrature.v_dot_n_scale = mapped_normal_magnitude / reference_normal_magnitude;
if (!vector_is_finite(context.quadrature.normal) || !std::isfinite(context.reference_surface_weight) ||
!std::isfinite(context.physical_surface_weight) || !std::isfinite(context.quadrature.v_dot_n_scale)) {
return MappingStatus::non_finite_result;
}
return MappingStatus::valid;
}
MappingStatus DomainMapperStateless::EvaluatePointVariation(
const ElementMappingData &element_data,
const ElementDisplacementData &direction,
mfem::ElementTransformation &transformation,
const mfem::IntegrationPoint &integration_point,
const MappingPointContext &base_context,
Workspace &workspace,
MappingPointVariation &variation
) const {
ValidateElementData(element_data);
const ElementMappingData direction_data{
.displacement = direction, .compactification = element_data.compactification
};
ValidateElementData(direction_data);
if (element_data.displacement.GetDofCount() != direction.GetDofCount())
throw std::invalid_argument(
"The displacement and direction elements have different "
"degree-of-freedom counts."
);
if (workspace.GetDimension() != m_options.dimension)
throw std::invalid_argument("The mapping workspace has the wrong dimension.");
if (base_context.compactified != IsCompactifiedElement(transformation))
throw std::invalid_argument(
"The base mapping context does not match the current element "
"domain."
);
EvaluateField(
direction, transformation, integration_point, workspace, workspace.m_field_value, workspace.m_field_jacobian
);
if (!vector_is_finite(workspace.m_field_value) || !matrix_is_finite(workspace.m_field_jacobian))
return MappingStatus::non_finite_input;
variation.displacement_variation = workspace.m_field_value;
variation.displacement_jacobian_variation = workspace.m_field_jacobian;
if (base_context.compactified) {
const MappingStatus coordinate_status = EvaluateCompactificationCoordinate(
element_data.compactification, transformation, integration_point, workspace,
workspace.m_compactification_point
);
if (coordinate_status != MappingStatus::valid)
return coordinate_status;
const compactification::ExteriorMapInput exterior_input{
.reference_position = base_context.reference_position,
.displaced_position = base_context.displaced_position,
.displacement_jacobian = base_context.displacement_jacobian,
.compactification_coordinate = workspace.m_compactification_point.coordinate,
.compactification_coordinate_gradient = workspace.m_compactification_point.coordinate_gradient
};
workspace.m_exterior_result.physical_position = base_context.physical_position;
workspace.m_exterior_result.mapping_jacobian = base_context.mapping_jacobian;
const compactification::ExteriorMapDirection exterior_direction{
.displaced_position_variation = variation.displacement_variation,
.displacement_jacobian_variation = variation.displacement_jacobian_variation
};
// ReSharper disable once CppTooWideScopeInitStatement
const MappingStatus exterior_status = m_exterior_map->EvaluateVariation(
exterior_input, workspace.m_exterior_result, exterior_direction, workspace.m_exterior_variation
);
if (exterior_status != MappingStatus::valid) {
return exterior_status;
}
variation.physical_position_variation = workspace.m_exterior_variation.physical_position_variation;
variation.mapping_jacobian_variation = workspace.m_exterior_variation.mapping_jacobian_variation;
} else {
variation.physical_position_variation = variation.displacement_variation;
variation.mapping_jacobian_variation = variation.displacement_jacobian_variation;
}
mfem::Mult(
base_context.inverse_mapping_jacobian, variation.mapping_jacobian_variation, workspace.m_matrix_temp_1
);
double trace = 0.0;
for (int i = 0; i < m_options.dimension; ++i)
trace += workspace.m_matrix_temp_1(i, i);
variation.mapping_determinant_variation = base_context.mapping_determinant * trace;
variation.inverse_mapping_jacobian_variation.SetSize(m_options.dimension, m_options.dimension);
mfem::Mult(
workspace.m_matrix_temp_1, base_context.inverse_mapping_jacobian,
variation.inverse_mapping_jacobian_variation
);
variation.inverse_mapping_jacobian_variation *= -1.0;
if (!vector_is_finite(variation.physical_position_variation) ||
!matrix_is_finite(variation.mapping_jacobian_variation) ||
!matrix_is_finite(variation.inverse_mapping_jacobian_variation) ||
!std::isfinite(variation.mapping_determinant_variation)) {
return MappingStatus::non_finite_result;
}
return MappingStatus::valid;
}
MappingStatus DomainMapperStateless::EvaluateVolumeVariation(
const ElementMappingData &element_data,
const ElementDisplacementData &direction,
mfem::ElementTransformation &transformation,
const mfem::IntegrationPoint &integration_point,
const VolumeMappingContext &base_context,
Workspace &workspace,
VolumeMappingVariation &variation
) const {
const MappingStatus point_status = EvaluatePointVariation(
element_data, direction, transformation, integration_point, base_context.mapping, workspace,
variation.mapping
);
if (point_status != MappingStatus::valid)
return point_status;
transformation.SetIntPoint(&integration_point);
mfem::Mult(
variation.mapping.mapping_jacobian_variation, transformation.Jacobian(), workspace.m_full_element_jacobian
);
mfem::Mult(base_context.quadrature.J_inv, workspace.m_full_element_jacobian, workspace.m_matrix_temp_1);
variation.inverse_element_jacobian_variation.SetSize(m_options.dimension, m_options.dimension);
mfem::Mult(
workspace.m_matrix_temp_1, base_context.quadrature.J_inv, variation.inverse_element_jacobian_variation
);
variation.inverse_element_jacobian_variation *= -1.0;
variation.weight_variation =
integration_point.weight * transformation.Weight() * variation.mapping.mapping_determinant_variation;
if (!matrix_is_finite(variation.inverse_element_jacobian_variation) ||
!std::isfinite(variation.weight_variation))
return MappingStatus::non_finite_result;
return MappingStatus::valid;
}
MappingStatus DomainMapperStateless::EvaluateFaceVariation(
const ElementMappingData &element_data,
const ElementDisplacementData &direction,
mfem::FaceElementTransformations &transformation,
const FaceElementSide side,
const mfem::IntegrationPoint &integration_point,
const FaceMappingContext &base_context,
Workspace &workspace,
FaceMappingVariation &variation
) const {
transformation.SetAllIntPoints(&integration_point);
mfem::ElementTransformation &element_transformation = SelectFaceElementTransformation(transformation, side);
const mfem::IntegrationPoint &element_integration_point =
SelectFaceElementIntegrationPoint(transformation, side);
const MappingStatus point_status = EvaluatePointVariation(
element_data, direction, element_transformation, element_integration_point, base_context.mapping, workspace,
variation.mapping
);
if (point_status != MappingStatus::valid)
return point_status;
workspace.m_reference_normal.SetSize(m_options.dimension);
mfem::CalcOrtho(transformation.Jacobian(), workspace.m_reference_normal);
if (side == FaceElementSide::element_2)
workspace.m_reference_normal *= -1.0;
const double reference_normal_magnitude = workspace.m_reference_normal.Norml2();
if (!std::isfinite(reference_normal_magnitude) || reference_normal_magnitude <= 0.0)
return MappingStatus::non_finite_result;
base_context.mapping.inverse_mapping_jacobian.MultTranspose(
workspace.m_reference_normal, workspace.m_vector_temp
);
workspace.m_mapped_normal = workspace.m_vector_temp;
workspace.m_mapped_normal *= base_context.mapping.mapping_determinant;
variation.physical_normal_variation.SetSize(m_options.dimension);
variation.mapping.inverse_mapping_jacobian_variation.MultTranspose(
workspace.m_reference_normal, variation.physical_normal_variation
);
variation.physical_normal_variation *= base_context.mapping.mapping_determinant;
variation.physical_normal_variation.Add(
variation.mapping.mapping_determinant_variation, workspace.m_vector_temp
);
const double mapped_normal_magnitude = workspace.m_mapped_normal.Norml2();
if (!std::isfinite(mapped_normal_magnitude) || mapped_normal_magnitude <= 0.0)
return MappingStatus::non_finite_result;
const double mapped_normal_magnitude_variation =
base_context.quadrature.normal * variation.physical_normal_variation;
variation.physical_normal_variation.Add(-mapped_normal_magnitude_variation, base_context.quadrature.normal);
variation.physical_normal_variation /= mapped_normal_magnitude;
variation.physical_surface_weight_variation = integration_point.weight * mapped_normal_magnitude_variation;
variation.normal_flux_scale_variation = mapped_normal_magnitude_variation / reference_normal_magnitude;
if (!vector_is_finite(variation.physical_normal_variation) ||
!std::isfinite(variation.physical_surface_weight_variation) ||
!std::isfinite(variation.normal_flux_scale_variation)) {
return MappingStatus::non_finite_result;
}
return MappingStatus::valid;
}
} // namespace mean_field::mapping

View File

@@ -35,7 +35,7 @@ namespace {
namespace mean_field::operators::context::barotropic {
BarotropicClosureLinearizationContext::BarotropicClosureLinearizationContext(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const mapping::DomainMapper &domainMapper,
const field::FieldDofMap &densityMap,
const field::FieldDofMap &enthalpyMap,
const field::FieldDofMap &displacementMap

View File

@@ -9,6 +9,29 @@ import :operators.context.gravity_field;
namespace {
using DomainSchema = mean_field::utils::domain::CoreEnvelopeVacuumDomainSchema;
[[nodiscard]] std::unique_ptr<mfem::ParMixedBilinearForm> make_divergence_operator(const mean_field::fem::FEM &f) {
auto divergence =
std::make_unique<mfem::ParMixedBilinearForm>(f.gravityFluxFes.get(), f.gravityPotentialFes.get());
divergence->SetAssemblyLevel(mfem::AssemblyLevel::PARTIAL);
auto integrator = std::make_unique<mfem::VectorFEDivergenceIntegrator>();
const mfem::FiniteElement &trialElement = *f.gravityFluxFes->GetTypicalFE();
const mfem::FiniteElement &testElement = *f.gravityPotentialFes->GetTypicalFE();
const mfem::ElementTransformation &transformation = *f.mesh->GetElementTransformation(0);
f.quadratureFactory->configure_gravity_divergence(
*integrator, mean_field::quadrature::QuadratureRole::discretization, trialElement, testElement,
transformation, mean_field::utils::DOMAINS::ALL, mean_field::quadrature::MappingKind::none
);
divergence->AddDomainIntegrator(integrator.release());
divergence->Assemble();
return divergence;
}
void validate_displacement(
const mean_field::field::FieldDofMap &displacement_map,
const mfem::Vector &displacement
@@ -96,7 +119,7 @@ namespace {
namespace mean_field::operators::context::gravity_field {
GravityFieldGeometryContext::GravityFieldGeometryContext(
const fem::FEM &f,
const mapping::DomainMapperStateless &domain_mapper
const mapping::DomainMapper &domain_mapper
)
: m_fem(f),
m_domain_mapper(domain_mapper),
@@ -170,18 +193,23 @@ namespace mean_field::operators::context::gravity_field {
}
if (discretization_changed) {
auto mass_operator = std::make_unique<PreparedMappedHDivMassOperator>(m_fem, m_domain_mapper);
auto source_operator = std::make_unique<PreparedMappedGravitySourceOperator>(m_fem, m_domain_mapper);
auto mass_operator = std::make_unique<PreparedMappedHDivMassOperator>(m_fem, m_domain_mapper);
auto source_operator = std::make_unique<PreparedMappedGravitySourceOperator>(m_fem, m_domain_mapper);
auto divergence_operator = make_divergence_operator(m_fem);
auto transpose_divergence_operator = std::make_unique<mfem::TransposeOperator>(divergence_operator.get());
mass_operator->Prepare(displacement);
source_operator->Prepare(displacement);
m_mass_operator = std::move(mass_operator);
m_source_operator = std::move(source_operator);
m_mass_operator = std::move(mass_operator);
m_source_operator = std::move(source_operator);
m_divergence_operator = std::move(divergence_operator);
m_transpose_divergence_operator = std::move(transpose_divergence_operator);
preparation.reconstructed_operators = true;
preparation.rebuilt_mass_operator = true;
preparation.rebuilt_source_operator = true;
preparation.reconstructed_operators = true;
preparation.rebuilt_mass_operator = true;
preparation.rebuilt_source_operator = true;
preparation.rebuilt_divergence_operator = true;
} else {
MFEM_VERIFY(
m_mass_operator != nullptr, "GravityFieldGeometryContext has "
@@ -231,6 +259,23 @@ namespace mean_field::operators::context::gravity_field {
return *m_source_operator;
}
const mfem::Operator &GravityFieldGeometryContext::GetDivergenceOperator() const {
MFEM_VERIFY(m_is_prepared, "GravityFieldGeometryContext must be prepared before accessing divergence.");
MFEM_VERIFY(m_divergence_operator != nullptr, "GravityFieldGeometryContext has no divergence operator.");
return *m_divergence_operator;
}
const mfem::Operator &GravityFieldGeometryContext::GetTransposeDivergenceOperator() const {
MFEM_VERIFY(
m_is_prepared, "GravityFieldGeometryContext must be prepared before accessing transpose divergence."
);
MFEM_VERIFY(
m_transpose_divergence_operator != nullptr,
"GravityFieldGeometryContext has no transpose-divergence operator."
);
return *m_transpose_divergence_operator;
}
const mfem::Vector &GravityFieldGeometryContext::GetDisplacementTrue() const {
MFEM_VERIFY(
m_is_prepared, "GravityFieldGeometryContext must be prepared before "
@@ -257,7 +302,7 @@ namespace mean_field::operators::context::gravity_field {
GravityFieldLinearizationContext::GravityFieldLinearizationContext(
const fem::FEM &f,
const mapping::DomainMapperStateless &domain_mapper
const mapping::DomainMapper &domain_mapper
)
: m_fem(f),
m_geometry_context(

View File

@@ -75,7 +75,7 @@ namespace {
namespace mean_field::operators::context::hydrostatic {
HydrostaticEquilibriumContext::HydrostaticEquilibriumContext(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper
const mapping::DomainMapper &domainMapper
)
: m_f(f),
m_domainMapper(domainMapper),

View File

@@ -37,7 +37,7 @@ namespace {
namespace mean_field::operators::context::pressure_force {
PressureForceLinearizationContext::PressureForceLinearizationContext(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const mapping::DomainMapper &domainMapper,
const field::FieldDofMap &enthalpyMap,
const field::FieldDofMap &displacementMap
)

View File

@@ -33,7 +33,7 @@ namespace {
namespace mean_field::operators::context::rotational_displacement_force {
RotationalDisplacementForceLinearizationContext::RotationalDisplacementForceLinearizationContext(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper
const mapping::DomainMapper &domainMapper
)
: m_f(f),
m_densityMap(

File diff suppressed because it is too large Load Diff

View File

@@ -129,7 +129,7 @@ namespace {
namespace mean_field::operators {
GravityFieldJacobianOperator::GravityFieldJacobianOperator(
fem::FEM &f,
const mapping::DomainMapperStateless &domain_mapper,
const mapping::DomainMapper &domain_mapper,
const context::gravity_field::GravityFieldLinearizationContext &linearization_context,
const mfem::Array<int> &state_offsets,
const mfem::Array<int> &residual_offsets
@@ -159,13 +159,6 @@ namespace mean_field::operators {
f.displacementFes != nullptr, "GravityFieldJacobianOperator requires the "
"displacement finite-element space."
);
MFEM_VERIFY(
f.gravityContext.b_form != nullptr, "GravityFieldJacobianOperator requires the divergence operator."
);
MFEM_VERIFY(
f.gravityContext.BT != nullptr, "GravityFieldJacobianOperator requires the transpose divergence "
"operator."
);
MFEM_VERIFY(
f.quadratureFactory != nullptr, "GravityFieldJacobianOperator requires the quadrature-rule factory."
);
@@ -263,14 +256,16 @@ namespace mean_field::operators {
potential_map.gather(source_variation_action_true, source_variation_action);
transpose_divergence_action_true.SetSize(flux_map.full_size());
m_fem.gravityContext.BT->Mult(gravity_potential_direction_true, transpose_divergence_action_true);
geometry_context.GetTransposeDivergenceOperator().Mult(
gravity_potential_direction_true, transpose_divergence_action_true
);
flux_map.gather(transpose_divergence_action_true, transpose_divergence_action);
gravity_gradient_action += transpose_divergence_action;
gravity_gradient_action += mass_variation_action;
divergence_action_true.SetSize(potential_map.full_size());
m_fem.gravityContext.b_form->Mult(gravity_gradient_direction_true, divergence_action_true);
geometry_context.GetDivergenceOperator().Mult(gravity_gradient_direction_true, divergence_action_true);
potential_map.gather(divergence_action_true, gravity_poisson_action);
gravity_poisson_action -= source_action;

View File

@@ -111,7 +111,7 @@ namespace {
void validate_common_inputs(
const mean_field::fem::FEM &f,
const mean_field::mapping::DomainMapperStateless &domainMapper,
const mean_field::mapping::DomainMapper &domainMapper,
const mfem::Vector &displacementTrue
) {
MFEM_VERIFY(f.mesh != nullptr, "The EOS closure kernel requires a mesh.");
@@ -150,7 +150,7 @@ namespace {
void apply_closure_action(
const mean_field::fem::FEM &f,
const mean_field::mapping::DomainMapperStateless &domainMapper,
const mean_field::mapping::DomainMapper &domainMapper,
const mean_field::eos::Polytrope &barotrope,
const ClosureAction closureAction,
const mfem::Vector *densityInputTrue,
@@ -204,7 +204,7 @@ namespace {
mfem::Vector localAction(f.densityFes->GetVSize());
localAction = 0.0;
mean_field::mapping::DomainMapperStateless::Workspace workspace(f.mesh->Dimension());
mean_field::mapping::DomainMapper::Workspace workspace(f.mesh->Dimension());
mfem::Array<int> densityDofs;
mfem::Array<int> enthalpyDofs;
@@ -365,7 +365,7 @@ namespace {
namespace mean_field::operators::kernels {
void apply_barotropic_closure(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const mapping::DomainMapper &domainMapper,
const eos::Polytrope &barotrope,
const mfem::Vector &densityTrue,
const mfem::Vector &enthalpyTrue,
@@ -380,7 +380,7 @@ namespace mean_field::operators::kernels {
void apply_barotropic_closure_density_action(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const mapping::DomainMapper &domainMapper,
const eos::Polytrope &barotrope,
const mfem::Vector &densityVariationTrue,
const mfem::Vector &displacementTrue,
@@ -394,7 +394,7 @@ namespace mean_field::operators::kernels {
void apply_barotropic_closure_enthalpy_action(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const mapping::DomainMapper &domainMapper,
const eos::Polytrope &barotrope,
const mfem::Vector &baseEnthalpyTrue,
const mfem::Vector &enthalpyVariationTrue,
@@ -409,7 +409,7 @@ namespace mean_field::operators::kernels {
void apply_barotropic_closure_displacement_action(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const mapping::DomainMapper &domainMapper,
const eos::Polytrope &barotrope,
const mfem::Vector &baseDensityTrue,
const mfem::Vector &baseEnthalpyTrue,
@@ -490,7 +490,7 @@ namespace mean_field::operators::kernels {
mfem::Vector localAction(f.densityFes->GetVSize());
localAction = 0.0;
mapping::DomainMapperStateless::Workspace workspace(f.mesh->Dimension());
mapping::DomainMapper::Workspace workspace(f.mesh->Dimension());
mfem::Array<int> densityDofs;
mfem::Array<int> enthalpyDofs;

File diff suppressed because it is too large Load Diff

View File

@@ -183,7 +183,7 @@ namespace mean_field::operators {
PreparedBarotropicClosureOperator::PreparedBarotropicClosureOperator(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const mapping::DomainMapper &domainMapper,
const eos::Polytrope &equationOfState
)
: PreparedBarotropicClosureOperator(
@@ -196,7 +196,7 @@ namespace mean_field::operators {
PreparedBarotropicClosureOperator::PreparedBarotropicClosureOperator(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const mapping::DomainMapper &domainMapper,
const eos::Polytrope &equationOfState,
ConstructionData constructionData
)
@@ -285,7 +285,7 @@ namespace mean_field::operators {
true_to_local(*m_fem.enthalpyFes, m_baseEnthalpyTrue, baseEnthalpyLocal);
true_to_local(*m_fem.displacementFes, m_baseDisplacementTrue, displacementLocal);
mapping::DomainMapperStateless::Workspace workspace(m_fem.mesh->Dimension());
mapping::DomainMapper::Workspace workspace(m_fem.mesh->Dimension());
mfem::Array<int> displacementDofs;
mfem::Array<int> compactificationDofs;

View File

@@ -76,7 +76,7 @@ namespace {
namespace mean_field::operators {
PreparedDisplacementResidualOperator::PreparedDisplacementResidualOperator(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const mapping::DomainMapper &domainMapper,
const eos::Polytrope &barotrope,
const context::gravity_field::GravityFieldLinearizationContext &gravityContext
)

View File

@@ -20,7 +20,7 @@ namespace {
namespace mean_field::operators {
PreparedGravityDisplacementForceOperator::PreparedGravityDisplacementForceOperator(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const mapping::DomainMapper &domainMapper,
const context::gravity_field::GravityFieldLinearizationContext &gravityContext
)
: m_fem(f),

View File

@@ -9,533 +9,531 @@ module mean_field;
import :operators.prepared_gravity_source;
namespace {
using DomainSchema = mean_field::utils::domain::CoreEnvelopeVacuumDomainSchema;
using DomainSchema = mean_field::utils::domain::CoreEnvelopeVacuumDomainSchema;
int get_operator_height(const mean_field::fem::FEM &f) {
MFEM_VERIFY(
f.gravityPotentialFes != nullptr, "PreparedMappedGravitySourceOperator requires the "
"gravity-potential "
"finite-element space."
);
return mean_field::field::make_field_dof_map<mean_field::field::Gravity, DomainSchema>(*f.gravityPotentialFes)
.reduced_size();
}
int get_operator_height(const mean_field::fem::FEM &f) {
MFEM_VERIFY(f.gravityPotentialFes != nullptr,
"PreparedMappedGravitySourceOperator requires the "
"gravity-potential "
"finite-element space.");
return mean_field::field::make_field_dof_map<mean_field::field::Gravity,
DomainSchema>(
*f.gravityPotentialFes)
.reduced_size();
}
int get_operator_width(const mean_field::fem::FEM &f) {
MFEM_VERIFY(
f.densityFes != nullptr, "PreparedMappedGravitySourceOperator requires the density "
"finite-element space."
);
return mean_field::field::make_field_dof_map<mean_field::field::Density, DomainSchema>(*f.densityFes)
.reduced_size();
}
int get_operator_width(const mean_field::fem::FEM &f) {
MFEM_VERIFY(f.densityFes != nullptr,
"PreparedMappedGravitySourceOperator requires the density "
"finite-element space.");
return mean_field::field::make_field_dof_map<mean_field::field::Density,
DomainSchema>(*f.densityFes)
.reduced_size();
}
void true_to_local(
const mfem::ParFiniteElementSpace &finite_element_space,
const mfem::Vector &true_vector,
mfem::Vector &local_vector
) {
local_vector.SetSize(finite_element_space.GetVSize());
void true_to_local(const mfem::ParFiniteElementSpace &finite_element_space,
const mfem::Vector &true_vector,
mfem::Vector &local_vector) {
local_vector.SetSize(finite_element_space.GetVSize());
const mfem::Operator *prolongation = finite_element_space.GetProlongationMatrix();
const mfem::Operator *prolongation =
finite_element_space.GetProlongationMatrix();
if (prolongation != nullptr) {
prolongation->Mult(true_vector, local_vector);
} else {
local_vector = true_vector;
}
}
if (prolongation != nullptr) {
prolongation->Mult(true_vector, local_vector);
} else {
local_vector = true_vector;
}
}
void local_to_true(
const mfem::ParFiniteElementSpace &finite_element_space,
const mfem::Vector &local_vector,
mfem::Vector &true_vector
) {
MFEM_VERIFY(local_vector.Size() == finite_element_space.GetVSize(), "Local vector has the wrong size.");
void local_to_true(const mfem::ParFiniteElementSpace &finite_element_space,
const mfem::Vector &local_vector,
mfem::Vector &true_vector) {
MFEM_VERIFY(local_vector.Size() == finite_element_space.GetVSize(),
"Local vector has the wrong size.");
true_vector.SetSize(finite_element_space.GetTrueVSize());
true_vector = 0.0;
true_vector.SetSize(finite_element_space.GetTrueVSize());
true_vector = 0.0;
const mfem::Operator *prolongation = finite_element_space.GetProlongationMatrix();
const mfem::Operator *prolongation =
finite_element_space.GetProlongationMatrix();
if (prolongation != nullptr) {
prolongation->MultTranspose(local_vector, true_vector);
} else {
true_vector = local_vector;
}
}
if (prolongation != nullptr) {
prolongation->MultTranspose(local_vector, true_vector);
} else {
true_vector = local_vector;
}
}
const mfem::IntegrationRule &get_source_rule(
const mean_field::fem::FEM &f,
const mfem::FiniteElement &density_element,
const mfem::FiniteElement &potential_element,
const mfem::ElementTransformation &transformation
) {
using GravityField = mean_field::field::Field<mean_field::field::Gravity>;
MFEM_VERIFY(
density_element.GetOrder() == mean_field::field::Density::Scalar::familyOrder,
"The prepared source trial element does not match the registered "
"density field."
);
MFEM_VERIFY(
potential_element.GetOrder() == mean_field::field::Gravity::Potential::familyOrder,
"The prepared source test element does not match the registered "
"gravity potential."
);
const mean_field::quadrature::Query query =
GravityField::make_query<mean_field::field::Gravity::Form::SourceProjection>(
mean_field::quadrature::QuadratureRole::discretization, transformation.OrderW(), {},
mean_field::utils::DOMAINS::STELLAR, mean_field::quadrature::MappingKind::general
);
const mfem::IntegrationRule &
get_source_rule(const mean_field::fem::FEM &f,
const mfem::FiniteElement &density_element,
const mfem::FiniteElement &potential_element,
const mfem::ElementTransformation &transformation) {
using GravityField = mean_field::field::Field<mean_field::field::Gravity>;
MFEM_VERIFY(density_element.GetOrder() ==
mean_field::field::Density::Scalar::familyOrder,
"The prepared source trial element does not match the registered "
"density field.");
MFEM_VERIFY(potential_element.GetOrder() ==
mean_field::field::Gravity::Potential::familyOrder,
"The prepared source test element does not match the registered "
"gravity potential.");
const mean_field::quadrature::Query query = GravityField::make_query<
mean_field::field::Gravity::Form::SourceProjection>(
mean_field::quadrature::QuadratureRole::discretization,
transformation.OrderW(), {}, mean_field::utils::DOMAINS::STELLAR,
mean_field::quadrature::MappingKind::general);
return *f.quadratureFactory->get(query, transformation.GetGeometryType()).integration_rule;
}
return *f.quadratureFactory->get(query, transformation.GetGeometryType())
.integration_rule;
}
class FrozenMappedGravitySourceCoefficient final : public mfem::Coefficient {
public:
FrozenMappedGravitySourceCoefficient(
const mean_field::fem::FEM &f,
const mean_field::mapping::DomainMapperStateless &domain_mapper,
const mfem::Vector &displacement_true
)
: m_fem(f),
m_domain_mapper(domain_mapper),
m_workspace(domain_mapper.GetDimension()) {
true_to_local(*m_fem.displacementFes, displacement_true, m_displacement_local);
}
class FrozenMappedGravitySourceCoefficient final : public mfem::Coefficient {
public:
FrozenMappedGravitySourceCoefficient(
const mean_field::fem::FEM &f,
const mean_field::mapping::DomainMapper &domain_mapper,
const mfem::Vector &displacement_true)
: m_fem(f), m_domain_mapper(domain_mapper),
m_workspace(domain_mapper.GetDimension()) {
true_to_local(*m_fem.displacementFes, displacement_true,
m_displacement_local);
}
double Eval(
mfem::ElementTransformation &transformation,
const mfem::IntegrationPoint &integration_point
) override {
transformation.SetIntPoint(&integration_point);
double Eval(mfem::ElementTransformation &transformation,
const mfem::IntegrationPoint &integration_point) override {
transformation.SetIntPoint(&integration_point);
const int element_id = transformation.ElementNo;
MFEM_VERIFY(
element_id >= 0 && element_id < m_fem.mesh->GetNE(),
const int element_id = transformation.ElementNo;
MFEM_VERIFY(element_id >= 0 && element_id < m_fem.mesh->GetNE(),
"Mapped gravity source coefficient received an invalid element "
"ID."
);
if (transformation.Attribute == m_domain_mapper.GetVacuumElementAttribute()) {
return 0.0;
}
"ID.");
if (DomainSchema::template attribute_belongs_to<
mean_field::utils::domain::Vacuum>(transformation.Attribute)) {
return 0.0;
}
LoadElement(element_id);
const mean_field::mapping::ElementMappingData mapping_data{
.displacement = *m_displacement_data, .compactification = *m_compactification_data
};
LoadElement(element_id);
const mean_field::mapping::ElementMappingData mapping_data{
.displacement = *m_displacement_data,
.compactification = *m_compactification_data};
mean_field::mapping::VolumeMappingContext mapping_context;
mean_field::mapping::VolumeMappingContext mapping_context;
const mean_field::mapping::MappingStatus status = m_domain_mapper.EvaluateVolume(
mapping_data, transformation, integration_point, m_workspace, mapping_context
);
const mean_field::mapping::MappingStatus status =
m_domain_mapper.EvaluateVolume(mapping_data, transformation,
integration_point, m_workspace,
mapping_context);
if (status != mean_field::mapping::MappingStatus::valid) {
const mfem::FiniteElement &displacement_element = *m_fem.displacementFes->GetFE(element_id);
const mfem::FiniteElement &compactification_element = *m_fem.compactificationFes->GetFE(element_id);
if (status != mean_field::mapping::MappingStatus::valid) {
const mfem::FiniteElement &displacement_element =
*m_fem.displacementFes->GetFE(element_id);
const mfem::FiniteElement &compactification_element =
*m_fem.compactificationFes->GetFE(element_id);
mfem::Vector displacement_shape(displacement_element.GetDof());
mfem::Vector compactification_shape(compactification_element.GetDof());
mfem::Vector reference_position(m_domain_mapper.GetDimension());
mfem::Vector displacement_value(m_domain_mapper.GetDimension());
mfem::Vector displacement_shape(displacement_element.GetDof());
mfem::Vector compactification_shape(compactification_element.GetDof());
mfem::Vector reference_position(m_domain_mapper.GetDimension());
mfem::Vector displacement_value(m_domain_mapper.GetDimension());
displacement_element.CalcShape(integration_point, displacement_shape);
compactification_element.CalcShape(integration_point, compactification_shape);
transformation.Transform(integration_point, reference_position);
m_displacement_data->GetDofMatrix().MultTranspose(displacement_shape, displacement_value);
displacement_element.CalcShape(integration_point, displacement_shape);
compactification_element.CalcShape(integration_point,
compactification_shape);
transformation.Transform(integration_point, reference_position);
m_displacement_data->GetDofMatrix().MultTranspose(displacement_shape,
displacement_value);
const double compactification_coordinate = m_compactification_data->GetDofs() * compactification_shape;
const double compactification_coordinate =
m_compactification_data->GetDofs() * compactification_shape;
MFEM_ABORT(
"Stateless domain mapping failed while preparing the "
"gravity "
"source operator."
<< "\nMapping status = " << static_cast<int>(status) << "\nElement ID = " << element_id
<< "\nElement attribute = " << transformation.Attribute
<< "\nIntegration-point index = " << integration_point.index << "\nIntegration point = <"
<< integration_point.x << ", " << integration_point.y << ", " << integration_point.z << ">"
<< "\nReference position = <" << reference_position(0) << ", " << reference_position(1) << ", "
<< reference_position(2) << ">"
<< "\nReference radius = " << reference_position.Norml2() << "\nDisplacement value = <"
<< displacement_value(0) << ", " << displacement_value(1) << ", " << displacement_value(2) << ">"
<< "\nDisplacement magnitude = " << displacement_value.Norml2()
<< "\nCompactification coordinate = " << compactification_coordinate
<< "\nDisplacement ordering = " << static_cast<int>(m_fem.displacementFes->GetOrdering())
);
}
const double mapping_determinant = mapping_context.mapping.mapping_determinant;
MFEM_VERIFY(
std::isfinite(mapping_determinant) && mapping_determinant > 0.0,
MFEM_ABORT(
"Stateless domain mapping failed while preparing the "
"gravity "
"source operator."
<< "\nMapping status = " << static_cast<int>(status)
<< "\nElement ID = " << element_id
<< "\nElement attribute = " << transformation.Attribute
<< "\nIntegration-point index = " << integration_point.index
<< "\nIntegration point = <" << integration_point.x << ", "
<< integration_point.y << ", " << integration_point.z << ">"
<< "\nReference position = <" << reference_position(0) << ", "
<< reference_position(1) << ", " << reference_position(2) << ">"
<< "\nReference radius = " << reference_position.Norml2()
<< "\nDisplacement value = <" << displacement_value(0) << ", "
<< displacement_value(1) << ", " << displacement_value(2) << ">"
<< "\nDisplacement magnitude = " << displacement_value.Norml2()
<< "\nCompactification coordinate = " << compactification_coordinate
<< "\nDisplacement ordering = "
<< static_cast<int>(m_fem.displacementFes->GetOrdering()));
}
const double mapping_determinant =
mapping_context.mapping.mapping_determinant;
MFEM_VERIFY(std::isfinite(mapping_determinant) && mapping_determinant > 0.0,
"Prepared gravity source operator encountered a non-positive "
"or "
"non-finite mapping determinant."
);
"non-finite mapping determinant.");
return 4.0 * std::numbers::pi * mean_field::utils::G * mapping_determinant;
}
return 4.0 * std::numbers::pi * mean_field::utils::G * mapping_determinant;
}
private:
void LoadElement(const int element_id) {
if (element_id == m_cached_element_id) {
return;
}
private:
void LoadElement(const int element_id) {
if (element_id == m_cached_element_id) {
return;
}
const mfem::FiniteElement &displacement_element = *m_fem.displacementFes->GetFE(element_id);
const mfem::FiniteElement &compactification_element = *m_fem.compactificationFes->GetFE(element_id);
const mfem::FiniteElement &displacement_element =
*m_fem.displacementFes->GetFE(element_id);
const mfem::FiniteElement &compactification_element =
*m_fem.compactificationFes->GetFE(element_id);
mfem::DofTransformation *displacement_dof_transformation =
m_fem.displacementFes->GetElementVDofs(element_id, m_displacement_dofs);
mfem::DofTransformation *compactification_dof_transformation =
m_fem.compactificationFes->GetElementDofs(element_id, m_compactification_dofs);
mfem::DofTransformation *displacement_dof_transformation =
m_fem.displacementFes->GetElementVDofs(element_id, m_displacement_dofs);
mfem::DofTransformation *compactification_dof_transformation =
m_fem.compactificationFes->GetElementDofs(element_id,
m_compactification_dofs);
m_displacement_local.GetSubVector(m_displacement_dofs, m_element_displacement);
m_fem.compactificationCoordinate->GetSubVector(m_compactification_dofs, m_element_compactification);
m_displacement_local.GetSubVector(m_displacement_dofs,
m_element_displacement);
m_fem.compactificationCoordinate->GetSubVector(m_compactification_dofs,
m_element_compactification);
if (displacement_dof_transformation != nullptr) {
displacement_dof_transformation->InvTransformPrimal(m_element_displacement);
}
if (displacement_dof_transformation != nullptr) {
displacement_dof_transformation->InvTransformPrimal(
m_element_displacement);
}
if (compactification_dof_transformation != nullptr) {
compactification_dof_transformation->InvTransformPrimal(m_element_compactification);
}
if (compactification_dof_transformation != nullptr) {
compactification_dof_transformation->InvTransformPrimal(
m_element_compactification);
}
m_displacement_data = std::make_unique<mean_field::mapping::ElementDisplacementData>(
mean_field::mapping::ElementDisplacementDataFromElementVDofs(
displacement_element, m_element_displacement
)
);
m_displacement_data =
std::make_unique<mean_field::mapping::ElementDisplacementData>(
mean_field::mapping::ElementDisplacementDataFromElementVDofs(
displacement_element, m_element_displacement));
m_compactification_data = std::make_unique<mean_field::mapping::ElementCompactificationData>(
compactification_element, m_element_compactification
);
m_compactification_data =
std::make_unique<mean_field::mapping::ElementCompactificationData>(
compactification_element, m_element_compactification);
m_cached_element_id = element_id;
}
m_cached_element_id = element_id;
}
const mean_field::fem::FEM &m_fem;
const mean_field::mapping::DomainMapperStateless &m_domain_mapper;
const mean_field::fem::FEM &m_fem;
const mean_field::mapping::DomainMapper &m_domain_mapper;
mfem::Vector m_displacement_local;
mfem::Vector m_displacement_local;
mfem::Array<int> m_displacement_dofs;
mfem::Array<int> m_compactification_dofs;
mfem::Array<int> m_displacement_dofs;
mfem::Array<int> m_compactification_dofs;
mfem::Vector m_element_displacement;
mfem::Vector m_element_compactification;
mfem::Vector m_element_displacement;
mfem::Vector m_element_compactification;
std::unique_ptr<mean_field::mapping::ElementDisplacementData> m_displacement_data;
std::unique_ptr<mean_field::mapping::ElementCompactificationData> m_compactification_data;
std::unique_ptr<mean_field::mapping::ElementDisplacementData>
m_displacement_data;
std::unique_ptr<mean_field::mapping::ElementCompactificationData>
m_compactification_data;
mean_field::mapping::DomainMapperStateless::Workspace m_workspace;
int m_cached_element_id{-1};
};
mean_field::mapping::DomainMapper::Workspace m_workspace;
int m_cached_element_id{-1};
};
} // namespace
namespace mean_field::operators {
PreparedMappedGravitySourceOperator::PreparedMappedGravitySourceOperator(
const fem::FEM &f,
const mapping::DomainMapperStateless &domain_mapper
)
: Operator(
get_operator_height(f),
get_operator_width(f)
),
m_fem(f),
m_domain_mapper(domain_mapper),
m_density_map(
field::make_field_dof_map<
field::Density,
DomainSchema>(*f.densityFes)
),
m_potential_map(
field::make_field_dof_map<
field::Gravity,
DomainSchema>(*f.gravityPotentialFes)
),
m_displacement_map(
field::make_field_dof_map<
field::Displacement,
DomainSchema>(*f.displacementFes)
) {
MFEM_VERIFY(f.mesh != nullptr, "PreparedMappedGravitySourceOperator requires a mesh.");
MFEM_VERIFY(
f.densityFes != nullptr, "PreparedMappedGravitySourceOperator requires the density "
"finite-element space."
);
MFEM_VERIFY(
f.gravityPotentialFes != nullptr, "PreparedMappedGravitySourceOperator requires the "
"gravity-potential "
"finite-element space."
);
MFEM_VERIFY(
f.displacementFes != nullptr, "PreparedMappedGravitySourceOperator requires "
"the displacement finite-element space."
);
MFEM_VERIFY(
f.compactificationFes != nullptr, "PreparedMappedGravitySourceOperator requires the compactification "
"finite-element space."
);
MFEM_VERIFY(
f.compactificationCoordinate != nullptr,
"PreparedMappedGravitySourceOperator requires the compactification "
"coordinate."
);
MFEM_VERIFY(
f.quadratureFactory != nullptr, "PreparedMappedGravitySourceOperator "
"requires the quadrature-rule factory."
);
MFEM_VERIFY(
domain_mapper.GetDimension() == f.mesh->Dimension(),
"The stateless domain-mapper dimension does not match the mesh "
"dimension."
);
PreparedMappedGravitySourceOperator::PreparedMappedGravitySourceOperator(
const fem::FEM &f, const mapping::DomainMapper &domain_mapper)
: Operator(get_operator_height(f), get_operator_width(f)), m_fem(f),
m_domain_mapper(domain_mapper),
m_density_map(field::make_field_dof_map<field::Density, DomainSchema>(
*f.densityFes)),
m_potential_map(field::make_field_dof_map<field::Gravity, DomainSchema>(
*f.gravityPotentialFes)),
m_displacement_map(
field::make_field_dof_map<field::Displacement, DomainSchema>(
*f.displacementFes)) {
MFEM_VERIFY(f.mesh != nullptr,
"PreparedMappedGravitySourceOperator requires a mesh.");
MFEM_VERIFY(f.densityFes != nullptr,
"PreparedMappedGravitySourceOperator requires the density "
"finite-element space.");
MFEM_VERIFY(f.gravityPotentialFes != nullptr,
"PreparedMappedGravitySourceOperator requires the "
"gravity-potential "
"finite-element space.");
MFEM_VERIFY(f.displacementFes != nullptr,
"PreparedMappedGravitySourceOperator requires "
"the displacement finite-element space.");
MFEM_VERIFY(
f.compactificationFes != nullptr,
"PreparedMappedGravitySourceOperator requires the compactification "
"finite-element space.");
MFEM_VERIFY(
f.compactificationCoordinate != nullptr,
"PreparedMappedGravitySourceOperator requires the compactification "
"coordinate.");
MFEM_VERIFY(f.quadratureFactory != nullptr,
"PreparedMappedGravitySourceOperator "
"requires the quadrature-rule factory.");
MFEM_VERIFY(domain_mapper.GetDimension() == f.mesh->Dimension(),
"The stateless domain-mapper dimension does not match the mesh "
"dimension.");
utils::populate_element_mask(f.mesh.get(), utils::DOMAINS::STELLAR, m_stellar_marker);
m_stellar_marker =
utils::domain::make_attribute_marker<utils::domain::Stellar,
DomainSchema>(*f.mesh);
}
void PreparedMappedGravitySourceOperator::Prepare(
const mfem::Vector &displacement) {
MFEM_VERIFY(displacement.Size() == m_displacement_map.reduced_size(),
"PreparedMappedGravitySourceOperator received a displacement "
"vector "
"with the wrong size.");
for (int i = 0; i < displacement.Size(); ++i) {
MFEM_VERIFY(std::isfinite(displacement(i)),
"PreparedMappedGravitySourceOperator received a non-finite "
"displacement value.");
}
m_is_prepared = false;
m_displacement_true.SetSize(m_displacement_map.full_size());
m_displacement_map.scatter(displacement, m_displacement_true);
m_elements.clear();
m_elements.reserve(m_fem.mesh->GetNE());
FrozenMappedGravitySourceCoefficient source_coefficient(
m_fem, m_domain_mapper, m_displacement_true);
for (int element_id = 0; element_id < m_fem.mesh->GetNE(); ++element_id) {
const int attribute = m_fem.mesh->GetAttribute(element_id);
if (attribute <= 0 || attribute > m_stellar_marker.Size() ||
m_stellar_marker[attribute - 1] == 0) {
continue;
}
void PreparedMappedGravitySourceOperator::Prepare(const mfem::Vector &displacement) {
MFEM_VERIFY(
displacement.Size() == m_displacement_map.reduced_size(),
"PreparedMappedGravitySourceOperator received a displacement "
"vector "
"with the wrong size."
);
m_elements.emplace_back();
ElementPAData &data = m_elements.back();
for (int i = 0; i < displacement.Size(); ++i) {
MFEM_VERIFY(
std::isfinite(displacement(i)), "PreparedMappedGravitySourceOperator received a non-finite "
"displacement value."
);
}
data.element_id = element_id;
m_is_prepared = false;
m_displacement_true.SetSize(m_displacement_map.full_size());
m_displacement_map.scatter(displacement, m_displacement_true);
m_elements.clear();
m_elements.reserve(m_fem.mesh->GetNE());
data.density_dof_transformation =
m_fem.densityFes->GetElementDofs(element_id, data.density_dofs);
FrozenMappedGravitySourceCoefficient source_coefficient(m_fem, m_domain_mapper, m_displacement_true);
data.potential_dof_transformation =
m_fem.gravityPotentialFes->GetElementDofs(element_id,
data.potential_dofs);
for (int element_id = 0; element_id < m_fem.mesh->GetNE(); ++element_id) {
const int attribute = m_fem.mesh->GetAttribute(element_id);
const mfem::FiniteElement &density_element =
*m_fem.densityFes->GetFE(element_id);
if (attribute <= 0 || attribute > m_stellar_marker.Size() || m_stellar_marker[attribute - 1] == 0) {
continue;
}
const mfem::FiniteElement &potential_element =
*m_fem.gravityPotentialFes->GetFE(element_id);
m_elements.emplace_back();
ElementPAData &data = m_elements.back();
mfem::ElementTransformation &transformation =
*m_fem.mesh->GetElementTransformation(element_id);
data.element_id = element_id;
const mfem::IntegrationRule &integration_rule = get_source_rule(
m_fem, density_element, potential_element, transformation);
data.density_dof_transformation = m_fem.densityFes->GetElementDofs(element_id, data.density_dofs);
const int quadrature_point_count = integration_rule.GetNPoints();
data.potential_dof_transformation =
m_fem.gravityPotentialFes->GetElementDofs(element_id, data.potential_dofs);
const int density_dof_count = density_element.GetDof();
const mfem::FiniteElement &density_element = *m_fem.densityFes->GetFE(element_id);
const int potential_dof_count = potential_element.GetDof();
const mfem::FiniteElement &potential_element = *m_fem.gravityPotentialFes->GetFE(element_id);
data.density_basis.SetSize(quadrature_point_count, density_dof_count);
mfem::ElementTransformation &transformation = *m_fem.mesh->GetElementTransformation(element_id);
data.potential_basis.SetSize(quadrature_point_count, potential_dof_count);
const mfem::IntegrationRule &integration_rule =
get_source_rule(m_fem, density_element, potential_element, transformation);
data.quadrature_data.SetSize(quadrature_point_count);
const int quadrature_point_count = integration_rule.GetNPoints();
mfem::Vector density_shape(density_dof_count);
mfem::Vector potential_shape(potential_dof_count);
const int density_dof_count = density_element.GetDof();
for (int quadrature_point = 0; quadrature_point < quadrature_point_count;
++quadrature_point) {
const mfem::IntegrationPoint &integration_point =
integration_rule.IntPoint(quadrature_point);
const int potential_dof_count = potential_element.GetDof();
transformation.SetIntPoint(&integration_point);
data.density_basis.SetSize(quadrature_point_count, density_dof_count);
// CalcPhysShape matches the scalar mixed-mass discretization,
// including the finite-element map type.
density_element.CalcPhysShape(transformation, density_shape);
data.potential_basis.SetSize(quadrature_point_count, potential_dof_count);
potential_element.CalcPhysShape(transformation, potential_shape);
data.quadrature_data.SetSize(quadrature_point_count);
for (int i = 0; i < density_dof_count; ++i) {
data.density_basis(quadrature_point, i) = density_shape(i);
}
mfem::Vector density_shape(density_dof_count);
mfem::Vector potential_shape(potential_dof_count);
for (int i = 0; i < potential_dof_count; ++i) {
data.potential_basis(quadrature_point, i) = potential_shape(i);
}
for (int quadrature_point = 0; quadrature_point < quadrature_point_count; ++quadrature_point) {
const mfem::IntegrationPoint &integration_point = integration_rule.IntPoint(quadrature_point);
const double coefficient_value =
source_coefficient.Eval(transformation, integration_point);
transformation.SetIntPoint(&integration_point);
transformation.SetIntPoint(&integration_point);
// CalcPhysShape matches the scalar mixed-mass discretization,
// including the finite-element map type.
density_element.CalcPhysShape(transformation, density_shape);
const double quadrature_value = integration_point.weight *
transformation.Weight() *
coefficient_value;
potential_element.CalcPhysShape(transformation, potential_shape);
MFEM_VERIFY(std::isfinite(quadrature_value) && quadrature_value > 0.0,
"Prepared gravity source operator encountered invalid "
"quadrature data on element "
<< element_id << ", quadrature point " << quadrature_point
<< ".");
for (int i = 0; i < density_dof_count; ++i) {
data.density_basis(quadrature_point, i) = density_shape(i);
}
for (int i = 0; i < potential_dof_count; ++i) {
data.potential_basis(quadrature_point, i) = potential_shape(i);
}
const double coefficient_value = source_coefficient.Eval(transformation, integration_point);
transformation.SetIntPoint(&integration_point);
const double quadrature_value = integration_point.weight * transformation.Weight() * coefficient_value;
MFEM_VERIFY(
std::isfinite(quadrature_value) && quadrature_value > 0.0,
"Prepared gravity source operator encountered invalid "
"quadrature data on element "
<< element_id << ", quadrature point " << quadrature_point << "."
);
data.quadrature_data(quadrature_point) = quadrature_value;
}
}
MFEM_VERIFY(!m_elements.empty(), "PreparedMappedGravitySourceOperator found no stellar elements.");
m_is_prepared = true;
++m_preparation_count;
data.quadrature_data(quadrature_point) = quadrature_value;
}
void PreparedMappedGravitySourceOperator::Mult(
const mfem::Vector &density,
mfem::Vector &action
) const {
MFEM_VERIFY(
m_is_prepared, "PreparedMappedGravitySourceOperator must be prepared before "
"Mult is called."
);
}
MFEM_VERIFY(
density.Size() == Width(), "PreparedMappedGravitySourceOperator received a density vector "
"with the wrong size."
);
MFEM_VERIFY(!m_elements.empty(),
"PreparedMappedGravitySourceOperator found no stellar elements.");
m_density_true.SetSize(m_density_map.full_size());
m_density_map.scatter(density, m_density_true);
m_is_prepared = true;
++m_preparation_count;
}
void PreparedMappedGravitySourceOperator::Mult(const mfem::Vector &density,
mfem::Vector &action) const {
MFEM_VERIFY(m_is_prepared,
"PreparedMappedGravitySourceOperator must be prepared before "
"Mult is called.");
mfem::Vector density_local;
MFEM_VERIFY(density.Size() == Width(),
"PreparedMappedGravitySourceOperator received a density vector "
"with the wrong size.");
true_to_local(*m_fem.densityFes, m_density_true, density_local);
m_density_true.SetSize(m_density_map.full_size());
m_density_map.scatter(density, m_density_true);
mfem::Vector local_action(m_fem.gravityPotentialFes->GetVSize());
local_action = 0.0;
mfem::Vector density_local;
mfem::Vector element_density;
mfem::Vector quadrature_density;
mfem::Vector element_action;
true_to_local(*m_fem.densityFes, m_density_true, density_local);
for (const ElementPAData &data : m_elements) {
density_local.GetSubVector(data.density_dofs, element_density);
mfem::Vector local_action(m_fem.gravityPotentialFes->GetVSize());
local_action = 0.0;
if (data.density_dof_transformation != nullptr) {
data.density_dof_transformation->InvTransformPrimal(element_density);
}
mfem::Vector element_density;
mfem::Vector quadrature_density;
mfem::Vector element_action;
quadrature_density.SetSize(data.quadrature_data.Size());
for (const ElementPAData &data : m_elements) {
density_local.GetSubVector(data.density_dofs, element_density);
// B_density * x_e
data.density_basis.Mult(element_density, quadrature_density);
// D * B_density * x_e
for (int q = 0; q < quadrature_density.Size(); ++q) {
quadrature_density(q) *= data.quadrature_data(q);
}
element_action.SetSize(data.potential_dofs.Size());
// B_potential^T * D * B_density * x_e
data.potential_basis.MultTranspose(quadrature_density, element_action);
if (data.potential_dof_transformation != nullptr) {
data.potential_dof_transformation->TransformDual(element_action);
}
local_action.AddElementVector(data.potential_dofs, element_action);
}
local_to_true(*m_fem.gravityPotentialFes, local_action, m_action_true);
action.SetSize(Height());
m_potential_map.gather(m_action_true, action);
if (data.density_dof_transformation != nullptr) {
data.density_dof_transformation->InvTransformPrimal(element_density);
}
void PreparedMappedGravitySourceOperator::MultTranspose(
const mfem::Vector &potential,
mfem::Vector &action
) const {
MFEM_VERIFY(
m_is_prepared, "PreparedMappedGravitySourceOperator must be prepared before "
"MultTranspose is called."
);
quadrature_density.SetSize(data.quadrature_data.Size());
MFEM_VERIFY(
potential.Size() == Height(), "PreparedMappedGravitySourceOperator received a potential vector "
"with the wrong size."
);
// B_density * x_e
data.density_basis.Mult(element_density, quadrature_density);
m_potential_true.SetSize(m_potential_map.full_size());
m_potential_map.scatter(potential, m_potential_true);
mfem::Vector potential_local;
true_to_local(*m_fem.gravityPotentialFes, m_potential_true, potential_local);
mfem::Vector local_action(m_fem.densityFes->GetVSize());
local_action = 0.0;
mfem::Vector element_potential;
mfem::Vector quadrature_potential;
mfem::Vector element_action;
for (const ElementPAData &data : m_elements) {
potential_local.GetSubVector(data.potential_dofs, element_potential);
if (data.potential_dof_transformation != nullptr) {
data.potential_dof_transformation->InvTransformPrimal(element_potential);
}
quadrature_potential.SetSize(data.quadrature_data.Size());
data.potential_basis.Mult(element_potential, quadrature_potential);
for (int q = 0; q < quadrature_potential.Size(); ++q) {
quadrature_potential(q) *= data.quadrature_data(q);
}
element_action.SetSize(data.density_dofs.Size());
data.density_basis.MultTranspose(quadrature_potential, element_action);
if (data.density_dof_transformation != nullptr) {
data.density_dof_transformation->TransformDual(element_action);
}
local_action.AddElementVector(data.density_dofs, element_action);
}
local_to_true(*m_fem.densityFes, local_action, m_action_true);
action.SetSize(Width());
m_density_map.gather(m_action_true, action);
}
bool PreparedMappedGravitySourceOperator::IsPrepared() const noexcept {
return m_is_prepared;
// D * B_density * x_e
for (int q = 0; q < quadrature_density.Size(); ++q) {
quadrature_density(q) *= data.quadrature_data(q);
}
std::uint64_t PreparedMappedGravitySourceOperator::GetPreparationCount() const noexcept {
return m_preparation_count;
element_action.SetSize(data.potential_dofs.Size());
// B_potential^T * D * B_density * x_e
data.potential_basis.MultTranspose(quadrature_density, element_action);
if (data.potential_dof_transformation != nullptr) {
data.potential_dof_transformation->TransformDual(element_action);
}
const field::FieldDofMap &PreparedMappedGravitySourceOperator::GetDensityMap() const noexcept {
return m_density_map;
local_action.AddElementVector(data.potential_dofs, element_action);
}
local_to_true(*m_fem.gravityPotentialFes, local_action, m_action_true);
action.SetSize(Height());
m_potential_map.gather(m_action_true, action);
}
void PreparedMappedGravitySourceOperator::MultTranspose(
const mfem::Vector &potential, mfem::Vector &action) const {
MFEM_VERIFY(m_is_prepared,
"PreparedMappedGravitySourceOperator must be prepared before "
"MultTranspose is called.");
MFEM_VERIFY(potential.Size() == Height(),
"PreparedMappedGravitySourceOperator received a potential vector "
"with the wrong size.");
m_potential_true.SetSize(m_potential_map.full_size());
m_potential_map.scatter(potential, m_potential_true);
mfem::Vector potential_local;
true_to_local(*m_fem.gravityPotentialFes, m_potential_true, potential_local);
mfem::Vector local_action(m_fem.densityFes->GetVSize());
local_action = 0.0;
mfem::Vector element_potential;
mfem::Vector quadrature_potential;
mfem::Vector element_action;
for (const ElementPAData &data : m_elements) {
potential_local.GetSubVector(data.potential_dofs, element_potential);
if (data.potential_dof_transformation != nullptr) {
data.potential_dof_transformation->InvTransformPrimal(element_potential);
}
const field::FieldDofMap &PreparedMappedGravitySourceOperator::GetPotentialMap() const noexcept {
return m_potential_map;
quadrature_potential.SetSize(data.quadrature_data.Size());
data.potential_basis.Mult(element_potential, quadrature_potential);
for (int q = 0; q < quadrature_potential.Size(); ++q) {
quadrature_potential(q) *= data.quadrature_data(q);
}
const field::FieldDofMap &PreparedMappedGravitySourceOperator::GetDisplacementMap() const noexcept {
return m_displacement_map;
element_action.SetSize(data.density_dofs.Size());
data.density_basis.MultTranspose(quadrature_potential, element_action);
if (data.density_dof_transformation != nullptr) {
data.density_dof_transformation->TransformDual(element_action);
}
local_action.AddElementVector(data.density_dofs, element_action);
}
local_to_true(*m_fem.densityFes, local_action, m_action_true);
action.SetSize(Width());
m_density_map.gather(m_action_true, action);
}
bool PreparedMappedGravitySourceOperator::IsPrepared() const noexcept {
return m_is_prepared;
}
std::uint64_t
PreparedMappedGravitySourceOperator::GetPreparationCount() const noexcept {
return m_preparation_count;
}
const field::FieldDofMap &
PreparedMappedGravitySourceOperator::GetDensityMap() const noexcept {
return m_density_map;
}
const field::FieldDofMap &
PreparedMappedGravitySourceOperator::GetPotentialMap() const noexcept {
return m_potential_map;
}
const field::FieldDofMap &
PreparedMappedGravitySourceOperator::GetDisplacementMap() const noexcept {
return m_displacement_map;
}
} // namespace mean_field::operators

View File

@@ -8,379 +8,405 @@ module mean_field;
import :operators.prepared_hdiv_mass;
namespace {
using DomainSchema = mean_field::utils::domain::CoreEnvelopeVacuumDomainSchema;
using DomainSchema = mean_field::utils::domain::CoreEnvelopeVacuumDomainSchema;
int get_operator_size(const mean_field::fem::FEM &f) {
MFEM_VERIFY(
f.gravityFluxFes != nullptr, "PreparedMappedHDivMassOperator requires the "
"gravity-gradient finite-element space."
);
return mean_field::field::make_field_dof_map<mean_field::field::Gravity, DomainSchema>(*f.gravityFluxFes)
.reduced_size();
int get_operator_size(const mean_field::fem::FEM &f) {
MFEM_VERIFY(f.gravityFluxFes != nullptr,
"PreparedMappedHDivMassOperator requires the "
"gravity-gradient finite-element space.");
return mean_field::field::make_field_dof_map<mean_field::field::Gravity,
DomainSchema>(*f.gravityFluxFes)
.reduced_size();
}
void true_to_local(const mfem::ParFiniteElementSpace &finite_element_space,
const mfem::Vector &true_vector,
mfem::Vector &local_vector) {
local_vector.SetSize(finite_element_space.GetVSize());
const mfem::Operator *prolongation =
finite_element_space.GetProlongationMatrix();
if (prolongation != nullptr) {
prolongation->Mult(true_vector, local_vector);
} else {
local_vector = true_vector;
}
}
int find_representative_element(const mean_field::fem::FEM &f,
const mfem::Array<int> &marker) {
for (int element_id = 0; element_id < f.mesh->GetNE(); ++element_id) {
const int attribute = f.mesh->GetAttribute(element_id);
if (attribute > 0 && attribute <= marker.Size() &&
marker[attribute - 1] != 0) {
return element_id;
}
}
return -1;
}
void validate_uniform_domain_discretization(
const mean_field::fem::FEM &f, const mfem::Array<int> &marker,
const int representative_element_id) {
const mfem::FiniteElement &representative_element =
*f.gravityFluxFes->GetFE(representative_element_id);
const mfem::ElementTransformation &representative_transformation =
*f.mesh->GetElementTransformation(representative_element_id);
for (int element_id = 0; element_id < f.mesh->GetNE(); ++element_id) {
const int attribute = f.mesh->GetAttribute(element_id);
if (attribute <= 0 || attribute > marker.Size() ||
marker[attribute - 1] == 0) {
continue;
}
void true_to_local(
const mfem::ParFiniteElementSpace &finite_element_space,
const mfem::Vector &true_vector,
mfem::Vector &local_vector
) {
local_vector.SetSize(finite_element_space.GetVSize());
const mfem::FiniteElement &element = *f.gravityFluxFes->GetFE(element_id);
const mfem::ElementTransformation &transformation =
*f.mesh->GetElementTransformation(element_id);
const mfem::Operator *prolongation = finite_element_space.GetProlongationMatrix();
if (prolongation != nullptr) {
prolongation->Mult(true_vector, local_vector);
} else {
local_vector = true_vector;
}
}
int find_representative_element(
const mean_field::fem::FEM &f,
const mfem::Array<int> &marker
) {
for (int element_id = 0; element_id < f.mesh->GetNE(); ++element_id) {
const int attribute = f.mesh->GetAttribute(element_id);
if (attribute > 0 && attribute <= marker.Size() && marker[attribute - 1] != 0) {
return element_id;
}
}
return -1;
}
void validate_uniform_domain_discretization(
const mean_field::fem::FEM &f,
const mfem::Array<int> &marker,
const int representative_element_id
) {
const mfem::FiniteElement &representative_element = *f.gravityFluxFes->GetFE(representative_element_id);
const mfem::ElementTransformation &representative_transformation =
*f.mesh->GetElementTransformation(representative_element_id);
for (int element_id = 0; element_id < f.mesh->GetNE(); ++element_id) {
const int attribute = f.mesh->GetAttribute(element_id);
if (attribute <= 0 || attribute > marker.Size() || marker[attribute - 1] == 0) {
continue;
}
const mfem::FiniteElement &element = *f.gravityFluxFes->GetFE(element_id);
const mfem::ElementTransformation &transformation = *f.mesh->GetElementTransformation(element_id);
MFEM_VERIFY(
element.GetGeomType() == representative_element.GetGeomType(),
MFEM_VERIFY(element.GetGeomType() == representative_element.GetGeomType(),
"Prepared H(div) mass domains currently require a uniform "
"element "
"geometry."
);
MFEM_VERIFY(
element.GetOrder() == representative_element.GetOrder(),
"geometry.");
MFEM_VERIFY(element.GetOrder() == representative_element.GetOrder(),
"Prepared H(div) mass domains currently require a uniform "
"finite-element order."
);
MFEM_VERIFY(
transformation.OrderW() == representative_transformation.OrderW(),
"finite-element order.");
MFEM_VERIFY(transformation.OrderW() ==
representative_transformation.OrderW(),
"Prepared H(div) mass domains currently require a uniform "
"geometry-weight order."
);
}
"geometry-weight order.");
}
}
class FrozenMappedHDivMassCoefficient final : public mfem::MatrixCoefficient {
public:
FrozenMappedHDivMassCoefficient(
const mean_field::fem::FEM &f,
const mean_field::mapping::DomainMapper &domain_mapper,
const mfem::Vector &displacement_true, bool elevates_vacuum)
: MatrixCoefficient(domain_mapper.GetDimension()), m_fem(f),
m_domain_mapper(domain_mapper),
m_workspace(domain_mapper.GetDimension()),
m_elevates_vacuum(elevates_vacuum) {
true_to_local(*m_fem.displacementFes, displacement_true,
m_displacement_local);
}
void Eval(mfem::DenseMatrix &mass_tensor,
mfem::ElementTransformation &transformation,
const mfem::IntegrationPoint &integration_point) override {
transformation.SetIntPoint(&integration_point);
const int element_id = transformation.ElementNo;
MFEM_VERIFY(
element_id >= 0 && element_id < m_fem.mesh->GetNE(),
"Mapped H(div) mass coefficient received an invalid element ID.");
const bool element_is_vacuum = DomainSchema::template attribute_belongs_to<
mean_field::utils::domain::Vacuum>(transformation.Attribute);
if (element_is_vacuum != m_elevates_vacuum) {
mass_tensor.SetSize(m_domain_mapper.GetDimension());
mass_tensor = 0.0;
return;
}
class FrozenMappedHDivMassCoefficient final : public mfem::MatrixCoefficient {
public:
FrozenMappedHDivMassCoefficient(
const mean_field::fem::FEM &f,
const mean_field::mapping::DomainMapperStateless &domain_mapper,
const mfem::Vector &displacement_true,
bool elevates_vacuum
)
: MatrixCoefficient(domain_mapper.GetDimension()),
m_fem(f),
m_domain_mapper(domain_mapper),
m_workspace(domain_mapper.GetDimension()),
m_elevates_vacuum(elevates_vacuum) {
true_to_local(*m_fem.displacementFes, displacement_true, m_displacement_local);
}
LoadElement(element_id);
void Eval(
mfem::DenseMatrix &mass_tensor,
mfem::ElementTransformation &transformation,
const mfem::IntegrationPoint &integration_point
) override {
transformation.SetIntPoint(&integration_point);
const mean_field::mapping::ElementMappingData mapping_data{
.displacement = *m_displacement_data,
.compactification = *m_compactification_data};
const int element_id = transformation.ElementNo;
MFEM_VERIFY(
element_id >= 0 && element_id < m_fem.mesh->GetNE(),
"Mapped H(div) mass coefficient received an invalid element ID."
);
mean_field::mapping::VolumeMappingContext mapping_context;
const bool element_is_vacuum = transformation.Attribute == m_domain_mapper.GetVacuumElementAttribute();
const mean_field::mapping::MappingStatus status =
m_domain_mapper.EvaluateVolume(mapping_data, transformation,
integration_point, m_workspace,
mapping_context);
if (element_is_vacuum != m_elevates_vacuum) {
mass_tensor.SetSize(m_domain_mapper.GetDimension());
mass_tensor = 0.0;
return;
}
LoadElement(element_id);
const mean_field::mapping::ElementMappingData mapping_data{
.displacement = *m_displacement_data, .compactification = *m_compactification_data
};
mean_field::mapping::VolumeMappingContext mapping_context;
const mean_field::mapping::MappingStatus status = m_domain_mapper.EvaluateVolume(
mapping_data, transformation, integration_point, m_workspace, mapping_context
);
MFEM_VERIFY(
status == mean_field::mapping::MappingStatus::valid,
MFEM_VERIFY(status == mean_field::mapping::MappingStatus::valid,
"Stateless domain mapping failed while preparing the H(div) "
"mass "
"operator. Mapping status = "
<< static_cast<int>(status) << ", element ID = " << element_id
<< static_cast<int>(status)
<< ", element ID = " << element_id
<< ", element attribute = " << transformation.Attribute
<< ", coefficient domain = " << (m_elevates_vacuum ? "vacuum" : "stellar")
);
<< ", coefficient domain = "
<< (m_elevates_vacuum ? "vacuum" : "stellar"));
const mfem::DenseMatrix &mapping_jacobian = mapping_context.mapping.mapping_jacobian;
const double mapping_determinant = mapping_context.mapping.mapping_determinant;
const mfem::DenseMatrix &mapping_jacobian =
mapping_context.mapping.mapping_jacobian;
const double mapping_determinant =
mapping_context.mapping.mapping_determinant;
MFEM_VERIFY(
std::isfinite(mapping_determinant) && mapping_determinant > 0.0,
MFEM_VERIFY(std::isfinite(mapping_determinant) && mapping_determinant > 0.0,
"Prepared H(div) mass operator encountered a non-positive or "
"non-finite mapping determinant."
);
"non-finite mapping determinant.");
mfem::MultAtB(mapping_jacobian, mapping_jacobian, mass_tensor);
mass_tensor *= 1.0 / mapping_determinant;
}
mfem::MultAtB(mapping_jacobian, mapping_jacobian, mass_tensor);
mass_tensor *= 1.0 / mapping_determinant;
}
private:
void LoadElement(const int element_id) {
if (element_id == m_cached_element_id) {
return;
}
private:
void LoadElement(const int element_id) {
if (element_id == m_cached_element_id) {
return;
}
const mfem::FiniteElement &displacement_element = *m_fem.displacementFes->GetFE(element_id);
const mfem::FiniteElement &compactification_element = *m_fem.compactificationFes->GetFE(element_id);
const mfem::FiniteElement &displacement_element =
*m_fem.displacementFes->GetFE(element_id);
const mfem::FiniteElement &compactification_element =
*m_fem.compactificationFes->GetFE(element_id);
mfem::DofTransformation *displacement_dof_transformation =
m_fem.displacementFes->GetElementVDofs(element_id, m_displacement_dofs);
mfem::DofTransformation *compactification_dof_transformation =
m_fem.compactificationFes->GetElementDofs(element_id, m_compactification_dofs);
mfem::DofTransformation *displacement_dof_transformation =
m_fem.displacementFes->GetElementVDofs(element_id, m_displacement_dofs);
mfem::DofTransformation *compactification_dof_transformation =
m_fem.compactificationFes->GetElementDofs(element_id,
m_compactification_dofs);
m_displacement_local.GetSubVector(m_displacement_dofs, m_element_displacement);
m_fem.compactificationCoordinate->GetSubVector(m_compactification_dofs, m_element_compactification);
m_displacement_local.GetSubVector(m_displacement_dofs,
m_element_displacement);
m_fem.compactificationCoordinate->GetSubVector(m_compactification_dofs,
m_element_compactification);
if (displacement_dof_transformation != nullptr) {
displacement_dof_transformation->InvTransformPrimal(m_element_displacement);
}
if (displacement_dof_transformation != nullptr) {
displacement_dof_transformation->InvTransformPrimal(
m_element_displacement);
}
if (compactification_dof_transformation != nullptr) {
compactification_dof_transformation->InvTransformPrimal(m_element_compactification);
}
if (compactification_dof_transformation != nullptr) {
compactification_dof_transformation->InvTransformPrimal(
m_element_compactification);
}
m_displacement_data = std::make_unique<mean_field::mapping::ElementDisplacementData>(
mean_field::mapping::ElementDisplacementDataFromElementVDofs(
displacement_element, m_element_displacement
)
);
m_displacement_data =
std::make_unique<mean_field::mapping::ElementDisplacementData>(
mean_field::mapping::ElementDisplacementDataFromElementVDofs(
displacement_element, m_element_displacement));
m_compactification_data = std::make_unique<mean_field::mapping::ElementCompactificationData>(
compactification_element, m_element_compactification
);
m_compactification_data =
std::make_unique<mean_field::mapping::ElementCompactificationData>(
compactification_element, m_element_compactification);
m_cached_element_id = element_id;
}
m_cached_element_id = element_id;
}
const mean_field::fem::FEM &m_fem;
const mean_field::mapping::DomainMapperStateless &m_domain_mapper;
const mean_field::fem::FEM &m_fem;
const mean_field::mapping::DomainMapper &m_domain_mapper;
mfem::Vector m_displacement_local;
mfem::Vector m_displacement_local;
mfem::Array<int> m_displacement_dofs;
mfem::Array<int> m_compactification_dofs;
mfem::Array<int> m_displacement_dofs;
mfem::Array<int> m_compactification_dofs;
mfem::Vector m_element_displacement;
mfem::Vector m_element_compactification;
mfem::Vector m_element_displacement;
mfem::Vector m_element_compactification;
std::unique_ptr<mean_field::mapping::ElementDisplacementData> m_displacement_data;
std::unique_ptr<mean_field::mapping::ElementCompactificationData> m_compactification_data;
std::unique_ptr<mean_field::mapping::ElementDisplacementData>
m_displacement_data;
std::unique_ptr<mean_field::mapping::ElementCompactificationData>
m_compactification_data;
mean_field::mapping::DomainMapperStateless::Workspace m_workspace;
int m_cached_element_id{-1};
bool m_elevates_vacuum;
};
mean_field::mapping::DomainMapper::Workspace m_workspace;
int m_cached_element_id{-1};
bool m_elevates_vacuum;
};
} // namespace
namespace mean_field::operators {
PreparedMappedHDivMassOperator::PreparedMappedHDivMassOperator(
const fem::FEM &f,
const mapping::DomainMapperStateless &domain_mapper
)
: Operator(get_operator_size(f)),
m_fem(f),
m_domain_mapper(domain_mapper),
m_flux_map(
field::make_field_dof_map<
field::Gravity,
DomainSchema>(*f.gravityFluxFes)
),
m_displacement_map(
field::make_field_dof_map<
field::Displacement,
DomainSchema>(*f.displacementFes)
) {
MFEM_VERIFY(f.mesh != nullptr, "PreparedMappedHDivMassOperator requires a mesh.");
MFEM_VERIFY(
f.gravityFluxFes != nullptr, "PreparedMappedHDivMassOperator requires the "
"gravity-gradient finite-element space."
);
MFEM_VERIFY(
f.displacementFes != nullptr, "PreparedMappedHDivMassOperator requires the "
"displacement finite-element space."
);
MFEM_VERIFY(
f.compactificationFes != nullptr, "PreparedMappedHDivMassOperator requires the compactification "
"finite-element space."
);
MFEM_VERIFY(
f.compactificationCoordinate != nullptr, "PreparedMappedHDivMassOperator requires the compactification "
"coordinate."
);
MFEM_VERIFY(
f.quadratureFactory != nullptr, "PreparedMappedHDivMassOperator requires the quadrature-rule "
"factory."
);
MFEM_VERIFY(
domain_mapper.GetDimension() == f.mesh->Dimension(),
"The stateless domain-mapper dimension does not match the mesh "
"dimension."
);
PreparedMappedHDivMassOperator::PreparedMappedHDivMassOperator(
const fem::FEM &f, const mapping::DomainMapper &domain_mapper)
: Operator(get_operator_size(f)), m_fem(f), m_domain_mapper(domain_mapper),
m_flux_map(field::make_field_dof_map<field::Gravity, DomainSchema>(
*f.gravityFluxFes)),
m_displacement_map(
field::make_field_dof_map<field::Displacement, DomainSchema>(
*f.displacementFes)) {
MFEM_VERIFY(f.mesh != nullptr,
"PreparedMappedHDivMassOperator requires a mesh.");
MFEM_VERIFY(f.gravityFluxFes != nullptr,
"PreparedMappedHDivMassOperator requires the "
"gravity-gradient finite-element space.");
MFEM_VERIFY(f.displacementFes != nullptr,
"PreparedMappedHDivMassOperator requires the "
"displacement finite-element space.");
MFEM_VERIFY(f.compactificationFes != nullptr,
"PreparedMappedHDivMassOperator requires the compactification "
"finite-element space.");
MFEM_VERIFY(f.compactificationCoordinate != nullptr,
"PreparedMappedHDivMassOperator requires the compactification "
"coordinate.");
MFEM_VERIFY(f.quadratureFactory != nullptr,
"PreparedMappedHDivMassOperator requires the quadrature-rule "
"factory.");
MFEM_VERIFY(domain_mapper.GetDimension() == f.mesh->Dimension(),
"The stateless domain-mapper dimension does not match the mesh "
"dimension.");
utils::populate_element_mask(f.mesh.get(), utils::DOMAINS::STELLAR, m_stellar_marker);
utils::populate_element_mask(f.mesh.get(), utils::DOMAINS::VACUUM, m_vacuum_marker);
m_stellar_marker =
utils::domain::make_attribute_marker<utils::domain::Stellar,
DomainSchema>(*f.mesh);
m_vacuum_marker =
utils::domain::make_attribute_marker<utils::domain::Vacuum, DomainSchema>(
*f.mesh);
const int stellar_element_id = find_representative_element(f, m_stellar_marker);
const int vacuum_element_id = find_representative_element(f, m_vacuum_marker);
const int stellar_element_id =
find_representative_element(f, m_stellar_marker);
const int vacuum_element_id = find_representative_element(f, m_vacuum_marker);
MFEM_VERIFY(
stellar_element_id >= 0, "PreparedMappedHDivMassOperator requires "
"at least one stellar element."
);
MFEM_VERIFY(
vacuum_element_id >= 0, "PreparedMappedHDivMassOperator requires at "
"least one compactified vacuum element."
);
MFEM_VERIFY(stellar_element_id >= 0,
"PreparedMappedHDivMassOperator requires "
"at least one stellar element.");
MFEM_VERIFY(vacuum_element_id >= 0,
"PreparedMappedHDivMassOperator requires at "
"least one compactified vacuum element.");
validate_uniform_domain_discretization(f, m_stellar_marker, stellar_element_id);
validate_uniform_domain_discretization(f, m_vacuum_marker, vacuum_element_id);
}
validate_uniform_domain_discretization(f, m_stellar_marker,
stellar_element_id);
validate_uniform_domain_discretization(f, m_vacuum_marker, vacuum_element_id);
}
void PreparedMappedHDivMassOperator::Prepare(const mfem::Vector &displacement) {
MFEM_VERIFY(
displacement.Size() == m_displacement_map.reduced_size(),
"PreparedMappedHDivMassOperator received a displacement vector "
"with "
"the wrong size."
);
void PreparedMappedHDivMassOperator::Prepare(const mfem::Vector &displacement) {
MFEM_VERIFY(displacement.Size() == m_displacement_map.reduced_size(),
"PreparedMappedHDivMassOperator received a displacement vector "
"with "
"the wrong size.");
for (int i = 0; i < displacement.Size(); ++i) {
MFEM_VERIFY(
std::isfinite(displacement(i)), "PreparedMappedHDivMassOperator received a non-finite "
"displacement "
"value."
);
}
for (int i = 0; i < displacement.Size(); ++i) {
MFEM_VERIFY(std::isfinite(displacement(i)),
"PreparedMappedHDivMassOperator received a non-finite "
"displacement "
"value.");
}
m_displacement_true.SetSize(m_displacement_map.full_size());
m_displacement_map.scatter(displacement, m_displacement_true);
m_displacement_true.SetSize(m_displacement_map.full_size());
m_displacement_map.scatter(displacement, m_displacement_true);
const int stellar_element_id = find_representative_element(m_fem, m_stellar_marker);
const int vacuum_element_id = find_representative_element(m_fem, m_vacuum_marker);
const int stellar_element_id =
find_representative_element(m_fem, m_stellar_marker);
const int vacuum_element_id =
find_representative_element(m_fem, m_vacuum_marker);
const mfem::FiniteElement &stellar_element = *m_fem.gravityFluxFes->GetFE(stellar_element_id);
const mfem::FiniteElement &vacuum_element = *m_fem.gravityFluxFes->GetFE(vacuum_element_id);
const mfem::FiniteElement &stellar_element =
*m_fem.gravityFluxFes->GetFE(stellar_element_id);
const mfem::FiniteElement &vacuum_element =
*m_fem.gravityFluxFes->GetFE(vacuum_element_id);
mfem::ElementTransformation &stellar_transformation = *m_fem.mesh->GetElementTransformation(stellar_element_id);
mfem::ElementTransformation &vacuum_transformation = *m_fem.mesh->GetElementTransformation(vacuum_element_id);
mfem::ElementTransformation &stellar_transformation =
*m_fem.mesh->GetElementTransformation(stellar_element_id);
mfem::ElementTransformation &vacuum_transformation =
*m_fem.mesh->GetElementTransformation(vacuum_element_id);
m_mass_form.reset();
m_stellar_mass_coefficient.reset();
m_vacuum_mass_coefficient.reset();
m_stellar_mass_form.reset();
m_vacuum_mass_form.reset();
m_stellar_mass_coefficient.reset();
m_vacuum_mass_coefficient.reset();
m_stellar_mass_coefficient =
std::make_unique<FrozenMappedHDivMassCoefficient>(m_fem, m_domain_mapper, m_displacement_true, false);
m_vacuum_mass_coefficient =
std::make_unique<FrozenMappedHDivMassCoefficient>(m_fem, m_domain_mapper, m_displacement_true, true);
m_stellar_mass_coefficient =
std::make_unique<FrozenMappedHDivMassCoefficient>(
m_fem, m_domain_mapper, m_displacement_true, false);
m_vacuum_mass_coefficient = std::make_unique<FrozenMappedHDivMassCoefficient>(
m_fem, m_domain_mapper, m_displacement_true, true);
m_mass_form = std::make_unique<mfem::ParBilinearForm>(m_fem.gravityFluxFes.get());
m_mass_form->SetAssemblyLevel(mfem::AssemblyLevel::PARTIAL);
m_stellar_mass_form =
std::make_unique<mfem::ParBilinearForm>(m_fem.gravityFluxFes.get());
m_vacuum_mass_form =
std::make_unique<mfem::ParBilinearForm>(m_fem.gravityFluxFes.get());
m_stellar_mass_form->SetAssemblyLevel(mfem::AssemblyLevel::PARTIAL);
m_vacuum_mass_form->SetAssemblyLevel(mfem::AssemblyLevel::PARTIAL);
auto stellar_integrator = std::make_unique<mfem::VectorFEMassIntegrator>(*m_stellar_mass_coefficient);
auto vacuum_integrator = std::make_unique<mfem::VectorFEMassIntegrator>(*m_vacuum_mass_coefficient);
auto stellar_integrator = std::make_unique<mfem::VectorFEMassIntegrator>(
*m_stellar_mass_coefficient);
auto vacuum_integrator = std::make_unique<mfem::VectorFEMassIntegrator>(
*m_vacuum_mass_coefficient);
m_fem.quadratureFactory->configure_gravity_hdiv_mass(
*stellar_integrator, quadrature::QuadratureRole::discretization, stellar_element, stellar_transformation,
utils::DOMAINS::STELLAR, quadrature::MappingKind::general
);
m_fem.quadratureFactory->configure_gravity_hdiv_mass(
*stellar_integrator, quadrature::QuadratureRole::discretization,
stellar_element, stellar_transformation, utils::DOMAINS::STELLAR,
quadrature::MappingKind::general);
m_fem.quadratureFactory->configure_gravity_hdiv_mass(
*vacuum_integrator, quadrature::QuadratureRole::discretization, vacuum_element, vacuum_transformation,
utils::DOMAINS::VACUUM, quadrature::MappingKind::kelvin
);
m_fem.quadratureFactory->configure_gravity_hdiv_mass(
*vacuum_integrator, quadrature::QuadratureRole::discretization,
vacuum_element, vacuum_transformation, utils::DOMAINS::VACUUM,
quadrature::MappingKind::kelvin);
m_mass_form->AddDomainIntegrator(stellar_integrator.release(), m_stellar_marker);
m_mass_form->AddDomainIntegrator(vacuum_integrator.release(), m_vacuum_marker);
m_mass_form->Assemble();
m_stellar_mass_form->AddDomainIntegrator(stellar_integrator.release(),
m_stellar_marker);
m_vacuum_mass_form->AddDomainIntegrator(vacuum_integrator.release(),
m_vacuum_marker);
m_stellar_mass_form->Assemble();
m_vacuum_mass_form->Assemble();
m_is_prepared = true;
++m_preparation_count;
}
m_is_prepared = true;
++m_preparation_count;
}
void PreparedMappedHDivMassOperator::Mult(
const mfem::Vector &gravity_gradient,
mfem::Vector &action
) const {
MFEM_VERIFY(
m_is_prepared, "PreparedMappedHDivMassOperator must be prepared "
"before Mult is called."
);
MFEM_VERIFY(
m_mass_form != nullptr, "PreparedMappedHDivMassOperator has no "
"assembled partial-assembly form."
);
MFEM_VERIFY(
gravity_gradient.Size() == Width(), "PreparedMappedHDivMassOperator received a gravity-gradient vector "
"with the wrong size."
);
void PreparedMappedHDivMassOperator::Mult(const mfem::Vector &gravity_gradient,
mfem::Vector &action) const {
MFEM_VERIFY(m_is_prepared, "PreparedMappedHDivMassOperator must be prepared "
"before Mult is called.");
MFEM_VERIFY(
m_stellar_mass_form != nullptr && m_vacuum_mass_form != nullptr,
"PreparedMappedHDivMassOperator has incomplete domain mass forms.");
MFEM_VERIFY(
gravity_gradient.Size() == Width(),
"PreparedMappedHDivMassOperator received a gravity-gradient vector "
"with the wrong size.");
m_flux_true.SetSize(m_flux_map.full_size());
m_action_true.SetSize(m_flux_map.full_size());
m_flux_map.scatter(gravity_gradient, m_flux_true);
m_mass_form->Mult(m_flux_true, m_action_true);
action.SetSize(Height());
m_flux_map.gather(m_action_true, action);
}
m_flux_true.SetSize(m_flux_map.full_size());
m_action_true.SetSize(m_flux_map.full_size());
m_domain_action_true.SetSize(m_flux_map.full_size());
m_flux_map.scatter(gravity_gradient, m_flux_true);
m_stellar_mass_form->Mult(m_flux_true, m_action_true);
m_vacuum_mass_form->Mult(m_flux_true, m_domain_action_true);
m_action_true += m_domain_action_true;
action.SetSize(Height());
m_flux_map.gather(m_action_true, action);
}
bool PreparedMappedHDivMassOperator::IsPrepared() const noexcept {
return m_is_prepared;
}
void PreparedMappedHDivMassOperator::AssembleDiagonal(
mfem::Vector &diagonal) const {
mfem::Vector true_diagonal;
AssembleTrueDiagonal(true_diagonal);
diagonal.SetSize(Height());
m_flux_map.gather(true_diagonal, diagonal);
}
std::uint64_t PreparedMappedHDivMassOperator::GetPreparationCount() const noexcept {
return m_preparation_count;
}
void PreparedMappedHDivMassOperator::AssembleTrueDiagonal(
mfem::Vector &diagonal) const {
MFEM_VERIFY(m_is_prepared, "PreparedMappedHDivMassOperator must be prepared "
"before assembling its diagonal.");
MFEM_VERIFY(
m_stellar_mass_form != nullptr && m_vacuum_mass_form != nullptr,
"PreparedMappedHDivMassOperator has incomplete domain mass forms.");
const field::FieldDofMap &PreparedMappedHDivMassOperator::GetFluxMap() const noexcept {
return m_flux_map;
}
diagonal.SetSize(m_flux_map.full_size());
mfem::Vector domain_diagonal(m_flux_map.full_size());
m_stellar_mass_form->AssembleDiagonal(diagonal);
m_vacuum_mass_form->AssembleDiagonal(domain_diagonal);
diagonal += domain_diagonal;
}
const field::FieldDofMap &PreparedMappedHDivMassOperator::GetDisplacementMap() const noexcept {
return m_displacement_map;
}
bool PreparedMappedHDivMassOperator::IsPrepared() const noexcept {
return m_is_prepared;
}
std::uint64_t
PreparedMappedHDivMassOperator::GetPreparationCount() const noexcept {
return m_preparation_count;
}
const field::FieldDofMap &
PreparedMappedHDivMassOperator::GetFluxMap() const noexcept {
return m_flux_map;
}
const field::FieldDofMap &
PreparedMappedHDivMassOperator::GetDisplacementMap() const noexcept {
return m_displacement_map;
}
} // namespace mean_field::operators

File diff suppressed because it is too large Load Diff

View File

@@ -213,7 +213,7 @@ namespace mean_field::operators {
PreparedPressureForceOperator::PreparedPressureForceOperator(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const mapping::DomainMapper &domainMapper,
const eos::Polytrope &equationOfState
)
: PreparedPressureForceOperator(
@@ -226,7 +226,7 @@ namespace mean_field::operators {
PreparedPressureForceOperator::PreparedPressureForceOperator(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const mapping::DomainMapper &domainMapper,
const eos::Polytrope &equationOfState,
ConstructionData constructionData
)
@@ -460,7 +460,7 @@ namespace mean_field::operators {
true_to_local(*m_fem.displacementFes, m_baseDisplacementTrue, displacementLocal);
mapping::DomainMapperStateless::Workspace workspace(m_fem.mesh->Dimension());
mapping::DomainMapper::Workspace workspace(m_fem.mesh->Dimension());
mfem::Vector elementDisplacement;
mfem::Vector elementCompactification;
@@ -807,7 +807,7 @@ namespace mean_field::operators {
localAction = 0.0;
mapping::DomainMapperStateless::Workspace workspace(m_fem.mesh->Dimension());
mapping::DomainMapper::Workspace workspace(m_fem.mesh->Dimension());
mfem::Vector elementDisplacementVariation;
mfem::Vector elementAction;

View File

@@ -10,7 +10,7 @@ import :operators.prepared_rotational_displacement_force;
namespace mean_field::operators {
PreparedRotationalDisplacementForceOperator::PreparedRotationalDisplacementForceOperator(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper
const mapping::DomainMapper &domainMapper
)
: m_fem(f),
m_domainMapper(domainMapper),

View File

@@ -9,28 +9,16 @@ module;
module mean_field;
import :operators.prepared_stellar_equilibrium;
import :physics.gravity;
namespace {
using DomainSchema = mean_field::utils::domain::CoreEnvelopeVacuumDomainSchema;
[[nodiscard]] mean_field::fem::FEM &ensure_gravity_static_operators(mean_field::fem::FEM &f) {
void verify_coupled_discretization(const mean_field::fem::FEM &f) {
MFEM_VERIFY(
f.mesh != nullptr && f.densityFes != nullptr && f.displacementFes != nullptr &&
f.gravityFluxFes != nullptr && f.gravityPotentialFes != nullptr && f.enthalpyFes != nullptr,
"PreparedStellarEquilibriumOperator requires the complete coupled finite-element discretization."
);
if (f.gravityContext.b_form == nullptr || f.gravityContext.BT == nullptr) {
mean_field::physics::update_stiffness_matrix(f);
}
MFEM_VERIFY(
f.gravityContext.b_form != nullptr && f.gravityContext.BT != nullptr,
"PreparedStellarEquilibriumOperator could not initialize the static gravity divergence operators."
);
return f;
}
[[nodiscard]] mean_field::operators::StellarEquilibriumLayout make_layout(
@@ -318,13 +306,13 @@ namespace mean_field::operators {
PreparedStellarEquilibriumOperator::ConstructionData
PreparedStellarEquilibriumOperator::MakeConstructionData(fem::FEM &f) {
ensure_gravity_static_operators(f);
verify_coupled_discretization(f);
return ConstructionData(f);
}
PreparedStellarEquilibriumOperator::PreparedStellarEquilibriumOperator(
fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const mapping::DomainMapper &domainMapper,
const eos::Polytrope &equationOfState,
const models::StellarModel &stellarModel
)
@@ -338,7 +326,7 @@ namespace mean_field::operators {
PreparedStellarEquilibriumOperator::PreparedStellarEquilibriumOperator(
fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const mapping::DomainMapper &domainMapper,
const eos::Polytrope &equationOfState,
const double targetMass
)
@@ -353,7 +341,7 @@ namespace mean_field::operators {
PreparedStellarEquilibriumOperator::PreparedStellarEquilibriumOperator(
fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const mapping::DomainMapper &domainMapper,
const eos::Polytrope &equationOfState,
const double targetMass,
ConstructionData constructionData
@@ -587,8 +575,7 @@ namespace mean_field::operators {
);
assign_residual_block(
m_cachedResidual, m_layout, enthalpyResidual, hydrostatic,
"The hydrostatic residual has the wrong size."
m_cachedResidual, m_layout, enthalpyResidual, hydrostatic, "The hydrostatic residual has the wrong size."
);
assign_residual_block(
@@ -712,8 +699,7 @@ namespace mean_field::operators {
);
assign_residual_block(
action, m_layout, enthalpyResidual, hydrostaticAction,
"The hydrostatic Jacobian action has the wrong size."
action, m_layout, enthalpyResidual, hydrostaticAction, "The hydrostatic Jacobian action has the wrong size."
);
assign_residual_block(

View File

@@ -2,171 +2,10 @@ module;
#include "mfem.hpp"
#include <array>
#include <cmath>
#include <format>
#include <source_location>
#include <string_view>
#include <unordered_map>
module mean_field;
import :mapping.coefficients;
import :analysis.integral;
namespace {
double centrifugal_potential(
const mfem::Vector &phys_x,
const double omega
) {
const double s2 = std::pow(phys_x(0), 2) + std::pow(phys_x(1), 2);
return -0.5 * s2 * std::pow(omega, 2);
}
void grid_function_to_true_dofs(
const mfem::ParFiniteElementSpace &finite_element_space,
const mfem::GridFunction &grid_function,
mfem::Vector &true_dofs
) {
MFEM_VERIFY(
grid_function.Size() == finite_element_space.GetVSize(),
"The grid function does not match the requested finite-element "
"space."
);
true_dofs.SetSize(finite_element_space.GetTrueVSize());
const mfem::Operator *restriction = finite_element_space.GetRestrictionMatrix();
if (restriction != nullptr) {
restriction->Mult(grid_function, true_dofs);
} else {
MFEM_VERIFY(
grid_function.Size() == true_dofs.Size(), "A finite-element space without a restriction operator must "
"have "
"matching local and true sizes."
);
true_dofs = grid_function;
}
}
} // namespace
namespace mean_field::physics {
GravitySolution grav_potential(
fem::FEM &f,
const utils::Args &args,
const mfem::GridFunction &rho,
const bool phi_warm
) {
MFEM_VERIFY(
f.densityFes != nullptr && rho.FESpace() == f.densityFes.get(),
"Gravity solve requires rho to use the registered density space."
);
MFEM_VERIFY(f.gravityPotentialFes != nullptr, "Gravity solve requires the registered gravity-potential space.");
mfem::Array<int> outer_bdr_marker(f.mesh->bdr_attributes.Max());
outer_bdr_marker = 0;
outer_bdr_marker[1] = 1;
mfem::ParLinearForm g_rhs(f.gravityFluxFes.get());
// ReSharper disable once CppTooWideScope
std::unique_ptr<mfem::Coefficient> boundary_potential_coeff;
if (!f.has_mapping()) { // We only need to explicitly add a boundary
// integrator if a mapping is not being used. In
// the case where the outer domain has been
// compactified the φ=0 boundary condition is
// the natural condition and MFEM automatically
// handles this
auto boundary_potential = [&f](const mfem::Vector &x_physical) {
return l2_multipole_potential(f, utils::MASS, x_physical);
};
boundary_potential_coeff = std::make_unique<mfem::FunctionCoefficient>(boundary_potential);
auto boundary_integrator =
std::make_unique<mfem::VectorFEBoundaryFluxLFIntegrator>(*boundary_potential_coeff);
const mfem::FiniteElement &boundary_element = *f.gravityFluxFes->GetTypicalTraceElement();
f.quadratureFactory->configure_gravity_boundary(
*boundary_integrator, quadrature::QuadratureRole::discretization, boundary_element,
utils::DOMAINS::VACUUM, quadrature::MappingKind::none
);
g_rhs.AddBoundaryIntegrator(boundary_integrator.release(), outer_bdr_marker);
}
g_rhs.Assemble();
mfem::GridFunctionCoefficient rho_coeff(&rho);
mfem::ConstantCoefficient G4pi(4.0 * M_PI * utils::G);
mfem::ProductCoefficient source_coeff(G4pi, rho_coeff);
mfem::ParLinearForm f_rhs(f.gravityPotentialFes.get());
std::unique_ptr<mfem::Coefficient> mapped_source_coeff;
mfem::Coefficient *active_source_coeff = &source_coeff;
quadrature::MappingKind source_mapping_kind = quadrature::MappingKind::none;
if (f.has_mapping()) {
mapped_source_coeff = std::make_unique<mapping::MappedScalarCoefficient>(*f.mapping, source_coeff);
active_source_coeff = mapped_source_coeff.get();
source_mapping_kind = quadrature::MappingKind::general;
}
auto source_integrator = std::make_unique<mfem::DomainLFIntegrator>(*active_source_coeff);
const mfem::FiniteElement &source_test_element = *f.gravityPotentialFes->GetTypicalFE();
const mfem::ElementTransformation &source_transformation = *f.mesh->GetElementTransformation(0);
const int source_coefficient_order = f.densityFes->GetMaxElementOrder();
f.quadratureFactory->configure_gravity_source(
*source_integrator, quadrature::QuadratureRole::discretization, source_test_element, source_transformation,
source_coefficient_order, utils::DOMAINS::STELLAR, source_mapping_kind
);
f_rhs.AddDomainIntegrator(source_integrator.release(), f.gravityContext.stellar_mask);
f_rhs.Assemble();
mfem::BlockVector RHS(f.gravityBlockTrueOffsets);
RHS.GetBlock(0) = *g_rhs.ParallelAssemble();
RHS.GetBlock(1) = *f_rhs.ParallelAssemble();
mfem::BlockVector X(f.gravityBlockTrueOffsets);
X = 0.0;
f.gravityContext.minres->SetOperator(*f.gravityContext.block_A);
f.gravityContext.minres->Mult(RHS, X);
GravitySolution solution(f);
solution.gradPhi.SetFromTrueDofs(X.GetBlock(0));
solution.phi.SetFromTrueDofs(X.GetBlock(1));
return solution;
}
mfem::GridFunction get_potential(
fem::FEM &fem,
const utils::Args &args,
const mfem::GridFunction &rho,
const bool warm
) {
auto phi = grav_potential(fem, args, rho, warm);
if (args.r.enabled) {
auto rot = [&fem, &args](const mfem::Vector &x) {
mfem::Vector rel_x = x;
rel_x -= fem.com;
return centrifugal_potential(rel_x, args.r.omega);
};
std::unique_ptr<mfem::Coefficient> centrifugal_coeff;
if (fem.has_mapping()) {
centrifugal_coeff = std::make_unique<mapping::PhysicalPositionFunctionCoefficient>(*fem.mapping, rot);
} else {
centrifugal_coeff = std::make_unique<mfem::FunctionCoefficient>(rot);
}
mfem::GridFunction centrifugal_gf(fem.gravityPotentialFes.get());
centrifugal_gf.ProjectCoefficient(*centrifugal_coeff);
phi.phi += centrifugal_gf;
}
return phi.phi;
}
mfem::DenseMatrix compute_quadrupole_moment_tensor(
const fem::FEM &fem,
const mfem::GridFunction &rho,
@@ -175,9 +14,15 @@ namespace mean_field::physics {
const int dim = fem.mesh->Dimension();
mfem::DenseMatrix local_Q(dim, dim);
local_Q = 0.0;
using DomainSchema = utils::domain::CoreEnvelopeVacuumDomainSchema;
mapping::GridFunctionMappingEvaluator mapping_evaluator(
*fem.domainMapperStateless, *fem.displacement,
*fem.compactificationCoordinate
);
for (int i = 0; i < fem.mesh->GetNE(); ++i) {
if (fem.mesh->GetAttribute(i) == 3)
if (!DomainSchema::template attribute_belongs_to<utils::domain::Stellar>(
fem.mesh->GetAttribute(i)))
continue;
mfem::ElementTransformation *trans = fem.mesh->GetElementTransformation(i);
@@ -193,20 +38,17 @@ namespace mean_field::physics {
const mfem::IntegrationPoint &ip = ir.IntPoint(j);
trans->SetIntPoint(&ip);
double weight = trans->Weight() * ip.weight;
if (fem.has_mapping()) {
weight *= fem.mapping->ComputeDetJ(*trans, ip);
}
mapping::VolumeMappingContext mapping_context;
MFEM_VERIFY(
mapping_evaluator.EvaluateVolume(*trans, ip, mapping_context) ==
mapping::MappingStatus::valid,
"Quadrupole integration encountered an invalid mapping."
);
const double weight = mapping_context.quadrature.weight;
const double rho_val = rho.GetValue(i, ip);
mfem::Vector phys_point(dim);
if (fem.has_mapping()) {
fem.mapping->GetPhysicalPoint(*trans, ip, phys_point);
} else {
trans->Transform(ip, phys_point);
}
const mfem::Vector &phys_point = mapping_context.mapping.physical_position;
mfem::Vector x_prime(dim);
double r_sq = 0.0;
@@ -261,141 +103,7 @@ namespace mean_field::physics {
return l0_contrib + l2_contrib;
}
void update_stiffness_matrix(fem::FEM &f) {
mfem::Array<int> empty_tdofs;
// ==========================================
// 1. Partially Assemble the High-Order Mass Block
// ==========================================
f.gravityContext.m_form = std::make_unique<mfem::ParBilinearForm>(f.gravityFluxFes.get());
f.gravityContext.m_form->SetAssemblyLevel(mfem::AssemblyLevel::PARTIAL);
std::unique_ptr<mfem::VectorFEMassIntegrator> hdiv_mass_integrator;
if (f.has_mapping()) {
f.gravityContext.mapped_hdiv_mass_coeff =
std::make_unique<mapping::MappedHDivMassCoefficient>(*f.mapping, f.mesh->Dimension());
hdiv_mass_integrator =
std::make_unique<mfem::VectorFEMassIntegrator>(*f.gravityContext.mapped_hdiv_mass_coeff);
} else {
f.gravityContext.mapped_hdiv_mass_coeff.reset();
hdiv_mass_integrator = std::make_unique<mfem::VectorFEMassIntegrator>();
}
const mfem::FiniteElement &hdiv_element = *f.gravityFluxFes->GetTypicalFE();
const mfem::ElementTransformation &hdiv_transformation = *f.mesh->GetElementTransformation(0);
const quadrature::MappingKind mapping_kind =
f.has_mapping() ? quadrature::MappingKind::general : quadrature::MappingKind::none;
f.quadratureFactory->configure_gravity_hdiv_mass(
*hdiv_mass_integrator, quadrature::QuadratureRole::discretization, hdiv_element, hdiv_transformation,
utils::DOMAINS::ALL, mapping_kind
);
f.gravityContext.m_form->AddDomainIntegrator(hdiv_mass_integrator.release());
f.gravityContext.m_form->Assemble();
// ==========================================
// 2. Partially Assemble the High-Order Divergence Block
// ==========================================
f.gravityContext.b_form =
std::make_unique<mfem::ParMixedBilinearForm>(f.gravityFluxFes.get(), f.gravityPotentialFes.get());
f.gravityContext.b_form->SetAssemblyLevel(mfem::AssemblyLevel::PARTIAL);
auto divergence_discretization_integrator = std::make_unique<mfem::VectorFEDivergenceIntegrator>();
const mfem::FiniteElement &divergence_discretization_test_element = *f.gravityPotentialFes->GetTypicalFE();
f.quadratureFactory->configure_gravity_divergence(
*divergence_discretization_integrator, quadrature::QuadratureRole::discretization, hdiv_element,
divergence_discretization_test_element, hdiv_transformation, utils::DOMAINS::ALL,
quadrature::MappingKind::none
);
f.gravityContext.b_form->AddDomainIntegrator(divergence_discretization_integrator.release());
f.gravityContext.b_form->Assemble();
MFEM_VERIFY(
f.domainMapperStateless != nullptr, "Gravity source partial assembly requires the stateless domain "
"mapper."
);
mfem::Vector displacement_true(f.displacementFes->GetTrueVSize());
displacement_true = 0.0;
const mfem::GridFunction *active_displacement = f.mapping->GetDisplacement();
if (active_displacement != nullptr) {
grid_function_to_true_dofs(*f.displacementFes, *active_displacement, displacement_true);
}
auto source_form =
std::make_unique<operators::PreparedMappedGravitySourceOperator>(f, *f.domainMapperStateless);
using DomainSchema = utils::domain::CoreEnvelopeVacuumDomainSchema;
const field::FieldDofMap displacement_map =
field::make_field_dof_map<field::Displacement, DomainSchema>(*f.displacementFes);
source_form->Prepare(displacement_map.gather(displacement_true));
f.gravityContext.source_form = std::move(source_form);
// ==========================================
// 3. Assemble Global Block Operator
// ==========================================
f.gravityContext.BT = std::make_unique<mfem::TransposeOperator>(f.gravityContext.b_form.get());
f.gravityContext.block_A = std::make_unique<mfem::BlockOperator>(f.gravityBlockTrueOffsets);
f.gravityContext.block_A->SetBlock(0, 0, f.gravityContext.m_form.get());
f.gravityContext.block_A->SetBlock(0, 1, f.gravityContext.BT.get());
f.gravityContext.block_A->SetBlock(1, 0, f.gravityContext.b_form.get());
// ==========================================
// 4. Construct a mapped Schur preconditioner
// ==========================================
mfem::Vector mass_diagonal(f.gravityFluxFes->GetTrueVSize());
f.gravityContext.m_form->AssembleDiagonal(mass_diagonal);
mfem::Vector inverse_mass_diagonal(mass_diagonal);
for (int i = 0; i < inverse_mass_diagonal.Size(); ++i) {
MFEM_VERIFY(
std::isfinite(inverse_mass_diagonal(i)) && inverse_mass_diagonal(i) > 0.0,
"Mapped RT mass matrix has a non-positive or non-finite "
"diagonal "
"entry."
);
inverse_mass_diagonal(i) = 1.0 / inverse_mass_diagonal(i);
}
mfem::ParMixedBilinearForm b_preconditioner(f.gravityFluxFes.get(), f.gravityPotentialFes.get());
auto divergence_preconditioner_integrator = std::make_unique<mfem::VectorFEDivergenceIntegrator>();
const mfem::FiniteElement &divergence_trial_element = *f.gravityFluxFes->GetTypicalFE();
const mfem::FiniteElement &divergence_test_element = *f.gravityPotentialFes->GetTypicalFE();
const mfem::ElementTransformation &divergence_transformation = *f.mesh->GetElementTransformation(0);
f.quadratureFactory->configure_gravity_divergence(
*divergence_preconditioner_integrator, quadrature::QuadratureRole::preconditioner, divergence_trial_element,
divergence_test_element, divergence_transformation, utils::DOMAINS::ALL, quadrature::MappingKind::none
);
b_preconditioner.AddDomainIntegrator(divergence_preconditioner_integrator.release());
b_preconditioner.Assemble();
b_preconditioner.Finalize();
std::unique_ptr<mfem::HypreParMatrix> b_matrix(b_preconditioner.ParallelAssemble());
std::unique_ptr<mfem::HypreParMatrix> inverse_mass_b_transpose(b_matrix->Transpose());
inverse_mass_b_transpose->ScaleRows(inverse_mass_diagonal);
f.gravityContext.Schur.reset(mfem::ParMult(b_matrix.get(), inverse_mass_b_transpose.get()));
// ==========================================
// 5. Wire Up the preconditioners
// ==========================================
f.gravityContext.prec_M = std::make_unique<mfem::OperatorJacobiSmoother>(mass_diagonal, empty_tdofs);
f.gravityContext.prec_Phi->SetOperator(*f.gravityContext.Schur);
f.gravityContext.block_prec->SetDiagonalBlock(0, f.gravityContext.prec_M.get());
f.gravityContext.block_prec->SetDiagonalBlock(1, f.gravityContext.prec_Phi.get());
}
GravitySolution grav_potential_new(
GravitySolution solve_gravity_field(
fem::FEM &f,
const utils::Args &args,
const mfem::GridFunction &rho,
@@ -417,13 +125,6 @@ namespace mean_field::physics {
"displacement finite-element space."
);
MFEM_VERIFY(f.domainMapperStateless != nullptr, "Gravity initialization requires the stateless domain mapper.");
MFEM_VERIFY(f.gravityContext.b_form != nullptr, "Gravity initialization requires the divergence operator.");
MFEM_VERIFY(
f.gravityContext.BT != nullptr, "Gravity initialization requires the transpose divergence operator."
);
MFEM_VERIFY(
f.gravityContext.block_prec != nullptr, "Gravity initialization requires the gravity block preconditioner."
);
MFEM_VERIFY(
rho.FESpace() == f.densityFes.get(), "Gravity initialization requires density to use the FEM density "
"space."
@@ -434,6 +135,7 @@ namespace mean_field::physics {
"Vec_H1 "
"space."
);
MFEM_VERIFY(args.p.max_iters > 0, "Gravity solve requires a positive MINRES iteration limit.");
using form = utils::blocks::gravity_field_form;
@@ -444,13 +146,19 @@ namespace mean_field::physics {
utils::blocks::get_residual_block<form>(utils::blocks::gravity_field.poisson_term);
using DomainSchema = utils::domain::CoreEnvelopeVacuumDomainSchema;
const field::FieldDofMap density_map = field::make_field_dof_map<field::Density, DomainSchema>(*f.densityFes);
const field::FieldDofMap displacement_map =
field::make_field_dof_map<field::Displacement, DomainSchema>(*f.displacementFes);
const field::FieldDofMap gravity_flux_map =
field::make_field_dof_map<field::Gravity, DomainSchema>(*f.gravityFluxFes);
const field::FieldDofMap gravity_potential_map =
field::make_field_dof_map<field::Gravity, DomainSchema>(*f.gravityPotentialFes);
const field::FieldDofGridFunctionAdapter density_adapter =
field::make_field_dof_grid_function_adapter<field::Density, DomainSchema>(*f.densityFes);
const field::FieldDofGridFunctionAdapter displacement_adapter =
field::make_field_dof_grid_function_adapter<field::Displacement, DomainSchema>(*f.displacementFes);
const field::FieldDofGridFunctionAdapter gravity_flux_adapter =
field::make_field_dof_grid_function_adapter<field::Gravity, DomainSchema>(*f.gravityFluxFes);
const field::FieldDofGridFunctionAdapter gravity_potential_adapter =
field::make_field_dof_grid_function_adapter<field::Gravity, DomainSchema>(*f.gravityPotentialFes);
const field::FieldDofMap &density_map = density_adapter.dof_map();
const field::FieldDofMap &displacement_map = displacement_adapter.dof_map();
const field::FieldDofMap &gravity_flux_map = gravity_flux_adapter.dof_map();
const field::FieldDofMap &gravity_potential_map = gravity_potential_adapter.dof_map();
const std::array<int, form::value_block_count> value_sizes{
density_map.reduced_size(), displacement_map.reduced_size(), gravity_flux_map.reduced_size(),
@@ -463,14 +171,8 @@ namespace mean_field::physics {
const utils::blocks::form_layout<form> layout(value_sizes, residual_sizes);
mfem::Vector density_true;
mfem::Vector displacement_true;
grid_function_to_true_dofs(*f.densityFes, rho, density_true);
grid_function_to_true_dofs(*f.displacementFes, displacement, displacement_true);
const mfem::Vector density = density_map.gather(density_true);
const mfem::Vector reduced_displacement = displacement_map.gather(displacement_true);
const mfem::Vector density = density_adapter.gather(rho);
const mfem::Vector reduced_displacement = displacement_adapter.gather(displacement);
operators::context::gravity_field::GravityFieldLinearizationContext linearization_context(
f, *f.domainMapperStateless
@@ -491,6 +193,7 @@ namespace mean_field::physics {
operators::ReducedGravityFieldOperator reduced_operator(
gravity_operator, reduced_geometry_context, reduced_displacement
);
operators::ReducedGravityFieldPreconditioner reduced_preconditioner(f, reduced_geometry_context);
mfem::Vector right_hand_side;
reduced_operator.BuildRightHandSide(density, right_hand_side);
@@ -505,25 +208,24 @@ namespace mean_field::physics {
mfem::MINRESSolver minres(f.mesh->GetComm());
minres.SetOperator(reduced_operator);
minres.SetPreconditioner(*f.gravityContext.block_prec);
minres.SetPreconditioner(reduced_preconditioner);
minres.SetRelTol(args.p.rtol);
minres.SetAbsTol(args.p.atol);
minres.SetMaxIter(args.p.max_iters);
minres.SetPrintLevel(1);
// minres.SetPrintLevel(args.verbose ? 1 : 0);
minres.SetPrintLevel(0);
minres.Mult(right_hand_side, gravity_state);
MFEM_VERIFY(minres.GetConverged(), "The reduced gravity solve failed to converge.");
GravitySolution solution(f);
const mfem::Vector gravity_flux_true =
gravity_flux_map.scatter(gravity_state.GetBlock(gravity_gradient_residual_block));
const mfem::Vector gravity_potential_true =
gravity_potential_map.scatter(gravity_state.GetBlock(gravity_poisson_residual_block));
solution.gradPhi.SetFromTrueDofs(gravity_flux_true);
solution.phi.SetFromTrueDofs(gravity_potential_true);
gravity_flux_adapter.scatter(
gravity_state.GetBlock(gravity_gradient_residual_block), solution.gradPhi
);
gravity_potential_adapter.scatter(
gravity_state.GetBlock(gravity_poisson_residual_block), solution.phi
);
return solution;
}

View File

@@ -10,9 +10,15 @@ namespace mean_field::physics {
const mfem::GridFunction &rho_ref
) {
double local_I = 0.0;
using DomainSchema = utils::domain::CoreEnvelopeVacuumDomainSchema;
mapping::GridFunctionMappingEvaluator mapping_evaluator(
*fem.domainMapperStateless, *fem.displacement,
*fem.compactificationCoordinate
);
for (int i = 0; i < fem.mesh->GetNE(); i++) {
if (fem.mesh->GetAttribute(i) == 3)
if (!DomainSchema::template attribute_belongs_to<utils::domain::Stellar>(
fem.mesh->GetAttribute(i)))
continue;
mfem::ElementTransformation *T = fem.mesh->GetElementTransformation(i);
@@ -29,12 +35,16 @@ namespace mean_field::physics {
const double rho_hat = rho_ref.GetValue(i, ip);
mfem::Vector x_phys;
fem.mapping->GetPhysicalPoint(*T, ip, x_phys);
mapping::VolumeMappingContext mapping_context;
MFEM_VERIFY(
mapping_evaluator.EvaluateVolume(*T, ip, mapping_context) ==
mapping::MappingStatus::valid,
"Moment-of-inertia integration encountered an invalid mapping."
);
const mfem::Vector &x_phys = mapping_context.mapping.physical_position;
const double r_cyl_sq = x_phys(0) * x_phys(0) + x_phys(1) * x_phys(1);
const double detJ = std::fabs(fem.mapping->ComputeDetJ(*T, ip));
const double weight = T->Weight() * ip.weight * detJ;
const double weight = mapping_context.quadrature.weight;
local_I += rho_hat * r_cyl_sq * weight;
}

View File

@@ -12,6 +12,10 @@ namespace mean_field::utils {
) {
const int dim = fem.mesh->Dimension();
x_ref = x_phys_target;
mapping::GridFunctionMappingEvaluator mapping_evaluator(
*fem.domainMapperStateless, *fem.displacement,
*fem.compactificationCoordinate
);
mfem::Array<int> init_elem;
mfem::Array<mfem::IntegrationPoint> init_ip;
@@ -29,15 +33,18 @@ namespace mean_field::utils {
mfem::Array<mfem::IntegrationPoint> origin_ip;
fem.mesh->FindPoints(P_origin, origin_elem, origin_ip, false);
if (origin_elem.Size() > 0 && origin_elem[0] >= 0 && fem.mapping->HasDisplacementField()) {
if (origin_elem.Size() > 0 && origin_elem[0] >= 0) {
mfem::ElementTransformation *T0 = fem.mesh->GetElementTransformation(origin_elem[0]);
T0->SetIntPoint(&origin_ip[0]);
mfem::DenseMatrix J0(dim, dim), J0_inv(dim, dim);
fem.mapping->ComputeJacobian(*T0, J0);
mfem::CalcInverse(J0, J0_inv);
mapping::MappingPointContext context;
MFEM_VERIFY(
mapping_evaluator.EvaluatePoint(*T0, origin_ip[0], context) ==
mapping::MappingStatus::valid,
"Reference-point initialization encountered an invalid mapping."
);
J0_inv.Mult(x_phys_target, x_ref);
context.inverse_mapping_jacobian.Mult(x_phys_target, x_ref);
}
init_P.SetCol(0, x_ref);
@@ -70,9 +77,6 @@ namespace mean_field::utils {
mfem::Vector residual(dim);
mfem::Vector step(dim);
mfem::DenseMatrix J_map(dim, dim);
mfem::DenseMatrix J_map_inv(dim, dim);
int find_failures = 0;
for (int iter = 0; iter < max_iter; ++iter) {
@@ -99,8 +103,12 @@ namespace mean_field::utils {
mfem::ElementTransformation *T = fem.mesh->GetElementTransformation(elemID);
T->SetIntPoint(&ip);
mfem::Vector current_x_phys(dim);
fem.mapping->GetPhysicalPoint(*T, ip, current_x_phys);
mapping::MappingPointContext context;
if (mapping_evaluator.EvaluatePoint(*T, ip, context) !=
mapping::MappingStatus::valid) {
return false;
}
const mfem::Vector &current_x_phys = context.physical_position;
for (int i = 0; i < dim; ++i) {
residual(i) = current_x_phys(i) - x_phys_target(i);
@@ -110,9 +118,7 @@ namespace mean_field::utils {
return true;
}
fem.mapping->ComputeJacobian(*T, J_map);
mfem::CalcInverse(J_map, J_map_inv);
J_map_inv.Mult(residual, step);
context.inverse_mapping_jacobian.Mult(residual, step);
double alpha = 1.0;
mfem::Vector x_ref_candidate(dim);

View File

@@ -1,123 +1,24 @@
module;
#include <expected>
#include <mfem.hpp>
module mean_field;
import :boundary.contexts;
namespace mean_field::utils {
DOMAINS operator|(
DOMAINS lhs,
DOMAINS rhs
) {
return static_cast<DOMAINS>(static_cast<uint8_t>(lhs) | static_cast<uint8_t>(rhs));
}
DOMAINS operator|(DOMAINS lhs, DOMAINS rhs) {
return static_cast<DOMAINS>(static_cast<uint8_t>(lhs) |
static_cast<uint8_t>(rhs));
}
DOMAINS operator&(
DOMAINS lhs,
DOMAINS rhs
) {
return static_cast<DOMAINS>(static_cast<uint8_t>(lhs) & static_cast<uint8_t>(rhs));
}
DOMAINS operator&(DOMAINS lhs, DOMAINS rhs) {
return static_cast<DOMAINS>(static_cast<uint8_t>(lhs) &
static_cast<uint8_t>(rhs));
}
void populate_element_mask(
const mfem::Mesh *mesh,
const DOMAINS domain,
mfem::Array<int> &mask
) {
const int max_attr = mesh->attributes.Max();
mask.SetSize(max_attr);
mask = 0;
int get_mesh_order(const mfem::Mesh &mesh) {
if (mesh.GetNodes() != nullptr) {
return mesh.GetNodes()->FESpace()->GetMaxElementOrder();
}
return 1;
}
if ((domain & DOMAINS::CORE) == DOMAINS::CORE && max_attr >= 1) {
mask[0] = 1;
}
if ((domain & DOMAINS::ENVELOPE) == DOMAINS::ENVELOPE && max_attr >= 2) {
mask[1] = 1;
}
if ((domain & DOMAINS::VACUUM) == DOMAINS::VACUUM && max_attr >= 3) {
mask[2] = 1;
}
}
void populate_domain_tdofs(
const mfem::ParFiniteElementSpace *fes,
const mfem::Array<int> &element_mask,
mfem::Array<int> &ess_tdof
) {
mfem::Array<int> vdof_marker(fes->GetVSize());
vdof_marker = 0;
for (int i = 0; i < fes->GetMesh()->GetNE(); i++) {
const int attr = fes->GetMesh()->GetAttribute(i);
if (element_mask[attr - 1]) {
mfem::Array<int> dofs;
fes->GetElementVDofs(i, dofs);
for (int j = 0; j < dofs.Size(); j++) {
int index = dofs[j];
if (index < 0)
index = -1 - index;
vdof_marker[index] = 1;
}
}
}
fes->MarkerToList(vdof_marker, ess_tdof);
}
std::expected<
boundary::Bounds,
boundary::BoundsError>
discover_bounds(
const mfem::Mesh *mesh,
const int vacuum_attr
) {
double local_min_r = std::numeric_limits<double>::max();
double local_max_r = -std::numeric_limits<double>::max();
bool found_vacuum = false;
for (int i = 0; i < mesh->GetNE(); ++i) {
if (mesh->GetAttribute(i) == vacuum_attr) {
found_vacuum = true;
mfem::Array<int> vertices;
mesh->GetElementVertices(i, vertices);
for (const int v : vertices) {
const double *coords = mesh->GetVertex(v);
double r = std::sqrt(coords[0] * coords[0] + coords[1] * coords[1] + coords[2] * coords[2]);
local_min_r = std::min(local_min_r, r);
local_max_r = std::max(local_max_r, r);
}
}
}
double global_min_r, global_max_r;
int global_found_vacuum;
int l_found = found_vacuum ? 1 : 0;
MPI_Comm comm = MPI_COMM_WORLD;
if (const auto *pmesh = dynamic_cast<const mfem::ParMesh *>(mesh)) {
comm = pmesh->GetComm();
}
MPI_Allreduce(&local_min_r, &global_min_r, 1, MPI_DOUBLE, MPI_MIN, comm);
MPI_Allreduce(&local_max_r, &global_max_r, 1, MPI_DOUBLE, MPI_MAX, comm);
MPI_Allreduce(&l_found, &global_found_vacuum, 1, MPI_INT, MPI_MAX, comm);
if (global_found_vacuum) {
return boundary::Bounds(global_min_r, global_max_r);
}
return std::unexpected(boundary::BoundsError::CANNOT_FIND_VACUUM);
}
int get_mesh_order(const mfem::Mesh &mesh) {
if (mesh.GetNodes() != nullptr) {
return mesh.GetNodes()->FESpace()->GetMaxElementOrder();
}
return 1;
}
} // namespace mean_field::utils
} // namespace mean_field::utils

View File

@@ -121,7 +121,7 @@ export namespace mean_field::eos {
std::pow(density, 1.0 / m_polytropic_index);
}
[[nodiscard]] double enthalpy_from_pressure(double pressure) const override {
[[nodiscard]] double enthalpy_from_pressure(const double pressure) const override {
validate_nonnegativity(pressure, "pressure");
const double np1 = m_polytropic_index + 1;
return np1 * std::pow(m_polytropic_constant, m_polytropic_index / np1) * std::pow(pressure, 1.0 / np1);
@@ -159,12 +159,8 @@ export namespace mean_field::eos {
);
}
}
public:
private:
double m_polytropic_index;
double m_polytropic_constant;
double m_enthalpy_scale;
};
} // namespace mean_field::eos
} // namespace mean_field::eos

View File

@@ -8,7 +8,6 @@ module;
export module mean_field:fem;
export import :physics.contexts;
export import :boundary.contexts;
export import :mapping.domain_mapper;
export import :utils.misc;
@@ -92,38 +91,9 @@ export namespace mean_field::fem {
// =====================================================================
// Domain mapping
//
// These are declared after displacement so that they are destroyed
// before the displacement grid function to which mapping may refer.
// DomainMapper is retained only for legacy integrators. New operators
// use DomainMapperStateless exclusively.
// =====================================================================
std::unique_ptr<mapping::DomainMapper> mapping;
std::unique_ptr<mapping::DomainMapperStateless> domainMapperStateless;
// =====================================================================
// Block layouts
//
// These arrays are retained only for legacy code. Canonical operator
// layouts are defined by the compile-time forms in :utils.blocks.
//
// Main system: [Displacement | Density]
// Gravity system: [Flux | Potential]
// =====================================================================
mfem::Array<int> blockTrueOffsets;
mfem::Array<int> gravityBlockTrueOffsets;
// =====================================================================
// Boundary conditions and domain masks
// =====================================================================
mfem::Array<int> essentialDisplacementTdofs;
mfem::Array<int> vacuumDensityTdofs;
mfem::Array<int> vacuumEnthalpyTdofs;
mfem::Array<int> vacuumDisplacementTdofs;
std::unique_ptr<mapping::DomainMapper> domainMapperStateless;
// =====================================================================
// Global diagnostics
@@ -133,10 +103,9 @@ export namespace mean_field::fem {
mfem::DenseMatrix Q;
// =====================================================================
// Physics and boundary contexts
// Boundary context
// =====================================================================
physics::GravityContext gravityContext;
boundary::BoundaryContext boundaryContext;
std::unique_ptr<quadrature::RuleFactory> quadratureFactory;
@@ -160,13 +129,11 @@ export namespace mean_field::fem {
compactificationFec != nullptr && compactificationFes != nullptr &&
compactificationCoordinate != nullptr &&
mapping != nullptr && domainMapperStateless != nullptr && quadratureFactory != nullptr &&
blockTrueOffsets.Size() == 3 && gravityBlockTrueOffsets.Size() == 3;
domainMapperStateless != nullptr && quadratureFactory != nullptr;
}
[[nodiscard]] bool has_mapping() const {
return mapping != nullptr;
return domainMapperStateless != nullptr && displacement != nullptr && compactificationCoordinate != nullptr;
}
};

View File

@@ -5,6 +5,7 @@ module;
#include <cstddef>
#include <memory>
#include <stdexcept>
#include <utility>
#include <mfem.hpp>
@@ -887,6 +888,110 @@ export namespace mean_field::field {
mfem::Array<int> m_trueToReduced;
};
/*
* Canonical adapter between an MFEM GridFunction and a reduced field
* vector.
*
* FieldDofMap deliberately contains only indexing information. This
* adapter binds that indexing to the exact finite-element space whose true
* DOFs the map describes. Consequently, a grid function from another
* finite-element space is rejected even when it happens to have the same
* vector size.
*
* The finite-element space must outlive the adapter.
*/
class FieldDofGridFunctionAdapter {
public:
FieldDofGridFunctionAdapter(
FieldDofMap dofMap,
const mfem::FiniteElementSpace &finiteElementSpace
)
: m_dofMap(std::move(dofMap)),
m_finiteElementSpace(&finiteElementSpace) {
if (m_dofMap.full_size() != finiteElementSpace.GetTrueVSize()) {
throw std::invalid_argument(
"FieldDofGridFunctionAdapter map and finite-element "
"space have incompatible true-DOF sizes."
);
}
}
[[nodiscard]]
const FieldDofMap &dof_map() const noexcept {
return m_dofMap;
}
[[nodiscard]]
const mfem::FiniteElementSpace &finite_element_space() const noexcept {
return *m_finiteElementSpace;
}
/*
* Gather the grid function's true DOFs into reduced field ordering.
* The output vector is not resized so MFEM vector views remain valid.
*/
void gather(
const mfem::GridFunction &gridFunction,
mfem::Vector &reduced
) const {
validate_grid_function(gridFunction);
mfem::Vector full;
gridFunction.GetTrueDofs(full);
m_dofMap.gather(full, reduced);
}
[[nodiscard]]
mfem::Vector gather(const mfem::GridFunction &gridFunction) const {
mfem::Vector reduced(m_dofMap.reduced_size());
gather(gridFunction, reduced);
return reduced;
}
/*
* Scatter with projection semantics. Unsupported true DOFs are zeroed
* before the complete true vector is distributed to the grid function.
*/
void scatter(
const mfem::Vector &reduced,
mfem::GridFunction &gridFunction
) const {
validate_grid_function(gridFunction);
const mfem::Vector full = m_dofMap.scatter(reduced);
gridFunction.SetFromTrueDofs(full);
}
/*
* Scatter while preserving the grid function's existing unsupported
* true DOFs.
*/
void scatter_into(
const mfem::Vector &reduced,
mfem::GridFunction &gridFunction
) const {
validate_grid_function(gridFunction);
mfem::Vector full;
gridFunction.GetTrueDofs(full);
m_dofMap.scatter_into(reduced, full);
gridFunction.SetFromTrueDofs(full);
}
private:
void validate_grid_function(const mfem::GridFunction &gridFunction) const {
if (gridFunction.FESpace() != m_finiteElementSpace) {
throw std::invalid_argument(
"FieldDofGridFunctionAdapter received a grid function "
"from a different finite-element space."
);
}
}
FieldDofMap m_dofMap;
const mfem::FiniteElementSpace *m_finiteElementSpace;
};
/*
* Construct the canonical solver map for a registered spatial field.
*
@@ -903,4 +1008,16 @@ export namespace mean_field::field {
return FieldDofMap(support);
}
template <
MfemDomainField FieldT,
utils::domain::IsSchema SchemaT>
[[nodiscard]]
FieldDofGridFunctionAdapter
make_field_dof_grid_function_adapter(const mfem::ParFiniteElementSpace &finiteElementSpace) {
return FieldDofGridFunctionAdapter(
make_field_dof_map<FieldT, SchemaT>(finiteElementSpace),
finiteElementSpace
);
}
} // namespace mean_field::field

View File

@@ -6,7 +6,11 @@ import :mapping.domain_mapper;
export namespace mean_field::integrators {
class AdvectionIntegrator : public mfem::BlockNonlinearFormIntegrator {
public:
explicit AdvectionIntegrator(const mapping::DomainMapper &map);
AdvectionIntegrator(
const mapping::DomainMapper &mapper,
const mfem::GridFunction &displacement,
const mfem::GridFunction &compactification_coordinate
);
void AssembleElementVector(
const mfem::Array<const mfem::FiniteElement *> &el,
@@ -23,6 +27,6 @@ export namespace mean_field::integrators {
) override;
private:
const mapping::DomainMapper &m_map;
mapping::GridFunctionMappingEvaluator m_mapping;
};
} // namespace mean_field::integrators
} // namespace mean_field::integrators

View File

@@ -7,7 +7,9 @@ export namespace mean_field::integrators {
class CentrifugalForceIntegrator : public mfem::BlockNonlinearFormIntegrator {
public:
CentrifugalForceIntegrator(
const mapping::DomainMapper &map,
const mapping::DomainMapper &mapper,
const mfem::GridFunction &displacement,
const mfem::GridFunction &compactification_coordinate,
const mfem::Vector &omega
);
@@ -29,9 +31,9 @@ export namespace mean_field::integrators {
) override;
private:
const mapping::DomainMapper &m_map;
mapping::GridFunctionMappingEvaluator m_mapping;
mfem::Vector m_omega;
const mfem::IntegrationRule *m_ir = nullptr;
};
} // namespace mean_field::integrators
} // namespace mean_field::integrators

View File

@@ -7,7 +7,9 @@ export namespace mean_field::integrators {
class CoriolisIntegrator : public mfem::BlockNonlinearFormIntegrator {
public:
CoriolisIntegrator(
const mapping::DomainMapper &map,
const mapping::DomainMapper &mapper,
const mfem::GridFunction &displacement,
const mfem::GridFunction &compactification_coordinate,
const mfem::Vector &omega
);
@@ -26,9 +28,9 @@ export namespace mean_field::integrators {
) override;
private:
const mapping::DomainMapper &m_map;
mapping::GridFunctionMappingEvaluator m_mapping;
mfem::Vector m_omega;
mfem::DenseMatrix m_omega_mat;
};
} // namespace mean_field::integrators
} // namespace mean_field::integrators

View File

@@ -10,7 +10,9 @@ export namespace mean_field::integrators {
class GravityMomentumIntegrator : public mfem::BlockNonlinearFormIntegrator {
public:
explicit GravityMomentumIntegrator(
const mapping::DomainMapper &map,
const mapping::DomainMapper &mapper,
const mfem::GridFunction &displacement,
const mfem::GridFunction &compactification_coordinate,
GravityForceJacobianMode jacobian_mode = GravityForceJacobianMode::field_coupled
);
@@ -33,8 +35,8 @@ export namespace mean_field::integrators {
) override;
private:
const mapping::DomainMapper &m_map;
mapping::GridFunctionMappingEvaluator m_mapping;
GravityForceJacobianMode m_jacobian_mode;
const mfem::IntegrationRule *m_integration_rule{nullptr};
};
} // namespace mean_field::integrators
} // namespace mean_field::integrators

View File

@@ -6,7 +6,11 @@ import :mapping.domain_mapper;
export namespace mean_field::integrators {
class ContinuityVolumeIntegrator : public mfem::BlockNonlinearFormIntegrator {
public:
explicit ContinuityVolumeIntegrator(const mapping::DomainMapper &map);
ContinuityVolumeIntegrator(
const mapping::DomainMapper &mapper,
const mfem::GridFunction &displacement,
const mfem::GridFunction &compactification_coordinate
);
void AssembleElementVector(
const mfem::Array<const mfem::FiniteElement *> &el,
@@ -23,12 +27,16 @@ export namespace mean_field::integrators {
) override;
private:
const mapping::DomainMapper &m_map;
mapping::GridFunctionMappingEvaluator m_mapping;
};
class ContinuityFaceIntegrator : public mfem::BlockNonlinearFormIntegrator {
public:
explicit ContinuityFaceIntegrator(const mapping::DomainMapper &map);
ContinuityFaceIntegrator(
const mapping::DomainMapper &mapper,
const mfem::GridFunction &displacement,
const mfem::GridFunction &compactification_coordinate
);
void AssembleFaceVector(
const mfem::Array<const mfem::FiniteElement *> &el1,
@@ -58,7 +66,7 @@ export namespace mean_field::integrators {
);
private:
const mapping::DomainMapper &m_map;
mapping::GridFunctionMappingEvaluator m_mapping;
};
} // namespace mean_field::integrators

View File

@@ -10,7 +10,9 @@ export namespace mean_field::integrators {
template <utils::is_xad EOS_T> class PressureGradientIntegrator : public mfem::BlockNonlinearFormIntegrator {
public:
PressureGradientIntegrator(
const mapping::DomainMapper &map,
const mapping::DomainMapper &mapper,
const mfem::GridFunction &displacement,
const mfem::GridFunction &compactification_coordinate,
utils::EOS_P<EOS_T> eos
);
@@ -28,16 +30,18 @@ export namespace mean_field::integrators {
) override;
private:
const mapping::DomainMapper &m_map;
mapping::GridFunctionMappingEvaluator m_mapping;
utils::EOS_P<EOS_T> m_eos;
};
template <utils::is_xad EOS_T>
PressureGradientIntegrator<EOS_T>::PressureGradientIntegrator(
const mapping::DomainMapper &map,
const mapping::DomainMapper &mapper,
const mfem::GridFunction &displacement,
const mfem::GridFunction &compactification_coordinate,
utils::EOS_P<EOS_T> eos
)
: m_map(map),
: m_mapping(mapper, displacement, compactification_coordinate),
m_eos(std::move(eos)) {
}
@@ -48,6 +52,8 @@ export namespace mean_field::integrators {
const mfem::Array<const mfem::Vector *> &elfun,
const mfem::Array<mfem::Vector *> &elvec
) {
m_mapping.InvalidateCache();
if (utils::is_vacuum(Tr, elvec)) {
return;
}
@@ -78,7 +84,7 @@ export namespace mean_field::integrators {
const mfem::IntegrationPoint &ip = ir->IntPoint(q);
Tr.SetIntPoint(&ip);
auto [J_inv, detJ, weight] = m_map.GetQuadratureContext(Tr, ip);
auto [J_inv, detJ, weight] = m_mapping.GetQuadratureContext(Tr, ip);
fe_v->CalcDShape(ip, dshape_v_ref);
mfem::Mult(dshape_v_ref, J_inv, dshape_v_phys);
@@ -111,6 +117,8 @@ export namespace mean_field::integrators {
const mfem::Array<const mfem::Vector *> &elfun,
const mfem::Array2D<mfem::DenseMatrix *> &elmats
) {
m_mapping.InvalidateCache();
const mfem::FiniteElement *fe_v = el[0];
const mfem::FiniteElement *fe_rho = el[1];
@@ -141,7 +149,7 @@ export namespace mean_field::integrators {
const mfem::IntegrationPoint &ip = ir->IntPoint(q);
Tr.SetIntPoint(&ip);
auto [J_inv, detJ, weight] = m_map.GetQuadratureContext(Tr, ip);
auto [J_inv, detJ, weight] = m_mapping.GetQuadratureContext(Tr, ip);
fe_v->CalcDShape(ip, dshape_v_ref);
mfem::Mult(dshape_v_ref, J_inv, dshape_v_phys);

View File

@@ -7,7 +7,9 @@ export namespace mean_field::integrators {
class ViscosityIntegrator : public mfem::BlockNonlinearFormIntegrator {
public:
ViscosityIntegrator(
const mapping::DomainMapper &map,
const mapping::DomainMapper &mapper,
const mfem::GridFunction &displacement,
const mfem::GridFunction &compactification_coordinate,
double mu,
int quad_boost
);
@@ -29,7 +31,7 @@ export namespace mean_field::integrators {
) override;
private:
const mapping::DomainMapper &m_map;
mapping::GridFunctionMappingEvaluator m_mapping;
double m_mu;
int m_quad_boost;
};

View File

@@ -9,7 +9,9 @@ export namespace mean_field::mapping {
class MappedScalarCoefficient : public mfem::Coefficient {
public:
MappedScalarCoefficient(
const DomainMapper &map,
const DomainMapper &mapper,
const mfem::GridFunction &displacement,
const mfem::GridFunction &compactification_coordinate,
Coefficient &coeff,
COORDINATE_SPACE coord_space = COORDINATE_SPACE::PHYSICAL
);
@@ -27,7 +29,7 @@ export namespace mean_field::mapping {
);
private:
const DomainMapper &m_map;
GridFunctionMappingEvaluator m_mapping;
Coefficient &m_coeff;
COORDINATE_SPACE m_coord_space;
};
@@ -35,13 +37,17 @@ export namespace mean_field::mapping {
class MappedDiffusionCoefficient : public mfem::MatrixCoefficient {
public:
MappedDiffusionCoefficient(
const DomainMapper &map,
const DomainMapper &mapper,
const mfem::GridFunction &displacement,
const mfem::GridFunction &compactification_coordinate,
mfem::Coefficient &sigma,
int dim
);
MappedDiffusionCoefficient(
const DomainMapper &map,
const DomainMapper &mapper,
const mfem::GridFunction &displacement,
const mfem::GridFunction &compactification_coordinate,
MatrixCoefficient &sigma
);
@@ -52,7 +58,7 @@ export namespace mean_field::mapping {
) override;
private:
const DomainMapper &m_map;
GridFunctionMappingEvaluator m_mapping;
mfem::Coefficient *m_scalar;
MatrixCoefficient *m_tensor;
};
@@ -60,7 +66,9 @@ export namespace mean_field::mapping {
class MappedVectorCoefficient : public mfem::VectorCoefficient {
public:
MappedVectorCoefficient(
const DomainMapper &map,
const DomainMapper &mapper,
const mfem::GridFunction &displacement,
const mfem::GridFunction &compactification_coordinate,
VectorCoefficient &coeff
);
@@ -71,7 +79,7 @@ export namespace mean_field::mapping {
) override;
private:
const DomainMapper &m_map;
GridFunctionMappingEvaluator m_mapping;
VectorCoefficient &m_coeff;
};
@@ -80,7 +88,9 @@ export namespace mean_field::mapping {
using Func = std::function<double(const mfem::Vector &x)>;
PhysicalPositionFunctionCoefficient(
const DomainMapper &map,
const DomainMapper &mapper,
const mfem::GridFunction &displacement,
const mfem::GridFunction &compactification_coordinate,
Func f
);
@@ -91,13 +101,15 @@ export namespace mean_field::mapping {
private:
Func m_f;
const DomainMapper &m_map;
GridFunctionMappingEvaluator m_mapping;
};
class MappedHDivMassCoefficient final : public mfem::MatrixCoefficient {
public:
MappedHDivMassCoefficient(
const DomainMapper &map,
const DomainMapper &mapper,
const mfem::GridFunction &displacement,
const mfem::GridFunction &compactification_coordinate,
const int dim
);
@@ -108,6 +120,6 @@ export namespace mean_field::mapping {
) override;
private:
const DomainMapper &m_map;
GridFunctionMappingEvaluator m_mapping;
};
} // namespace mean_field::mapping

View File

@@ -8,347 +8,259 @@ import :mapping.compactification;
import :utils.user;
export namespace mean_field::mapping {
enum class FaceElementSide : uint8_t { element_1, element_2 };
class ElementDisplacementData {
public:
ElementDisplacementData(
const mfem::FiniteElement &element,
const mfem::Vector &displacement_dofs,
mfem::Ordering::Type ordering = mfem::Ordering::byNODES
);
[[nodiscard]] const mfem::FiniteElement &GetElement() const noexcept;
[[nodiscard]] const mfem::DenseMatrix &GetDofMatrix() const noexcept;
[[nodiscard]] int GetDimension() const noexcept;
[[nodiscard]] int GetDofCount() const noexcept;
[[nodiscard]] mfem::Ordering::Type GetOrdering() const noexcept;
private:
const mfem::FiniteElement *m_element;
mfem::DenseMatrix m_dof_matrix;
int m_dimension;
mfem::Ordering::Type m_ordering;
};
struct CompactificationPointData {
double coordinate{0.0};
mfem::Vector coordinate_gradient;
};
[[nodiscard]] ElementDisplacementData ElementDisplacementDataFromElementVDofs(
const mfem::FiniteElement &element,
const mfem::Vector &displacement_dofs
);
class ElementCompactificationData {
public:
ElementCompactificationData(
const mfem::FiniteElement &element,
const mfem::Vector &dofs
);
[[nodiscard]] const mfem::FiniteElement &GetElement() const noexcept;
[[nodiscard]] const mfem::Vector &GetDofs() const noexcept;
[[nodiscard]] int GetDofCount() const noexcept;
private:
const mfem::FiniteElement *m_element;
mfem::Vector m_dofs;
};
struct ElementMappingData {
const ElementDisplacementData &displacement;
const ElementCompactificationData &compactification;
};
class DomainMapperStateless {
public:
class Workspace {
public:
explicit Workspace(int dimension = 3);
void SetDimension(int dimension);
[[nodiscard]] int GetDimension() const noexcept;
private:
friend class DomainMapperStateless;
int m_dimension;
mfem::Vector m_shape;
mfem::DenseMatrix m_mesh_dshape;
mfem::Vector m_field_value;
mfem::DenseMatrix m_field_jacobian;
mfem::Vector m_compactification_shape;
mfem::DenseMatrix m_compactification_dshape;
CompactificationPointData m_compactification_point;
mfem::Vector m_reference_normal;
mfem::Vector m_mapped_normal;
mfem::DenseMatrix m_full_element_jacobian;
mfem::Vector m_vector_temp;
mfem::DenseMatrix m_matrix_temp_1;
mfem::DenseMatrix m_matrix_temp_2;
compactification::ExteriorMapResult m_exterior_result;
compactification::ExteriorMapVariation m_exterior_variation;
};
public:
DomainMapperStateless(
utils::DomainMapperStatelessOptions options,
std::unique_ptr<const compactification::ExteriorDomainMap> exterior_map
);
DomainMapperStateless(const DomainMapperStateless &) = delete;
DomainMapperStateless &operator=(const DomainMapperStateless &) = delete;
DomainMapperStateless(DomainMapperStateless &&) = default;
DomainMapperStateless &operator=(DomainMapperStateless &&) = default;
[[nodiscard]] MappingStatus EvaluatePoint(
const ElementMappingData &element_data,
mfem::ElementTransformation &transformation,
const mfem::IntegrationPoint &integration_point,
Workspace &workspace,
MappingPointContext &context
) const;
[[nodiscard]] MappingStatus EvaluateVolume(
const ElementMappingData &element_data,
mfem::ElementTransformation &transformation,
const mfem::IntegrationPoint &integration_point,
Workspace &workspace,
VolumeMappingContext &context
) const;
[[nodiscard]] MappingStatus EvaluateFace(
const ElementMappingData &element_data,
mfem::FaceElementTransformations &transformation,
FaceElementSide side,
const mfem::IntegrationPoint &integration_point,
Workspace &workspace,
FaceMappingContext &context
) const;
[[nodiscard]] MappingStatus EvaluatePointVariation(
const ElementMappingData &element_data,
const ElementDisplacementData &direction,
mfem::ElementTransformation &transformation,
const mfem::IntegrationPoint &integration_point,
const MappingPointContext &base_context,
Workspace &workspace,
MappingPointVariation &variation
) const;
[[nodiscard]] MappingStatus EvaluateVolumeVariation(
const ElementMappingData &element_data,
const ElementDisplacementData &direction,
mfem::ElementTransformation &transformation,
const mfem::IntegrationPoint &integration_point,
const VolumeMappingContext &base_context,
Workspace &workspace,
VolumeMappingVariation &variation
) const;
[[nodiscard]] MappingStatus EvaluateFaceVariation(
const ElementMappingData &element_data,
const ElementDisplacementData &direction,
mfem::FaceElementTransformations &transformation,
FaceElementSide side,
const mfem::IntegrationPoint &integration_point,
const FaceMappingContext &base_context,
Workspace &workspace,
FaceMappingVariation &variation
) const;
[[nodiscard]] bool IsCompactifiedElement(const mfem::ElementTransformation &transformation) const noexcept;
[[nodiscard]] int GetDimension() const noexcept;
[[nodiscard]] int GetVacuumElementAttribute() const noexcept;
[[nodiscard]] const compactification::ExteriorDomainMap &GetExteriorMap() const noexcept;
private:
void ValidateElementData(const ElementMappingData &element_data) const;
void EvaluateField(
const ElementDisplacementData &field,
mfem::ElementTransformation &transformation,
const mfem::IntegrationPoint &integration_point,
Workspace &workspace,
mfem::Vector &value,
mfem::DenseMatrix &jacobian
) const;
[[nodiscard]] MappingStatus EvaluateCompactificationCoordinate(
const ElementCompactificationData &compactification,
mfem::ElementTransformation &transformation,
const mfem::IntegrationPoint &integration_point,
Workspace &workspace,
CompactificationPointData &point_data
) const;
[[nodiscard]] static mfem::ElementTransformation &SelectFaceElementTransformation(
mfem::FaceElementTransformations &transformation,
FaceElementSide side
);
[[nodiscard]] static const mfem::IntegrationPoint &SelectFaceElementIntegrationPoint(
mfem::FaceElementTransformations &transformation,
FaceElementSide side
);
utils::DomainMapperStatelessOptions m_options;
std::unique_ptr<const compactification::ExteriorDomainMap> m_exterior_map;
};
class DomainMapper {
public:
explicit DomainMapper(
const double r_star_ref,
const double r_inf_ref
);
explicit DomainMapper(
const mfem::GridFunction &d,
const double r_star_ref,
const double r_inf_ref
);
[[nodiscard]] bool is_vacuum(const mfem::ElementTransformation &T) const;
void SetDisplacement(const mfem::GridFunction &d);
[[nodiscard]] bool HasCompactification() const noexcept;
[[nodiscard]] bool HasDisplacementField() const noexcept;
[[nodiscard]] bool CalcIsIdentity() const;
void ResetDisplacement();
void ComputeJacobian(
mfem::ElementTransformation &T,
mfem::DenseMatrix &J
) const;
double ComputeDetJ(
mfem::ElementTransformation &T,
const mfem::IntegrationPoint &ip
) const;
void ComputeMappedDiffusionTensor(
mfem::ElementTransformation &T,
mfem::DenseMatrix &D
) const;
void ComputeInverseJacobian(
mfem::ElementTransformation &T,
mfem::DenseMatrix &JInv
) const;
VolumeQuadratureContext GetQuadratureContext(
mfem::ElementTransformation &T,
const mfem::IntegrationPoint &ip
) const;
FaceQuadratureContext GetFaceQuadratureContext(
mfem::FaceElementTransformations &T,
const mfem::IntegrationPoint &ip
) const;
void GetPhysicalPoint(
mfem::ElementTransformation &T,
const mfem::IntegrationPoint &ip,
mfem::Vector &x_phys
) const;
void GetVectorValue(
const int i,
const mfem::IntegrationPoint &ip,
mfem::Vector &val
) const;
void MapHDivFluxToPhysical(
mfem::ElementTransformation &transformation,
const mfem::IntegrationPoint &integration_point,
const mfem::Vector &reference_flux,
mfem::Vector &physical_flux
) const;
void MapPhysicalFluxToHDivReference(
mfem::ElementTransformation &transformation,
const mfem::IntegrationPoint &integration_point,
const mfem::Vector &physical_flux,
mfem::Vector &reference_flux
) const;
void MapReferenceGradientToPhysical(
mfem::ElementTransformation &transformation,
const mfem::IntegrationPoint &integration_point,
const mfem::Vector &reference_gradient,
mfem::Vector &physical_gradient
) const;
[[nodiscard]] const mfem::GridFunction *GetDisplacement() const;
[[nodiscard]] double GetPhysInfRadius() const;
[[nodiscard]] size_t GetCacheHits() const;
[[nodiscard]] size_t GetCacheMisses() const;
[[nodiscard]] double GetCacheHitRate() const;
void ResetCacheStats() const;
private:
void InitAllScratchSpaces() const;
void ApplyKelvinMapping(
const mfem::Vector &x_ref,
mfem::Vector &x_phys
) const;
void ComputeKelvinJacobian(
const mfem::Vector &x_ref,
const mfem::Vector &x_disp,
const mfem::DenseMatrix &J_D,
mfem::DenseMatrix &J
) const;
void InvalidateCache() const;
void UpdateElementCache(const mfem::ElementTransformation &T) const;
private:
const mfem::GridFunction *m_d;
std::unique_ptr<mfem::GridFunction> m_internal_d;
const int m_dim{3};
const int m_vacuum_attr{3};
const double m_r_star_ref{1.0};
const double m_r_inf_ref{2.0};
const double m_xi_clamp{0.9999};
mutable int m_cached_elem_id{-1};
mutable int m_cached_elem_type{mfem::ElementTransformation::ELEMENT};
mutable const mfem::FiniteElement *m_fe{nullptr};
mutable mfem::Vector m_elem_dofs;
mutable mfem::DenseMatrix m_dof_mat;
mutable mfem::DenseMatrix m_dshape;
mutable mfem::Vector m_shape;
mutable size_t m_cache_hits{0};
mutable size_t m_cache_misses{0};
mutable mfem::DenseMatrix m_J_D;
mutable mfem::DenseMatrix m_J_temp;
mutable mfem::DenseMatrix m_JInv_temp;
mutable mfem::Vector m_x_ref;
mutable mfem::Vector m_x_disp;
mutable mfem::Vector m_d_val;
bool m_displacement_is_identity{true};
};
enum class FaceElementSide : uint8_t { element_1, element_2 };
class ElementDisplacementData {
public:
ElementDisplacementData(
const mfem::FiniteElement &element, const mfem::Vector &displacement_dofs,
mfem::Ordering::Type ordering = mfem::Ordering::byNODES);
[[nodiscard]] const mfem::FiniteElement &GetElement() const noexcept;
[[nodiscard]] const mfem::DenseMatrix &GetDofMatrix() const noexcept;
[[nodiscard]] int GetDimension() const noexcept;
[[nodiscard]] int GetDofCount() const noexcept;
[[nodiscard]] mfem::Ordering::Type GetOrdering() const noexcept;
private:
const mfem::FiniteElement *m_element;
mfem::DenseMatrix m_dof_matrix;
int m_dimension;
mfem::Ordering::Type m_ordering;
};
struct CompactificationPointData {
double coordinate{0.0};
mfem::Vector coordinate_gradient;
};
[[nodiscard]] ElementDisplacementData
ElementDisplacementDataFromElementVDofs(const mfem::FiniteElement &element,
const mfem::Vector &displacement_dofs);
class ElementCompactificationData {
public:
ElementCompactificationData(const mfem::FiniteElement &element,
const mfem::Vector &dofs);
[[nodiscard]] const mfem::FiniteElement &GetElement() const noexcept;
[[nodiscard]] const mfem::Vector &GetDofs() const noexcept;
[[nodiscard]] int GetDofCount() const noexcept;
private:
const mfem::FiniteElement *m_element;
mfem::Vector m_dofs;
};
struct ElementMappingData {
const ElementDisplacementData &displacement;
const ElementCompactificationData &compactification;
};
class DomainMapper {
public:
class Workspace {
public:
explicit Workspace(int dimension = 3);
void SetDimension(int dimension);
[[nodiscard]] int GetDimension() const noexcept;
private:
friend class DomainMapper;
int m_dimension;
mfem::Vector m_shape;
mfem::DenseMatrix m_mesh_dshape;
mfem::Vector m_field_value;
mfem::DenseMatrix m_field_jacobian;
mfem::Vector m_compactification_shape;
mfem::DenseMatrix m_compactification_dshape;
CompactificationPointData m_compactification_point;
mfem::Vector m_reference_normal;
mfem::Vector m_mapped_normal;
mfem::DenseMatrix m_full_element_jacobian;
mfem::Vector m_vector_temp;
mfem::DenseMatrix m_matrix_temp_1;
mfem::DenseMatrix m_matrix_temp_2;
compactification::ExteriorMapResult m_exterior_result;
compactification::ExteriorMapVariation m_exterior_variation;
};
public:
DomainMapper(
utils::DomainMapperOptions options,
std::unique_ptr<const compactification::ExteriorDomainMap> exterior_map);
DomainMapper(const DomainMapper &) = delete;
DomainMapper &operator=(const DomainMapper &) = delete;
DomainMapper(DomainMapper &&) = default;
DomainMapper &operator=(DomainMapper &&) = default;
[[nodiscard]] MappingStatus
EvaluatePoint(const ElementMappingData &element_data,
mfem::ElementTransformation &transformation,
const mfem::IntegrationPoint &integration_point,
Workspace &workspace, MappingPointContext &context) const;
[[nodiscard]] MappingStatus
EvaluateVolume(const ElementMappingData &element_data,
mfem::ElementTransformation &transformation,
const mfem::IntegrationPoint &integration_point,
Workspace &workspace, VolumeMappingContext &context) const;
[[nodiscard]] MappingStatus
EvaluateFace(const ElementMappingData &element_data,
mfem::FaceElementTransformations &transformation,
FaceElementSide side,
const mfem::IntegrationPoint &integration_point,
Workspace &workspace, FaceMappingContext &context) const;
[[nodiscard]] MappingStatus
EvaluatePointVariation(const ElementMappingData &element_data,
const ElementDisplacementData &direction,
mfem::ElementTransformation &transformation,
const mfem::IntegrationPoint &integration_point,
const MappingPointContext &base_context,
Workspace &workspace,
MappingPointVariation &variation) const;
[[nodiscard]] MappingStatus
EvaluateVolumeVariation(const ElementMappingData &element_data,
const ElementDisplacementData &direction,
mfem::ElementTransformation &transformation,
const mfem::IntegrationPoint &integration_point,
const VolumeMappingContext &base_context,
Workspace &workspace,
VolumeMappingVariation &variation) const;
[[nodiscard]] MappingStatus EvaluateFaceVariation(
const ElementMappingData &element_data,
const ElementDisplacementData &direction,
mfem::FaceElementTransformations &transformation, FaceElementSide side,
const mfem::IntegrationPoint &integration_point,
const FaceMappingContext &base_context, Workspace &workspace,
FaceMappingVariation &variation) const;
[[nodiscard]] bool IsCompactifiedElement(
const mfem::ElementTransformation &transformation) const noexcept;
[[nodiscard]] int GetDimension() const noexcept;
[[nodiscard]] const compactification::ExteriorDomainMap &
GetExteriorMap() const noexcept;
private:
void ValidateElementData(const ElementMappingData &element_data) const;
void EvaluateField(const ElementDisplacementData &field,
mfem::ElementTransformation &transformation,
const mfem::IntegrationPoint &integration_point,
Workspace &workspace, mfem::Vector &value,
mfem::DenseMatrix &jacobian) const;
[[nodiscard]] MappingStatus EvaluateCompactificationCoordinate(
const ElementCompactificationData &compactification,
mfem::ElementTransformation &transformation,
const mfem::IntegrationPoint &integration_point, Workspace &workspace,
CompactificationPointData &point_data) const;
[[nodiscard]] static mfem::ElementTransformation &
SelectFaceElementTransformation(
mfem::FaceElementTransformations &transformation, FaceElementSide side);
[[nodiscard]] static const mfem::IntegrationPoint &
SelectFaceElementIntegrationPoint(
mfem::FaceElementTransformations &transformation, FaceElementSide side);
utils::DomainMapperOptions m_options;
std::unique_ptr<const compactification::ExteriorDomainMap> m_exterior_map;
};
class GridFunctionMappingEvaluator {
public:
/*
* The evaluator references the supplied grid functions and caches copies of
* their element-local DOFs. Call InvalidateCache() or Refresh() after either
* grid function's values are modified. Finite-element-space sequence changes
* are detected automatically.
*
* This object owns mutable workspace and cache state and is not thread-safe.
*/
GridFunctionMappingEvaluator(
const DomainMapper &mapper,
const mfem::GridFunction &displacement,
const mfem::GridFunction &compactification_coordinate);
/*
* Discard all element-local field data. The next evaluation reloads its
* requested element lazily. This operation is idempotent.
*/
void InvalidateCache() noexcept;
/*
* Reload the currently cached element immediately. If no element has been
* evaluated yet, Refresh() is a validated no-op. If either finite-element
* space changed sequence, the old element ID is discarded and the next
* evaluation reloads lazily against the updated spaces.
*/
void Refresh();
[[nodiscard]] MappingStatus
EvaluatePoint(mfem::ElementTransformation &transformation,
const mfem::IntegrationPoint &integration_point,
MappingPointContext &context);
[[nodiscard]] MappingStatus
EvaluateVolume(mfem::ElementTransformation &transformation,
const mfem::IntegrationPoint &integration_point,
VolumeMappingContext &context);
[[nodiscard]] MappingStatus
EvaluateFace(mfem::FaceElementTransformations &transformation,
FaceElementSide side,
const mfem::IntegrationPoint &integration_point,
FaceMappingContext &context);
[[nodiscard]] VolumeQuadratureContext
GetQuadratureContext(mfem::ElementTransformation &transformation,
const mfem::IntegrationPoint &integration_point);
[[nodiscard]] FaceQuadratureContext
GetFaceQuadratureContext(
mfem::FaceElementTransformations &transformation,
const mfem::IntegrationPoint &integration_point,
FaceElementSide side = FaceElementSide::element_1);
void GetPhysicalPoint(mfem::ElementTransformation &transformation,
const mfem::IntegrationPoint &integration_point,
mfem::Vector &physical_position);
private:
void ValidateFieldBindings() const;
[[nodiscard]] bool InvalidateForChangedSpaces();
void LoadElement(int element_id);
const DomainMapper &m_mapper;
const mfem::GridFunction &m_displacement;
const mfem::GridFunction &m_compactification_coordinate;
const mfem::FiniteElementSpace *m_displacement_space;
const mfem::FiniteElementSpace *m_compactification_space;
long m_displacement_space_sequence;
long m_compactification_space_sequence;
DomainMapper::Workspace m_workspace;
mfem::Array<int> m_displacement_dofs;
mfem::Array<int> m_compactification_dofs;
mfem::Vector m_element_displacement;
mfem::Vector m_element_compactification;
std::unique_ptr<ElementDisplacementData> m_displacement_data;
std::unique_ptr<ElementCompactificationData> m_compactification_data;
int m_cached_element_id{-1};
};
} // namespace mean_field::mapping

View File

@@ -6,8 +6,6 @@ export import :utils.user;
export import :utils.domain;
export import :physics.gravity;
export import :physics.solid_body;
export import :physics.barotrope;
export import :physics.contexts;
export import :boundary.contexts;
export import :analysis.integral;
export import :mapping.domain_mapper;

View File

@@ -74,7 +74,7 @@ export namespace mean_field::operators::context::barotropic {
public:
BarotropicClosureLinearizationContext(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const mapping::DomainMapper &domainMapper,
const field::FieldDofMap &densityMap,
const field::FieldDofMap &enthalpyMap,
const field::FieldDofMap &displacementMap
@@ -103,7 +103,7 @@ export namespace mean_field::operators::context::barotropic {
void VerifyPrepared() const;
const fem::FEM &m_f;
const mapping::DomainMapperStateless &m_domainMapper;
const mapping::DomainMapper &m_domainMapper;
int m_densitySize{0};
int m_enthalpySize{0};

View File

@@ -49,11 +49,12 @@ export namespace mean_field::operators::context::gravity_field {
bool reconstructed_operators{false};
bool rebuilt_mass_operator{false};
bool rebuilt_source_operator{false};
bool rebuilt_divergence_operator{false};
bool refreshed_variation_state{false};
[[nodiscard]] bool DidAnyWork() const noexcept {
return reconstructed_operators || rebuilt_mass_operator || rebuilt_source_operator ||
refreshed_variation_state;
rebuilt_divergence_operator || refreshed_variation_state;
}
};
@@ -61,7 +62,7 @@ export namespace mean_field::operators::context::gravity_field {
public:
GravityFieldGeometryContext(
const fem::FEM &f,
const mapping::DomainMapperStateless &domain_mapper
const mapping::DomainMapper &domain_mapper
);
GravityFieldGeometryContext(const GravityFieldGeometryContext &) = delete;
@@ -77,6 +78,8 @@ export namespace mean_field::operators::context::gravity_field {
[[nodiscard]] const PreparedMappedHDivMassOperator &GetMassOperator() const;
[[nodiscard]] const PreparedMappedGravitySourceOperator &GetSourceOperator() const;
[[nodiscard]] const mfem::Operator &GetDivergenceOperator() const;
[[nodiscard]] const mfem::Operator &GetTransposeDivergenceOperator() const;
[[nodiscard]] const mfem::Vector &GetDisplacementTrue() const;
[[nodiscard]] const field::FieldDofMap &GetDisplacementMap() const noexcept;
[[nodiscard]] DiscretizationRevision GetDiscretizationRevision() const noexcept;
@@ -85,10 +88,12 @@ export namespace mean_field::operators::context::gravity_field {
private:
const fem::FEM &m_fem;
const mapping::DomainMapperStateless &m_domain_mapper;
const mapping::DomainMapper &m_domain_mapper;
std::unique_ptr<PreparedMappedHDivMassOperator> m_mass_operator;
std::unique_ptr<PreparedMappedGravitySourceOperator> m_source_operator;
std::unique_ptr<mfem::ParMixedBilinearForm> m_divergence_operator;
std::unique_ptr<mfem::TransposeOperator> m_transpose_divergence_operator;
field::FieldDofMap m_displacement_map;
mfem::Vector m_displacement_true;
@@ -113,7 +118,7 @@ export namespace mean_field::operators::context::gravity_field {
public:
GravityFieldLinearizationContext(
const fem::FEM &f,
const mapping::DomainMapperStateless &domain_mapper
const mapping::DomainMapper &domain_mapper
);
GravityFieldLinearizationContext(const GravityFieldLinearizationContext &) = delete;

View File

@@ -96,7 +96,7 @@ export namespace mean_field::operators::context::hydrostatic {
public:
HydrostaticEquilibriumContext(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper
const mapping::DomainMapper &domainMapper
);
HydrostaticEquilibriumContext(const HydrostaticEquilibriumContext &) = delete;
@@ -138,7 +138,7 @@ export namespace mean_field::operators::context::hydrostatic {
void VerifyPrepared() const;
const fem::FEM &m_f;
const mapping::DomainMapperStateless &m_domainMapper;
const mapping::DomainMapper &m_domainMapper;
field::FieldDofMap m_enthalpyMap;
field::FieldDofMap m_gravityPotentialMap;

View File

@@ -81,7 +81,7 @@ export namespace mean_field::operators::context::pressure_force {
public:
PressureForceLinearizationContext(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const mapping::DomainMapper &domainMapper,
const field::FieldDofMap &enthalpyMap,
const field::FieldDofMap &displacementMap
);

View File

@@ -78,7 +78,7 @@ export namespace mean_field::operators::context::rotational_displacement_force {
public:
RotationalDisplacementForceLinearizationContext(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper
const mapping::DomainMapper &domainMapper
);
RotationalDisplacementForceLinearizationContext(const RotationalDisplacementForceLinearizationContext &) =

View File

@@ -13,7 +13,7 @@ export namespace mean_field::operators {
public:
GravityFieldOperator(
fem::FEM &f,
const mapping::DomainMapperStateless &domain_mapper,
const mapping::DomainMapper &domain_mapper,
context::gravity_field::GravityFieldLinearizationContext &linearization_context,
const mfem::Array<int> &state_offsets,
GravityFieldJacobianOperator &jacobian
@@ -55,7 +55,7 @@ export namespace mean_field::operators {
private:
fem::FEM &m_fem;
const mapping::DomainMapperStateless &m_domain_mapper;
const mapping::DomainMapper &m_domain_mapper;
context::gravity_field::GravityFieldLinearizationContext &m_linearization_context;
mfem::Array<int> m_state_offsets;
mfem::Array<int> m_residual_offsets;
@@ -112,4 +112,39 @@ export namespace mean_field::operators {
context::gravity_field::GravityFieldGeometryContext &m_gravity_field_geometry_context;
mfem::Vector m_displacement;
};
class ReducedGravityFieldPreconditioner final : public mfem::Solver {
public:
ReducedGravityFieldPreconditioner(
const fem::FEM &f,
const context::gravity_field::GravityFieldGeometryContext &geometry_context
);
ReducedGravityFieldPreconditioner(const ReducedGravityFieldPreconditioner &) = delete;
ReducedGravityFieldPreconditioner &operator=(const ReducedGravityFieldPreconditioner &) = delete;
ReducedGravityFieldPreconditioner(ReducedGravityFieldPreconditioner &&) = delete;
ReducedGravityFieldPreconditioner &operator=(ReducedGravityFieldPreconditioner &&) = delete;
void SetOperator(const mfem::Operator &gravity_operator) override;
void Mult(
const mfem::Vector &right_hand_side,
mfem::Vector &action
) const override;
[[nodiscard]] const mfem::Array<int> &GetOffsets() const noexcept;
private:
field::FieldDofMap m_flux_map;
field::FieldDofMap m_potential_map;
mfem::Array<int> m_offsets;
mfem::Array<int> m_empty_tdofs;
std::unique_ptr<mfem::OperatorJacobiSmoother> m_mass_preconditioner;
std::unique_ptr<mfem::HypreParMatrix> m_schur;
std::unique_ptr<mfem::HypreBoomerAMG> m_potential_preconditioner;
mutable mfem::Vector m_potential_rhs_true;
mutable mfem::Vector m_potential_action_true;
};
} // namespace mean_field::operators

View File

@@ -11,7 +11,7 @@ export namespace mean_field::operators {
public:
GravityFieldJacobianOperator(
fem::FEM &f,
const mapping::DomainMapperStateless &domain_mapper,
const mapping::DomainMapper &domain_mapper,
const context::gravity_field::GravityFieldLinearizationContext &linearization_context,
const mfem::Array<int> &state_offsets,
const mfem::Array<int> &residual_offsets
@@ -27,7 +27,7 @@ export namespace mean_field::operators {
private:
fem::FEM &m_fem;
const mapping::DomainMapperStateless &m_domain_mapper;
const mapping::DomainMapper &m_domain_mapper;
const context::gravity_field::GravityFieldLinearizationContext &m_linearization_context;
mfem::Array<int> m_state_offsets;
mfem::Array<int> m_residual_offsets;

View File

@@ -22,7 +22,7 @@ export namespace mean_field::operators::kernels {
*/
void apply_barotropic_closure(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const mapping::DomainMapper &domainMapper,
const eos::Polytrope &equationOfState,
const mfem::Vector &densityTrue,
const mfem::Vector &enthalpyTrue,
@@ -32,7 +32,7 @@ export namespace mean_field::operators::kernels {
void apply_barotropic_closure_density_action(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const mapping::DomainMapper &domainMapper,
const eos::Polytrope &equationOfState,
const mfem::Vector &densityVariationTrue,
const mfem::Vector &displacementTrue,
@@ -41,7 +41,7 @@ export namespace mean_field::operators::kernels {
void apply_barotropic_closure_enthalpy_action(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const mapping::DomainMapper &domainMapper,
const eos::Polytrope &equationOfState,
const mfem::Vector &baseEnthalpyTrue,
const mfem::Vector &enthalpyVariationTrue,
@@ -51,7 +51,7 @@ export namespace mean_field::operators::kernels {
void apply_barotropic_closure_displacement_action(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const mapping::DomainMapper &domainMapper,
const eos::Polytrope &equationOfState,
const mfem::Vector &baseDensityTrue,
const mfem::Vector &baseEnthalpyTrue,

View File

@@ -10,7 +10,7 @@ export import :mapping.domain_mapper;
export namespace mean_field::operators::kernels {
void apply_gravity_displacement_force_residual(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const mapping::DomainMapper &domainMapper,
const mfem::Vector &densityTrue,
const mfem::Vector &gravityGradientTrue,
const mfem::Vector &displacementTrue,
@@ -19,7 +19,7 @@ export namespace mean_field::operators::kernels {
void apply_gravity_displacement_force_density_action(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const mapping::DomainMapper &domainMapper,
const mfem::Vector &densityVariationTrue,
const mfem::Vector &baseGravityGradientTrue,
const mfem::Vector &displacementTrue,
@@ -28,7 +28,7 @@ export namespace mean_field::operators::kernels {
void apply_gravity_displacement_force_gradient_action(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const mapping::DomainMapper &domainMapper,
const mfem::Vector &baseDensityTrue,
const mfem::Vector &gravityGradientVariationTrue,
const mfem::Vector &displacementTrue,
@@ -37,7 +37,7 @@ export namespace mean_field::operators::kernels {
void apply_gravity_displacement_force_displacement_action(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const mapping::DomainMapper &domainMapper,
const mfem::Vector &baseDensityTrue,
const mfem::Vector &baseGravityGradientTrue,
const mfem::Vector &displacementVariationTrue,
@@ -47,7 +47,7 @@ export namespace mean_field::operators::kernels {
void apply_gravity_displacement_force_complete_action(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const mapping::DomainMapper &domainMapper,
const mfem::Vector &baseDensityTrue,
const mfem::Vector &densityVariationTrue,
const mfem::Vector &baseGravityGradientTrue,

View File

@@ -15,7 +15,7 @@ export namespace mean_field::operators::kernels {
void apply_mapped_hdiv_mass(
const fem::FEM &f,
const mapping::DomainMapperStateless &domain_mapper,
const mapping::DomainMapper &domain_mapper,
const mfem::Vector &gravity_gradient_true,
const mfem::Vector &displacement_true,
mfem::Vector &action
@@ -23,7 +23,7 @@ export namespace mean_field::operators::kernels {
void apply_mapped_source(
const fem::FEM &f,
const mapping::DomainMapperStateless &domain_mapper,
const mapping::DomainMapper &domain_mapper,
const mfem::Vector &density_true,
const mfem::Vector &displacement_true,
mfem::Vector &action
@@ -31,7 +31,7 @@ export namespace mean_field::operators::kernels {
void apply_mapped_hdiv_mass_variation(
const fem::FEM &f,
const mapping::DomainMapperStateless &domain_mapper,
const mapping::DomainMapper &domain_mapper,
const mfem::Vector &gravity_gradient_true,
const mfem::Vector &displacement_true,
const mfem::Vector &displacement_variation_true,
@@ -40,7 +40,7 @@ export namespace mean_field::operators::kernels {
void apply_mapped_source_variation(
const fem::FEM &f,
const mapping::DomainMapperStateless &domain_mapper,
const mapping::DomainMapper &domain_mapper,
const mfem::Vector &density_true,
const mfem::Vector &displacement_true,
const mfem::Vector &displacement_variation_true,

View File

@@ -11,7 +11,7 @@ export import :physics.rigid_rotation;
export namespace mean_field::operators::kernels {
void apply_hydrostatic_equilibrium(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const mapping::DomainMapper &domainMapper,
const physics::RigidRotation &rotation,
const mfem::Vector &enthalpyTrue,
const mfem::Vector &potentialTrue,
@@ -22,7 +22,7 @@ export namespace mean_field::operators::kernels {
void apply_hydrostatic_equilibrium_enthalpy_action(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const mapping::DomainMapper &domainMapper,
const mfem::Vector &enthalpyVariationTrue,
const mfem::Vector &displacementTrue,
mfem::Vector &action
@@ -30,7 +30,7 @@ export namespace mean_field::operators::kernels {
void apply_hydrostatic_equilibrium_potential_action(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const mapping::DomainMapper &domainMapper,
const mfem::Vector &potentialVariationTrue,
const mfem::Vector &displacementTrue,
mfem::Vector &action
@@ -38,7 +38,7 @@ export namespace mean_field::operators::kernels {
void apply_hydrostatic_equilibrium_constant_action(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const mapping::DomainMapper &domainMapper,
double constantVariation,
const mfem::Vector &displacementTrue,
mfem::Vector &action
@@ -46,7 +46,7 @@ export namespace mean_field::operators::kernels {
void apply_hydrostatic_equilibrium_displacement_action(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const mapping::DomainMapper &domainMapper,
const physics::RigidRotation &rotation,
const mfem::Vector &baseEnthalpyTrue,
const mfem::Vector &basePotentialTrue,
@@ -58,7 +58,7 @@ export namespace mean_field::operators::kernels {
void apply_hydrostatic_equilibrium_action(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const mapping::DomainMapper &domainMapper,
const physics::RigidRotation &rotation,
const mfem::Vector &baseEnthalpyTrue,
const mfem::Vector &basePotentialTrue,

View File

@@ -11,7 +11,7 @@ export import :eos.polytrope;
export namespace mean_field::operators::kernels {
void apply_pressure_force_residual(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const mapping::DomainMapper &domainMapper,
const eos::Polytrope &barotrope,
const mfem::Vector &enthalpyTrue,
const mfem::Vector &displacementTrue,
@@ -20,7 +20,7 @@ export namespace mean_field::operators::kernels {
void apply_pressure_force_enthalpy_action(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const mapping::DomainMapper &domainMapper,
const eos::Polytrope &barotrope,
const mfem::Vector &baseEnthalpyTrue,
const mfem::Vector &enthalpyVariationTrue,
@@ -30,7 +30,7 @@ export namespace mean_field::operators::kernels {
void apply_pressure_force_displacement_action(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const mapping::DomainMapper &domainMapper,
const eos::Polytrope &barotrope,
const mfem::Vector &baseEnthalpyTrue,
const mfem::Vector &displacementVariationTrue,

View File

@@ -25,7 +25,7 @@ export namespace mean_field::operators::kernels {
*/
void apply_rotational_displacement_force_residual(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const mapping::DomainMapper &domainMapper,
const physics::RigidRotation &rotation,
const mfem::Vector &densityTrue,
const mfem::Vector &displacementTrue,
@@ -34,7 +34,7 @@ export namespace mean_field::operators::kernels {
void apply_rotational_displacement_force_density_action(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const mapping::DomainMapper &domainMapper,
const physics::RigidRotation &rotation,
const mfem::Vector &densityVariationTrue,
const mfem::Vector &displacementTrue,
@@ -43,7 +43,7 @@ export namespace mean_field::operators::kernels {
void apply_rotational_displacement_force_displacement_action(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const mapping::DomainMapper &domainMapper,
const physics::RigidRotation &rotation,
const mfem::Vector &baseDensityTrue,
const mfem::Vector &displacementVariationTrue,
@@ -53,7 +53,7 @@ export namespace mean_field::operators::kernels {
void apply_rotational_displacement_force_complete_action(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const mapping::DomainMapper &domainMapper,
const physics::RigidRotation &rotation,
const mfem::Vector &baseDensityTrue,
const mfem::Vector &densityVariationTrue,

View File

@@ -26,7 +26,7 @@ export namespace mean_field::operators {
public:
PreparedBarotropicClosureOperator(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const mapping::DomainMapper &domainMapper,
const eos::Polytrope &equationOfState
);
@@ -71,7 +71,7 @@ export namespace mean_field::operators {
PreparedBarotropicClosureOperator(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const mapping::DomainMapper &domainMapper,
const eos::Polytrope &equationOfState,
ConstructionData constructionData
);
@@ -100,7 +100,7 @@ export namespace mean_field::operators {
};
const fem::FEM &m_fem;
const mapping::DomainMapperStateless &m_domainMapper;
const mapping::DomainMapper &m_domainMapper;
const eos::Polytrope &m_equationOfState;
field::FieldDofMap m_densityMap;

View File

@@ -85,7 +85,7 @@ export namespace mean_field::operators {
public:
PreparedDisplacementResidualOperator(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const mapping::DomainMapper &domainMapper,
const eos::Polytrope &barotrope,
const context::gravity_field::GravityFieldLinearizationContext &gravityContext
);
@@ -158,7 +158,7 @@ export namespace mean_field::operators {
void VerifyPrepared() const;
const fem::FEM &m_fem;
const mapping::DomainMapperStateless &m_domainMapper;
const mapping::DomainMapper &m_domainMapper;
const context::gravity_field::GravityFieldLinearizationContext &m_gravityContext;
PreparedPressureForceOperator m_pressureOperator;

View File

@@ -37,7 +37,7 @@ export namespace mean_field::operators {
public:
PreparedGravityDisplacementForceOperator(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const mapping::DomainMapper &domainMapper,
const context::gravity_field::GravityFieldLinearizationContext &gravityContext
);
@@ -107,7 +107,7 @@ export namespace mean_field::operators {
void VerifyPrepared() const;
const fem::FEM &m_fem;
const mapping::DomainMapperStateless &m_domainMapper;
const mapping::DomainMapper &m_domainMapper;
const context::gravity_field::GravityFieldLinearizationContext &m_gravityContext;
context::gravity_field::GravityFieldRevisions m_preparedRevisions;

View File

@@ -14,7 +14,7 @@ export namespace mean_field::operators {
public:
PreparedMappedGravitySourceOperator(
const fem::FEM &f,
const mapping::DomainMapperStateless &domain_mapper
const mapping::DomainMapper &domain_mapper
);
void Prepare(const mfem::Vector &displacement);
@@ -55,7 +55,7 @@ export namespace mean_field::operators {
};
const fem::FEM &m_fem;
const mapping::DomainMapperStateless &m_domain_mapper;
const mapping::DomainMapper &m_domain_mapper;
field::FieldDofMap m_density_map;
field::FieldDofMap m_potential_map;

View File

@@ -13,7 +13,7 @@ export namespace mean_field::operators {
public:
PreparedMappedHDivMassOperator(
const fem::FEM &f,
const mapping::DomainMapperStateless &domain_mapper
const mapping::DomainMapper &domain_mapper
);
void Prepare(const mfem::Vector &displacement);
@@ -21,6 +21,8 @@ export namespace mean_field::operators {
const mfem::Vector &gravity_gradient,
mfem::Vector &action
) const override;
void AssembleDiagonal(mfem::Vector &diagonal) const override;
void AssembleTrueDiagonal(mfem::Vector &diagonal) const;
[[nodiscard]] bool IsPrepared() const noexcept;
[[nodiscard]] std::uint64_t GetPreparationCount() const noexcept;
@@ -30,7 +32,7 @@ export namespace mean_field::operators {
private:
const fem::FEM &m_fem;
const mapping::DomainMapperStateless &m_domain_mapper;
const mapping::DomainMapper &m_domain_mapper;
field::FieldDofMap m_flux_map;
field::FieldDofMap m_displacement_map;
@@ -40,9 +42,11 @@ export namespace mean_field::operators {
std::unique_ptr<mfem::MatrixCoefficient> m_stellar_mass_coefficient;
std::unique_ptr<mfem::MatrixCoefficient> m_vacuum_mass_coefficient;
std::unique_ptr<mfem::ParBilinearForm> m_mass_form;
std::unique_ptr<mfem::ParBilinearForm> m_stellar_mass_form;
std::unique_ptr<mfem::ParBilinearForm> m_vacuum_mass_form;
mutable mfem::Vector m_flux_true;
mutable mfem::Vector m_action_true;
mutable mfem::Vector m_domain_action_true;
mfem::Vector m_displacement_true;
std::uint64_t m_preparation_count{0};
bool m_is_prepared{false};

View File

@@ -84,7 +84,7 @@ export namespace mean_field::operators {
public:
PreparedHydrostaticEquilibriumOperator(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper
const mapping::DomainMapper &domainMapper
);
PreparedHydrostaticEquilibriumOperator(const PreparedHydrostaticEquilibriumOperator &) = delete;
@@ -217,7 +217,7 @@ export namespace mean_field::operators {
void VerifyPrepared() const;
const fem::FEM &m_fem;
const mapping::DomainMapperStateless &m_domainMapper;
const mapping::DomainMapper &m_domainMapper;
context::hydrostatic::HydrostaticEquilibriumContext m_context;

View File

@@ -68,7 +68,7 @@ export namespace mean_field::operators {
public:
PreparedMassNormalizationOperator(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const mapping::DomainMapper &domainMapper,
const context::gravity_field::GravityFieldLinearizationContext &gravityContext
);
@@ -148,7 +148,7 @@ export namespace mean_field::operators {
[[nodiscard]] double GlobalSum(double localValue) const;
const fem::FEM &m_fem;
const mapping::DomainMapperStateless &m_domainMapper;
const mapping::DomainMapper &m_domainMapper;
const context::gravity_field::GravityFieldLinearizationContext &m_gravityContext;
std::vector<ElementPAData> m_elements;

View File

@@ -75,7 +75,7 @@ export namespace mean_field::operators {
public:
PreparedPressureForceOperator(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const mapping::DomainMapper &domainMapper,
const eos::Polytrope &equationOfState
);
@@ -155,7 +155,7 @@ export namespace mean_field::operators {
PreparedPressureForceOperator(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const mapping::DomainMapper &domainMapper,
const eos::Polytrope &equationOfState,
ConstructionData constructionData
);
@@ -217,7 +217,7 @@ export namespace mean_field::operators {
const fem::FEM &m_fem;
const mapping::DomainMapperStateless &m_domainMapper;
const mapping::DomainMapper &m_domainMapper;
const eos::Polytrope &m_equationOfState;

View File

@@ -42,7 +42,7 @@ export namespace mean_field::operators {
public:
PreparedRotationalDisplacementForceOperator(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper
const mapping::DomainMapper &domainMapper
);
PreparedRotationalDisplacementForceOperator(const PreparedRotationalDisplacementForceOperator &) = delete;
@@ -105,7 +105,7 @@ export namespace mean_field::operators {
void VerifyPrepared() const;
const fem::FEM &m_fem;
const mapping::DomainMapperStateless &m_domainMapper;
const mapping::DomainMapper &m_domainMapper;
context::rotational_displacement_force::RotationalDisplacementForceLinearizationContext m_context;

View File

@@ -76,14 +76,14 @@ export namespace mean_field::operators {
public:
PreparedStellarEquilibriumOperator(
fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const mapping::DomainMapper &domainMapper,
const eos::Polytrope &equationOfState,
double targetMass
);
PreparedStellarEquilibriumOperator(
fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const mapping::DomainMapper &domainMapper,
const eos::Polytrope &equationOfState,
const models::StellarModel &stellarModel
);
@@ -130,7 +130,7 @@ export namespace mean_field::operators {
PreparedStellarEquilibriumOperator(
fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const mapping::DomainMapper &domainMapper,
const eos::Polytrope &equationOfState,
double targetMass,
ConstructionData constructionData

View File

@@ -1,162 +0,0 @@
module;
#include <cmath>
#include <format>
#include <stdexcept>
export module mean_field:physics.barotrope;
export namespace mean_field::physics {
class PolytropicBarotrope final {
public:
PolytropicBarotrope(
const double polytropic_index,
const double polytropic_constant
)
: m_polytropic_index(polytropic_index),
m_polytropic_constant(polytropic_constant),
m_enthalpy_scale((polytropic_index + 1.0) * polytropic_constant) {
if (!std::isfinite(polytropic_index) || polytropic_index < 1.0) {
throw std::invalid_argument(
std::format(
"The differentiable polytropic closure requires a "
"finite polytropic index greater than or equal to one. "
"Instead a value of {} has been provided",
polytropic_index
)
);
}
if (!std::isfinite(polytropic_constant) || polytropic_constant <= 0.0) {
throw std::invalid_argument(
std::format(
"The polytropic constant must be finite and positive. "
"Instead a value of {} has been provided",
polytropic_constant
)
);
}
};
[[nodiscard]] double polytropic_index() const noexcept {
return m_polytropic_index;
}
[[nodiscard]] double polytropic_constant() const noexcept {
return m_polytropic_constant;
}
[[nodiscard]] double enthalpy_scale() const noexcept {
return m_enthalpy_scale;
}
[[nodiscard]] double pressure_from_density(const double density) const {
validate_nonnegativity(density, "density");
if (density == 0.0) {
return 0.0;
}
return m_polytropic_constant * std::pow(density, 1.0 + 1.0 / m_polytropic_index);
}
[[nodiscard]] double enthalpy_from_density(const double density) const {
validate_nonnegativity(density, "density");
if (density == 0.0) {
return 0.0;
}
return m_enthalpy_scale * std::pow(density, 1.0 / m_polytropic_index);
}
[[nodiscard]] double density_from_enthalpy(const double enthalpy) const {
validate_finite(enthalpy, "enthalpy");
if (enthalpy <= 0.0) {
return 0.0;
}
return std::pow(enthalpy / m_enthalpy_scale, m_polytropic_index);
}
[[nodiscard]] double pressure_from_enthalpy(const double enthalpy) const {
validate_finite(enthalpy, "enthalpy");
if (enthalpy <= 0.0) {
return 0.0;
}
return density_from_enthalpy(enthalpy) * enthalpy / (m_polytropic_index + 1.0);
}
[[nodiscard]] double density_derivative_from_enthalpy(const double enthalpy) const {
validate_finite(enthalpy, "enthalpy");
if (enthalpy < 0.0) {
return 0.0;
}
if (enthalpy == 0.0) {
return m_polytropic_index == 1.0 ? 1.0 / m_enthalpy_scale : 0.0;
}
return m_polytropic_index / m_enthalpy_scale *
std::pow(enthalpy / m_enthalpy_scale, m_polytropic_index - 1.0);
}
[[nodiscard]] double pressure_derivative_from_enthalpy(const double enthalpy) const {
validate_finite(enthalpy, "enthalpy");
if (enthalpy <= 0.0) {
return 0.0;
}
return density_from_enthalpy(enthalpy);
}
[[nodiscard]] double pressure_derivative_from_density(const double density) const {
validate_nonnegativity(density, "density");
if (density == 0.0) {
return 0.0;
}
return m_polytropic_constant * (1.0 + 1.0 / m_polytropic_index) *
std::pow(density, 1.0 / m_polytropic_index);
}
private:
static void validate_finite(
const double value,
const char *quantity
) {
if (!std::isfinite(value)) {
throw std::domain_error(
std::format(
"The {} must be finite. Instead a value of {} has been "
"provided",
quantity, value
)
);
}
}
static void validate_nonnegativity(
const double value,
const char *quantity
) {
validate_finite(value, quantity);
if (value < 0.0) {
throw std::domain_error(
std::format(
"The {} must be non-negative. Instead a value of {} "
"has been "
"provided",
quantity, value
)
);
}
}
double m_polytropic_index;
double m_polytropic_constant;
double m_enthalpy_scale;
};
} // namespace mean_field::physics

View File

@@ -1,29 +0,0 @@
module;
#include <memory>
#include <mfem.hpp>
export module mean_field:physics.contexts;
export import :mapping.coefficients;
export namespace mean_field::physics {
struct GravityContext {
std::unique_ptr<mfem::ParBilinearForm> m_form;
std::unique_ptr<mfem::ParMixedBilinearForm> b_form;
std::unique_ptr<mfem::BlockOperator> block_A;
std::unique_ptr<mfem::Solver> prec_M;
std::unique_ptr<mfem::HypreBoomerAMG> prec_Phi;
std::unique_ptr<mfem::BlockDiagonalPreconditioner> block_prec;
std::unique_ptr<mfem::MINRESSolver> minres;
mfem::Array<int> stellar_mask;
std::unique_ptr<mfem::TransposeOperator> BT;
std::unique_ptr<mfem::HypreParMatrix> Schur;
std::unique_ptr<mfem::MatrixCoefficient> mapped_hdiv_mass_coeff;
std::unique_ptr<mfem::Operator> source_form;
};
} // namespace mean_field::physics

View File

@@ -16,27 +16,13 @@ export namespace mean_field::physics {
}
};
GravitySolution grav_potential(
fem::FEM &f,
const utils::Args &args,
const mfem::GridFunction &rho,
bool phi_warm = false
);
GravitySolution grav_potential_new(
GravitySolution solve_gravity_field(
fem::FEM &f,
const utils::Args &args,
const mfem::GridFunction &rho,
const mfem::GridFunction &displacement
);
mfem::GridFunction get_potential(
fem::FEM &fem,
const utils::Args &args,
const mfem::GridFunction &rho,
bool warm = false
);
mfem::DenseMatrix compute_quadrupole_moment_tensor(
const fem::FEM &fem,
const mfem::GridFunction &rho,
@@ -49,5 +35,4 @@ export namespace mean_field::physics {
const mfem::Vector &phys_x
);
void update_stiffness_matrix(fem::FEM &fem);
} // namespace mean_field::physics

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,4 @@
module;
#include <expected>
#include <functional>
#include <string_view>
@@ -9,109 +8,85 @@ module;
#include <XAD/XAD.hpp>
export module mean_field:utils.misc;
import :boundary.contexts;
import :utils.domain;
export namespace mean_field::utils {
constexpr double APPROX_MAX_ACCEPTABLE_POTENTIAL_ERROR_SI_BURNING = 1e-4;
constexpr double APPROX_MAX_ACCEPTABLE_POTENTIAL_ERROR_SI_BURNING = 1e-4;
bool is_vacuum(
const mfem::ElementTransformation &Tr,
mfem::Array<mfem::Vector *> elvec
) {
if (Tr.Attribute == 3) {
const int size_elvec = elvec.Size();
for (int i = 0; i < size_elvec; i++) {
if (elvec[i]) {
*elvec[i] = 0.0;
}
}
return true;
}
return false;
bool is_vacuum(const mfem::ElementTransformation &Tr,
mfem::Array<mfem::Vector *> elvec) {
using Schema = domain::CoreEnvelopeVacuumDomainSchema;
if (Schema::template attribute_belongs_to<domain::Vacuum>(Tr.Attribute)) {
const int size_elvec = elvec.Size();
for (int i = 0; i < size_elvec; i++) {
if (elvec[i]) {
*elvec[i] = 0.0;
}
}
return true;
}
return false;
}
bool is_vacuum(
const mfem::ElementTransformation &Tr,
const mfem::Array2D<mfem::DenseMatrix *> &elmats
) {
if (Tr.Attribute == 3) {
const int cols = elmats.NumCols();
const int rows = elmats.NumRows();
for (int rowID = 0; rowID < rows; rowID++) {
for (int colID = 0; colID < cols; colID++) {
if (elmats(rowID, colID)) {
*elmats(rowID, colID) = 0.0;
}
}
}
return true;
bool is_vacuum(const mfem::ElementTransformation &Tr,
const mfem::Array2D<mfem::DenseMatrix *> &elmats) {
using Schema = domain::CoreEnvelopeVacuumDomainSchema;
if (Schema::template attribute_belongs_to<domain::Vacuum>(Tr.Attribute)) {
const int cols = elmats.NumCols();
const int rows = elmats.NumRows();
for (int rowID = 0; rowID < rows; rowID++) {
for (int colID = 0; colID < cols; colID++) {
if (elmats(rowID, colID)) {
*elmats(rowID, colID) = 0.0;
}
return false;
}
}
return true;
}
return false;
}
constexpr std::string_view ANSI_GREEN = "\033[32m";
constexpr std::string_view ANSI_RED = "\033[31m";
constexpr std::string_view ANSI_YELLOW = "\033[33m";
constexpr std::string_view ANSI_BLUE = "\033[34m";
constexpr std::string_view ANSI_MAGENTA = "\033[35m";
constexpr std::string_view ANSI_CYAN = "\033[36m";
constexpr std::string_view ANSI_RESET = "\033[0m";
constexpr std::string_view ANSI_BCYAN = "\033[1;36m";
constexpr std::string_view ANSI_GREEN = "\033[32m";
constexpr std::string_view ANSI_RED = "\033[31m";
constexpr std::string_view ANSI_YELLOW = "\033[33m";
constexpr std::string_view ANSI_BLUE = "\033[34m";
constexpr std::string_view ANSI_MAGENTA = "\033[35m";
constexpr std::string_view ANSI_CYAN = "\033[36m";
constexpr std::string_view ANSI_RESET = "\033[0m";
constexpr std::string_view ANSI_BCYAN = "\033[1;36m";
constexpr double G = 1.0;
constexpr double MASS = 1.0;
constexpr double RADIUS = 1.0;
constexpr double G = 1.0;
constexpr double MASS = 1.0;
constexpr double RADIUS = 1.0;
[[maybe_unused]] constexpr char HOST[10] = "localhost";
[[maybe_unused]] constexpr int PORT = 19916;
[[maybe_unused]] constexpr char HOST[10] = "localhost";
[[maybe_unused]] constexpr int PORT = 19916;
template <typename T>
concept is_xad = std::is_same_v<T, xad::AReal<long double>> || std::is_same_v<T, xad::AReal<double>> ||
std::is_same_v<T, xad::AReal<float>>;
template <typename T>
concept is_xad = std::is_same_v<T, xad::AReal<long double>> ||
std::is_same_v<T, xad::AReal<double>> ||
std::is_same_v<T, xad::AReal<float>>;
template <typename T>
concept is_real = std::is_floating_point_v<T> || is_xad<T>;
template <typename T>
concept is_real = std::is_floating_point_v<T> || is_xad<T>;
template <is_real T> using EOS_P = std::function<T(const T &rho, const T &temp)>;
template <is_real T>
using EOS_P = std::function<T(const T &rho, const T &temp)>;
enum class DOMAINS : uint8_t {
CORE = 1 << 0,
ENVELOPE = 1 << 1,
VACUUM = 1 << 2,
STELLAR = CORE | ENVELOPE,
ALL = CORE | ENVELOPE | VACUUM
};
enum class DOMAINS : uint8_t {
CORE = 1 << 0,
ENVELOPE = 1 << 1,
VACUUM = 1 << 2,
STELLAR = CORE | ENVELOPE,
ALL = CORE | ENVELOPE | VACUUM
};
DOMAINS operator|(
DOMAINS lhs,
DOMAINS rhs
);
DOMAINS operator|(DOMAINS lhs, DOMAINS rhs);
DOMAINS operator&(
DOMAINS lhs,
DOMAINS rhs
);
DOMAINS operator&(DOMAINS lhs, DOMAINS rhs);
void populate_element_mask(
const mfem::Mesh *mesh,
DOMAINS domain,
mfem::Array<int> &mask
);
void populate_domain_tdofs(
const mfem::ParFiniteElementSpace *fes,
const mfem::Array<int> &element_mask,
mfem::Array<int> &ess_tdof
);
std::expected<
boundary::Bounds,
boundary::BoundsError>
discover_bounds(
const mfem::Mesh *mesh,
int vacuum_attr
);
int get_mesh_order(const mfem::Mesh &mesh);
int get_mesh_order(const mfem::Mesh &mesh);
} // namespace mean_field::utils

View File

@@ -7,9 +7,9 @@ export import :mapping.compactification.options;
export namespace mean_field::utils {
struct potential {
double rtol;
double atol;
int max_iters;
double rtol{1.0e-12};
double atol{1.0e-12};
int max_iters{1000};
};
struct rot {
@@ -18,7 +18,7 @@ export namespace mean_field::utils {
double L;
};
struct DomainMapperStatelessOptions {
struct DomainMapperOptions {
int dimension{3};
int vacuum_element_attribute{3};
};
@@ -31,7 +31,7 @@ export namespace mean_field::utils {
double index{};
double mass{};
double c{};
DomainMapperStatelessOptions domain_mapper_options{};
DomainMapperOptions domain_mapper_options{};
mapping::compactification::options::KelvinCompactificationOptions kelvin_options{};
int max_iters{};
double tol{};