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

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

View File

@@ -13,23 +13,16 @@ namespace {
const std::array<
int,
FormT::dynamicOrderCount> &dynamic_orders = {},
const mean_field::utils::DOMAINS domain =
mean_field::utils::DOMAINS::ALL
const mean_field::utils::DOMAINS domain = mean_field::utils::DOMAINS::ALL
) {
using DensityField =
mean_field::field::Field<mean_field::field::Density>;
using DensityField = mean_field::field::Field<mean_field::field::Density>;
const mean_field::quadrature::Query query =
DensityField::make_query<FormT>(
mean_field::quadrature::QuadratureRole::diagnostic,
transformation.OrderW(), dynamic_orders, domain,
fem.has_mapping() ? mean_field::quadrature::MappingKind::general
: mean_field::quadrature::MappingKind::none
);
const mean_field::quadrature::Query query = DensityField::make_query<FormT>(
mean_field::quadrature::QuadratureRole::diagnostic, transformation.OrderW(), dynamic_orders, domain,
fem.has_mapping() ? mean_field::quadrature::MappingKind::general : mean_field::quadrature::MappingKind::none
);
return *fem.quadratureFactory
->get(query, transformation.GetGeometryType())
.integration_rule;
return *fem.quadratureFactory->get(query, transformation.GetGeometryType()).integration_rule;
}
} // namespace
@@ -45,15 +38,11 @@ namespace mean_field::analysis {
double local_integral;
mfem::Array<int> elem_markers;
populate_element_mask(fem.mesh.get(), domain, elem_markers);
const mfem::ElementTransformation &representative_transformation =
*fem.mesh->GetElementTransformation(0);
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
);
get_density_rule<field::Density::Form::MassConservation>(fem, representative_transformation, {}, domain);
if (fem.has_mapping() &&
coord_space == mapping::COORDINATE_SPACE::PHYSICAL) {
if (fem.has_mapping() && coord_space == mapping::COORDINATE_SPACE::PHYSICAL) {
mapping::MappedScalarCoefficient mapped_gf_c(*fem.mapping, gf_c);
// ReSharper disable once CppDFAMemoryLeak // Disabled because MFEM
@@ -80,10 +69,7 @@ namespace mean_field::analysis {
}
double global_integral = 0.0;
MPI_Allreduce(
&local_integral, &global_integral, 1, MPI_DOUBLE, MPI_SUM,
fem.mesh->GetComm()
);
MPI_Allreduce(&local_integral, &global_integral, 1, MPI_DOUBLE, MPI_SUM, fem.mesh->GetComm());
return global_integral;
}
@@ -99,12 +85,10 @@ namespace mean_field::analysis {
for (int i = 0; i < fem.mesh->GetNE(); ++i) {
if (fem.mesh->GetAttribute(i) == 3)
continue;
mfem::ElementTransformation *trans =
fem.mesh->GetElementTransformation(i);
const mfem::IntegrationRule &ir =
get_density_rule<field::Density::Form::CenterOfMass>(
fem, *trans, std::array<int, 1>{1}, utils::DOMAINS::STELLAR
);
mfem::ElementTransformation *trans = fem.mesh->GetElementTransformation(i);
const mfem::IntegrationRule &ir = get_density_rule<field::Density::Form::CenterOfMass>(
fem, *trans, std::array<int, 1>{1}, utils::DOMAINS::STELLAR
);
for (int j = 0; j < ir.GetNPoints(); ++j) {
const mfem::IntegrationPoint &ip = ir.IntPoint(j);
@@ -138,10 +122,7 @@ namespace mean_field::analysis {
MPI_Allreduce(&local_mass, &global_mass, 1, MPI_DOUBLE, MPI_SUM, comm);
MPI_Allreduce(
local_com.GetData(), global_com.GetData(), dim, MPI_DOUBLE, MPI_SUM,
comm
);
MPI_Allreduce(local_com.GetData(), global_com.GetData(), dim, MPI_DOUBLE, MPI_SUM, comm);
if (global_mass > 1e-18) {
global_com /= global_mass;
@@ -157,9 +138,7 @@ namespace mean_field::analysis {
mfem::GridFunction &rho,
const double target_mass
) {
if (const double current_mass = domain_integrate_grid_function(
fem, rho, utils::DOMAINS::STELLAR
);
if (const double current_mass = domain_integrate_grid_function(fem, rho, utils::DOMAINS::STELLAR);
current_mass > 1e-15)
rho *= (target_mass / current_mass);
}
@@ -168,16 +147,11 @@ namespace mean_field::analysis {
const fem::FEM &fem,
const mfem::GridFunction &rho
) {
auto s2_func = [](const mfem::Vector &x) {
return std::pow(x(0), 2) + std::pow(x(1), 2);
};
auto s2_func = [](const mfem::Vector &x) { return std::pow(x(0), 2) + std::pow(x(1), 2); };
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.mapping, s2_func);
} else {
s2_coeff = std::make_unique<mfem::FunctionCoefficient>(s2_func);
}
@@ -186,23 +160,16 @@ namespace mean_field::analysis {
mfem::ProductCoefficient I_integrand(rho_coeff, *s2_coeff);
mfem::LinearForm I_lf(fem.densityFes.get());
const mfem::ElementTransformation &representative_transformation =
*fem.mesh->GetElementTransformation(0);
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
const mfem::ElementTransformation &representative_transformation = *fem.mesh->GetElementTransformation(0);
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);
double local_I = 0.0;
if (fem.has_mapping()) {
mapping::MappedScalarCoefficient mapped_integrand(
*fem.mapping, I_integrand
);
mapping::MappedScalarCoefficient mapped_integrand(*fem.mapping, I_integrand);
auto *integrator = new mfem::DomainLFIntegrator(mapped_integrand);
integrator->SetIntRule(&integration_rule);
I_lf.AddDomainIntegrator(integrator, stellar_markers);
@@ -217,9 +184,7 @@ namespace mean_field::analysis {
}
double global_I = 0.0;
MPI_Allreduce(
&local_I, &global_I, 1, MPI_DOUBLE, MPI_SUM, fem.mesh->GetComm()
);
MPI_Allreduce(&local_I, &global_I, 1, MPI_DOUBLE, MPI_SUM, fem.mesh->GetComm());
return global_I;
}
@@ -229,13 +194,10 @@ namespace mean_field::analysis {
const utils::DOMAINS domain
) {
mfem::ParMesh &mesh = *fem.mesh;
const bool physical =
(coordinate_space == mapping::COORDINATE_SPACE::PHYSICAL);
const bool physical = (coordinate_space == mapping::COORDINATE_SPACE::PHYSICAL);
if (physical && !fem.has_mapping()) {
MFEM_ABORT(
"Physical volume requested but no domain mapping is available."
);
MFEM_ABORT("Physical volume requested but no domain mapping is available.");
}
double local_volume = 0.0;
@@ -258,9 +220,7 @@ namespace mean_field::analysis {
}
mfem::ElementTransformation *T = mesh.GetElementTransformation(e);
const mfem::IntegrationRule &ir =
get_density_rule<field::Density::Form::MassConservation>(
fem, *T, {}, domain
);
get_density_rule<field::Density::Form::MassConservation>(fem, *T, {}, domain);
for (int q = 0; q < ir.GetNPoints(); ++q) {
const mfem::IntegrationPoint &ip = ir.IntPoint(q);
@@ -277,10 +237,7 @@ namespace mean_field::analysis {
}
double global_volume = 0.0;
MPI_Allreduce(
&local_volume, &global_volume, 1, MPI_DOUBLE, MPI_SUM,
mesh.GetComm()
);
MPI_Allreduce(&local_volume, &global_volume, 1, MPI_DOUBLE, MPI_SUM, mesh.GetComm());
return global_volume;
}
} // namespace mean_field::analysis

View File

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

View File

@@ -4,8 +4,7 @@ module;
module mean_field;
namespace mean_field::integrators {
AdvectionIntegrator::AdvectionIntegrator(const mapping::DomainMapper &map)
: m_map(map) {
AdvectionIntegrator::AdvectionIntegrator(const mapping::DomainMapper &map) : m_map(map) {
}
void AdvectionIntegrator::AssembleElementVector(
@@ -39,8 +38,7 @@ namespace mean_field::integrators {
mfem::Vector shape_v(dof_v), shape_rho(dof_rho);
mfem::DenseMatrix dshape_v_ref(dof_v, dim), dshape_v_phys(dof_v, dim);
const mfem::IntegrationRule *ir =
&mfem::IntRules.Get(fe_v->GetGeomType(), 2 * fe_v->GetOrder() + 1);
const mfem::IntegrationRule *ir = &mfem::IntRules.Get(fe_v->GetGeomType(), 2 * fe_v->GetOrder() + 1);
for (int q = 0; q < ir->GetNPoints(); q++) {
const mfem::IntegrationPoint &ip = ir->IntPoint(q);
@@ -83,8 +81,7 @@ namespace mean_field::integrators {
for (int i = 0; i < dof_v; ++i) {
for (int c = 0; c < dim; ++c) {
r_v(i + c * dof_v) +=
shape_v(i) * rho_val * adv_val(c) * weight;
r_v(i + c * dof_v) += shape_v(i) * rho_val * adv_val(c) * weight;
}
}
}
@@ -117,8 +114,7 @@ namespace mean_field::integrators {
mfem::Vector shape_v(dof_v), shape_rho(dof_rho);
mfem::DenseMatrix dshape_v_ref(dof_v, dim), dshape_v_phys(dof_v, dim);
const mfem::IntegrationRule *ir =
&mfem::IntRules.Get(fe_v->GetGeomType(), 2 * fe_v->GetOrder() + 1);
const mfem::IntegrationRule *ir = &mfem::IntRules.Get(fe_v->GetGeomType(), 2 * fe_v->GetOrder() + 1);
for (int q = 0; q < ir->GetNPoints(); q++) {
const mfem::IntegrationPoint &ip = ir->IntPoint(q);
@@ -171,8 +167,7 @@ namespace mean_field::integrators {
double v_dot_grad_phi_j = 0.0;
for (int k = 0; k < dim; ++k) {
v_dot_grad_phi_j +=
v_val(k) * dshape_v_phys(j, k);
v_dot_grad_phi_j += v_val(k) * dshape_v_phys(j, k);
}
for (int d = 0; d < dim; ++d) {
@@ -187,11 +182,9 @@ namespace mean_field::integrators {
// \rho(\vec{v} \cdot \nabla \delta \vec{v})
// Only non-zero when the advected component
// matches the test component
double termB =
(c == d) ? v_dot_grad_phi_j : 0.0;
double termB = (c == d) ? v_dot_grad_phi_j : 0.0;
(*dv_dv)(row, col) += shape_v(i) * rho_val *
(termA + termB) * weight;
(*dv_dv)(row, col) += shape_v(i) * rho_val * (termA + termB) * weight;
}
}
}

View File

@@ -18,9 +18,7 @@ namespace mean_field::integrators {
m_omega = omega;
}
void CentrifugalForceIntegrator::SetIntegrationRule(
const mfem::IntegrationRule &ir
) {
void CentrifugalForceIntegrator::SetIntegrationRule(const mfem::IntegrationRule &ir) {
m_ir = &ir;
}
@@ -130,8 +128,7 @@ namespace mean_field::integrators {
mfem::Vector x_phys(dim);
mfem::Vector a(dim), b(dim);
const mfem::IntegrationRule *ir =
&mfem::IntRules.Get(fe_v->GetGeomType(), 2 * fe_v->GetOrder());
const mfem::IntegrationRule *ir = &mfem::IntRules.Get(fe_v->GetGeomType(), 2 * fe_v->GetOrder());
for (int q = 0; q < ir->GetNPoints(); ++q) {
const mfem::IntegrationPoint &ip = ir->IntPoint(q);
@@ -159,8 +156,7 @@ namespace mean_field::integrators {
for (int c = 0; c < dim; ++c) {
const int row = i + c * dof_v;
for (int j = 0; j < dof_rho; ++j) {
(*dv_drho)(row, j) +=
shape_v(i) * shape_rho(j) * b(c) * weight;
(*dv_drho)(row, j) += shape_v(i) * shape_rho(j) * b(c) * weight;
}
}
}

View File

@@ -49,8 +49,7 @@ namespace mean_field::integrators {
}
mfem::Vector shape_v(dof_v), shape_rho(dof_rho);
const mfem::IntegrationRule *ir =
&mfem::IntRules.Get(fe_v->GetGeomType(), 2 * fe_v->GetOrder());
const mfem::IntegrationRule *ir = &mfem::IntRules.Get(fe_v->GetGeomType(), 2 * fe_v->GetOrder());
for (int q = 0; q < ir->GetNPoints(); ++q) {
const mfem::IntegrationPoint &ip = ir->IntPoint(q);
@@ -78,8 +77,7 @@ namespace mean_field::integrators {
for (int i = 0; i < dof_v; ++i) {
for (int c = 0; c < dim; ++c) {
r_v(i + c * dof_v) +=
shape_v(i) * rho_val * F_coriolis(c) * weight;
r_v(i + c * dof_v) += shape_v(i) * rho_val * F_coriolis(c) * weight;
}
}
}
@@ -111,8 +109,7 @@ namespace mean_field::integrators {
*dv_drho = 0.0;
mfem::Vector shape_v(dof_v), shape_rho(dof_rho);
const mfem::IntegrationRule *ir =
&mfem::IntRules.Get(fe_v->GetGeomType(), 2 * fe_v->GetOrder());
const mfem::IntegrationRule *ir = &mfem::IntRules.Get(fe_v->GetGeomType(), 2 * fe_v->GetOrder());
for (int q = 0; q < ir->GetNPoints(); ++q) {
const mfem::IntegrationPoint &ip = ir->IntPoint(q);
@@ -146,9 +143,7 @@ namespace mean_field::integrators {
for (int d = 0; d < dim; ++d) {
int col = j + d * dof_v;
double coupling = m_omega_mat(c, d);
(*dv_dv)(row, col) += shape_v(i) * shape_v(j) *
2.0 * rho_val * coupling *
weight;
(*dv_dv)(row, col) += shape_v(i) * shape_v(j) * 2.0 * rho_val * coupling * weight;
}
}
}
@@ -161,8 +156,7 @@ namespace mean_field::integrators {
int row = i + c * dof_v;
for (int j = 0; j < dof_rho; ++j) {
int col = j;
(*dv_drho)(row, col) += shape_v(i) * shape_rho(j) *
F_coriolis(c) * weight;
(*dv_drho)(row, col) += shape_v(i) * shape_rho(j) * F_coriolis(c) * weight;
}
}
}

View File

@@ -6,14 +6,10 @@ import :solver.fields;
namespace {
using namespace mean_field;
constexpr int velocity_block =
solver::block_index(solver::FieldBlock::velocity);
constexpr int density_block =
solver::block_index(solver::FieldBlock::density);
constexpr int gravity_gradient_block =
solver::block_index(solver::FieldBlock::gravity_gradient);
constexpr int displacement_block =
solver::block_index(solver::FieldBlock::displacement);
constexpr int velocity_block = solver::block_index(solver::FieldBlock::velocity);
constexpr int density_block = solver::block_index(solver::FieldBlock::density);
constexpr int gravity_gradient_block = solver::block_index(solver::FieldBlock::gravity_gradient);
constexpr int displacement_block = solver::block_index(solver::FieldBlock::displacement);
} // namespace
namespace mean_field::integrators {
@@ -25,20 +21,15 @@ namespace mean_field::integrators {
m_jacobian_mode(jacobian_mode) {
}
void GravityMomentumIntegrator::SetJacobianMode(
const GravityForceJacobianMode jacobian_mode
) {
void GravityMomentumIntegrator::SetJacobianMode(const GravityForceJacobianMode jacobian_mode) {
m_jacobian_mode = jacobian_mode;
}
void GravityMomentumIntegrator::SetIntegrationRule(
const mfem::IntegrationRule &integration_rule
) {
void GravityMomentumIntegrator::SetIntegrationRule(const mfem::IntegrationRule &integration_rule) {
m_integration_rule = &integration_rule;
}
GravityForceJacobianMode
GravityMomentumIntegrator::GetJacobianMode() const {
GravityForceJacobianMode GravityMomentumIntegrator::GetJacobianMode() const {
return m_jacobian_mode;
}
@@ -53,27 +44,23 @@ namespace mean_field::integrators {
}
MFEM_VERIFY(
m_integration_rule,
"GravityForceIntegrator must be configured with an "
"integration rule before assembly."
m_integration_rule, "GravityForceIntegrator must be configured with an "
"integration rule before assembly."
);
MFEM_VERIFY(
el.Size() > gravity_gradient_block,
"GravityForceIntegrator requires velocity, density, and "
"gravity-gradient finite elements."
el.Size() > gravity_gradient_block, "GravityForceIntegrator requires velocity, density, and "
"gravity-gradient finite elements."
);
MFEM_VERIFY(
elfun.Size() > gravity_gradient_block,
"GravityForceIntegrator requires velocity, density, and "
"gravity-gradient element states."
elfun.Size() > gravity_gradient_block, "GravityForceIntegrator requires velocity, density, and "
"gravity-gradient element states."
);
MFEM_VERIFY(
elvec.Size() > velocity_block && elvec[velocity_block],
"GravityForceIntegrator requires a velocity residual block."
);
MFEM_VERIFY(
el[velocity_block] && el[density_block] &&
el[gravity_gradient_block],
el[velocity_block] && el[density_block] && el[gravity_gradient_block],
"GravityForceIntegrator received a null finite element."
);
MFEM_VERIFY(
@@ -81,25 +68,21 @@ namespace mean_field::integrators {
"GravityForceIntegrator received a null element state."
);
const mfem::FiniteElement *velocity_element = el[velocity_block];
const mfem::FiniteElement *density_element = el[density_block];
const mfem::FiniteElement *gravity_gradient_element =
el[gravity_gradient_block];
const mfem::FiniteElement *velocity_element = el[velocity_block];
const mfem::FiniteElement *density_element = el[density_block];
const mfem::FiniteElement *gravity_gradient_element = el[gravity_gradient_block];
const int velocity_dofs_count = velocity_element->GetDof();
const int density_dofs_count = density_element->GetDof();
const int gravity_gradient_dofs_count =
gravity_gradient_element->GetDof();
const int dim = Tr.GetSpaceDim();
const int velocity_dofs_count = velocity_element->GetDof();
const int density_dofs_count = density_element->GetDof();
const int gravity_gradient_dofs_count = gravity_gradient_element->GetDof();
const int dim = Tr.GetSpaceDim();
const mfem::Vector &density_dofs = *elfun[density_block];
const mfem::Vector &gravity_gradient_dofs =
*elfun[gravity_gradient_block];
const mfem::Vector &density_dofs = *elfun[density_block];
const mfem::Vector &gravity_gradient_dofs = *elfun[gravity_gradient_block];
MFEM_VERIFY(
density_dofs.Size() == density_dofs_count,
"GravityForceIntegrator received an incorrectly sized density "
"state."
density_dofs.Size() == density_dofs_count, "GravityForceIntegrator received an incorrectly sized density "
"state."
);
MFEM_VERIFY(
gravity_gradient_dofs.Size() == gravity_gradient_dofs_count,
@@ -123,40 +106,31 @@ namespace mean_field::integrators {
*elvec[density_block] = 0.0;
}
if (elvec.Size() > gravity_gradient_block &&
elvec[gravity_gradient_block]) {
if (elvec.Size() > gravity_gradient_block && elvec[gravity_gradient_block]) {
elvec[gravity_gradient_block]->SetSize(gravity_gradient_dofs_count);
*elvec[gravity_gradient_block] = 0.0;
}
mfem::Vector velocity_shape(velocity_dofs_count);
mfem::Vector density_shape(density_dofs_count);
mfem::DenseMatrix gravity_gradient_shape(
gravity_gradient_dofs_count, dim
);
mfem::DenseMatrix gravity_gradient_shape(gravity_gradient_dofs_count, dim);
mfem::Vector gravity_gradient_element_value(dim);
mfem::Vector gravity_gradient_physical_value(dim);
const mfem::IntegrationRule &integration_rule = *m_integration_rule;
for (int q = 0; q < integration_rule.GetNPoints(); ++q) {
const mfem::IntegrationPoint &integration_point =
integration_rule.IntPoint(q);
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_map.GetQuadratureContext(Tr, integration_point);
velocity_element->CalcShape(integration_point, velocity_shape);
density_element->CalcShape(integration_point, density_shape);
gravity_gradient_element->CalcVShape(Tr, gravity_gradient_shape);
gravity_gradient_shape.MultTranspose(
gravity_gradient_dofs, gravity_gradient_element_value
);
context.J_inv.MultTranspose(
gravity_gradient_element_value, gravity_gradient_physical_value
);
gravity_gradient_shape.MultTranspose(gravity_gradient_dofs, gravity_gradient_element_value);
context.J_inv.MultTranspose(gravity_gradient_element_value, gravity_gradient_physical_value);
double density_value = 0.0;
for (int i = 0; i < density_dofs_count; ++i) {
@@ -166,9 +140,7 @@ namespace mean_field::integrators {
for (int i = 0; i < velocity_dofs_count; ++i) {
for (int component = 0; component < dim; ++component) {
velocity_residual(i + component * velocity_dofs_count) +=
velocity_shape(i) * density_value *
gravity_gradient_physical_value(component) *
context.weight;
velocity_shape(i) * density_value * gravity_gradient_physical_value(component) * context.weight;
}
}
}
@@ -185,23 +157,19 @@ namespace mean_field::integrators {
}
MFEM_VERIFY(
m_integration_rule,
"GravityForceIntegrator must be configured with an "
"integration rule before assembly."
m_integration_rule, "GravityForceIntegrator must be configured with an "
"integration rule before assembly."
);
MFEM_VERIFY(
el.Size() > gravity_gradient_block,
"GravityForceIntegrator requires velocity, density, and "
"gravity-gradient finite elements."
el.Size() > gravity_gradient_block, "GravityForceIntegrator requires velocity, density, and "
"gravity-gradient finite elements."
);
MFEM_VERIFY(
elfun.Size() > gravity_gradient_block,
"GravityForceIntegrator requires velocity, density, and "
"gravity-gradient element states."
elfun.Size() > gravity_gradient_block, "GravityForceIntegrator requires velocity, density, and "
"gravity-gradient element states."
);
MFEM_VERIFY(
el[velocity_block] && el[density_block] &&
el[gravity_gradient_block],
el[velocity_block] && el[density_block] && el[gravity_gradient_block],
"GravityForceIntegrator received a null finite element."
);
MFEM_VERIFY(
@@ -226,25 +194,21 @@ namespace mean_field::integrators {
);
}
const mfem::FiniteElement *velocity_element = el[velocity_block];
const mfem::FiniteElement *density_element = el[density_block];
const mfem::FiniteElement *gravity_gradient_element =
el[gravity_gradient_block];
const mfem::FiniteElement *velocity_element = el[velocity_block];
const mfem::FiniteElement *density_element = el[density_block];
const mfem::FiniteElement *gravity_gradient_element = el[gravity_gradient_block];
const int velocity_dofs_count = velocity_element->GetDof();
const int density_dofs_count = density_element->GetDof();
const int gravity_gradient_dofs_count =
gravity_gradient_element->GetDof();
const int dim = Tr.GetSpaceDim();
const int velocity_dofs_count = velocity_element->GetDof();
const int density_dofs_count = density_element->GetDof();
const int gravity_gradient_dofs_count = gravity_gradient_element->GetDof();
const int dim = Tr.GetSpaceDim();
const mfem::Vector &density_dofs = *elfun[density_block];
const mfem::Vector &gravity_gradient_dofs =
*elfun[gravity_gradient_block];
const mfem::Vector &density_dofs = *elfun[density_block];
const mfem::Vector &gravity_gradient_dofs = *elfun[gravity_gradient_block];
MFEM_VERIFY(
density_dofs.Size() == density_dofs_count,
"GravityForceIntegrator received an incorrectly sized density "
"state."
density_dofs.Size() == density_dofs_count, "GravityForceIntegrator received an incorrectly sized density "
"state."
);
MFEM_VERIFY(
gravity_gradient_dofs.Size() == gravity_gradient_dofs_count,
@@ -253,11 +217,10 @@ namespace mean_field::integrators {
"state."
);
mfem::DenseMatrix *dv_drho = elmats(velocity_block, density_block);
mfem::DenseMatrix *dv_dgrad_phi =
m_jacobian_mode == GravityForceJacobianMode::field_coupled
? elmats(velocity_block, gravity_gradient_block)
: nullptr;
mfem::DenseMatrix *dv_drho = elmats(velocity_block, density_block);
mfem::DenseMatrix *dv_dgrad_phi = m_jacobian_mode == GravityForceJacobianMode::field_coupled
? elmats(velocity_block, gravity_gradient_block)
: nullptr;
if (!dv_drho && !dv_dgrad_phi) {
return;
@@ -265,9 +228,7 @@ namespace mean_field::integrators {
mfem::Vector velocity_shape(velocity_dofs_count);
mfem::Vector density_shape(density_dofs_count);
mfem::DenseMatrix gravity_gradient_shape(
gravity_gradient_dofs_count, dim
);
mfem::DenseMatrix gravity_gradient_shape(gravity_gradient_dofs_count, dim);
mfem::Vector gravity_gradient_element_value(dim);
mfem::Vector gravity_gradient_physical_value(dim);
mfem::Vector gravity_basis_element(dim);
@@ -276,23 +237,17 @@ namespace mean_field::integrators {
const mfem::IntegrationRule &integration_rule = *m_integration_rule;
for (int q = 0; q < integration_rule.GetNPoints(); ++q) {
const mfem::IntegrationPoint &integration_point =
integration_rule.IntPoint(q);
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_map.GetQuadratureContext(Tr, integration_point);
velocity_element->CalcShape(integration_point, velocity_shape);
density_element->CalcShape(integration_point, density_shape);
gravity_gradient_element->CalcVShape(Tr, gravity_gradient_shape);
gravity_gradient_shape.MultTranspose(
gravity_gradient_dofs, gravity_gradient_element_value
);
context.J_inv.MultTranspose(
gravity_gradient_element_value, gravity_gradient_physical_value
);
gravity_gradient_shape.MultTranspose(gravity_gradient_dofs, gravity_gradient_element_value);
context.J_inv.MultTranspose(gravity_gradient_element_value, gravity_gradient_physical_value);
double density_value = 0.0;
for (int i = 0; i < density_dofs_count; ++i) {
@@ -305,10 +260,8 @@ namespace mean_field::integrators {
const int row = i + component * velocity_dofs_count;
for (int j = 0; j < density_dofs_count; ++j) {
(*dv_drho)(row, j) +=
velocity_shape(i) * density_shape(j) *
gravity_gradient_physical_value(component) *
context.weight;
(*dv_drho)(row, j) += velocity_shape(i) * density_shape(j) *
gravity_gradient_physical_value(component) * context.weight;
}
}
}
@@ -317,21 +270,16 @@ namespace mean_field::integrators {
if (dv_dgrad_phi) {
for (int j = 0; j < gravity_gradient_dofs_count; ++j) {
for (int component = 0; component < dim; ++component) {
gravity_basis_element(component) =
gravity_gradient_shape(j, component);
gravity_basis_element(component) = gravity_gradient_shape(j, component);
}
context.J_inv.MultTranspose(
gravity_basis_element, gravity_basis_physical
);
context.J_inv.MultTranspose(gravity_basis_element, gravity_basis_physical);
for (int i = 0; i < velocity_dofs_count; ++i) {
for (int component = 0; component < dim; ++component) {
const int row = i + component * velocity_dofs_count;
(*dv_dgrad_phi)(row, j) +=
velocity_shape(i) * density_value *
gravity_basis_physical(component) *
context.weight;
velocity_shape(i) * density_value * gravity_basis_physical(component) * context.weight;
}
}
}

View File

@@ -4,10 +4,7 @@ module;
module mean_field;
namespace mean_field::integrators {
ContinuityVolumeIntegrator::ContinuityVolumeIntegrator(
const mapping::DomainMapper &map
)
: m_map(map) { };
ContinuityVolumeIntegrator::ContinuityVolumeIntegrator(const mapping::DomainMapper &map) : m_map(map) { };
void ContinuityVolumeIntegrator::AssembleElementVector(
const mfem::Array<const mfem::FiniteElement *> &el,
@@ -29,9 +26,8 @@ namespace mean_field::integrators {
const mfem::Vector v_dofs = *elfun[0];
const mfem::Vector rho_dofs = *elfun[1];
void *data_rho_before =
elvec[1] ? (void *)elvec[1]->GetData() : nullptr;
int size_rho_before = elvec[1] ? elvec[1]->Size() : -1;
void *data_rho_before = elvec[1] ? (void *)elvec[1]->GetData() : nullptr;
int size_rho_before = elvec[1] ? elvec[1]->Size() : -1;
if (elvec[0]) {
elvec[0]->SetSize(dof_v * dim);
@@ -42,11 +38,9 @@ namespace mean_field::integrators {
r_rho = 0.0;
mfem::Vector shape_v(dof_v), shape_rho(dof_rho);
mfem::DenseMatrix dshape_rho_ref(dof_rho, dim),
dshape_rho_phys(dof_rho, dim);
mfem::DenseMatrix dshape_rho_ref(dof_rho, dim), dshape_rho_phys(dof_rho, dim);
const mfem::IntegrationRule *ir =
&mfem::IntRules.Get(fe_v->GetGeomType(), 2 * fe_v->GetOrder());
const mfem::IntegrationRule *ir = &mfem::IntRules.Get(fe_v->GetGeomType(), 2 * fe_v->GetOrder());
for (int q = 0; q < ir->GetNPoints(); ++q) {
const mfem::IntegrationPoint &ip = ir->IntPoint(q);
@@ -113,11 +107,9 @@ namespace mean_field::integrators {
*drho_drho = 0.0;
mfem::Vector shape_v(dof_v), shape_rho(dof_rho);
mfem::DenseMatrix dshape_rho_ref(dof_rho, dim),
dshape_rho_phys(dof_rho, dim);
mfem::DenseMatrix dshape_rho_ref(dof_rho, dim), dshape_rho_phys(dof_rho, dim);
const mfem::IntegrationRule *ir =
&mfem::IntRules.Get(fe_v->GetGeomType(), 2 * fe_v->GetOrder());
const mfem::IntegrationRule *ir = &mfem::IntRules.Get(fe_v->GetGeomType(), 2 * fe_v->GetOrder());
for (int q = 0; q < ir->GetNPoints(); ++q) {
const mfem::IntegrationPoint &ip = ir->IntPoint(q);
@@ -149,8 +141,7 @@ namespace mean_field::integrators {
for (int j = 0; j < dof_v; ++j) {
for (int d = 0; d < dim; ++d) {
const int col = j + d * dof_v;
(*drho_dv)(i, col) -= dshape_rho_phys(i, d) *
rho_val * shape_v(j) * weight;
(*drho_dv)(i, col) -= dshape_rho_phys(i, d) * rho_val * shape_v(j) * weight;
}
}
}
@@ -163,18 +154,14 @@ namespace mean_field::integrators {
grad_psi_dot_v += dshape_rho_phys(i, c) * v_val(c);
}
for (int j = 0; j < dof_rho; ++j) {
(*drho_drho)(i, j) -=
grad_psi_dot_v * shape_rho(j) * weight;
(*drho_drho)(i, j) -= grad_psi_dot_v * shape_rho(j) * weight;
}
}
}
}
}
ContinuityFaceIntegrator::ContinuityFaceIntegrator(
const mapping::DomainMapper &map
)
: m_map(map) {
ContinuityFaceIntegrator::ContinuityFaceIntegrator(const mapping::DomainMapper &map) : m_map(map) {
}
void ContinuityFaceIntegrator::AssembleFaceVector(
@@ -204,10 +191,10 @@ namespace mean_field::integrators {
}
mfem::Vector &r_rho = *elvect[1];
r_rho.SetSize(dof_rho_minus + dof_rho_plus);
r_rho = 0.0;
r_rho = 0.0;
const int attr_minus = Tr.Elem1->Attribute;
const int attr_plus = (Tr.Elem2 != nullptr) ? Tr.Elem2->Attribute : -1;
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) {
@@ -218,29 +205,21 @@ namespace mean_field::integrators {
return; // Boundary face,
}
const mfem::Vector &v_dofs =
*elfun[0]; // Size: dim * dof_v_minus + dim*dof_v_plus
const mfem::Vector &rho_dofs =
*elfun[1]; // Size: dof_rho_minus + dof_rho_plus
const mfem::Vector &v_dofs = *elfun[0]; // Size: dim * dof_v_minus + dim*dof_v_plus
const mfem::Vector &rho_dofs = *elfun[1]; // Size: dof_rho_minus + dof_rho_plus
// Helpers to auto offset to the correct point in the dof array
auto rho_minus_dof = [&](const int i) { return rho_dofs(i); };
auto rho_plus_dof = [&](const int i) {
return rho_dofs(i + dof_rho_minus);
};
auto v_minus_dof = [&](const int k, const int c) {
return v_dofs(k + c * dof_v_minus);
};
auto rho_minus_dof = [&](const int i) { return rho_dofs(i); };
auto rho_plus_dof = [&](const int i) { return rho_dofs(i + dof_rho_minus); };
auto v_minus_dof = [&](const int k, const int c) { return v_dofs(k + c * dof_v_minus); };
const int p_v = fe_v_minus->GetOrder();
const int p_rho = fe_rho_minus->GetOrder();
const int int_order = 2 * std::max(p_v, p_rho) + 1;
const int p_v = fe_v_minus->GetOrder();
const int p_rho = fe_rho_minus->GetOrder();
const int int_order = 2 * std::max(p_v, p_rho) + 1;
const mfem::IntegrationRule *ir =
&mfem::IntRules.Get(Tr.GetGeometryType(), int_order);
const mfem::IntegrationRule *ir = &mfem::IntRules.Get(Tr.GetGeometryType(), int_order);
mfem::Vector shape_v_minus(dof_v_minus), shape_rho_minus(dof_rho_minus),
shape_rho_plus(dof_rho_plus);
mfem::Vector shape_v_minus(dof_v_minus), shape_rho_minus(dof_rho_minus), shape_rho_plus(dof_rho_plus);
for (int q = 0; q < ir->GetNPoints(); ++q) {
const mfem::IntegrationPoint &face_ip = ir->IntPoint(q);
@@ -249,8 +228,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_map.GetFaceQuadratureContext(Tr, face_ip);
fe_v_minus->CalcShape(ip_minus, shape_v_minus);
fe_rho_minus->CalcShape(ip_minus, shape_rho_minus);
@@ -280,7 +258,7 @@ namespace mean_field::integrators {
// Upwind density
// I use the convention that the flow is positive when moving from
// minus to plus
const double rho_up = (u_n >= 0) ? rho_minus_val : rho_plus_val;
const double rho_up = (u_n >= 0) ? rho_minus_val : rho_plus_val;
const double flux_weighted = u_n * rho_up * ds;
@@ -314,11 +292,10 @@ namespace mean_field::integrators {
const int dof_rho_plus = fe_rho_plus->GetDof();
const int dim = Tr.GetSpaceDim();
const int N_v_total = dim * (dof_v_minus + dof_v_plus);
const int N_rho_total = dof_rho_minus + dof_rho_plus;
const int N_v_total = dim * (dof_v_minus + dof_v_plus);
const int N_rho_total = dof_rho_minus + dof_rho_plus;
auto size_and_zero_mat = [&](mfem::DenseMatrix *mat, const int r_size,
const int c_size) {
auto size_and_zero_mat = [&](mfem::DenseMatrix *mat, const int r_size, const int c_size) {
if (mat) {
mat->SetSize(r_size, c_size);
*mat = 0.0;
@@ -339,16 +316,13 @@ namespace mean_field::integrators {
if (!drho_dv && !drho_drho)
return;
const mfem::Vector &v_dofs = *elfun[0];
const mfem::Vector &rho_dofs = *elfun[1];
const mfem::Vector &v_dofs = *elfun[0];
const mfem::Vector &rho_dofs = *elfun[1];
const int int_order =
2 * std::max(fe_v_minus->GetOrder(), fe_rho_minus->GetOrder()) + 1;
const mfem::IntegrationRule *ir =
&mfem::IntRules.Get(Tr.GetGeometryType(), int_order);
const int int_order = 2 * std::max(fe_v_minus->GetOrder(), fe_rho_minus->GetOrder()) + 1;
const mfem::IntegrationRule *ir = &mfem::IntRules.Get(Tr.GetGeometryType(), int_order);
mfem::Vector shape_v_minus(dof_v_minus), shape_rho_minus(dof_rho_minus),
shape_rho_plus(dof_rho_plus);
mfem::Vector shape_v_minus(dof_v_minus), shape_rho_minus(dof_rho_minus), shape_rho_plus(dof_rho_plus);
for (int q = 0; q < ir->GetNPoints(); ++q) {
const mfem::IntegrationPoint &face_ip = ir->IntPoint(q);
@@ -356,15 +330,13 @@ 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_map.GetFaceQuadratureContext(Tr, face_ip);
fe_v_minus->CalcShape(ip_minus, shape_v_minus);
fe_rho_minus->CalcShape(ip_minus, shape_rho_minus);
fe_rho_plus->CalcShape(ip_plus, shape_rho_plus);
const double u_n =
compute_u_n(v_dofs, shape_v_minus, n_unit, dof_v_minus, dim);
const double u_n = compute_u_n(v_dofs, shape_v_minus, n_unit, dof_v_minus, dim);
double rho_minus_val = 0.0;
for (int i = 0; i < dof_rho_minus; ++i) {
@@ -377,7 +349,7 @@ namespace mean_field::integrators {
}
const bool upwind_minus = (u_n >= 0.0);
const double rho_up = upwind_minus ? rho_minus_val : rho_plus_val;
const double rho_up = upwind_minus ? rho_minus_val : rho_plus_val;
// (1, 1)
if (drho_drho) {
@@ -390,8 +362,7 @@ namespace mean_field::integrators {
(*drho_drho)(i, ip) += shape_rho_minus(i) * col_w;
}
for (int j = 0; j < dof_rho_plus; ++j) {
(*drho_drho)(dof_rho_minus + j, ip) -=
shape_rho_plus(j) * col_w;
(*drho_drho)(dof_rho_minus + j, ip) -= shape_rho_plus(j) * col_w;
}
}
} else {
@@ -399,12 +370,10 @@ namespace mean_field::integrators {
const double col_w = u_w * shape_rho_plus(jp);
const int col_idx = dof_rho_minus + jp;
for (int i = 0; i < dof_rho_minus; ++i) {
(*drho_drho)(i, col_idx) +=
shape_rho_minus(i) * col_w;
(*drho_drho)(i, col_idx) += shape_rho_minus(i) * col_w;
}
for (int j = 0; j < dof_rho_plus; ++j) {
(*drho_drho)(dof_rho_minus + j, col_idx) -=
shape_rho_plus(j) * col_w;
(*drho_drho)(dof_rho_minus + j, col_idx) -= shape_rho_plus(j) * col_w;
}
}
}
@@ -418,12 +387,10 @@ namespace mean_field::integrators {
const int col_idx = k + c * dof_v_minus;
const double col_w = n_c_rho_w * shape_v_minus(k);
for (int i = 0; i < dof_rho_minus; ++i) {
(*drho_dv)(i, col_idx) +=
shape_rho_minus(i) * col_w;
(*drho_dv)(i, col_idx) += shape_rho_minus(i) * col_w;
}
for (int j = 0; j < dof_rho_plus; ++j) {
(*drho_dv)(dof_rho_minus + j, col_idx) -=
shape_rho_plus(j) * col_w;
(*drho_dv)(dof_rho_minus + j, col_idx) -= shape_rho_plus(j) * col_w;
}
}
}
@@ -431,12 +398,10 @@ namespace mean_field::integrators {
}
}
bool ContinuityFaceIntegrator::skip_face(
const mfem::FaceElementTransformations &Tr
) {
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;
const int attr_plus = (Tr.Elem2 != nullptr) ? Tr.Elem2->Attribute : -1;
if (attr_minus == VACUUM_ATTR || attr_plus == VACUUM_ATTR) {
return true; // No flux contribution for vacuum faces
}

View File

@@ -49,9 +49,7 @@ namespace mean_field::integrators {
mfem::DenseMatrix dshape_v_ref(dof_v, dim), dshape_v_phys(dof_v, dim);
const mfem::IntegrationRule *ir = &mfem::IntRules.Get(
fe_v->GetGeomType(), 2 * fe_v->GetOrder() + m_quad_boost
);
const mfem::IntegrationRule *ir = &mfem::IntRules.Get(fe_v->GetGeomType(), 2 * fe_v->GetOrder() + m_quad_boost);
for (int q = 0; q < ir->GetNPoints(); ++q) {
@@ -127,8 +125,7 @@ namespace mean_field::integrators {
mfem::DenseMatrix dshape_v_ref(dof_v, dim), dshape_v_phys(dof_v, dim);
const mfem::IntegrationRule *ir =
&mfem::IntRules.Get(fe_v->GetGeomType(), 2 * fe_v->GetOrder());
const mfem::IntegrationRule *ir = &mfem::IntRules.Get(fe_v->GetGeomType(), 2 * fe_v->GetOrder());
for (int q = 0; q < ir->GetNPoints(); ++q) {
const mfem::IntegrationPoint &ip = ir->IntPoint(q);
Tr.SetIntPoint(&ip);
@@ -158,8 +155,7 @@ namespace mean_field::integrators {
val += dshape_v_phys(i, d) * dshape_v_phys(n, c);
val -= (2.0 / 3.0) * dshape_v_phys(i, c) *
dshape_v_phys(n, d);
val -= (2.0 / 3.0) * dshape_v_phys(i, c) * dshape_v_phys(n, d);
(*dv_dv)(row, col) += mu_w * val;
}
}

View File

@@ -168,10 +168,7 @@ namespace mean_field::mapping {
const double map_determinant = map_jacobian.Det();
MFEM_VERIFY(
map_determinant > 0.0,
"Domain mapping has a non-positive Jacobian determinant."
);
MFEM_VERIFY(map_determinant > 0.0, "Domain mapping has a non-positive Jacobian determinant.");
mfem::MultAtB(map_jacobian, map_jacobian, matrix);
matrix *= 1.0 / std::abs(map_determinant);

View File

@@ -27,26 +27,17 @@ namespace {
} // namespace
namespace mean_field::mapping::compactification {
KelvinCompactification::KelvinCompactification(
options::KelvinCompactificationOptions options
)
KelvinCompactification::KelvinCompactification(options::KelvinCompactificationOptions options)
: m_options(options) {
if (!std::isfinite(m_options.r_star_ref) ||
!std::isfinite(m_options.r_inf_ref)) {
throw std::invalid_argument(
"Kelvin compactification radii must be finite."
);
if (!std::isfinite(m_options.r_star_ref) || !std::isfinite(m_options.r_inf_ref)) {
throw std::invalid_argument("Kelvin compactification radii must be finite.");
}
if (m_options.r_star_ref <= 0.0 ||
m_options.r_inf_ref <= m_options.r_star_ref) {
throw std::invalid_argument(
"Kelvin compactification requires 0 < r_star_ref < r_inf_ref."
);
if (m_options.r_star_ref <= 0.0 || m_options.r_inf_ref <= m_options.r_star_ref) {
throw std::invalid_argument("Kelvin compactification requires 0 < r_star_ref < r_inf_ref.");
}
if (!std::isfinite(m_options.coordinate_tolerance) ||
m_options.coordinate_tolerance < 0.0 ||
if (!std::isfinite(m_options.coordinate_tolerance) || m_options.coordinate_tolerance < 0.0 ||
m_options.coordinate_tolerance >= 1.0) {
throw std::invalid_argument(
"Kelvin compactification coordinate tolerance must be finite "
@@ -65,8 +56,7 @@ namespace mean_field::mapping::compactification {
const double tolerance = m_options.coordinate_tolerance;
if (compactification_coordinate < -tolerance ||
compactification_coordinate > 1.0 + tolerance) {
if (compactification_coordinate < -tolerance || compactification_coordinate > 1.0 + tolerance) {
return MappingStatus::outside_reference_domain;
}
@@ -78,26 +68,22 @@ namespace mean_field::mapping::compactification {
return MappingStatus::at_compactified_infinity;
}
const double radial_extent = m_options.r_inf_ref - m_options.r_star_ref;
const double computational_radius =
m_options.r_star_ref + coordinate * radial_extent;
const double radial_extent = m_options.r_inf_ref - m_options.r_star_ref;
const double computational_radius = m_options.r_star_ref + coordinate * radial_extent;
if (!std::isfinite(computational_radius) ||
computational_radius <= 0.0) {
if (!std::isfinite(computational_radius) || computational_radius <= 0.0) {
return MappingStatus::invalid_reference_radius;
}
const double one_minus_coordinate = 1.0 - coordinate;
const double denominator = computational_radius * one_minus_coordinate;
const double denominator = computational_radius * one_minus_coordinate;
if (!std::isfinite(denominator) || denominator <= 0.0) {
return MappingStatus::non_finite_result;
}
const double scale = m_options.r_star_ref / denominator;
const double scale_derivative =
scale *
(1.0 / one_minus_coordinate - radial_extent / computational_radius);
const double scale = m_options.r_star_ref / denominator;
const double scale_derivative = scale * (1.0 / one_minus_coordinate - radial_extent / computational_radius);
if (!std::isfinite(scale) || !std::isfinite(scale_derivative)) {
return MappingStatus::non_finite_result;
@@ -122,21 +108,18 @@ namespace mean_field::mapping::compactification {
return MappingStatus::invalid_dimension;
}
if (input.displacement_jacobian.Height() != dimension ||
input.displacement_jacobian.Width() != dimension) {
if (input.displacement_jacobian.Height() != dimension || input.displacement_jacobian.Width() != dimension) {
return MappingStatus::invalid_dimension;
}
if (!vector_is_finite(input.reference_position) ||
!vector_is_finite(input.displaced_position) ||
if (!vector_is_finite(input.reference_position) || !vector_is_finite(input.displaced_position) ||
!vector_is_finite(input.compactification_coordinate_gradient) ||
!matrix_is_finite(input.displacement_jacobian)) {
return MappingStatus::non_finite_input;
}
RadialFactors factors;
const MappingStatus factor_status =
ComputeRadialFactors(input.compactification_coordinate, factors);
const MappingStatus factor_status = ComputeRadialFactors(input.compactification_coordinate, factors);
if (factor_status != MappingStatus::valid)
return factor_status;
@@ -144,21 +127,16 @@ namespace mean_field::mapping::compactification {
result.mapping_jacobian.SetSize(dimension, dimension);
for (int i = 0; i < dimension; ++i) {
result.physical_position(i) =
factors.scale * input.displaced_position(i);
result.physical_position(i) = factors.scale * input.displaced_position(i);
for (int j = 0; j < dimension; ++j) {
const double scale_gradient =
factors.scale_derivative *
input.compactification_coordinate_gradient(j);
const double scale_gradient = factors.scale_derivative * input.compactification_coordinate_gradient(j);
result.mapping_jacobian(i, j) =
factors.scale * input.displacement_jacobian(i, j) +
input.displaced_position(i) * scale_gradient;
factors.scale * input.displacement_jacobian(i, j) + input.displaced_position(i) * scale_gradient;
}
}
if (!vector_is_finite(result.physical_position) ||
!matrix_is_finite(result.mapping_jacobian)) {
if (!vector_is_finite(result.physical_position) || !matrix_is_finite(result.mapping_jacobian)) {
return MappingStatus::non_finite_result;
}
@@ -185,13 +163,11 @@ namespace mean_field::mapping::compactification {
return MappingStatus::invalid_dimension;
}
if (input.displacement_jacobian.Height() != dimension ||
input.displacement_jacobian.Width() != dimension) {
if (input.displacement_jacobian.Height() != dimension || input.displacement_jacobian.Width() != dimension) {
return MappingStatus::invalid_dimension;
}
if (result.physical_position.Size() != dimension ||
result.mapping_jacobian.Height() != dimension ||
if (result.physical_position.Size() != dimension || result.mapping_jacobian.Height() != dimension ||
result.mapping_jacobian.Width() != dimension) {
return MappingStatus::invalid_dimension;
}
@@ -202,23 +178,20 @@ namespace mean_field::mapping::compactification {
return MappingStatus::invalid_dimension;
}
if (!vector_is_finite(input.reference_position) ||
!vector_is_finite(input.displaced_position) ||
if (!vector_is_finite(input.reference_position) || !vector_is_finite(input.displaced_position) ||
!vector_is_finite(input.compactification_coordinate_gradient) ||
!matrix_is_finite(input.displacement_jacobian)) {
return MappingStatus::non_finite_input;
}
if (!vector_is_finite(result.physical_position) ||
!matrix_is_finite(result.mapping_jacobian) ||
if (!vector_is_finite(result.physical_position) || !matrix_is_finite(result.mapping_jacobian) ||
!vector_is_finite(direction.displaced_position_variation) ||
!matrix_is_finite(direction.displacement_jacobian_variation)) {
return MappingStatus::non_finite_input;
}
RadialFactors factors;
const MappingStatus factor_status =
ComputeRadialFactors(input.compactification_coordinate, factors);
const MappingStatus factor_status = ComputeRadialFactors(input.compactification_coordinate, factors);
if (factor_status != MappingStatus::valid)
return factor_status;
@@ -226,16 +199,12 @@ namespace mean_field::mapping::compactification {
variation.mapping_jacobian_variation.SetSize(dimension, dimension);
for (int i = 0; i < dimension; ++i) {
variation.physical_position_variation(i) =
factors.scale * direction.displaced_position_variation(i);
variation.physical_position_variation(i) = factors.scale * direction.displaced_position_variation(i);
for (int j = 0; j < dimension; ++j) {
const double scale_gradient =
factors.scale_derivative *
input.compactification_coordinate_gradient(j);
const double scale_gradient = factors.scale_derivative * input.compactification_coordinate_gradient(j);
variation.mapping_jacobian_variation(i, j) =
factors.scale *
direction.displacement_jacobian_variation(i, j) +
factors.scale * direction.displacement_jacobian_variation(i, j) +
direction.displaced_position_variation(i) * scale_gradient;
}
}

View File

@@ -15,10 +15,7 @@ namespace {
domain_mapper.ComputeJacobian(transformation, map_jacobian);
const double map_determinant = map_jacobian.Det();
MFEM_VERIFY(
map_determinant > 0.0,
"Domain mapping has a non-positive Jacobian determinant."
);
MFEM_VERIFY(map_determinant > 0.0, "Domain mapping has a non-positive Jacobian determinant.");
return map_determinant;
}
} // namespace
@@ -32,8 +29,7 @@ namespace mean_field::mapping {
m_r_star_ref(r_star_ref),
m_r_inf_ref(r_inf_ref) {
InitAllScratchSpaces();
CalcIsIdentity() ? m_displacement_is_identity = true
: m_displacement_is_identity = false;
CalcIsIdentity() ? m_displacement_is_identity = true : m_displacement_is_identity = false;
}
DomainMapper::DomainMapper(
@@ -46,8 +42,7 @@ namespace mean_field::mapping {
m_r_star_ref(r_star_ref),
m_r_inf_ref(r_inf_ref) {
InitAllScratchSpaces();
CalcIsIdentity() ? m_displacement_is_identity = true
: m_displacement_is_identity = false;
CalcIsIdentity() ? m_displacement_is_identity = true : m_displacement_is_identity = false;
}
bool DomainMapper::is_vacuum(const mfem::ElementTransformation &T) const {
@@ -76,14 +71,12 @@ namespace mean_field::mapping {
m_d = &d;
InvalidateCache();
CalcIsIdentity() ? m_displacement_is_identity = true
: m_displacement_is_identity = false;
CalcIsIdentity() ? m_displacement_is_identity = true : m_displacement_is_identity = false;
}
bool DomainMapper::HasCompactification() const noexcept {
return std::isfinite(m_r_star_ref) && std::isfinite(m_r_inf_ref) &&
m_r_star_ref > 0.0 && m_r_inf_ref > m_r_star_ref &&
m_xi_clamp > 0.0 && m_xi_clamp < 1.0;
return std::isfinite(m_r_star_ref) && std::isfinite(m_r_inf_ref) && m_r_star_ref > 0.0 &&
m_r_inf_ref > m_r_star_ref && m_xi_clamp > 0.0 && m_xi_clamp < 1.0;
}
bool DomainMapper::HasDisplacementField() const noexcept {
@@ -95,10 +88,9 @@ namespace mean_field::mapping {
return true;
}
const int local_identity = m_d->Normlinf() == 0.0 ? 1 : 0;
const int local_identity = m_d->Normlinf() == 0.0 ? 1 : 0;
const auto *parallel_displacement =
dynamic_cast<const mfem::ParGridFunction *>(m_d);
const auto *parallel_displacement = dynamic_cast<const mfem::ParGridFunction *>(m_d);
if (parallel_displacement == nullptr) {
return local_identity == 1;
@@ -106,8 +98,7 @@ namespace mean_field::mapping {
int global_identity = 0;
MPI_Allreduce(
&local_identity, &global_identity, 1, MPI_INT, MPI_MIN,
parallel_displacement->ParFESpace()->GetComm()
&local_identity, &global_identity, 1, MPI_INT, MPI_MIN, parallel_displacement->ParFESpace()->GetComm()
);
return global_identity == 1;
@@ -116,8 +107,7 @@ namespace mean_field::mapping {
void DomainMapper::ResetDisplacement() {
m_d = nullptr;
InvalidateCache();
CalcIsIdentity() ? m_displacement_is_identity = true
: m_displacement_is_identity = false;
CalcIsIdentity() ? m_displacement_is_identity = true : m_displacement_is_identity = false;
}
void DomainMapper::ComputeJacobian(
@@ -223,11 +213,7 @@ namespace mean_field::mapping {
mfem::Vector n_unit(dim);
n_unit = n_raw;
n_unit /= n_raw_mag;
return FaceQuadratureContext{
.normal = n_unit,
.ds = ip.weight * n_raw_mag,
.v_dot_n_scale = 1.0
};
return FaceQuadratureContext{.normal = n_unit, .ds = ip.weight * n_raw_mag, .v_dot_n_scale = 1.0};
}
// Nanson's Formula
@@ -253,9 +239,7 @@ namespace mean_field::mapping {
const double n_raw_mag = n_raw.Norml2();
return FaceQuadratureContext{
.normal = n_unit,
.ds = ip.weight * n_raw_mag,
.v_dot_n_scale = n_phys_mag / n_raw_mag
.normal = n_unit, .ds = ip.weight * n_raw_mag, .v_dot_n_scale = n_phys_mag / n_raw_mag
};
}
@@ -297,15 +281,11 @@ namespace mean_field::mapping {
const mfem::Vector &reference_flux,
mfem::Vector &physical_flux
) const {
MFEM_VERIFY(
reference_flux.Size() == m_dim,
"The reference H(div) flux has the wrong dimension."
);
MFEM_VERIFY(reference_flux.Size() == m_dim, "The reference H(div) flux has the wrong dimension.");
mfem::DenseMatrix map_jacobian(m_dim, m_dim);
const double map_determinant = get_positive_map_jacobian(
*this, transformation, integration_point, map_jacobian
);
const double map_determinant =
get_positive_map_jacobian(*this, transformation, integration_point, map_jacobian);
mfem::Vector mapped_flux(m_dim);
map_jacobian.Mult(reference_flux, mapped_flux);
@@ -320,15 +300,11 @@ namespace mean_field::mapping {
const mfem::Vector &physical_flux,
mfem::Vector &reference_flux
) const {
MFEM_VERIFY(
physical_flux.Size() == m_dim,
"The physical flux has the wrong dimension."
);
MFEM_VERIFY(physical_flux.Size() == m_dim, "The physical flux has the wrong dimension.");
mfem::DenseMatrix map_jacobian(m_dim, m_dim);
const double map_determinant = get_positive_map_jacobian(
*this, transformation, integration_point, map_jacobian
);
const double map_determinant =
get_positive_map_jacobian(*this, transformation, integration_point, map_jacobian);
mfem::DenseMatrix inverse_map_jacobian(m_dim, m_dim);
mfem::CalcInverse(map_jacobian, inverse_map_jacobian);
@@ -346,15 +322,10 @@ namespace mean_field::mapping {
const mfem::Vector &reference_gradient,
mfem::Vector &physical_gradient
) const {
MFEM_VERIFY(
reference_gradient.Size() == m_dim,
"The reference gradient has the wrong dimension."
);
MFEM_VERIFY(reference_gradient.Size() == m_dim, "The reference gradient has the wrong dimension.");
mfem::DenseMatrix map_jacobian(m_dim, m_dim);
get_positive_map_jacobian(
*this, transformation, integration_point, map_jacobian
);
get_positive_map_jacobian(*this, transformation, integration_point, map_jacobian);
mfem::DenseMatrix inverse_map_jacobian(m_dim, m_dim);
mfem::CalcInverse(map_jacobian, inverse_map_jacobian);
@@ -382,8 +353,7 @@ namespace mean_field::mapping {
}
double DomainMapper::GetCacheHitRate() const {
return (static_cast<double>(m_cache_hits)) /
static_cast<double>(m_cache_misses + m_cache_hits);
return (static_cast<double>(m_cache_hits)) / static_cast<double>(m_cache_misses + m_cache_hits);
}
void DomainMapper::ResetCacheStats() const {
@@ -404,9 +374,9 @@ namespace mean_field::mapping {
const mfem::Vector &x_ref,
mfem::Vector &x_phys
) const {
const double r_ref = x_ref.Norml2();
double xi = (r_ref - m_r_star_ref) / (m_r_inf_ref - m_r_star_ref);
xi = std::clamp(xi, 0.0, m_xi_clamp);
const double r_ref = x_ref.Norml2();
double xi = (r_ref - m_r_star_ref) / (m_r_inf_ref - m_r_star_ref);
xi = std::clamp(xi, 0.0, m_xi_clamp);
const double factor = m_r_star_ref / (r_ref * (1 - xi));
x_phys *= factor;
}
@@ -428,8 +398,7 @@ namespace mean_field::mapping {
const double k = m_r_star_ref / (r_ref * denom);
const double dk_dr =
m_r_star_ref * ((1.0 / (delta_R * r_ref * denom * denom)) -
(1.0 / (r_ref * r_ref * denom)));
m_r_star_ref * ((1.0 / (delta_R * r_ref * denom * denom)) - (1.0 / (r_ref * r_ref * denom)));
J.SetSize(m_dim, m_dim);
const double outer_factor = dk_dr / r_ref;
@@ -445,14 +414,11 @@ namespace mean_field::mapping {
m_cached_elem_id = -1;
}
void DomainMapper::UpdateElementCache(
const mfem::ElementTransformation &T
) const {
void DomainMapper::UpdateElementCache(const mfem::ElementTransformation &T) const {
if (!HasDisplacementField())
return;
if (T.ElementNo != m_cached_elem_id ||
T.ElementType != m_cached_elem_type) {
if (T.ElementNo != m_cached_elem_id || T.ElementType != m_cached_elem_type) {
m_cache_misses++;
m_cached_elem_id = T.ElementNo;
m_cached_elem_type = T.ElementType;

View File

@@ -39,9 +39,7 @@ namespace mean_field::mapping {
: m_element(&element),
m_dofs(dofs) {
if (element.GetRangeType() != mfem::FiniteElement::SCALAR) {
throw std::invalid_argument(
"Compactification coordinate requires a scalar finite element."
);
throw std::invalid_argument("Compactification coordinate requires a scalar finite element.");
}
if (element.GetMapType() != mfem::FiniteElement::VALUE) {
@@ -75,8 +73,7 @@ namespace mean_field::mapping {
}
}
const mfem::FiniteElement &
ElementCompactificationData::GetElement() const noexcept {
const mfem::FiniteElement &ElementCompactificationData::GetElement() const noexcept {
return *m_element;
}
@@ -102,8 +99,7 @@ namespace mean_field::mapping {
"The displacement element must have at least one degree of "
"freedom."
);
if (displacement_dofs.Size() <= 0 ||
displacement_dofs.Size() % dof_count != 0) {
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 "
@@ -117,31 +113,25 @@ namespace mean_field::mapping {
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);
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);
m_dof_matrix(i, component) = displacement_dofs(component + i * m_dimension);
}
}
} else {
throw std::invalid_argument(
"Unsupported MFEM displacement ordering."
);
throw std::invalid_argument("Unsupported MFEM displacement ordering.");
}
}
const mfem::FiniteElement &
ElementDisplacementData::GetElement() const noexcept {
const mfem::FiniteElement &ElementDisplacementData::GetElement() const noexcept {
return *m_element;
}
const mfem::DenseMatrix &
ElementDisplacementData::GetDofMatrix() const noexcept {
const mfem::DenseMatrix &ElementDisplacementData::GetDofMatrix() const noexcept {
return m_dof_matrix;
}
@@ -161,9 +151,7 @@ namespace mean_field::mapping {
const mfem::FiniteElement &element,
const mfem::Vector &displacement_dofs
) {
return ElementDisplacementData(
element, displacement_dofs, mfem::Ordering::byNODES
);
return ElementDisplacementData(element, displacement_dofs, mfem::Ordering::byNODES);
}
DomainMapperStateless::Workspace::Workspace(const int dimension) {
@@ -172,9 +160,7 @@ namespace mean_field::mapping {
void DomainMapperStateless::Workspace::SetDimension(const int dimension) {
if (dimension <= 0) {
throw std::invalid_argument(
"Domain mapping workspace dimension must be positive."
);
throw std::invalid_argument("Domain mapping workspace dimension must be positive.");
}
m_dimension = dimension;
@@ -197,9 +183,7 @@ namespace mean_field::mapping {
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
);
m_exterior_variation.mapping_jacobian_variation.SetSize(dimension, dimension);
}
int DomainMapperStateless::Workspace::GetDimension() const noexcept {
@@ -213,22 +197,15 @@ namespace mean_field::mapping {
: 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."
);
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."
);
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."
);
throw std::invalid_argument("DomainMapperStateless requires an exterior-domain mapping.");
}
bool DomainMapperStateless::IsCompactifiedElement(
const mfem::ElementTransformation &transformation
) const noexcept {
bool
DomainMapperStateless::IsCompactifiedElement(const mfem::ElementTransformation &transformation) const noexcept {
return transformation.Attribute == m_options.vacuum_element_attribute;
}
@@ -240,17 +217,13 @@ namespace mean_field::mapping {
return m_options.vacuum_element_attribute;
}
const compactification::ExteriorDomainMap &
DomainMapperStateless::GetExteriorMap() const noexcept {
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;
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(
@@ -275,8 +248,7 @@ namespace mean_field::mapping {
);
}
if (displacement.GetElement().GetGeomType() !=
compactification.GetElement().GetGeomType()) {
if (displacement.GetElement().GetGeomType() != compactification.GetElement().GetGeomType()) {
throw std::invalid_argument(
"Displacement and compactification finite elements have "
"different "
@@ -284,31 +256,25 @@ namespace mean_field::mapping {
);
}
if (compactification.GetElement().GetRangeType() !=
mfem::FiniteElement::SCALAR) {
throw std::invalid_argument(
"Compactification coordinate requires a scalar finite element."
);
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) {
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) {
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()) {
if (compactification.GetDofCount() != compactification.GetElement().GetDof()) {
throw std::invalid_argument(
"Compactification coordinate DOF count does not match its "
"finite "
@@ -328,8 +294,7 @@ namespace mean_field::mapping {
const mfem::Vector &dofs = compactification.GetDofs();
const int dof_count = element.GetDof();
if (workspace.GetDimension() != m_options.dimension ||
transformation.GetSpaceDim() != m_options.dimension ||
if (workspace.GetDimension() != m_options.dimension || transformation.GetSpaceDim() != m_options.dimension ||
element.GetDim() != m_options.dimension) {
return MappingStatus::invalid_dimension;
}
@@ -346,22 +311,14 @@ namespace mean_field::mapping {
transformation.SetIntPoint(&integration_point);
workspace.m_compactification_shape.SetSize(dof_count);
workspace.m_compactification_dshape.SetSize(
dof_count, m_options.dimension
);
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
);
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
);
workspace.m_compactification_dshape.MultTranspose(dofs, point_data.coordinate_gradient);
if (!std::isfinite(point_data.coordinate)) {
return MappingStatus::non_finite_result;
@@ -411,15 +368,10 @@ namespace mean_field::mapping {
ValidateElementData(element_data);
if (workspace.GetDimension() != m_options.dimension)
throw std::invalid_argument(
"The mapping workspace has the wrong 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 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 "
@@ -432,12 +384,11 @@ namespace mean_field::mapping {
transformation.Transform(integration_point, context.reference_position);
EvaluateField(
element_data.displacement, transformation, integration_point,
workspace, workspace.m_field_value, workspace.m_field_jacobian
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) ||
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;
}
@@ -446,9 +397,7 @@ namespace mean_field::mapping {
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.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;
@@ -456,43 +405,34 @@ namespace mean_field::mapping {
context.compactified = IsCompactifiedElement(transformation);
if (context.compactified) {
const MappingStatus coordinate_status =
EvaluateCompactificationCoordinate(
element_data.compactification, transformation,
integration_point, workspace,
workspace.m_compactification_point
);
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
.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
);
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;
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))
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();
@@ -501,12 +441,8 @@ namespace mean_field::mapping {
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
);
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;
@@ -521,33 +457,22 @@ namespace mean_field::mapping {
Workspace &workspace,
VolumeMappingContext &context
) const {
const MappingStatus point_status = EvaluatePoint(
element_data, transformation, integration_point, workspace,
context.mapping
);
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
);
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.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;
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))
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;
@@ -555,33 +480,24 @@ namespace mean_field::mapping {
return MappingStatus::valid;
}
mfem::ElementTransformation &
DomainMapperStateless::SelectFaceElementTransformation(
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."
);
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."
);
MFEM_VERIFY(transformation.Elem2 != nullptr, "The face does not have an element-2 transformation.");
return *transformation.Elem2;
}
const mfem::IntegrationPoint &
DomainMapperStateless::SelectFaceElementIntegrationPoint(
const mfem::IntegrationPoint &DomainMapperStateless::SelectFaceElementIntegrationPoint(
mfem::FaceElementTransformations &transformation,
const FaceElementSide side
) {
mfem::ElementTransformation &element_transformation =
SelectFaceElementTransformation(transformation, side);
mfem::ElementTransformation &element_transformation = SelectFaceElementTransformation(transformation, side);
return element_transformation.GetIntPoint();
}
@@ -594,63 +510,47 @@ namespace mean_field::mapping {
FaceMappingContext &context
) const {
transformation.SetAllIntPoints(&integration_point);
mfem::ElementTransformation &element_transformation =
SelectFaceElementTransformation(transformation, side);
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
);
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
);
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)
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
);
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)
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.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;
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)) {
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;
}
@@ -668,8 +568,7 @@ namespace mean_field::mapping {
) const {
ValidateElementData(element_data);
const ElementMappingData direction_data{
.displacement = direction,
.compactification = element_data.compactification
.displacement = direction, .compactification = element_data.compactification
};
ValidateElementData(direction_data);
@@ -679,9 +578,7 @@ namespace mean_field::mapping {
"degree-of-freedom counts."
);
if (workspace.GetDimension() != m_options.dimension)
throw std::invalid_argument(
"The mapping workspace has the wrong 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 "
@@ -689,86 +586,66 @@ namespace mean_field::mapping {
);
EvaluateField(
direction, transformation, integration_point, workspace,
workspace.m_field_value, workspace.m_field_jacobian
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))
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
);
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
.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;
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
.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
);
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;
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;
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
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.mapping_determinant_variation = base_context.mapping_determinant * trace;
variation.inverse_mapping_jacobian_variation.SetSize(
m_options.dimension, m_options.dimension
);
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
@@ -795,34 +672,26 @@ namespace mean_field::mapping {
VolumeMappingVariation &variation
) const {
const MappingStatus point_status = EvaluatePointVariation(
element_data, direction, transformation, integration_point,
base_context.mapping, workspace, variation.mapping
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.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
);
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
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;
integration_point.weight * transformation.Weight() * variation.mapping.mapping_determinant_variation;
if (!matrix_is_finite(variation.inverse_element_jacobian_variation) ||
!std::isfinite(variation.weight_variation))
@@ -842,30 +711,24 @@ namespace mean_field::mapping {
FaceMappingVariation &variation
) const {
transformation.SetAllIntPoints(&integration_point);
mfem::ElementTransformation &element_transformation =
SelectFaceElementTransformation(transformation, side);
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,
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
);
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)
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(
@@ -878,32 +741,23 @@ namespace mean_field::mapping {
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 *= base_context.mapping.mapping_determinant;
variation.physical_normal_variation.Add(
variation.mapping.mapping_determinant_variation,
workspace.m_vector_temp
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)
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;
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.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;
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) ||

View File

@@ -41,15 +41,12 @@ namespace mean_field::mapping {
mfem::Vector &physical_gradient
) {
MFEM_VERIFY(
reference_gradient.Size() ==
context.inverse_mapping_jacobian.Height(),
reference_gradient.Size() == context.inverse_mapping_jacobian.Height(),
"The reference scalar gradient has the wrong dimension."
);
physical_gradient.SetSize(reference_gradient.Size());
context.inverse_mapping_jacobian.MultTranspose(
reference_gradient, physical_gradient
);
context.inverse_mapping_jacobian.MultTranspose(reference_gradient, physical_gradient);
}
void MapPhysicalGradientToReference(
@@ -63,9 +60,7 @@ namespace mean_field::mapping {
);
reference_gradient.SetSize(physical_gradient.Size());
context.mapping_jacobian.MultTranspose(
physical_gradient, reference_gradient
);
context.mapping_jacobian.MultTranspose(physical_gradient, reference_gradient);
}
void MapReferenceVectorGradientToPhysical(
@@ -74,19 +69,12 @@ namespace mean_field::mapping {
mfem::DenseMatrix &physical_gradient
) {
MFEM_VERIFY(
reference_gradient.Width() ==
context.inverse_mapping_jacobian.Height(),
reference_gradient.Width() == context.inverse_mapping_jacobian.Height(),
"The reference vector gradient has the wrong dimension."
);
physical_gradient.SetSize(
reference_gradient.Height(),
context.inverse_mapping_jacobian.Width()
);
mfem::Mult(
reference_gradient, context.inverse_mapping_jacobian,
physical_gradient
);
physical_gradient.SetSize(reference_gradient.Height(), context.inverse_mapping_jacobian.Width());
mfem::Mult(reference_gradient, context.inverse_mapping_jacobian, physical_gradient);
}
void MapPhysicalVectorGradientToReference(
@@ -99,12 +87,8 @@ namespace mean_field::mapping {
"The physical vector gradient has the wrong dimension."
);
reference_gradient.SetSize(
physical_gradient.Height(), context.mapping_jacobian.Width()
);
mfem::Mult(
physical_gradient, context.mapping_jacobian, reference_gradient
);
reference_gradient.SetSize(physical_gradient.Height(), context.mapping_jacobian.Width());
mfem::Mult(physical_gradient, context.mapping_jacobian, reference_gradient);
}
double MapHDivDivergenceToPhysical(
@@ -120,19 +104,11 @@ namespace mean_field::mapping {
) {
const int dimension = context.mapping_jacobian.Height();
MFEM_VERIFY(
context.mapping_jacobian.Width() == dimension,
"The mapping Jacobian must be square."
);
MFEM_VERIFY(
context.mapping_determinant > 0.0,
"The mapping determinant must be positive."
);
MFEM_VERIFY(context.mapping_jacobian.Width() == dimension, "The mapping Jacobian must be square.");
MFEM_VERIFY(context.mapping_determinant > 0.0, "The mapping determinant must be positive.");
mass_tensor.SetSize(dimension, dimension);
mfem::MultAtB(
context.mapping_jacobian, context.mapping_jacobian, mass_tensor
);
mfem::MultAtB(context.mapping_jacobian, context.mapping_jacobian, mass_tensor);
mass_tensor *= 1 / context.mapping_determinant;
}
@@ -143,19 +119,12 @@ namespace mean_field::mapping {
const int dimension = context.inverse_mapping_jacobian.Height();
MFEM_VERIFY(
context.inverse_mapping_jacobian.Width() == dimension,
"The inverse mapping Jacobian must be square."
);
MFEM_VERIFY(
context.mapping_determinant > 0.0,
"The mapping determinant must be positive."
context.inverse_mapping_jacobian.Width() == dimension, "The inverse mapping Jacobian must be square."
);
MFEM_VERIFY(context.mapping_determinant > 0.0, "The mapping determinant must be positive.");
diffusion_tensor.SetSize(dimension, dimension);
mfem::MultABt(
context.inverse_mapping_jacobian, context.inverse_mapping_jacobian,
diffusion_tensor
);
mfem::MultABt(context.inverse_mapping_jacobian, context.inverse_mapping_jacobian, diffusion_tensor);
diffusion_tensor *= context.mapping_determinant;
}
@@ -170,9 +139,7 @@ namespace mean_field::mapping {
);
physical_field.SetSize(reference_field.Size());
context.inverse_mapping_jacobian.MultTranspose(
reference_field, physical_field
);
context.inverse_mapping_jacobian.MultTranspose(reference_field, physical_field);
}
void MapPhysicalFieldToHCurlReference(
@@ -239,35 +206,24 @@ namespace mean_field::mapping {
const MappingPointVariation &variation,
mfem::DenseMatrix &mass_tensor_variation
) {
const double determinant = context.mapping_determinant;
const double determinant_variation =
variation.mapping_determinant_variation;
const int dimension = context.inverse_mapping_jacobian.Width();
const double determinant = context.mapping_determinant;
const double determinant_variation = variation.mapping_determinant_variation;
const int dimension = context.inverse_mapping_jacobian.Width();
mass_tensor_variation.SetSize(dimension, dimension);
MFEM_VERIFY(
std::isfinite(determinant) && determinant > 0.0,
"The mapping determinant must be positive and finite."
);
MFEM_VERIFY(
std::isfinite(determinant_variation),
"The mapping determinant variation must be finite."
std::isfinite(determinant) && determinant > 0.0, "The mapping determinant must be positive and finite."
);
MFEM_VERIFY(std::isfinite(determinant_variation), "The mapping determinant variation must be finite.");
mfem::DenseMatrix determinant_correction(dimension, dimension);
ComputeHDivMassTensor(context, determinant_correction);
determinant_correction *= determinant_variation / determinant;
mfem::DenseMatrix right_jacobian_variation(dimension, dimension);
mfem::MultAtB(
context.mapping_jacobian, variation.mapping_jacobian_variation,
right_jacobian_variation
);
mfem::MultAtB(
variation.mapping_jacobian_variation, context.mapping_jacobian,
mass_tensor_variation
);
mfem::MultAtB(context.mapping_jacobian, variation.mapping_jacobian_variation, right_jacobian_variation);
mfem::MultAtB(variation.mapping_jacobian_variation, context.mapping_jacobian, mass_tensor_variation);
mass_tensor_variation += right_jacobian_variation;
mass_tensor_variation *= 1 / determinant;

View File

@@ -0,0 +1,260 @@
module;
#include <cmath>
#include <format>
#include <numbers>
module mean_field;
import :model.structure.polytropic;
namespace mean_field::models::structure {
PolytropicStructure::PolytropicStructure(
eos::Polytrope equationOfState,
const double targetMass
)
: m_equationOfState(std::move(equationOfState)),
m_targetMass(targetMass) {
validate();
}
const eos::EquationOfState &PolytropicStructure::equationOfState() const noexcept {
return m_equationOfState;
}
double PolytropicStructure::targetMass() const noexcept {
return m_targetMass;
}
StructureSeed PolytropicStructure::makeInitialSeed(const StructureSeedRequest &request) const {
validateSeedRequest(request);
const double polytropicIndex = m_equationOfState.polytropic_index();
const std::vector<LaneEmdenPoint> laneEmdenSolution = solveLaneEmden(polytropicIndex);
const double surfaceCoordinate = laneEmdenSolution.back().coordinate;
const double centralEnthalpy = m_equationOfState.enthalpy_from_density(request.centralDensity);
const double radialScaleSquared =
centralEnthalpy / (4.0 * std::numbers::pi_v<double> * mean_field::utils::G * request.centralDensity);
if (!std::isfinite(radialScaleSquared) || radialScaleSquared <= 0.0) {
throw std::runtime_error(
"The polytropic Lane-Emden radial scale is not "
"finite and positive."
);
}
const double radialScale = std::sqrt(radialScaleSquared);
StructureSeed seed;
seed.radius.SetSize(request.radialSampleCount);
seed.density.SetSize(request.radialSampleCount);
seed.enthalpy.SetSize(request.radialSampleCount);
seed.stellarRadius = radialScale * surfaceCoordinate;
seed.centralDensity = request.centralDensity;
seed.centralEnthalpy = centralEnthalpy;
std::size_t interpolationIndex = 0;
for (int sampleIndex = 0; sampleIndex < request.radialSampleCount; ++sampleIndex) {
const double sampleFraction =
static_cast<double>(sampleIndex) / static_cast<double>(request.radialSampleCount - 1);
const double dimensionlessRadius = sampleFraction * surfaceCoordinate;
const double laneEmdenValue =
interpolateLaneEmdenValue(laneEmdenSolution, dimensionlessRadius, interpolationIndex);
const double density = request.centralDensity * std::pow(laneEmdenValue, polytropicIndex);
seed.radius(sampleIndex) = radialScale * dimensionlessRadius;
seed.density(sampleIndex) = density;
seed.enthalpy(sampleIndex) = m_equationOfState.enthalpy_from_density(density);
}
seed.radius(0) = 0.0;
seed.density(0) = request.centralDensity;
seed.enthalpy(0) = centralEnthalpy;
const int surfaceIndex = request.radialSampleCount - 1;
seed.radius(surfaceIndex) = seed.stellarRadius;
seed.density(surfaceIndex) = 0.0;
seed.enthalpy(surfaceIndex) = 0.0;
return seed;
}
void PolytropicStructure::validate() const {
const double polytropicIndex = m_equationOfState.polytropic_index();
if (!std::isfinite(polytropicIndex) || polytropicIndex < 1.0 || polytropicIndex >= 5.0) {
throw std::invalid_argument(
std::format(
"PolytropicStructure requires a finite-radius "
"polytrope with 1 <= n < 5. Instead n = {} was "
"provided.",
polytropicIndex
)
);
}
if (!std::isfinite(m_targetMass) || m_targetMass <= 0.0) {
throw std::invalid_argument(
std::format(
"The target stellar mass must be finite and "
"positive. Instead a value of {} was provided.",
m_targetMass
)
);
}
}
void PolytropicStructure::validateSeedRequest(const StructureSeedRequest &request) {
if (!std::isfinite(request.centralDensity) || request.centralDensity <= 0.0) {
throw std::invalid_argument(
std::format(
"The seed central density must be finite and "
"positive. Instead a value of {} was provided.",
request.centralDensity
)
);
}
if (request.radialSampleCount < 2) {
throw std::invalid_argument(
std::format(
"A polytropic seed requires at least two radial "
"samples. Instead {} samples were requested.",
request.radialSampleCount
)
);
}
}
PolytropicStructure::LaneEmdenDerivative PolytropicStructure::evaluateLaneEmdenRhs(
const double coordinate,
const double value,
const double derivative,
const double polytropicIndex
) {
const double nonnegativeValue = std::max(value, 0.0);
return {
.value = derivative,
.derivative = -2.0 * derivative / coordinate - std::pow(nonnegativeValue, polytropicIndex)
};
}
PolytropicStructure::LaneEmdenPoint PolytropicStructure::takeLaneEmdenStep(
const LaneEmdenPoint &point,
const double step,
const double polytropicIndex
) {
const LaneEmdenDerivative first =
evaluateLaneEmdenRhs(point.coordinate, point.value, point.derivative, polytropicIndex);
const LaneEmdenDerivative second = evaluateLaneEmdenRhs(
point.coordinate + 0.5 * step, point.value + 0.5 * step * first.value,
point.derivative + 0.5 * step * first.derivative, polytropicIndex
);
const LaneEmdenDerivative third = evaluateLaneEmdenRhs(
point.coordinate + 0.5 * step, point.value + 0.5 * step * second.value,
point.derivative + 0.5 * step * second.derivative, polytropicIndex
);
const LaneEmdenDerivative fourth = evaluateLaneEmdenRhs(
point.coordinate + step, point.value + step * third.value, point.derivative + step * third.derivative,
polytropicIndex
);
return {
.coordinate = point.coordinate + step,
.value = point.value + step / 6.0 * (first.value + 2.0 * second.value + 2.0 * third.value + fourth.value),
.derivative =
point.derivative +
step / 6.0 * (first.derivative + 2.0 * second.derivative + 2.0 * third.derivative + fourth.derivative)
};
}
std::vector<PolytropicStructure::LaneEmdenPoint> PolytropicStructure::solveLaneEmden(const double polytropicIndex) {
constexpr double initialCoordinate = 1.0e-6;
constexpr double integrationStep = 1.0e-3;
constexpr int maximumStepCount = 2'000'000;
const double coordinateSquared = initialCoordinate * initialCoordinate;
const double coordinateCubed = coordinateSquared * initialCoordinate;
const double coordinateFourth = coordinateSquared * coordinateSquared;
LaneEmdenPoint point{
.coordinate = initialCoordinate,
.value = 1.0 - coordinateSquared / 6.0 + polytropicIndex * coordinateFourth / 120.0,
.derivative = -initialCoordinate / 3.0 + polytropicIndex * coordinateCubed / 30.0
};
std::vector<LaneEmdenPoint> solution;
solution.reserve(8192);
solution.push_back({.coordinate = 0.0, .value = 1.0, .derivative = 0.0});
solution.push_back(point);
for (int stepIndex = 0; stepIndex < maximumStepCount; ++stepIndex) {
LaneEmdenPoint nextPoint = takeLaneEmdenStep(point, integrationStep, polytropicIndex);
if (!std::isfinite(nextPoint.value)) {
throw std::runtime_error(
"The Lane-Emden integration produced a non-finite "
"solution before reaching the stellar surface."
);
}
if (nextPoint.value <= 0.0) {
const double rootFraction = point.value / (point.value - nextPoint.value);
solution.push_back(
{.coordinate = point.coordinate + rootFraction * (nextPoint.coordinate - point.coordinate),
.value = 0.0,
.derivative = point.derivative + rootFraction * (nextPoint.derivative - point.derivative)}
);
return solution;
}
solution.push_back(nextPoint);
point = nextPoint;
}
throw std::runtime_error(
"The Lane-Emden integration did not reach its first zero "
"within the configured step limit."
);
}
double PolytropicStructure::interpolateLaneEmdenValue(
const std::vector<LaneEmdenPoint> &solution,
const double coordinate,
std::size_t &lowerIndex
) {
while (lowerIndex + 1 < solution.size() && solution[lowerIndex + 1].coordinate < coordinate) {
++lowerIndex;
}
if (lowerIndex + 1 >= solution.size()) {
return 0.0;
}
const LaneEmdenPoint &lower = solution[lowerIndex];
const LaneEmdenPoint &upper = solution[lowerIndex + 1];
const double interval = upper.coordinate - lower.coordinate;
if (interval <= 0.0) {
throw std::runtime_error(
"The Lane-Emden interpolation grid is not strictly "
"increasing."
);
}
const double fraction = (coordinate - lower.coordinate) / interval;
return std::clamp(lower.value + fraction * (upper.value - lower.value), 0.0, 1.0);
}
}; // namespace mean_field::models::structure

View File

@@ -1,135 +1,190 @@
module;
#include <cstdint>
#include <cmath>
#include <mfem.hpp>
module mean_field;
import :operators.context.barotropic_closure_linearization;
namespace {
void validate_finite_vector(
const mfem::Vector &vector,
const char *message
) {
for (int i = 0; i < vector.Size(); ++i) {
MFEM_VERIFY(std::isfinite(vector(i)), message);
}
}
template <typename Stamp>
void validate_dependency_transition(
const Stamp &prepared,
const Stamp &requested,
const char *message
) {
MFEM_VERIFY(requested.CanFollow(prepared), message);
MFEM_VERIFY(
prepared.identity == requested.identity || prepared.revision != requested.revision,
"A new barotropic-closure dependency identity must also carry a visibly different revision."
);
}
} // namespace
namespace mean_field::operators::context::barotropic {
BarotropicClosureLinearizationContext::
BarotropicClosureLinearizationContext(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const physics::PolytropicBarotrope &barotrope
)
BarotropicClosureLinearizationContext::BarotropicClosureLinearizationContext(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const field::FieldDofMap &densityMap,
const field::FieldDofMap &enthalpyMap,
const field::FieldDofMap &displacementMap
)
: m_f(f),
m_operator(
f,
domainMapper,
barotrope
) {
m_domainMapper(domainMapper),
m_densitySize(densityMap.reduced_size()),
m_enthalpySize(enthalpyMap.reduced_size()),
m_displacementSize(displacementMap.reduced_size()) {
MFEM_VERIFY(m_f.mesh != nullptr, "BarotropicClosureLinearizationContext requires a mesh.");
MFEM_VERIFY(m_f.densityFes != nullptr, "BarotropicClosureLinearizationContext requires the density FE space.");
MFEM_VERIFY(
m_f.densityFes != nullptr,
"The closure linearization context requires the "
"density finite-element space."
m_f.enthalpyFes != nullptr, "BarotropicClosureLinearizationContext requires the enthalpy FE space."
);
MFEM_VERIFY(
m_f.enthalpyFes != nullptr,
"The closure linearization context requires the "
"enthalpy finite-element space."
m_f.displacementFes != nullptr, "BarotropicClosureLinearizationContext requires the displacement FE space."
);
MFEM_VERIFY(
m_f.displacementFes != nullptr,
"The closure linearization context requires the "
"displacement finite-element space."
m_domainMapper.GetDimension() == m_f.mesh->Dimension(),
"The barotropic-closure context domain-mapper dimension does not match the mesh dimension."
);
MFEM_VERIFY(
densityMap.full_size() == m_f.densityFes->GetTrueVSize(),
"The density FieldDofMap does not match the density FE space."
);
MFEM_VERIFY(
enthalpyMap.full_size() == m_f.enthalpyFes->GetTrueVSize(),
"The enthalpy FieldDofMap does not match the enthalpy FE space."
);
MFEM_VERIFY(
displacementMap.full_size() == m_f.displacementFes->GetTrueVSize(),
"The displacement FieldDofMap does not match the displacement FE space."
);
}
void BarotropicClosureLinearizationContext::Prepare(
const mfem::Vector &baseDensityTrue,
const mfem::Vector &baseEnthalpyTrue,
const mfem::Vector &displacementTrue,
const BarotropicClosureRevisions &revisions
BarotropicClosurePreparationReport BarotropicClosureLinearizationContext::Prepare(
const BarotropicClosureStateView &state,
const BarotropicClosureDependencies &dependencies
) {
MFEM_VERIFY(state.density.Size() == m_densitySize, "The supported closure density vector has the wrong size.");
MFEM_VERIFY(
baseDensityTrue.Size() == m_f.densityFes->GetTrueVSize(),
"The closure base-density vector has the wrong size."
state.enthalpy.Size() == m_enthalpySize, "The supported closure enthalpy vector has the wrong size."
);
MFEM_VERIFY(
state.displacement.Size() == m_displacementSize,
"The supported closure displacement vector has the wrong size."
);
MFEM_VERIFY(
baseEnthalpyTrue.Size() == m_f.enthalpyFes->GetTrueVSize(),
"The closure base-enthalpy vector has the wrong size."
);
validate_finite_vector(state.density, "The closure density state contains a non-finite value.");
validate_finite_vector(state.enthalpy, "The closure enthalpy state contains a non-finite value.");
validate_finite_vector(state.displacement, "The closure displacement state contains a non-finite value.");
MFEM_VERIFY(
displacementTrue.Size() == m_f.displacementFes->GetTrueVSize(),
"The closure displacement vector has the wrong size."
);
if (m_isPrepared && revisions == m_revisions) {
return;
if (m_isPrepared) {
validate_dependency_transition(
m_dependencies.discretization, dependencies.discretization,
"BarotropicClosureLinearizationContext received an older discretization revision for the same identity."
);
validate_dependency_transition(
m_dependencies.density, dependencies.density,
"BarotropicClosureLinearizationContext received an older density revision for the same identity."
);
validate_dependency_transition(
m_dependencies.enthalpy, dependencies.enthalpy,
"BarotropicClosureLinearizationContext received an older enthalpy revision for the same identity."
);
validate_dependency_transition(
m_dependencies.displacement, dependencies.displacement,
"BarotropicClosureLinearizationContext received an older displacement revision for the same identity."
);
}
m_operator.Prepare(baseDensityTrue, baseEnthalpyTrue, displacementTrue);
const bool staticChanged = !m_isPrepared || dependencies.discretization != m_dependencies.discretization;
const bool densityChanged = !m_isPrepared || dependencies.density != m_dependencies.density;
const bool enthalpyChanged = !m_isPrepared || dependencies.enthalpy != m_dependencies.enthalpy;
const bool displacementChanged = !m_isPrepared || dependencies.displacement != m_dependencies.displacement;
m_baseDensityTrue = baseDensityTrue;
m_baseEnthalpyTrue = baseEnthalpyTrue;
m_displacementTrue = displacementTrue;
const bool geometryPreparationRequired = staticChanged || displacementChanged;
const bool baseStatePreparationRequired =
staticChanged || geometryPreparationRequired || densityChanged || enthalpyChanged;
m_revisions = revisions;
m_isPrepared = true;
++m_preparationCount;
BarotropicClosurePreparationReport report;
report.preparedStaticDependencies = staticChanged;
report.preparedGeometryState = geometryPreparationRequired;
report.preparedBaseState = baseStatePreparationRequired;
if (staticChanged || densityChanged) {
m_baseDensity = state.density;
report.updatedDensity = true;
}
if (staticChanged || enthalpyChanged) {
m_baseEnthalpy = state.enthalpy;
report.updatedEnthalpy = true;
}
if (geometryPreparationRequired) {
m_displacement = state.displacement;
report.updatedDisplacement = true;
}
if (report.preparedStaticDependencies) {
++m_statistics.staticPreparations;
}
if (report.preparedGeometryState) {
++m_statistics.geometryPreparations;
}
if (report.preparedBaseState) {
++m_statistics.baseStatePreparations;
}
m_dependencies = dependencies;
m_isPrepared = true;
return report;
}
bool BarotropicClosureLinearizationContext::IsPrepared() const noexcept {
return m_isPrepared;
}
bool BarotropicClosureLinearizationContext::MatchesRevisions(
const BarotropicClosureRevisions &revisions
bool BarotropicClosureLinearizationContext::MatchesDependencies(
const BarotropicClosureDependencies &dependencies
) const noexcept {
return m_isPrepared && revisions == m_revisions;
return m_isPrepared && dependencies == m_dependencies;
}
std::uint64_t BarotropicClosureLinearizationContext::
GetPreparationCount() const noexcept {
return m_preparationCount;
}
const BarotropicClosureRevisions &
BarotropicClosureLinearizationContext::GetRevisions() const {
const BarotropicClosureDependencies &BarotropicClosureLinearizationContext::GetDependencies() const {
VerifyPrepared();
return m_revisions;
return m_dependencies;
}
const mfem::Vector &
BarotropicClosureLinearizationContext::GetBaseDensityTrue() const {
const BarotropicClosurePreparationStatistics &
BarotropicClosureLinearizationContext::GetPreparationStatistics() const noexcept {
return m_statistics;
}
const mfem::Vector &BarotropicClosureLinearizationContext::GetBaseDensity() const {
VerifyPrepared();
return m_baseDensityTrue;
return m_baseDensity;
}
const mfem::Vector &
BarotropicClosureLinearizationContext::GetBaseEnthalpyTrue() const {
const mfem::Vector &BarotropicClosureLinearizationContext::GetBaseEnthalpy() const {
VerifyPrepared();
return m_baseEnthalpyTrue;
return m_baseEnthalpy;
}
const mfem::Vector &
BarotropicClosureLinearizationContext::GetDisplacementTrue() const {
const mfem::Vector &BarotropicClosureLinearizationContext::GetDisplacement() const {
VerifyPrepared();
return m_displacementTrue;
}
const PreparedBarotropicClosureOperator &
BarotropicClosureLinearizationContext::GetOperator() const noexcept {
return m_operator;
}
void BarotropicClosureLinearizationContext::BuildResidual(
mfem::Vector &residual
) const {
VerifyPrepared();
m_operator.BuildResidual(residual);
return m_displacement;
}
void BarotropicClosureLinearizationContext::VerifyPrepared() const {
MFEM_VERIFY(
m_isPrepared, "The barotropic-closure linearization context "
"has not been prepared."
);
MFEM_VERIFY(m_isPrepared, "BarotropicClosureLinearizationContext has not been prepared.");
}
} // namespace mean_field::operators::context::barotropic
} // namespace mean_field::operators::context::barotropic

View File

@@ -12,9 +12,8 @@ namespace {
const mfem::Vector &displacement_true
) {
MFEM_VERIFY(
f.displacementFes != nullptr,
"GravityFieldGeometryContext requires the "
"displacement finite-element space."
f.displacementFes != nullptr, "GravityFieldGeometryContext requires the "
"displacement finite-element space."
);
MFEM_VERIFY(
displacement_true.Size() == f.displacementFes->GetTrueVSize(),
@@ -25,18 +24,16 @@ namespace {
for (int i = 0; i < displacement_true.Size(); ++i) {
MFEM_VERIFY(
std::isfinite(displacement_true(i)),
"GravityFieldGeometryContext received a non-finite "
"displacement "
"value."
std::isfinite(displacement_true(i)), "GravityFieldGeometryContext received a non-finite "
"displacement "
"value."
);
}
}
void validate_linearization_state(
const mean_field::fem::FEM &f,
const mean_field::operators::context::gravity_field::
GravityFieldStateView &state
const mean_field::operators::context::gravity_field::GravityFieldStateView &state
) {
MFEM_VERIFY(
f.densityFes != nullptr, "GravityFieldLinearizationContext "
@@ -44,19 +41,16 @@ namespace {
"space."
);
MFEM_VERIFY(
f.gravityPotentialFes != nullptr,
"GravityFieldLinearizationContext requires the gravity-potential "
"finite-element space."
f.gravityPotentialFes != nullptr, "GravityFieldLinearizationContext requires the gravity-potential "
"finite-element space."
);
MFEM_VERIFY(
f.gravityFluxFes != nullptr,
"GravityFieldLinearizationContext requires the "
"gravity-gradient finite-element space."
f.gravityFluxFes != nullptr, "GravityFieldLinearizationContext requires the "
"gravity-gradient finite-element space."
);
MFEM_VERIFY(
f.displacementFes != nullptr,
"GravityFieldLinearizationContext requires "
"the displacement finite-element space."
f.displacementFes != nullptr, "GravityFieldLinearizationContext requires "
"the displacement finite-element space."
);
MFEM_VERIFY(
@@ -78,8 +72,7 @@ namespace {
"with the wrong size."
);
MFEM_VERIFY(
state.gravity_potential.Size() ==
f.gravityPotentialFes->GetTrueVSize(),
state.gravity_potential.Size() == f.gravityPotentialFes->GetTrueVSize(),
"GravityFieldLinearizationContext received a gravity-potential "
"vector "
"with the wrong size."
@@ -87,35 +80,31 @@ namespace {
for (int i = 0; i < state.density.Size(); ++i) {
MFEM_VERIFY(
std::isfinite(state.density(i)),
"GravityFieldLinearizationContext received a non-finite "
"density "
"value."
std::isfinite(state.density(i)), "GravityFieldLinearizationContext received a non-finite "
"density "
"value."
);
}
for (int i = 0; i < state.displacement.Size(); ++i) {
MFEM_VERIFY(
std::isfinite(state.displacement(i)),
"GravityFieldLinearizationContext received a non-finite "
"displacement "
"value."
std::isfinite(state.displacement(i)), "GravityFieldLinearizationContext received a non-finite "
"displacement "
"value."
);
}
for (int i = 0; i < state.gravity_gradient.Size(); ++i) {
MFEM_VERIFY(
std::isfinite(state.gravity_gradient(i)),
"GravityFieldLinearizationContext received a non-finite "
"gravity-gradient value."
std::isfinite(state.gravity_gradient(i)), "GravityFieldLinearizationContext received a non-finite "
"gravity-gradient value."
);
}
for (int i = 0; i < state.gravity_potential.Size(); ++i) {
MFEM_VERIFY(
std::isfinite(state.gravity_potential(i)),
"GravityFieldLinearizationContext received a non-finite "
"gravity-potential value."
std::isfinite(state.gravity_potential(i)), "GravityFieldLinearizationContext received a non-finite "
"gravity-potential value."
);
}
}
@@ -128,42 +117,33 @@ namespace mean_field::operators::context::gravity_field {
)
: m_fem(f),
m_domain_mapper(domain_mapper) {
MFEM_VERIFY(f.mesh != nullptr, "GravityFieldGeometryContext requires a mesh.");
MFEM_VERIFY(
f.mesh != nullptr, "GravityFieldGeometryContext requires a mesh."
f.gravityFluxFes != nullptr, "GravityFieldGeometryContext requires the "
"gravity-gradient finite-element space."
);
MFEM_VERIFY(
f.gravityFluxFes != nullptr,
"GravityFieldGeometryContext requires the "
"gravity-gradient finite-element space."
f.densityFes != nullptr, "GravityFieldGeometryContext requires the density finite-element "
"space."
);
MFEM_VERIFY(
f.densityFes != nullptr,
"GravityFieldGeometryContext requires the density finite-element "
"space."
f.gravityPotentialFes != nullptr, "GravityFieldGeometryContext requires the gravity-potential "
"finite-element space."
);
MFEM_VERIFY(
f.gravityPotentialFes != nullptr,
"GravityFieldGeometryContext requires the gravity-potential "
"finite-element space."
f.displacementFes != nullptr, "GravityFieldGeometryContext requires the "
"displacement finite-element space."
);
MFEM_VERIFY(
f.displacementFes != nullptr,
"GravityFieldGeometryContext requires the "
"displacement finite-element space."
f.compactificationFes != nullptr, "GravityFieldGeometryContext requires the compactification "
"finite-element space."
);
MFEM_VERIFY(
f.compactificationFes != nullptr,
"GravityFieldGeometryContext requires the compactification "
"finite-element space."
f.compactificationCoordinate != nullptr, "GravityFieldGeometryContext requires the compactification "
"coordinate."
);
MFEM_VERIFY(
f.compactificationCoordinate != nullptr,
"GravityFieldGeometryContext requires the compactification "
"coordinate."
);
MFEM_VERIFY(
f.quadratureFactory != nullptr,
"GravityFieldGeometryContext requires the quadrature-rule factory."
f.quadratureFactory != nullptr, "GravityFieldGeometryContext requires the quadrature-rule factory."
);
MFEM_VERIFY(
domain_mapper.GetDimension() == f.mesh->Dimension(),
@@ -192,11 +172,8 @@ namespace mean_field::operators::context::gravity_field {
);
}
const bool discretization_changed =
!m_is_prepared ||
discretization_revision != m_discretization_revision;
const bool displacement_changed =
!m_is_prepared || displacement_revision != m_displacement_revision;
const bool discretization_changed = !m_is_prepared || discretization_revision != m_discretization_revision;
const bool displacement_changed = !m_is_prepared || displacement_revision != m_displacement_revision;
GravityFieldGeometryPreparation preparation;
@@ -205,14 +182,8 @@ 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);
mass_operator->Prepare(displacement_true);
source_operator->Prepare(displacement_true);
@@ -229,9 +200,8 @@ namespace mean_field::operators::context::gravity_field {
"no prepared H(div) mass operator."
);
MFEM_VERIFY(
m_source_operator != nullptr,
"GravityFieldGeometryContext has no prepared gravity source "
"operator."
m_source_operator != nullptr, "GravityFieldGeometryContext has no prepared gravity source "
"operator."
);
m_mass_operator->Prepare(displacement_true);
@@ -251,26 +221,19 @@ namespace mean_field::operators::context::gravity_field {
return preparation;
}
const PreparedMappedHDivMassOperator &
GravityFieldGeometryContext::GetMassOperator() const {
const PreparedMappedHDivMassOperator &GravityFieldGeometryContext::GetMassOperator() const {
MFEM_VERIFY(
m_is_prepared,
"GravityFieldGeometryContext must be prepared before "
"accessing its mass operator."
);
MFEM_VERIFY(
m_mass_operator != nullptr,
"GravityFieldGeometryContext has no prepared H(div) mass operator."
m_is_prepared, "GravityFieldGeometryContext must be prepared before "
"accessing its mass operator."
);
MFEM_VERIFY(m_mass_operator != nullptr, "GravityFieldGeometryContext has no prepared H(div) mass operator.");
return *m_mass_operator;
}
const PreparedMappedGravitySourceOperator &
GravityFieldGeometryContext::GetSourceOperator() const {
const PreparedMappedGravitySourceOperator &GravityFieldGeometryContext::GetSourceOperator() const {
MFEM_VERIFY(
m_is_prepared,
"GravityFieldGeometryContext must be prepared before "
"accessing its source operator."
m_is_prepared, "GravityFieldGeometryContext must be prepared before "
"accessing its source operator."
);
MFEM_VERIFY(
m_source_operator != nullptr, "GravityFieldGeometryContext has no "
@@ -281,20 +244,17 @@ namespace mean_field::operators::context::gravity_field {
const mfem::Vector &GravityFieldGeometryContext::GetDisplacement() const {
MFEM_VERIFY(
m_is_prepared,
"GravityFieldGeometryContext must be prepared before "
"accessing its displacement."
m_is_prepared, "GravityFieldGeometryContext must be prepared before "
"accessing its displacement."
);
return m_displacement_true;
}
DiscretizationRevision
GravityFieldGeometryContext::GetDiscretizationRevision() const noexcept {
DiscretizationRevision GravityFieldGeometryContext::GetDiscretizationRevision() const noexcept {
return m_discretization_revision;
}
DisplacementRevision
GravityFieldGeometryContext::GetDisplacementRevision() const noexcept {
DisplacementRevision GravityFieldGeometryContext::GetDisplacementRevision() const noexcept {
return m_displacement_revision;
}
@@ -317,19 +277,16 @@ namespace mean_field::operators::context::gravity_field {
"space."
);
MFEM_VERIFY(
f.gravityPotentialFes != nullptr,
"GravityFieldLinearizationContext requires the gravity-potential "
"finite-element space."
f.gravityPotentialFes != nullptr, "GravityFieldLinearizationContext requires the gravity-potential "
"finite-element space."
);
MFEM_VERIFY(
f.gravityFluxFes != nullptr,
"GravityFieldLinearizationContext requires the "
"gravity-gradient finite-element space."
f.gravityFluxFes != nullptr, "GravityFieldLinearizationContext requires the "
"gravity-gradient finite-element space."
);
MFEM_VERIFY(
f.displacementFes != nullptr,
"GravityFieldLinearizationContext requires "
"the displacement finite-element space."
f.displacementFes != nullptr, "GravityFieldLinearizationContext requires "
"the displacement finite-element space."
);
}
@@ -353,9 +310,8 @@ namespace mean_field::operators::context::gravity_field {
"revision."
);
MFEM_VERIFY(
revisions.density >= m_revisions.density,
"GravityFieldLinearizationContext received an older density "
"revision."
revisions.density >= m_revisions.density, "GravityFieldLinearizationContext received an older density "
"revision."
);
MFEM_VERIFY(
revisions.gravity_gradient >= m_revisions.gravity_gradient,
@@ -370,20 +326,16 @@ namespace mean_field::operators::context::gravity_field {
);
}
const bool discretization_changed =
!m_is_prepared ||
revisions.discretization != m_revisions.discretization;
const bool density_changed = !m_is_prepared || discretization_changed ||
revisions.density != m_revisions.density;
const bool discretization_changed = !m_is_prepared || revisions.discretization != m_revisions.discretization;
const bool density_changed =
!m_is_prepared || discretization_changed || revisions.density != m_revisions.density;
const bool gravity_gradient_changed =
!m_is_prepared || discretization_changed ||
revisions.gravity_gradient != m_revisions.gravity_gradient;
!m_is_prepared || discretization_changed || revisions.gravity_gradient != m_revisions.gravity_gradient;
GravityFieldPreparationReport report;
report.geometry = m_geometry_context.Prepare(
state.displacement, revisions.discretization, revisions.displacement
);
report.geometry =
m_geometry_context.Prepare(state.displacement, revisions.discretization, revisions.displacement);
if (density_changed) {
m_density_true = state.density;
@@ -401,8 +353,7 @@ namespace mean_field::operators::context::gravity_field {
return report;
}
const GravityFieldGeometryContext &
GravityFieldLinearizationContext::GetGeometryContext() const {
const GravityFieldGeometryContext &GravityFieldLinearizationContext::GetGeometryContext() const {
MFEM_VERIFY(
m_is_prepared, "GravityFieldLinearizationContext must be prepared "
"before accessing its geometry context."
@@ -418,8 +369,7 @@ namespace mean_field::operators::context::gravity_field {
return m_density_true;
}
const mfem::Vector &
GravityFieldLinearizationContext::GetGravityGradient() const {
const mfem::Vector &GravityFieldLinearizationContext::GetGravityGradient() const {
MFEM_VERIFY(
m_is_prepared, "GravityFieldLinearizationContext must be prepared "
"before accessing its gravity gradient."
@@ -427,8 +377,7 @@ namespace mean_field::operators::context::gravity_field {
return m_gravity_gradient_true;
}
const GravityFieldRevisions &
GravityFieldLinearizationContext::GetRevisions() const {
const GravityFieldRevisions &GravityFieldLinearizationContext::GetRevisions() const {
MFEM_VERIFY(
m_is_prepared, "GravityFieldLinearizationContext must be prepared "
"before accessing its revisions."

View File

@@ -20,44 +20,37 @@ namespace {
void validate_state(
const mean_field::fem::FEM &f,
const mean_field::operators::context::hydrostatic::
HydrostaticEquilibriumStateView &state
const mean_field::operators::context::hydrostatic::HydrostaticEquilibriumStateView &state
) {
MFEM_VERIFY(
f.enthalpyFes != nullptr,
"HydrostaticEquilibriumContext requires the "
"enthalpy finite-element space."
f.enthalpyFes != nullptr, "HydrostaticEquilibriumContext requires the "
"enthalpy finite-element space."
);
MFEM_VERIFY(
f.gravityPotentialFes != nullptr,
"HydrostaticEquilibriumContext requires the "
"gravity-potential finite-element space."
f.gravityPotentialFes != nullptr, "HydrostaticEquilibriumContext requires the "
"gravity-potential finite-element space."
);
MFEM_VERIFY(
f.displacementFes != nullptr,
"HydrostaticEquilibriumContext requires the "
"displacement finite-element space."
f.displacementFes != nullptr, "HydrostaticEquilibriumContext requires the "
"displacement finite-element space."
);
MFEM_VERIFY(
state.enthalpy.Size() == f.enthalpyFes->GetTrueVSize(),
"HydrostaticEquilibriumContext received an "
"enthalpy vector with the wrong size."
state.enthalpy.Size() == f.enthalpyFes->GetTrueVSize(), "HydrostaticEquilibriumContext received an "
"enthalpy vector with the wrong size."
);
MFEM_VERIFY(
state.gravityPotential.Size() ==
f.gravityPotentialFes->GetTrueVSize(),
state.gravityPotential.Size() == f.gravityPotentialFes->GetTrueVSize(),
"HydrostaticEquilibriumContext received a "
"gravity-potential vector with the wrong size."
);
MFEM_VERIFY(
state.displacement.Size() == f.displacementFes->GetTrueVSize(),
"HydrostaticEquilibriumContext received a "
"displacement vector with the wrong size."
state.displacement.Size() == f.displacementFes->GetTrueVSize(), "HydrostaticEquilibriumContext received a "
"displacement vector with the wrong size."
);
validate_finite_vector(
@@ -76,9 +69,8 @@ namespace {
);
MFEM_VERIFY(
std::isfinite(state.bernoulliConstant),
"HydrostaticEquilibriumContext received a "
"non-finite Bernoulli constant."
std::isfinite(state.bernoulliConstant), "HydrostaticEquilibriumContext received a "
"non-finite Bernoulli constant."
);
}
@@ -99,34 +91,27 @@ namespace mean_field::operators::context::hydrostatic {
)
: m_f(f),
m_domainMapper(domainMapper) {
MFEM_VERIFY(m_f.mesh != nullptr, "HydrostaticEquilibriumContext requires a mesh.");
MFEM_VERIFY(
m_f.mesh != nullptr,
"HydrostaticEquilibriumContext requires a mesh."
m_f.enthalpyFes != nullptr, "HydrostaticEquilibriumContext requires the "
"enthalpy finite-element space."
);
MFEM_VERIFY(
m_f.enthalpyFes != nullptr,
"HydrostaticEquilibriumContext requires the "
"enthalpy finite-element space."
m_f.gravityPotentialFes != nullptr, "HydrostaticEquilibriumContext requires the "
"gravity-potential finite-element space."
);
MFEM_VERIFY(
m_f.gravityPotentialFes != nullptr,
"HydrostaticEquilibriumContext requires the "
"gravity-potential finite-element space."
m_f.displacementFes != nullptr, "HydrostaticEquilibriumContext requires the "
"displacement finite-element space."
);
MFEM_VERIFY(
m_f.displacementFes != nullptr,
"HydrostaticEquilibriumContext requires the "
"displacement finite-element space."
);
MFEM_VERIFY(
m_domainMapper.GetDimension() == m_f.mesh->Dimension(),
"The hydrostatic context's stateless "
"domain-mapper dimension does not match the mesh "
"dimension."
m_domainMapper.GetDimension() == m_f.mesh->Dimension(), "The hydrostatic context's stateless "
"domain-mapper dimension does not match the mesh "
"dimension."
);
}
@@ -168,44 +153,32 @@ namespace mean_field::operators::context::hydrostatic {
);
validate_dependency_transition(
m_dependencies.bernoulliConstant,
dependencies.bernoulliConstant,
m_dependencies.bernoulliConstant, dependencies.bernoulliConstant,
"HydrostaticEquilibriumContext received an older "
"Bernoulli-constant revision for the same identity."
);
}
const bool staticChanged =
!m_isPrepared ||
dependencies.discretization != m_dependencies.discretization;
const bool staticChanged = !m_isPrepared || dependencies.discretization != m_dependencies.discretization;
const bool enthalpyChanged =
!m_isPrepared || dependencies.enthalpy != m_dependencies.enthalpy;
const bool enthalpyChanged = !m_isPrepared || dependencies.enthalpy != m_dependencies.enthalpy;
const bool gravityPotentialChanged =
!m_isPrepared ||
dependencies.gravityPotential != m_dependencies.gravityPotential;
!m_isPrepared || dependencies.gravityPotential != m_dependencies.gravityPotential;
const bool displacementChanged =
!m_isPrepared ||
dependencies.displacement != m_dependencies.displacement;
const bool displacementChanged = !m_isPrepared || dependencies.displacement != m_dependencies.displacement;
const bool rotationChanged =
!m_isPrepared || dependencies.rotation != m_dependencies.rotation;
const bool rotationChanged = !m_isPrepared || dependencies.rotation != m_dependencies.rotation;
const bool bernoulliConstantChanged =
!m_isPrepared ||
dependencies.bernoulliConstant != m_dependencies.bernoulliConstant;
!m_isPrepared || dependencies.bernoulliConstant != m_dependencies.bernoulliConstant;
const bool geometryPreparationRequired =
staticChanged || displacementChanged;
const bool geometryPreparationRequired = staticChanged || displacementChanged;
const bool rotationPreparationRequired =
geometryPreparationRequired || rotationChanged;
const bool rotationPreparationRequired = geometryPreparationRequired || rotationChanged;
const bool baseStatePreparationRequired =
rotationPreparationRequired || enthalpyChanged ||
gravityPotentialChanged || bernoulliConstantChanged;
rotationPreparationRequired || enthalpyChanged || gravityPotentialChanged || bernoulliConstantChanged;
HydrostaticPreparationReport report;
@@ -267,31 +240,26 @@ namespace mean_field::operators::context::hydrostatic {
return m_isPrepared && dependencies == m_dependencies;
}
const HydrostaticEquilibriumDependencies &
HydrostaticEquilibriumContext::GetDependencies() const {
const HydrostaticEquilibriumDependencies &HydrostaticEquilibriumContext::GetDependencies() const {
VerifyPrepared();
return m_dependencies;
}
const HydrostaticPreparationStatistics &
HydrostaticEquilibriumContext::GetPreparationStatistics() const noexcept {
const HydrostaticPreparationStatistics &HydrostaticEquilibriumContext::GetPreparationStatistics() const noexcept {
return m_statistics;
}
const mfem::Vector &
HydrostaticEquilibriumContext::GetBaseEnthalpyTrue() const {
const mfem::Vector &HydrostaticEquilibriumContext::GetBaseEnthalpyTrue() const {
VerifyPrepared();
return m_baseEnthalpyTrue;
}
const mfem::Vector &
HydrostaticEquilibriumContext::GetBaseGravityPotentialTrue() const {
const mfem::Vector &HydrostaticEquilibriumContext::GetBaseGravityPotentialTrue() const {
VerifyPrepared();
return m_baseGravityPotentialTrue;
}
const mfem::Vector &
HydrostaticEquilibriumContext::GetDisplacementTrue() const {
const mfem::Vector &HydrostaticEquilibriumContext::GetDisplacementTrue() const {
VerifyPrepared();
return m_displacementTrue;
}
@@ -302,8 +270,6 @@ namespace mean_field::operators::context::hydrostatic {
}
void HydrostaticEquilibriumContext::VerifyPrepared() const {
MFEM_VERIFY(
m_isPrepared, "HydrostaticEquilibriumContext has not been prepared."
);
MFEM_VERIFY(m_isPrepared, "HydrostaticEquilibriumContext has not been prepared.");
}
} // namespace mean_field::operators::context::hydrostatic

View File

@@ -0,0 +1,219 @@
module;
#include <cmath>
#include <mfem.hpp>
module mean_field;
import :operators.context.pressure_force;
namespace {
void validate_finite_vector(
const mfem::Vector &vector,
const char *message
) {
for (int index = 0; index < vector.Size(); ++index) {
MFEM_VERIFY(std::isfinite(vector(index)), message);
}
}
template <typename Dependency>
void validate_dependency_transition(
const Dependency &prepared,
const Dependency &requested,
const char *message
) {
MFEM_VERIFY(requested.CanFollow(prepared), message);
MFEM_VERIFY(
prepared.identity == requested.identity || prepared.revision != requested.revision,
"A new pressure-force dependency identity must also carry "
"a visibly different revision."
);
}
} // namespace
namespace mean_field::operators::context::pressure_force {
PressureForceLinearizationContext::PressureForceLinearizationContext(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const field::FieldDofMap &enthalpyMap,
const field::FieldDofMap &displacementMap
)
: m_enthalpySize(enthalpyMap.reduced_size()),
m_displacementSize(displacementMap.reduced_size()) {
MFEM_VERIFY(f.mesh != nullptr, "PressureForceLinearizationContext requires a mesh.");
MFEM_VERIFY(
f.enthalpyFes != nullptr, "PressureForceLinearizationContext requires the enthalpy "
"finite-element space."
);
MFEM_VERIFY(
f.displacementFes != nullptr, "PressureForceLinearizationContext requires the displacement "
"finite-element space."
);
MFEM_VERIFY(
domainMapper.GetDimension() == f.mesh->Dimension(), "The pressure-force context's stateless domain-mapper "
"dimension does not match the mesh dimension."
);
MFEM_VERIFY(
enthalpyMap.full_size() == f.enthalpyFes->GetTrueVSize(),
"The pressure-force enthalpy FieldDofMap does not match the "
"enthalpy finite-element space."
);
MFEM_VERIFY(
displacementMap.full_size() == f.displacementFes->GetTrueVSize(),
"The pressure-force displacement FieldDofMap does not match "
"the displacement finite-element space."
);
}
PressureForcePreparationReport PressureForceLinearizationContext::Prepare(
const PressureForceStateView &state,
const PressureForceDependencies &dependencies
) {
MFEM_VERIFY(
state.enthalpy.Size() == m_enthalpySize, "PressureForceLinearizationContext received a supported "
"enthalpy vector with the wrong size."
);
MFEM_VERIFY(
state.displacement.Size() == m_displacementSize, "PressureForceLinearizationContext received a supported "
"displacement vector with the wrong size."
);
validate_finite_vector(
state.enthalpy, "PressureForceLinearizationContext received a non-finite "
"enthalpy value."
);
validate_finite_vector(
state.displacement, "PressureForceLinearizationContext received a non-finite "
"displacement value."
);
if (m_isPrepared) {
validate_dependency_transition(
m_dependencies.discretization, dependencies.discretization,
"PressureForceLinearizationContext received an older "
"discretization revision for the same identity."
);
validate_dependency_transition(
m_dependencies.enthalpy, dependencies.enthalpy,
"PressureForceLinearizationContext received an older "
"enthalpy revision for the same identity."
);
validate_dependency_transition(
m_dependencies.displacement, dependencies.displacement,
"PressureForceLinearizationContext received an older "
"displacement revision for the same identity."
);
}
const bool discretizationChanged =
!m_isPrepared || dependencies.discretization != m_dependencies.discretization;
const bool enthalpyChanged = !m_isPrepared || dependencies.enthalpy != m_dependencies.enthalpy;
const bool displacementChanged = !m_isPrepared || dependencies.displacement != m_dependencies.displacement;
/*
* Static data depend only on discretization.
*
* Geometry data depend on discretization and displacement.
*
* Material data depend on both geometry and enthalpy because
* pressure and its enthalpy derivative are evaluated on the frozen
* mapped state.
*/
const bool geometryPreparationRequired = discretizationChanged || displacementChanged;
const bool materialPreparationRequired = geometryPreparationRequired || enthalpyChanged;
PressureForcePreparationReport report;
report.preparedStaticDependencies = discretizationChanged;
report.preparedGeometryState = geometryPreparationRequired;
report.preparedMaterialState = materialPreparationRequired;
/*
* A discretization change invalidates every frozen field because
* their coordinate interpretation may have changed.
*/
if (discretizationChanged || enthalpyChanged) {
m_baseEnthalpy = state.enthalpy;
report.updatedEnthalpy = true;
}
if (geometryPreparationRequired) {
m_displacement = state.displacement;
report.updatedDisplacement = true;
}
if (report.preparedStaticDependencies) {
++m_statistics.staticPreparations;
}
if (report.preparedGeometryState) {
++m_statistics.geometryPreparations;
}
if (report.preparedMaterialState) {
++m_statistics.materialPreparations;
}
m_dependencies = dependencies;
m_isPrepared = true;
return report;
}
const PressureForcePreparationStatistics &
PressureForceLinearizationContext::GetPreparationStatistics() const noexcept {
return m_statistics;
}
bool PressureForceLinearizationContext::IsPrepared() const noexcept {
return m_isPrepared;
}
bool PressureForceLinearizationContext::MatchesDependencies(
const PressureForceDependencies &dependencies
) const noexcept {
return m_isPrepared && dependencies == m_dependencies;
}
const PressureForceDependencies &PressureForceLinearizationContext::GetDependencies() const {
VerifyPrepared();
return m_dependencies;
}
const mfem::Vector &PressureForceLinearizationContext::GetBaseEnthalpy() const {
VerifyPrepared();
return m_baseEnthalpy;
}
const mfem::Vector &PressureForceLinearizationContext::GetDisplacement() const {
VerifyPrepared();
return m_displacement;
}
void PressureForceLinearizationContext::VerifyPrepared() const {
MFEM_VERIFY(m_isPrepared, "PressureForceLinearizationContext has not been prepared.");
}
} // namespace mean_field::operators::context::pressure_force

View File

@@ -0,0 +1,203 @@
module;
#include <cmath>
#include <mfem.hpp>
module mean_field;
import :operators.context.rotational_displacement_force;
namespace {
void validate_finite_vector(
const mfem::Vector &vector,
const char *message
) {
for (int index = 0; index < vector.Size(); ++index) {
MFEM_VERIFY(std::isfinite(vector(index)), message);
}
}
template <typename Dependency>
void validate_dependency_transition(
const Dependency &prepared,
const Dependency &requested,
const char *message
) {
MFEM_VERIFY(requested.CanFollow(prepared), message);
}
} // namespace
namespace mean_field::operators::context::rotational_displacement_force {
RotationalDisplacementForceLinearizationContext::RotationalDisplacementForceLinearizationContext(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper
)
: m_f(f) {
MFEM_VERIFY(
m_f.mesh != nullptr, "RotationalDisplacementForceLinearizationContext requires a "
"mesh."
);
MFEM_VERIFY(
m_f.densityFes != nullptr, "RotationalDisplacementForceLinearizationContext requires the "
"density finite-element space."
);
MFEM_VERIFY(
m_f.displacementFes != nullptr, "RotationalDisplacementForceLinearizationContext requires the "
"displacement finite-element space."
);
MFEM_VERIFY(
domainMapper.GetDimension() == m_f.mesh->Dimension(),
"The rotational-displacement-force context's stateless "
"domain-mapper dimension does not match the mesh dimension."
);
}
RotationalDisplacementForcePreparationReport RotationalDisplacementForceLinearizationContext::Prepare(
const RotationalDisplacementForceStateView &state,
const RotationalDisplacementForceDependencies &dependencies
) {
MFEM_VERIFY(
state.density.Size() == m_f.densityFes->GetTrueVSize(),
"RotationalDisplacementForceLinearizationContext received a "
"density vector with the wrong size."
);
MFEM_VERIFY(
state.displacement.Size() == m_f.displacementFes->GetTrueVSize(),
"RotationalDisplacementForceLinearizationContext received a "
"displacement vector with the wrong size."
);
validate_finite_vector(
state.density, "RotationalDisplacementForceLinearizationContext received a "
"non-finite density value."
);
validate_finite_vector(
state.displacement, "RotationalDisplacementForceLinearizationContext received a "
"non-finite displacement value."
);
if (m_isPrepared) {
validate_dependency_transition(
m_dependencies.discretization, dependencies.discretization,
"RotationalDisplacementForceLinearizationContext received "
"an older discretization revision for the same identity."
);
validate_dependency_transition(
m_dependencies.density, dependencies.density,
"RotationalDisplacementForceLinearizationContext received "
"an older density revision for the same identity."
);
validate_dependency_transition(
m_dependencies.displacement, dependencies.displacement,
"RotationalDisplacementForceLinearizationContext received "
"an older displacement revision for the same identity."
);
validate_dependency_transition(
m_dependencies.rotation, dependencies.rotation,
"RotationalDisplacementForceLinearizationContext received "
"an older rotation revision for the same identity."
);
}
const bool discretizationChanged =
!m_isPrepared || dependencies.discretization != m_dependencies.discretization;
const bool densityChanged = !m_isPrepared || dependencies.density != m_dependencies.density;
const bool displacementChanged = !m_isPrepared || dependencies.displacement != m_dependencies.displacement;
const bool rotationChanged = !m_isPrepared || dependencies.rotation != m_dependencies.rotation;
const bool geometryPreparationRequired = discretizationChanged || displacementChanged;
const bool rotationPreparationRequired = discretizationChanged || rotationChanged;
const bool baseStatePreparationRequired =
geometryPreparationRequired || rotationPreparationRequired || densityChanged;
RotationalDisplacementForcePreparationReport report;
report.preparedStaticDependencies = discretizationChanged;
report.preparedGeometryState = geometryPreparationRequired;
report.preparedRotationDependencies = rotationPreparationRequired;
report.preparedBaseState = baseStatePreparationRequired;
if (discretizationChanged || densityChanged) {
m_baseDensityTrue = state.density;
report.updatedDensity = true;
}
if (geometryPreparationRequired) {
m_displacementTrue = state.displacement;
report.updatedDisplacement = true;
}
if (report.preparedStaticDependencies) {
++m_statistics.staticPreparations;
}
if (report.preparedGeometryState) {
++m_statistics.geometryPreparations;
}
if (report.preparedRotationDependencies) {
++m_statistics.rotationPreparations;
}
if (report.preparedBaseState) {
++m_statistics.baseStatePreparations;
}
m_dependencies = dependencies;
m_isPrepared = true;
return report;
}
bool RotationalDisplacementForceLinearizationContext::IsPrepared() const noexcept {
return m_isPrepared;
}
bool RotationalDisplacementForceLinearizationContext::MatchesDependencies(
const RotationalDisplacementForceDependencies &dependencies
) const noexcept {
return m_isPrepared && dependencies == m_dependencies;
}
const RotationalDisplacementForceDependencies &
RotationalDisplacementForceLinearizationContext::GetDependencies() const {
VerifyPrepared();
return m_dependencies;
}
const RotationalDisplacementForcePreparationStatistics &
RotationalDisplacementForceLinearizationContext::GetPreparationStatistics() const noexcept {
return m_statistics;
}
const mfem::Vector &RotationalDisplacementForceLinearizationContext::GetBaseDensityTrue() const {
VerifyPrepared();
return m_baseDensityTrue;
}
const mfem::Vector &RotationalDisplacementForceLinearizationContext::GetDisplacementTrue() const {
VerifyPrepared();
return m_displacementTrue;
}
void RotationalDisplacementForceLinearizationContext::VerifyPrepared() const {
MFEM_VERIFY(
m_isPrepared, "RotationalDisplacementForceLinearizationContext has not been "
"prepared."
);
}
} // namespace mean_field::operators::context::rotational_displacement_force

View File

@@ -13,43 +13,31 @@ import :operators.kernels.gravity_field;
namespace {
using namespace mean_field;
int get_state_width(const mfem::Array<int> &state_true_offsets) {
MFEM_VERIFY(
state_true_offsets.Size() >= 2,
"The coupled state requires at least one block."
);
MFEM_VERIFY(
state_true_offsets[0] == 0,
"The coupled state offsets must begin at zero."
);
MFEM_VERIFY(state_true_offsets.Size() >= 2, "The coupled state requires at least one block.");
MFEM_VERIFY(state_true_offsets[0] == 0, "The coupled state offsets must begin at zero.");
for (int i = 0; i < state_true_offsets.Size() - 1; ++i) {
MFEM_VERIFY(
state_true_offsets[i + 1] >= state_true_offsets[i],
"The coupled state offsets must be nondecreasing."
state_true_offsets[i + 1] >= state_true_offsets[i], "The coupled state offsets must be nondecreasing."
);
}
MFEM_VERIFY(
state_true_offsets.Last() > 0, "The coupled state cannot be empty."
);
MFEM_VERIFY(state_true_offsets.Last() > 0, "The coupled state cannot be empty.");
return state_true_offsets.Last();
}
int get_gravity_residual_height(const fem::FEM &f) {
MFEM_VERIFY(
f.gravityFluxFes != nullptr,
"GravityFieldOperator requires the gravity-gradient finite-element "
"space (RT: Raviart-Thomas)."
f.gravityFluxFes != nullptr, "GravityFieldOperator requires the gravity-gradient finite-element "
"space (RT: Raviart-Thomas)."
);
MFEM_VERIFY(
f.gravityPotentialFes != nullptr,
"GravityFieldOperator requires the gravity-potential "
"finite-element "
"space (L2: Lebesgue "
"space of square-integrable functions)."
f.gravityPotentialFes != nullptr, "GravityFieldOperator requires the gravity-potential "
"finite-element "
"space (L2: Lebesgue "
"space of square-integrable functions)."
);
return f.gravityFluxFes->GetTrueVSize() +
f.gravityPotentialFes->GetTrueVSize();
return f.gravityFluxFes->GetTrueVSize() + f.gravityPotentialFes->GetTrueVSize();
}
mfem::Array<int> make_gravity_residual_offsets(const fem::FEM &f) {
@@ -65,10 +53,7 @@ namespace {
const mfem::Array<int> &state_true_offsets,
const utils::blocks::value_block<index>
) {
MFEM_VERIFY(
index + 1 < state_true_offsets.Size(),
"Value block is not present in the state offsets."
);
MFEM_VERIFY(index + 1 < state_true_offsets.Size(), "Value block is not present in the state offsets.");
return state_true_offsets[index + 1] - state_true_offsets[index];
}
@@ -76,10 +61,7 @@ namespace {
const fem::FEM &f,
const mfem::Array<int> &state_true_offsets
) {
MFEM_VERIFY(
f.densityFes != nullptr,
"GravityFieldOperator requires the density finite-element space."
);
MFEM_VERIFY(f.densityFes != nullptr, "GravityFieldOperator requires the density finite-element space.");
MFEM_VERIFY(
f.displacementFes != nullptr, "GravityFieldOperator requires the "
"displacement finite-element space."
@@ -87,65 +69,44 @@ namespace {
using form = utils::blocks::gravity_field_form;
constexpr auto density_block = utils::blocks::get_value_block<form>(
utils::blocks::density_field.mass_term
);
constexpr auto density_block = utils::blocks::get_value_block<form>(utils::blocks::density_field.mass_term);
constexpr auto displacement_block =
utils::blocks::get_value_block<form>(
utils::blocks::displacement_field.geometry_term
);
utils::blocks::get_value_block<form>(utils::blocks::displacement_field.geometry_term);
constexpr auto gravity_gradient_block =
utils::blocks::get_value_block<form>(
utils::blocks::gravity_field.gradient_term
);
utils::blocks::get_value_block<form>(utils::blocks::gravity_field.gradient_term);
constexpr auto gravity_potential_block =
utils::blocks::get_value_block<form>(
utils::blocks::gravity_field.poisson_term
);
utils::blocks::get_value_block<form>(utils::blocks::gravity_field.poisson_term);
MFEM_VERIFY(
state_true_offsets.Size() == form::value_block_count + 1,
"The gravity state offsets do not match gravity_field_form."
);
MFEM_VERIFY(
get_state_block_size(state_true_offsets, density_block) ==
f.densityFes->GetTrueVSize(),
get_state_block_size(state_true_offsets, density_block) == f.densityFes->GetTrueVSize(),
"The density block does not match the density finite-element space."
);
MFEM_VERIFY(
get_state_block_size(state_true_offsets, displacement_block) ==
f.displacementFes->GetTrueVSize(),
get_state_block_size(state_true_offsets, displacement_block) == f.displacementFes->GetTrueVSize(),
"The displacement block does not match the displacement "
"finite-element "
"space."
);
MFEM_VERIFY(
get_state_block_size(state_true_offsets, gravity_gradient_block) ==
f.gravityFluxFes->GetTrueVSize(),
get_state_block_size(state_true_offsets, gravity_gradient_block) == f.gravityFluxFes->GetTrueVSize(),
"The gravity-gradient block does not match the RT finite-element "
"space."
);
MFEM_VERIFY(
get_state_block_size(state_true_offsets, gravity_potential_block) ==
f.gravityPotentialFes->GetTrueVSize(),
get_state_block_size(state_true_offsets, gravity_potential_block) == f.gravityPotentialFes->GetTrueVSize(),
"The gravity-potential block does not match the potential "
"finite-element space."
);
}
void validate_gravity_context(const fem::FEM &f) {
MFEM_VERIFY(
f.gravityContext.b_form != nullptr,
"GravityFieldOperator requires the divergence operator."
);
MFEM_VERIFY(
f.gravityContext.BT != nullptr,
"GravityFieldOperator requires the transpose divergence operator."
);
MFEM_VERIFY(
f.quadratureFactory != nullptr,
"GravityFieldOperator requires the quadrature-rule factory."
);
MFEM_VERIFY(f.gravityContext.b_form != nullptr, "GravityFieldOperator requires the divergence operator.");
MFEM_VERIFY(f.gravityContext.BT != nullptr, "GravityFieldOperator requires the transpose divergence operator.");
MFEM_VERIFY(f.quadratureFactory != nullptr, "GravityFieldOperator requires the quadrature-rule factory.");
}
template <int index>
@@ -154,21 +115,13 @@ namespace {
const mfem::Array<int> &offsets,
const utils::blocks::value_block<index>
) {
MFEM_VERIFY(
index + 1 < offsets.Size(),
"Value block is not present in the supplied offset array."
);
MFEM_VERIFY(index + 1 < offsets.Size(), "Value block is not present in the supplied offset array.");
const int begin = offsets[index];
const int size = offsets[index + 1] - begin;
MFEM_VERIFY(
vector.Size() == offsets.Last(),
"Vector size does not match the value-block offsets."
);
return mfem::Vector(
const_cast<mfem::real_t *>(vector.GetData()) + begin, size
);
MFEM_VERIFY(vector.Size() == offsets.Last(), "Vector size does not match the value-block offsets.");
return mfem::Vector(const_cast<mfem::real_t *>(vector.GetData()) + begin, size);
}
template <int index>
@@ -181,10 +134,7 @@ namespace {
const int begin = offsets[block_id];
const int size = offsets[block_id + 1] - begin;
MFEM_VERIFY(
vector.Size() == offsets.Last(),
"The vector does not match the residual-block layout."
);
MFEM_VERIFY(vector.Size() == offsets.Last(), "The vector does not match the residual-block layout.");
mfem::Vector view;
view.MakeRef(const_cast<mfem::Vector &>(vector), begin, size);
@@ -197,18 +147,12 @@ namespace {
const mfem::Array<int> &offsets,
const utils::blocks::residual_block<index>
) {
MFEM_VERIFY(
index + 1 < offsets.Size(),
"Residual block is not present in the supplied offset array."
);
MFEM_VERIFY(index + 1 < offsets.Size(), "Residual block is not present in the supplied offset array.");
const int begin = offsets[index];
const int size = offsets[index + 1] - begin;
MFEM_VERIFY(
vector.Size() == offsets.Last(),
"Vector size does not match the residual-block offsets."
);
MFEM_VERIFY(vector.Size() == offsets.Last(), "Vector size does not match the residual-block offsets.");
return mfem::Vector(vector.GetData() + begin, size);
}
} // namespace
@@ -217,8 +161,7 @@ namespace mean_field::operators {
GravityFieldOperator::GravityFieldOperator(
fem::FEM &f,
const mapping::DomainMapperStateless &domain_mapper,
context::gravity_field::GravityFieldLinearizationContext
&linearization_context,
context::gravity_field::GravityFieldLinearizationContext &linearization_context,
const mfem::Array<int> &state_true_offsets,
GravityFieldJacobianOperator &jacobian
)
@@ -238,14 +181,12 @@ namespace mean_field::operators {
"displacement finite-element space."
);
MFEM_VERIFY(
f.smesh.exterior_coordinate != nullptr,
"GravityFieldOperator requires the STROID exterior coordinate."
f.smesh.exterior_coordinate != nullptr, "GravityFieldOperator requires the STROID exterior coordinate."
);
MFEM_VERIFY(
f.smesh.exterior_coordinate->space != nullptr,
"GravityFieldOperator requires the exterior-coordinate "
"finite-element "
"space."
f.smesh.exterior_coordinate->space != nullptr, "GravityFieldOperator requires the exterior-coordinate "
"finite-element "
"space."
);
MFEM_VERIFY(
f.smesh.exterior_coordinate->values != nullptr,
@@ -263,67 +204,46 @@ namespace mean_field::operators {
bool has_vacuum_domain = false;
for (int i = 0; i < f.mesh->attributes.Size(); ++i) {
if (f.mesh->attributes[i] ==
domain_mapper.GetVacuumElementAttribute()) {
if (f.mesh->attributes[i] == domain_mapper.GetVacuumElementAttribute()) {
has_vacuum_domain = true;
break;
}
}
MFEM_VERIFY(has_vacuum_domain, "GravityFieldOperator requires a compactified vacuum domain.");
MFEM_VERIFY(
has_vacuum_domain,
"GravityFieldOperator requires a compactified vacuum domain."
m_residual_true_offsets.Last() == Height(), "The gravity residual offsets do not match the operator height."
);
MFEM_VERIFY(
m_residual_true_offsets.Last() == Height(),
"The gravity residual offsets do not match the operator height."
);
MFEM_VERIFY(
m_state_true_offsets.Last() == Width(),
"The coupled state offsets do not match the operator width."
m_state_true_offsets.Last() == Width(), "The coupled state offsets do not match the operator width."
);
}
context::gravity_field::GravityFieldPreparationReport
GravityFieldOperator::Prepare(
context::gravity_field::GravityFieldPreparationReport GravityFieldOperator::Prepare(
const mfem::Vector &state,
const context::gravity_field::GravityFieldRevisions &revisions
) {
using form = utils::blocks::gravity_field_form;
constexpr auto density_block = utils::blocks::get_value_block<form>(
utils::blocks::density_field.mass_term
);
constexpr auto density_block = utils::blocks::get_value_block<form>(utils::blocks::density_field.mass_term);
constexpr auto displacement_block =
utils::blocks::get_value_block<form>(
utils::blocks::displacement_field.geometry_term
);
utils::blocks::get_value_block<form>(utils::blocks::displacement_field.geometry_term);
constexpr auto gravity_gradient_block =
utils::blocks::get_value_block<form>(
utils::blocks::gravity_field.gradient_term
);
utils::blocks::get_value_block<form>(utils::blocks::gravity_field.gradient_term);
constexpr auto gravity_potential_block =
utils::blocks::get_value_block<form>(
utils::blocks::gravity_field.poisson_term
);
utils::blocks::get_value_block<form>(utils::blocks::gravity_field.poisson_term);
MFEM_VERIFY(
state.Size() == Width(), "GravityFieldOperator received a "
"preparation state with the wrong size."
);
const mfem::Vector density = make_read_only_value_view(
state, m_state_true_offsets, density_block
);
const mfem::Vector displacement = make_read_only_value_view(
state, m_state_true_offsets, displacement_block
);
const mfem::Vector gravity_gradient = make_read_only_value_view(
state, m_state_true_offsets, gravity_gradient_block
);
const mfem::Vector gravity_potential = make_read_only_value_view(
state, m_state_true_offsets, gravity_potential_block
);
const mfem::Vector density = make_read_only_value_view(state, m_state_true_offsets, density_block);
const mfem::Vector displacement = make_read_only_value_view(state, m_state_true_offsets, displacement_block);
const mfem::Vector gravity_gradient =
make_read_only_value_view(state, m_state_true_offsets, gravity_gradient_block);
const mfem::Vector gravity_potential =
make_read_only_value_view(state, m_state_true_offsets, gravity_potential_block);
return m_linearization_context.Prepare(
{.density = density,
@@ -334,92 +254,65 @@ namespace mean_field::operators {
);
}
const mfem::Array<int> &
GravityFieldOperator::GetStateTrueOffsets() const noexcept {
const mfem::Array<int> &GravityFieldOperator::GetStateTrueOffsets() const noexcept {
return m_state_true_offsets;
}
const mfem::Array<int> &
GravityFieldOperator::GetResidualTrueOffsets() const noexcept {
const mfem::Array<int> &GravityFieldOperator::GetResidualTrueOffsets() const noexcept {
return m_residual_true_offsets;
}
void GravityFieldOperator::ApplyGravityUnknowns(
const mfem::Vector &gravity_gradient,
const mfem::Vector &gravity_potential,
const context::gravity_field::GravityFieldGeometryContext
&geometry_context,
const context::gravity_field::GravityFieldGeometryContext &geometry_context,
mfem::Vector &action
) const {
using form = utils::blocks::gravity_field_form;
constexpr auto gravity_gradient_residual_block =
utils::blocks::get_residual_block<form>(
utils::blocks::gravity_field.gradient_term
);
utils::blocks::get_residual_block<form>(utils::blocks::gravity_field.gradient_term);
constexpr auto gravity_poisson_residual_block =
utils::blocks::get_residual_block<form>(
utils::blocks::gravity_field.poisson_term
);
utils::blocks::get_residual_block<form>(utils::blocks::gravity_field.poisson_term);
MFEM_VERIFY(
geometry_context.IsPrepared(),
"GravityFieldOperator received an unprepared geometry context."
);
MFEM_VERIFY(geometry_context.IsPrepared(), "GravityFieldOperator received an unprepared geometry context.");
MFEM_VERIFY(
gravity_gradient.Size() == m_fem.gravityFluxFes->GetTrueVSize(),
"GravityFieldOperator received a gravity-gradient vector with the "
"wrong size."
);
MFEM_VERIFY(
gravity_potential.Size() ==
m_fem.gravityPotentialFes->GetTrueVSize(),
gravity_potential.Size() == m_fem.gravityPotentialFes->GetTrueVSize(),
"GravityFieldOperator received a gravity-potential vector with the "
"wrong size."
);
action.SetSize(Height());
action = 0.0;
action = 0.0;
mfem::Vector gravity_gradient_action = make_residual_view(
action, m_residual_true_offsets, gravity_gradient_residual_block
);
mfem::Vector gravity_poisson_action = make_residual_view(
action, m_residual_true_offsets, gravity_poisson_residual_block
);
mfem::Vector transpose_divergence_action(
gravity_gradient_action.Size()
);
mfem::Vector gravity_gradient_action =
make_residual_view(action, m_residual_true_offsets, gravity_gradient_residual_block);
mfem::Vector gravity_poisson_action =
make_residual_view(action, m_residual_true_offsets, gravity_poisson_residual_block);
mfem::Vector transpose_divergence_action(gravity_gradient_action.Size());
geometry_context.GetMassOperator().Mult(
gravity_gradient, gravity_gradient_action
);
m_fem.gravityContext.BT->Mult(
gravity_potential, transpose_divergence_action
);
geometry_context.GetMassOperator().Mult(gravity_gradient, gravity_gradient_action);
m_fem.gravityContext.BT->Mult(gravity_potential, transpose_divergence_action);
gravity_gradient_action += transpose_divergence_action;
m_fem.gravityContext.b_form->Mult(
gravity_gradient, gravity_poisson_action
);
m_fem.gravityContext.b_form->Mult(gravity_gradient, gravity_poisson_action);
}
void GravityFieldOperator::ApplyDensitySource(
const mfem::Vector &density,
const context::gravity_field::GravityFieldGeometryContext
&geometry_context,
const context::gravity_field::GravityFieldGeometryContext &geometry_context,
mfem::Vector &action
) const {
using form = utils::blocks::gravity_field_form;
constexpr auto gravity_poisson_residual_block =
utils::blocks::get_residual_block<form>(
utils::blocks::gravity_field.poisson_term
);
utils::blocks::get_residual_block<form>(utils::blocks::gravity_field.poisson_term);
MFEM_VERIFY(
geometry_context.IsPrepared(),
"GravityFieldOperator received an unprepared geometry context."
);
MFEM_VERIFY(geometry_context.IsPrepared(), "GravityFieldOperator received an unprepared geometry context.");
MFEM_VERIFY(
density.Size() == m_fem.densityFes->GetTrueVSize(),
"GravityFieldOperator received a density vector with the wrong "
@@ -427,14 +320,11 @@ namespace mean_field::operators {
);
action.SetSize(Height());
action = 0.0;
action = 0.0;
mfem::Vector gravity_poisson_action = make_residual_view(
action, m_residual_true_offsets, gravity_poisson_residual_block
);
geometry_context.GetSourceOperator().Mult(
density, gravity_poisson_action
);
mfem::Vector gravity_poisson_action =
make_residual_view(action, m_residual_true_offsets, gravity_poisson_residual_block);
geometry_context.GetSourceOperator().Mult(density, gravity_poisson_action);
}
void GravityFieldOperator::Mult(
@@ -445,51 +335,34 @@ namespace mean_field::operators {
using form = utils::blocks::gravity_field_form;
constexpr auto density_block = utils::blocks::get_value_block<form>(
utils::blocks::density_field.mass_term
);
constexpr auto density_block = utils::blocks::get_value_block<form>(utils::blocks::density_field.mass_term);
constexpr auto gravity_gradient_block =
utils::blocks::get_value_block<form>(
utils::blocks::gravity_field.gradient_term
);
utils::blocks::get_value_block<form>(utils::blocks::gravity_field.gradient_term);
constexpr auto gravity_potential_block =
utils::blocks::get_value_block<form>(
utils::blocks::gravity_field.poisson_term
);
utils::blocks::get_value_block<form>(utils::blocks::gravity_field.poisson_term);
MFEM_VERIFY(state.Size() == Width(), "GravityFieldOperator received a state with the wrong size.");
MFEM_VERIFY(
state.Size() == Width(),
"GravityFieldOperator received a state with the wrong size."
);
MFEM_VERIFY(
m_linearization_context.IsPrepared(),
"GravityFieldOperator must be prepared before Mult is called."
m_linearization_context.IsPrepared(), "GravityFieldOperator must be prepared before Mult is called."
);
const mfem::Vector density = make_read_only_value_view(
state, m_state_true_offsets, density_block
);
const mfem::Vector gravity_gradient = make_read_only_value_view(
state, m_state_true_offsets, gravity_gradient_block
);
const mfem::Vector gravity_potential = make_read_only_value_view(
state, m_state_true_offsets, gravity_potential_block
);
const context::gravity_field::GravityFieldGeometryContext
&geometry_context = m_linearization_context.GetGeometryContext();
const mfem::Vector density = make_read_only_value_view(state, m_state_true_offsets, density_block);
const mfem::Vector gravity_gradient =
make_read_only_value_view(state, m_state_true_offsets, gravity_gradient_block);
const mfem::Vector gravity_potential =
make_read_only_value_view(state, m_state_true_offsets, gravity_potential_block);
const context::gravity_field::GravityFieldGeometryContext &geometry_context =
m_linearization_context.GetGeometryContext();
mfem::Vector source;
ApplyGravityUnknowns(
gravity_gradient, gravity_potential, geometry_context, residual
);
ApplyGravityUnknowns(gravity_gradient, gravity_potential, geometry_context, residual);
ApplyDensitySource(density, geometry_context, source);
residual -= source;
}
context::gravity_field::GravityFieldLinearizationContext &
GravityFieldOperator::GetLinearizationContext() noexcept {
context::gravity_field::GravityFieldLinearizationContext &GravityFieldOperator::GetLinearizationContext() noexcept {
return m_linearization_context;
}
@@ -498,24 +371,21 @@ namespace mean_field::operators {
return m_linearization_context;
}
mfem::Operator &
GravityFieldOperator::GetGradient(const mfem::Vector &state) const {
mfem::Operator &GravityFieldOperator::GetGradient(const mfem::Vector &state) const {
MFEM_VERIFY(
state.Size() == Width(), "GravityFieldOperator received a "
"linearization state with the wrong size."
);
MFEM_VERIFY(
m_linearization_context.IsPrepared(),
"GravityFieldOperator must be prepared before GetGradient is "
"called."
m_linearization_context.IsPrepared(), "GravityFieldOperator must be prepared before GetGradient is "
"called."
);
return m_jacobian;
}
ReducedGravityFieldOperator::ReducedGravityFieldOperator(
GravityFieldOperator &gravity_field_operator,
context::gravity_field::GravityFieldGeometryContext
&gravity_field_geometry_context,
context::gravity_field::GravityFieldGeometryContext &gravity_field_geometry_context,
const mfem::Vector &displacement
)
: Operator(
@@ -523,31 +393,20 @@ namespace mean_field::operators {
gravity_field_operator.Height()
),
m_gravity_field_operator(gravity_field_operator),
m_gravity_true_offsets(
gravity_field_operator.GetResidualTrueOffsets()
),
m_gravity_true_offsets(gravity_field_operator.GetResidualTrueOffsets()),
m_gravity_field_geometry_context(gravity_field_geometry_context) {
using form = utils::blocks::gravity_field_form;
constexpr auto gravity_gradient_block =
utils::blocks::get_value_block<form>(
utils::blocks::gravity_field.gradient_term
);
utils::blocks::get_value_block<form>(utils::blocks::gravity_field.gradient_term);
constexpr auto gravity_potential_block =
utils::blocks::get_value_block<form>(
utils::blocks::gravity_field.poisson_term
);
utils::blocks::get_value_block<form>(utils::blocks::gravity_field.poisson_term);
constexpr auto gravity_gradient_residual_block =
utils::blocks::get_residual_block<form>(
utils::blocks::gravity_field.gradient_term
);
utils::blocks::get_residual_block<form>(utils::blocks::gravity_field.gradient_term);
constexpr auto gravity_poisson_residual_block =
utils::blocks::get_residual_block<form>(
utils::blocks::gravity_field.poisson_term
);
utils::blocks::get_residual_block<form>(utils::blocks::gravity_field.poisson_term);
const mfem::Array<int> &state_offsets =
m_gravity_field_operator.GetStateTrueOffsets();
const mfem::Array<int> &state_offsets = m_gravity_field_operator.GetStateTrueOffsets();
MFEM_VERIFY(
state_offsets.Size() == form::value_block_count + 1,
@@ -558,13 +417,8 @@ namespace mean_field::operators {
"ReducedGravityFieldOperator received an invalid gravity-residual "
"layout."
);
MFEM_VERIFY(
state_offsets[0] == 0, "The full-state offsets must begin at zero."
);
MFEM_VERIFY(
m_gravity_true_offsets[0] == 0,
"The reduced gravity offsets must begin at zero."
);
MFEM_VERIFY(state_offsets[0] == 0, "The full-state offsets must begin at zero.");
MFEM_VERIFY(m_gravity_true_offsets[0] == 0, "The reduced gravity offsets must begin at zero.");
MFEM_VERIFY(
state_offsets.Last() == m_gravity_field_operator.Width(),
"The full-state offsets do not match the gravity-field operator "
@@ -576,23 +430,17 @@ namespace mean_field::operators {
"operator "
"height."
);
MFEM_VERIFY(
Width() == Height(), "ReducedGravityFieldOperator must be square."
);
MFEM_VERIFY(Width() == Height(), "ReducedGravityFieldOperator must be square.");
const int full_gradient_size =
state_offsets[static_cast<int>(gravity_gradient_block) + 1] -
state_offsets[gravity_gradient_block];
state_offsets[static_cast<int>(gravity_gradient_block) + 1] - state_offsets[gravity_gradient_block];
const int full_potential_size =
state_offsets[static_cast<int>(gravity_potential_block) + 1] -
state_offsets[gravity_potential_block];
state_offsets[static_cast<int>(gravity_potential_block) + 1] - state_offsets[gravity_potential_block];
const int reduced_gradient_size =
m_gravity_true_offsets
[static_cast<int>(gravity_gradient_residual_block) + 1] -
m_gravity_true_offsets[static_cast<int>(gravity_gradient_residual_block) + 1] -
m_gravity_true_offsets[gravity_gradient_residual_block];
const int reduced_potential_size =
m_gravity_true_offsets
[static_cast<int>(gravity_poisson_residual_block) + 1] -
m_gravity_true_offsets[static_cast<int>(gravity_poisson_residual_block) + 1] -
m_gravity_true_offsets[gravity_poisson_residual_block];
MFEM_VERIFY(
@@ -609,31 +457,24 @@ namespace mean_field::operators {
SetDisplacement(displacement);
}
void ReducedGravityFieldOperator::SetDisplacement(
const mfem::Vector &displacement
) {
void ReducedGravityFieldOperator::SetDisplacement(const mfem::Vector &displacement) {
ValidateDisplacement(displacement);
context::gravity_field::DiscretizationRevision discretization_revision;
context::gravity_field::DisplacementRevision displacement_revision;
if (m_gravity_field_geometry_context.IsPrepared()) {
discretization_revision =
m_gravity_field_geometry_context.GetDiscretizationRevision();
displacement_revision =
m_gravity_field_geometry_context.GetDisplacementRevision();
discretization_revision = m_gravity_field_geometry_context.GetDiscretizationRevision();
displacement_revision = m_gravity_field_geometry_context.GetDisplacementRevision();
MFEM_VERIFY(
displacement_revision.value <
std::numeric_limits<std::uint64_t>::max(),
displacement_revision.value < std::numeric_limits<std::uint64_t>::max(),
"The reduced gravity displacement revision has overflowed."
);
++displacement_revision.value;
}
m_gravity_field_geometry_context.Prepare(
displacement, discretization_revision, displacement_revision
);
m_gravity_field_geometry_context.Prepare(displacement, discretization_revision, displacement_revision);
}
const mfem::Vector &ReducedGravityFieldOperator::GetDisplacement() const {
@@ -646,15 +487,12 @@ namespace mean_field::operators {
) const {
ValidateDensity(density);
m_gravity_field_operator.ApplyDensitySource(
density, m_gravity_field_geometry_context, right_hand_side
);
m_gravity_field_operator.ApplyDensitySource(density, m_gravity_field_geometry_context, right_hand_side);
MFEM_VERIFY(
right_hand_side.Size() == Height(),
"ReducedGravityFieldOperator produced a right-hand side with the "
"wrong "
"size."
right_hand_side.Size() == Height(), "ReducedGravityFieldOperator produced a right-hand side with the "
"wrong "
"size."
);
}
@@ -667,29 +505,19 @@ namespace mean_field::operators {
using form = utils::blocks::gravity_field_form;
constexpr auto gravity_gradient_residual_block =
utils::blocks::get_residual_block<form>(
utils::blocks::gravity_field.gradient_term
);
utils::blocks::get_residual_block<form>(utils::blocks::gravity_field.gradient_term);
constexpr auto gravity_poisson_residual_block =
utils::blocks::get_residual_block<form>(
utils::blocks::gravity_field.poisson_term
);
utils::blocks::get_residual_block<form>(utils::blocks::gravity_field.poisson_term);
ValidateGravityState(gravity_state);
const mfem::Vector gravity_gradient_true = make_read_only_residual_view(
gravity_state, m_gravity_true_offsets,
gravity_gradient_residual_block
);
const mfem::Vector gravity_gradient_true =
make_read_only_residual_view(gravity_state, m_gravity_true_offsets, gravity_gradient_residual_block);
const mfem::Vector gravity_potential_true =
make_read_only_residual_view(
gravity_state, m_gravity_true_offsets,
gravity_poisson_residual_block
);
make_read_only_residual_view(gravity_state, m_gravity_true_offsets, gravity_poisson_residual_block);
m_gravity_field_operator.ApplyGravityUnknowns(
gravity_gradient_true, gravity_potential_true,
m_gravity_field_geometry_context, action
gravity_gradient_true, gravity_potential_true, m_gravity_field_geometry_context, action
);
MFEM_VERIFY(
@@ -698,18 +526,15 @@ namespace mean_field::operators {
);
}
GravityFieldOperator &
ReducedGravityFieldOperator::GetGravityFieldOperator() noexcept {
GravityFieldOperator &ReducedGravityFieldOperator::GetGravityFieldOperator() noexcept {
return m_gravity_field_operator;
}
const GravityFieldOperator &
ReducedGravityFieldOperator::GetGravityFieldOperator() const noexcept {
const GravityFieldOperator &ReducedGravityFieldOperator::GetGravityFieldOperator() const noexcept {
return m_gravity_field_operator;
}
context::gravity_field::GravityFieldGeometryContext &
ReducedGravityFieldOperator::GetGeometryContext() noexcept {
context::gravity_field::GravityFieldGeometryContext &ReducedGravityFieldOperator::GetGeometryContext() noexcept {
return m_gravity_field_geometry_context;
}
@@ -718,73 +543,53 @@ namespace mean_field::operators {
return m_gravity_field_geometry_context;
}
const mfem::Array<int> &
ReducedGravityFieldOperator::GetGravityTrueOffsets() const noexcept {
const mfem::Array<int> &ReducedGravityFieldOperator::GetGravityTrueOffsets() const noexcept {
return m_gravity_true_offsets;
}
void ReducedGravityFieldOperator::ValidateDisplacement(
const mfem::Vector &displacement
) const {
void ReducedGravityFieldOperator::ValidateDisplacement(const mfem::Vector &displacement) const {
using form = utils::blocks::gravity_field_form;
constexpr auto displacement_block =
utils::blocks::get_value_block<form>(
utils::blocks::displacement_field.geometry_term
);
utils::blocks::get_value_block<form>(utils::blocks::displacement_field.geometry_term);
const mfem::Array<int> &state_offsets =
m_gravity_field_operator.GetStateTrueOffsets();
const mfem::Array<int> &state_offsets = m_gravity_field_operator.GetStateTrueOffsets();
const int expected_size =
state_offsets[static_cast<int>(displacement_block) + 1] -
state_offsets[displacement_block];
state_offsets[static_cast<int>(displacement_block) + 1] - state_offsets[displacement_block];
MFEM_VERIFY(
displacement.Size() == expected_size,
"ReducedGravityFieldOperator received a displacement with the "
"wrong "
"size."
displacement.Size() == expected_size, "ReducedGravityFieldOperator received a displacement with the "
"wrong "
"size."
);
for (int i = 0; i < displacement.Size(); ++i) {
MFEM_VERIFY(
std::isfinite(displacement(i)),
"ReducedGravityFieldOperator received a non-finite "
"displacement "
"value."
std::isfinite(displacement(i)), "ReducedGravityFieldOperator received a non-finite "
"displacement "
"value."
);
}
}
void ReducedGravityFieldOperator::ValidateDensity(
const mfem::Vector &density
) const {
void ReducedGravityFieldOperator::ValidateDensity(const mfem::Vector &density) const {
using form = utils::blocks::gravity_field_form;
constexpr auto density_block = utils::blocks::get_value_block<form>(
utils::blocks::density_field.mass_term
);
constexpr auto density_block = utils::blocks::get_value_block<form>(utils::blocks::density_field.mass_term);
const mfem::Array<int> &state_offsets =
m_gravity_field_operator.GetStateTrueOffsets();
const int expected_size =
state_offsets[static_cast<int>(density_block) + 1] -
state_offsets[density_block];
const mfem::Array<int> &state_offsets = m_gravity_field_operator.GetStateTrueOffsets();
const int expected_size = state_offsets[static_cast<int>(density_block) + 1] - state_offsets[density_block];
MFEM_VERIFY(
density.Size() == expected_size,
"ReducedGravityFieldOperator received a density with the wrong "
"size."
density.Size() == expected_size, "ReducedGravityFieldOperator received a density with the wrong "
"size."
);
}
void ReducedGravityFieldOperator::ValidateGravityState(
const mfem::Vector &gravity_state
) const {
void ReducedGravityFieldOperator::ValidateGravityState(const mfem::Vector &gravity_state) const {
MFEM_VERIFY(
gravity_state.Size() == Width(),
"ReducedGravityFieldOperator received "
"a gravity state with the wrong size."
gravity_state.Size() == Width(), "ReducedGravityFieldOperator received "
"a gravity state with the wrong size."
);
}
} // namespace mean_field::operators

View File

@@ -15,9 +15,7 @@ namespace {
) {
const int offset = offsets[index];
const int size = offsets[index + 1] - offset;
return mfem::Vector(
const_cast<mfem::real_t *>(vector.GetData()) + offset, size
);
return mfem::Vector(const_cast<mfem::real_t *>(vector.GetData()) + offset, size);
}
template <int index>
@@ -56,10 +54,7 @@ namespace {
MFEM_VERIFY(offsets[0] == 0, "Block offsets must begin at zero.");
for (int i = 0; i < block_count; ++i)
MFEM_VERIFY(
offsets[i + 1] >= offsets[i],
"Block offsets must be nondecreasing."
);
MFEM_VERIFY(offsets[i + 1] >= offsets[i], "Block offsets must be nondecreasing.");
}
void validate_layout(
@@ -70,29 +65,18 @@ namespace {
using form = mean_field::utils::blocks::gravity_field_form;
constexpr auto density_block =
mean_field::utils::blocks::get_value_block<form>(
mean_field::utils::blocks::density_field.mass_term
);
constexpr auto displacement_block =
mean_field::utils::blocks::get_value_block<form>(
mean_field::utils::blocks::displacement_field.geometry_term
);
mean_field::utils::blocks::get_value_block<form>(mean_field::utils::blocks::density_field.mass_term);
constexpr auto displacement_block = mean_field::utils::blocks::get_value_block<form>(
mean_field::utils::blocks::displacement_field.geometry_term
);
constexpr auto gravity_gradient_block =
mean_field::utils::blocks::get_value_block<form>(
mean_field::utils::blocks::gravity_field.gradient_term
);
mean_field::utils::blocks::get_value_block<form>(mean_field::utils::blocks::gravity_field.gradient_term);
constexpr auto gravity_potential_block =
mean_field::utils::blocks::get_value_block<form>(
mean_field::utils::blocks::gravity_field.poisson_term
);
mean_field::utils::blocks::get_value_block<form>(mean_field::utils::blocks::gravity_field.poisson_term);
constexpr auto gravity_gradient_residual_block =
mean_field::utils::blocks::get_residual_block<form>(
mean_field::utils::blocks::gravity_field.gradient_term
);
mean_field::utils::blocks::get_residual_block<form>(mean_field::utils::blocks::gravity_field.gradient_term);
constexpr auto gravity_poisson_residual_block =
mean_field::utils::blocks::get_residual_block<form>(
mean_field::utils::blocks::gravity_field.poisson_term
);
mean_field::utils::blocks::get_residual_block<form>(mean_field::utils::blocks::gravity_field.poisson_term);
validate_offsets(
state_offsets, form::value_block_count,
@@ -106,33 +90,27 @@ namespace {
);
MFEM_VERIFY(
get_block_size(state_offsets, density_block) ==
f.densityFes->GetTrueVSize(),
get_block_size(state_offsets, density_block) == f.densityFes->GetTrueVSize(),
"The Jacobian density block has the wrong size."
);
MFEM_VERIFY(
get_block_size(state_offsets, displacement_block) ==
f.displacementFes->GetTrueVSize(),
get_block_size(state_offsets, displacement_block) == f.displacementFes->GetTrueVSize(),
"The Jacobian displacement block has the wrong size."
);
MFEM_VERIFY(
get_block_size(state_offsets, gravity_gradient_block) ==
f.gravityFluxFes->GetTrueVSize(),
get_block_size(state_offsets, gravity_gradient_block) == f.gravityFluxFes->GetTrueVSize(),
"The Jacobian gravity-gradient block has the wrong size."
);
MFEM_VERIFY(
get_block_size(state_offsets, gravity_potential_block) ==
f.gravityPotentialFes->GetTrueVSize(),
get_block_size(state_offsets, gravity_potential_block) == f.gravityPotentialFes->GetTrueVSize(),
"The Jacobian gravity-potential block has the wrong size."
);
MFEM_VERIFY(
get_block_size(residual_offsets, gravity_gradient_residual_block) ==
f.gravityFluxFes->GetTrueVSize(),
get_block_size(residual_offsets, gravity_gradient_residual_block) == f.gravityFluxFes->GetTrueVSize(),
"The Jacobian gradient-residual block has the wrong size."
);
MFEM_VERIFY(
get_block_size(residual_offsets, gravity_poisson_residual_block) ==
f.gravityPotentialFes->GetTrueVSize(),
get_block_size(residual_offsets, gravity_poisson_residual_block) == f.gravityPotentialFes->GetTrueVSize(),
"The Jacobian Poisson-residual block has the wrong size."
);
}
@@ -142,8 +120,7 @@ namespace mean_field::operators {
GravityFieldJacobianOperator::GravityFieldJacobianOperator(
fem::FEM &f,
const mapping::DomainMapperStateless &domain_mapper,
const context::gravity_field::GravityFieldLinearizationContext
&linearization_context,
const context::gravity_field::GravityFieldLinearizationContext &linearization_context,
const mfem::Array<int> &state_true_offsets,
const mfem::Array<int> &residual_true_offsets
)
@@ -157,37 +134,30 @@ namespace mean_field::operators {
m_state_true_offsets(state_true_offsets),
m_residual_true_offsets(residual_true_offsets) {
MFEM_VERIFY(
f.densityFes != nullptr,
"GravityFieldJacobianOperator requires the density finite-element "
"space."
f.densityFes != nullptr, "GravityFieldJacobianOperator requires the density finite-element "
"space."
);
MFEM_VERIFY(
f.gravityPotentialFes != nullptr,
"GravityFieldJacobianOperator requires the gravity-potential "
"finite-element space."
f.gravityPotentialFes != nullptr, "GravityFieldJacobianOperator requires the gravity-potential "
"finite-element space."
);
MFEM_VERIFY(
f.gravityFluxFes != nullptr,
"GravityFieldJacobianOperator requires the "
"gravity-gradient finite-element space."
f.gravityFluxFes != nullptr, "GravityFieldJacobianOperator requires the "
"gravity-gradient finite-element space."
);
MFEM_VERIFY(
f.displacementFes != nullptr,
"GravityFieldJacobianOperator requires the "
"displacement finite-element space."
f.displacementFes != nullptr, "GravityFieldJacobianOperator requires the "
"displacement finite-element space."
);
MFEM_VERIFY(
f.gravityContext.b_form != nullptr,
"GravityFieldJacobianOperator requires the divergence operator."
f.gravityContext.b_form != nullptr, "GravityFieldJacobianOperator requires the divergence operator."
);
MFEM_VERIFY(
f.gravityContext.BT != nullptr,
"GravityFieldJacobianOperator requires the transpose divergence "
"operator."
f.gravityContext.BT != nullptr, "GravityFieldJacobianOperator requires the transpose divergence "
"operator."
);
MFEM_VERIFY(
f.quadratureFactory != nullptr,
"GravityFieldJacobianOperator requires the quadrature-rule factory."
f.quadratureFactory != nullptr, "GravityFieldJacobianOperator requires the quadrature-rule factory."
);
MFEM_VERIFY(
domain_mapper.GetDimension() == f.mesh->Dimension(),
@@ -204,107 +174,74 @@ namespace mean_field::operators {
mfem::Vector &action
) const {
MFEM_VERIFY(
m_linearization_context.IsPrepared(),
"GravityFieldJacobianOperator requires a prepared linearization "
"context."
m_linearization_context.IsPrepared(), "GravityFieldJacobianOperator requires a prepared linearization "
"context."
);
MFEM_VERIFY(
direction.Size() == Width(),
"GravityFieldJacobianOperator received a direction with the wrong "
"size."
direction.Size() == Width(), "GravityFieldJacobianOperator received a direction with the wrong "
"size."
);
using form = utils::blocks::gravity_field_form;
constexpr auto density_block = utils::blocks::get_value_block<form>(
utils::blocks::density_field.mass_term
);
constexpr auto density_block = utils::blocks::get_value_block<form>(utils::blocks::density_field.mass_term);
constexpr auto displacement_block =
utils::blocks::get_value_block<form>(
utils::blocks::displacement_field.geometry_term
);
utils::blocks::get_value_block<form>(utils::blocks::displacement_field.geometry_term);
constexpr auto gravity_gradient_block =
utils::blocks::get_value_block<form>(
utils::blocks::gravity_field.gradient_term
);
utils::blocks::get_value_block<form>(utils::blocks::gravity_field.gradient_term);
constexpr auto gravity_potential_block =
utils::blocks::get_value_block<form>(
utils::blocks::gravity_field.poisson_term
);
utils::blocks::get_value_block<form>(utils::blocks::gravity_field.poisson_term);
constexpr auto gravity_gradient_residual_block =
utils::blocks::get_residual_block<form>(
utils::blocks::gravity_field.gradient_term
);
utils::blocks::get_residual_block<form>(utils::blocks::gravity_field.gradient_term);
constexpr auto gravity_poisson_residual_block =
utils::blocks::get_residual_block<form>(
utils::blocks::gravity_field.poisson_term
);
utils::blocks::get_residual_block<form>(utils::blocks::gravity_field.poisson_term);
const context::gravity_field::GravityFieldGeometryContext
&geometry_context = m_linearization_context.GetGeometryContext();
const mfem::Vector &density = m_linearization_context.GetDensity();
const mfem::Vector &displacement = geometry_context.GetDisplacement();
const mfem::Vector &gravity_gradient =
m_linearization_context.GetGravityGradient();
const context::gravity_field::GravityFieldGeometryContext &geometry_context =
m_linearization_context.GetGeometryContext();
const mfem::Vector &density = m_linearization_context.GetDensity();
const mfem::Vector &displacement = geometry_context.GetDisplacement();
const mfem::Vector &gravity_gradient = m_linearization_context.GetGravityGradient();
const mfem::Vector density_direction = make_read_only_value_view(
direction, m_state_true_offsets, density_block
);
const mfem::Vector displacement_direction = make_read_only_value_view(
direction, m_state_true_offsets, displacement_block
);
const mfem::Vector density_direction =
make_read_only_value_view(direction, m_state_true_offsets, density_block);
const mfem::Vector displacement_direction =
make_read_only_value_view(direction, m_state_true_offsets, displacement_block);
const mfem::Vector gravity_gradient_direction =
make_read_only_value_view(
direction, m_state_true_offsets, gravity_gradient_block
);
make_read_only_value_view(direction, m_state_true_offsets, gravity_gradient_block);
const mfem::Vector gravity_potential_direction =
make_read_only_value_view(
direction, m_state_true_offsets, gravity_potential_block
);
make_read_only_value_view(direction, m_state_true_offsets, gravity_potential_block);
action.SetSize(Height());
action = 0.0;
action = 0.0;
mfem::Vector gravity_gradient_action = make_residual_view(
action, m_residual_true_offsets, gravity_gradient_residual_block
);
mfem::Vector gravity_poisson_action = make_residual_view(
action, m_residual_true_offsets, gravity_poisson_residual_block
);
mfem::Vector gravity_gradient_action =
make_residual_view(action, m_residual_true_offsets, gravity_gradient_residual_block);
mfem::Vector gravity_poisson_action =
make_residual_view(action, m_residual_true_offsets, gravity_poisson_residual_block);
mfem::Vector transpose_divergence_action;
mfem::Vector source_action;
mfem::Vector mass_variation_action;
mfem::Vector source_variation_action;
geometry_context.GetMassOperator().Mult(
gravity_gradient_direction, gravity_gradient_action
);
geometry_context.GetSourceOperator().Mult(
density_direction, source_action
);
geometry_context.GetMassOperator().Mult(gravity_gradient_direction, gravity_gradient_action);
geometry_context.GetSourceOperator().Mult(density_direction, source_action);
kernels::apply_mapped_hdiv_mass_variation(
m_fem, m_domain_mapper, gravity_gradient, displacement,
displacement_direction, mass_variation_action
m_fem, m_domain_mapper, gravity_gradient, displacement, displacement_direction, mass_variation_action
);
kernels::apply_mapped_source_variation(
m_fem, m_domain_mapper, density, displacement,
displacement_direction, source_variation_action
m_fem, m_domain_mapper, density, displacement, displacement_direction, source_variation_action
);
transpose_divergence_action.SetSize(gravity_gradient_action.Size());
m_fem.gravityContext.BT->Mult(
gravity_potential_direction, transpose_divergence_action
);
m_fem.gravityContext.BT->Mult(gravity_potential_direction, transpose_divergence_action);
gravity_gradient_action += transpose_divergence_action;
gravity_gradient_action += mass_variation_action;
m_fem.gravityContext.b_form->Mult(
gravity_gradient_direction, gravity_poisson_action
);
m_fem.gravityContext.b_form->Mult(gravity_gradient_direction, gravity_poisson_action);
gravity_poisson_action -= source_action;
gravity_poisson_action -= source_variation_action;

View File

@@ -8,24 +8,29 @@ module;
module mean_field;
import :operators.kernels.barotropic_closure;
import :field.registry;
import :utils.domain;
namespace {
using DomainSchema = mean_field::utils::domain::CoreEnvelopeVacuumDomainSchema;
using ClosureDomain = mean_field::field::FieldDomainT<mean_field::field::Density>;
enum class ClosureAction { residual, density, enthalpy };
[[nodiscard]] bool element_is_in_closure_support(const int attribute) {
return DomainSchema::template attribute_belongs_to<ClosureDomain>(attribute);
}
void true_to_local(
const mfem::ParFiniteElementSpace &finiteElementSpace,
const mfem::Vector &trueVector,
mfem::Vector &localVector
) {
MFEM_VERIFY(
trueVector.Size() == finiteElementSpace.GetTrueVSize(),
"True vector has the wrong size."
);
MFEM_VERIFY(trueVector.Size() == finiteElementSpace.GetTrueVSize(), "True vector has the wrong size.");
localVector.SetSize(finiteElementSpace.GetVSize());
const mfem::Operator *prolongation =
finiteElementSpace.GetProlongationMatrix();
const mfem::Operator *prolongation = finiteElementSpace.GetProlongationMatrix();
if (prolongation != nullptr) {
prolongation->Mult(trueVector, localVector);
@@ -39,16 +44,12 @@ namespace {
const mfem::Vector &localVector,
mfem::Vector &trueVector
) {
MFEM_VERIFY(
localVector.Size() == finiteElementSpace.GetVSize(),
"Local vector has the wrong size."
);
MFEM_VERIFY(localVector.Size() == finiteElementSpace.GetVSize(), "Local vector has the wrong size.");
trueVector.SetSize(finiteElementSpace.GetTrueVSize());
trueVector = 0.0;
trueVector = 0.0;
const mfem::Operator *prolongation =
finiteElementSpace.GetProlongationMatrix();
const mfem::Operator *prolongation = finiteElementSpace.GetProlongationMatrix();
if (prolongation != nullptr) {
prolongation->MultTranspose(localVector, trueVector);
@@ -57,19 +58,13 @@ namespace {
}
}
int get_eos_extra_order(
const mean_field::physics::PolytropicBarotrope &barotrope
) {
const double extraOrder =
(barotrope.polytropic_index() - 1.0) *
static_cast<double>(
mean_field::field::Enthalpy::Scalar::familyOrder
);
int get_eos_extra_order(const mean_field::eos::Polytrope &barotrope) {
const double extraOrder = (barotrope.polytropic_index() - 1.0) *
static_cast<double>(mean_field::field::Enthalpy::Scalar::familyOrder);
MFEM_VERIFY(
std::isfinite(extraOrder) && extraOrder >= 0.0 &&
extraOrder <=
static_cast<double>(std::numeric_limits<int>::max()),
extraOrder <= static_cast<double>(std::numeric_limits<int>::max()),
"The EOS effective polynomial order is invalid."
);
@@ -78,44 +73,37 @@ namespace {
const mfem::IntegrationRule &get_eos_rule(
const mean_field::fem::FEM &f,
const mean_field::physics::PolytropicBarotrope &barotrope,
const mean_field::eos::Polytrope &barotrope,
const mfem::FiniteElement &densityElement,
const mfem::FiniteElement &enthalpyElement,
const mfem::ElementTransformation &transformation
) {
using EnthalpyField =
mean_field::field::Field<mean_field::field::Enthalpy>;
using EnthalpyField = mean_field::field::Field<mean_field::field::Enthalpy>;
MFEM_VERIFY(
densityElement.GetOrder() ==
mean_field::field::Density::Scalar::familyOrder,
densityElement.GetOrder() == mean_field::field::Density::Scalar::familyOrder,
"The EOS test element does not match the "
"registered density field."
);
MFEM_VERIFY(
enthalpyElement.GetOrder() ==
mean_field::field::Enthalpy::Scalar::familyOrder,
enthalpyElement.GetOrder() == mean_field::field::Enthalpy::Scalar::familyOrder,
"The EOS trial element does not match the "
"registered enthalpy field."
);
const mean_field::quadrature::Query query = EnthalpyField::make_query<
mean_field::field::Enthalpy::Form::EosClosureSource>(
mean_field::quadrature::QuadratureRole::discretization,
transformation.OrderW(),
std::array<int, 1>{get_eos_extra_order(barotrope)},
mean_field::utils::DOMAINS::STELLAR,
mean_field::quadrature::MappingKind::general
);
const mean_field::quadrature::Query query =
EnthalpyField::make_query<mean_field::field::Enthalpy::Form::EosClosureSource>(
mean_field::quadrature::QuadratureRole::discretization, transformation.OrderW(),
std::array<int, 1>{get_eos_extra_order(barotrope)}, mean_field::utils::DOMAINS::STELLAR,
mean_field::quadrature::MappingKind::general
);
const auto resolution =
f.quadratureFactory->get(query, transformation.GetGeometryType());
const auto resolution = f.quadratureFactory->get(query, transformation.GetGeometryType());
MFEM_VERIFY(
resolution.integration_rule != nullptr,
"The quadrature policy did not return an "
"EOS-closure integration rule."
resolution.integration_rule != nullptr, "The quadrature policy did not return an "
"EOS-closure integration rule."
);
return *resolution.integration_rule;
@@ -126,54 +114,44 @@ namespace {
const mean_field::mapping::DomainMapperStateless &domainMapper,
const mfem::Vector &displacementTrue
) {
MFEM_VERIFY(f.mesh != nullptr, "The EOS closure kernel requires a mesh.");
MFEM_VERIFY(
f.mesh != nullptr, "The EOS closure kernel requires a mesh."
f.densityFes != nullptr, "The EOS closure kernel requires the density "
"finite-element space."
);
MFEM_VERIFY(
f.densityFes != nullptr,
"The EOS closure kernel requires the density "
"finite-element space."
f.enthalpyFes != nullptr, "The EOS closure kernel requires the enthalpy "
"finite-element space."
);
MFEM_VERIFY(
f.enthalpyFes != nullptr,
"The EOS closure kernel requires the enthalpy "
"finite-element space."
f.displacementFes != nullptr, "The EOS closure kernel requires the displacement "
"finite-element space."
);
MFEM_VERIFY(
f.displacementFes != nullptr,
"The EOS closure kernel requires the displacement "
"finite-element space."
f.compactificationFes != nullptr, "The EOS closure kernel requires the "
"compactification finite-element space."
);
MFEM_VERIFY(
f.compactificationFes != nullptr,
"The EOS closure kernel requires the "
"compactification finite-element space."
f.compactificationCoordinate != nullptr, "The EOS closure kernel requires the "
"compactification coordinate."
);
MFEM_VERIFY(
f.compactificationCoordinate != nullptr,
"The EOS closure kernel requires the "
"compactification coordinate."
f.quadratureFactory != nullptr, "The EOS closure kernel requires the quadrature "
"rule factory."
);
MFEM_VERIFY(
f.quadratureFactory != nullptr,
"The EOS closure kernel requires the quadrature "
"rule factory."
displacementTrue.Size() == f.displacementFes->GetTrueVSize(), "The displacement vector has the wrong size."
);
MFEM_VERIFY(
displacementTrue.Size() == f.displacementFes->GetTrueVSize(),
"The displacement vector has the wrong size."
);
MFEM_VERIFY(
domainMapper.GetDimension() == f.mesh->Dimension(),
"The domain-mapper dimension does not match "
"the mesh dimension."
domainMapper.GetDimension() == f.mesh->Dimension(), "The domain-mapper dimension does not match "
"the mesh dimension."
);
}
void apply_closure_action(
const mean_field::fem::FEM &f,
const mean_field::mapping::DomainMapperStateless &domainMapper,
const mean_field::physics::PolytropicBarotrope &barotrope,
const mean_field::eos::Polytrope &barotrope,
const ClosureAction closureAction,
const mfem::Vector *densityInputTrue,
const mfem::Vector *baseEnthalpyTrue,
@@ -183,29 +161,23 @@ namespace {
) {
validate_common_inputs(f, domainMapper, displacementTrue);
if (closureAction == ClosureAction::residual ||
closureAction == ClosureAction::density) {
if (closureAction == ClosureAction::residual || closureAction == ClosureAction::density) {
MFEM_VERIFY(
densityInputTrue != nullptr &&
densityInputTrue->Size() == f.densityFes->GetTrueVSize(),
densityInputTrue != nullptr && densityInputTrue->Size() == f.densityFes->GetTrueVSize(),
"The density input has the wrong size."
);
}
if (closureAction == ClosureAction::residual ||
closureAction == ClosureAction::enthalpy) {
if (closureAction == ClosureAction::residual || closureAction == ClosureAction::enthalpy) {
MFEM_VERIFY(
baseEnthalpyTrue != nullptr &&
baseEnthalpyTrue->Size() == f.enthalpyFes->GetTrueVSize(),
baseEnthalpyTrue != nullptr && baseEnthalpyTrue->Size() == f.enthalpyFes->GetTrueVSize(),
"The base enthalpy has the wrong size."
);
}
if (closureAction == ClosureAction::enthalpy) {
MFEM_VERIFY(
enthalpyVariationTrue != nullptr &&
enthalpyVariationTrue->Size() ==
f.enthalpyFes->GetTrueVSize(),
enthalpyVariationTrue != nullptr && enthalpyVariationTrue->Size() == f.enthalpyFes->GetTrueVSize(),
"The enthalpy variation has the wrong size."
);
}
@@ -224,9 +196,7 @@ namespace {
}
if (enthalpyVariationTrue != nullptr) {
true_to_local(
*f.enthalpyFes, *enthalpyVariationTrue, enthalpyVariationLocal
);
true_to_local(*f.enthalpyFes, *enthalpyVariationTrue, enthalpyVariationLocal);
}
true_to_local(*f.displacementFes, displacementTrue, displacementLocal);
@@ -234,9 +204,7 @@ namespace {
mfem::Vector localAction(f.densityFes->GetVSize());
localAction = 0.0;
mean_field::mapping::DomainMapperStateless::Workspace workspace(
f.mesh->Dimension()
);
mean_field::mapping::DomainMapperStateless::Workspace workspace(f.mesh->Dimension());
mfem::Array<int> densityDofs;
mfem::Array<int> enthalpyDofs;
@@ -253,150 +221,105 @@ namespace {
mfem::Vector densityShape;
mfem::Vector enthalpyShape;
const int vacuumAttribute = domainMapper.GetVacuumElementAttribute();
for (int elementId = 0; elementId < f.mesh->GetNE(); ++elementId) {
mfem::ElementTransformation *transformation =
f.mesh->GetElementTransformation(elementId);
mfem::ElementTransformation *transformation = f.mesh->GetElementTransformation(elementId);
MFEM_VERIFY(
transformation != nullptr,
"The EOS closure kernel received a null "
"element transformation."
transformation != nullptr, "The EOS closure kernel received a null "
"element transformation."
);
if (transformation->Attribute == vacuumAttribute) {
if (!element_is_in_closure_support(transformation->Attribute)) {
continue;
}
const mfem::FiniteElement &densityElement =
*f.densityFes->GetFE(elementId);
const mfem::FiniteElement &enthalpyElement =
*f.enthalpyFes->GetFE(elementId);
const mfem::FiniteElement &displacementElement =
*f.displacementFes->GetFE(elementId);
const mfem::FiniteElement &compactificationElement =
*f.compactificationFes->GetFE(elementId);
const mfem::FiniteElement &densityElement = *f.densityFes->GetFE(elementId);
const mfem::FiniteElement &enthalpyElement = *f.enthalpyFes->GetFE(elementId);
const mfem::FiniteElement &displacementElement = *f.displacementFes->GetFE(elementId);
const mfem::FiniteElement &compactificationElement = *f.compactificationFes->GetFE(elementId);
mfem::DofTransformation *densityDofTransformation =
f.densityFes->GetElementDofs(elementId, densityDofs);
mfem::DofTransformation *densityDofTransformation = f.densityFes->GetElementDofs(elementId, densityDofs);
mfem::DofTransformation *enthalpyDofTransformation =
f.enthalpyFes->GetElementDofs(elementId, enthalpyDofs);
mfem::DofTransformation *enthalpyDofTransformation = f.enthalpyFes->GetElementDofs(elementId, enthalpyDofs);
mfem::DofTransformation *displacementDofTransformation =
f.displacementFes->GetElementVDofs(elementId, displacementDofs);
mfem::DofTransformation *compactificationDofTransformation =
f.compactificationFes->GetElementDofs(
elementId, compactificationDofs
);
f.compactificationFes->GetElementDofs(elementId, compactificationDofs);
if (densityInputTrue != nullptr) {
densityInputLocal.GetSubVector(
densityDofs, elementDensityInput
);
densityInputLocal.GetSubVector(densityDofs, elementDensityInput);
if (densityDofTransformation != nullptr) {
densityDofTransformation->InvTransformPrimal(
elementDensityInput
);
densityDofTransformation->InvTransformPrimal(elementDensityInput);
}
}
if (baseEnthalpyTrue != nullptr) {
baseEnthalpyLocal.GetSubVector(
enthalpyDofs, elementBaseEnthalpy
);
baseEnthalpyLocal.GetSubVector(enthalpyDofs, elementBaseEnthalpy);
if (enthalpyDofTransformation != nullptr) {
enthalpyDofTransformation->InvTransformPrimal(
elementBaseEnthalpy
);
enthalpyDofTransformation->InvTransformPrimal(elementBaseEnthalpy);
}
}
if (enthalpyVariationTrue != nullptr) {
enthalpyVariationLocal.GetSubVector(
enthalpyDofs, elementEnthalpyVariation
);
enthalpyVariationLocal.GetSubVector(enthalpyDofs, elementEnthalpyVariation);
if (enthalpyDofTransformation != nullptr) {
enthalpyDofTransformation->InvTransformPrimal(
elementEnthalpyVariation
);
enthalpyDofTransformation->InvTransformPrimal(elementEnthalpyVariation);
}
}
displacementLocal.GetSubVector(
displacementDofs, elementDisplacement
);
displacementLocal.GetSubVector(displacementDofs, elementDisplacement);
f.compactificationCoordinate->GetSubVector(
compactificationDofs, elementCompactification
);
f.compactificationCoordinate->GetSubVector(compactificationDofs, elementCompactification);
if (displacementDofTransformation != nullptr) {
displacementDofTransformation->InvTransformPrimal(
elementDisplacement
);
displacementDofTransformation->InvTransformPrimal(elementDisplacement);
}
if (compactificationDofTransformation != nullptr) {
compactificationDofTransformation->InvTransformPrimal(
elementCompactification
);
compactificationDofTransformation->InvTransformPrimal(elementCompactification);
}
const mean_field::mapping::ElementDisplacementData
displacementData = mean_field::mapping::
ElementDisplacementDataFromElementVDofs(
displacementElement, elementDisplacement
);
const mean_field::mapping::ElementDisplacementData displacementData =
mean_field::mapping::ElementDisplacementDataFromElementVDofs(displacementElement, elementDisplacement);
const mean_field::mapping::ElementCompactificationData
compactificationData(
compactificationElement, elementCompactification
);
const mean_field::mapping::ElementCompactificationData compactificationData(
compactificationElement, elementCompactification
);
const mean_field::mapping::ElementMappingData mappingData{
.displacement = displacementData,
.compactification = compactificationData
.displacement = displacementData, .compactification = compactificationData
};
densityShape.SetSize(densityElement.GetDof());
enthalpyShape.SetSize(enthalpyElement.GetDof());
elementAction.SetSize(densityElement.GetDof());
elementAction = 0.0;
elementAction = 0.0;
const mfem::IntegrationRule &integrationRule = get_eos_rule(
f, barotrope, densityElement, enthalpyElement, *transformation
);
const mfem::IntegrationRule &integrationRule =
get_eos_rule(f, barotrope, densityElement, enthalpyElement, *transformation);
for (int quadratureIndex = 0;
quadratureIndex < integrationRule.GetNPoints();
++quadratureIndex) {
const mfem::IntegrationPoint &integrationPoint =
integrationRule.IntPoint(quadratureIndex);
for (int quadratureIndex = 0; quadratureIndex < integrationRule.GetNPoints(); ++quadratureIndex) {
const mfem::IntegrationPoint &integrationPoint = integrationRule.IntPoint(quadratureIndex);
transformation->SetIntPoint(&integrationPoint);
mean_field::mapping::VolumeMappingContext mappingContext;
const mean_field::mapping::MappingStatus mappingStatus =
domainMapper.EvaluateVolume(
mappingData, *transformation, integrationPoint,
workspace, mappingContext
);
const mean_field::mapping::MappingStatus mappingStatus = domainMapper.EvaluateVolume(
mappingData, *transformation, integrationPoint, workspace, mappingContext
);
MFEM_VERIFY(
mappingStatus == mean_field::mapping::MappingStatus::valid,
"Stateless mapping failed in the EOS "
"closure kernel. Element: "
<< elementId
<< ", attribute: " << transformation->Attribute
<< ", quadrature point: " << quadratureIndex
<< ", status: " << static_cast<int>(mappingStatus)
<< elementId << ", attribute: " << transformation->Attribute
<< ", quadrature point: " << quadratureIndex << ", status: " << static_cast<int>(mappingStatus)
);
densityElement.CalcShape(integrationPoint, densityShape);
@@ -408,34 +331,23 @@ namespace {
} else {
enthalpyElement.CalcShape(integrationPoint, enthalpyShape);
const double baseEnthalpy =
elementBaseEnthalpy * enthalpyShape;
const double baseEnthalpy = elementBaseEnthalpy * enthalpyShape;
if (closureAction == ClosureAction::residual) {
const double density =
elementDensityInput * densityShape;
const double density = elementDensityInput * densityShape;
integrand =
density -
barotrope.density_from_enthalpy(baseEnthalpy);
integrand = density - barotrope.density_from_enthalpy(baseEnthalpy);
} else {
const double enthalpyVariation =
elementEnthalpyVariation * enthalpyShape;
const double enthalpyVariation = elementEnthalpyVariation * enthalpyShape;
integrand = -barotrope.density_derivative_from_enthalpy(
baseEnthalpy
) *
enthalpyVariation;
integrand = -barotrope.density_derivative_from_enthalpy(baseEnthalpy) * enthalpyVariation;
}
}
const double weightedIntegrand =
mappingContext.quadrature.weight * integrand;
const double weightedIntegrand = mappingContext.quadrature.weight * integrand;
for (int densityDof = 0; densityDof < densityElement.GetDof();
++densityDof) {
elementAction(densityDof) +=
weightedIntegrand * densityShape(densityDof);
for (int densityDof = 0; densityDof < densityElement.GetDof(); ++densityDof) {
elementAction(densityDof) += weightedIntegrand * densityShape(densityDof);
}
}
@@ -454,51 +366,51 @@ namespace mean_field::operators::kernels {
void apply_barotropic_closure(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const physics::PolytropicBarotrope &barotrope,
const eos::Polytrope &barotrope,
const mfem::Vector &densityTrue,
const mfem::Vector &enthalpyTrue,
const mfem::Vector &displacementTrue,
mfem::Vector &residual
) {
apply_closure_action(
f, domainMapper, barotrope, ClosureAction::residual, &densityTrue,
&enthalpyTrue, nullptr, displacementTrue, residual
f, domainMapper, barotrope, ClosureAction::residual, &densityTrue, &enthalpyTrue, nullptr, displacementTrue,
residual
);
}
void apply_barotropic_closure_density_action(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const physics::PolytropicBarotrope &barotrope,
const eos::Polytrope &barotrope,
const mfem::Vector &densityVariationTrue,
const mfem::Vector &displacementTrue,
mfem::Vector &action
) {
apply_closure_action(
f, domainMapper, barotrope, ClosureAction::density,
&densityVariationTrue, nullptr, nullptr, displacementTrue, action
f, domainMapper, barotrope, ClosureAction::density, &densityVariationTrue, nullptr, nullptr,
displacementTrue, action
);
}
void apply_barotropic_closure_enthalpy_action(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const physics::PolytropicBarotrope &barotrope,
const eos::Polytrope &barotrope,
const mfem::Vector &baseEnthalpyTrue,
const mfem::Vector &enthalpyVariationTrue,
const mfem::Vector &displacementTrue,
mfem::Vector &action
) {
apply_closure_action(
f, domainMapper, barotrope, ClosureAction::enthalpy, nullptr,
&baseEnthalpyTrue, &enthalpyVariationTrue, displacementTrue, action
f, domainMapper, barotrope, ClosureAction::enthalpy, nullptr, &baseEnthalpyTrue, &enthalpyVariationTrue,
displacementTrue, action
);
}
void apply_barotropic_closure_displacement_action(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const physics::PolytropicBarotrope &barotrope,
const eos::Polytrope &barotrope,
const mfem::Vector &baseDensityTrue,
const mfem::Vector &baseEnthalpyTrue,
const mfem::Vector &displacementTrue,
@@ -511,66 +423,55 @@ namespace mean_field::operators::kernels {
);
MFEM_VERIFY(
f.densityFes != nullptr,
"The barotropic-closure displacement action "
"requires the density finite-element space."
f.densityFes != nullptr, "The barotropic-closure displacement action "
"requires the density finite-element space."
);
MFEM_VERIFY(
f.enthalpyFes != nullptr,
"The barotropic-closure displacement action "
"requires the enthalpy finite-element space."
f.enthalpyFes != nullptr, "The barotropic-closure displacement action "
"requires the enthalpy finite-element space."
);
MFEM_VERIFY(
f.displacementFes != nullptr,
"The barotropic-closure displacement action "
"requires the displacement finite-element space."
f.displacementFes != nullptr, "The barotropic-closure displacement action "
"requires the displacement finite-element space."
);
MFEM_VERIFY(
f.compactificationFes != nullptr,
"The barotropic-closure displacement action "
"requires the compactification finite-element space."
f.compactificationFes != nullptr, "The barotropic-closure displacement action "
"requires the compactification finite-element space."
);
MFEM_VERIFY(
f.compactificationCoordinate != nullptr,
"The barotropic-closure displacement action "
"requires the compactification coordinate."
f.compactificationCoordinate != nullptr, "The barotropic-closure displacement action "
"requires the compactification coordinate."
);
MFEM_VERIFY(
f.quadratureFactory != nullptr,
"The barotropic-closure displacement action "
"requires the quadrature-rule factory."
f.quadratureFactory != nullptr, "The barotropic-closure displacement action "
"requires the quadrature-rule factory."
);
MFEM_VERIFY(
baseDensityTrue.Size() == f.densityFes->GetTrueVSize(),
"The base-density vector has the wrong size."
baseDensityTrue.Size() == f.densityFes->GetTrueVSize(), "The base-density vector has the wrong size."
);
MFEM_VERIFY(
baseEnthalpyTrue.Size() == f.enthalpyFes->GetTrueVSize(),
"The base-enthalpy vector has the wrong size."
baseEnthalpyTrue.Size() == f.enthalpyFes->GetTrueVSize(), "The base-enthalpy vector has the wrong size."
);
MFEM_VERIFY(
displacementTrue.Size() == f.displacementFes->GetTrueVSize(),
"The displacement vector has the wrong size."
displacementTrue.Size() == f.displacementFes->GetTrueVSize(), "The displacement vector has the wrong size."
);
MFEM_VERIFY(
displacementVariationTrue.Size() ==
f.displacementFes->GetTrueVSize(),
displacementVariationTrue.Size() == f.displacementFes->GetTrueVSize(),
"The displacement-variation vector has the wrong size."
);
MFEM_VERIFY(
domainMapper.GetDimension() == f.mesh->Dimension(),
"The domain-mapper dimension does not match the "
"mesh dimension."
domainMapper.GetDimension() == f.mesh->Dimension(), "The domain-mapper dimension does not match the "
"mesh dimension."
);
mfem::Vector baseDensityLocal;
@@ -584,17 +485,12 @@ namespace mean_field::operators::kernels {
true_to_local(*f.displacementFes, displacementTrue, displacementLocal);
true_to_local(
*f.displacementFes, displacementVariationTrue,
displacementVariationLocal
);
true_to_local(*f.displacementFes, displacementVariationTrue, displacementVariationLocal);
mfem::Vector localAction(f.densityFes->GetVSize());
localAction = 0.0;
mapping::DomainMapperStateless::Workspace workspace(
f.mesh->Dimension()
);
mapping::DomainMapperStateless::Workspace workspace(f.mesh->Dimension());
mfem::Array<int> densityDofs;
mfem::Array<int> enthalpyDofs;
@@ -614,109 +510,76 @@ namespace mean_field::operators::kernels {
mapping::VolumeMappingContext mappingContext;
mapping::VolumeMappingVariation mappingVariation;
const int vacuumAttribute = domainMapper.GetVacuumElementAttribute();
for (int elementId = 0; elementId < f.mesh->GetNE(); ++elementId) {
mfem::ElementTransformation *transformation =
f.mesh->GetElementTransformation(elementId);
mfem::ElementTransformation *transformation = f.mesh->GetElementTransformation(elementId);
MFEM_VERIFY(
transformation != nullptr,
"The barotropic-closure displacement action "
"received a null element transformation."
transformation != nullptr, "The barotropic-closure displacement action "
"received a null element transformation."
);
if (transformation->Attribute == vacuumAttribute) {
if (!element_is_in_closure_support(transformation->Attribute)) {
continue;
}
const mfem::FiniteElement &densityElement =
*f.densityFes->GetFE(elementId);
const mfem::FiniteElement &densityElement = *f.densityFes->GetFE(elementId);
const mfem::FiniteElement &enthalpyElement =
*f.enthalpyFes->GetFE(elementId);
const mfem::FiniteElement &enthalpyElement = *f.enthalpyFes->GetFE(elementId);
const mfem::FiniteElement &displacementElement =
*f.displacementFes->GetFE(elementId);
const mfem::FiniteElement &displacementElement = *f.displacementFes->GetFE(elementId);
const mfem::FiniteElement &compactificationElement =
*f.compactificationFes->GetFE(elementId);
const mfem::FiniteElement &compactificationElement = *f.compactificationFes->GetFE(elementId);
mfem::DofTransformation *densityDofTransformation =
f.densityFes->GetElementDofs(elementId, densityDofs);
mfem::DofTransformation *densityDofTransformation = f.densityFes->GetElementDofs(elementId, densityDofs);
mfem::DofTransformation *enthalpyDofTransformation =
f.enthalpyFes->GetElementDofs(elementId, enthalpyDofs);
mfem::DofTransformation *enthalpyDofTransformation = f.enthalpyFes->GetElementDofs(elementId, enthalpyDofs);
mfem::DofTransformation *displacementDofTransformation =
f.displacementFes->GetElementVDofs(elementId, displacementDofs);
mfem::DofTransformation *compactificationDofTransformation =
f.compactificationFes->GetElementDofs(
elementId, compactificationDofs
);
f.compactificationFes->GetElementDofs(elementId, compactificationDofs);
baseDensityLocal.GetSubVector(densityDofs, elementBaseDensity);
baseEnthalpyLocal.GetSubVector(enthalpyDofs, elementBaseEnthalpy);
displacementLocal.GetSubVector(
displacementDofs, elementDisplacement
);
displacementLocal.GetSubVector(displacementDofs, elementDisplacement);
displacementVariationLocal.GetSubVector(
displacementDofs, elementDisplacementVariation
);
displacementVariationLocal.GetSubVector(displacementDofs, elementDisplacementVariation);
f.compactificationCoordinate->GetSubVector(
compactificationDofs, elementCompactification
);
f.compactificationCoordinate->GetSubVector(compactificationDofs, elementCompactification);
if (densityDofTransformation != nullptr) {
densityDofTransformation->InvTransformPrimal(
elementBaseDensity
);
densityDofTransformation->InvTransformPrimal(elementBaseDensity);
}
if (enthalpyDofTransformation != nullptr) {
enthalpyDofTransformation->InvTransformPrimal(
elementBaseEnthalpy
);
enthalpyDofTransformation->InvTransformPrimal(elementBaseEnthalpy);
}
if (displacementDofTransformation != nullptr) {
displacementDofTransformation->InvTransformPrimal(
elementDisplacement
);
displacementDofTransformation->InvTransformPrimal(elementDisplacement);
displacementDofTransformation->InvTransformPrimal(
elementDisplacementVariation
);
displacementDofTransformation->InvTransformPrimal(elementDisplacementVariation);
}
if (compactificationDofTransformation != nullptr) {
compactificationDofTransformation->InvTransformPrimal(
elementCompactification
);
compactificationDofTransformation->InvTransformPrimal(elementCompactification);
}
const mapping::ElementDisplacementData displacementData =
mapping::ElementDisplacementDataFromElementVDofs(
displacementElement, elementDisplacement
);
mapping::ElementDisplacementDataFromElementVDofs(displacementElement, elementDisplacement);
const mapping::ElementDisplacementData displacementVariationData =
mapping::ElementDisplacementDataFromElementVDofs(
displacementElement, elementDisplacementVariation
);
mapping::ElementDisplacementDataFromElementVDofs(displacementElement, elementDisplacementVariation);
const mapping::ElementCompactificationData compactificationData(
compactificationElement, elementCompactification
);
const mapping::ElementMappingData mappingData{
.displacement = displacementData,
.compactification = compactificationData
.displacement = displacementData, .compactification = compactificationData
};
densityShape.SetSize(densityElement.GetDof());
@@ -724,74 +587,57 @@ namespace mean_field::operators::kernels {
enthalpyShape.SetSize(enthalpyElement.GetDof());
elementAction.SetSize(densityElement.GetDof());
elementAction = 0.0;
elementAction = 0.0;
const mfem::IntegrationRule &integrationRule = get_eos_rule(
f, barotrope, densityElement, enthalpyElement, *transformation
);
const mfem::IntegrationRule &integrationRule =
get_eos_rule(f, barotrope, densityElement, enthalpyElement, *transformation);
for (int quadraturePoint = 0;
quadraturePoint < integrationRule.GetNPoints();
++quadraturePoint) {
const mfem::IntegrationPoint &integrationPoint =
integrationRule.IntPoint(quadraturePoint);
for (int quadraturePoint = 0; quadraturePoint < integrationRule.GetNPoints(); ++quadraturePoint) {
const mfem::IntegrationPoint &integrationPoint = integrationRule.IntPoint(quadraturePoint);
transformation->SetIntPoint(&integrationPoint);
const mapping::MappingStatus mappingStatus =
domainMapper.EvaluateVolume(
mappingData, *transformation, integrationPoint,
workspace, mappingContext
);
const mapping::MappingStatus mappingStatus = domainMapper.EvaluateVolume(
mappingData, *transformation, integrationPoint, workspace, mappingContext
);
MFEM_VERIFY(
mappingStatus == mapping::MappingStatus::valid,
"The base mapping is invalid while applying "
"the barotropic-closure displacement action. "
"Element: "
<< elementId
<< ", attribute: " << transformation->Attribute
<< ", quadrature point: " << quadraturePoint
<< ", status: " << static_cast<int>(mappingStatus)
<< elementId << ", attribute: " << transformation->Attribute
<< ", quadrature point: " << quadraturePoint << ", status: " << static_cast<int>(mappingStatus)
);
const mapping::MappingStatus variationStatus =
domainMapper.EvaluateVolumeVariation(
mappingData, displacementVariationData, *transformation,
integrationPoint, mappingContext, workspace,
mappingVariation
);
const mapping::MappingStatus variationStatus = domainMapper.EvaluateVolumeVariation(
mappingData, displacementVariationData, *transformation, integrationPoint, mappingContext,
workspace, mappingVariation
);
MFEM_VERIFY(
variationStatus == mapping::MappingStatus::valid,
"The mapping variation is invalid while "
"applying the barotropic-closure "
"displacement action. Element: "
<< elementId
<< ", attribute: " << transformation->Attribute
<< ", quadrature point: " << quadraturePoint
<< ", status: " << static_cast<int>(variationStatus)
<< elementId << ", attribute: " << transformation->Attribute << ", quadrature point: "
<< quadraturePoint << ", status: " << static_cast<int>(variationStatus)
);
densityElement.CalcShape(integrationPoint, densityShape);
enthalpyElement.CalcShape(integrationPoint, enthalpyShape);
const double densityValue = elementBaseDensity * densityShape;
const double densityValue = elementBaseDensity * densityShape;
const double enthalpyValue =
elementBaseEnthalpy * enthalpyShape;
const double enthalpyValue = elementBaseEnthalpy * enthalpyShape;
const double closureValue =
densityValue -
barotrope.density_from_enthalpy(enthalpyValue);
const double closureValue = densityValue - barotrope.density_from_enthalpy(enthalpyValue);
const double geometryActionValue =
closureValue * mappingVariation.weight_variation;
const double geometryActionValue = closureValue * mappingVariation.weight_variation;
MFEM_VERIFY(
std::isfinite(closureValue) &&
std::isfinite(geometryActionValue),
std::isfinite(closureValue) && std::isfinite(geometryActionValue),
"The barotropic-closure displacement action "
"encountered a non-finite quadrature value."
);
@@ -808,4 +654,4 @@ namespace mean_field::operators::kernels {
local_to_true(*f.densityFes, localAction, action);
}
} // namespace mean_field::operators::kernels
} // namespace mean_field::operators::kernels

View File

@@ -0,0 +1,718 @@
module;
#include <array>
#include <cmath>
#include <optional>
#include <mfem.hpp>
module mean_field;
import :operators.kernels.gravity_displacement_force;
namespace {
enum class GravityDisplacementForceAction { residual, density, gravityGradient, displacement, complete };
void true_to_local(
const mfem::ParFiniteElementSpace &finiteElementSpace,
const mfem::Vector &trueVector,
mfem::Vector &localVector
) {
MFEM_VERIFY(
trueVector.Size() == finiteElementSpace.GetTrueVSize(),
"The gravity-displacement-force true vector has the wrong size."
);
localVector.SetSize(finiteElementSpace.GetVSize());
const mfem::Operator *prolongation = finiteElementSpace.GetProlongationMatrix();
if (prolongation != nullptr) {
prolongation->Mult(trueVector, localVector);
} else {
localVector = trueVector;
}
}
void local_to_true(
const mfem::ParFiniteElementSpace &finiteElementSpace,
const mfem::Vector &localVector,
mfem::Vector &trueVector
) {
MFEM_VERIFY(
localVector.Size() == finiteElementSpace.GetVSize(),
"The gravity-displacement-force local vector has the wrong size."
);
trueVector.SetSize(finiteElementSpace.GetTrueVSize());
trueVector = 0.0;
const mfem::Operator *prolongation = finiteElementSpace.GetProlongationMatrix();
if (prolongation != nullptr) {
prolongation->MultTranspose(localVector, trueVector);
} else {
trueVector = localVector;
}
}
[[nodiscard]] int vector_dof_index(
const mfem::Ordering::Type ordering,
const int scalarDof,
const int component,
const int scalarDofCount,
const int dimension
) {
if (ordering == mfem::Ordering::byNODES) {
return scalarDof + component * scalarDofCount;
}
if (ordering == mfem::Ordering::byVDIM) {
return scalarDof * dimension + component;
}
MFEM_ABORT(
"The gravity-displacement-force test space uses an unsupported "
"ordering."
);
return -1;
}
[[nodiscard]] const mfem::IntegrationRule &get_gravity_force_rule(
const mean_field::fem::FEM &f,
const mfem::FiniteElement &densityElement,
const mfem::FiniteElement &gravityGradientElement,
const mfem::FiniteElement &displacementElement,
const mfem::ElementTransformation &transformation
) {
using DisplacementField = mean_field::field::Field<mean_field::field::Displacement>;
MFEM_VERIFY(
densityElement.GetOrder() == mean_field::field::Density::Scalar::familyOrder,
"The gravity-displacement-force density element does not match "
"the registered density field."
);
MFEM_VERIFY(
gravityGradientElement.GetOrder() == mean_field::field::Gravity::Flux::familyOrder + 1,
"The gravity-displacement-force RT element does not match the "
"registered gravity-gradient field."
);
MFEM_VERIFY(
displacementElement.GetOrder() == mean_field::field::Displacement::Vector::familyOrder,
"The gravity-displacement-force test element does not match the "
"registered displacement field."
);
const mean_field::quadrature::Query query =
DisplacementField::make_query<mean_field::field::Displacement::Form::GravityForce>(
mean_field::quadrature::QuadratureRole::discretization, transformation.OrderW(), {},
mean_field::utils::DOMAINS::STELLAR, mean_field::quadrature::MappingKind::general
);
const mean_field::quadrature::MfemRule rule = f.quadratureFactory->get(query, transformation.GetGeometryType());
MFEM_VERIFY(
rule.integration_rule != nullptr, "The quadrature policy did not return a gravity-displacement-"
"force integration rule."
);
return *rule.integration_rule;
}
void validate_finite_vector(
const mfem::Vector &vector,
const char *message
) {
for (int index = 0; index < vector.Size(); ++index) {
MFEM_VERIFY(std::isfinite(vector(index)), message);
}
}
void validate_common_inputs(
const mean_field::fem::FEM &f,
const mean_field::mapping::DomainMapperStateless &domainMapper,
const mfem::Vector &displacementTrue
) {
MFEM_VERIFY(f.mesh != nullptr, "The gravity-displacement-force kernel requires a mesh.");
MFEM_VERIFY(
f.densityFes != nullptr, "The gravity-displacement-force kernel requires the density "
"finite-element space."
);
MFEM_VERIFY(
f.gravityFluxFes != nullptr, "The gravity-displacement-force kernel requires the gravity-"
"gradient finite-element space."
);
MFEM_VERIFY(
f.displacementFes != nullptr, "The gravity-displacement-force kernel requires the displacement "
"finite-element space."
);
MFEM_VERIFY(
f.compactificationFes != nullptr && f.compactificationCoordinate != nullptr,
"The gravity-displacement-force kernel requires the "
"compactification coordinate."
);
MFEM_VERIFY(
f.quadratureFactory != nullptr, "The gravity-displacement-force kernel requires the quadrature "
"rule factory."
);
MFEM_VERIFY(
displacementTrue.Size() == f.displacementFes->GetTrueVSize(),
"The gravity-displacement-force displacement vector has the "
"wrong size."
);
MFEM_VERIFY(
domainMapper.GetDimension() == f.mesh->Dimension(),
"The gravity-displacement-force mapper dimension does not match "
"the mesh dimension."
);
MFEM_VERIFY(
f.displacementFes->GetVDim() == f.mesh->Dimension(),
"The gravity-displacement-force displacement dimension does not "
"match the mesh dimension."
);
validate_finite_vector(
displacementTrue, "The gravity-displacement-force displacement contains a "
"non-finite value."
);
}
void validate_density(
const mean_field::fem::FEM &f,
const mfem::Vector &density,
const char *message
) {
MFEM_VERIFY(density.Size() == f.densityFes->GetTrueVSize(), message);
validate_finite_vector(density, message);
}
void validate_gravity_gradient(
const mean_field::fem::FEM &f,
const mfem::Vector &gravityGradient,
const char *message
) {
MFEM_VERIFY(gravityGradient.Size() == f.gravityFluxFes->GetTrueVSize(), message);
validate_finite_vector(gravityGradient, message);
}
void apply_gravity_displacement_force_action(
const mean_field::fem::FEM &f,
const mean_field::mapping::DomainMapperStateless &domainMapper,
const GravityDisplacementForceAction requestedAction,
const mfem::Vector *baseDensityTrue,
const mfem::Vector *densityVariationTrue,
const mfem::Vector *baseGravityGradientTrue,
const mfem::Vector *gravityGradientVariationTrue,
const mfem::Vector *displacementVariationTrue,
const mfem::Vector &displacementTrue,
mfem::Vector &actionTrue
) {
validate_common_inputs(f, domainMapper, displacementTrue);
const bool needsBaseDensity = requestedAction == GravityDisplacementForceAction::residual ||
requestedAction == GravityDisplacementForceAction::gravityGradient ||
requestedAction == GravityDisplacementForceAction::displacement ||
requestedAction == GravityDisplacementForceAction::complete;
const bool needsDensityVariation = requestedAction == GravityDisplacementForceAction::density ||
requestedAction == GravityDisplacementForceAction::complete;
const bool needsBaseGravityGradient = requestedAction == GravityDisplacementForceAction::residual ||
requestedAction == GravityDisplacementForceAction::density ||
requestedAction == GravityDisplacementForceAction::displacement ||
requestedAction == GravityDisplacementForceAction::complete;
const bool needsGravityGradientVariation = requestedAction == GravityDisplacementForceAction::gravityGradient ||
requestedAction == GravityDisplacementForceAction::complete;
const bool needsDisplacementVariation = requestedAction == GravityDisplacementForceAction::displacement ||
requestedAction == GravityDisplacementForceAction::complete;
if (needsBaseDensity) {
MFEM_VERIFY(
baseDensityTrue != nullptr, "The gravity-displacement-force action requires a base "
"density."
);
validate_density(f, *baseDensityTrue, "The gravity-displacement-force base density is invalid.");
}
if (needsDensityVariation) {
MFEM_VERIFY(
densityVariationTrue != nullptr, "The gravity-displacement-force action requires a density "
"variation."
);
validate_density(
f, *densityVariationTrue,
"The gravity-displacement-force density variation is "
"invalid."
);
}
if (needsBaseGravityGradient) {
MFEM_VERIFY(
baseGravityGradientTrue != nullptr, "The gravity-displacement-force action requires a base "
"gravity gradient."
);
validate_gravity_gradient(
f, *baseGravityGradientTrue,
"The gravity-displacement-force base gravity gradient is "
"invalid."
);
}
if (needsGravityGradientVariation) {
MFEM_VERIFY(
gravityGradientVariationTrue != nullptr, "The gravity-displacement-force action requires a gravity-"
"gradient variation."
);
validate_gravity_gradient(
f, *gravityGradientVariationTrue,
"The gravity-displacement-force gravity-gradient variation "
"is invalid."
);
}
if (needsDisplacementVariation) {
MFEM_VERIFY(
displacementVariationTrue != nullptr &&
displacementVariationTrue->Size() == f.displacementFes->GetTrueVSize(),
"The gravity-displacement-force displacement variation is "
"invalid."
);
validate_finite_vector(
*displacementVariationTrue, "The gravity-displacement-force displacement variation "
"contains a non-finite value."
);
}
mfem::Vector baseDensityLocal;
mfem::Vector densityVariationLocal;
mfem::Vector baseGravityGradientLocal;
mfem::Vector gravityGradientVariationLocal;
mfem::Vector displacementLocal;
mfem::Vector displacementVariationLocal;
if (needsBaseDensity) {
true_to_local(*f.densityFes, *baseDensityTrue, baseDensityLocal);
}
if (needsDensityVariation) {
true_to_local(*f.densityFes, *densityVariationTrue, densityVariationLocal);
}
if (needsBaseGravityGradient) {
true_to_local(*f.gravityFluxFes, *baseGravityGradientTrue, baseGravityGradientLocal);
}
if (needsGravityGradientVariation) {
true_to_local(*f.gravityFluxFes, *gravityGradientVariationTrue, gravityGradientVariationLocal);
}
true_to_local(*f.displacementFes, displacementTrue, displacementLocal);
if (needsDisplacementVariation) {
true_to_local(*f.displacementFes, *displacementVariationTrue, displacementVariationLocal);
}
mfem::Vector localAction(f.displacementFes->GetVSize());
localAction = 0.0;
mean_field::mapping::DomainMapperStateless::Workspace workspace(f.mesh->Dimension());
mfem::Array<int> densityDofs;
mfem::Array<int> gravityGradientDofs;
mfem::Array<int> displacementDofs;
mfem::Array<int> compactificationDofs;
mfem::Vector elementBaseDensity;
mfem::Vector elementDensityVariation;
mfem::Vector elementBaseGravityGradient;
mfem::Vector elementGravityGradientVariation;
mfem::Vector elementDisplacement;
mfem::Vector elementDisplacementVariation;
mfem::Vector elementCompactification;
mfem::Vector elementAction;
mfem::Vector densityShape;
mfem::Vector displacementShape;
mfem::DenseMatrix gravityGradientShape;
mfem::Vector baseGravityReferenceValue;
mfem::Vector gravityVariationReferenceValue;
mfem::Vector mappedBaseGravity;
mfem::Vector mappedGravityVariation;
mfem::Vector mappedGeometryVariation;
mfem::Vector forceValue;
mean_field::mapping::VolumeMappingContext mappingContext;
mean_field::mapping::VolumeMappingVariation mappingVariation;
const int dimension = f.mesh->Dimension();
const int vacuumAttribute = domainMapper.GetVacuumElementAttribute();
const mfem::Ordering::Type displacementOrdering = f.displacementFes->GetOrdering();
for (int elementId = 0; elementId < f.mesh->GetNE(); ++elementId) {
mfem::ElementTransformation *transformation = f.mesh->GetElementTransformation(elementId);
MFEM_VERIFY(
transformation != nullptr, "The gravity-displacement-force kernel received a null "
"element transformation."
);
if (transformation->Attribute == vacuumAttribute) {
continue;
}
const mfem::FiniteElement &densityElement = *f.densityFes->GetFE(elementId);
const mfem::FiniteElement &gravityGradientElement = *f.gravityFluxFes->GetFE(elementId);
const mfem::FiniteElement &displacementElement = *f.displacementFes->GetFE(elementId);
const mfem::FiniteElement &compactificationElement = *f.compactificationFes->GetFE(elementId);
mfem::DofTransformation *densityDofTransformation = f.densityFes->GetElementDofs(elementId, densityDofs);
mfem::DofTransformation *gravityGradientDofTransformation =
f.gravityFluxFes->GetElementVDofs(elementId, gravityGradientDofs);
mfem::DofTransformation *displacementDofTransformation =
f.displacementFes->GetElementVDofs(elementId, displacementDofs);
mfem::DofTransformation *compactificationDofTransformation =
f.compactificationFes->GetElementDofs(elementId, compactificationDofs);
if (needsBaseDensity) {
baseDensityLocal.GetSubVector(densityDofs, elementBaseDensity);
}
if (needsDensityVariation) {
densityVariationLocal.GetSubVector(densityDofs, elementDensityVariation);
}
if (needsBaseGravityGradient) {
baseGravityGradientLocal.GetSubVector(gravityGradientDofs, elementBaseGravityGradient);
}
if (needsGravityGradientVariation) {
gravityGradientVariationLocal.GetSubVector(gravityGradientDofs, elementGravityGradientVariation);
}
displacementLocal.GetSubVector(displacementDofs, elementDisplacement);
if (needsDisplacementVariation) {
displacementVariationLocal.GetSubVector(displacementDofs, elementDisplacementVariation);
}
f.compactificationCoordinate->GetSubVector(compactificationDofs, elementCompactification);
if (densityDofTransformation != nullptr) {
if (needsBaseDensity) {
densityDofTransformation->InvTransformPrimal(elementBaseDensity);
}
if (needsDensityVariation) {
densityDofTransformation->InvTransformPrimal(elementDensityVariation);
}
}
if (gravityGradientDofTransformation != nullptr) {
if (needsBaseGravityGradient) {
gravityGradientDofTransformation->InvTransformPrimal(elementBaseGravityGradient);
}
if (needsGravityGradientVariation) {
gravityGradientDofTransformation->InvTransformPrimal(elementGravityGradientVariation);
}
}
if (displacementDofTransformation != nullptr) {
displacementDofTransformation->InvTransformPrimal(elementDisplacement);
if (needsDisplacementVariation) {
displacementDofTransformation->InvTransformPrimal(elementDisplacementVariation);
}
}
if (compactificationDofTransformation != nullptr) {
compactificationDofTransformation->InvTransformPrimal(elementCompactification);
}
const mean_field::mapping::ElementDisplacementData displacementData =
mean_field::mapping::ElementDisplacementDataFromElementVDofs(displacementElement, elementDisplacement);
const mean_field::mapping::ElementCompactificationData compactificationData(
compactificationElement, elementCompactification
);
const mean_field::mapping::ElementMappingData mappingData{
.displacement = displacementData, .compactification = compactificationData
};
std::optional<mean_field::mapping::ElementDisplacementData> displacementVariationData;
if (needsDisplacementVariation) {
displacementVariationData.emplace(
mean_field::mapping::ElementDisplacementDataFromElementVDofs(
displacementElement, elementDisplacementVariation
)
);
}
const int scalarDisplacementDofCount = displacementElement.GetDof();
MFEM_VERIFY(
displacementDofs.Size() == scalarDisplacementDofCount * dimension,
"The gravity-displacement-force element displacement vector "
"has the wrong size."
);
densityShape.SetSize(densityElement.GetDof());
displacementShape.SetSize(scalarDisplacementDofCount);
gravityGradientShape.SetSize(gravityGradientElement.GetDof(), dimension);
baseGravityReferenceValue.SetSize(dimension);
gravityVariationReferenceValue.SetSize(dimension);
mappedBaseGravity.SetSize(dimension);
mappedGravityVariation.SetSize(dimension);
mappedGeometryVariation.SetSize(dimension);
forceValue.SetSize(dimension);
elementAction.SetSize(displacementDofs.Size());
elementAction = 0.0;
const mfem::IntegrationRule &integrationRule =
get_gravity_force_rule(f, densityElement, gravityGradientElement, displacementElement, *transformation);
for (int quadratureIndex = 0; quadratureIndex < integrationRule.GetNPoints(); ++quadratureIndex) {
const mfem::IntegrationPoint &integrationPoint = integrationRule.IntPoint(quadratureIndex);
transformation->SetIntPoint(&integrationPoint);
const mean_field::mapping::MappingStatus mappingStatus = domainMapper.EvaluateVolume(
mappingData, *transformation, integrationPoint, workspace, mappingContext
);
MFEM_VERIFY(
mappingStatus == mean_field::mapping::MappingStatus::valid,
"Stateless mapping failed in the gravity-displacement-"
"force kernel. Element: "
<< elementId << ", attribute: " << transformation->Attribute
<< ", quadrature point: " << quadratureIndex << ", status: " << static_cast<int>(mappingStatus)
);
if (needsDisplacementVariation) {
const mean_field::mapping::MappingStatus variationStatus = domainMapper.EvaluateVolumeVariation(
mappingData, *displacementVariationData, *transformation, integrationPoint, mappingContext,
workspace, mappingVariation
);
MFEM_VERIFY(
variationStatus == mean_field::mapping::MappingStatus::valid,
"Stateless mapping variation failed in the gravity-"
"displacement-force kernel. Element: "
<< elementId << ", attribute: " << transformation->Attribute << ", quadrature point: "
<< quadratureIndex << ", status: " << static_cast<int>(variationStatus)
);
}
densityElement.CalcShape(integrationPoint, densityShape);
displacementElement.CalcShape(integrationPoint, displacementShape);
gravityGradientElement.CalcVShape(*transformation, gravityGradientShape);
double baseDensityValue = 0.0;
double densityVariationValue = 0.0;
if (needsBaseDensity) {
baseDensityValue = elementBaseDensity * densityShape;
}
if (needsDensityVariation) {
densityVariationValue = elementDensityVariation * densityShape;
}
if (needsBaseGravityGradient) {
gravityGradientShape.MultTranspose(elementBaseGravityGradient, baseGravityReferenceValue);
mappingContext.mapping.mapping_jacobian.Mult(baseGravityReferenceValue, mappedBaseGravity);
} else {
mappedBaseGravity = 0.0;
}
if (needsGravityGradientVariation) {
gravityGradientShape.MultTranspose(elementGravityGradientVariation, gravityVariationReferenceValue);
mappingContext.mapping.mapping_jacobian.Mult(
gravityVariationReferenceValue, mappedGravityVariation
);
} else {
mappedGravityVariation = 0.0;
}
if (needsDisplacementVariation) {
mappingVariation.mapping.mapping_jacobian_variation.Mult(
baseGravityReferenceValue, mappedGeometryVariation
);
} else {
mappedGeometryVariation = 0.0;
}
forceValue = 0.0;
if (requestedAction == GravityDisplacementForceAction::residual) {
forceValue.Add(baseDensityValue, mappedBaseGravity);
} else {
if (needsDensityVariation) {
forceValue.Add(densityVariationValue, mappedBaseGravity);
}
if (needsGravityGradientVariation) {
forceValue.Add(baseDensityValue, mappedGravityVariation);
}
if (needsDisplacementVariation) {
forceValue.Add(baseDensityValue, mappedGeometryVariation);
}
}
/*
* If g_ref is the RT pullback, then
*
* g_phys = J_map g_ref / det(J_map),
* dV_phys = det(J_map) dV_ref.
*
* The determinant cancels exactly. Consequently the base
* integrand uses J_map g_ref and its geometry derivative uses
* delta(J_map) g_ref. This is algebraically identical to
* differentiating the Piola map and physical volume weight,
* but avoids a numerically pointless cancellation.
*/
const double referenceWeight = integrationPoint.weight * transformation->Weight();
forceValue *= referenceWeight;
for (int scalarDof = 0; scalarDof < scalarDisplacementDofCount; ++scalarDof) {
for (int component = 0; component < dimension; ++component) {
const int vectorDof = vector_dof_index(
displacementOrdering, scalarDof, component, scalarDisplacementDofCount, dimension
);
const double contribution = displacementShape(scalarDof) * forceValue(component);
MFEM_VERIFY(
std::isfinite(contribution), "The gravity-displacement-force kernel "
"encountered a non-finite contribution."
);
elementAction(vectorDof) += contribution;
}
}
}
if (displacementDofTransformation != nullptr) {
displacementDofTransformation->TransformDual(elementAction);
}
localAction.AddElementVector(displacementDofs, elementAction);
}
local_to_true(*f.displacementFes, localAction, actionTrue);
}
} // namespace
namespace mean_field::operators::kernels {
void apply_gravity_displacement_force_residual(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const mfem::Vector &densityTrue,
const mfem::Vector &gravityGradientTrue,
const mfem::Vector &displacementTrue,
mfem::Vector &residualTrue
) {
apply_gravity_displacement_force_action(
f, domainMapper, GravityDisplacementForceAction::residual, &densityTrue, nullptr, &gravityGradientTrue,
nullptr, nullptr, displacementTrue, residualTrue
);
}
void apply_gravity_displacement_force_density_action(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const mfem::Vector &densityVariationTrue,
const mfem::Vector &baseGravityGradientTrue,
const mfem::Vector &displacementTrue,
mfem::Vector &actionTrue
) {
apply_gravity_displacement_force_action(
f, domainMapper, GravityDisplacementForceAction::density, nullptr, &densityVariationTrue,
&baseGravityGradientTrue, nullptr, nullptr, displacementTrue, actionTrue
);
}
void apply_gravity_displacement_force_gradient_action(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const mfem::Vector &baseDensityTrue,
const mfem::Vector &gravityGradientVariationTrue,
const mfem::Vector &displacementTrue,
mfem::Vector &actionTrue
) {
apply_gravity_displacement_force_action(
f, domainMapper, GravityDisplacementForceAction::gravityGradient, &baseDensityTrue, nullptr, nullptr,
&gravityGradientVariationTrue, nullptr, displacementTrue, actionTrue
);
}
void apply_gravity_displacement_force_displacement_action(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const mfem::Vector &baseDensityTrue,
const mfem::Vector &baseGravityGradientTrue,
const mfem::Vector &displacementVariationTrue,
const mfem::Vector &displacementTrue,
mfem::Vector &actionTrue
) {
apply_gravity_displacement_force_action(
f, domainMapper, GravityDisplacementForceAction::displacement, &baseDensityTrue, nullptr,
&baseGravityGradientTrue, nullptr, &displacementVariationTrue, displacementTrue, actionTrue
);
}
void apply_gravity_displacement_force_complete_action(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const mfem::Vector &baseDensityTrue,
const mfem::Vector &densityVariationTrue,
const mfem::Vector &baseGravityGradientTrue,
const mfem::Vector &gravityGradientVariationTrue,
const mfem::Vector &displacementVariationTrue,
const mfem::Vector &displacementTrue,
mfem::Vector &actionTrue
) {
apply_gravity_displacement_force_action(
f, domainMapper, GravityDisplacementForceAction::complete, &baseDensityTrue, &densityVariationTrue,
&baseGravityGradientTrue, &gravityGradientVariationTrue, &displacementVariationTrue, displacementTrue,
actionTrue
);
}
} // namespace mean_field::operators::kernels

File diff suppressed because it is too large Load Diff

View File

@@ -16,15 +16,11 @@ namespace {
const mfem::Vector &trueVector,
mfem::Vector &localVector
) {
MFEM_VERIFY(
trueVector.Size() == finiteElementSpace.GetTrueVSize(),
"True vector has the wrong size."
);
MFEM_VERIFY(trueVector.Size() == finiteElementSpace.GetTrueVSize(), "True vector has the wrong size.");
localVector.SetSize(finiteElementSpace.GetVSize());
const mfem::Operator *prolongation =
finiteElementSpace.GetProlongationMatrix();
const mfem::Operator *prolongation = finiteElementSpace.GetProlongationMatrix();
if (prolongation != nullptr) {
prolongation->Mult(trueVector, localVector);
@@ -38,17 +34,13 @@ namespace {
const mfem::Vector &localVector,
mfem::Vector &trueVector
) {
MFEM_VERIFY(
localVector.Size() == finiteElementSpace.GetVSize(),
"Local vector has the wrong size."
);
MFEM_VERIFY(localVector.Size() == finiteElementSpace.GetVSize(), "Local vector has the wrong size.");
trueVector.SetSize(finiteElementSpace.GetTrueVSize());
trueVector = 0.0;
trueVector = 0.0;
const mfem::Operator *prolongation =
finiteElementSpace.GetProlongationMatrix();
const mfem::Operator *prolongation = finiteElementSpace.GetProlongationMatrix();
if (prolongation != nullptr) {
prolongation->MultTranspose(localVector, trueVector);
@@ -61,9 +53,7 @@ namespace {
const mean_field::fem::FEM &f,
const mean_field::mapping::DomainMapperStateless &domainMapper
) {
MFEM_VERIFY(
f.mesh != nullptr, "The hydrostatic kernel requires a mesh."
);
MFEM_VERIFY(f.mesh != nullptr, "The hydrostatic kernel requires a mesh.");
MFEM_VERIFY(
f.enthalpyFes != nullptr, "The hydrostatic kernel requires the "
@@ -71,9 +61,8 @@ namespace {
);
MFEM_VERIFY(
f.gravityPotentialFes != nullptr,
"The hydrostatic kernel requires the "
"gravity-potential finite-element space."
f.gravityPotentialFes != nullptr, "The hydrostatic kernel requires the "
"gravity-potential finite-element space."
);
MFEM_VERIFY(
@@ -82,33 +71,28 @@ namespace {
);
MFEM_VERIFY(
f.compactificationFes != nullptr,
"The hydrostatic kernel requires the "
"compactification finite-element space."
f.compactificationFes != nullptr, "The hydrostatic kernel requires the "
"compactification finite-element space."
);
MFEM_VERIFY(
f.compactificationCoordinate != nullptr,
"The hydrostatic kernel requires the "
"compactification coordinate."
f.compactificationCoordinate != nullptr, "The hydrostatic kernel requires the "
"compactification coordinate."
);
MFEM_VERIFY(
f.quadratureFactory != nullptr,
"The hydrostatic kernel requires the "
"quadrature-rule factory."
f.quadratureFactory != nullptr, "The hydrostatic kernel requires the "
"quadrature-rule factory."
);
MFEM_VERIFY(
f.mesh->Dimension() == 3,
"The rigid-rotation hydrostatic kernel "
"currently requires a three-dimensional mesh."
f.mesh->Dimension() == 3, "The rigid-rotation hydrostatic kernel "
"currently requires a three-dimensional mesh."
);
MFEM_VERIFY(
domainMapper.GetDimension() == f.mesh->Dimension(),
"The domain-mapper dimension does not match "
"the mesh dimension."
domainMapper.GetDimension() == f.mesh->Dimension(), "The domain-mapper dimension does not match "
"the mesh dimension."
);
}
@@ -118,69 +102,51 @@ namespace {
const mfem::FiniteElement &potentialElement,
const mfem::ElementTransformation &transformation
) {
using EnthalpyField =
mean_field::field::Field<mean_field::field::Enthalpy>;
using EnthalpyField = mean_field::field::Field<mean_field::field::Enthalpy>;
MFEM_VERIFY(
enthalpyElement.GetOrder() ==
mean_field::field::Enthalpy::Scalar::familyOrder,
enthalpyElement.GetOrder() == mean_field::field::Enthalpy::Scalar::familyOrder,
"The hydrostatic test element does not match "
"the registered enthalpy field."
);
MFEM_VERIFY(
potentialElement.GetOrder() ==
mean_field::field::Gravity::Potential::familyOrder,
potentialElement.GetOrder() == mean_field::field::Gravity::Potential::familyOrder,
"The hydrostatic potential element does not "
"match the registered gravity-potential field."
);
const auto enthalpyQuery = EnthalpyField::make_query<
mean_field::field::Enthalpy::Form::EquilibriumEnthalpy>(
mean_field::quadrature::QuadratureRole::discretization,
transformation.OrderW(), {}, mean_field::utils::DOMAINS::STELLAR,
mean_field::quadrature::MappingKind::general
const auto enthalpyQuery = EnthalpyField::make_query<mean_field::field::Enthalpy::Form::EquilibriumEnthalpy>(
mean_field::quadrature::QuadratureRole::discretization, transformation.OrderW(), {},
mean_field::utils::DOMAINS::STELLAR, mean_field::quadrature::MappingKind::general
);
const auto gravityQuery = EnthalpyField::make_query<
mean_field::field::Enthalpy::Form::EquilibriumGravity>(
mean_field::quadrature::QuadratureRole::discretization,
transformation.OrderW(), {}, mean_field::utils::DOMAINS::STELLAR,
mean_field::quadrature::MappingKind::general
const auto gravityQuery = EnthalpyField::make_query<mean_field::field::Enthalpy::Form::EquilibriumGravity>(
mean_field::quadrature::QuadratureRole::discretization, transformation.OrderW(), {},
mean_field::utils::DOMAINS::STELLAR, mean_field::quadrature::MappingKind::general
);
const auto rotationQuery = EnthalpyField::make_query<
mean_field::field::Enthalpy::Form::EquilibriumRotation>(
mean_field::quadrature::QuadratureRole::discretization,
transformation.OrderW(), std::array<int, 1>{2},
mean_field::utils::DOMAINS::STELLAR,
mean_field::quadrature::MappingKind::general
const auto rotationQuery = EnthalpyField::make_query<mean_field::field::Enthalpy::Form::EquilibriumRotation>(
mean_field::quadrature::QuadratureRole::discretization, transformation.OrderW(), std::array<int, 1>{2},
mean_field::utils::DOMAINS::STELLAR, mean_field::quadrature::MappingKind::general
);
const auto constantQuery = EnthalpyField::make_query<
mean_field::field::Enthalpy::Form::EquilibriumConstant>(
mean_field::quadrature::QuadratureRole::discretization,
transformation.OrderW(), {}, mean_field::utils::DOMAINS::STELLAR,
mean_field::quadrature::MappingKind::general
const auto constantQuery = EnthalpyField::make_query<mean_field::field::Enthalpy::Form::EquilibriumConstant>(
mean_field::quadrature::QuadratureRole::discretization, transformation.OrderW(), {},
mean_field::utils::DOMAINS::STELLAR, mean_field::quadrature::MappingKind::general
);
int integrationOrder = 0;
const auto update_order = [&f, &transformation, &integrationOrder](
const mean_field::quadrature::Query &query
) {
const auto rule = f.quadratureFactory->get(
query, transformation.GetGeometryType()
);
const auto update_order = [&f, &transformation, &integrationOrder](const mean_field::quadrature::Query &query) {
const auto rule = f.quadratureFactory->get(query, transformation.GetGeometryType());
MFEM_VERIFY(
rule.integration_rule != nullptr,
"The quadrature policy did not return "
"a hydrostatic-equilibrium rule."
rule.integration_rule != nullptr, "The quadrature policy did not return "
"a hydrostatic-equilibrium rule."
);
integrationOrder =
std::max(integrationOrder, rule.resolution.order);
integrationOrder = std::max(integrationOrder, rule.resolution.order);
};
update_order(enthalpyQuery);
@@ -188,9 +154,7 @@ namespace {
update_order(rotationQuery);
update_order(constantQuery);
return mfem::IntRules.Get(
transformation.GetGeometryType(), integrationOrder
);
return mfem::IntRules.Get(transformation.GetGeometryType(), integrationOrder);
}
struct HydrostaticAssemblyRequest {
@@ -219,81 +183,64 @@ namespace {
validate_fem(f, domainMapper);
MFEM_VERIFY(
displacementTrue.Size() == f.displacementFes->GetTrueVSize(),
"The hydrostatic displacement vector has "
"the wrong size."
displacementTrue.Size() == f.displacementFes->GetTrueVSize(), "The hydrostatic displacement vector has "
"the wrong size."
);
MFEM_VERIFY(
std::isfinite(request.bernoulliConstant),
"The Bernoulli constant is non-finite."
);
MFEM_VERIFY(std::isfinite(request.bernoulliConstant), "The Bernoulli constant is non-finite.");
MFEM_VERIFY(
std::isfinite(request.constantVariation),
"The Bernoulli-constant variation is non-finite."
);
MFEM_VERIFY(std::isfinite(request.constantVariation), "The Bernoulli-constant variation is non-finite.");
const bool requiresBaseState =
request.buildResidual ||
request.displacementVariationTrue != nullptr;
const bool requiresBaseState = request.buildResidual || request.displacementVariationTrue != nullptr;
if (requiresBaseState) {
MFEM_VERIFY(
request.rotation != nullptr,
"The hydrostatic residual or geometry "
"action requires the rotation model."
request.rotation != nullptr, "The hydrostatic residual or geometry "
"action requires the rotation model."
);
MFEM_VERIFY(
request.baseEnthalpyTrue != nullptr,
"The hydrostatic residual or geometry "
"action requires the base enthalpy."
request.baseEnthalpyTrue != nullptr, "The hydrostatic residual or geometry "
"action requires the base enthalpy."
);
MFEM_VERIFY(
request.basePotentialTrue != nullptr,
"The hydrostatic residual or geometry "
"action requires the base potential."
request.basePotentialTrue != nullptr, "The hydrostatic residual or geometry "
"action requires the base potential."
);
}
if (request.baseEnthalpyTrue != nullptr) {
MFEM_VERIFY(
request.baseEnthalpyTrue->Size() ==
f.enthalpyFes->GetTrueVSize(),
request.baseEnthalpyTrue->Size() == f.enthalpyFes->GetTrueVSize(),
"The base enthalpy vector has the wrong size."
);
}
if (request.basePotentialTrue != nullptr) {
MFEM_VERIFY(
request.basePotentialTrue->Size() ==
f.gravityPotentialFes->GetTrueVSize(),
request.basePotentialTrue->Size() == f.gravityPotentialFes->GetTrueVSize(),
"The base potential vector has the wrong size."
);
}
if (request.enthalpyVariationTrue != nullptr) {
MFEM_VERIFY(
request.enthalpyVariationTrue->Size() ==
f.enthalpyFes->GetTrueVSize(),
request.enthalpyVariationTrue->Size() == f.enthalpyFes->GetTrueVSize(),
"The enthalpy variation has the wrong size."
);
}
if (request.potentialVariationTrue != nullptr) {
MFEM_VERIFY(
request.potentialVariationTrue->Size() ==
f.gravityPotentialFes->GetTrueVSize(),
request.potentialVariationTrue->Size() == f.gravityPotentialFes->GetTrueVSize(),
"The potential variation has the wrong size."
);
}
if (request.displacementVariationTrue != nullptr) {
MFEM_VERIFY(
request.displacementVariationTrue->Size() ==
f.displacementFes->GetTrueVSize(),
request.displacementVariationTrue->Size() == f.displacementFes->GetTrueVSize(),
"The displacement variation has the wrong size."
);
}
@@ -308,46 +255,30 @@ namespace {
mfem::Vector displacementVariationLocal;
if (request.baseEnthalpyTrue != nullptr) {
true_to_local(
*f.enthalpyFes, *request.baseEnthalpyTrue, baseEnthalpyLocal
);
true_to_local(*f.enthalpyFes, *request.baseEnthalpyTrue, baseEnthalpyLocal);
}
if (request.basePotentialTrue != nullptr) {
true_to_local(
*f.gravityPotentialFes, *request.basePotentialTrue,
basePotentialLocal
);
true_to_local(*f.gravityPotentialFes, *request.basePotentialTrue, basePotentialLocal);
}
if (request.enthalpyVariationTrue != nullptr) {
true_to_local(
*f.enthalpyFes, *request.enthalpyVariationTrue,
enthalpyVariationLocal
);
true_to_local(*f.enthalpyFes, *request.enthalpyVariationTrue, enthalpyVariationLocal);
}
if (request.potentialVariationTrue != nullptr) {
true_to_local(
*f.gravityPotentialFes, *request.potentialVariationTrue,
potentialVariationLocal
);
true_to_local(*f.gravityPotentialFes, *request.potentialVariationTrue, potentialVariationLocal);
}
if (request.displacementVariationTrue != nullptr) {
true_to_local(
*f.displacementFes, *request.displacementVariationTrue,
displacementVariationLocal
);
true_to_local(*f.displacementFes, *request.displacementVariationTrue, displacementVariationLocal);
}
mfem::Vector localResult(f.enthalpyFes->GetVSize());
localResult = 0.0;
mean_field::mapping::DomainMapperStateless::Workspace workspace(
f.mesh->Dimension()
);
mean_field::mapping::DomainMapperStateless::Workspace workspace(f.mesh->Dimension());
mfem::Array<int> enthalpyDofs;
mfem::Array<int> potentialDofs;
@@ -369,33 +300,26 @@ namespace {
const int vacuumAttribute = domainMapper.GetVacuumElementAttribute();
for (int elementId = 0; elementId < f.mesh->GetNE(); ++elementId) {
mfem::ElementTransformation *transformation =
f.mesh->GetElementTransformation(elementId);
mfem::ElementTransformation *transformation = f.mesh->GetElementTransformation(elementId);
MFEM_VERIFY(
transformation != nullptr,
"The hydrostatic kernel received a null "
"element transformation."
transformation != nullptr, "The hydrostatic kernel received a null "
"element transformation."
);
if (transformation->Attribute == vacuumAttribute) {
continue;
}
const mfem::FiniteElement &enthalpyElement =
*f.enthalpyFes->GetFE(elementId);
const mfem::FiniteElement &enthalpyElement = *f.enthalpyFes->GetFE(elementId);
const mfem::FiniteElement &potentialElement =
*f.gravityPotentialFes->GetFE(elementId);
const mfem::FiniteElement &potentialElement = *f.gravityPotentialFes->GetFE(elementId);
const mfem::FiniteElement &displacementElement =
*f.displacementFes->GetFE(elementId);
const mfem::FiniteElement &displacementElement = *f.displacementFes->GetFE(elementId);
const mfem::FiniteElement &compactificationElement =
*f.compactificationFes->GetFE(elementId);
const mfem::FiniteElement &compactificationElement = *f.compactificationFes->GetFE(elementId);
mfem::DofTransformation *enthalpyDofTransformation =
f.enthalpyFes->GetElementDofs(elementId, enthalpyDofs);
mfem::DofTransformation *enthalpyDofTransformation = f.enthalpyFes->GetElementDofs(elementId, enthalpyDofs);
mfem::DofTransformation *potentialDofTransformation =
f.gravityPotentialFes->GetElementDofs(elementId, potentialDofs);
@@ -404,119 +328,82 @@ namespace {
f.displacementFes->GetElementVDofs(elementId, displacementDofs);
mfem::DofTransformation *compactificationDofTransformation =
f.compactificationFes->GetElementDofs(
elementId, compactificationDofs
);
f.compactificationFes->GetElementDofs(elementId, compactificationDofs);
displacementLocal.GetSubVector(
displacementDofs, elementDisplacement
);
displacementLocal.GetSubVector(displacementDofs, elementDisplacement);
f.compactificationCoordinate->GetSubVector(
compactificationDofs, elementCompactification
);
f.compactificationCoordinate->GetSubVector(compactificationDofs, elementCompactification);
if (request.baseEnthalpyTrue != nullptr) {
baseEnthalpyLocal.GetSubVector(
enthalpyDofs, elementBaseEnthalpy
);
baseEnthalpyLocal.GetSubVector(enthalpyDofs, elementBaseEnthalpy);
}
if (request.basePotentialTrue != nullptr) {
basePotentialLocal.GetSubVector(
potentialDofs, elementBasePotential
);
basePotentialLocal.GetSubVector(potentialDofs, elementBasePotential);
}
if (request.enthalpyVariationTrue != nullptr) {
enthalpyVariationLocal.GetSubVector(
enthalpyDofs, elementEnthalpyVariation
);
enthalpyVariationLocal.GetSubVector(enthalpyDofs, elementEnthalpyVariation);
}
if (request.potentialVariationTrue != nullptr) {
potentialVariationLocal.GetSubVector(
potentialDofs, elementPotentialVariation
);
potentialVariationLocal.GetSubVector(potentialDofs, elementPotentialVariation);
}
if (request.displacementVariationTrue != nullptr) {
displacementVariationLocal.GetSubVector(
displacementDofs, elementDisplacementVariation
);
displacementVariationLocal.GetSubVector(displacementDofs, elementDisplacementVariation);
}
if (enthalpyDofTransformation != nullptr) {
if (request.baseEnthalpyTrue != nullptr) {
enthalpyDofTransformation->InvTransformPrimal(
elementBaseEnthalpy
);
enthalpyDofTransformation->InvTransformPrimal(elementBaseEnthalpy);
}
if (request.enthalpyVariationTrue != nullptr) {
enthalpyDofTransformation->InvTransformPrimal(
elementEnthalpyVariation
);
enthalpyDofTransformation->InvTransformPrimal(elementEnthalpyVariation);
}
}
if (potentialDofTransformation != nullptr) {
if (request.basePotentialTrue != nullptr) {
potentialDofTransformation->InvTransformPrimal(
elementBasePotential
);
potentialDofTransformation->InvTransformPrimal(elementBasePotential);
}
if (request.potentialVariationTrue != nullptr) {
potentialDofTransformation->InvTransformPrimal(
elementPotentialVariation
);
potentialDofTransformation->InvTransformPrimal(elementPotentialVariation);
}
}
if (displacementDofTransformation != nullptr) {
displacementDofTransformation->InvTransformPrimal(
elementDisplacement
);
displacementDofTransformation->InvTransformPrimal(elementDisplacement);
if (request.displacementVariationTrue != nullptr) {
displacementDofTransformation->InvTransformPrimal(
elementDisplacementVariation
);
displacementDofTransformation->InvTransformPrimal(elementDisplacementVariation);
}
}
if (compactificationDofTransformation != nullptr) {
compactificationDofTransformation->InvTransformPrimal(
elementCompactification
);
compactificationDofTransformation->InvTransformPrimal(elementCompactification);
}
const mean_field::mapping::ElementDisplacementData
displacementData = mean_field::mapping::
ElementDisplacementDataFromElementVDofs(
displacementElement, elementDisplacement
);
const mean_field::mapping::ElementDisplacementData displacementData =
mean_field::mapping::ElementDisplacementDataFromElementVDofs(displacementElement, elementDisplacement);
const mean_field::mapping::ElementCompactificationData
compactificationData(
compactificationElement, elementCompactification
);
const mean_field::mapping::ElementCompactificationData compactificationData(
compactificationElement, elementCompactification
);
const mean_field::mapping::ElementMappingData mappingData{
.displacement = displacementData,
.compactification = compactificationData
.displacement = displacementData, .compactification = compactificationData
};
std::optional<mean_field::mapping::ElementDisplacementData>
displacementVariationData;
std::optional<mean_field::mapping::ElementDisplacementData> displacementVariationData;
if (request.displacementVariationTrue != nullptr) {
displacementVariationData.emplace(
mean_field::mapping::
ElementDisplacementDataFromElementVDofs(
displacementElement, elementDisplacementVariation
)
mean_field::mapping::ElementDisplacementDataFromElementVDofs(
displacementElement, elementDisplacementVariation
)
);
}
@@ -528,32 +415,25 @@ namespace {
potentialShape.SetSize(potentialElement.GetDof());
const mfem::IntegrationRule &integrationRule = get_hydrostatic_rule(
f, enthalpyElement, potentialElement, *transformation
);
const mfem::IntegrationRule &integrationRule =
get_hydrostatic_rule(f, enthalpyElement, potentialElement, *transformation);
for (int quadraturePoint = 0;
quadraturePoint < integrationRule.GetNPoints();
++quadraturePoint) {
const mfem::IntegrationPoint &integrationPoint =
integrationRule.IntPoint(quadraturePoint);
for (int quadraturePoint = 0; quadraturePoint < integrationRule.GetNPoints(); ++quadraturePoint) {
const mfem::IntegrationPoint &integrationPoint = integrationRule.IntPoint(quadraturePoint);
transformation->SetIntPoint(&integrationPoint);
mean_field::mapping::VolumeMappingContext mappingContext;
const mean_field::mapping::MappingStatus mappingStatus =
domainMapper.EvaluateVolume(
mappingData, *transformation, integrationPoint,
workspace, mappingContext
);
const mean_field::mapping::MappingStatus mappingStatus = domainMapper.EvaluateVolume(
mappingData, *transformation, integrationPoint, workspace, mappingContext
);
MFEM_VERIFY(
mappingStatus == mean_field::mapping::MappingStatus::valid,
"The base mapping is invalid in the "
"hydrostatic kernel. Element: "
<< elementId
<< ", quadrature point: " << quadraturePoint
<< elementId << ", quadrature point: " << quadraturePoint
<< ", status: " << static_cast<int>(mappingStatus)
);
@@ -564,27 +444,18 @@ namespace {
double baseIntegrand = 0.0;
if (requiresBaseState) {
const double enthalpyValue =
elementBaseEnthalpy * enthalpyShape;
const double enthalpyValue = elementBaseEnthalpy * enthalpyShape;
const double potentialValue =
elementBasePotential * potentialShape;
const double potentialValue = elementBasePotential * potentialShape;
const double rotationPotential =
request.rotation->potential(
mappingContext.mapping.physical_position
);
request.rotation->potential(mappingContext.mapping.physical_position);
baseIntegrand = enthalpyValue + potentialValue -
rotationPotential -
request.bernoulliConstant;
baseIntegrand = enthalpyValue + potentialValue - rotationPotential - request.bernoulliConstant;
}
if (request.buildResidual) {
elementResult.Add(
mappingContext.quadrature.weight * baseIntegrand,
enthalpyShape
);
elementResult.Add(mappingContext.quadrature.weight * baseIntegrand, enthalpyShape);
continue;
}
@@ -592,45 +463,35 @@ namespace {
double materialVariation = -request.constantVariation;
if (request.enthalpyVariationTrue != nullptr) {
materialVariation +=
elementEnthalpyVariation * enthalpyShape;
materialVariation += elementEnthalpyVariation * enthalpyShape;
}
if (request.potentialVariationTrue != nullptr) {
materialVariation +=
elementPotentialVariation * potentialShape;
materialVariation += elementPotentialVariation * potentialShape;
}
double weightedVariation =
mappingContext.quadrature.weight * materialVariation;
double weightedVariation = mappingContext.quadrature.weight * materialVariation;
if (request.displacementVariationTrue != nullptr) {
mean_field::mapping::VolumeMappingVariation
mappingVariation;
mean_field::mapping::VolumeMappingVariation mappingVariation;
const mean_field::mapping::MappingStatus variationStatus =
domainMapper.EvaluateVolumeVariation(
mappingData, *displacementVariationData,
*transformation, integrationPoint, mappingContext,
workspace, mappingVariation
);
const mean_field::mapping::MappingStatus variationStatus = domainMapper.EvaluateVolumeVariation(
mappingData, *displacementVariationData, *transformation, integrationPoint, mappingContext,
workspace, mappingVariation
);
MFEM_VERIFY(
variationStatus ==
mean_field::mapping::MappingStatus::valid,
variationStatus == mean_field::mapping::MappingStatus::valid,
"The mapping variation is invalid "
"in the hydrostatic kernel."
);
const double rotationVariation =
request.rotation->potential_directional_derivative(
mappingContext.mapping.physical_position,
mappingVariation.mapping.physical_position_variation
);
const double rotationVariation = request.rotation->potential_directional_derivative(
mappingContext.mapping.physical_position, mappingVariation.mapping.physical_position_variation
);
weightedVariation +=
baseIntegrand * mappingVariation.weight_variation -
rotationVariation * mappingContext.quadrature.weight;
weightedVariation += baseIntegrand * mappingVariation.weight_variation -
rotationVariation * mappingContext.quadrature.weight;
}
elementResult.Add(weightedVariation, enthalpyShape);
@@ -666,9 +527,7 @@ namespace mean_field::operators::kernels {
request.bernoulliConstant = bernoulliConstant;
request.buildResidual = true;
assemble_hydrostatic_form(
f, domainMapper, displacementTrue, request, residual
);
assemble_hydrostatic_form(f, domainMapper, displacementTrue, request, residual);
}
void apply_hydrostatic_equilibrium_enthalpy_action(
@@ -682,9 +541,7 @@ namespace mean_field::operators::kernels {
request.enthalpyVariationTrue = &enthalpyVariationTrue;
assemble_hydrostatic_form(
f, domainMapper, displacementTrue, request, action
);
assemble_hydrostatic_form(f, domainMapper, displacementTrue, request, action);
}
void apply_hydrostatic_equilibrium_potential_action(
@@ -698,9 +555,7 @@ namespace mean_field::operators::kernels {
request.potentialVariationTrue = &potentialVariationTrue;
assemble_hydrostatic_form(
f, domainMapper, displacementTrue, request, action
);
assemble_hydrostatic_form(f, domainMapper, displacementTrue, request, action);
}
void apply_hydrostatic_equilibrium_constant_action(
@@ -714,9 +569,7 @@ namespace mean_field::operators::kernels {
request.constantVariation = constantVariation;
assemble_hydrostatic_form(
f, domainMapper, displacementTrue, request, action
);
assemble_hydrostatic_form(f, domainMapper, displacementTrue, request, action);
}
void apply_hydrostatic_equilibrium_displacement_action(
@@ -738,9 +591,7 @@ namespace mean_field::operators::kernels {
request.displacementVariationTrue = &displacementVariationTrue;
request.bernoulliConstant = baseBernoulliConstant;
assemble_hydrostatic_form(
f, domainMapper, baseDisplacementTrue, request, action
);
assemble_hydrostatic_form(f, domainMapper, baseDisplacementTrue, request, action);
}
void apply_hydrostatic_equilibrium_action(
@@ -768,8 +619,6 @@ namespace mean_field::operators::kernels {
request.bernoulliConstant = baseBernoulliConstant;
request.constantVariation = constantVariation;
assemble_hydrostatic_form(
f, domainMapper, baseDisplacementTrue, request, action
);
assemble_hydrostatic_form(f, domainMapper, baseDisplacementTrue, request, action);
}
} // namespace mean_field::operators::kernels

View File

@@ -3,6 +3,7 @@ module;
#include <array>
#include <cmath>
#include <limits>
#include <optional>
#include <mfem.hpp>
@@ -11,20 +12,20 @@ module mean_field;
import :operators.kernels.pressure_force;
namespace {
enum class PressureForceAction { residual, enthalpy, displacement };
void true_to_local(
const mfem::ParFiniteElementSpace &finiteElementSpace,
const mfem::Vector &trueVector,
mfem::Vector &localVector
) {
MFEM_VERIFY(
trueVector.Size() == finiteElementSpace.GetTrueVSize(),
"The pressure-force true vector has the wrong size."
trueVector.Size() == finiteElementSpace.GetTrueVSize(), "The pressure-force true vector has the wrong size."
);
localVector.SetSize(finiteElementSpace.GetVSize());
const mfem::Operator *prolongation =
finiteElementSpace.GetProlongationMatrix();
const mfem::Operator *prolongation = finiteElementSpace.GetProlongationMatrix();
if (prolongation != nullptr) {
prolongation->Mult(trueVector, localVector);
@@ -39,15 +40,13 @@ namespace {
mfem::Vector &trueVector
) {
MFEM_VERIFY(
localVector.Size() == finiteElementSpace.GetVSize(),
"The pressure-force local vector has the wrong size."
localVector.Size() == finiteElementSpace.GetVSize(), "The pressure-force local vector has the wrong size."
);
trueVector.SetSize(finiteElementSpace.GetTrueVSize());
trueVector = 0.0;
trueVector = 0.0;
const mfem::Operator *prolongation =
finiteElementSpace.GetProlongationMatrix();
const mfem::Operator *prolongation = finiteElementSpace.GetProlongationMatrix();
if (prolongation != nullptr) {
prolongation->MultTranspose(localVector, trueVector);
@@ -68,15 +67,14 @@ namespace {
}
if (ordering == mfem::Ordering::byVDIM) {
return component + scalarDof * dimension;
return scalarDof * dimension + component;
}
MFEM_ABORT("The displacement space uses an unsupported ordering.");
return -1;
}
[[nodiscard]] int get_pressure_extra_order(
const mean_field::physics::PolytropicBarotrope &barotrope
) {
[[nodiscard]] int get_pressure_extra_order(const mean_field::eos::Polytrope &barotrope) {
/*
* Pressure has the enthalpy dependence
*
@@ -87,15 +85,11 @@ namespace {
* contribution is therefore n times that order.
*/
const double extraOrder =
barotrope.polytropic_index() *
static_cast<double>(
mean_field::field::Enthalpy::Scalar::familyOrder
);
barotrope.polytropic_index() * static_cast<double>(mean_field::field::Enthalpy::Scalar::familyOrder);
MFEM_VERIFY(
std::isfinite(extraOrder) && extraOrder >= 0.0 &&
extraOrder <=
static_cast<double>(std::numeric_limits<int>::max()),
extraOrder <= static_cast<double>(std::numeric_limits<int>::max()),
"The pressure EOS effective polynomial order is invalid."
);
@@ -104,44 +98,37 @@ namespace {
[[nodiscard]] const mfem::IntegrationRule &get_pressure_force_rule(
const mean_field::fem::FEM &f,
const mean_field::physics::PolytropicBarotrope &barotrope,
const mean_field::eos::Polytrope &barotrope,
const mfem::FiniteElement &enthalpyElement,
const mfem::FiniteElement &displacementElement,
const mfem::ElementTransformation &transformation
) {
using EnthalpyField =
mean_field::field::Field<mean_field::field::Enthalpy>;
using EnthalpyField = mean_field::field::Field<mean_field::field::Enthalpy>;
MFEM_VERIFY(
enthalpyElement.GetOrder() ==
mean_field::field::Enthalpy::Scalar::familyOrder,
enthalpyElement.GetOrder() == mean_field::field::Enthalpy::Scalar::familyOrder,
"The pressure-force enthalpy element does not match the "
"registered enthalpy field."
);
MFEM_VERIFY(
displacementElement.GetOrder() ==
mean_field::field::Displacement::Vector::familyOrder,
displacementElement.GetOrder() == mean_field::field::Displacement::Vector::familyOrder,
"The pressure-force test element does not match the "
"registered displacement field."
);
const mean_field::quadrature::Query query = EnthalpyField::make_query<
mean_field::field::Enthalpy::Form::PressureForce>(
mean_field::quadrature::QuadratureRole::discretization,
transformation.OrderW(),
std::array<int, 1>{get_pressure_extra_order(barotrope)},
mean_field::utils::DOMAINS::STELLAR,
mean_field::quadrature::MappingKind::general
);
const mean_field::quadrature::Query query =
EnthalpyField::make_query<mean_field::field::Enthalpy::Form::PressureForce>(
mean_field::quadrature::QuadratureRole::discretization, transformation.OrderW(),
std::array<int, 1>{get_pressure_extra_order(barotrope)}, mean_field::utils::DOMAINS::STELLAR,
mean_field::quadrature::MappingKind::general
);
const mean_field::quadrature::MfemRule rule =
f.quadratureFactory->get(query, transformation.GetGeometryType());
const mean_field::quadrature::MfemRule rule = f.quadratureFactory->get(query, transformation.GetGeometryType());
MFEM_VERIFY(
rule.integration_rule != nullptr,
"The quadrature policy did not return a pressure-force "
"integration rule."
rule.integration_rule != nullptr, "The quadrature policy did not return a pressure-force "
"integration rule."
);
return *rule.integration_rule;
@@ -153,38 +140,31 @@ namespace {
const mfem::Vector &enthalpyTrue,
const mfem::Vector &displacementTrue
) {
MFEM_VERIFY(f.mesh != nullptr, "The pressure-force kernel requires a mesh.");
MFEM_VERIFY(
f.mesh != nullptr, "The pressure-force kernel requires a mesh."
f.enthalpyFes != nullptr, "The pressure-force kernel requires the enthalpy "
"finite-element space."
);
MFEM_VERIFY(
f.enthalpyFes != nullptr,
"The pressure-force kernel requires the enthalpy "
"finite-element space."
f.displacementFes != nullptr, "The pressure-force kernel requires the displacement "
"finite-element space."
);
MFEM_VERIFY(
f.displacementFes != nullptr,
"The pressure-force kernel requires the displacement "
"finite-element space."
f.compactificationFes != nullptr, "The pressure-force kernel requires the compactification "
"finite-element space."
);
MFEM_VERIFY(
f.compactificationFes != nullptr,
"The pressure-force kernel requires the compactification "
"finite-element space."
f.compactificationCoordinate != nullptr, "The pressure-force kernel requires the compactification "
"coordinate."
);
MFEM_VERIFY(
f.compactificationCoordinate != nullptr,
"The pressure-force kernel requires the compactification "
"coordinate."
);
MFEM_VERIFY(
f.quadratureFactory != nullptr,
"The pressure-force kernel requires the quadrature "
"rule factory."
f.quadratureFactory != nullptr, "The pressure-force kernel requires the quadrature "
"rule factory."
);
MFEM_VERIFY(
@@ -204,73 +184,105 @@ namespace {
);
MFEM_VERIFY(
f.displacementFes->GetVDim() == f.mesh->Dimension(),
"The displacement vector dimension does not match the "
"mesh dimension."
f.displacementFes->GetVDim() == f.mesh->Dimension(), "The displacement vector dimension does not match the "
"mesh dimension."
);
/*
* ElementDisplacementDataFromElementVDofs currently consumes the
* registered byNODES layout. Keep this explicit so a future
* registry change fails immediately rather than silently
* corrupting the geometry.
*/
MFEM_VERIFY(
f.displacementFes->GetOrdering() == mfem::Ordering::byNODES,
"The pressure-force kernel requires the registered byNODES "
"displacement ordering."
);
}
} // namespace
namespace mean_field::operators::kernels {
void apply_pressure_force_residual(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const physics::PolytropicBarotrope &barotrope,
const mfem::Vector &enthalpyTrue,
void apply_pressure_force_action(
const mean_field::fem::FEM &f,
const mean_field::mapping::DomainMapperStateless &domainMapper,
const mean_field::eos::Polytrope &barotrope,
const PressureForceAction pressureForceAction,
const mfem::Vector &baseEnthalpyTrue,
const mfem::Vector *enthalpyVariationTrue,
const mfem::Vector *displacementVariationTrue,
const mfem::Vector &displacementTrue,
mfem::Vector &residualTrue
mfem::Vector &actionTrue
) {
validate_inputs(f, domainMapper, enthalpyTrue, displacementTrue);
validate_inputs(f, domainMapper, baseEnthalpyTrue, displacementTrue);
mfem::Vector enthalpyLocal;
if (pressureForceAction == PressureForceAction::enthalpy) {
MFEM_VERIFY(
enthalpyVariationTrue != nullptr && enthalpyVariationTrue->Size() == f.enthalpyFes->GetTrueVSize(),
"The pressure-force enthalpy variation has the wrong size."
);
}
if (pressureForceAction == PressureForceAction::displacement) {
MFEM_VERIFY(
displacementVariationTrue != nullptr &&
displacementVariationTrue->Size() == f.displacementFes->GetTrueVSize(),
"The pressure-force displacement variation has the wrong "
"size."
);
}
mfem::Vector baseEnthalpyLocal;
mfem::Vector enthalpyVariationLocal;
mfem::Vector displacementLocal;
mfem::Vector displacementVariationLocal;
true_to_local(*f.enthalpyFes, enthalpyTrue, enthalpyLocal);
true_to_local(*f.enthalpyFes, baseEnthalpyTrue, baseEnthalpyLocal);
if (enthalpyVariationTrue != nullptr) {
true_to_local(*f.enthalpyFes, *enthalpyVariationTrue, enthalpyVariationLocal);
}
true_to_local(*f.displacementFes, displacementTrue, displacementLocal);
mfem::Vector localResidual(f.displacementFes->GetVSize());
localResidual = 0.0;
if (displacementVariationTrue != nullptr) {
true_to_local(*f.displacementFes, *displacementVariationTrue, displacementVariationLocal);
}
mapping::DomainMapperStateless::Workspace workspace(
f.mesh->Dimension()
);
mfem::Vector localAction(f.displacementFes->GetVSize());
localAction = 0.0;
mfem::Array<int> enthalpyDofs;
mean_field::mapping::DomainMapperStateless::Workspace workspace(f.mesh->Dimension());
mfem::Array<int> enthalpyDofsofs;
mfem::Array<int> displacementDofs;
mfem::Array<int> compactificationDofs;
mfem::Vector elementEnthalpy;
mfem::Vector elementBaseEnthalpy;
mfem::Vector elementEnthalpyVariation;
mfem::Vector elementDisplacement;
mfem::Vector elementDisplacementVariation;
mfem::Vector elementCompactification;
mfem::Vector elementResidual;
mfem::Vector elementAction;
mfem::Vector enthalpyShape;
mfem::Array<int> enthalpyDofs;
mfem::DenseMatrix displacementDShapeReference;
mfem::DenseMatrix displacementDShapePhysical;
mfem::DenseMatrix displacementDShapePhysicalVariation;
mapping::VolumeMappingContext mappingContext;
mean_field::mapping::VolumeMappingContext mappingContext;
const int dimension = f.mesh->Dimension();
const int vacuumAttribute = domainMapper.GetVacuumElementAttribute();
const int dimension = f.mesh->Dimension();
const int vacuumAttribute = domainMapper.GetVacuumElementAttribute();
const mfem::Ordering::Type displacementOrdering =
f.displacementFes->GetOrdering();
const mfem::Ordering::Type displacementOrdering = f.displacementFes->GetOrdering();
for (int elementId = 0; elementId < f.mesh->GetNE(); ++elementId) {
mfem::ElementTransformation *transformation =
f.mesh->GetElementTransformation(elementId);
mfem::ElementTransformation *transformation = f.mesh->GetElementTransformation(elementId);
MFEM_VERIFY(
transformation != nullptr,
"The pressure-force kernel received a null element "
"transformation."
transformation != nullptr, "The pressure-force kernel received a null element "
"transformation."
);
/*
@@ -281,128 +293,131 @@ namespace mean_field::operators::kernels {
continue;
}
const mfem::FiniteElement &enthalpyElement =
*f.enthalpyFes->GetFE(elementId);
const mfem::FiniteElement &enthalpyElement = *f.enthalpyFes->GetFE(elementId);
const mfem::FiniteElement &displacementElement =
*f.displacementFes->GetFE(elementId);
const mfem::FiniteElement &displacementElement = *f.displacementFes->GetFE(elementId);
const mfem::FiniteElement &compactificationElement =
*f.compactificationFes->GetFE(elementId);
const mfem::FiniteElement &compactificationElement = *f.compactificationFes->GetFE(elementId);
mfem::DofTransformation *enthalpyDofTransformation =
f.enthalpyFes->GetElementDofs(elementId, enthalpyDofs);
mfem::DofTransformation *enthalpyDofTransformation = f.enthalpyFes->GetElementDofs(elementId, enthalpyDofs);
mfem::DofTransformation *displacementDofTransformation =
f.displacementFes->GetElementVDofs(elementId, displacementDofs);
mfem::DofTransformation *compactificationDofTransformation =
f.compactificationFes->GetElementDofs(
elementId, compactificationDofs
);
f.compactificationFes->GetElementDofs(elementId, compactificationDofs);
enthalpyLocal.GetSubVector(enthalpyDofs, elementEnthalpy);
baseEnthalpyLocal.GetSubVector(enthalpyDofs, elementBaseEnthalpy);
displacementLocal.GetSubVector(
displacementDofs, elementDisplacement
);
if (enthalpyVariationTrue != nullptr) {
enthalpyVariationLocal.GetSubVector(enthalpyDofs, elementEnthalpyVariation);
}
f.compactificationCoordinate->GetSubVector(
compactificationDofs, elementCompactification
);
displacementLocal.GetSubVector(displacementDofs, elementDisplacement);
if (displacementVariationTrue != nullptr) {
displacementVariationLocal.GetSubVector(displacementDofs, elementDisplacementVariation);
}
f.compactificationCoordinate->GetSubVector(compactificationDofs, elementCompactification);
if (enthalpyDofTransformation != nullptr) {
enthalpyDofTransformation->InvTransformPrimal(elementEnthalpy);
enthalpyDofTransformation->InvTransformPrimal(elementBaseEnthalpy);
if (enthalpyVariationTrue != nullptr) {
enthalpyDofTransformation->InvTransformPrimal(elementEnthalpyVariation);
}
}
if (displacementDofTransformation != nullptr) {
displacementDofTransformation->InvTransformPrimal(
elementDisplacement
);
displacementDofTransformation->InvTransformPrimal(elementDisplacement);
if (displacementVariationTrue != nullptr) {
displacementDofTransformation->InvTransformPrimal(elementDisplacementVariation);
}
}
if (compactificationDofTransformation != nullptr) {
compactificationDofTransformation->InvTransformPrimal(
elementCompactification
);
compactificationDofTransformation->InvTransformPrimal(elementCompactification);
}
const mapping::ElementDisplacementData displacementData =
mapping::ElementDisplacementDataFromElementVDofs(
displacementElement, elementDisplacement
);
const mean_field::mapping::ElementDisplacementData displacementData =
mean_field::mapping::ElementDisplacementDataFromElementVDofs(displacementElement, elementDisplacement);
const mapping::ElementCompactificationData compactificationData(
const mean_field::mapping::ElementCompactificationData compactificationData(
compactificationElement, elementCompactification
);
const mapping::ElementMappingData mappingData{
.displacement = displacementData,
.compactification = compactificationData
const mean_field::mapping::ElementMappingData mappingData{
.displacement = displacementData, .compactification = compactificationData
};
std::optional<mean_field::mapping::ElementDisplacementData> displacementVariationData;
if (displacementVariationTrue != nullptr) {
displacementVariationData.emplace(
mean_field::mapping::ElementDisplacementDataFromElementVDofs(
displacementElement, elementDisplacementVariation
)
);
}
const int scalarDisplacementDofCount = displacementElement.GetDof();
MFEM_VERIFY(
displacementDofs.Size() ==
scalarDisplacementDofCount * dimension,
displacementDofs.Size() == scalarDisplacementDofCount * dimension,
"The pressure-force element displacement vector has "
"the wrong size."
);
enthalpyShape.SetSize(enthalpyElement.GetDof());
displacementDShapeReference.SetSize(
scalarDisplacementDofCount, dimension
);
displacementDShapeReference.SetSize(scalarDisplacementDofCount, dimension);
displacementDShapePhysical.SetSize(
scalarDisplacementDofCount, dimension
);
displacementDShapePhysical.SetSize(scalarDisplacementDofCount, dimension);
elementResidual.SetSize(displacementDofs.Size());
elementResidual = 0.0;
displacementDShapePhysicalVariation.SetSize(scalarDisplacementDofCount, dimension);
elementAction.SetSize(displacementDofs.Size());
elementAction = 0.0;
const mfem::IntegrationRule &integrationRule =
get_pressure_force_rule(
f, barotrope, enthalpyElement, displacementElement,
*transformation
);
get_pressure_force_rule(f, barotrope, enthalpyElement, displacementElement, *transformation);
for (int quadratureIndex = 0;
quadratureIndex < integrationRule.GetNPoints();
++quadratureIndex) {
const mfem::IntegrationPoint &integrationPoint =
integrationRule.IntPoint(quadratureIndex);
for (int quadratureIndex = 0; quadratureIndex < integrationRule.GetNPoints(); ++quadratureIndex) {
const mfem::IntegrationPoint &integrationPoint = integrationRule.IntPoint(quadratureIndex);
transformation->SetIntPoint(&integrationPoint);
const mapping::MappingStatus mappingStatus =
domainMapper.EvaluateVolume(
mappingData, *transformation, integrationPoint,
workspace, mappingContext
);
const mean_field::mapping::MappingStatus mappingStatus = domainMapper.EvaluateVolume(
mappingData, *transformation, integrationPoint, workspace, mappingContext
);
MFEM_VERIFY(
mappingStatus == mapping::MappingStatus::valid,
mappingStatus == mean_field::mapping::MappingStatus::valid,
"Stateless mapping failed in the pressure-force "
"kernel. Element: "
<< elementId
<< ", attribute: " << transformation->Attribute
<< ", quadrature point: " << quadratureIndex
<< ", status: " << static_cast<int>(mappingStatus)
<< elementId << ", attribute: " << transformation->Attribute
<< ", quadrature point: " << quadratureIndex << ", status: " << static_cast<int>(mappingStatus)
);
enthalpyElement.CalcShape(integrationPoint, enthalpyShape);
const double enthalpyValue = elementEnthalpy * enthalpyShape;
const double enthalpyValue = elementBaseEnthalpy * enthalpyShape;
const double pressureValue =
barotrope.pressure_from_enthalpy(enthalpyValue);
double pressureFactor = 0.0;
displacementElement.CalcDShape(
integrationPoint, displacementDShapeReference
);
if (pressureForceAction == PressureForceAction::residual ||
pressureForceAction == PressureForceAction::displacement) {
pressureFactor = barotrope.pressure_from_enthalpy(enthalpyValue);
} else {
const double enthalpyVariationValue = elementEnthalpyVariation * enthalpyShape;
pressureFactor =
barotrope.pressure_derivative_from_enthalpy(enthalpyValue) * enthalpyVariationValue;
}
displacementElement.CalcDShape(integrationPoint, displacementDShapeReference);
/*
* Row i of DShape is grad_reference(N_i). Multiplication
@@ -411,17 +426,45 @@ namespace mean_field::operators::kernels {
* grad_physical(N_i)
* = grad_reference(N_i) J^{-1}.
*/
mfem::Mult(
displacementDShapeReference,
mappingContext.quadrature.J_inv, displacementDShapePhysical
);
mfem::Mult(displacementDShapeReference, mappingContext.quadrature.J_inv, displacementDShapePhysical);
const double weightedPressure =
pressureValue * mappingContext.quadrature.weight;
std::optional<mean_field::mapping::VolumeMappingVariation> mappingVariation;
if (pressureForceAction == PressureForceAction::displacement) {
mappingVariation.emplace();
const mean_field::mapping::MappingStatus variationStatus = domainMapper.EvaluateVolumeVariation(
mappingData, *displacementVariationData, *transformation, integrationPoint, mappingContext,
workspace, *mappingVariation
);
MFEM_VERIFY(
variationStatus == mean_field::mapping::MappingStatus::valid,
"Stateless mapping variation failed in the "
"pressure-force kernel. Element: "
<< elementId << ", attribute: " << transformation->Attribute << ", quadrature point: "
<< quadratureIndex << ", status: " << static_cast<int>(variationStatus)
);
/*
* Differentiating
*
* grad_x(N_i) = grad_reference(N_i) J^{-1}
*
* at the frozen base geometry gives the physical
* test-gradient variation used by the geometric
* pressure block.
*/
mfem::Mult(
displacementDShapeReference, mappingVariation->inverse_element_jacobian_variation,
displacementDShapePhysicalVariation
);
}
const double weightedPressureFactor = pressureFactor * mappingContext.quadrature.weight;
MFEM_VERIFY(
std::isfinite(pressureValue) &&
std::isfinite(weightedPressure),
std::isfinite(pressureFactor) && std::isfinite(weightedPressureFactor),
"The pressure-force kernel encountered a non-finite "
"quadrature value."
);
@@ -436,29 +479,96 @@ namespace mean_field::operators::kernels {
* R_(i,c)
* = -integral P partial_c N_i dV.
*/
for (int scalarDof = 0; scalarDof < scalarDisplacementDofCount;
++scalarDof) {
for (int component = 0; component < dimension;
++component) {
for (int scalarDof = 0; scalarDof < scalarDisplacementDofCount; ++scalarDof) {
for (int component = 0; component < dimension; ++component) {
const int vectorDof = vector_dof_index(
displacementOrdering, scalarDof, component,
scalarDisplacementDofCount, dimension
displacementOrdering, scalarDof, component, scalarDisplacementDofCount, dimension
);
elementResidual(vectorDof) -=
weightedPressure *
displacementDShapePhysical(scalarDof, component);
if (pressureForceAction == PressureForceAction::displacement) {
/*
* Differentiate the complete discrete factor
*
* grad_x(N_i) dV_x.
*
* The enthalpy DOFs, and therefore P(h), are
* frozen in this Jacobian column.
*/
const double gradientWeightVariation =
mappingContext.quadrature.weight *
displacementDShapePhysicalVariation(scalarDof, component) +
mappingVariation->weight_variation * displacementDShapePhysical(scalarDof, component);
const double contribution = pressureFactor * gradientWeightVariation;
MFEM_VERIFY(
std::isfinite(gradientWeightVariation) && std::isfinite(contribution),
"The pressure-force geometry action "
"encountered a non-finite contribution."
);
elementAction(vectorDof) -= contribution;
} else {
elementAction(vectorDof) -=
weightedPressureFactor * displacementDShapePhysical(scalarDof, component);
}
}
}
}
if (displacementDofTransformation != nullptr) {
displacementDofTransformation->TransformDual(elementResidual);
displacementDofTransformation->TransformDual(elementAction);
}
localResidual.AddElementVector(displacementDofs, elementResidual);
localAction.AddElementVector(displacementDofs, elementAction);
}
local_to_true(*f.displacementFes, localResidual, residualTrue);
local_to_true(*f.displacementFes, localAction, actionTrue);
}
} // namespace mean_field::operators::kernels
} // namespace
namespace mean_field::operators::kernels {
void apply_pressure_force_residual(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const eos::Polytrope &barotrope,
const mfem::Vector &enthalpyTrue,
const mfem::Vector &displacementTrue,
mfem::Vector &residualTrue
) {
apply_pressure_force_action(
f, domainMapper, barotrope, PressureForceAction::residual, enthalpyTrue, nullptr, nullptr, displacementTrue,
residualTrue
);
}
void apply_pressure_force_enthalpy_action(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const eos::Polytrope &barotrope,
const mfem::Vector &baseEnthalpyTrue,
const mfem::Vector &enthalpyVariationTrue,
const mfem::Vector &displacementTrue,
mfem::Vector &actionTrue
) {
apply_pressure_force_action(
f, domainMapper, barotrope, PressureForceAction::enthalpy, baseEnthalpyTrue, &enthalpyVariationTrue,
nullptr, displacementTrue, actionTrue
);
}
void apply_pressure_force_displacement_action(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const eos::Polytrope &barotrope,
const mfem::Vector &baseEnthalpyTrue,
const mfem::Vector &displacementVariationTrue,
const mfem::Vector &displacementTrue,
mfem::Vector &actionTrue
) {
apply_pressure_force_action(
f, domainMapper, barotrope, PressureForceAction::displacement, baseEnthalpyTrue, nullptr,
&displacementVariationTrue, displacementTrue, actionTrue
);
}
} // namespace mean_field::operators::kernels

View File

@@ -0,0 +1,590 @@
module;
#include <array>
#include <cmath>
#include <optional>
#include <mfem.hpp>
module mean_field;
import :operators.kernels.rotational_displacement_force;
namespace {
enum class RotationalDisplacementForceAction { residual, density, displacement, complete };
void true_to_local(
const mfem::ParFiniteElementSpace &finiteElementSpace,
const mfem::Vector &trueVector,
mfem::Vector &localVector
) {
MFEM_VERIFY(
trueVector.Size() == finiteElementSpace.GetTrueVSize(),
"The rotational-displacement-force true vector has the wrong "
"size."
);
localVector.SetSize(finiteElementSpace.GetVSize());
const mfem::Operator *prolongation = finiteElementSpace.GetProlongationMatrix();
if (prolongation != nullptr) {
prolongation->Mult(trueVector, localVector);
} else {
localVector = trueVector;
}
}
void local_to_true(
const mfem::ParFiniteElementSpace &finiteElementSpace,
const mfem::Vector &localVector,
mfem::Vector &trueVector
) {
MFEM_VERIFY(
localVector.Size() == finiteElementSpace.GetVSize(),
"The rotational-displacement-force local vector has the wrong "
"size."
);
trueVector.SetSize(finiteElementSpace.GetTrueVSize());
trueVector = 0.0;
const mfem::Operator *prolongation = finiteElementSpace.GetProlongationMatrix();
if (prolongation != nullptr) {
prolongation->MultTranspose(localVector, trueVector);
} else {
trueVector = localVector;
}
}
[[nodiscard]] int vector_dof_index(
const mfem::Ordering::Type ordering,
const int scalarDof,
const int component,
const int scalarDofCount,
const int dimension
) {
if (ordering == mfem::Ordering::byNODES) {
return scalarDof + component * scalarDofCount;
}
if (ordering == mfem::Ordering::byVDIM) {
return scalarDof * dimension + component;
}
MFEM_ABORT(
"The rotational-displacement-force test space uses an "
"unsupported ordering."
);
return -1;
}
[[nodiscard]] const mfem::IntegrationRule &get_rotation_force_rule(
const mean_field::fem::FEM &f,
const mfem::FiniteElement &densityElement,
const mfem::FiniteElement &displacementElement,
const mfem::ElementTransformation &transformation
) {
using DisplacementField = mean_field::field::Field<mean_field::field::Displacement>;
MFEM_VERIFY(
densityElement.GetOrder() == mean_field::field::Density::Scalar::familyOrder,
"The rotational-displacement-force density element does not "
"match the registered density field."
);
MFEM_VERIFY(
displacementElement.GetOrder() == mean_field::field::Displacement::Vector::familyOrder,
"The rotational-displacement-force test element does not match "
"the registered displacement field."
);
/*
* grad(Psi_rotation) is linear in physical position, so it adds one
* dynamic polynomial-order contribution.
*/
const mean_field::quadrature::Query query =
DisplacementField::make_query<mean_field::field::Displacement::Form::CentrifugalForce>(
mean_field::quadrature::QuadratureRole::discretization, transformation.OrderW(), std::array<int, 1>{1},
mean_field::utils::DOMAINS::STELLAR, mean_field::quadrature::MappingKind::general
);
const mean_field::quadrature::MfemRule rule = f.quadratureFactory->get(query, transformation.GetGeometryType());
MFEM_VERIFY(
rule.integration_rule != nullptr, "The quadrature policy did not return a rotational-"
"displacement-force integration rule."
);
return *rule.integration_rule;
}
void validate_finite_vector(
const mfem::Vector &vector,
const char *message
) {
for (int index = 0; index < vector.Size(); ++index) {
MFEM_VERIFY(std::isfinite(vector(index)), message);
}
}
void validate_common_inputs(
const mean_field::fem::FEM &f,
const mean_field::mapping::DomainMapperStateless &domainMapper,
const mfem::Vector &displacementTrue
) {
MFEM_VERIFY(f.mesh != nullptr, "The rotational-displacement-force kernel requires a mesh.");
MFEM_VERIFY(
f.mesh->Dimension() == 3, "The rotational-displacement-force kernel requires a "
"three-dimensional mesh."
);
MFEM_VERIFY(
f.densityFes != nullptr, "The rotational-displacement-force kernel requires the density "
"finite-element space."
);
MFEM_VERIFY(
f.displacementFes != nullptr, "The rotational-displacement-force kernel requires the "
"displacement finite-element space."
);
MFEM_VERIFY(
f.compactificationFes != nullptr && f.compactificationCoordinate != nullptr,
"The rotational-displacement-force kernel requires the "
"compactification coordinate."
);
MFEM_VERIFY(
f.quadratureFactory != nullptr, "The rotational-displacement-force kernel requires the "
"quadrature-rule factory."
);
MFEM_VERIFY(
displacementTrue.Size() == f.displacementFes->GetTrueVSize(),
"The rotational-displacement-force displacement vector has the "
"wrong size."
);
MFEM_VERIFY(
domainMapper.GetDimension() == f.mesh->Dimension(),
"The rotational-displacement-force mapper dimension does not "
"match the mesh dimension."
);
MFEM_VERIFY(
f.displacementFes->GetVDim() == f.mesh->Dimension(),
"The rotational-displacement-force displacement dimension does "
"not match the mesh dimension."
);
validate_finite_vector(
displacementTrue, "The rotational-displacement-force displacement contains a "
"non-finite value."
);
}
void validate_density(
const mean_field::fem::FEM &f,
const mfem::Vector &density,
const char *message
) {
MFEM_VERIFY(density.Size() == f.densityFes->GetTrueVSize(), message);
validate_finite_vector(density, message);
}
void apply_rotational_displacement_force_action(
const mean_field::fem::FEM &f,
const mean_field::mapping::DomainMapperStateless &domainMapper,
const mean_field::physics::RigidRotation &rotation,
const RotationalDisplacementForceAction requestedAction,
const mfem::Vector *baseDensityTrue,
const mfem::Vector *densityVariationTrue,
const mfem::Vector *displacementVariationTrue,
const mfem::Vector &displacementTrue,
mfem::Vector &actionTrue
) {
validate_common_inputs(f, domainMapper, displacementTrue);
const bool needsBaseDensity = requestedAction == RotationalDisplacementForceAction::residual ||
requestedAction == RotationalDisplacementForceAction::displacement ||
requestedAction == RotationalDisplacementForceAction::complete;
const bool needsDensityVariation = requestedAction == RotationalDisplacementForceAction::density ||
requestedAction == RotationalDisplacementForceAction::complete;
const bool needsDisplacementVariation = requestedAction == RotationalDisplacementForceAction::displacement ||
requestedAction == RotationalDisplacementForceAction::complete;
if (needsBaseDensity) {
MFEM_VERIFY(
baseDensityTrue != nullptr, "The rotational-displacement-force action requires a base "
"density."
);
validate_density(f, *baseDensityTrue, "The rotational-displacement-force base density is invalid.");
}
if (needsDensityVariation) {
MFEM_VERIFY(
densityVariationTrue != nullptr, "The rotational-displacement-force action requires a "
"density variation."
);
validate_density(
f, *densityVariationTrue,
"The rotational-displacement-force density variation is "
"invalid."
);
}
if (needsDisplacementVariation) {
MFEM_VERIFY(
displacementVariationTrue != nullptr &&
displacementVariationTrue->Size() == f.displacementFes->GetTrueVSize(),
"The rotational-displacement-force displacement variation "
"is invalid."
);
validate_finite_vector(
*displacementVariationTrue, "The rotational-displacement-force displacement variation "
"contains a non-finite value."
);
}
mfem::Vector baseDensityLocal;
mfem::Vector densityVariationLocal;
mfem::Vector displacementLocal;
mfem::Vector displacementVariationLocal;
if (needsBaseDensity) {
true_to_local(*f.densityFes, *baseDensityTrue, baseDensityLocal);
}
if (needsDensityVariation) {
true_to_local(*f.densityFes, *densityVariationTrue, densityVariationLocal);
}
true_to_local(*f.displacementFes, displacementTrue, displacementLocal);
if (needsDisplacementVariation) {
true_to_local(*f.displacementFes, *displacementVariationTrue, displacementVariationLocal);
}
mfem::Vector localAction(f.displacementFes->GetVSize());
localAction = 0.0;
mean_field::mapping::DomainMapperStateless::Workspace workspace(f.mesh->Dimension());
mfem::Array<int> densityDofs;
mfem::Array<int> displacementDofs;
mfem::Array<int> compactificationDofs;
mfem::Vector elementBaseDensity;
mfem::Vector elementDensityVariation;
mfem::Vector elementDisplacement;
mfem::Vector elementDisplacementVariation;
mfem::Vector elementCompactification;
mfem::Vector elementAction;
mfem::Vector densityShape;
mfem::Vector displacementShape;
mfem::Vector potentialGradient;
mfem::Vector potentialGradientVariation;
mfem::Vector centrifugalAcceleration;
mfem::Vector centrifugalAccelerationVariation;
mfem::Vector weightedForce;
mean_field::mapping::VolumeMappingContext mappingContext;
mean_field::mapping::VolumeMappingVariation mappingVariation;
const int dimension = f.mesh->Dimension();
const int vacuumAttribute = domainMapper.GetVacuumElementAttribute();
const mfem::Ordering::Type displacementOrdering = f.displacementFes->GetOrdering();
for (int elementId = 0; elementId < f.mesh->GetNE(); ++elementId) {
mfem::ElementTransformation *transformation = f.mesh->GetElementTransformation(elementId);
MFEM_VERIFY(
transformation != nullptr, "The rotational-displacement-force kernel received a null "
"element transformation."
);
if (transformation->Attribute == vacuumAttribute) {
continue;
}
const mfem::FiniteElement &densityElement = *f.densityFes->GetFE(elementId);
const mfem::FiniteElement &displacementElement = *f.displacementFes->GetFE(elementId);
const mfem::FiniteElement &compactificationElement = *f.compactificationFes->GetFE(elementId);
mfem::DofTransformation *densityDofTransformation = f.densityFes->GetElementDofs(elementId, densityDofs);
mfem::DofTransformation *displacementDofTransformation =
f.displacementFes->GetElementVDofs(elementId, displacementDofs);
mfem::DofTransformation *compactificationDofTransformation =
f.compactificationFes->GetElementDofs(elementId, compactificationDofs);
if (needsBaseDensity) {
baseDensityLocal.GetSubVector(densityDofs, elementBaseDensity);
}
if (needsDensityVariation) {
densityVariationLocal.GetSubVector(densityDofs, elementDensityVariation);
}
displacementLocal.GetSubVector(displacementDofs, elementDisplacement);
if (needsDisplacementVariation) {
displacementVariationLocal.GetSubVector(displacementDofs, elementDisplacementVariation);
}
f.compactificationCoordinate->GetSubVector(compactificationDofs, elementCompactification);
if (densityDofTransformation != nullptr) {
if (needsBaseDensity) {
densityDofTransformation->InvTransformPrimal(elementBaseDensity);
}
if (needsDensityVariation) {
densityDofTransformation->InvTransformPrimal(elementDensityVariation);
}
}
if (displacementDofTransformation != nullptr) {
displacementDofTransformation->InvTransformPrimal(elementDisplacement);
if (needsDisplacementVariation) {
displacementDofTransformation->InvTransformPrimal(elementDisplacementVariation);
}
}
if (compactificationDofTransformation != nullptr) {
compactificationDofTransformation->InvTransformPrimal(elementCompactification);
}
const mean_field::mapping::ElementDisplacementData displacementData =
mean_field::mapping::ElementDisplacementDataFromElementVDofs(displacementElement, elementDisplacement);
const mean_field::mapping::ElementCompactificationData compactificationData(
compactificationElement, elementCompactification
);
const mean_field::mapping::ElementMappingData mappingData{
.displacement = displacementData, .compactification = compactificationData
};
std::optional<mean_field::mapping::ElementDisplacementData> displacementVariationData;
if (needsDisplacementVariation) {
displacementVariationData.emplace(
mean_field::mapping::ElementDisplacementDataFromElementVDofs(
displacementElement, elementDisplacementVariation
)
);
}
const int scalarDisplacementDofCount = displacementElement.GetDof();
MFEM_VERIFY(
displacementDofs.Size() == scalarDisplacementDofCount * dimension,
"The rotational-displacement-force element displacement "
"vector has the wrong size."
);
densityShape.SetSize(densityElement.GetDof());
displacementShape.SetSize(scalarDisplacementDofCount);
potentialGradient.SetSize(dimension);
potentialGradientVariation.SetSize(dimension);
centrifugalAcceleration.SetSize(dimension);
centrifugalAccelerationVariation.SetSize(dimension);
weightedForce.SetSize(dimension);
elementAction.SetSize(displacementDofs.Size());
elementAction = 0.0;
const mfem::IntegrationRule &integrationRule =
get_rotation_force_rule(f, densityElement, displacementElement, *transformation);
for (int quadratureIndex = 0; quadratureIndex < integrationRule.GetNPoints(); ++quadratureIndex) {
const mfem::IntegrationPoint &integrationPoint = integrationRule.IntPoint(quadratureIndex);
transformation->SetIntPoint(&integrationPoint);
const mean_field::mapping::MappingStatus mappingStatus = domainMapper.EvaluateVolume(
mappingData, *transformation, integrationPoint, workspace, mappingContext
);
MFEM_VERIFY(
mappingStatus == mean_field::mapping::MappingStatus::valid,
"Stateless mapping failed in the rotational-"
"displacement-force kernel. Element: "
<< elementId << ", attribute: " << transformation->Attribute
<< ", quadrature point: " << quadratureIndex << ", status: " << static_cast<int>(mappingStatus)
);
if (needsDisplacementVariation) {
const mean_field::mapping::MappingStatus variationStatus = domainMapper.EvaluateVolumeVariation(
mappingData, *displacementVariationData, *transformation, integrationPoint, mappingContext,
workspace, mappingVariation
);
MFEM_VERIFY(
variationStatus == mean_field::mapping::MappingStatus::valid,
"Stateless mapping variation failed in the "
"rotational-displacement-force kernel. Element: "
<< elementId << ", attribute: " << transformation->Attribute << ", quadrature point: "
<< quadratureIndex << ", status: " << static_cast<int>(variationStatus)
);
}
densityElement.CalcShape(integrationPoint, densityShape);
displacementElement.CalcShape(integrationPoint, displacementShape);
double baseDensityValue = 0.0;
double densityVariationValue = 0.0;
if (needsBaseDensity) {
baseDensityValue = elementBaseDensity * densityShape;
}
if (needsDensityVariation) {
densityVariationValue = elementDensityVariation * densityShape;
}
rotation.potential_gradient(mappingContext.mapping.physical_position, potentialGradient);
centrifugalAcceleration = potentialGradient;
centrifugalAcceleration *= -1.0;
if (needsDisplacementVariation) {
rotation.potential_gradient_directional_derivative(
mappingVariation.mapping.physical_position_variation, potentialGradientVariation
);
centrifugalAccelerationVariation = potentialGradientVariation;
centrifugalAccelerationVariation *= -1.0;
} else {
centrifugalAccelerationVariation = 0.0;
}
weightedForce = 0.0;
if (requestedAction == RotationalDisplacementForceAction::residual) {
weightedForce.Add(baseDensityValue * mappingContext.quadrature.weight, centrifugalAcceleration);
} else {
if (needsDensityVariation) {
weightedForce.Add(
densityVariationValue * mappingContext.quadrature.weight, centrifugalAcceleration
);
}
if (needsDisplacementVariation) {
weightedForce.Add(
baseDensityValue * mappingContext.quadrature.weight, centrifugalAccelerationVariation
);
weightedForce.Add(
baseDensityValue * mappingVariation.weight_variation, centrifugalAcceleration
);
}
}
for (int scalarDof = 0; scalarDof < scalarDisplacementDofCount; ++scalarDof) {
for (int component = 0; component < dimension; ++component) {
const int vectorDof = vector_dof_index(
displacementOrdering, scalarDof, component, scalarDisplacementDofCount, dimension
);
const double contribution = displacementShape(scalarDof) * weightedForce(component);
MFEM_VERIFY(
std::isfinite(contribution), "The rotational-displacement-force kernel "
"encountered a non-finite contribution."
);
elementAction(vectorDof) += contribution;
}
}
}
if (displacementDofTransformation != nullptr) {
displacementDofTransformation->TransformDual(elementAction);
}
localAction.AddElementVector(displacementDofs, elementAction);
}
local_to_true(*f.displacementFes, localAction, actionTrue);
}
} // namespace
namespace mean_field::operators::kernels {
void apply_rotational_displacement_force_residual(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const physics::RigidRotation &rotation,
const mfem::Vector &densityTrue,
const mfem::Vector &displacementTrue,
mfem::Vector &residualTrue
) {
apply_rotational_displacement_force_action(
f, domainMapper, rotation, RotationalDisplacementForceAction::residual, &densityTrue, nullptr, nullptr,
displacementTrue, residualTrue
);
}
void apply_rotational_displacement_force_density_action(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const physics::RigidRotation &rotation,
const mfem::Vector &densityVariationTrue,
const mfem::Vector &displacementTrue,
mfem::Vector &actionTrue
) {
apply_rotational_displacement_force_action(
f, domainMapper, rotation, RotationalDisplacementForceAction::density, nullptr, &densityVariationTrue,
nullptr, displacementTrue, actionTrue
);
}
void apply_rotational_displacement_force_displacement_action(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const physics::RigidRotation &rotation,
const mfem::Vector &baseDensityTrue,
const mfem::Vector &displacementVariationTrue,
const mfem::Vector &displacementTrue,
mfem::Vector &actionTrue
) {
apply_rotational_displacement_force_action(
f, domainMapper, rotation, RotationalDisplacementForceAction::displacement, &baseDensityTrue, nullptr,
&displacementVariationTrue, displacementTrue, actionTrue
);
}
void apply_rotational_displacement_force_complete_action(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const physics::RigidRotation &rotation,
const mfem::Vector &baseDensityTrue,
const mfem::Vector &densityVariationTrue,
const mfem::Vector &displacementVariationTrue,
const mfem::Vector &displacementTrue,
mfem::Vector &actionTrue
) {
apply_rotational_displacement_force_action(
f, domainMapper, rotation, RotationalDisplacementForceAction::complete, &baseDensityTrue,
&densityVariationTrue, &displacementVariationTrue, displacementTrue, actionTrue
);
}
} // namespace mean_field::operators::kernels

View File

@@ -5,38 +5,54 @@ module;
#include <cstdint>
#include <limits>
#include <mfem.hpp>
#include <utility>
module mean_field;
import :operators.prepared_barotropic_closure;
import :operators.kernels.barotropic_closure;
import :field.registry;
import :utils.domain;
namespace {
int get_density_size(const mean_field::fem::FEM &f) {
MFEM_VERIFY(
f.densityFes != nullptr,
"PreparedBarotropicClosureOperator requires the "
"density finite-element space."
);
using DomainSchema = mean_field::utils::domain::CoreEnvelopeVacuumDomainSchema;
using ClosureDomain = mean_field::field::FieldDomainT<mean_field::field::Density>;
return f.densityFes->GetTrueVSize();
void verify_required_spaces(const mean_field::fem::FEM &f) {
MFEM_VERIFY(f.mesh != nullptr, "PreparedBarotropicClosureOperator requires a mesh.");
MFEM_VERIFY(
f.densityFes != nullptr, "PreparedBarotropicClosureOperator requires the density finite-element space."
);
MFEM_VERIFY(
f.enthalpyFes != nullptr, "PreparedBarotropicClosureOperator requires the enthalpy finite-element space."
);
MFEM_VERIFY(
f.displacementFes != nullptr,
"PreparedBarotropicClosureOperator requires the displacement finite-element space."
);
MFEM_VERIFY(
f.compactificationFes != nullptr,
"PreparedBarotropicClosureOperator requires the compactification finite-element space."
);
MFEM_VERIFY(
f.compactificationCoordinate != nullptr,
"PreparedBarotropicClosureOperator requires the compactification coordinate."
);
MFEM_VERIFY(
f.quadratureFactory != nullptr, "PreparedBarotropicClosureOperator requires the quadrature factory."
);
}
int get_enthalpy_size(const mean_field::fem::FEM &f) {
MFEM_VERIFY(
f.enthalpyFes != nullptr,
"PreparedBarotropicClosureOperator requires the "
"enthalpy finite-element space."
);
return f.enthalpyFes->GetTrueVSize();
[[nodiscard]] bool element_is_in_closure_support(const int attribute) {
return DomainSchema::template attribute_belongs_to<ClosureDomain>(attribute);
}
void validate_finite_vector(
const mfem::Vector &vector,
const char *message
) {
for (int i = 0; i < vector.Size(); ++i) {
MFEM_VERIFY(std::isfinite(vector(i)), message);
for (int index = 0; index < vector.Size(); ++index) {
MFEM_VERIFY(std::isfinite(vector(index)), message);
}
}
@@ -45,15 +61,11 @@ namespace {
const mfem::Vector &trueVector,
mfem::Vector &localVector
) {
MFEM_VERIFY(
trueVector.Size() == finiteElementSpace.GetTrueVSize(),
"True vector has the wrong size."
);
MFEM_VERIFY(trueVector.Size() == finiteElementSpace.GetTrueVSize(), "True vector has the wrong size.");
localVector.SetSize(finiteElementSpace.GetVSize());
const mfem::Operator *prolongation =
finiteElementSpace.GetProlongationMatrix();
const mfem::Operator *prolongation = finiteElementSpace.GetProlongationMatrix();
if (prolongation != nullptr) {
prolongation->Mult(trueVector, localVector);
@@ -67,16 +79,12 @@ namespace {
const mfem::Vector &localVector,
mfem::Vector &trueVector
) {
MFEM_VERIFY(
localVector.Size() == finiteElementSpace.GetVSize(),
"Local vector has the wrong size."
);
MFEM_VERIFY(localVector.Size() == finiteElementSpace.GetVSize(), "Local vector has the wrong size.");
trueVector.SetSize(finiteElementSpace.GetTrueVSize());
trueVector = 0.0;
trueVector = 0.0;
const mfem::Operator *prolongation =
finiteElementSpace.GetProlongationMatrix();
const mfem::Operator *prolongation = finiteElementSpace.GetProlongationMatrix();
if (prolongation != nullptr) {
prolongation->MultTranspose(localVector, trueVector);
@@ -85,65 +93,57 @@ namespace {
}
}
int get_eos_extra_order(
const mean_field::physics::PolytropicBarotrope &barotrope
) {
const double extraOrder =
(barotrope.polytropic_index() - 1.0) *
static_cast<double>(
mean_field::field::Enthalpy::Scalar::familyOrder
);
[[nodiscard]] int get_eos_extra_order(const mean_field::eos::Polytrope &equationOfState) {
const double extraOrder = (equationOfState.polytropic_index() - 1.0) *
static_cast<double>(mean_field::field::Enthalpy::Scalar::familyOrder);
MFEM_VERIFY(
std::isfinite(extraOrder) && extraOrder >= 0.0 &&
extraOrder <=
static_cast<double>(std::numeric_limits<int>::max()),
extraOrder <= static_cast<double>(std::numeric_limits<int>::max()),
"The EOS effective polynomial order is invalid."
);
return static_cast<int>(std::ceil(extraOrder));
}
const mfem::IntegrationRule &get_eos_rule(
[[nodiscard]] const mfem::IntegrationRule &get_eos_rule(
const mean_field::fem::FEM &f,
const mean_field::physics::PolytropicBarotrope &barotrope,
const mean_field::eos::Polytrope &equationOfState,
const mfem::FiniteElement &densityElement,
const mfem::FiniteElement &enthalpyElement,
const mfem::ElementTransformation &transformation
) {
using EnthalpyField =
mean_field::field::Field<mean_field::field::Enthalpy>;
using EnthalpyField = mean_field::field::Field<mean_field::field::Enthalpy>;
MFEM_VERIFY(
densityElement.GetOrder() ==
mean_field::field::Density::Scalar::familyOrder,
"The prepared EOS test element does not match "
"the registered density field."
densityElement.GetOrder() == mean_field::field::Density::Scalar::familyOrder,
"The prepared EOS test element does not match the registered density field."
);
MFEM_VERIFY(
enthalpyElement.GetOrder() ==
mean_field::field::Enthalpy::Scalar::familyOrder,
"The prepared EOS trial element does not match "
"the registered enthalpy field."
enthalpyElement.GetOrder() == mean_field::field::Enthalpy::Scalar::familyOrder,
"The prepared EOS trial element does not match the registered enthalpy field."
);
const mean_field::quadrature::Query query = EnthalpyField::make_query<
mean_field::field::Enthalpy::Form::EosClosureSource>(
mean_field::quadrature::QuadratureRole::discretization,
transformation.OrderW(),
std::array<int, 1>{get_eos_extra_order(barotrope)},
mean_field::utils::DOMAINS::STELLAR,
mean_field::quadrature::MappingKind::general
);
/*
* The quadrature Query still carries the legacy DOMAINS metadata.
* Element support itself is no longer selected through that enum;
* support is determined above through Density::Support + DomainSchema.
* The Query metadata can be migrated independently with the quadrature
* subsystem without changing this operator's algebra.
*/
const mean_field::quadrature::Query query =
EnthalpyField::make_query<mean_field::field::Enthalpy::Form::EosClosureSource>(
mean_field::quadrature::QuadratureRole::discretization, transformation.OrderW(),
std::array<int, 1>{get_eos_extra_order(equationOfState)}, mean_field::utils::DOMAINS::STELLAR,
mean_field::quadrature::MappingKind::general
);
const auto resolution =
f.quadratureFactory->get(query, transformation.GetGeometryType());
const auto resolution = f.quadratureFactory->get(query, transformation.GetGeometryType());
MFEM_VERIFY(
resolution.integration_rule != nullptr,
"The quadrature policy did not return a prepared "
"EOS-closure integration rule."
"The quadrature policy did not return a prepared EOS-closure integration rule."
);
return *resolution.integration_rule;
@@ -151,91 +151,127 @@ namespace {
} // namespace
namespace mean_field::operators {
struct PreparedBarotropicClosureOperator::ConstructionData final {
field::FieldDofMap densityMap;
field::FieldDofMap enthalpyMap;
field::FieldDofMap displacementMap;
explicit ConstructionData(const fem::FEM &f)
: densityMap(
field::make_field_dof_map<
field::Density,
DomainSchema>(*f.densityFes)
),
enthalpyMap(
field::make_field_dof_map<
field::Enthalpy,
DomainSchema>(*f.enthalpyFes)
),
displacementMap(
field::make_field_dof_map<
field::Displacement,
DomainSchema>(*f.displacementFes)
) {
}
};
PreparedBarotropicClosureOperator::ConstructionData
PreparedBarotropicClosureOperator::MakeConstructionData(const fem::FEM &f) {
verify_required_spaces(f);
return ConstructionData(f);
}
PreparedBarotropicClosureOperator::PreparedBarotropicClosureOperator(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const physics::PolytropicBarotrope &barotrope
const eos::Polytrope &equationOfState
)
: PreparedBarotropicClosureOperator(
f,
domainMapper,
equationOfState,
MakeConstructionData(f)
) {
}
PreparedBarotropicClosureOperator::PreparedBarotropicClosureOperator(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const eos::Polytrope &equationOfState,
ConstructionData constructionData
)
: mfem::Operator(
f.densityFes->GetTrueVSize(),
f.densityFes->GetTrueVSize() + f.enthalpyFes->GetTrueVSize() +
f.displacementFes->GetTrueVSize()
constructionData.densityMap.reduced_size(),
constructionData.densityMap.reduced_size() + constructionData.enthalpyMap.reduced_size() +
constructionData.displacementMap.reduced_size()
),
m_fem(f),
m_domainMapper(domainMapper),
m_barotrope(barotrope),
m_densitySize(f.densityFes->GetTrueVSize()),
m_enthalpySize(f.enthalpyFes->GetTrueVSize()) {
m_equationOfState(equationOfState),
m_densityMap(std::move(constructionData.densityMap)),
m_enthalpyMap(std::move(constructionData.enthalpyMap)),
m_displacementMap(std::move(constructionData.displacementMap)),
m_context(
f,
domainMapper,
m_densityMap,
m_enthalpyMap,
m_displacementMap
) {
MFEM_VERIFY(
m_fem.densityFes != nullptr,
"PreparedBarotropicClosureOperator requires "
"a density finite-element space."
m_densityMap.full_size() == m_fem.densityFes->GetTrueVSize(),
"The density FieldDofMap does not match the density finite-element space."
);
MFEM_VERIFY(
m_enthalpyMap.full_size() == m_fem.enthalpyFes->GetTrueVSize(),
"The enthalpy FieldDofMap does not match the enthalpy finite-element space."
);
MFEM_VERIFY(
m_displacementMap.full_size() == m_fem.displacementFes->GetTrueVSize(),
"The displacement FieldDofMap does not match the displacement finite-element space."
);
MFEM_VERIFY(
m_fem.enthalpyFes != nullptr,
"PreparedBarotropicClosureOperator requires "
"an enthalpy finite-element space."
);
m_baseDensityTrue.SetSize(m_densityMap.full_size());
m_baseEnthalpyTrue.SetSize(m_enthalpyMap.full_size());
m_baseDisplacementTrue.SetSize(m_displacementMap.full_size());
MFEM_VERIFY(
m_fem.displacementFes != nullptr,
"PreparedBarotropicClosureOperator requires "
"a displacement finite-element space."
);
m_densityVariationTrue.SetSize(m_densityMap.full_size());
m_enthalpyVariationTrue.SetSize(m_enthalpyMap.full_size());
m_displacementVariationTrue.SetSize(m_displacementMap.full_size());
m_fullThermodynamicAction.SetSize(m_densityMap.full_size());
m_fullDisplacementAction.SetSize(m_densityMap.full_size());
m_fullResidual.SetSize(m_densityMap.full_size());
m_baseDensityTrue = 0.0;
m_baseEnthalpyTrue = 0.0;
m_baseDisplacementTrue = 0.0;
m_densityVariationTrue = 0.0;
m_enthalpyVariationTrue = 0.0;
m_displacementVariationTrue = 0.0;
m_fullThermodynamicAction = 0.0;
m_fullDisplacementAction = 0.0;
m_fullResidual = 0.0;
}
void PreparedBarotropicClosureOperator::Prepare(
const mfem::Vector &baseDensityTrue,
const mfem::Vector &baseEnthalpyTrue,
const mfem::Vector &displacementTrue
PreparedBarotropicClosureReport PreparedBarotropicClosureOperator::Prepare(
const context::barotropic::BarotropicClosureStateView &state,
const context::barotropic::BarotropicClosureDependencies &dependencies
) {
MFEM_VERIFY(
baseDensityTrue.Size() == m_densitySize,
"PreparedBarotropicClosureOperator received a "
"base-density vector with the wrong size."
);
PreparedBarotropicClosureReport report;
report.contextReport = m_context.Prepare(state, dependencies);
MFEM_VERIFY(
baseEnthalpyTrue.Size() == m_enthalpySize,
"PreparedBarotropicClosureOperator received a "
"base-enthalpy vector with the wrong size."
);
if (!report.contextReport.DidAnyWork() && m_isPrepared) {
return report;
}
MFEM_VERIFY(
displacementTrue.Size() == m_fem.displacementFes->GetTrueVSize(),
"PreparedBarotropicClosureOperator received a "
"displacement vector with the wrong size."
);
MFEM_VERIFY(
baseDensityTrue.Size() == m_fem.densityFes->GetTrueVSize(),
"The base density true vector has the wrong size."
);
MFEM_VERIFY(
baseEnthalpyTrue.Size() == m_fem.enthalpyFes->GetTrueVSize(),
"The base enthalpy true vector has the wrong size."
);
MFEM_VERIFY(
displacementTrue.Size() == m_fem.displacementFes->GetTrueVSize(),
"The base displacement true vector has the wrong size."
);
validate_finite_vector(
baseDensityTrue, "PreparedBarotropicClosureOperator received a "
"non-finite base-density value."
);
validate_finite_vector(
baseEnthalpyTrue, "PreparedBarotropicClosureOperator received a "
"non-finite base-enthalpy value."
);
validate_finite_vector(
displacementTrue, "PreparedBarotropicClosureOperator received a "
"non-finite displacement value."
);
/*
* Canonical solver -> MFEM expansion. Unsupported density and
* enthalpy DOFs are zero. Displacement is currently an identity map,
* but it is deliberately routed through the same abstraction.
*/
m_densityMap.scatter(m_context.GetBaseDensity(), m_baseDensityTrue);
m_enthalpyMap.scatter(m_context.GetBaseEnthalpy(), m_baseEnthalpyTrue);
m_displacementMap.scatter(m_context.GetDisplacement(), m_baseDisplacementTrue);
m_isPrepared = false;
m_elements.clear();
@@ -245,17 +281,11 @@ namespace mean_field::operators {
mfem::Vector baseEnthalpyLocal;
mfem::Vector displacementLocal;
true_to_local(*m_fem.densityFes, baseDensityTrue, baseDensityLocal);
true_to_local(*m_fem.densityFes, m_baseDensityTrue, baseDensityLocal);
true_to_local(*m_fem.enthalpyFes, m_baseEnthalpyTrue, baseEnthalpyLocal);
true_to_local(*m_fem.displacementFes, m_baseDisplacementTrue, displacementLocal);
true_to_local(*m_fem.enthalpyFes, baseEnthalpyTrue, baseEnthalpyLocal);
true_to_local(
*m_fem.displacementFes, displacementTrue, displacementLocal
);
mapping::DomainMapperStateless::Workspace workspace(
m_fem.mesh->Dimension()
);
mapping::DomainMapperStateless::Workspace workspace(m_fem.mesh->Dimension());
mfem::Array<int> displacementDofs;
mfem::Array<int> compactificationDofs;
@@ -268,222 +298,134 @@ namespace mean_field::operators {
mfem::Vector densityShape;
mfem::Vector enthalpyShape;
const int vacuumAttribute = m_domainMapper.GetVacuumElementAttribute();
for (int elementId = 0; elementId < m_fem.mesh->GetNE(); ++elementId) {
mfem::ElementTransformation *transformation =
m_fem.mesh->GetElementTransformation(elementId);
mfem::ElementTransformation *transformation = m_fem.mesh->GetElementTransformation(elementId);
MFEM_VERIFY(
transformation != nullptr,
"PreparedBarotropicClosureOperator received "
"a null element transformation."
transformation != nullptr, "PreparedBarotropicClosureOperator received a null element transformation."
);
if (transformation->Attribute == vacuumAttribute) {
if (!element_is_in_closure_support(transformation->Attribute)) {
continue;
}
m_elements.emplace_back();
ElementPAData &data = m_elements.back();
ElementPAData &data = m_elements.back();
data.densityDofTransformation =
m_fem.densityFes->GetElementDofs(elementId, data.densityDofs);
data.enthalpyDofTransformation =
m_fem.enthalpyFes->GetElementDofs(elementId, data.enthalpyDofs);
data.densityDofTransformation = m_fem.densityFes->GetElementDofs(elementId, data.densityDofs);
data.enthalpyDofTransformation = m_fem.enthalpyFes->GetElementDofs(elementId, data.enthalpyDofs);
mfem::DofTransformation *displacementDofTransformation =
m_fem.displacementFes->GetElementVDofs(
elementId, displacementDofs
);
m_fem.displacementFes->GetElementVDofs(elementId, displacementDofs);
mfem::DofTransformation *compactificationDofTransformation =
m_fem.compactificationFes->GetElementDofs(
elementId, compactificationDofs
);
m_fem.compactificationFes->GetElementDofs(elementId, compactificationDofs);
baseDensityLocal.GetSubVector(data.densityDofs, elementBaseDensity);
baseEnthalpyLocal.GetSubVector(
data.enthalpyDofs, elementBaseEnthalpy
);
displacementLocal.GetSubVector(
displacementDofs, elementDisplacement
);
m_fem.compactificationCoordinate->GetSubVector(
compactificationDofs, elementCompactification
);
baseEnthalpyLocal.GetSubVector(data.enthalpyDofs, elementBaseEnthalpy);
displacementLocal.GetSubVector(displacementDofs, elementDisplacement);
m_fem.compactificationCoordinate->GetSubVector(compactificationDofs, elementCompactification);
if (data.densityDofTransformation != nullptr) {
data.densityDofTransformation->InvTransformPrimal(
elementBaseDensity
);
data.densityDofTransformation->InvTransformPrimal(elementBaseDensity);
}
if (data.enthalpyDofTransformation != nullptr) {
data.enthalpyDofTransformation->InvTransformPrimal(
elementBaseEnthalpy
);
data.enthalpyDofTransformation->InvTransformPrimal(elementBaseEnthalpy);
}
if (displacementDofTransformation != nullptr) {
displacementDofTransformation->InvTransformPrimal(
elementDisplacement
);
displacementDofTransformation->InvTransformPrimal(elementDisplacement);
}
if (compactificationDofTransformation != nullptr) {
compactificationDofTransformation->InvTransformPrimal(
elementCompactification
);
compactificationDofTransformation->InvTransformPrimal(elementCompactification);
}
const mfem::FiniteElement &densityElement =
*m_fem.densityFes->GetFE(elementId);
const mfem::FiniteElement &enthalpyElement =
*m_fem.enthalpyFes->GetFE(elementId);
const mfem::FiniteElement &displacementElement =
*m_fem.displacementFes->GetFE(elementId);
const mfem::FiniteElement &compactificationElement =
*m_fem.compactificationFes->GetFE(elementId);
const mfem::FiniteElement &densityElement = *m_fem.densityFes->GetFE(elementId);
const mfem::FiniteElement &enthalpyElement = *m_fem.enthalpyFes->GetFE(elementId);
const mfem::FiniteElement &displacementElement = *m_fem.displacementFes->GetFE(elementId);
const mfem::FiniteElement &compactificationElement = *m_fem.compactificationFes->GetFE(elementId);
const mapping::ElementDisplacementData displacementData =
mapping::ElementDisplacementDataFromElementVDofs(
displacementElement, elementDisplacement
);
mapping::ElementDisplacementDataFromElementVDofs(displacementElement, elementDisplacement);
const mapping::ElementCompactificationData compactificationData(
compactificationElement, elementCompactification
);
const mapping::ElementMappingData mappingData{
.displacement = displacementData,
.compactification = compactificationData
.displacement = displacementData, .compactification = compactificationData
};
const mfem::IntegrationRule &integrationRule = get_eos_rule(
m_fem, m_barotrope, densityElement, enthalpyElement,
*transformation
);
const mfem::IntegrationRule &integrationRule =
get_eos_rule(m_fem, m_equationOfState, densityElement, enthalpyElement, *transformation);
const int quadraturePointCount = integrationRule.GetNPoints();
const int densityDofCount = densityElement.GetDof();
const int enthalpyDofCount = enthalpyElement.GetDof();
data.densityBasis.SetSize(quadraturePointCount, densityDofCount);
data.enthalpyBasis.SetSize(quadraturePointCount, enthalpyDofCount);
data.weightedResidual.SetSize(quadraturePointCount);
data.quadratureWeights.SetSize(quadraturePointCount);
data.weightedEnthalpyDerivative.SetSize(quadraturePointCount);
densityShape.SetSize(densityDofCount);
enthalpyShape.SetSize(enthalpyDofCount);
for (int quadraturePoint = 0;
quadraturePoint < quadraturePointCount; ++quadraturePoint) {
const mfem::IntegrationPoint &integrationPoint =
integrationRule.IntPoint(quadraturePoint);
for (int quadraturePoint = 0; quadraturePoint < quadraturePointCount; ++quadraturePoint) {
const mfem::IntegrationPoint &integrationPoint = integrationRule.IntPoint(quadraturePoint);
transformation->SetIntPoint(&integrationPoint);
mapping::VolumeMappingContext mappingContext;
const mapping::MappingStatus mappingStatus =
m_domainMapper.EvaluateVolume(
mappingData, *transformation, integrationPoint,
workspace, mappingContext
);
const mapping::MappingStatus mappingStatus = m_domainMapper.EvaluateVolume(
mappingData, *transformation, integrationPoint, workspace, mappingContext
);
MFEM_VERIFY(
mappingStatus == mapping::MappingStatus::valid,
"Stateless mapping failed while preparing "
"the barotropic closure operator. Element: "
<< elementId
<< ", attribute: " << transformation->Attribute
<< ", quadrature point: " << quadraturePoint
<< ", status: " << static_cast<int>(mappingStatus)
"Stateless mapping failed while preparing the barotropic closure operator. Element: "
<< elementId << ", attribute: " << transformation->Attribute
<< ", quadrature point: " << quadraturePoint << ", status: " << static_cast<int>(mappingStatus)
);
densityElement.CalcShape(integrationPoint, densityShape);
enthalpyElement.CalcShape(integrationPoint, enthalpyShape);
for (int densityDof = 0; densityDof < densityDofCount;
++densityDof) {
data.densityBasis(quadraturePoint, densityDof) =
densityShape(densityDof);
for (int densityDof = 0; densityDof < densityDofCount; ++densityDof) {
data.densityBasis(quadraturePoint, densityDof) = densityShape(densityDof);
}
for (int enthalpyDof = 0; enthalpyDof < enthalpyDofCount; ++enthalpyDof) {
data.enthalpyBasis(quadraturePoint, enthalpyDof) = enthalpyShape(enthalpyDof);
}
for (int enthalpyDof = 0; enthalpyDof < enthalpyDofCount;
++enthalpyDof) {
data.enthalpyBasis(quadraturePoint, enthalpyDof) =
enthalpyShape(enthalpyDof);
}
const double density = elementBaseDensity * densityShape;
const double enthalpy = elementBaseEnthalpy * enthalpyShape;
const double quadratureWeight =
mappingContext.quadrature.weight;
const double eosDensity =
m_barotrope.density_from_enthalpy(enthalpy);
const double enthalpyDerivative =
m_barotrope.density_derivative_from_enthalpy(enthalpy);
const double density = elementBaseDensity * densityShape;
const double enthalpy = elementBaseEnthalpy * enthalpyShape;
const double quadratureWeight = mappingContext.quadrature.weight;
const double eosDensity = m_equationOfState.density_from_enthalpy(enthalpy);
const double enthalpyDerivative = m_equationOfState.density_derivative_from_enthalpy(enthalpy);
MFEM_VERIFY(
std::isfinite(quadratureWeight) && quadratureWeight > 0.0 &&
std::isfinite(eosDensity) &&
std::isfinite(quadratureWeight) && quadratureWeight > 0.0 && std::isfinite(eosDensity) &&
std::isfinite(enthalpyDerivative),
"PreparedBarotropicClosureOperator "
"encountered invalid quadrature data."
"PreparedBarotropicClosureOperator encountered invalid quadrature data."
);
data.quadratureWeights(quadraturePoint) = quadratureWeight;
data.weightedResidual(quadraturePoint) =
quadratureWeight * (density - eosDensity);
data.weightedEnthalpyDerivative(quadraturePoint) =
quadratureWeight * enthalpyDerivative;
data.quadratureWeights(quadraturePoint) = quadratureWeight;
data.weightedResidual(quadraturePoint) = quadratureWeight * (density - eosDensity);
data.weightedEnthalpyDerivative(quadraturePoint) = quadratureWeight * enthalpyDerivative;
}
}
MFEM_VERIFY(
!m_elements.empty(), "PreparedBarotropicClosureOperator found no "
"stellar elements."
);
MFEM_VERIFY(!m_elements.empty(), "PreparedBarotropicClosureOperator found no elements in Density::Support.");
m_baseDensityTrue = baseDensityTrue;
m_baseEnthalpyTrue = baseEnthalpyTrue;
m_baseDisplacementTrue = displacementTrue;
m_isPrepared = true;
m_isPrepared = true;
++m_preparationCount;
report.preparedElementData = true;
return report;
}
void PreparedBarotropicClosureOperator::BuildResidual(
mfem::Vector &residual
) const {
MFEM_VERIFY(
m_isPrepared, "PreparedBarotropicClosureOperator must be "
"prepared before BuildResidual is called."
);
void PreparedBarotropicClosureOperator::BuildResidual(mfem::Vector &residual) const {
VerifyPrepared();
mfem::Vector localResidual(m_fem.densityFes->GetVSize());
localResidual = 0.0;
@@ -492,10 +434,7 @@ namespace mean_field::operators {
for (const ElementPAData &data : m_elements) {
elementResidual.SetSize(data.densityDofs.Size());
data.densityBasis.MultTranspose(
data.weightedResidual, elementResidual
);
data.densityBasis.MultTranspose(data.weightedResidual, elementResidual);
if (data.densityDofTransformation != nullptr) {
data.densityDofTransformation->TransformDual(elementResidual);
@@ -504,53 +443,56 @@ namespace mean_field::operators {
localResidual.AddElementVector(data.densityDofs, elementResidual);
}
local_to_true(*m_fem.densityFes, localResidual, residual);
local_to_true(*m_fem.densityFes, localResidual, m_fullResidual);
residual.SetSize(m_densityMap.reduced_size());
m_densityMap.gather(m_fullResidual, residual);
}
void PreparedBarotropicClosureOperator::Mult(
const mfem::Vector &densityVariationTrue,
const mfem::Vector &enthalpyVariationTrue,
const mfem::Vector &displacementVariationTrue,
const mfem::Vector &densityVariation,
const mfem::Vector &enthalpyVariation,
const mfem::Vector &displacementVariation,
mfem::Vector &action
) const {
VerifyPrepared();
MFEM_VERIFY(
densityVariationTrue.Size() == m_densitySize,
"The density-variation true vector has "
"the wrong size."
densityVariation.Size() == m_densityMap.reduced_size(),
"The supported density-variation vector has the wrong size."
);
MFEM_VERIFY(
enthalpyVariationTrue.Size() == m_enthalpySize,
"The enthalpy-variation true vector has "
"the wrong size."
enthalpyVariation.Size() == m_enthalpyMap.reduced_size(),
"The supported enthalpy-variation vector has the wrong size."
);
MFEM_VERIFY(
displacementVariationTrue.Size() ==
m_fem.displacementFes->GetTrueVSize(),
"The displacement-variation true vector has "
"the wrong size."
displacementVariation.Size() == m_displacementMap.reduced_size(),
"The supported displacement-variation vector has the wrong size."
);
Mult(densityVariationTrue, enthalpyVariationTrue, action);
validate_finite_vector(densityVariation, "The density variation contains a non-finite value.");
validate_finite_vector(enthalpyVariation, "The enthalpy variation contains a non-finite value.");
validate_finite_vector(displacementVariation, "The displacement variation contains a non-finite value.");
mfem::Vector displacementAction;
m_densityMap.scatter(densityVariation, m_densityVariationTrue);
m_enthalpyMap.scatter(enthalpyVariation, m_enthalpyVariationTrue);
m_displacementMap.scatter(displacementVariation, m_displacementVariationTrue);
ApplyThermodynamicActionFull(m_densityVariationTrue, m_enthalpyVariationTrue, m_fullThermodynamicAction);
kernels::apply_barotropic_closure_displacement_action(
m_fem, m_domainMapper, m_barotrope, m_baseDensityTrue,
m_baseEnthalpyTrue, m_baseDisplacementTrue,
displacementVariationTrue, displacementAction
m_fem, m_domainMapper, m_equationOfState, m_baseDensityTrue, m_baseEnthalpyTrue, m_baseDisplacementTrue,
m_displacementVariationTrue, m_fullDisplacementAction
);
MFEM_VERIFY(
displacementAction.Size() == m_densitySize,
"The barotropic-closure displacement action "
"returned a vector with the wrong size."
m_fullThermodynamicAction.Size() == m_densityMap.full_size() &&
m_fullDisplacementAction.Size() == m_densityMap.full_size(),
"A full barotropic-closure Jacobian action has an incompatible density-space size."
);
action += displacementAction;
m_fullThermodynamicAction += m_fullDisplacementAction;
action.SetSize(m_densityMap.reduced_size());
m_densityMap.gather(m_fullThermodynamicAction, action);
}
void PreparedBarotropicClosureOperator::Mult(
@@ -559,70 +501,40 @@ namespace mean_field::operators {
) const {
VerifyPrepared();
const int displacementSize = m_fem.displacementFes->GetTrueVSize();
const int combinedSize =
m_densitySize + m_enthalpySize + displacementSize;
MFEM_VERIFY(
combinedVariation.Size() == combinedSize,
"The combined barotropic-closure variation "
"vector has the wrong size. Expected "
<< combinedSize << " entries but received "
<< combinedVariation.Size() << "."
combinedVariation.Size() == Width(), "The packed supported barotropic-closure variation has the wrong size."
);
mfem::real_t *combinedData =
const_cast<mfem::real_t *>(combinedVariation.HostRead());
mfem::real_t *combinedData = const_cast<mfem::real_t *>(combinedVariation.HostRead());
const mfem::Vector densityVariationTrue(combinedData, m_densitySize);
const int densitySize = m_densityMap.reduced_size();
const int enthalpySize = m_enthalpyMap.reduced_size();
const int displacementSize = m_displacementMap.reduced_size();
const mfem::Vector enthalpyVariationTrue(
combinedData + m_densitySize, m_enthalpySize
);
const mfem::Vector densityVariation(combinedData, densitySize);
const mfem::Vector enthalpyVariation(combinedData + densitySize, enthalpySize);
const mfem::Vector displacementVariation(combinedData + densitySize + enthalpySize, displacementSize);
const mfem::Vector displacementVariationTrue(
combinedData + m_densitySize + m_enthalpySize, displacementSize
);
Mult(
densityVariationTrue, enthalpyVariationTrue,
displacementVariationTrue, action
);
Mult(densityVariation, enthalpyVariation, displacementVariation, action);
}
void PreparedBarotropicClosureOperator::Mult(
void PreparedBarotropicClosureOperator::ApplyThermodynamicActionFull(
const mfem::Vector &densityVariationTrue,
const mfem::Vector &enthalpyVariationTrue,
mfem::Vector &action
mfem::Vector &actionTrue
) const {
MFEM_VERIFY(
m_isPrepared, "PreparedBarotropicClosureOperator must be "
"prepared before Mult is called."
densityVariationTrue.Size() == m_densityMap.full_size(), "The full density variation has the wrong size."
);
MFEM_VERIFY(
densityVariationTrue.Size() == m_densitySize,
"PreparedBarotropicClosureOperator received a "
"density variation with the wrong size."
);
MFEM_VERIFY(
enthalpyVariationTrue.Size() == m_enthalpySize,
"PreparedBarotropicClosureOperator received an "
"enthalpy variation with the wrong size."
enthalpyVariationTrue.Size() == m_enthalpyMap.full_size(), "The full enthalpy variation has the wrong size."
);
mfem::Vector densityVariationLocal;
mfem::Vector enthalpyVariationLocal;
true_to_local(
*m_fem.densityFes, densityVariationTrue, densityVariationLocal
);
true_to_local(
*m_fem.enthalpyFes, enthalpyVariationTrue, enthalpyVariationLocal
);
true_to_local(*m_fem.densityFes, densityVariationTrue, densityVariationLocal);
true_to_local(*m_fem.enthalpyFes, enthalpyVariationTrue, enthalpyVariationLocal);
mfem::Vector localAction(m_fem.densityFes->GetVSize());
localAction = 0.0;
@@ -635,51 +547,30 @@ namespace mean_field::operators {
mfem::Vector elementAction;
for (const ElementPAData &data : m_elements) {
densityVariationLocal.GetSubVector(
data.densityDofs, elementDensityVariation
);
enthalpyVariationLocal.GetSubVector(
data.enthalpyDofs, elementEnthalpyVariation
);
densityVariationLocal.GetSubVector(data.densityDofs, elementDensityVariation);
enthalpyVariationLocal.GetSubVector(data.enthalpyDofs, elementEnthalpyVariation);
if (data.densityDofTransformation != nullptr) {
data.densityDofTransformation->InvTransformPrimal(
elementDensityVariation
);
data.densityDofTransformation->InvTransformPrimal(elementDensityVariation);
}
if (data.enthalpyDofTransformation != nullptr) {
data.enthalpyDofTransformation->InvTransformPrimal(
elementEnthalpyVariation
);
data.enthalpyDofTransformation->InvTransformPrimal(elementEnthalpyVariation);
}
quadratureDensityVariation.SetSize(data.quadratureWeights.Size());
quadratureEnthalpyVariation.SetSize(data.quadratureWeights.Size());
quadratureAction.SetSize(data.quadratureWeights.Size());
data.densityBasis.Mult(
elementDensityVariation, quadratureDensityVariation
);
data.densityBasis.Mult(elementDensityVariation, quadratureDensityVariation);
data.enthalpyBasis.Mult(elementEnthalpyVariation, quadratureEnthalpyVariation);
data.enthalpyBasis.Mult(
elementEnthalpyVariation, quadratureEnthalpyVariation
);
for (int quadraturePoint = 0;
quadraturePoint < quadratureAction.Size(); ++quadraturePoint) {
for (int quadraturePoint = 0; quadraturePoint < quadratureAction.Size(); ++quadraturePoint) {
quadratureAction(quadraturePoint) =
data.quadratureWeights(quadraturePoint) *
quadratureDensityVariation(quadraturePoint) -
data.weightedEnthalpyDerivative(quadraturePoint) *
quadratureEnthalpyVariation(quadraturePoint);
data.quadratureWeights(quadraturePoint) * quadratureDensityVariation(quadraturePoint) -
data.weightedEnthalpyDerivative(quadraturePoint) * quadratureEnthalpyVariation(quadraturePoint);
}
elementAction.SetSize(data.densityDofs.Size());
data.densityBasis.MultTranspose(quadratureAction, elementAction);
if (data.densityDofTransformation != nullptr) {
@@ -689,30 +580,42 @@ namespace mean_field::operators {
localAction.AddElementVector(data.densityDofs, elementAction);
}
local_to_true(*m_fem.densityFes, localAction, action);
local_to_true(*m_fem.densityFes, localAction, actionTrue);
}
bool PreparedBarotropicClosureOperator::IsPrepared() const noexcept {
return m_isPrepared;
return m_isPrepared && m_context.IsPrepared();
}
std::uint64_t
PreparedBarotropicClosureOperator::GetPreparationCount() const noexcept {
std::uint64_t PreparedBarotropicClosureOperator::GetPreparationCount() const noexcept {
return m_preparationCount;
}
int PreparedBarotropicClosureOperator::GetDensitySize() const noexcept {
return m_densitySize;
return m_densityMap.reduced_size();
}
int PreparedBarotropicClosureOperator::GetEnthalpySize() const noexcept {
return m_enthalpySize;
return m_enthalpyMap.reduced_size();
}
int PreparedBarotropicClosureOperator::GetDisplacementSize() const noexcept {
return m_displacementMap.reduced_size();
}
const context::barotropic::BarotropicClosureLinearizationContext &
PreparedBarotropicClosureOperator::GetContext() const noexcept {
return m_context;
}
const context::barotropic::BarotropicClosurePreparationStatistics &
PreparedBarotropicClosureOperator::GetContextPreparationStatistics() const noexcept {
return m_context.GetPreparationStatistics();
}
void PreparedBarotropicClosureOperator::VerifyPrepared() const {
MFEM_VERIFY(
m_isPrepared, "PreparedBarotropicClosureOperator must be "
"prepared before this operation is called."
m_isPrepared, "PreparedBarotropicClosureOperator must be prepared before this operation is called."
);
}
} // namespace mean_field::operators
} // namespace mean_field::operators

View File

@@ -0,0 +1,554 @@
module;
#include <mfem.hpp>
module mean_field;
import :operators.prepared_displacement_residual;
namespace {
using Dependencies = mean_field::operators::DisplacementResidualDependencies;
[[nodiscard]] mean_field::operators::context::pressure_force::PressureForceDependencies
make_pressure_dependencies(const Dependencies &dependencies) {
return {
.discretization =
{.identity = dependencies.discretization.identity, .revision = dependencies.discretization.revision},
.enthalpy = {.identity = dependencies.enthalpy.identity, .revision = dependencies.enthalpy.revision},
.displacement = {
.identity = dependencies.displacement.identity, .revision = dependencies.displacement.revision
}
};
}
[[nodiscard]] mean_field::operators::context::rotational_displacement_force::RotationalDisplacementForceDependencies
make_rotational_dependencies(const Dependencies &dependencies) {
return {
.discretization =
{.identity = dependencies.discretization.identity, .revision = dependencies.discretization.revision},
.density = {.identity = dependencies.density.identity, .revision = dependencies.density.revision},
.displacement =
{.identity = dependencies.displacement.identity, .revision = dependencies.displacement.revision},
.rotation = {.identity = dependencies.rotation.identity, .revision = dependencies.rotation.revision}
};
}
void validate_shared_gravity_revisions(
const mean_field::operators::context::gravity_field::GravityFieldLinearizationContext &gravityContext,
const Dependencies &dependencies
) {
MFEM_VERIFY(
gravityContext.IsPrepared(), "PreparedDisplacementResidualOperator requires the shared "
"gravity linearization context to be prepared first."
);
const mean_field::operators::context::gravity_field::GravityFieldRevisions &gravityRevisions =
gravityContext.GetRevisions();
MFEM_VERIFY(
gravityRevisions.discretization.value == dependencies.discretization.revision &&
gravityRevisions.density.value == dependencies.density.revision &&
gravityRevisions.displacement.value == dependencies.displacement.revision &&
gravityRevisions.gravity_gradient.value == dependencies.gravityGradient.revision,
"PreparedDisplacementResidualOperator received dependency "
"revisions that do not match the shared gravity context."
);
}
void validate_shared_identity_transition(
const mean_field::operators::DisplacementResidualDependencyStamp &prepared,
const mean_field::operators::DisplacementResidualDependencyStamp &requested,
const char *message
) {
MFEM_VERIFY(prepared.identity == requested.identity || prepared.revision != requested.revision, message);
}
void add_compatible(
mfem::Vector &destination,
const mfem::Vector &source,
const char *message
) {
MFEM_VERIFY(destination.Size() == source.Size(), message);
destination += source;
}
} // namespace
namespace mean_field::operators {
PreparedDisplacementResidualOperator::PreparedDisplacementResidualOperator(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const eos::Polytrope &barotrope,
const context::gravity_field::GravityFieldLinearizationContext &gravityContext
)
: m_fem(f),
m_domainMapper(domainMapper),
m_gravityContext(gravityContext),
m_pressureOperator(
f,
domainMapper,
barotrope
),
m_gravityOperator(
f,
domainMapper,
gravityContext
),
m_rotationalOperator(
f,
domainMapper
) {
MFEM_VERIFY(m_fem.mesh != nullptr, "PreparedDisplacementResidualOperator requires a mesh.");
MFEM_VERIFY(
m_fem.densityFes != nullptr && m_fem.displacementFes != nullptr && m_fem.gravityFluxFes != nullptr &&
m_fem.enthalpyFes != nullptr,
"PreparedDisplacementResidualOperator requires density, "
"displacement, gravity-gradient, and enthalpy finite-element "
"spaces."
);
MFEM_VERIFY(
m_domainMapper.GetDimension() == m_fem.mesh->Dimension(),
"PreparedDisplacementResidualOperator received a mapper with "
"the wrong dimension."
);
}
PreparedDisplacementResidualReport PreparedDisplacementResidualOperator::Prepare(
const DisplacementResidualStateView &state,
const DisplacementResidualDependencies &dependencies,
const physics::RigidRotation &rotation
) {
validate_shared_gravity_revisions(m_gravityContext, dependencies);
if (m_isPrepared) {
/*
* GravityFieldLinearizationContext currently tracks revisions
* but not semantic identities. Require an identity replacement
* to be accompanied by a visible revision change so it cannot
* silently reuse the old shared density, geometry, or flux.
*/
validate_shared_identity_transition(
m_preparedDependencies.discretization, dependencies.discretization,
"A new displacement-residual discretization identity must "
"also change the shared gravity revision."
);
validate_shared_identity_transition(
m_preparedDependencies.density, dependencies.density,
"A new displacement-residual density identity must also "
"change the shared gravity revision."
);
validate_shared_identity_transition(
m_preparedDependencies.displacement, dependencies.displacement,
"A new displacement-residual displacement identity must "
"also change the shared gravity revision."
);
validate_shared_identity_transition(
m_preparedDependencies.gravityGradient, dependencies.gravityGradient,
"A new displacement-residual gravity-gradient identity "
"must also change the shared gravity revision."
);
}
const mfem::Vector &density = m_gravityContext.GetDensity();
const mfem::Vector &displacement = m_gravityContext.GetGeometryContext().GetDisplacement();
m_isPrepared = false;
PreparedDisplacementResidualReport report;
report.pressure = m_pressureOperator.Prepare(
{.enthalpy = state.enthalpy, .displacement = displacement}, make_pressure_dependencies(dependencies)
);
report.gravity = m_gravityOperator.Prepare();
report.rotation = m_rotationalOperator.Prepare(
{.density = density, .displacement = displacement}, make_rotational_dependencies(dependencies), rotation
);
if (report.DidAnyChildWork() || m_cachedResidual.Size() != m_fem.displacementFes->GetTrueVSize()) {
AssembleResidual();
report.assembledResidual = true;
}
MFEM_VERIFY(
m_cachedResidual.Size() == m_fem.displacementFes->GetTrueVSize(),
"PreparedDisplacementResidualOperator produced a cached "
"residual with the wrong size."
);
m_preparedDependencies = dependencies;
m_isPrepared = true;
return report;
}
void PreparedDisplacementResidualOperator::AssembleResidual() {
mfem::Vector pressureResidual;
mfem::Vector gravityResidual;
mfem::Vector rotationalResidual;
m_pressureOperator.BuildResidual(pressureResidual);
m_gravityOperator.BuildResidual(gravityResidual);
m_rotationalOperator.BuildResidual(rotationalResidual);
m_cachedResidual = pressureResidual;
add_compatible(
m_cachedResidual, gravityResidual,
"Cannot combine pressure and gravity displacement residuals "
"with different sizes."
);
add_compatible(
m_cachedResidual, rotationalResidual,
"Cannot combine mechanical displacement residuals with "
"different sizes."
);
++m_residualPreparationCount;
}
void PreparedDisplacementResidualOperator::BuildResidual(mfem::Vector &residual) const {
VerifyPrepared();
residual = m_cachedResidual;
++m_residualApplicationCount;
}
void PreparedDisplacementResidualOperator::ApplyDensityJacobianAction(
const mfem::Vector &densityVariation,
mfem::Vector &action
) const {
VerifyPrepared();
mfem::Vector rotationalAction;
m_gravityOperator.ApplyDensityJacobianAction(densityVariation, action);
m_rotationalOperator.ApplyDensityJacobianAction(densityVariation, rotationalAction);
add_compatible(
action, rotationalAction,
"Cannot combine gravity and rotation density-column actions "
"with different sizes."
);
++m_actionStatistics.densityApplications;
}
void PreparedDisplacementResidualOperator::ApplyDisplacementJacobianAction(
const mfem::Vector &displacementVariation,
mfem::Vector &action
) const {
VerifyPrepared();
mfem::Vector gravityAction;
mfem::Vector rotationalAction;
m_pressureOperator.ApplyDisplacementJacobianAction(displacementVariation, action);
m_gravityOperator.ApplyDisplacementJacobianAction(displacementVariation, gravityAction);
m_rotationalOperator.ApplyDisplacementJacobianAction(displacementVariation, rotationalAction);
add_compatible(
action, gravityAction,
"Cannot combine pressure and gravity displacement-column "
"actions with different sizes."
);
add_compatible(
action, rotationalAction,
"Cannot combine mechanical displacement-column actions with "
"different sizes."
);
++m_actionStatistics.displacementApplications;
}
void PreparedDisplacementResidualOperator::ApplyGravityGradientJacobianAction(
const mfem::Vector &gravityGradientVariation,
mfem::Vector &action
) const {
VerifyPrepared();
m_gravityOperator.ApplyGravityGradientJacobianAction(gravityGradientVariation, action);
++m_actionStatistics.gravityGradientApplications;
}
void PreparedDisplacementResidualOperator::ApplyEnthalpyJacobianAction(
const mfem::Vector &enthalpyVariation,
mfem::Vector &action
) const {
VerifyPrepared();
m_pressureOperator.ApplyEnthalpyJacobianAction(enthalpyVariation, action);
++m_actionStatistics.enthalpyApplications;
}
void PreparedDisplacementResidualOperator::ApplyCompleteJacobianAction(
const mfem::Vector &densityVariation,
const mfem::Vector &displacementVariation,
const mfem::Vector &gravityGradientVariation,
const mfem::Vector &enthalpyVariation,
mfem::Vector &action
) const {
VerifyPrepared();
mfem::Vector gravityAction;
mfem::Vector rotationalAction;
m_pressureOperator.ApplyCompleteJacobianAction(enthalpyVariation, displacementVariation, action);
m_gravityOperator.ApplyCompleteJacobianAction(
densityVariation, displacementVariation, gravityGradientVariation, gravityAction
);
m_rotationalOperator.ApplyCompleteJacobianAction(densityVariation, displacementVariation, rotationalAction);
add_compatible(
action, gravityAction,
"Cannot combine pressure and gravity complete Jacobian "
"actions with different sizes."
);
add_compatible(
action, rotationalAction,
"Cannot combine mechanical complete Jacobian actions with "
"different sizes."
);
++m_actionStatistics.densityApplications;
++m_actionStatistics.displacementApplications;
++m_actionStatistics.gravityGradientApplications;
++m_actionStatistics.enthalpyApplications;
++m_actionStatistics.completeApplications;
}
bool PreparedDisplacementResidualOperator::IsPrepared() const noexcept {
if (!m_isPrepared || !m_pressureOperator.IsPrepared() || !m_gravityOperator.IsPrepared() ||
!m_rotationalOperator.IsPrepared() || !m_gravityContext.IsPrepared()) {
return false;
}
const context::gravity_field::GravityFieldRevisions &gravityRevisions = m_gravityContext.GetRevisions();
return gravityRevisions.discretization.value == m_preparedDependencies.discretization.revision &&
gravityRevisions.density.value == m_preparedDependencies.density.revision &&
gravityRevisions.displacement.value == m_preparedDependencies.displacement.revision &&
gravityRevisions.gravity_gradient.value == m_preparedDependencies.gravityGradient.revision;
}
std::uint64_t PreparedDisplacementResidualOperator::GetResidualPreparationCount() const noexcept {
return m_residualPreparationCount;
}
std::uint64_t PreparedDisplacementResidualOperator::GetResidualApplicationCount() const noexcept {
return m_residualApplicationCount;
}
const PreparedDisplacementResidualActionStatistics &
PreparedDisplacementResidualOperator::GetActionStatistics() const noexcept {
return m_actionStatistics;
}
const PreparedPressureForceOperator &PreparedDisplacementResidualOperator::GetPressureOperator() const noexcept {
return m_pressureOperator;
}
const PreparedGravityDisplacementForceOperator &
PreparedDisplacementResidualOperator::GetGravityOperator() const noexcept {
return m_gravityOperator;
}
const PreparedRotationalDisplacementForceOperator &
PreparedDisplacementResidualOperator::GetRotationalOperator() const noexcept {
return m_rotationalOperator;
}
const fem::FEM &PreparedDisplacementResidualOperator::GetFEM() const noexcept {
return m_fem;
}
const context::gravity_field::GravityFieldLinearizationContext &
PreparedDisplacementResidualOperator::GetGravityContext() const noexcept {
return m_gravityContext;
}
void PreparedDisplacementResidualOperator::VerifyPrepared() const {
MFEM_VERIFY(
IsPrepared(), "PreparedDisplacementResidualOperator must be prepared for "
"the current shared gravity-context revisions before residual "
"or Jacobian application."
);
}
PreparedDisplacementResidualJacobianOperator::PreparedDisplacementResidualJacobianOperator(
const DisplacementResidualLayout &layout,
const PreparedDisplacementResidualOperator &preparedOperator
)
: mfem::Operator(
layout.residual_offsets().Last(),
layout.value_offsets().Last()
),
m_layout(layout),
m_preparedOperator(preparedOperator) {
const fem::FEM &f = m_preparedOperator.GetFEM();
MFEM_VERIFY(
f.densityFes != nullptr && f.displacementFes != nullptr && f.gravityFluxFes != nullptr &&
f.gravityPotentialFes != nullptr && f.enthalpyFes != nullptr,
"Prepared displacement-residual MFEM adapter requires every "
"finite-element space in the barotropic equilibrium layout."
);
using Form = utils::blocks::barotropic_equilibrium_form;
constexpr auto densityValue = utils::blocks::get_value_block<Form>(utils::blocks::density_field.mass_term);
constexpr auto displacementValue =
utils::blocks::get_value_block<Form>(utils::blocks::displacement_field.geometry_term);
constexpr auto gravityGradientValue =
utils::blocks::get_value_block<Form>(utils::blocks::gravity_field.gradient_term);
constexpr auto gravityPotentialValue =
utils::blocks::get_value_block<Form>(utils::blocks::gravity_field.poisson_term);
constexpr auto enthalpyValue =
utils::blocks::get_value_block<Form>(utils::blocks::enthalpy_field.specific_term);
constexpr auto barotropicConstantValue =
utils::blocks::get_value_block<Form>(utils::blocks::barotropic_constant_field.mass_normalization_term);
constexpr auto gravityGradientResidual =
utils::blocks::get_residual_block<Form>(utils::blocks::gravity_field.gradient_term);
constexpr auto gravityPotentialResidual =
utils::blocks::get_residual_block<Form>(utils::blocks::gravity_field.poisson_term);
constexpr auto densityResidual =
utils::blocks::get_residual_block<Form>(utils::blocks::density_field.mass_term);
constexpr auto displacementResidual =
utils::blocks::get_residual_block<Form>(utils::blocks::displacement_field.geometry_term);
constexpr auto enthalpyResidual =
utils::blocks::get_residual_block<Form>(utils::blocks::enthalpy_field.specific_term);
constexpr auto massResidual =
utils::blocks::get_residual_block<Form>(utils::blocks::barotropic_constant_field.mass_normalization_term);
MFEM_VERIFY(
m_layout.size(densityValue) == f.densityFes->GetTrueVSize() &&
m_layout.size(displacementValue) == f.displacementFes->GetTrueVSize() &&
m_layout.size(gravityGradientValue) == f.gravityFluxFes->GetTrueVSize() &&
m_layout.size(gravityPotentialValue) == f.gravityPotentialFes->GetTrueVSize() &&
m_layout.size(barotropicConstantValue) == 1,
"Prepared displacement-residual MFEM adapter received "
"incompatible barotropic value-block sizes."
);
MFEM_VERIFY(
m_layout.size(enthalpyValue) == m_preparedOperator.GetPressureOperator().GetEnthalpySize(),
"Prepared displacement-residual MFEM adapter received an "
"incompatible enthalpy value block."
);
MFEM_VERIFY(
m_layout.size(gravityGradientResidual) == f.gravityFluxFes->GetTrueVSize() &&
m_layout.size(gravityPotentialResidual) == f.gravityPotentialFes->GetTrueVSize() &&
m_layout.size(densityResidual) == f.densityFes->GetTrueVSize() &&
m_layout.size(displacementResidual) == f.displacementFes->GetTrueVSize() &&
m_layout.size(enthalpyResidual) == f.enthalpyFes->GetTrueVSize() && m_layout.size(massResidual) == 1,
"Prepared displacement-residual MFEM adapter received "
"incompatible barotropic residual-block sizes."
);
MFEM_VERIFY(
Height() == m_layout.residual_offsets().Last() && Width() == m_layout.value_offsets().Last(),
"Prepared displacement-residual MFEM adapter has inconsistent "
"operator dimensions."
);
}
void PreparedDisplacementResidualJacobianOperator::Mult(
const mfem::Vector &direction,
mfem::Vector &action
) const {
MFEM_VERIFY(
m_preparedOperator.IsPrepared(), "Prepared displacement-residual MFEM adapter requires a "
"prepared row operator."
);
MFEM_VERIFY(
direction.Size() == Width(), "Prepared displacement-residual MFEM adapter received a "
"direction with the wrong size."
);
using Form = utils::blocks::barotropic_equilibrium_form;
constexpr auto densityValue = utils::blocks::get_value_block<Form>(utils::blocks::density_field.mass_term);
constexpr auto displacementValue =
utils::blocks::get_value_block<Form>(utils::blocks::displacement_field.geometry_term);
constexpr auto gravityGradientValue =
utils::blocks::get_value_block<Form>(utils::blocks::gravity_field.gradient_term);
constexpr auto enthalpyValue =
utils::blocks::get_value_block<Form>(utils::blocks::enthalpy_field.specific_term);
constexpr auto displacementResidual =
utils::blocks::get_residual_block<Form>(utils::blocks::displacement_field.geometry_term);
const mfem::Vector densityVariation(
const_cast<mfem::real_t *>(direction.GetData()) + m_layout.offset(densityValue), m_layout.size(densityValue)
);
const mfem::Vector displacementVariation(
const_cast<mfem::real_t *>(direction.GetData()) + m_layout.offset(displacementValue),
m_layout.size(displacementValue)
);
const mfem::Vector gravityGradientVariation(
const_cast<mfem::real_t *>(direction.GetData()) + m_layout.offset(gravityGradientValue),
m_layout.size(gravityGradientValue)
);
const mfem::Vector enthalpyVariation(
const_cast<mfem::real_t *>(direction.GetData()) + m_layout.offset(enthalpyValue),
m_layout.size(enthalpyValue)
);
mfem::Vector displacementAction;
m_preparedOperator.ApplyCompleteJacobianAction(
densityVariation, displacementVariation, gravityGradientVariation, enthalpyVariation, displacementAction
);
MFEM_VERIFY(
displacementAction.Size() == m_layout.size(displacementResidual),
"Prepared displacement-residual MFEM adapter produced an "
"action with the wrong size."
);
action.SetSize(Height());
action = 0.0;
const int residualOffset = m_layout.offset(displacementResidual);
for (int entry = 0; entry < displacementAction.Size(); ++entry) {
action(residualOffset + entry) = displacementAction(entry);
}
}
const DisplacementResidualLayout &PreparedDisplacementResidualJacobianOperator::GetLayout() const noexcept {
return m_layout;
}
} // namespace mean_field::operators

View File

@@ -0,0 +1,290 @@
module;
#include <mfem.hpp>
module mean_field;
import :operators.kernels.gravity_displacement_force;
import :operators.prepared_gravity_displacement_force;
namespace {
[[nodiscard]] bool relevant_revisions_match(
const mean_field::operators::context::gravity_field::GravityFieldRevisions &left,
const mean_field::operators::context::gravity_field::GravityFieldRevisions &right
) noexcept {
return left.discretization == right.discretization && left.displacement == right.displacement &&
left.density == right.density && left.gravity_gradient == right.gravity_gradient;
}
} // namespace
namespace mean_field::operators {
PreparedGravityDisplacementForceOperator::PreparedGravityDisplacementForceOperator(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const context::gravity_field::GravityFieldLinearizationContext &gravityContext
)
: m_fem(f),
m_domainMapper(domainMapper),
m_gravityContext(gravityContext) {
MFEM_VERIFY(m_fem.mesh != nullptr, "PreparedGravityDisplacementForceOperator requires a mesh.");
MFEM_VERIFY(
m_fem.densityFes != nullptr && m_fem.gravityFluxFes != nullptr && m_fem.displacementFes != nullptr,
"PreparedGravityDisplacementForceOperator requires density, "
"gravity-gradient, and displacement finite-element spaces."
);
MFEM_VERIFY(
m_domainMapper.GetDimension() == m_fem.mesh->Dimension(),
"PreparedGravityDisplacementForceOperator received a mapper "
"with the wrong dimension."
);
}
PreparedGravityDisplacementForceReport PreparedGravityDisplacementForceOperator::Prepare() {
MFEM_VERIFY(
m_gravityContext.IsPrepared(), "PreparedGravityDisplacementForceOperator requires the shared "
"gravity linearization context to be prepared first."
);
const context::gravity_field::GravityFieldRevisions &requestedRevisions = m_gravityContext.GetRevisions();
if (m_isPrepared && relevant_revisions_match(requestedRevisions, m_preparedRevisions)) {
return {};
}
kernels::apply_gravity_displacement_force_residual(
m_fem, m_domainMapper, m_gravityContext.GetDensity(), m_gravityContext.GetGravityGradient(),
m_gravityContext.GetGeometryContext().GetDisplacement(), m_cachedResidual
);
m_preparedRevisions = requestedRevisions;
++m_residualPreparationCount;
m_isPrepared = true;
return {.preparedResidual = true};
}
void PreparedGravityDisplacementForceOperator::BuildResidual(mfem::Vector &residual) const {
VerifyPrepared();
residual = m_cachedResidual;
++m_residualApplicationCount;
}
void PreparedGravityDisplacementForceOperator::ApplyDensityJacobianAction(
const mfem::Vector &densityVariation,
mfem::Vector &action
) const {
VerifyPrepared();
kernels::apply_gravity_displacement_force_density_action(
m_fem, m_domainMapper, densityVariation, m_gravityContext.GetGravityGradient(),
m_gravityContext.GetGeometryContext().GetDisplacement(), action
);
++m_densityJacobianStatistics.applications;
}
void PreparedGravityDisplacementForceOperator::ApplyGravityGradientJacobianAction(
const mfem::Vector &gravityGradientVariation,
mfem::Vector &action
) const {
VerifyPrepared();
kernels::apply_gravity_displacement_force_gradient_action(
m_fem, m_domainMapper, m_gravityContext.GetDensity(), gravityGradientVariation,
m_gravityContext.GetGeometryContext().GetDisplacement(), action
);
++m_gravityGradientJacobianStatistics.applications;
}
void PreparedGravityDisplacementForceOperator::ApplyDisplacementJacobianAction(
const mfem::Vector &displacementVariation,
mfem::Vector &action
) const {
VerifyPrepared();
kernels::apply_gravity_displacement_force_displacement_action(
m_fem, m_domainMapper, m_gravityContext.GetDensity(), m_gravityContext.GetGravityGradient(),
displacementVariation, m_gravityContext.GetGeometryContext().GetDisplacement(), action
);
++m_displacementJacobianStatistics.applications;
}
void PreparedGravityDisplacementForceOperator::ApplyCompleteJacobianAction(
const mfem::Vector &densityVariation,
const mfem::Vector &displacementVariation,
const mfem::Vector &gravityGradientVariation,
mfem::Vector &action
) const {
VerifyPrepared();
kernels::apply_gravity_displacement_force_complete_action(
m_fem, m_domainMapper, m_gravityContext.GetDensity(), densityVariation,
m_gravityContext.GetGravityGradient(), gravityGradientVariation, displacementVariation,
m_gravityContext.GetGeometryContext().GetDisplacement(), action
);
++m_densityJacobianStatistics.applications;
++m_gravityGradientJacobianStatistics.applications;
++m_displacementJacobianStatistics.applications;
++m_completeJacobianStatistics.applications;
}
bool PreparedGravityDisplacementForceOperator::IsPrepared() const noexcept {
if (!m_isPrepared || !m_gravityContext.IsPrepared()) {
return false;
}
return relevant_revisions_match(m_gravityContext.GetRevisions(), m_preparedRevisions);
}
std::uint64_t PreparedGravityDisplacementForceOperator::GetResidualPreparationCount() const noexcept {
return m_residualPreparationCount;
}
std::uint64_t PreparedGravityDisplacementForceOperator::GetResidualApplicationCount() const noexcept {
return m_residualApplicationCount;
}
const PreparedGravityDisplacementForceColumnStatistics &
PreparedGravityDisplacementForceOperator::GetDensityJacobianStatistics() const noexcept {
return m_densityJacobianStatistics;
}
const PreparedGravityDisplacementForceColumnStatistics &
PreparedGravityDisplacementForceOperator::GetGravityGradientJacobianStatistics() const noexcept {
return m_gravityGradientJacobianStatistics;
}
const PreparedGravityDisplacementForceColumnStatistics &
PreparedGravityDisplacementForceOperator::GetDisplacementJacobianStatistics() const noexcept {
return m_displacementJacobianStatistics;
}
const PreparedGravityDisplacementForceCompleteStatistics &
PreparedGravityDisplacementForceOperator::GetCompleteJacobianStatistics() const noexcept {
return m_completeJacobianStatistics;
}
const fem::FEM &PreparedGravityDisplacementForceOperator::GetFEM() const noexcept {
return m_fem;
}
const context::gravity_field::GravityFieldLinearizationContext &
PreparedGravityDisplacementForceOperator::GetGravityContext() const noexcept {
return m_gravityContext;
}
void PreparedGravityDisplacementForceOperator::VerifyPrepared() const {
MFEM_VERIFY(
IsPrepared(), "PreparedGravityDisplacementForceOperator must be prepared for "
"the current shared gravity-context revisions before residual or "
"Jacobian application."
);
}
PreparedGravityDisplacementForceJacobianOperator::PreparedGravityDisplacementForceJacobianOperator(
const GravityDisplacementForceLayout &layout,
const PreparedGravityDisplacementForceOperator &preparedOperator
)
: mfem::Operator(
layout.residual_offsets().Last(),
layout.value_offsets().Last()
),
m_layout(layout),
m_preparedOperator(preparedOperator) {
const fem::FEM &f = m_preparedOperator.GetFEM();
using Form = utils::blocks::barotropic_equilibrium_form;
constexpr auto densityValue = utils::blocks::get_value_block<Form>(utils::blocks::density_field.mass_term);
constexpr auto displacementValue =
utils::blocks::get_value_block<Form>(utils::blocks::displacement_field.geometry_term);
constexpr auto gravityGradientValue =
utils::blocks::get_value_block<Form>(utils::blocks::gravity_field.gradient_term);
constexpr auto displacementResidual =
utils::blocks::get_residual_block<Form>(utils::blocks::displacement_field.geometry_term);
MFEM_VERIFY(
m_layout.size(densityValue) == f.densityFes->GetTrueVSize() &&
m_layout.size(displacementValue) == f.displacementFes->GetTrueVSize() &&
m_layout.size(gravityGradientValue) == f.gravityFluxFes->GetTrueVSize() &&
m_layout.size(displacementResidual) == f.displacementFes->GetTrueVSize(),
"Prepared gravity-displacement-force MFEM adapter received "
"incompatible coupled block sizes."
);
}
void PreparedGravityDisplacementForceJacobianOperator::Mult(
const mfem::Vector &direction,
mfem::Vector &action
) const {
MFEM_VERIFY(
m_preparedOperator.IsPrepared(), "Prepared gravity-displacement-force MFEM adapter requires a "
"prepared operator."
);
MFEM_VERIFY(
direction.Size() == Width(), "Prepared gravity-displacement-force MFEM adapter received a "
"direction with the wrong size."
);
using Form = utils::blocks::barotropic_equilibrium_form;
constexpr auto densityValue = utils::blocks::get_value_block<Form>(utils::blocks::density_field.mass_term);
constexpr auto displacementValue =
utils::blocks::get_value_block<Form>(utils::blocks::displacement_field.geometry_term);
constexpr auto gravityGradientValue =
utils::blocks::get_value_block<Form>(utils::blocks::gravity_field.gradient_term);
constexpr auto displacementResidual =
utils::blocks::get_residual_block<Form>(utils::blocks::displacement_field.geometry_term);
const mfem::Vector densityVariation(
const_cast<mfem::real_t *>(direction.GetData()) + m_layout.offset(densityValue), m_layout.size(densityValue)
);
const mfem::Vector displacementVariation(
const_cast<mfem::real_t *>(direction.GetData()) + m_layout.offset(displacementValue),
m_layout.size(displacementValue)
);
const mfem::Vector gravityGradientVariation(
const_cast<mfem::real_t *>(direction.GetData()) + m_layout.offset(gravityGradientValue),
m_layout.size(gravityGradientValue)
);
mfem::Vector displacementAction;
m_preparedOperator.ApplyCompleteJacobianAction(
densityVariation, displacementVariation, gravityGradientVariation, displacementAction
);
MFEM_VERIFY(
displacementAction.Size() == m_layout.size(displacementResidual),
"Prepared gravity-displacement-force MFEM adapter produced a "
"displacement action with the wrong size."
);
action.SetSize(Height());
action = 0.0;
const int residualOffset = m_layout.offset(displacementResidual);
for (int entry = 0; entry < displacementAction.Size(); ++entry) {
action(residualOffset + entry) = displacementAction(entry);
}
}
const GravityDisplacementForceLayout &PreparedGravityDisplacementForceJacobianOperator::GetLayout() const noexcept {
return m_layout;
}
} // namespace mean_field::operators

View File

@@ -11,19 +11,17 @@ import :operators.prepared_gravity_source;
namespace {
int get_operator_height(const mean_field::fem::FEM &f) {
MFEM_VERIFY(
f.gravityPotentialFes != nullptr,
"PreparedMappedGravitySourceOperator requires the "
"gravity-potential "
"finite-element space."
f.gravityPotentialFes != nullptr, "PreparedMappedGravitySourceOperator requires the "
"gravity-potential "
"finite-element space."
);
return f.gravityPotentialFes->GetTrueVSize();
}
int get_operator_width(const mean_field::fem::FEM &f) {
MFEM_VERIFY(
f.densityFes != nullptr,
"PreparedMappedGravitySourceOperator requires the density "
"finite-element space."
f.densityFes != nullptr, "PreparedMappedGravitySourceOperator requires the density "
"finite-element space."
);
return f.densityFes->GetTrueVSize();
}
@@ -35,8 +33,7 @@ namespace {
) {
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);
@@ -50,16 +47,12 @@ namespace {
const mfem::Vector &local_vector,
mfem::Vector &true_vector
) {
MFEM_VERIFY(
local_vector.Size() == finite_element_space.GetVSize(),
"Local vector has the wrong size."
);
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 = 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);
@@ -74,34 +67,27 @@ namespace {
const mfem::FiniteElement &potential_element,
const mfem::ElementTransformation &transformation
) {
using GravityField =
mean_field::field::Field<mean_field::field::Gravity>;
using GravityField = mean_field::field::Field<mean_field::field::Gravity>;
MFEM_VERIFY(
density_element.GetOrder() ==
mean_field::field::Density::Scalar::familyOrder,
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,
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 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 {
class FrozenMappedGravitySourceCoefficient final : public mfem::Coefficient {
public:
FrozenMappedGravitySourceCoefficient(
const mean_field::fem::FEM &f,
@@ -111,9 +97,7 @@ namespace {
: m_fem(f),
m_domain_mapper(domain_mapper),
m_workspace(domain_mapper.GetDimension()) {
true_to_local(
*m_fem.displacementFes, displacement_true, m_displacement_local
);
true_to_local(*m_fem.displacementFes, displacement_true, m_displacement_local);
}
double Eval(
@@ -128,79 +112,55 @@ namespace {
"Mapped gravity source coefficient received an invalid element "
"ID."
);
if (transformation.Attribute ==
m_domain_mapper.GetVacuumElementAttribute()) {
if (transformation.Attribute == m_domain_mapper.GetVacuumElementAttribute()) {
return 0.0;
}
LoadElement(element_id);
const mean_field::mapping::ElementMappingData mapping_data{
.displacement = *m_displacement_data,
.compactification = *m_compactification_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
);
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);
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 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
);
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
);
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
<< "\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) << ", "
<< "\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())
<< "\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;
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 "
@@ -208,8 +168,7 @@ namespace {
"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:
@@ -218,48 +177,32 @@ namespace {
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
);
m_fem.displacementFes->GetElementVDofs(element_id, m_displacement_dofs);
mfem::DofTransformation *compactification_dof_transformation =
m_fem.compactificationFes->GetElementDofs(
element_id, m_compactification_dofs
);
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
);
displacement_dof_transformation->InvTransformPrimal(m_element_displacement);
}
if (compactification_dof_transformation != nullptr) {
compactification_dof_transformation->InvTransformPrimal(
m_element_compactification
);
compactification_dof_transformation->InvTransformPrimal(m_element_compactification);
}
m_displacement_data = std::make_unique<
mean_field::mapping::ElementDisplacementData>(
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>(
m_compactification_data = std::make_unique<mean_field::mapping::ElementCompactificationData>(
compactification_element, m_element_compactification
);
@@ -277,10 +220,8 @@ namespace {
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};
@@ -298,30 +239,23 @@ namespace mean_field::operators {
),
m_fem(f),
m_domain_mapper(domain_mapper) {
MFEM_VERIFY(f.mesh != nullptr, "PreparedMappedGravitySourceOperator requires a mesh.");
MFEM_VERIFY(
f.mesh != nullptr,
"PreparedMappedGravitySourceOperator requires a mesh."
f.densityFes != nullptr, "PreparedMappedGravitySourceOperator requires the density "
"finite-element space."
);
MFEM_VERIFY(
f.densityFes != nullptr,
"PreparedMappedGravitySourceOperator requires the density "
"finite-element space."
f.gravityPotentialFes != nullptr, "PreparedMappedGravitySourceOperator requires the "
"gravity-potential "
"finite-element space."
);
MFEM_VERIFY(
f.gravityPotentialFes != nullptr,
"PreparedMappedGravitySourceOperator requires the "
"gravity-potential "
"finite-element space."
f.displacementFes != nullptr, "PreparedMappedGravitySourceOperator requires "
"the displacement 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."
f.compactificationFes != nullptr, "PreparedMappedGravitySourceOperator requires the compactification "
"finite-element space."
);
MFEM_VERIFY(
f.compactificationCoordinate != nullptr,
@@ -329,9 +263,8 @@ namespace mean_field::operators {
"coordinate."
);
MFEM_VERIFY(
f.quadratureFactory != nullptr,
"PreparedMappedGravitySourceOperator "
"requires the quadrature-rule factory."
f.quadratureFactory != nullptr, "PreparedMappedGravitySourceOperator "
"requires the quadrature-rule factory."
);
MFEM_VERIFY(
domain_mapper.GetDimension() == f.mesh->Dimension(),
@@ -339,14 +272,10 @@ namespace mean_field::operators {
"dimension."
);
utils::populate_element_mask(
f.mesh.get(), utils::DOMAINS::STELLAR, m_stellar_marker
);
utils::populate_element_mask(f.mesh.get(), utils::DOMAINS::STELLAR, m_stellar_marker);
}
void PreparedMappedGravitySourceOperator::Prepare(
const mfem::Vector &displacement_true
) {
void PreparedMappedGravitySourceOperator::Prepare(const mfem::Vector &displacement_true) {
MFEM_VERIFY(
displacement_true.Size() == m_fem.displacementFes->GetTrueVSize(),
"PreparedMappedGravitySourceOperator received a displacement "
@@ -356,9 +285,8 @@ namespace mean_field::operators {
for (int i = 0; i < displacement_true.Size(); ++i) {
MFEM_VERIFY(
std::isfinite(displacement_true(i)),
"PreparedMappedGravitySourceOperator received a non-finite "
"displacement value."
std::isfinite(displacement_true(i)), "PreparedMappedGravitySourceOperator received a non-finite "
"displacement value."
);
}
@@ -366,44 +294,33 @@ namespace mean_field::operators {
m_elements.clear();
m_elements.reserve(m_fem.mesh->GetNE());
FrozenMappedGravitySourceCoefficient source_coefficient(
m_fem, m_domain_mapper, displacement_true
);
FrozenMappedGravitySourceCoefficient source_coefficient(m_fem, m_domain_mapper, displacement_true);
for (int element_id = 0; element_id < m_fem.mesh->GetNE();
++element_id) {
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) {
if (attribute <= 0 || attribute > m_stellar_marker.Size() || m_stellar_marker[attribute - 1] == 0) {
continue;
}
m_elements.emplace_back();
ElementPAData &data = m_elements.back();
ElementPAData &data = m_elements.back();
data.element_id = element_id;
data.element_id = element_id;
data.density_dof_transformation =
m_fem.densityFes->GetElementDofs(element_id, data.density_dofs);
data.density_dof_transformation = m_fem.densityFes->GetElementDofs(element_id, data.density_dofs);
data.potential_dof_transformation =
m_fem.gravityPotentialFes->GetElementDofs(
element_id, data.potential_dofs
);
m_fem.gravityPotentialFes->GetElementDofs(element_id, data.potential_dofs);
const mfem::FiniteElement &density_element =
*m_fem.densityFes->GetFE(element_id);
const mfem::FiniteElement &density_element = *m_fem.densityFes->GetFE(element_id);
const mfem::FiniteElement &potential_element =
*m_fem.gravityPotentialFes->GetFE(element_id);
const mfem::FiniteElement &potential_element = *m_fem.gravityPotentialFes->GetFE(element_id);
mfem::ElementTransformation &transformation =
*m_fem.mesh->GetElementTransformation(element_id);
mfem::ElementTransformation &transformation = *m_fem.mesh->GetElementTransformation(element_id);
const mfem::IntegrationRule &integration_rule = get_source_rule(
m_fem, density_element, potential_element, transformation
);
const mfem::IntegrationRule &integration_rule =
get_source_rule(m_fem, density_element, potential_element, transformation);
const int quadrature_point_count = integration_rule.GetNPoints();
@@ -411,24 +328,17 @@ namespace mean_field::operators {
const int potential_dof_count = potential_element.GetDof();
data.density_basis.SetSize(
quadrature_point_count, density_dof_count
);
data.density_basis.SetSize(quadrature_point_count, density_dof_count);
data.potential_basis.SetSize(
quadrature_point_count, potential_dof_count
);
data.potential_basis.SetSize(quadrature_point_count, potential_dof_count);
data.quadrature_data.SetSize(quadrature_point_count);
mfem::Vector density_shape(density_dof_count);
mfem::Vector potential_shape(potential_dof_count);
for (int quadrature_point = 0;
quadrature_point < quadrature_point_count;
++quadrature_point) {
const mfem::IntegrationPoint &integration_point =
integration_rule.IntPoint(quadrature_point);
for (int quadrature_point = 0; quadrature_point < quadrature_point_count; ++quadrature_point) {
const mfem::IntegrationPoint &integration_point = integration_rule.IntPoint(quadrature_point);
transformation.SetIntPoint(&integration_point);
@@ -436,44 +346,34 @@ namespace mean_field::operators {
// including the finite-element map type.
density_element.CalcPhysShape(transformation, density_shape);
potential_element.CalcPhysShape(
transformation, potential_shape
);
potential_element.CalcPhysShape(transformation, potential_shape);
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);
data.potential_basis(quadrature_point, i) = potential_shape(i);
}
const double coefficient_value =
source_coefficient.Eval(transformation, integration_point);
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;
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 << "."
<< element_id << ", quadrature point " << quadrature_point << "."
);
data.quadrature_data(quadrature_point) = quadrature_value;
}
}
MFEM_VERIFY(
!m_elements.empty(),
"PreparedMappedGravitySourceOperator found no stellar elements."
);
MFEM_VERIFY(!m_elements.empty(), "PreparedMappedGravitySourceOperator found no stellar elements.");
m_is_prepared = true;
++m_preparation_count;
@@ -483,15 +383,13 @@ namespace mean_field::operators {
mfem::Vector &action
) const {
MFEM_VERIFY(
m_is_prepared,
"PreparedMappedGravitySourceOperator must be prepared before "
"Mult is called."
m_is_prepared, "PreparedMappedGravitySourceOperator must be prepared before "
"Mult is called."
);
MFEM_VERIFY(
density_true.Size() == Width(),
"PreparedMappedGravitySourceOperator received a density vector "
"with the wrong size."
density_true.Size() == Width(), "PreparedMappedGravitySourceOperator received a density vector "
"with the wrong size."
);
mfem::Vector density_local;
@@ -509,9 +407,7 @@ namespace mean_field::operators {
density_local.GetSubVector(data.density_dofs, element_density);
if (data.density_dof_transformation != nullptr) {
data.density_dof_transformation->InvTransformPrimal(
element_density
);
data.density_dof_transformation->InvTransformPrimal(element_density);
}
quadrature_density.SetSize(data.quadrature_data.Size());
@@ -527,14 +423,10 @@ namespace mean_field::operators {
element_action.SetSize(data.potential_dofs.Size());
// B_potential^T * D * B_density * x_e
data.potential_basis.MultTranspose(
quadrature_density, element_action
);
data.potential_basis.MultTranspose(quadrature_density, element_action);
if (data.potential_dof_transformation != nullptr) {
data.potential_dof_transformation->TransformDual(
element_action
);
data.potential_dof_transformation->TransformDual(element_action);
}
local_action.AddElementVector(data.potential_dofs, element_action);
@@ -548,22 +440,18 @@ namespace mean_field::operators {
mfem::Vector &action
) const {
MFEM_VERIFY(
m_is_prepared,
"PreparedMappedGravitySourceOperator must be prepared before "
"MultTranspose is called."
m_is_prepared, "PreparedMappedGravitySourceOperator must be prepared before "
"MultTranspose is called."
);
MFEM_VERIFY(
potential_true.Size() == Height(),
"PreparedMappedGravitySourceOperator received a potential vector "
"with the wrong size."
potential_true.Size() == Height(), "PreparedMappedGravitySourceOperator received a potential vector "
"with the wrong size."
);
mfem::Vector potential_local;
true_to_local(
*m_fem.gravityPotentialFes, potential_true, potential_local
);
true_to_local(*m_fem.gravityPotentialFes, potential_true, potential_local);
mfem::Vector local_action(m_fem.densityFes->GetVSize());
local_action = 0.0;
@@ -573,14 +461,10 @@ namespace mean_field::operators {
mfem::Vector element_action;
for (const ElementPAData &data : m_elements) {
potential_local.GetSubVector(
data.potential_dofs, element_potential
);
potential_local.GetSubVector(data.potential_dofs, element_potential);
if (data.potential_dof_transformation != nullptr) {
data.potential_dof_transformation->InvTransformPrimal(
element_potential
);
data.potential_dof_transformation->InvTransformPrimal(element_potential);
}
quadrature_potential.SetSize(data.quadrature_data.Size());
@@ -593,9 +477,7 @@ namespace mean_field::operators {
element_action.SetSize(data.density_dofs.Size());
data.density_basis.MultTranspose(
quadrature_potential, element_action
);
data.density_basis.MultTranspose(quadrature_potential, element_action);
if (data.density_dof_transformation != nullptr) {
data.density_dof_transformation->TransformDual(element_action);
@@ -610,8 +492,7 @@ namespace mean_field::operators {
return m_is_prepared;
}
std::uint64_t
PreparedMappedGravitySourceOperator::GetPreparationCount() const noexcept {
std::uint64_t PreparedMappedGravitySourceOperator::GetPreparationCount() const noexcept {
return m_preparation_count;
}
} // namespace mean_field::operators

View File

@@ -10,9 +10,8 @@ import :operators.prepared_hdiv_mass;
namespace {
int get_operator_size(const mean_field::fem::FEM &f) {
MFEM_VERIFY(
f.gravityFluxFes != nullptr,
"PreparedMappedHDivMassOperator requires the "
"gravity-gradient finite-element space."
f.gravityFluxFes != nullptr, "PreparedMappedHDivMassOperator requires the "
"gravity-gradient finite-element space."
);
return f.gravityFluxFes->GetTrueVSize();
}
@@ -24,8 +23,7 @@ namespace {
) {
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);
@@ -41,8 +39,7 @@ namespace {
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) {
if (attribute > 0 && attribute <= marker.Size() && marker[attribute - 1] != 0) {
return element_id;
}
}
@@ -55,23 +52,19 @@ namespace {
const mfem::Array<int> &marker,
const int representative_element_id
) {
const mfem::FiniteElement &representative_element =
*f.gravityFluxFes->GetFE(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) {
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);
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(),
@@ -85,16 +78,14 @@ namespace {
"finite-element order."
);
MFEM_VERIFY(
transformation.OrderW() ==
representative_transformation.OrderW(),
transformation.OrderW() == representative_transformation.OrderW(),
"Prepared H(div) mass domains currently require a uniform "
"geometry-weight order."
);
}
}
class FrozenMappedHDivMassCoefficient final
: public mfem::MatrixCoefficient {
class FrozenMappedHDivMassCoefficient final : public mfem::MatrixCoefficient {
public:
FrozenMappedHDivMassCoefficient(
const mean_field::fem::FEM &f,
@@ -107,9 +98,7 @@ namespace {
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
);
true_to_local(*m_fem.displacementFes, displacement_true, m_displacement_local);
}
void Eval(
@@ -125,9 +114,7 @@ namespace {
"Mapped H(div) mass coefficient received an invalid element ID."
);
const bool element_is_vacuum =
transformation.Attribute ==
m_domain_mapper.GetVacuumElementAttribute();
const bool element_is_vacuum = transformation.Attribute == m_domain_mapper.GetVacuumElementAttribute();
if (element_is_vacuum != m_elevates_vacuum) {
mass_tensor.SetSize(m_domain_mapper.GetDimension());
@@ -138,34 +125,27 @@ namespace {
LoadElement(element_id);
const mean_field::mapping::ElementMappingData mapping_data{
.displacement = *m_displacement_data,
.compactification = *m_compactification_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
);
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,
"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,
@@ -183,48 +163,32 @@ namespace {
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
);
m_fem.displacementFes->GetElementVDofs(element_id, m_displacement_dofs);
mfem::DofTransformation *compactification_dof_transformation =
m_fem.compactificationFes->GetElementDofs(
element_id, m_compactification_dofs
);
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
);
displacement_dof_transformation->InvTransformPrimal(m_element_displacement);
}
if (compactification_dof_transformation != nullptr) {
compactification_dof_transformation->InvTransformPrimal(
m_element_compactification
);
compactification_dof_transformation->InvTransformPrimal(m_element_compactification);
}
m_displacement_data = std::make_unique<
mean_field::mapping::ElementDisplacementData>(
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>(
m_compactification_data = std::make_unique<mean_field::mapping::ElementCompactificationData>(
compactification_element, m_element_compactification
);
@@ -242,10 +206,8 @@ namespace {
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};
@@ -261,33 +223,26 @@ namespace mean_field::operators {
: Operator(get_operator_size(f)),
m_fem(f),
m_domain_mapper(domain_mapper) {
MFEM_VERIFY(f.mesh != nullptr, "PreparedMappedHDivMassOperator requires a mesh.");
MFEM_VERIFY(
f.mesh != nullptr, "PreparedMappedHDivMassOperator requires a mesh."
f.gravityFluxFes != nullptr, "PreparedMappedHDivMassOperator requires the "
"gravity-gradient finite-element space."
);
MFEM_VERIFY(
f.gravityFluxFes != nullptr,
"PreparedMappedHDivMassOperator requires the "
"gravity-gradient finite-element space."
f.displacementFes != nullptr, "PreparedMappedHDivMassOperator requires the "
"displacement finite-element space."
);
MFEM_VERIFY(
f.displacementFes != nullptr,
"PreparedMappedHDivMassOperator requires the "
"displacement finite-element space."
f.compactificationFes != nullptr, "PreparedMappedHDivMassOperator requires the compactification "
"finite-element space."
);
MFEM_VERIFY(
f.compactificationFes != nullptr,
"PreparedMappedHDivMassOperator requires the compactification "
"finite-element space."
f.compactificationCoordinate != nullptr, "PreparedMappedHDivMassOperator requires the compactification "
"coordinate."
);
MFEM_VERIFY(
f.compactificationCoordinate != nullptr,
"PreparedMappedHDivMassOperator requires the compactification "
"coordinate."
);
MFEM_VERIFY(
f.quadratureFactory != nullptr,
"PreparedMappedHDivMassOperator requires the quadrature-rule "
"factory."
f.quadratureFactory != nullptr, "PreparedMappedHDivMassOperator requires the quadrature-rule "
"factory."
);
MFEM_VERIFY(
domain_mapper.GetDimension() == f.mesh->Dimension(),
@@ -295,39 +250,26 @@ namespace mean_field::operators {
"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
);
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);
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."
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_true
) {
void PreparedMappedHDivMassOperator::Prepare(const mfem::Vector &displacement_true) {
MFEM_VERIFY(
displacement_true.Size() == m_fem.displacementFes->GetTrueVSize(),
"PreparedMappedHDivMassOperator received a displacement vector "
@@ -337,71 +279,48 @@ namespace mean_field::operators {
for (int i = 0; i < displacement_true.Size(); ++i) {
MFEM_VERIFY(
std::isfinite(displacement_true(i)),
"PreparedMappedHDivMassOperator received a non-finite "
"displacement "
"value."
std::isfinite(displacement_true(i)), "PreparedMappedHDivMassOperator received a non-finite "
"displacement "
"value."
);
}
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_coefficient =
std::make_unique<FrozenMappedHDivMassCoefficient>(
m_fem, m_domain_mapper, displacement_true, false
);
std::make_unique<FrozenMappedHDivMassCoefficient>(m_fem, m_domain_mapper, displacement_true, false);
m_vacuum_mass_coefficient =
std::make_unique<FrozenMappedHDivMassCoefficient>(
m_fem, m_domain_mapper, displacement_true, true
);
std::make_unique<FrozenMappedHDivMassCoefficient>(m_fem, m_domain_mapper, displacement_true, true);
m_mass_form =
std::make_unique<mfem::ParBilinearForm>(m_fem.gravityFluxFes.get());
m_mass_form = std::make_unique<mfem::ParBilinearForm>(m_fem.gravityFluxFes.get());
m_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
*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->AddDomainIntegrator(stellar_integrator.release(), m_stellar_marker);
m_mass_form->AddDomainIntegrator(vacuum_integrator.release(), m_vacuum_marker);
m_mass_form->Assemble();
m_is_prepared = true;
@@ -434,8 +353,7 @@ namespace mean_field::operators {
return m_is_prepared;
}
std::uint64_t
PreparedMappedHDivMassOperator::GetPreparationCount() const noexcept {
std::uint64_t PreparedMappedHDivMassOperator::GetPreparationCount() const noexcept {
return m_preparation_count;
}
} // namespace mean_field::operators

View File

@@ -0,0 +1,668 @@
module;
#include <array>
#include <cmath>
#include <mfem.hpp>
module mean_field;
import :operators.prepared_mass_normalization;
namespace {
void validate_finite_vector(
const mfem::Vector &vector,
const char *message
) {
for (int index = 0; index < vector.Size(); ++index) {
MFEM_VERIFY(std::isfinite(vector(index)), message);
}
}
void true_to_local(
const mfem::ParFiniteElementSpace &finiteElementSpace,
const mfem::Vector &trueVector,
mfem::Vector &localVector
) {
MFEM_VERIFY(trueVector.Size() == finiteElementSpace.GetTrueVSize(), "True vector has the wrong size.");
localVector.SetSize(finiteElementSpace.GetVSize());
const mfem::Operator *prolongation = finiteElementSpace.GetProlongationMatrix();
if (prolongation != nullptr) {
prolongation->Mult(trueVector, localVector);
} else {
localVector = trueVector;
}
}
const mfem::IntegrationRule &get_mass_normalization_rule(
const mean_field::fem::FEM &f,
const mfem::FiniteElement &densityElement,
const mfem::ElementTransformation &transformation
) {
using DensityField = mean_field::field::Field<mean_field::field::Density>;
MFEM_VERIFY(
densityElement.GetOrder() == mean_field::field::Density::Scalar::familyOrder,
"The mass-normalization element does not match the registered "
"density field."
);
const mean_field::quadrature::Query query =
DensityField::make_query<mean_field::field::Density::Form::MassNormalization>(
mean_field::quadrature::QuadratureRole::discretization, transformation.OrderW(), std::array<int, 0>{},
mean_field::utils::DOMAINS::STELLAR, mean_field::quadrature::MappingKind::general
);
const auto resolution = f.quadratureFactory->get(query, transformation.GetGeometryType());
MFEM_VERIFY(
resolution.integration_rule != nullptr, "The quadrature policy did not return a mass-normalization rule."
);
return *resolution.integration_rule;
}
void validate_shared_gravity_revisions(
const mean_field::operators::context::gravity_field::GravityFieldLinearizationContext &gravityContext,
const mean_field::operators::MassNormalizationDependencies &dependencies
) {
MFEM_VERIFY(
gravityContext.IsPrepared(), "PreparedMassNormalizationOperator requires the shared gravity "
"linearization context to be prepared first."
);
const auto &revisions = gravityContext.GetRevisions();
MFEM_VERIFY(
revisions.discretization.value == dependencies.discretization.revision &&
revisions.density.value == dependencies.density.revision &&
revisions.displacement.value == dependencies.displacement.revision,
"PreparedMassNormalizationOperator received dependency revisions "
"that do not match the shared gravity context."
);
}
void validate_shared_identity_transition(
const mean_field::operators::MassNormalizationDependencyStamp &prepared,
const mean_field::operators::MassNormalizationDependencyStamp &requested,
const char *message
) {
MFEM_VERIFY(prepared.identity == requested.identity || prepared.revision != requested.revision, message);
}
} // namespace
namespace mean_field::operators {
PreparedMassNormalizationOperator::PreparedMassNormalizationOperator(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const context::gravity_field::GravityFieldLinearizationContext &gravityContext
)
: m_fem(f),
m_domainMapper(domainMapper),
m_gravityContext(gravityContext) {
MFEM_VERIFY(m_fem.mesh != nullptr, "PreparedMassNormalizationOperator requires a mesh.");
MFEM_VERIFY(
m_fem.densityFes != nullptr && m_fem.displacementFes != nullptr && m_fem.compactificationFes != nullptr &&
m_fem.compactificationCoordinate != nullptr && m_fem.quadratureFactory != nullptr,
"PreparedMassNormalizationOperator requires density, "
"displacement, compactification, and quadrature data."
);
MFEM_VERIFY(
m_domainMapper.GetDimension() == m_fem.mesh->Dimension(),
"PreparedMassNormalizationOperator received a mapper with the "
"wrong dimension."
);
}
PreparedMassNormalizationReport PreparedMassNormalizationOperator::Prepare(
const MassNormalizationStateView &state,
const MassNormalizationDependencies &dependencies
) {
MFEM_VERIFY(
std::isfinite(state.targetMass) && state.targetMass > 0.0,
"PreparedMassNormalizationOperator requires a finite, positive "
"target mass."
);
validate_shared_gravity_revisions(m_gravityContext, dependencies);
if (m_isPrepared) {
validate_shared_identity_transition(
m_preparedDependencies.discretization, dependencies.discretization,
"A new mass-normalization discretization identity must also "
"change the shared gravity revision."
);
validate_shared_identity_transition(
m_preparedDependencies.density, dependencies.density,
"A new mass-normalization density identity must also change "
"the shared gravity revision."
);
validate_shared_identity_transition(
m_preparedDependencies.displacement, dependencies.displacement,
"A new mass-normalization displacement identity must also "
"change the shared gravity revision."
);
}
const bool rebuildStaticPlan =
!m_isPrepared || dependencies.discretization != m_preparedDependencies.discretization;
const bool refreshGeometry =
rebuildStaticPlan || dependencies.displacement != m_preparedDependencies.displacement;
const bool refreshDensity = rebuildStaticPlan || dependencies.density != m_preparedDependencies.density;
const bool updateTargetMass = !m_isPrepared || dependencies.targetMass != m_preparedDependencies.targetMass ||
state.targetMass != m_targetMass;
m_isPrepared = false;
PreparedMassNormalizationReport report;
if (rebuildStaticPlan) {
BuildStaticPlan();
report.rebuiltStaticPlan = true;
}
if (refreshGeometry) {
RefreshGeometry(m_gravityContext.GetGeometryContext().GetDisplacement());
report.refreshedGeometry = true;
}
if (refreshDensity) {
RefreshDensity(m_gravityContext.GetDensity());
report.refreshedDensity = true;
}
if (updateTargetMass) {
m_targetMass = state.targetMass;
report.updatedTargetMass = true;
}
if (refreshGeometry || refreshDensity) {
AssembleResidual();
report.assembledResidual = true;
} else if (updateTargetMass) {
m_cachedResidual.SetSize(1);
m_cachedResidual(0) = m_currentMass - m_targetMass;
++m_preparationCount;
report.assembledResidual = true;
}
m_preparedDependencies = dependencies;
m_isPrepared = true;
return report;
}
void PreparedMassNormalizationOperator::BuildStaticPlan() {
m_elements.clear();
m_elements.reserve(m_fem.mesh->GetNE());
const int vacuumAttribute = m_domainMapper.GetVacuumElementAttribute();
int localStellarElementCount = 0;
for (int elementId = 0; elementId < m_fem.mesh->GetNE(); ++elementId) {
mfem::ElementTransformation *transformation = m_fem.mesh->GetElementTransformation(elementId);
MFEM_VERIFY(
transformation != nullptr, "PreparedMassNormalizationOperator received a null element "
"transformation."
);
if (transformation->Attribute == vacuumAttribute) {
continue;
}
++localStellarElementCount;
m_elements.emplace_back();
ElementPAData &data = m_elements.back();
data.elementId = elementId;
data.densityDofTransformation = m_fem.densityFes->GetElementDofs(elementId, data.densityDofs);
data.displacementDofTransformation =
m_fem.displacementFes->GetElementVDofs(elementId, data.displacementDofs);
data.compactificationDofTransformation =
m_fem.compactificationFes->GetElementDofs(elementId, data.compactificationDofs);
const mfem::FiniteElement &densityElement = *m_fem.densityFes->GetFE(elementId);
const mfem::IntegrationRule &integrationRule =
get_mass_normalization_rule(m_fem, densityElement, *transformation);
data.quadraturePoints.resize(integrationRule.GetNPoints());
for (int quadraturePoint = 0; quadraturePoint < integrationRule.GetNPoints(); ++quadraturePoint) {
QuadraturePointData &point = data.quadraturePoints[quadraturePoint];
point.integrationPoint = integrationRule.IntPoint(quadraturePoint);
point.densityShape.SetSize(densityElement.GetDof());
densityElement.CalcShape(point.integrationPoint, point.densityShape);
}
}
int globalStellarElementCount = 0;
MPI_Allreduce(
&localStellarElementCount, &globalStellarElementCount, 1, MPI_INT, MPI_SUM, m_fem.mesh->GetComm()
);
MFEM_VERIFY(globalStellarElementCount > 0, "PreparedMassNormalizationOperator found no stellar elements.");
}
void PreparedMassNormalizationOperator::RefreshGeometry(const mfem::Vector &displacement) {
MFEM_VERIFY(
displacement.Size() == m_fem.displacementFes->GetTrueVSize(),
"PreparedMassNormalizationOperator received a displacement "
"vector with the wrong size."
);
validate_finite_vector(
displacement, "PreparedMassNormalizationOperator received a non-finite "
"displacement value."
);
mfem::Vector displacementLocal;
true_to_local(*m_fem.displacementFes, displacement, displacementLocal);
mapping::DomainMapperStateless::Workspace workspace(m_fem.mesh->Dimension());
for (ElementPAData &data : m_elements) {
displacementLocal.GetSubVector(data.displacementDofs, data.baseDisplacement);
m_fem.compactificationCoordinate->GetSubVector(data.compactificationDofs, data.compactification);
if (data.displacementDofTransformation != nullptr) {
data.displacementDofTransformation->InvTransformPrimal(data.baseDisplacement);
}
if (data.compactificationDofTransformation != nullptr) {
data.compactificationDofTransformation->InvTransformPrimal(data.compactification);
}
const mfem::FiniteElement &displacementElement = *m_fem.displacementFes->GetFE(data.elementId);
const mfem::FiniteElement &compactificationElement = *m_fem.compactificationFes->GetFE(data.elementId);
const mapping::ElementDisplacementData displacementData =
mapping::ElementDisplacementDataFromElementVDofs(displacementElement, data.baseDisplacement);
const mapping::ElementCompactificationData compactificationData(
compactificationElement, data.compactification
);
const mapping::ElementMappingData mappingData{
.displacement = displacementData, .compactification = compactificationData
};
mfem::ElementTransformation *transformation = m_fem.mesh->GetElementTransformation(data.elementId);
for (QuadraturePointData &point : data.quadraturePoints) {
const mapping::MappingStatus status = m_domainMapper.EvaluateVolume(
mappingData, *transformation, point.integrationPoint, workspace, point.mappingContext
);
MFEM_VERIFY(
status == mapping::MappingStatus::valid, "Stateless mapping failed while preparing mass "
"normalization. Element: "
<< data.elementId
<< ", attribute: " << transformation->Attribute
<< ", status: " << static_cast<int>(status)
);
}
}
}
void PreparedMassNormalizationOperator::RefreshDensity(const mfem::Vector &density) {
MFEM_VERIFY(
density.Size() == m_fem.densityFes->GetTrueVSize(),
"PreparedMassNormalizationOperator received a density vector "
"with the wrong size."
);
validate_finite_vector(
density, "PreparedMassNormalizationOperator received a non-finite density "
"value."
);
mfem::Vector densityLocal;
true_to_local(*m_fem.densityFes, density, densityLocal);
mfem::Vector elementDensity;
for (ElementPAData &data : m_elements) {
densityLocal.GetSubVector(data.densityDofs, elementDensity);
if (data.densityDofTransformation != nullptr) {
data.densityDofTransformation->InvTransformPrimal(elementDensity);
}
for (QuadraturePointData &point : data.quadraturePoints) {
point.density = elementDensity * point.densityShape;
MFEM_VERIFY(
std::isfinite(point.density), "PreparedMassNormalizationOperator produced a non-finite "
"quadrature density."
);
}
}
}
void PreparedMassNormalizationOperator::AssembleResidual() {
double localMass = 0.0;
for (const ElementPAData &data : m_elements) {
for (const QuadraturePointData &point : data.quadraturePoints) {
localMass += point.density * point.mappingContext.quadrature.weight;
}
}
m_currentMass = GlobalSum(localMass);
MFEM_VERIFY(std::isfinite(m_currentMass), "PreparedMassNormalizationOperator assembled a non-finite mass.");
m_cachedResidual.SetSize(1);
m_cachedResidual(0) = m_currentMass - m_targetMass;
++m_preparationCount;
}
void PreparedMassNormalizationOperator::BuildResidual(mfem::Vector &residual) const {
VerifyPrepared();
residual = m_cachedResidual;
++m_residualApplicationCount;
}
double PreparedMassNormalizationOperator::EvaluateDensityActionLocal(const mfem::Vector &densityVariation) const {
MFEM_VERIFY(
densityVariation.Size() == m_fem.densityFes->GetTrueVSize(),
"Mass-normalization density action received a vector with the "
"wrong size."
);
validate_finite_vector(densityVariation, "Mass-normalization density action received a non-finite value.");
mfem::Vector densityVariationLocal;
true_to_local(*m_fem.densityFes, densityVariation, densityVariationLocal);
mfem::Vector elementDensityVariation;
double localAction = 0.0;
for (const ElementPAData &data : m_elements) {
densityVariationLocal.GetSubVector(data.densityDofs, elementDensityVariation);
if (data.densityDofTransformation != nullptr) {
data.densityDofTransformation->InvTransformPrimal(elementDensityVariation);
}
for (const QuadraturePointData &point : data.quadraturePoints) {
localAction += (elementDensityVariation * point.densityShape) * point.mappingContext.quadrature.weight;
}
}
return localAction;
}
double PreparedMassNormalizationOperator::EvaluateDisplacementActionLocal(
const mfem::Vector &displacementVariation
) const {
MFEM_VERIFY(
displacementVariation.Size() == m_fem.displacementFes->GetTrueVSize(),
"Mass-normalization displacement action received a vector with "
"the wrong size."
);
validate_finite_vector(
displacementVariation, "Mass-normalization displacement action received a non-finite "
"value."
);
mfem::Vector displacementVariationLocal;
true_to_local(*m_fem.displacementFes, displacementVariation, displacementVariationLocal);
mapping::DomainMapperStateless::Workspace workspace(m_fem.mesh->Dimension());
mfem::Vector elementDisplacementVariation;
double localAction = 0.0;
for (const ElementPAData &data : m_elements) {
displacementVariationLocal.GetSubVector(data.displacementDofs, elementDisplacementVariation);
if (data.displacementDofTransformation != nullptr) {
data.displacementDofTransformation->InvTransformPrimal(elementDisplacementVariation);
}
const mfem::FiniteElement &displacementElement = *m_fem.displacementFes->GetFE(data.elementId);
const mfem::FiniteElement &compactificationElement = *m_fem.compactificationFes->GetFE(data.elementId);
const mapping::ElementDisplacementData baseDisplacementData =
mapping::ElementDisplacementDataFromElementVDofs(displacementElement, data.baseDisplacement);
const mapping::ElementDisplacementData directionData =
mapping::ElementDisplacementDataFromElementVDofs(displacementElement, elementDisplacementVariation);
const mapping::ElementCompactificationData compactificationData(
compactificationElement, data.compactification
);
const mapping::ElementMappingData mappingData{
.displacement = baseDisplacementData, .compactification = compactificationData
};
mfem::ElementTransformation *transformation = m_fem.mesh->GetElementTransformation(data.elementId);
for (const QuadraturePointData &point : data.quadraturePoints) {
mapping::VolumeMappingVariation variation;
const mapping::MappingStatus status = m_domainMapper.EvaluateVolumeVariation(
mappingData, directionData, *transformation, point.integrationPoint, point.mappingContext,
workspace, variation
);
MFEM_VERIFY(
status == mapping::MappingStatus::valid, "Stateless mapping variation failed in the "
"mass-normalization displacement action. Element: "
<< data.elementId
<< ", status: " << static_cast<int>(status)
);
localAction += point.density * variation.weight_variation;
}
}
return localAction;
}
void PreparedMassNormalizationOperator::ApplyDensityJacobianAction(
const mfem::Vector &densityVariation,
mfem::Vector &action
) const {
VerifyPrepared();
action.SetSize(1);
action(0) = GlobalSum(EvaluateDensityActionLocal(densityVariation));
++m_actionStatistics.densityApplications;
}
void PreparedMassNormalizationOperator::ApplyDisplacementJacobianAction(
const mfem::Vector &displacementVariation,
mfem::Vector &action
) const {
VerifyPrepared();
action.SetSize(1);
action(0) = GlobalSum(EvaluateDisplacementActionLocal(displacementVariation));
++m_actionStatistics.displacementApplications;
}
void PreparedMassNormalizationOperator::ApplyCompleteJacobianAction(
const mfem::Vector &densityVariation,
const mfem::Vector &displacementVariation,
mfem::Vector &action
) const {
VerifyPrepared();
const double localAction =
EvaluateDensityActionLocal(densityVariation) + EvaluateDisplacementActionLocal(displacementVariation);
action.SetSize(1);
action(0) = GlobalSum(localAction);
++m_actionStatistics.completeApplications;
}
double PreparedMassNormalizationOperator::GlobalSum(const double localValue) const {
double globalValue = 0.0;
MPI_Allreduce(&localValue, &globalValue, 1, MPI_DOUBLE, MPI_SUM, m_fem.mesh->GetComm());
return globalValue;
}
bool PreparedMassNormalizationOperator::IsPrepared() const noexcept {
if (!m_isPrepared || !m_gravityContext.IsPrepared()) {
return false;
}
const auto &revisions = m_gravityContext.GetRevisions();
return revisions.discretization.value == m_preparedDependencies.discretization.revision &&
revisions.density.value == m_preparedDependencies.density.revision &&
revisions.displacement.value == m_preparedDependencies.displacement.revision;
}
double PreparedMassNormalizationOperator::GetCurrentMass() const {
VerifyPrepared();
return m_currentMass;
}
double PreparedMassNormalizationOperator::GetTargetMass() const {
VerifyPrepared();
return m_targetMass;
}
std::uint64_t PreparedMassNormalizationOperator::GetPreparationCount() const noexcept {
return m_preparationCount;
}
std::uint64_t PreparedMassNormalizationOperator::GetResidualApplicationCount() const noexcept {
return m_residualApplicationCount;
}
const PreparedMassNormalizationActionStatistics &
PreparedMassNormalizationOperator::GetActionStatistics() const noexcept {
return m_actionStatistics;
}
const fem::FEM &PreparedMassNormalizationOperator::GetFEM() const noexcept {
return m_fem;
}
const context::gravity_field::GravityFieldLinearizationContext &
PreparedMassNormalizationOperator::GetGravityContext() const noexcept {
return m_gravityContext;
}
void PreparedMassNormalizationOperator::VerifyPrepared() const {
MFEM_VERIFY(
IsPrepared(), "PreparedMassNormalizationOperator must be prepared for the "
"current shared gravity-context revisions."
);
}
PreparedMassNormalizationJacobianOperator::PreparedMassNormalizationJacobianOperator(
const MassNormalizationLayout &layout,
const PreparedMassNormalizationOperator &preparedOperator
)
: mfem::Operator(
layout.residual_offsets().Last(),
layout.value_offsets().Last()
),
m_layout(layout),
m_preparedOperator(preparedOperator) {
const fem::FEM &f = m_preparedOperator.GetFEM();
MFEM_VERIFY(
f.densityFes != nullptr && f.displacementFes != nullptr && f.gravityFluxFes != nullptr &&
f.gravityPotentialFes != nullptr && f.enthalpyFes != nullptr,
"Prepared mass-normalization MFEM adapter requires every "
"finite-element space in the barotropic equilibrium layout."
);
using Form = utils::blocks::barotropic_equilibrium_form;
constexpr auto densityValue = utils::blocks::get_value_block<Form>(utils::blocks::density_field.mass_term);
constexpr auto displacementValue =
utils::blocks::get_value_block<Form>(utils::blocks::displacement_field.geometry_term);
constexpr auto gravityGradientValue =
utils::blocks::get_value_block<Form>(utils::blocks::gravity_field.gradient_term);
constexpr auto gravityPotentialValue =
utils::blocks::get_value_block<Form>(utils::blocks::gravity_field.poisson_term);
constexpr auto enthalpyValue =
utils::blocks::get_value_block<Form>(utils::blocks::enthalpy_field.specific_term);
constexpr auto barotropicConstantValue =
utils::blocks::get_value_block<Form>(utils::blocks::barotropic_constant_field.mass_normalization_term);
constexpr auto gravityGradientResidual =
utils::blocks::get_residual_block<Form>(utils::blocks::gravity_field.gradient_term);
constexpr auto gravityPotentialResidual =
utils::blocks::get_residual_block<Form>(utils::blocks::gravity_field.poisson_term);
constexpr auto densityResidual =
utils::blocks::get_residual_block<Form>(utils::blocks::density_field.mass_term);
constexpr auto displacementResidual =
utils::blocks::get_residual_block<Form>(utils::blocks::displacement_field.geometry_term);
constexpr auto enthalpyResidual =
utils::blocks::get_residual_block<Form>(utils::blocks::enthalpy_field.specific_term);
constexpr auto massResidual =
utils::blocks::get_residual_block<Form>(utils::blocks::barotropic_constant_field.mass_normalization_term);
MFEM_VERIFY(
m_layout.size(densityValue) == f.densityFes->GetTrueVSize() &&
m_layout.size(displacementValue) == f.displacementFes->GetTrueVSize() &&
m_layout.size(gravityGradientValue) == f.gravityFluxFes->GetTrueVSize() &&
m_layout.size(gravityPotentialValue) == f.gravityPotentialFes->GetTrueVSize() &&
m_layout.size(enthalpyValue) == f.enthalpyFes->GetTrueVSize() &&
m_layout.size(barotropicConstantValue) == 1 &&
m_layout.size(gravityGradientResidual) == f.gravityFluxFes->GetTrueVSize() &&
m_layout.size(gravityPotentialResidual) == f.gravityPotentialFes->GetTrueVSize() &&
m_layout.size(densityResidual) == f.densityFes->GetTrueVSize() &&
m_layout.size(displacementResidual) == f.displacementFes->GetTrueVSize() &&
m_layout.size(enthalpyResidual) == f.enthalpyFes->GetTrueVSize() && m_layout.size(massResidual) == 1,
"Prepared mass-normalization MFEM adapter received incompatible "
"barotropic block sizes."
);
}
void PreparedMassNormalizationJacobianOperator::Mult(
const mfem::Vector &direction,
mfem::Vector &action
) const {
MFEM_VERIFY(
m_preparedOperator.IsPrepared(), "Prepared mass-normalization MFEM adapter requires a prepared "
"row operator."
);
MFEM_VERIFY(
direction.Size() == Width(), "Prepared mass-normalization MFEM adapter received a direction "
"with the wrong size."
);
using Form = utils::blocks::barotropic_equilibrium_form;
constexpr auto densityValue = utils::blocks::get_value_block<Form>(utils::blocks::density_field.mass_term);
constexpr auto displacementValue =
utils::blocks::get_value_block<Form>(utils::blocks::displacement_field.geometry_term);
constexpr auto massResidual =
utils::blocks::get_residual_block<Form>(utils::blocks::barotropic_constant_field.mass_normalization_term);
const mfem::Vector densityVariation(
const_cast<mfem::real_t *>(direction.GetData()) + m_layout.offset(densityValue), m_layout.size(densityValue)
);
const mfem::Vector displacementVariation(
const_cast<mfem::real_t *>(direction.GetData()) + m_layout.offset(displacementValue),
m_layout.size(displacementValue)
);
mfem::Vector massAction;
m_preparedOperator.ApplyCompleteJacobianAction(densityVariation, displacementVariation, massAction);
action.SetSize(Height());
action = 0.0;
action(m_layout.offset(massResidual)) = massAction(0);
}
const MassNormalizationLayout &PreparedMassNormalizationJacobianOperator::GetLayout() const noexcept {
return m_layout;
}
} // namespace mean_field::operators

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,288 @@
module;
#include <mfem.hpp>
module mean_field;
import :operators.kernels.rotational_displacement_force;
import :operators.prepared_rotational_displacement_force;
namespace mean_field::operators {
PreparedRotationalDisplacementForceOperator::PreparedRotationalDisplacementForceOperator(
const fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper
)
: m_fem(f),
m_domainMapper(domainMapper),
m_context(
f,
domainMapper
) {
MFEM_VERIFY(m_fem.mesh != nullptr, "PreparedRotationalDisplacementForceOperator requires a mesh.");
MFEM_VERIFY(
m_fem.mesh->Dimension() == 3, "PreparedRotationalDisplacementForceOperator requires a "
"three-dimensional mesh."
);
MFEM_VERIFY(
m_fem.densityFes != nullptr && m_fem.displacementFes != nullptr,
"PreparedRotationalDisplacementForceOperator requires density "
"and displacement finite-element spaces."
);
MFEM_VERIFY(
m_fem.compactificationFes != nullptr && m_fem.compactificationCoordinate != nullptr,
"PreparedRotationalDisplacementForceOperator requires the "
"compactification coordinate."
);
MFEM_VERIFY(
m_fem.quadratureFactory != nullptr, "PreparedRotationalDisplacementForceOperator requires the "
"quadrature-rule factory."
);
MFEM_VERIFY(
m_domainMapper.GetDimension() == m_fem.mesh->Dimension(),
"PreparedRotationalDisplacementForceOperator received a mapper "
"with the wrong dimension."
);
}
PreparedRotationalDisplacementForceReport PreparedRotationalDisplacementForceOperator::Prepare(
const context::rotational_displacement_force::RotationalDisplacementForceStateView &state,
const context::rotational_displacement_force::RotationalDisplacementForceDependencies &dependencies,
const physics::RigidRotation &rotation
) {
const bool rotationChanged =
!m_context.IsPrepared() || dependencies.rotation != m_context.GetDependencies().rotation;
PreparedRotationalDisplacementForceReport report;
report.contextReport = m_context.Prepare(state, dependencies);
if (!report.contextReport.DidAnyWork()) {
return report;
}
m_isPrepared = false;
if (rotationChanged) {
m_rotation = rotation;
report.updatedRotation = true;
}
MFEM_VERIFY(
m_rotation.has_value(), "PreparedRotationalDisplacementForceOperator has no frozen "
"rotation state."
);
if (report.contextReport.preparedBaseState) {
kernels::apply_rotational_displacement_force_residual(
m_fem, m_domainMapper, *m_rotation, m_context.GetBaseDensityTrue(), m_context.GetDisplacementTrue(),
m_cachedResidual
);
++m_residualPreparationCount;
report.preparedResidual = true;
}
MFEM_VERIFY(
m_cachedResidual.Size() == m_fem.displacementFes->GetTrueVSize(),
"The prepared rotational-displacement-force residual has the "
"wrong size."
);
m_preparedDependencies = dependencies;
m_isPrepared = true;
return report;
}
void PreparedRotationalDisplacementForceOperator::BuildResidual(mfem::Vector &residual) const {
VerifyPrepared();
residual = m_cachedResidual;
++m_residualApplicationCount;
}
void PreparedRotationalDisplacementForceOperator::ApplyDensityJacobianAction(
const mfem::Vector &densityVariation,
mfem::Vector &action
) const {
VerifyPrepared();
kernels::apply_rotational_displacement_force_density_action(
m_fem, m_domainMapper, *m_rotation, densityVariation, m_context.GetDisplacementTrue(), action
);
++m_densityJacobianStatistics.applications;
}
void PreparedRotationalDisplacementForceOperator::ApplyDisplacementJacobianAction(
const mfem::Vector &displacementVariation,
mfem::Vector &action
) const {
VerifyPrepared();
kernels::apply_rotational_displacement_force_displacement_action(
m_fem, m_domainMapper, *m_rotation, m_context.GetBaseDensityTrue(), displacementVariation,
m_context.GetDisplacementTrue(), action
);
++m_displacementJacobianStatistics.applications;
}
void PreparedRotationalDisplacementForceOperator::ApplyCompleteJacobianAction(
const mfem::Vector &densityVariation,
const mfem::Vector &displacementVariation,
mfem::Vector &action
) const {
VerifyPrepared();
kernels::apply_rotational_displacement_force_complete_action(
m_fem, m_domainMapper, *m_rotation, m_context.GetBaseDensityTrue(), densityVariation, displacementVariation,
m_context.GetDisplacementTrue(), action
);
++m_densityJacobianStatistics.applications;
++m_displacementJacobianStatistics.applications;
++m_completeJacobianStatistics.applications;
}
bool PreparedRotationalDisplacementForceOperator::IsPrepared() const noexcept {
return m_isPrepared && m_rotation.has_value() && m_context.MatchesDependencies(m_preparedDependencies);
}
const context::rotational_displacement_force::RotationalDisplacementForcePreparationStatistics &
PreparedRotationalDisplacementForceOperator::GetContextPreparationStatistics() const noexcept {
return m_context.GetPreparationStatistics();
}
std::uint64_t PreparedRotationalDisplacementForceOperator::GetResidualPreparationCount() const noexcept {
return m_residualPreparationCount;
}
std::uint64_t PreparedRotationalDisplacementForceOperator::GetResidualApplicationCount() const noexcept {
return m_residualApplicationCount;
}
const PreparedRotationalDisplacementForceColumnStatistics &
PreparedRotationalDisplacementForceOperator::GetDensityJacobianStatistics() const noexcept {
return m_densityJacobianStatistics;
}
const PreparedRotationalDisplacementForceColumnStatistics &
PreparedRotationalDisplacementForceOperator::GetDisplacementJacobianStatistics() const noexcept {
return m_displacementJacobianStatistics;
}
const PreparedRotationalDisplacementForceCompleteStatistics &
PreparedRotationalDisplacementForceOperator::GetCompleteJacobianStatistics() const noexcept {
return m_completeJacobianStatistics;
}
const fem::FEM &PreparedRotationalDisplacementForceOperator::GetFEM() const noexcept {
return m_fem;
}
const context::rotational_displacement_force::RotationalDisplacementForceLinearizationContext &
PreparedRotationalDisplacementForceOperator::GetContext() const noexcept {
return m_context;
}
void PreparedRotationalDisplacementForceOperator::VerifyPrepared() const {
MFEM_VERIFY(
IsPrepared(), "PreparedRotationalDisplacementForceOperator must be prepared "
"for the current revisions before residual or Jacobian "
"application."
);
}
PreparedRotationalDisplacementForceJacobianOperator::PreparedRotationalDisplacementForceJacobianOperator(
const RotationalDisplacementForceLayout &layout,
const PreparedRotationalDisplacementForceOperator &preparedOperator
)
: mfem::Operator(
layout.residual_offsets().Last(),
layout.value_offsets().Last()
),
m_layout(layout),
m_preparedOperator(preparedOperator) {
const fem::FEM &f = m_preparedOperator.GetFEM();
using Form = utils::blocks::barotropic_equilibrium_form;
constexpr auto densityValue = utils::blocks::get_value_block<Form>(utils::blocks::density_field.mass_term);
constexpr auto displacementValue =
utils::blocks::get_value_block<Form>(utils::blocks::displacement_field.geometry_term);
constexpr auto displacementResidual =
utils::blocks::get_residual_block<Form>(utils::blocks::displacement_field.geometry_term);
MFEM_VERIFY(
m_layout.size(densityValue) == f.densityFes->GetTrueVSize() &&
m_layout.size(displacementValue) == f.displacementFes->GetTrueVSize() &&
m_layout.size(displacementResidual) == f.displacementFes->GetTrueVSize(),
"Prepared rotational-displacement-force MFEM adapter received "
"incompatible coupled block sizes."
);
}
void PreparedRotationalDisplacementForceJacobianOperator::Mult(
const mfem::Vector &direction,
mfem::Vector &action
) const {
MFEM_VERIFY(
m_preparedOperator.IsPrepared(), "Prepared rotational-displacement-force MFEM adapter requires "
"a prepared operator."
);
MFEM_VERIFY(
direction.Size() == Width(), "Prepared rotational-displacement-force MFEM adapter received "
"a direction with the wrong size."
);
using Form = utils::blocks::barotropic_equilibrium_form;
constexpr auto densityValue = utils::blocks::get_value_block<Form>(utils::blocks::density_field.mass_term);
constexpr auto displacementValue =
utils::blocks::get_value_block<Form>(utils::blocks::displacement_field.geometry_term);
constexpr auto displacementResidual =
utils::blocks::get_residual_block<Form>(utils::blocks::displacement_field.geometry_term);
const mfem::Vector densityVariation(
const_cast<mfem::real_t *>(direction.GetData()) + m_layout.offset(densityValue), m_layout.size(densityValue)
);
const mfem::Vector displacementVariation(
const_cast<mfem::real_t *>(direction.GetData()) + m_layout.offset(displacementValue),
m_layout.size(displacementValue)
);
mfem::Vector displacementAction;
m_preparedOperator.ApplyCompleteJacobianAction(densityVariation, displacementVariation, displacementAction);
MFEM_VERIFY(
displacementAction.Size() == m_layout.size(displacementResidual),
"Prepared rotational-displacement-force MFEM adapter produced "
"a displacement action with the wrong size."
);
action.SetSize(Height());
action = 0.0;
const int residualOffset = m_layout.offset(displacementResidual);
for (int entry = 0; entry < displacementAction.Size(); ++entry) {
action(residualOffset + entry) = displacementAction(entry);
}
}
const RotationalDisplacementForceLayout &
PreparedRotationalDisplacementForceJacobianOperator::GetLayout() const noexcept {
return m_layout;
}
} // namespace mean_field::operators

View File

@@ -0,0 +1,809 @@
module;
#include <array>
#include <cmath>
#include <utility>
#include <mfem.hpp>
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) {
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(
const mean_field::field::FieldDofMap &densityMap,
const mean_field::field::FieldDofMap &displacementMap,
const mean_field::field::FieldDofMap &gravityFluxMap,
const mean_field::field::FieldDofMap &gravityPotentialMap,
const mean_field::field::FieldDofMap &enthalpyMap
) {
using Form = mean_field::utils::blocks::barotropic_equilibrium_form;
const std::array<int, Form::value_block_count> valueSizes{
densityMap.reduced_size(), displacementMap.reduced_size(), gravityFluxMap.reduced_size(),
gravityPotentialMap.reduced_size(), enthalpyMap.reduced_size(), 1
};
const std::array<int, Form::residual_block_count> residualSizes{
gravityFluxMap.reduced_size(), gravityPotentialMap.reduced_size(), densityMap.reduced_size(),
displacementMap.reduced_size(), enthalpyMap.reduced_size(), 1
};
return {valueSizes, residualSizes};
}
[[nodiscard]] mfem::Array<int> make_gravity_state_offsets(const mean_field::fem::FEM &f) {
mfem::Array<int> offsets(5);
offsets[0] = 0;
offsets[1] = offsets[0] + f.densityFes->GetTrueVSize();
offsets[2] = offsets[1] + f.displacementFes->GetTrueVSize();
offsets[3] = offsets[2] + f.gravityFluxFes->GetTrueVSize();
offsets[4] = offsets[3] + f.gravityPotentialFes->GetTrueVSize();
return offsets;
}
[[nodiscard]] mfem::Array<int> make_gravity_residual_offsets(const mean_field::fem::FEM &f) {
mfem::Array<int> offsets(3);
offsets[0] = 0;
offsets[1] = f.gravityFluxFes->GetTrueVSize();
offsets[2] = offsets[1] + f.gravityPotentialFes->GetTrueVSize();
return offsets;
}
template <int index>
[[nodiscard]] mfem::Vector make_value_view(
const mfem::Vector &vector,
const mean_field::operators::StellarEquilibriumLayout &layout,
const mean_field::utils::blocks::value_block<index> block
) {
MFEM_VERIFY(
vector.Size() == layout.value_offsets().Last(),
"The coupled vector does not match the stellar-equilibrium value layout."
);
return mfem::Vector(const_cast<mfem::real_t *>(vector.GetData()) + layout.offset(block), layout.size(block));
}
template <int index>
[[nodiscard]] mfem::Vector make_residual_view(
mfem::Vector &vector,
const mean_field::operators::StellarEquilibriumLayout &layout,
const mean_field::utils::blocks::residual_block<index> block
) {
MFEM_VERIFY(
vector.Size() == layout.residual_offsets().Last(),
"The coupled vector does not match the stellar-equilibrium residual layout."
);
return mfem::Vector(vector.GetData() + layout.offset(block), layout.size(block));
}
template <int index>
void assign_residual_block(
mfem::Vector &coupledResidual,
const mean_field::operators::StellarEquilibriumLayout &layout,
const mean_field::utils::blocks::residual_block<index> block,
const mfem::Vector &blockResidual,
const char *message
) {
MFEM_VERIFY(layout.size(block) == blockResidual.Size(), message);
mfem::Vector destination = make_residual_view(coupledResidual, layout, block);
destination = blockResidual;
}
void assign_gravity_block(
mfem::Vector &gravityState,
const mfem::Array<int> &offsets,
const int blockIndex,
const mfem::Vector &source,
const char *message
) {
MFEM_VERIFY(offsets.Size() == 5, "Gravity state offsets are invalid.");
MFEM_VERIFY(blockIndex >= 0 && blockIndex + 1 < offsets.Size(), "Requested gravity-state block is invalid.");
const int blockSize = offsets[blockIndex + 1] - offsets[blockIndex];
MFEM_VERIFY(blockSize == source.Size(), message);
MFEM_VERIFY(gravityState.Size() == offsets.Last(), "Packed gravity state has the wrong size.");
mfem::Vector destination(gravityState.GetData() + offsets[blockIndex], blockSize);
destination = source;
}
void pack_gravity_vector(
mfem::Vector &gravityState,
const mfem::Array<int> &offsets,
const mfem::Vector &density,
const mfem::Vector &displacement,
const mfem::Vector &gravityGradient,
const mfem::Vector &gravityPotential
) {
MFEM_VERIFY(offsets.Size() == 5, "Packed gravity state requires four blocks.");
if (gravityState.Size() != offsets.Last()) {
gravityState.SetSize(offsets.Last());
}
assign_gravity_block(
gravityState, offsets, 0, density, "The full density vector has the wrong gravity-state size."
);
assign_gravity_block(
gravityState, offsets, 1, displacement, "The displacement vector has the wrong gravity-state size."
);
assign_gravity_block(
gravityState, offsets, 2, gravityGradient, "The gravity-gradient vector has the wrong gravity-state size."
);
assign_gravity_block(
gravityState, offsets, 3, gravityPotential, "The gravity-potential vector has the wrong gravity-state size."
);
}
void validate_finite_vector(
const mfem::Vector &vector,
const char *message
) {
for (int index = 0; index < vector.Size(); ++index) {
MFEM_VERIFY(std::isfinite(vector(index)), message);
}
}
void validate_dependency_transition(
const mean_field::operators::StellarEquilibriumDependencyStamp &prepared,
const mean_field::operators::StellarEquilibriumDependencyStamp &requested,
const char *message
) {
MFEM_VERIFY(prepared.identity != requested.identity || requested.revision >= prepared.revision, message);
MFEM_VERIFY(
prepared.identity == requested.identity || prepared.revision != requested.revision,
"A new stellar-equilibrium dependency identity must also carry a visibly different revision."
);
}
[[nodiscard]] mean_field::operators::context::gravity_field::GravityFieldRevisions
make_gravity_revisions(const mean_field::operators::StellarEquilibriumDependencies &dependencies) {
return {
.discretization = {.value = dependencies.discretization.revision},
.displacement = {.value = dependencies.displacement.revision},
.density = {.value = dependencies.density.revision},
.gravity_gradient = {.value = dependencies.gravityGradient.revision},
.gravity_potential = {.value = dependencies.gravityPotential.revision}
};
}
[[nodiscard]] mean_field::operators::context::barotropic::BarotropicClosureDependencies
make_barotropic_closure_dependencies(const mean_field::operators::StellarEquilibriumDependencies &dependencies) {
return {
.discretization =
{.identity = dependencies.discretization.identity, .revision = dependencies.discretization.revision},
.density = {.identity = dependencies.density.identity, .revision = dependencies.density.revision},
.enthalpy = {.identity = dependencies.enthalpy.identity, .revision = dependencies.enthalpy.revision},
.displacement = {
.identity = dependencies.displacement.identity, .revision = dependencies.displacement.revision
}
};
}
[[nodiscard]] mean_field::operators::DisplacementResidualDependencies
make_displacement_dependencies(const mean_field::operators::StellarEquilibriumDependencies &dependencies) {
return {
.discretization =
{.identity = dependencies.discretization.identity, .revision = dependencies.discretization.revision},
.density = {.identity = dependencies.density.identity, .revision = dependencies.density.revision},
.displacement =
{.identity = dependencies.displacement.identity, .revision = dependencies.displacement.revision},
.gravityGradient =
{.identity = dependencies.gravityGradient.identity, .revision = dependencies.gravityGradient.revision},
.enthalpy = {.identity = dependencies.enthalpy.identity, .revision = dependencies.enthalpy.revision},
.rotation = {.identity = dependencies.rotation.identity, .revision = dependencies.rotation.revision}
};
}
[[nodiscard]] mean_field::operators::context::hydrostatic::HydrostaticEquilibriumDependencies
make_hydrostatic_dependencies(const mean_field::operators::StellarEquilibriumDependencies &dependencies) {
return {
.discretization =
{.identity = dependencies.discretization.identity, .revision = dependencies.discretization.revision},
.enthalpy = {.identity = dependencies.enthalpy.identity, .revision = dependencies.enthalpy.revision},
.gravityPotential =
{.identity = dependencies.gravityPotential.identity,
.revision = dependencies.gravityPotential.revision},
.displacement =
{.identity = dependencies.displacement.identity, .revision = dependencies.displacement.revision},
.rotation = {.identity = dependencies.rotation.identity, .revision = dependencies.rotation.revision},
.bernoulliConstant = {
.identity = dependencies.bernoulliConstant.identity, .revision = dependencies.bernoulliConstant.revision
}
};
}
[[nodiscard]] mean_field::operators::MassNormalizationDependencies
make_mass_dependencies(const mean_field::operators::StellarEquilibriumDependencies &dependencies) {
return {
.discretization =
{.identity = dependencies.discretization.identity, .revision = dependencies.discretization.revision},
.density = {.identity = dependencies.density.identity, .revision = dependencies.density.revision},
.displacement =
{.identity = dependencies.displacement.identity, .revision = dependencies.displacement.revision},
.targetMass = {.identity = dependencies.targetMass.identity, .revision = dependencies.targetMass.revision}
};
}
} // namespace
namespace mean_field::operators {
struct PreparedStellarEquilibriumOperator::ConstructionData {
field::FieldDofMap densityMap;
field::FieldDofMap displacementMap;
field::FieldDofMap gravityFluxMap;
field::FieldDofMap gravityPotentialMap;
field::FieldDofMap enthalpyMap;
StellarEquilibriumLayout layout;
mfem::Array<int> gravityStateOffsets;
mfem::Array<int> gravityResidualOffsets;
explicit ConstructionData(fem::FEM &f)
: densityMap(
field::make_field_dof_map<
field::Density,
DomainSchema>(*f.densityFes)
),
displacementMap(
field::make_field_dof_map<
field::Displacement,
DomainSchema>(*f.displacementFes)
),
gravityFluxMap(
field::make_field_dof_map<
field::Gravity,
DomainSchema>(*f.gravityFluxFes)
),
gravityPotentialMap(
field::make_field_dof_map<
field::Gravity,
DomainSchema>(*f.gravityPotentialFes)
),
enthalpyMap(
field::make_field_dof_map<
field::Enthalpy,
DomainSchema>(*f.enthalpyFes)
),
layout(make_layout(
densityMap,
displacementMap,
gravityFluxMap,
gravityPotentialMap,
enthalpyMap
)),
gravityStateOffsets(make_gravity_state_offsets(f)),
gravityResidualOffsets(make_gravity_residual_offsets(f)) {
}
};
PreparedStellarEquilibriumOperator::ConstructionData
PreparedStellarEquilibriumOperator::MakeConstructionData(fem::FEM &f) {
ensure_gravity_static_operators(f);
return ConstructionData(f);
}
PreparedStellarEquilibriumOperator::PreparedStellarEquilibriumOperator(
fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const eos::Polytrope &equationOfState,
const models::StellarModel &stellarModel
)
: PreparedStellarEquilibriumOperator(
f,
domainMapper,
equationOfState,
stellarModel.targetMass()
) {
}
PreparedStellarEquilibriumOperator::PreparedStellarEquilibriumOperator(
fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const eos::Polytrope &equationOfState,
const double targetMass
)
: PreparedStellarEquilibriumOperator(
f,
domainMapper,
equationOfState,
targetMass,
MakeConstructionData(f)
) {
}
PreparedStellarEquilibriumOperator::PreparedStellarEquilibriumOperator(
fem::FEM &f,
const mapping::DomainMapperStateless &domainMapper,
const eos::Polytrope &equationOfState,
const double targetMass,
ConstructionData constructionData
)
: mfem::Operator(
constructionData.layout.residual_offsets().Last(),
constructionData.layout.value_offsets().Last()
),
m_layout(constructionData.layout),
m_gravityStateOffsets(constructionData.gravityStateOffsets),
m_gravityContext(
f,
domainMapper
),
m_gravityJacobianOperator(
f,
domainMapper,
m_gravityContext,
m_gravityStateOffsets,
constructionData.gravityResidualOffsets
),
m_gravityOperator(
f,
domainMapper,
m_gravityContext,
m_gravityStateOffsets,
m_gravityJacobianOperator
),
m_barotropicClosureOperator(
f,
domainMapper,
equationOfState
),
m_hydrostaticOperator(
f,
domainMapper
),
m_displacementOperator(
f,
domainMapper,
equationOfState,
m_gravityContext
),
m_massNormalizationOperator(
f,
domainMapper,
m_gravityContext
),
m_targetMass(targetMass),
m_densityMap(std::move(constructionData.densityMap)),
m_displacementMap(std::move(constructionData.displacementMap)),
m_gravityFluxMap(std::move(constructionData.gravityFluxMap)),
m_gravityPotentialMap(std::move(constructionData.gravityPotentialMap)),
m_enthalpyMap(std::move(constructionData.enthalpyMap)) {
MFEM_VERIFY(
std::isfinite(m_targetMass) && m_targetMass > 0.0,
"PreparedStellarEquilibriumOperator requires a finite, positive target mass."
);
MFEM_VERIFY(
Width() == m_layout.value_offsets().Last() && Height() == m_layout.residual_offsets().Last(),
"PreparedStellarEquilibriumOperator has inconsistent block dimensions."
);
MFEM_VERIFY(
m_displacementMap.is_identity(), "PreparedStellarEquilibriumOperator currently requires Displacement "
"support to span the full MFEM true-DOF space."
);
MFEM_VERIFY(
m_gravityFluxMap.is_identity(), "PreparedStellarEquilibriumOperator currently requires gravity-flux "
"support to span the full MFEM true-DOF space."
);
MFEM_VERIFY(
m_gravityPotentialMap.is_identity(), "PreparedStellarEquilibriumOperator currently requires "
"gravity-potential support to span the full MFEM true-DOF space."
);
m_fullDensity.SetSize(m_densityMap.full_size());
m_fullEnthalpy.SetSize(m_enthalpyMap.full_size());
m_fullGravityState.SetSize(m_gravityStateOffsets.Last());
m_fullDensityVariation.SetSize(m_densityMap.full_size());
m_fullEnthalpyVariation.SetSize(m_enthalpyMap.full_size());
m_fullGravityDirection.SetSize(m_gravityStateOffsets.Last());
m_fullEnthalpyAction.SetSize(m_enthalpyMap.full_size());
m_fullDensity = 0.0;
m_fullEnthalpy = 0.0;
m_fullGravityState = 0.0;
m_fullDensityVariation = 0.0;
m_fullEnthalpyVariation = 0.0;
m_fullGravityDirection = 0.0;
m_fullEnthalpyAction = 0.0;
}
PreparedStellarEquilibriumReport PreparedStellarEquilibriumOperator::Prepare(
const mfem::Vector &state,
const StellarEquilibriumDependencies &dependencies,
const physics::RigidRotation &rotation
) {
MFEM_VERIFY(
state.Size() == Width(), "PreparedStellarEquilibriumOperator received a state with the wrong size."
);
validate_finite_vector(state, "PreparedStellarEquilibriumOperator received a non-finite state.");
const bool wasPrepared = m_isPrepared;
if (wasPrepared) {
validate_dependency_transition(
m_preparedDependencies.discretization, dependencies.discretization,
"The discretization revision cannot move backwards."
);
validate_dependency_transition(
m_preparedDependencies.density, dependencies.density, "The density revision cannot move backwards."
);
validate_dependency_transition(
m_preparedDependencies.displacement, dependencies.displacement,
"The displacement revision cannot move backwards."
);
validate_dependency_transition(
m_preparedDependencies.gravityGradient, dependencies.gravityGradient,
"The gravity-gradient revision cannot move backwards."
);
validate_dependency_transition(
m_preparedDependencies.gravityPotential, dependencies.gravityPotential,
"The gravity-potential revision cannot move backwards."
);
validate_dependency_transition(
m_preparedDependencies.enthalpy, dependencies.enthalpy, "The enthalpy revision cannot move backwards."
);
validate_dependency_transition(
m_preparedDependencies.bernoulliConstant, dependencies.bernoulliConstant,
"The Bernoulli-constant revision cannot move backwards."
);
validate_dependency_transition(
m_preparedDependencies.rotation, dependencies.rotation, "The rotation revision cannot move backwards."
);
validate_dependency_transition(
m_preparedDependencies.targetMass, dependencies.targetMass,
"The target-mass revision cannot move backwards."
);
}
m_isPrepared = false;
using Form = utils::blocks::barotropic_equilibrium_form;
constexpr auto densityValue = utils::blocks::get_value_block<Form>(utils::blocks::density_field.mass_term);
constexpr auto displacementValue =
utils::blocks::get_value_block<Form>(utils::blocks::displacement_field.geometry_term);
constexpr auto gravityGradientValue =
utils::blocks::get_value_block<Form>(utils::blocks::gravity_field.gradient_term);
constexpr auto gravityPotentialValue =
utils::blocks::get_value_block<Form>(utils::blocks::gravity_field.poisson_term);
constexpr auto enthalpyValue =
utils::blocks::get_value_block<Form>(utils::blocks::enthalpy_field.specific_term);
constexpr auto bernoulliValue =
utils::blocks::get_value_block<Form>(utils::blocks::barotropic_constant_field.mass_normalization_term);
const mfem::Vector reducedDensity = make_value_view(state, m_layout, densityValue);
const mfem::Vector displacement = make_value_view(state, m_layout, displacementValue);
const mfem::Vector gravityGradient = make_value_view(state, m_layout, gravityGradientValue);
const mfem::Vector gravityPotential = make_value_view(state, m_layout, gravityPotentialValue);
const mfem::Vector reducedEnthalpy = make_value_view(state, m_layout, enthalpyValue);
const mfem::Vector bernoulli = make_value_view(state, m_layout, bernoulliValue);
m_densityMap.scatter(reducedDensity, m_fullDensity);
m_enthalpyMap.scatter(reducedEnthalpy, m_fullEnthalpy);
pack_gravity_vector(
m_fullGravityState, m_gravityStateOffsets, m_fullDensity, displacement, gravityGradient, gravityPotential
);
PreparedStellarEquilibriumReport report;
report.gravity = m_gravityOperator.Prepare(m_fullGravityState, make_gravity_revisions(dependencies));
report.barotropicClosure = m_barotropicClosureOperator.Prepare(
{.density = reducedDensity, .enthalpy = reducedEnthalpy, .displacement = displacement},
make_barotropic_closure_dependencies(dependencies)
);
report.hydrostatic = m_hydrostaticOperator.Prepare(
{.enthalpy = m_fullEnthalpy,
.gravityPotential = gravityPotential,
.displacement = displacement,
.bernoulliConstant = bernoulli(0)},
make_hydrostatic_dependencies(dependencies), rotation
);
report.displacement = m_displacementOperator.Prepare(
{.enthalpy = reducedEnthalpy}, make_displacement_dependencies(dependencies), rotation
);
report.massNormalization =
m_massNormalizationOperator.Prepare({.targetMass = m_targetMass}, make_mass_dependencies(dependencies));
const bool dependenciesChanged = !wasPrepared || dependencies != m_preparedDependencies;
if (dependenciesChanged || report.DidAnyChildWork()) {
AssembleResidual();
report.assembledResidual = true;
}
m_preparedDependencies = dependencies;
m_isPrepared = true;
return report;
}
void PreparedStellarEquilibriumOperator::AssembleResidual() {
using Form = utils::blocks::barotropic_equilibrium_form;
constexpr auto gravityGradientResidual =
utils::blocks::get_residual_block<Form>(utils::blocks::gravity_field.gradient_term);
constexpr auto gravityPotentialResidual =
utils::blocks::get_residual_block<Form>(utils::blocks::gravity_field.poisson_term);
constexpr auto densityResidual =
utils::blocks::get_residual_block<Form>(utils::blocks::density_field.mass_term);
constexpr auto displacementResidual =
utils::blocks::get_residual_block<Form>(utils::blocks::displacement_field.geometry_term);
constexpr auto enthalpyResidual =
utils::blocks::get_residual_block<Form>(utils::blocks::enthalpy_field.specific_term);
constexpr auto massResidual =
utils::blocks::get_residual_block<Form>(utils::blocks::barotropic_constant_field.mass_normalization_term);
mfem::Vector gravity;
mfem::Vector closure;
mfem::Vector displacement;
mfem::Vector mass;
m_gravityOperator.Mult(m_fullGravityState, gravity);
m_barotropicClosureOperator.BuildResidual(closure);
m_displacementOperator.BuildResidual(displacement);
m_hydrostaticOperator.BuildResidual(m_fullEnthalpyAction);
m_massNormalizationOperator.BuildResidual(mass);
m_cachedResidual.SetSize(Height());
m_cachedResidual = 0.0;
MFEM_VERIFY(
gravity.Size() == m_layout.size(gravityGradientResidual) + m_layout.size(gravityPotentialResidual),
"The gravity residual has the wrong size."
);
mfem::Vector gravityGradient(gravity.GetData(), m_layout.size(gravityGradientResidual));
mfem::Vector gravityPotential(
gravity.GetData() + m_layout.size(gravityGradientResidual), m_layout.size(gravityPotentialResidual)
);
assign_residual_block(
m_cachedResidual, m_layout, gravityGradientResidual, gravityGradient,
"The gravity-gradient residual has the wrong size."
);
assign_residual_block(
m_cachedResidual, m_layout, gravityPotentialResidual, gravityPotential,
"The gravity-potential residual has the wrong size."
);
assign_residual_block(
m_cachedResidual, m_layout, densityResidual, closure, "The closure residual has the wrong size."
);
assign_residual_block(
m_cachedResidual, m_layout, displacementResidual, displacement,
"The displacement residual has the wrong size."
);
{
mfem::Vector reducedEnthalpyResidual = make_residual_view(m_cachedResidual, m_layout, enthalpyResidual);
m_enthalpyMap.gather(m_fullEnthalpyAction, reducedEnthalpyResidual);
}
assign_residual_block(
m_cachedResidual, m_layout, massResidual, mass, "The mass-normalization residual has the wrong size."
);
++m_statistics.residualAssemblies;
}
void PreparedStellarEquilibriumOperator::BuildResidual(mfem::Vector &residual) const {
VerifyPrepared();
residual = m_cachedResidual;
++m_statistics.residualApplications;
}
void PreparedStellarEquilibriumOperator::Mult(
const mfem::Vector &direction,
mfem::Vector &action
) const {
VerifyPrepared();
MFEM_VERIFY(
direction.Size() == Width(),
"PreparedStellarEquilibriumOperator received a Jacobian direction with the wrong size."
);
validate_finite_vector(
direction, "PreparedStellarEquilibriumOperator received a non-finite Jacobian direction."
);
using Form = utils::blocks::barotropic_equilibrium_form;
constexpr auto densityValue = utils::blocks::get_value_block<Form>(utils::blocks::density_field.mass_term);
constexpr auto displacementValue =
utils::blocks::get_value_block<Form>(utils::blocks::displacement_field.geometry_term);
constexpr auto gravityGradientValue =
utils::blocks::get_value_block<Form>(utils::blocks::gravity_field.gradient_term);
constexpr auto gravityPotentialValue =
utils::blocks::get_value_block<Form>(utils::blocks::gravity_field.poisson_term);
constexpr auto enthalpyValue =
utils::blocks::get_value_block<Form>(utils::blocks::enthalpy_field.specific_term);
constexpr auto bernoulliValue =
utils::blocks::get_value_block<Form>(utils::blocks::barotropic_constant_field.mass_normalization_term);
constexpr auto gravityGradientResidual =
utils::blocks::get_residual_block<Form>(utils::blocks::gravity_field.gradient_term);
constexpr auto gravityPotentialResidual =
utils::blocks::get_residual_block<Form>(utils::blocks::gravity_field.poisson_term);
constexpr auto densityResidual =
utils::blocks::get_residual_block<Form>(utils::blocks::density_field.mass_term);
constexpr auto displacementResidual =
utils::blocks::get_residual_block<Form>(utils::blocks::displacement_field.geometry_term);
constexpr auto enthalpyResidual =
utils::blocks::get_residual_block<Form>(utils::blocks::enthalpy_field.specific_term);
constexpr auto massResidual =
utils::blocks::get_residual_block<Form>(utils::blocks::barotropic_constant_field.mass_normalization_term);
const mfem::Vector reducedDensityDirection = make_value_view(direction, m_layout, densityValue);
const mfem::Vector displacementDirection = make_value_view(direction, m_layout, displacementValue);
const mfem::Vector gravityGradientDirection = make_value_view(direction, m_layout, gravityGradientValue);
const mfem::Vector gravityPotentialDirection = make_value_view(direction, m_layout, gravityPotentialValue);
const mfem::Vector reducedEnthalpyDirection = make_value_view(direction, m_layout, enthalpyValue);
const mfem::Vector bernoulliDirection = make_value_view(direction, m_layout, bernoulliValue);
m_densityMap.scatter(reducedDensityDirection, m_fullDensityVariation);
m_enthalpyMap.scatter(reducedEnthalpyDirection, m_fullEnthalpyVariation);
pack_gravity_vector(
m_fullGravityDirection, m_gravityStateOffsets, m_fullDensityVariation, displacementDirection,
gravityGradientDirection, gravityPotentialDirection
);
mfem::Vector gravityAction;
mfem::Vector closureAction;
mfem::Vector displacementAction;
mfem::Vector massAction;
m_gravityJacobianOperator.Mult(m_fullGravityDirection, gravityAction);
m_barotropicClosureOperator.Mult(
reducedDensityDirection, reducedEnthalpyDirection, displacementDirection, closureAction
);
m_displacementOperator.ApplyCompleteJacobianAction(
m_fullDensityVariation, displacementDirection, gravityGradientDirection, reducedEnthalpyDirection,
displacementAction
);
m_hydrostaticOperator.ApplyCompleteJacobianAction(
m_fullEnthalpyVariation, gravityPotentialDirection, bernoulliDirection(0), displacementDirection,
m_fullEnthalpyAction
);
m_massNormalizationOperator.ApplyCompleteJacobianAction(
m_fullDensityVariation, displacementDirection, massAction
);
action.SetSize(Height());
action = 0.0;
MFEM_VERIFY(
gravityAction.Size() == m_layout.size(gravityGradientResidual) + m_layout.size(gravityPotentialResidual),
"The gravity Jacobian action has the wrong size."
);
mfem::Vector gravityGradientAction(gravityAction.GetData(), m_layout.size(gravityGradientResidual));
mfem::Vector gravityPotentialAction(
gravityAction.GetData() + m_layout.size(gravityGradientResidual), m_layout.size(gravityPotentialResidual)
);
assign_residual_block(
action, m_layout, gravityGradientResidual, gravityGradientAction,
"The gravity-gradient Jacobian action has the wrong size."
);
assign_residual_block(
action, m_layout, gravityPotentialResidual, gravityPotentialAction,
"The gravity-potential Jacobian action has the wrong size."
);
assign_residual_block(
action, m_layout, densityResidual, closureAction, "The closure Jacobian action has the wrong size."
);
assign_residual_block(
action, m_layout, displacementResidual, displacementAction,
"The displacement Jacobian action has the wrong size."
);
{
mfem::Vector reducedEnthalpyAction = make_residual_view(action, m_layout, enthalpyResidual);
m_enthalpyMap.gather(m_fullEnthalpyAction, reducedEnthalpyAction);
}
assign_residual_block(
action, m_layout, massResidual, massAction, "The mass-normalization Jacobian action has the wrong size."
);
++m_statistics.jacobianApplications;
}
bool PreparedStellarEquilibriumOperator::IsPrepared() const noexcept {
return m_isPrepared && m_gravityContext.IsPrepared() && m_barotropicClosureOperator.IsPrepared() &&
m_hydrostaticOperator.IsPrepared() && m_displacementOperator.IsPrepared() &&
m_massNormalizationOperator.IsPrepared();
}
double PreparedStellarEquilibriumOperator::GetTargetMass() const noexcept {
return m_targetMass;
}
const StellarEquilibriumLayout &PreparedStellarEquilibriumOperator::GetLayout() const noexcept {
return m_layout;
}
const StellarEquilibriumDependencies &PreparedStellarEquilibriumOperator::GetDependencies() const {
VerifyPrepared();
return m_preparedDependencies;
}
const PreparedStellarEquilibriumStatistics &PreparedStellarEquilibriumOperator::GetStatistics() const noexcept {
return m_statistics;
}
const context::gravity_field::GravityFieldLinearizationContext &
PreparedStellarEquilibriumOperator::GetGravityContext() const noexcept {
return m_gravityContext;
}
const GravityFieldOperator &PreparedStellarEquilibriumOperator::GetGravityOperator() const noexcept {
return m_gravityOperator;
}
const GravityFieldJacobianOperator &
PreparedStellarEquilibriumOperator::GetGravityJacobianOperator() const noexcept {
return m_gravityJacobianOperator;
}
const PreparedBarotropicClosureOperator &
PreparedStellarEquilibriumOperator::GetBarotropicClosureOperator() const noexcept {
return m_barotropicClosureOperator;
}
const context::barotropic::BarotropicClosureLinearizationContext &
PreparedStellarEquilibriumOperator::GetBarotropicClosureContext() const noexcept {
return m_barotropicClosureOperator.GetContext();
}
const PreparedHydrostaticEquilibriumOperator &
PreparedStellarEquilibriumOperator::GetHydrostaticOperator() const noexcept {
return m_hydrostaticOperator;
}
const PreparedDisplacementResidualOperator &
PreparedStellarEquilibriumOperator::GetDisplacementOperator() const noexcept {
return m_displacementOperator;
}
const PreparedMassNormalizationOperator &
PreparedStellarEquilibriumOperator::GetMassNormalizationOperator() const noexcept {
return m_massNormalizationOperator;
}
void PreparedStellarEquilibriumOperator::VerifyPrepared() const {
MFEM_VERIFY(
IsPrepared(), "PreparedStellarEquilibriumOperator must be prepared before residual or Jacobian application."
);
}
} // namespace mean_field::operators

View File

@@ -33,17 +33,15 @@ namespace {
true_dofs.SetSize(finite_element_space.GetTrueVSize());
const mfem::Operator *restriction =
finite_element_space.GetRestrictionMatrix();
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."
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;
@@ -62,10 +60,7 @@ namespace mean_field::physics {
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_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;
@@ -86,23 +81,16 @@ namespace mean_field::physics {
return l2_multipole_potential(f, utils::MASS, x_physical);
};
boundary_potential_coeff =
std::make_unique<mfem::FunctionCoefficient>(boundary_potential);
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();
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,
*boundary_integrator, quadrature::QuadratureRole::discretization, boundary_element,
utils::DOMAINS::VACUUM, quadrature::MappingKind::none
);
g_rhs.AddBoundaryIntegrator(
boundary_integrator.release(), outer_bdr_marker
);
g_rhs.AddBoundaryIntegrator(boundary_integrator.release(), outer_bdr_marker);
}
g_rhs.Assemble();
@@ -112,36 +100,25 @@ namespace mean_field::physics {
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;
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
);
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();
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
*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);
@@ -177,13 +154,9 @@ namespace mean_field::physics {
std::unique_ptr<mfem::Coefficient> centrifugal_coeff;
if (fem.has_mapping()) {
centrifugal_coeff = std::make_unique<
mapping::PhysicalPositionFunctionCoefficient>(
*fem.mapping, rot
);
centrifugal_coeff = std::make_unique<mapping::PhysicalPositionFunctionCoefficient>(*fem.mapping, rot);
} else {
centrifugal_coeff =
std::make_unique<mfem::FunctionCoefficient>(rot);
centrifugal_coeff = std::make_unique<mfem::FunctionCoefficient>(rot);
}
mfem::GridFunction centrifugal_gf(fem.gravityPotentialFes.get());
@@ -207,19 +180,14 @@ namespace mean_field::physics {
if (fem.mesh->GetAttribute(i) == 3)
continue;
mfem::ElementTransformation *trans =
fem.mesh->GetElementTransformation(i);
using DensityField = field::Field<field::Density>;
const quadrature::Query query =
DensityField::make_query<field::Density::Form::Quadrupole>(
quadrature::QuadratureRole::diagnostic, trans->OrderW(),
std::array<int, 1>{2}, utils::DOMAINS::STELLAR,
fem.has_mapping() ? quadrature::MappingKind::general
: quadrature::MappingKind::none
);
mfem::ElementTransformation *trans = fem.mesh->GetElementTransformation(i);
using DensityField = field::Field<field::Density>;
const quadrature::Query query = DensityField::make_query<field::Density::Form::Quadrupole>(
quadrature::QuadratureRole::diagnostic, trans->OrderW(), std::array<int, 1>{2}, utils::DOMAINS::STELLAR,
fem.has_mapping() ? quadrature::MappingKind::general : quadrature::MappingKind::none
);
const mfem::IntegrationRule &ir =
*fem.quadratureFactory->get(query, trans->GetGeometryType())
.integration_rule;
*fem.quadratureFactory->get(query, trans->GetGeometryType()).integration_rule;
for (int j = 0; j < ir.GetNPoints(); ++j) {
const mfem::IntegrationPoint &ip = ir.IntPoint(j);
@@ -250,9 +218,8 @@ namespace mean_field::physics {
for (int m = 0; m < dim; ++m) {
for (int n = 0; n < dim; ++n) {
const double delta = (m == n) ? 1.0 : 0.0;
const double contrib =
3.0 * x_prime(m) * x_prime(n) - delta * r_sq;
const double delta = (m == n) ? 1.0 : 0.0;
const double contrib = 3.0 * x_prime(m) * x_prime(n) - delta * r_sq;
local_Q(m, n) += rho_val * contrib * weight;
}
}
@@ -260,10 +227,7 @@ namespace mean_field::physics {
}
mfem::DenseMatrix global_Q(dim, dim);
MPI_Allreduce(
local_Q.GetData(), global_Q.GetData(), dim * dim, MPI_DOUBLE,
MPI_SUM, fem.mesh->GetComm()
);
MPI_Allreduce(local_Q.GetData(), global_Q.GetData(), dim * dim, MPI_DOUBLE, MPI_SUM, fem.mesh->GetComm());
return global_Q;
}
@@ -289,8 +253,7 @@ namespace mean_field::physics {
}
}
const double l2_contrib =
-(utils::G / (2.0 * std::pow(r, 3))) * l2_mult_factor;
const double l2_contrib = -(utils::G / (2.0 * std::pow(r, 3))) * l2_mult_factor;
const double l0_contrib = -utils::G * total_mass / r;
@@ -304,92 +267,69 @@ namespace mean_field::physics {
// ==========================================
// 1. Partially Assemble the High-Order Mass Block
// ==========================================
f.gravityContext.m_form =
std::make_unique<mfem::ParBilinearForm>(f.gravityFluxFes.get());
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()
);
std::make_unique<mapping::MappedHDivMassCoefficient>(*f.mapping, f.mesh->Dimension());
hdiv_mass_integrator =
std::make_unique<mfem::VectorFEMassIntegrator>(
*f.gravityContext.mapped_hdiv_mass_coeff
);
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>();
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 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.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()
*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 =
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();
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()
*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."
f.domainMapperStateless != nullptr, "Gravity source partial assembly requires the stateless domain "
"mapper."
);
mfem::Vector displacement_true(f.displacementFes->GetTrueVSize());
displacement_true = 0.0;
displacement_true = 0.0;
const mfem::GridFunction *active_displacement =
f.mapping->GetDisplacement();
const mfem::GridFunction *active_displacement = f.mapping->GetDisplacement();
if (active_displacement != nullptr) {
grid_function_to_true_dofs(
*f.displacementFes, *active_displacement, displacement_true
);
grid_function_to_true_dofs(*f.displacementFes, *active_displacement, displacement_true);
}
auto source_form =
std::make_unique<operators::PreparedMappedGravitySourceOperator>(
f, *f.domainMapperStateless
);
std::make_unique<operators::PreparedMappedGravitySourceOperator>(f, *f.domainMapperStateless);
source_form->Prepare(displacement_true);
@@ -397,12 +337,9 @@ namespace mean_field::physics {
// ==========================================
// 3. Assemble Global Block Operator
// ==========================================
f.gravityContext.BT = std::make_unique<mfem::TransposeOperator>(
f.gravityContext.b_form.get()
);
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 = 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());
@@ -417,8 +354,7 @@ namespace mean_field::physics {
for (int i = 0; i < inverse_mass_diagonal.Size(); ++i) {
MFEM_VERIFY(
std::isfinite(inverse_mass_diagonal(i)) &&
inverse_mass_diagonal(i) > 0.0,
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."
@@ -426,58 +362,34 @@ namespace mean_field::physics {
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>();
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);
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()
*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()
);
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())
);
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_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()
);
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(
@@ -486,49 +398,32 @@ namespace mean_field::physics {
const mfem::GridFunction &rho,
const mfem::GridFunction &displacement
) {
MFEM_VERIFY(f.mesh != nullptr, "Gravity initialization requires a parallel mesh.");
MFEM_VERIFY(f.densityFes != nullptr, "Gravity initialization requires the density finite-element space.");
MFEM_VERIFY(
f.mesh != nullptr,
"Gravity initialization requires a parallel mesh."
f.gravityPotentialFes != nullptr, "Gravity initialization requires the gravity-potential "
"finite-element "
"space."
);
MFEM_VERIFY(
f.densityFes != nullptr,
"Gravity initialization requires the density finite-element space."
);
MFEM_VERIFY(
f.gravityPotentialFes != nullptr,
"Gravity initialization requires the gravity-potential "
"finite-element "
"space."
);
MFEM_VERIFY(
f.gravityFluxFes != nullptr,
"Gravity initialization requires the "
"gravity-gradient finite-element space."
f.gravityFluxFes != nullptr, "Gravity initialization requires the "
"gravity-gradient finite-element space."
);
MFEM_VERIFY(
f.displacementFes != nullptr, "Gravity initialization requires the "
"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.domainMapperStateless != nullptr,
"Gravity initialization requires the stateless domain mapper."
f.gravityContext.BT != nullptr, "Gravity initialization requires the transpose divergence operator."
);
MFEM_VERIFY(
f.gravityContext.b_form != nullptr,
"Gravity initialization requires the divergence operator."
f.gravityContext.block_prec != nullptr, "Gravity initialization requires the gravity block preconditioner."
);
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."
rho.FESpace() == f.densityFes.get(), "Gravity initialization requires density to use the FEM density "
"space."
);
MFEM_VERIFY(
displacement.FESpace() == f.displacementFes.get(),
@@ -540,53 +435,43 @@ namespace mean_field::physics {
using form = utils::blocks::gravity_field_form;
constexpr auto gravity_gradient_residual_block =
utils::blocks::get_residual_block<form>(
utils::blocks::gravity_field.gradient_term
);
utils::blocks::get_residual_block<form>(utils::blocks::gravity_field.gradient_term);
constexpr auto gravity_poisson_residual_block =
utils::blocks::get_residual_block<form>(
utils::blocks::gravity_field.poisson_term
);
utils::blocks::get_residual_block<form>(utils::blocks::gravity_field.poisson_term);
const std::array<int, form::value_block_count> value_sizes{
f.densityFes->GetTrueVSize(), f.displacementFes->GetTrueVSize(),
f.gravityFluxFes->GetTrueVSize(),
f.densityFes->GetTrueVSize(), f.displacementFes->GetTrueVSize(), f.gravityFluxFes->GetTrueVSize(),
f.gravityPotentialFes->GetTrueVSize()
};
const std::array<int, form::residual_block_count> residual_sizes{
f.gravityFluxFes->GetTrueVSize(),
f.gravityPotentialFes->GetTrueVSize()
f.gravityFluxFes->GetTrueVSize(), f.gravityPotentialFes->GetTrueVSize()
};
const utils::blocks::form_layout<form> layout(
value_sizes, residual_sizes
);
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
grid_function_to_true_dofs(*f.displacementFes, displacement, displacement_true);
operators::context::gravity_field::GravityFieldLinearizationContext linearization_context(
f, *f.domainMapperStateless
);
operators::context::gravity_field::GravityFieldLinearizationContext
linearization_context(f, *f.domainMapperStateless);
operators::GravityFieldJacobianOperator gravity_jacobian(
f, *f.domainMapperStateless, linearization_context,
layout.value_offsets(), layout.residual_offsets()
f, *f.domainMapperStateless, linearization_context, layout.value_offsets(), layout.residual_offsets()
);
operators::GravityFieldOperator gravity_operator(
f, *f.domainMapperStateless, linearization_context,
layout.value_offsets(), gravity_jacobian
f, *f.domainMapperStateless, linearization_context, layout.value_offsets(), gravity_jacobian
);
operators::context::gravity_field::GravityFieldGeometryContext
reduced_geometry_context(f, *f.domainMapperStateless);
operators::context::gravity_field::GravityFieldGeometryContext reduced_geometry_context(
f, *f.domainMapperStateless
);
operators::ReducedGravityFieldOperator reduced_operator(
gravity_operator, reduced_geometry_context, displacement_true
@@ -600,9 +485,7 @@ namespace mean_field::physics {
"The reduced gravity right-hand side has the wrong size."
);
mfem::BlockVector gravity_state(
reduced_operator.GetGravityTrueOffsets()
);
mfem::BlockVector gravity_state(reduced_operator.GetGravityTrueOffsets());
gravity_state = 0.0;
mfem::MINRESSolver minres(f.mesh->GetComm());
@@ -614,20 +497,13 @@ namespace mean_field::physics {
minres.SetPrintLevel(1);
minres.Mult(right_hand_side, gravity_state);
MFEM_VERIFY(
minres.GetConverged(),
"The reduced gravity solve failed to converge."
);
MFEM_VERIFY(minres.GetConverged(), "The reduced gravity solve failed to converge.");
GravitySolution solution(f);
solution.gradPhi.SetFromTrueDofs(
gravity_state.GetBlock(gravity_gradient_residual_block)
);
solution.gradPhi.SetFromTrueDofs(gravity_state.GetBlock(gravity_gradient_residual_block));
solution.phi.SetFromTrueDofs(
gravity_state.GetBlock(gravity_poisson_residual_block)
);
solution.phi.SetFromTrueDofs(gravity_state.GetBlock(gravity_poisson_residual_block));
return solution;
}

View File

@@ -15,18 +15,13 @@ namespace mean_field::physics {
if (fem.mesh->GetAttribute(i) == 3)
continue;
mfem::ElementTransformation *T =
fem.mesh->GetElementTransformation(i);
using DensityField = field::Field<field::Density>;
const quadrature::Query query =
DensityField::make_query<field::Density::Form::Quadrupole>(
quadrature::QuadratureRole::diagnostic, T->OrderW(),
std::array<int, 1>{2}, utils::DOMAINS::STELLAR,
quadrature::MappingKind::general
);
const mfem::IntegrationRule &ir =
*fem.quadratureFactory->get(query, T->GetGeometryType())
.integration_rule;
mfem::ElementTransformation *T = fem.mesh->GetElementTransformation(i);
using DensityField = field::Field<field::Density>;
const quadrature::Query query = DensityField::make_query<field::Density::Form::Quadrupole>(
quadrature::QuadratureRole::diagnostic, T->OrderW(), std::array<int, 1>{2}, utils::DOMAINS::STELLAR,
quadrature::MappingKind::general
);
const mfem::IntegrationRule &ir = *fem.quadratureFactory->get(query, T->GetGeometryType()).integration_rule;
for (int j = 0; j < ir.GetNPoints(); j++) {
const mfem::IntegrationPoint &ip = ir.IntPoint(j);
@@ -37,19 +32,16 @@ namespace mean_field::physics {
mfem::Vector x_phys;
fem.mapping->GetPhysicalPoint(*T, ip, x_phys);
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 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;
local_I += rho_hat * r_cyl_sq * weight;
}
}
double global_I = 0.0;
MPI_Allreduce(
&local_I, &global_I, 1, MPI_DOUBLE, MPI_SUM, fem.mesh->GetComm()
);
MPI_Allreduce(&local_I, &global_I, 1, MPI_DOUBLE, MPI_SUM, fem.mesh->GetComm());
return global_I;
}

View File

@@ -29,10 +29,8 @@ 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()) {
mfem::ElementTransformation *T0 =
fem.mesh->GetElementTransformation(origin_elem[0]);
if (origin_elem.Size() > 0 && origin_elem[0] >= 0 && fem.mapping->HasDisplacementField()) {
mfem::ElementTransformation *T0 = fem.mesh->GetElementTransformation(origin_elem[0]);
T0->SetIntPoint(&origin_ip[0]);
mfem::DenseMatrix J0(dim, dim), J0_inv(dim, dim);
@@ -98,8 +96,7 @@ namespace mean_field::utils {
int elemID = elem_ids[0];
const mfem::IntegrationPoint &ip = ips[0];
mfem::ElementTransformation *T =
fem.mesh->GetElementTransformation(elemID);
mfem::ElementTransformation *T = fem.mesh->GetElementTransformation(elemID);
T->SetIntPoint(&ip);
mfem::Vector current_x_phys(dim);
@@ -160,8 +157,7 @@ namespace mean_field::utils {
const mapping::COORDINATE_SPACE rspace
) {
mfem::Vector x_search;
if (vspace == mapping::COORDINATE_SPACE::PHYSICAL &&
fem.has_mapping()) {
if (vspace == mapping::COORDINATE_SPACE::PHYSICAL && fem.has_mapping()) {
GetReferencePoint(fem, x, x_search);
} else {
x_search = x;
@@ -177,8 +173,7 @@ namespace mean_field::utils {
double local_val = 0.0;
if (elem_ids.Size() > 0 && elem_ids[0] >= 0) {
const double val = u.GetValue(elem_ids[0], ips[0]);
if (rspace == mapping::COORDINATE_SPACE::PHYSICAL &&
!fem.has_mapping()) {
if (rspace == mapping::COORDINATE_SPACE::PHYSICAL && !fem.has_mapping()) {
MFEM_ABORT(
"Physical evaluation mode requested but no mapping "
"provided. Check "
@@ -189,9 +184,7 @@ namespace mean_field::utils {
}
double global_val = 0.0;
MPI_Allreduce(
&local_val, &global_val, 1, MPI_DOUBLE, MPI_MAX, fem.mesh->GetComm()
);
MPI_Allreduce(&local_val, &global_val, 1, MPI_DOUBLE, MPI_MAX, fem.mesh->GetComm());
return global_val;
}

View File

@@ -10,18 +10,14 @@ namespace mean_field::utils {
DOMAINS lhs,
DOMAINS rhs
) {
return static_cast<DOMAINS>(
static_cast<uint8_t>(lhs) | static_cast<uint8_t>(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)
);
return static_cast<DOMAINS>(static_cast<uint8_t>(lhs) & static_cast<uint8_t>(rhs));
}
void populate_element_mask(
@@ -37,8 +33,7 @@ namespace mean_field::utils {
mask[0] = 1;
}
if ((domain & DOMAINS::ENVELOPE) == DOMAINS::ENVELOPE &&
max_attr >= 2) {
if ((domain & DOMAINS::ENVELOPE) == DOMAINS::ENVELOPE && max_attr >= 2) {
mask[1] = 1;
}
@@ -92,10 +87,7 @@ namespace mean_field::utils {
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]
);
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);
}
@@ -111,15 +103,9 @@ namespace mean_field::utils {
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
);
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);