feat(FieldDofMap): Completed FieldDofMap migration

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

View File

@@ -44,7 +44,6 @@ target_sources(mean_field
libmeanfield/impl/analysis/integral.cpp libmeanfield/impl/analysis/integral.cpp
libmeanfield/impl/fem.cpp libmeanfield/impl/fem.cpp
libmeanfield/impl/mapping/coefficients.cpp libmeanfield/impl/mapping/coefficients.cpp
libmeanfield/impl/mapping/domain_mapper.cpp
libmeanfield/impl/mapping/compactification/kelvin.cpp libmeanfield/impl/mapping/compactification/kelvin.cpp
libmeanfield/impl/physics/gravity.cpp libmeanfield/impl/physics/gravity.cpp
libmeanfield/impl/physics/solid.cpp libmeanfield/impl/physics/solid.cpp
@@ -56,7 +55,7 @@ target_sources(mean_field
libmeanfield/impl/integrators/gravity.cpp libmeanfield/impl/integrators/gravity.cpp
libmeanfield/impl/integrators/mass_continuity.cpp libmeanfield/impl/integrators/mass_continuity.cpp
libmeanfield/impl/integrators/viscosity.cpp libmeanfield/impl/integrators/viscosity.cpp
libmeanfield/impl/mapping/domain_mapper_new.cpp libmeanfield/impl/mapping/domain_mapper.cpp
libmeanfield/impl/mapping/transformations.cpp libmeanfield/impl/mapping/transformations.cpp
libmeanfield/impl/operators/gravity_field.cpp libmeanfield/impl/operators/gravity_field.cpp
libmeanfield/impl/operators/gravity_field_jacobian.cpp libmeanfield/impl/operators/gravity_field_jacobian.cpp
@@ -98,7 +97,6 @@ target_sources(mean_field
libmeanfield/interface/mapping/compactification/compactification.cppm libmeanfield/interface/mapping/compactification/compactification.cppm
libmeanfield/interface/mapping/compactification/kelvin.cppm libmeanfield/interface/mapping/compactification/kelvin.cppm
libmeanfield/interface/mapping/compactification/options.cppm libmeanfield/interface/mapping/compactification/options.cppm
libmeanfield/interface/physics/context.cppm
libmeanfield/interface/physics/gravity.cppm libmeanfield/interface/physics/gravity.cppm
libmeanfield/interface/physics/solid.cppm libmeanfield/interface/physics/solid.cppm
libmeanfield/interface/utils/domain.cppm libmeanfield/interface/utils/domain.cppm
@@ -126,7 +124,6 @@ target_sources(mean_field
libmeanfield/interface/field/field_base.cppm libmeanfield/interface/field/field_base.cppm
libmeanfield/interface/field/field_registry.cppm libmeanfield/interface/field/field_registry.cppm
libmeanfield/interface/field/field_mfem.cppm libmeanfield/interface/field/field_mfem.cppm
libmeanfield/interface/physics/barotrope.cppm
libmeanfield/interface/operators/prepared_barotropic_closure_operator.cppm libmeanfield/interface/operators/prepared_barotropic_closure_operator.cppm
libmeanfield/interface/operators/contexts/barotropic_closure_linearization_context.cppm libmeanfield/interface/operators/contexts/barotropic_closure_linearization_context.cppm
libmeanfield/interface/physics/rigid_rotation.cppm libmeanfield/interface/physics/rigid_rotation.cppm

View File

@@ -10,7 +10,6 @@
#include <mfem.hpp> #include <mfem.hpp>
#include <mpi.h> #include <mpi.h>
import mean_field; import mean_field;
import test_helpers; import test_helpers;
import experiment; import experiment;
@@ -38,47 +37,66 @@ struct AccuracyBudgetMetrics {
static double global_norm(const mfem::Vector &vector, MPI_Comm communicator) { static double global_norm(const mfem::Vector &vector, MPI_Comm communicator) {
const double local_norm_squared = vector * vector; const double local_norm_squared = vector * vector;
double global_norm_squared = 0.0; double global_norm_squared = 0.0;
MPI_Allreduce(&local_norm_squared, &global_norm_squared, 1, MPI_DOUBLE, MPI_SUM, communicator); MPI_Allreduce(&local_norm_squared, &global_norm_squared, 1, MPI_DOUBLE,
MPI_SUM, communicator);
return std::sqrt(global_norm_squared); return std::sqrt(global_norm_squared);
} }
static double global_dot(const mfem::Vector& left, const mfem::Vector& right, MPI_Comm communicator) { static double global_dot(const mfem::Vector &left, const mfem::Vector &right,
MPI_Comm communicator) {
const double local_dot = left * right; const double local_dot = left * right;
double global_dot_product = 0.0; double global_dot_product = 0.0;
MPI_Allreduce(&local_dot, &global_dot_product, 1, MPI_DOUBLE, MPI_SUM, communicator); MPI_Allreduce(&local_dot, &global_dot_product, 1, MPI_DOUBLE, MPI_SUM,
communicator);
return global_dot_product; return global_dot_product;
} }
static void zero_vacuum_density(const mean_field::fem::FEM& fem, mfem::GridFunction& density) { static void zero_vacuum_density(const mean_field::fem::FEM &fem,
for (int index = 0; index < fem.vacuum_tdof_rho.Size(); ++index) { mfem::GridFunction &density) {
density(fem.vacuum_tdof_rho[index]) = 0.0; using DomainSchema =
} mean_field::utils::domain::CoreEnvelopeVacuumDomainSchema;
const mean_field::field::FieldDofMap density_map =
mean_field::field::make_field_dof_map<mean_field::field::Density,
DomainSchema>(*fem.densityFes);
mfem::Vector density_true;
density.GetTrueDofs(density_true);
const mfem::Vector supported_density = density_map.gather(density_true);
density_map.scatter(supported_density, density_true);
density.SetFromTrueDofs(density_true);
} }
static int diagnostic_quadrature_order(const mean_field::fem::FEM &fem) { static int diagnostic_quadrature_order(const mean_field::fem::FEM &fem) {
return 2 * std::max(fem.L2_fes->GetMaxElementOrder(), fem.RT_fes->GetMaxElementOrder()) + 8; return 2 * std::max(fem.gravityPotentialFes->GetMaxElementOrder(),
fem.gravityFluxFes->GetMaxElementOrder()) +
8;
} }
static mfem::Vector assemble_monopole_projection_rhs( static mfem::Vector assemble_monopole_projection_rhs(
mean_field::fem::FEM& fem, mean_field::fem::FEM &fem, const mfem::GridFunction &displacement,
const mfem::GridFunction& displacement, const double mass, const double stellar_radius) {
const double mass, *fem.displacement = displacement;
const double stellar_radius
) {
static_cast<void>(displacement);
mfem::Vector local_rhs(fem.RT_fes->GetVSize()); mfem::Vector local_rhs(fem.gravityFluxFes->GetVSize());
local_rhs = 0.0; local_rhs = 0.0;
const int vacuum_attribute = fem.domain_mapper_stateless->GetVacuumElementAttribute(); const int vacuum_attribute = field_dof_test_utils::vacuum_material_attribute;
const int quadrature_order = diagnostic_quadrature_order(fem); const int quadrature_order = diagnostic_quadrature_order(fem);
mean_field::mapping::GridFunctionMappingEvaluator mapping_evaluator(
*fem.domainMapperStateless, *fem.displacement,
*fem.compactificationCoordinate);
for (int element_id = 0; element_id < fem.mesh->GetNE(); ++element_id) { for (int element_id = 0; element_id < fem.mesh->GetNE(); ++element_id) {
const mfem::FiniteElement& gravity_element = *fem.RT_fes->GetFE(element_id); const mfem::FiniteElement &gravity_element =
mfem::ElementTransformation* transformation = fem.mesh->GetElementTransformation(element_id); *fem.gravityFluxFes->GetFE(element_id);
mfem::ElementTransformation *transformation =
fem.mesh->GetElementTransformation(element_id);
mfem::Array<int> gravity_dofs; mfem::Array<int> gravity_dofs;
mfem::DofTransformation* gravity_transform = fem.RT_fes->GetElementVDofs(element_id, gravity_dofs); mfem::DofTransformation *gravity_transform =
fem.gravityFluxFes->GetElementVDofs(element_id, gravity_dofs);
const int dof_count = gravity_element.GetDof(); const int dof_count = gravity_element.GetDof();
const int dimension = transformation->GetSpaceDim(); const int dimension = transformation->GetSpaceDim();
@@ -86,32 +104,38 @@ static mfem::Vector assemble_monopole_projection_rhs(
mfem::Vector physical_position(dimension); mfem::Vector physical_position(dimension);
mfem::Vector analytic_field(dimension); mfem::Vector analytic_field(dimension);
mfem::Vector pulled_field(dimension); mfem::Vector pulled_field(dimension);
mfem::DenseMatrix mapping_jacobian(dimension);
mfem::DenseMatrix vector_shape(dof_count, dimension); mfem::DenseMatrix vector_shape(dof_count, dimension);
element_rhs = 0.0; element_rhs = 0.0;
const mfem::IntegrationRule& rule = mfem::IntRules.Get( const mfem::IntegrationRule &rule =
transformation->GetGeometryType(), mfem::IntRules.Get(transformation->GetGeometryType(), quadrature_order);
quadrature_order
);
for (int quadrature_point_id = 0; quadrature_point_id < rule.GetNPoints(); ++quadrature_point_id) { for (int quadrature_point_id = 0; quadrature_point_id < rule.GetNPoints();
++quadrature_point_id) {
const mfem::IntegrationPoint &point = rule.IntPoint(quadrature_point_id); const mfem::IntegrationPoint &point = rule.IntPoint(quadrature_point_id);
fem.mapping->GetPhysicalPoint(*transformation, point, physical_position); mean_field::mapping::MappingPointContext mapping_context;
MFEM_VERIFY(
mapping_evaluator.EvaluatePoint(*transformation, point,
mapping_context) ==
mean_field::mapping::MappingStatus::valid,
"Invalid mapping in monopole projection RHS.");
physical_position = mapping_context.physical_position;
const double radius = physical_position.Norml2(); const double radius = physical_position.Norml2();
MFEM_VERIFY(std::isfinite(radius) && radius > 0.0, "Invalid radius in monopole projection RHS."); MFEM_VERIFY(std::isfinite(radius) && radius > 0.0,
"Invalid radius in monopole projection RHS.");
analytic_field = physical_position; analytic_field = physical_position;
if (transformation->Attribute == vacuum_attribute) { if (transformation->Attribute == vacuum_attribute) {
analytic_field *= mean_field::utils::G * mass / (radius * radius * radius); analytic_field *=
mean_field::utils::G * mass / (radius * radius * radius);
} else { } else {
analytic_field *= mean_field::utils::G * mass / analytic_field *= mean_field::utils::G * mass /
(stellar_radius * stellar_radius * stellar_radius); (stellar_radius * stellar_radius * stellar_radius);
} }
fem.mapping->ComputeJacobian(*transformation, mapping_jacobian); mapping_context.mapping_jacobian.MultTranspose(analytic_field,
mapping_jacobian.MultTranspose(analytic_field, pulled_field); pulled_field);
transformation->SetIntPoint(&point); transformation->SetIntPoint(&point);
gravity_element.CalcVShape(*transformation, vector_shape); gravity_element.CalcVShape(*transformation, vector_shape);
@@ -119,7 +143,8 @@ static mfem::Vector assemble_monopole_projection_rhs(
for (int dof = 0; dof < dof_count; ++dof) { for (int dof = 0; dof < dof_count; ++dof) {
for (int component = 0; component < dimension; ++component) { for (int component = 0; component < dimension; ++component) {
element_rhs(dof) += reference_weight * vector_shape(dof, component) * pulled_field(component); element_rhs(dof) += reference_weight * vector_shape(dof, component) *
pulled_field(component);
} }
} }
} }
@@ -130,9 +155,10 @@ static mfem::Vector assemble_monopole_projection_rhs(
local_rhs.AddElementVector(gravity_dofs, element_rhs); local_rhs.AddElementVector(gravity_dofs, element_rhs);
} }
mfem::Vector true_rhs(fem.RT_fes->GetTrueVSize()); mfem::Vector true_rhs(fem.gravityFluxFes->GetTrueVSize());
true_rhs = 0.0; true_rhs = 0.0;
const mfem::Operator* prolongation = fem.RT_fes->GetProlongationMatrix(); const mfem::Operator *prolongation =
fem.gravityFluxFes->GetProlongationMatrix();
if (prolongation != nullptr) { if (prolongation != nullptr) {
prolongation->MultTranspose(local_rhs, true_rhs); prolongation->MultTranspose(local_rhs, true_rhs);
} else { } else {
@@ -142,120 +168,134 @@ static mfem::Vector assemble_monopole_projection_rhs(
return true_rhs; return true_rhs;
} }
static mfem::Vector project_monopole_gradient( static mfem::Vector
mean_field::fem::FEM& fem, project_monopole_gradient(mean_field::fem::FEM &fem,
const mfem::GridFunction &displacement, const mfem::GridFunction &displacement,
const double mass, const double mass, const double stellar_radius) {
const double stellar_radius
) {
mfem::Vector displacement_true; mfem::Vector displacement_true;
displacement.GetTrueDofs(displacement_true); displacement.GetTrueDofs(displacement_true);
const mfem::Vector projection_rhs = assemble_monopole_projection_rhs( const mfem::Vector projection_rhs_true =
fem, assemble_monopole_projection_rhs(fem, displacement, mass, stellar_radius);
displacement,
mass,
stellar_radius
);
mean_field::operators::PreparedMappedHDivMassOperator mass_operator( mean_field::operators::PreparedMappedHDivMassOperator mass_operator(
fem, fem, *fem.domainMapperStateless);
*fem.domain_mapper_stateless mass_operator.Prepare(
); mass_operator.GetDisplacementMap().gather(displacement_true));
mass_operator.Prepare(displacement_true);
mfem::CGSolver solver(fem.RT_fes->GetComm()); const mfem::Vector projection_rhs =
mass_operator.GetFluxMap().gather(projection_rhs_true);
mfem::CGSolver solver(fem.gravityFluxFes->GetComm());
solver.SetOperator(mass_operator); solver.SetOperator(mass_operator);
solver.SetRelTol(1.0e-11); solver.SetRelTol(1.0e-11);
solver.SetAbsTol(1.0e-13); solver.SetAbsTol(1.0e-13);
solver.SetMaxIter(4000); solver.SetMaxIter(4000);
solver.SetPrintLevel(0); solver.SetPrintLevel(0);
mfem::Vector projected_gradient(fem.RT_fes->GetTrueVSize()); mfem::Vector projected_gradient_reduced(
projected_gradient = 0.0; mass_operator.GetFluxMap().reduced_size());
solver.Mult(projection_rhs, projected_gradient); projected_gradient_reduced = 0.0;
solver.Mult(projection_rhs, projected_gradient_reduced);
mfem::Vector residual; mfem::Vector residual;
mass_operator.Mult(projected_gradient, residual); mass_operator.Mult(projected_gradient_reduced, residual);
residual -= projection_rhs; residual -= projection_rhs;
const double relative_residual = global_norm(residual, fem.RT_fes->GetComm()) / const double relative_residual =
std::max(global_norm(projection_rhs, fem.RT_fes->GetComm()), std::numeric_limits<double>::epsilon()); global_norm(residual, fem.gravityFluxFes->GetComm()) /
std::max(global_norm(projection_rhs, fem.gravityFluxFes->GetComm()),
std::numeric_limits<double>::epsilon());
REQUIRE(std::isfinite(relative_residual)); REQUIRE(std::isfinite(relative_residual));
REQUIRE(relative_residual < 1.0e-8); REQUIRE(relative_residual < 1.0e-8);
return projected_gradient; return mass_operator.GetFluxMap().scatter(projected_gradient_reduced);
} }
static double mapped_hdiv_relative_gap( static double mapped_hdiv_relative_gap(mean_field::fem::FEM &fem,
mean_field::fem::FEM& fem,
const mfem::GridFunction &displacement, const mfem::GridFunction &displacement,
const mfem::Vector &calculated, const mfem::Vector &calculated,
const mfem::Vector& reference const mfem::Vector &reference) {
) {
mfem::Vector displacement_true; mfem::Vector displacement_true;
displacement.GetTrueDofs(displacement_true); displacement.GetTrueDofs(displacement_true);
mean_field::operators::PreparedMappedHDivMassOperator mass_operator( mean_field::operators::PreparedMappedHDivMassOperator mass_operator(
fem, fem, *fem.domainMapperStateless);
*fem.domain_mapper_stateless mass_operator.Prepare(
); mass_operator.GetDisplacementMap().gather(displacement_true));
mass_operator.Prepare(displacement_true);
mfem::Vector difference(calculated); mfem::Vector difference(calculated);
difference -= reference; difference -= reference;
mfem::Vector difference_action; mfem::Vector difference_action;
mfem::Vector reference_action; mfem::Vector reference_action;
mass_operator.Mult(difference, difference_action); const mfem::Vector reduced_difference =
mass_operator.Mult(reference, reference_action); mass_operator.GetFluxMap().gather(difference);
const mfem::Vector reduced_reference =
mass_operator.GetFluxMap().gather(reference);
mass_operator.Mult(reduced_difference, difference_action);
mass_operator.Mult(reduced_reference, reference_action);
const double difference_energy = global_dot(difference, difference_action, fem.RT_fes->GetComm()); const double difference_energy = global_dot(
const double reference_energy = global_dot(reference, reference_action, fem.RT_fes->GetComm()); reduced_difference, difference_action, fem.gravityFluxFes->GetComm());
MFEM_VERIFY(reference_energy > 0.0, "Projected monopole field has zero mapped H(div) norm."); const double reference_energy = global_dot(
reduced_reference, reference_action, fem.gravityFluxFes->GetComm());
MFEM_VERIFY(reference_energy > 0.0,
"Projected monopole field has zero mapped H(div) norm.");
return std::sqrt(std::max(0.0, difference_energy) / reference_energy); return std::sqrt(std::max(0.0, difference_energy) / reference_energy);
} }
static AccuracyBudgetEnergies measure_stellar_energies( static AccuracyBudgetEnergies
mean_field::fem::FEM& fem, measure_stellar_energies(mean_field::fem::FEM &fem,
const mfem::GridFunction &density, const mfem::GridFunction &density,
const mean_field::physics::GravitySolution& solution const mean_field::physics::GravitySolution &solution) {
) { const int vacuum_attribute = field_dof_test_utils::vacuum_material_attribute;
const int vacuum_attribute = fem.domain_mapper_stateless->GetVacuumElementAttribute();
const int quadrature_order = diagnostic_quadrature_order(fem); const int quadrature_order = diagnostic_quadrature_order(fem);
mean_field::mapping::GridFunctionMappingEvaluator mapping_evaluator(
*fem.domainMapperStateless, *fem.displacement,
*fem.compactificationCoordinate);
double local_binding = 0.0; double local_binding = 0.0;
double local_virial = 0.0; double local_virial = 0.0;
mfem::Vector physical_position(3); mfem::Vector physical_position(3);
mfem::Vector reference_field(3); mfem::Vector reference_field(3);
mfem::Vector physical_field(3); mfem::Vector physical_field(3);
mfem::DenseMatrix mapping_jacobian(3);
for (int element_id = 0; element_id < fem.mesh->GetNE(); ++element_id) { for (int element_id = 0; element_id < fem.mesh->GetNE(); ++element_id) {
mfem::ElementTransformation* transformation = fem.mesh->GetElementTransformation(element_id); mfem::ElementTransformation *transformation =
fem.mesh->GetElementTransformation(element_id);
if (transformation->Attribute == vacuum_attribute) { if (transformation->Attribute == vacuum_attribute) {
continue; continue;
} }
const mfem::IntegrationRule& rule = mfem::IntRules.Get( const mfem::IntegrationRule &rule =
transformation->GetGeometryType(), mfem::IntRules.Get(transformation->GetGeometryType(), quadrature_order);
quadrature_order
);
for (int quadrature_point_id = 0; quadrature_point_id < rule.GetNPoints(); ++quadrature_point_id) { for (int quadrature_point_id = 0; quadrature_point_id < rule.GetNPoints();
++quadrature_point_id) {
const mfem::IntegrationPoint &point = rule.IntPoint(quadrature_point_id); const mfem::IntegrationPoint &point = rule.IntPoint(quadrature_point_id);
transformation->SetIntPoint(&point); transformation->SetIntPoint(&point);
fem.mapping->GetPhysicalPoint(*transformation, point, physical_position); mean_field::mapping::MappingPointContext mapping_context;
fem.mapping->ComputeJacobian(*transformation, mapping_jacobian); MFEM_VERIFY(
const double mapping_determinant = mapping_jacobian.Det(); mapping_evaluator.EvaluatePoint(*transformation, point,
MFEM_VERIFY(mapping_determinant > 0.0, "Non-positive mapping determinant in energy diagnostic."); mapping_context) ==
mean_field::mapping::MappingStatus::valid,
"Invalid mapping in energy diagnostic.");
physical_position = mapping_context.physical_position;
const mfem::DenseMatrix &mapping_jacobian =
mapping_context.mapping_jacobian;
const double mapping_determinant =
mapping_context.mapping_determinant;
MFEM_VERIFY(mapping_determinant > 0.0,
"Non-positive mapping determinant in energy diagnostic.");
solution.gradPhi.GetVectorValue(element_id, point, reference_field); solution.gradPhi.GetVectorValue(element_id, point, reference_field);
mapping_jacobian.Mult(reference_field, physical_field); mapping_jacobian.Mult(reference_field, physical_field);
physical_field /= mapping_determinant; physical_field /= mapping_determinant;
const double weight = point.weight * transformation->Weight() * mapping_determinant; const double weight =
point.weight * transformation->Weight() * mapping_determinant;
const double rho = density.GetValue(element_id, point); const double rho = density.GetValue(element_id, point);
const double phi = solution.phi.GetValue(element_id, point); const double phi = solution.phi.GetValue(element_id, point);
local_binding += 0.5 * rho * phi * weight; local_binding += 0.5 * rho * phi * weight;
@@ -264,34 +304,49 @@ static AccuracyBudgetEnergies measure_stellar_energies(
} }
AccuracyBudgetEnergies energies; AccuracyBudgetEnergies energies;
MPI_Allreduce(&local_binding, &energies.binding, 1, MPI_DOUBLE, MPI_SUM, fem.L2_fes->GetComm()); MPI_Allreduce(&local_binding, &energies.binding, 1, MPI_DOUBLE, MPI_SUM,
MPI_Allreduce(&local_virial, &energies.virial, 1, MPI_DOUBLE, MPI_SUM, fem.L2_fes->GetComm()); fem.densityFes->GetComm());
MPI_Allreduce(&local_virial, &energies.virial, 1, MPI_DOUBLE, MPI_SUM,
fem.densityFes->GetComm());
return energies; return energies;
} }
static double reduced_gravity_relative_residual( static double reduced_gravity_relative_residual(
mean_field::fem::FEM& fem, mean_field::fem::FEM &fem, const mfem::GridFunction &density,
const mfem::GridFunction& density,
const mfem::GridFunction &displacement, const mfem::GridFunction &displacement,
const mean_field::physics::GravitySolution& solution const mean_field::physics::GravitySolution &solution) {
) {
using GravityFieldForm = mean_field::utils::blocks::gravity_field_form; using GravityFieldForm = mean_field::utils::blocks::gravity_field_form;
constexpr auto gradient_block = mean_field::utils::blocks::get_residual_block<GravityFieldForm>( constexpr auto gradient_block =
mean_field::utils::blocks::gravity_field.gradient_term mean_field::utils::blocks::get_residual_block<GravityFieldForm>(
); mean_field::utils::blocks::gravity_field.gradient_term);
constexpr auto poisson_block = mean_field::utils::blocks::get_residual_block<GravityFieldForm>( constexpr auto poisson_block =
mean_field::utils::blocks::gravity_field.poisson_term mean_field::utils::blocks::get_residual_block<GravityFieldForm>(
); mean_field::utils::blocks::gravity_field.poisson_term);
using DomainSchema =
mean_field::utils::domain::CoreEnvelopeVacuumDomainSchema;
const mean_field::field::FieldDofMap density_map =
mean_field::field::make_field_dof_map<mean_field::field::Density,
DomainSchema>(*fem.densityFes);
const mean_field::field::FieldDofMap displacement_map =
mean_field::field::make_field_dof_map<mean_field::field::Displacement,
DomainSchema>(*fem.displacementFes);
const mean_field::field::FieldDofMap gravity_flux_map =
mean_field::field::make_field_dof_map<mean_field::field::Gravity,
DomainSchema>(*fem.gravityFluxFes);
const mean_field::field::FieldDofMap gravity_potential_map =
mean_field::field::make_field_dof_map<mean_field::field::Gravity,
DomainSchema>(
*fem.gravityPotentialFes);
const std::array<int, GravityFieldForm::value_block_count> value_sizes{ const std::array<int, GravityFieldForm::value_block_count> value_sizes{
fem.L2_fes->GetTrueVSize(), fem.Vec_H1_fes->GetTrueVSize(), density_map.reduced_size(), displacement_map.reduced_size(),
fem.RT_fes->GetTrueVSize(), fem.L2_fes->GetTrueVSize() gravity_flux_map.reduced_size(), gravity_potential_map.reduced_size()};
};
const std::array<int, GravityFieldForm::residual_block_count> residual_sizes{ const std::array<int, GravityFieldForm::residual_block_count> residual_sizes{
fem.RT_fes->GetTrueVSize(), fem.L2_fes->GetTrueVSize() gravity_flux_map.reduced_size(), gravity_potential_map.reduced_size()};
}; const mean_field::utils::blocks::form_layout<GravityFieldForm> layout(
const mean_field::utils::blocks::form_layout<GravityFieldForm> layout(value_sizes, residual_sizes); value_sizes, residual_sizes);
mfem::Vector density_true; mfem::Vector density_true;
mfem::Vector displacement_true; mfem::Vector displacement_true;
@@ -302,60 +357,47 @@ static double reduced_gravity_relative_residual(
solution.gradPhi.GetTrueDofs(gradient_true); solution.gradPhi.GetTrueDofs(gradient_true);
solution.phi.GetTrueDofs(potential_true); solution.phi.GetTrueDofs(potential_true);
mean_field::operators::context::gravity_field::GravityFieldLinearizationContext linearization_context( mean_field::operators::context::gravity_field::
fem, GravityFieldLinearizationContext linearization_context(
*fem.domain_mapper_stateless fem, *fem.domainMapperStateless);
);
mean_field::operators::GravityFieldJacobianOperator jacobian( mean_field::operators::GravityFieldJacobianOperator jacobian(
fem, fem, *fem.domainMapperStateless, linearization_context,
*fem.domain_mapper_stateless, layout.value_offsets(), layout.residual_offsets());
linearization_context,
layout.value_offsets(),
layout.residual_offsets()
);
mean_field::operators::GravityFieldOperator field_operator( mean_field::operators::GravityFieldOperator field_operator(
fem, fem, *fem.domainMapperStateless, linearization_context,
*fem.domain_mapper_stateless, layout.value_offsets(), jacobian);
linearization_context, mean_field::operators::context::gravity_field::GravityFieldGeometryContext
layout.value_offsets(), geometry_context(fem, *fem.domainMapperStateless);
jacobian
);
mean_field::operators::context::gravity_field::GravityFieldGeometryContext geometry_context(
fem,
*fem.domain_mapper_stateless
);
mean_field::operators::ReducedGravityFieldOperator reduced_operator( mean_field::operators::ReducedGravityFieldOperator reduced_operator(
field_operator, field_operator, geometry_context,
geometry_context, displacement_map.gather(displacement_true));
displacement_true
);
mfem::Vector right_hand_side; mfem::Vector right_hand_side;
reduced_operator.BuildRightHandSide(density_true, right_hand_side); reduced_operator.BuildRightHandSide(density_map.gather(density_true),
right_hand_side);
mfem::BlockVector state(layout.residual_offsets()); mfem::BlockVector state(layout.residual_offsets());
state = 0.0; state = 0.0;
state.GetBlock(gradient_block) = gradient_true; state.GetBlock(gradient_block) = gravity_flux_map.gather(gradient_true);
state.GetBlock(poisson_block) = potential_true; state.GetBlock(poisson_block) = gravity_potential_map.gather(potential_true);
mfem::Vector residual; mfem::Vector residual;
reduced_operator.Mult(state, residual); reduced_operator.Mult(state, residual);
residual -= right_hand_side; residual -= right_hand_side;
return global_norm(residual, fem.L2_fes->GetComm()) / return global_norm(residual, fem.mesh->GetComm()) /
std::max(global_norm(right_hand_side, fem.L2_fes->GetComm()), std::numeric_limits<double>::epsilon()); std::max(global_norm(right_hand_side, fem.mesh->GetComm()),
std::numeric_limits<double>::epsilon());
} }
static AccuracyBudgetMetrics measure_monopole_accuracy( static AccuracyBudgetMetrics
mean_field::fem::FEM& fem, measure_monopole_accuracy(mean_field::fem::FEM &fem,
const mfem::GridFunction &density, const mfem::GridFunction &density,
const mfem::GridFunction &displacement, const mfem::GridFunction &displacement,
const mean_field::physics::GravitySolution &solution, const mean_field::physics::GravitySolution &solution,
const mfem::ParGridFunction &projected_potential, const mfem::ParGridFunction &projected_potential,
const mfem::Vector &projected_gradient, const mfem::Vector &projected_gradient,
const double mass, const double mass, const double stellar_radius) {
const double stellar_radius
) {
mfem::Vector solution_gradient; mfem::Vector solution_gradient;
solution.gradPhi.GetTrueDofs(solution_gradient); solution.gradPhi.GetTrueDofs(solution_gradient);
@@ -364,7 +406,8 @@ static AccuracyBudgetMetrics measure_monopole_accuracy(
solution.phi.GetTrueDofs(solution_potential); solution.phi.GetTrueDofs(solution_potential);
projected_potential.GetTrueDofs(projection_potential); projected_potential.GetTrueDofs(projection_potential);
mfem::ParGridFunction projected_gradient_grid_function(fem.RT_fes.get()); mfem::ParGridFunction projected_gradient_grid_function(
fem.gravityFluxFes.get());
projected_gradient_grid_function.SetFromTrueDofs(projected_gradient); projected_gradient_grid_function.SetFromTrueDofs(projected_gradient);
double local_solution_gradient_error = 0.0; double local_solution_gradient_error = 0.0;
@@ -374,146 +417,185 @@ static AccuracyBudgetMetrics measure_monopole_accuracy(
double local_projection_potential_error = 0.0; double local_projection_potential_error = 0.0;
double local_potential_norm = 0.0; double local_potential_norm = 0.0;
const int vacuum_attribute = fem.domain_mapper_stateless->GetVacuumElementAttribute(); const int vacuum_attribute = field_dof_test_utils::vacuum_material_attribute;
const int quadrature_order = diagnostic_quadrature_order(fem); const int quadrature_order = diagnostic_quadrature_order(fem);
mean_field::mapping::GridFunctionMappingEvaluator mapping_evaluator(
*fem.domainMapperStateless, *fem.displacement,
*fem.compactificationCoordinate);
mfem::Vector physical_position(3); mfem::Vector physical_position(3);
mfem::Vector analytic_gradient(3); mfem::Vector analytic_gradient(3);
mfem::Vector solution_reference_gradient(3); mfem::Vector solution_reference_gradient(3);
mfem::Vector projection_reference_gradient(3); mfem::Vector projection_reference_gradient(3);
mfem::Vector solution_physical_gradient(3); mfem::Vector solution_physical_gradient(3);
mfem::Vector projection_physical_gradient(3); mfem::Vector projection_physical_gradient(3);
mfem::DenseMatrix mapping_jacobian(3);
for (int element_id = 0; element_id < fem.mesh->GetNE(); ++element_id) { for (int element_id = 0; element_id < fem.mesh->GetNE(); ++element_id) {
mfem::ElementTransformation* transformation = fem.mesh->GetElementTransformation(element_id); mfem::ElementTransformation *transformation =
const mfem::IntegrationRule& rule = mfem::IntRules.Get( fem.mesh->GetElementTransformation(element_id);
transformation->GetGeometryType(), const mfem::IntegrationRule &rule =
quadrature_order mfem::IntRules.Get(transformation->GetGeometryType(), quadrature_order);
);
for (int quadrature_point_id = 0; quadrature_point_id < rule.GetNPoints(); ++quadrature_point_id) { for (int quadrature_point_id = 0; quadrature_point_id < rule.GetNPoints();
++quadrature_point_id) {
const mfem::IntegrationPoint &point = rule.IntPoint(quadrature_point_id); const mfem::IntegrationPoint &point = rule.IntPoint(quadrature_point_id);
transformation->SetIntPoint(&point); transformation->SetIntPoint(&point);
fem.mapping->GetPhysicalPoint(*transformation, point, physical_position); mean_field::mapping::MappingPointContext mapping_context;
fem.mapping->ComputeJacobian(*transformation, mapping_jacobian); MFEM_VERIFY(
const double mapping_determinant = mapping_jacobian.Det(); mapping_evaluator.EvaluatePoint(*transformation, point,
MFEM_VERIFY(mapping_determinant > 0.0, "Non-positive mapping determinant in accuracy diagnostic."); mapping_context) ==
mean_field::mapping::MappingStatus::valid,
"Invalid mapping in accuracy diagnostic.");
physical_position = mapping_context.physical_position;
const mfem::DenseMatrix &mapping_jacobian =
mapping_context.mapping_jacobian;
const double mapping_determinant =
mapping_context.mapping_determinant;
MFEM_VERIFY(mapping_determinant > 0.0,
"Non-positive mapping determinant in accuracy diagnostic.");
const double radius = physical_position.Norml2(); const double radius = physical_position.Norml2();
MFEM_VERIFY(std::isfinite(radius) && radius > 0.0, "Invalid radius in monopole diagnostic."); MFEM_VERIFY(std::isfinite(radius) && radius > 0.0,
"Invalid radius in monopole diagnostic.");
analytic_gradient = physical_position; analytic_gradient = physical_position;
double analytic_potential = 0.0; double analytic_potential = 0.0;
if (transformation->Attribute == vacuum_attribute) { if (transformation->Attribute == vacuum_attribute) {
analytic_gradient *= mean_field::utils::G * mass / (radius * radius * radius); analytic_gradient *=
mean_field::utils::G * mass / (radius * radius * radius);
analytic_potential = -mean_field::utils::G * mass / radius; analytic_potential = -mean_field::utils::G * mass / radius;
} else { } else {
analytic_gradient *= mean_field::utils::G * mass / analytic_gradient *= mean_field::utils::G * mass /
(stellar_radius * stellar_radius * stellar_radius); (stellar_radius * stellar_radius * stellar_radius);
analytic_potential = -mean_field::utils::G * mass * analytic_potential =
-mean_field::utils::G * mass *
(3.0 * stellar_radius * stellar_radius - radius * radius) / (3.0 * stellar_radius * stellar_radius - radius * radius) /
(2.0 * stellar_radius * stellar_radius * stellar_radius); (2.0 * stellar_radius * stellar_radius * stellar_radius);
} }
solution.gradPhi.GetVectorValue(element_id, point, solution_reference_gradient); solution.gradPhi.GetVectorValue(element_id, point,
mapping_jacobian.Mult(solution_reference_gradient, solution_physical_gradient); solution_reference_gradient);
mapping_jacobian.Mult(solution_reference_gradient,
solution_physical_gradient);
solution_physical_gradient /= mapping_determinant; solution_physical_gradient /= mapping_determinant;
projected_gradient_grid_function.GetVectorValue(element_id, point, projection_reference_gradient); projected_gradient_grid_function.GetVectorValue(
mapping_jacobian.Mult(projection_reference_gradient, projection_physical_gradient); element_id, point, projection_reference_gradient);
mapping_jacobian.Mult(projection_reference_gradient,
projection_physical_gradient);
projection_physical_gradient /= mapping_determinant; projection_physical_gradient /= mapping_determinant;
const double solution_potential_value = solution.phi.GetValue(element_id, point); const double solution_potential_value =
const double projection_potential_value = projected_potential.GetValue(element_id, point); solution.phi.GetValue(element_id, point);
const double weight = point.weight * transformation->Weight() * mapping_determinant; const double projection_potential_value =
projected_potential.GetValue(element_id, point);
const double weight =
point.weight * transformation->Weight() * mapping_determinant;
solution_physical_gradient -= analytic_gradient; solution_physical_gradient -= analytic_gradient;
projection_physical_gradient -= analytic_gradient; projection_physical_gradient -= analytic_gradient;
local_solution_gradient_error += weight * (solution_physical_gradient * solution_physical_gradient); local_solution_gradient_error +=
local_projection_gradient_error += weight * (projection_physical_gradient * projection_physical_gradient); weight * (solution_physical_gradient * solution_physical_gradient);
local_projection_gradient_error +=
weight *
(projection_physical_gradient * projection_physical_gradient);
local_gradient_norm += weight * (analytic_gradient * analytic_gradient); local_gradient_norm += weight * (analytic_gradient * analytic_gradient);
local_solution_potential_error += weight * local_solution_potential_error +=
(solution_potential_value - analytic_potential) * (solution_potential_value - analytic_potential); weight * (solution_potential_value - analytic_potential) *
local_projection_potential_error += weight * (solution_potential_value - analytic_potential);
(projection_potential_value - analytic_potential) * (projection_potential_value - analytic_potential); local_projection_potential_error +=
weight * (projection_potential_value - analytic_potential) *
(projection_potential_value - analytic_potential);
local_potential_norm += weight * analytic_potential * analytic_potential; local_potential_norm += weight * analytic_potential * analytic_potential;
} }
} }
const std::array<double, 6> local_values{ const std::array<double, 6> local_values{local_solution_gradient_error,
local_solution_gradient_error, local_projection_gradient_error, local_gradient_norm, local_projection_gradient_error,
local_solution_potential_error, local_projection_potential_error, local_potential_norm local_gradient_norm,
}; local_solution_potential_error,
local_projection_potential_error,
local_potential_norm};
std::array<double, 6> global_values{}; std::array<double, 6> global_values{};
MPI_Allreduce( MPI_Allreduce(local_values.data(), global_values.data(),
local_values.data(), global_values.data(), static_cast<int>(local_values.size()), static_cast<int>(local_values.size()), MPI_DOUBLE, MPI_SUM,
MPI_DOUBLE, MPI_SUM, fem.L2_fes->GetComm() fem.mesh->GetComm());
);
const AccuracyBudgetEnergies energies = measure_stellar_energies(fem, density, solution); const AccuracyBudgetEnergies energies =
const double analytic_energy = -3.0 * mean_field::utils::G * mass * mass / (5.0 * stellar_radius); measure_stellar_energies(fem, density, solution);
const double analytic_energy =
-3.0 * mean_field::utils::G * mass * mass / (5.0 * stellar_radius);
REQUIRE(global_values[2] > 0.0); REQUIRE(global_values[2] > 0.0);
REQUIRE(global_values[5] > 0.0); REQUIRE(global_values[5] > 0.0);
AccuracyBudgetMetrics metrics; AccuracyBudgetMetrics metrics;
metrics.direct_relative_residual = reduced_gravity_relative_residual(fem, density, displacement, solution); metrics.direct_relative_residual =
metrics.gradient_relative_error = std::sqrt(global_values[0] / global_values[2]); reduced_gravity_relative_residual(fem, density, displacement, solution);
metrics.gradient_projection_relative_error = std::sqrt(global_values[1] / global_values[2]); metrics.gradient_relative_error =
std::sqrt(global_values[0] / global_values[2]);
metrics.gradient_projection_relative_error =
std::sqrt(global_values[1] / global_values[2]);
metrics.gradient_solution_projection_gap = mapped_hdiv_relative_gap( metrics.gradient_solution_projection_gap = mapped_hdiv_relative_gap(
fem, displacement, solution_gradient, projected_gradient fem, displacement, solution_gradient, projected_gradient);
); metrics.potential_relative_error =
metrics.potential_relative_error = std::sqrt(global_values[3] / global_values[5]); std::sqrt(global_values[3] / global_values[5]);
metrics.potential_projection_relative_error = std::sqrt(global_values[4] / global_values[5]); metrics.potential_projection_relative_error =
std::sqrt(global_values[4] / global_values[5]);
mfem::Vector potential_difference(solution_potential); mfem::Vector potential_difference(solution_potential);
potential_difference -= projection_potential; potential_difference -= projection_potential;
const double projection_potential_norm = global_norm(projection_potential, fem.L2_fes->GetComm()); const double projection_potential_norm =
global_norm(projection_potential, fem.gravityPotentialFes->GetComm());
REQUIRE(projection_potential_norm > 0.0); REQUIRE(projection_potential_norm > 0.0);
metrics.potential_solution_projection_gap = global_norm(potential_difference, fem.L2_fes->GetComm()) / metrics.potential_solution_projection_gap =
global_norm(potential_difference, fem.gravityPotentialFes->GetComm()) /
projection_potential_norm; projection_potential_norm;
metrics.binding_relative_error = std::abs(energies.binding - analytic_energy) / std::abs(analytic_energy); metrics.binding_relative_error =
metrics.virial_relative_error = std::abs(energies.virial - analytic_energy) / std::abs(analytic_energy); std::abs(energies.binding - analytic_energy) / std::abs(analytic_energy);
metrics.virial_consistency_error = std::abs(energies.binding - energies.virial) / metrics.virial_relative_error =
std::max(std::abs(energies.binding), std::numeric_limits<double>::epsilon()); std::abs(energies.virial - analytic_energy) / std::abs(analytic_energy);
metrics.virial_consistency_error =
std::abs(energies.binding - energies.virial) /
std::max(std::abs(energies.binding),
std::numeric_limits<double>::epsilon());
return metrics; return metrics;
} }
static void run_monopole_case( static void run_monopole_case(const std::string &sweep_name,
const std::string& sweep_name,
const std::string &case_name, const std::string &case_name,
mean_field::utils::Args args, mean_field::utils::Args args,
const double solver_tolerance, const double solver_tolerance,
const int quadrature_boost const int quadrature_boost) {
) {
args.p.rtol = solver_tolerance; args.p.rtol = solver_tolerance;
args.p.atol = std::min(args.p.atol, solver_tolerance * 1.0e-2); args.p.atol = std::min(args.p.atol, solver_tolerance * 1.0e-2);
args.p.max_iters = std::max(args.p.max_iters, 2000); args.p.max_iters = std::max(args.p.max_iters, 2000);
args.quadrature.global_boost = quadrature_boost; args.quadrature.global_boost = quadrature_boost;
mean_field::fem::FEM fem = mean_field::fem::setup_fem(args.mesh_file, args, 0); mean_field::fem::FEM fem =
REQUIRE(fem.mapping != nullptr); mean_field::fem::setup_fem(args.mesh_file, args, 0);
REQUIRE(fem.domain_mapper_stateless != nullptr); REQUIRE(fem.domainMapperStateless != nullptr);
const double stellar_radius = mean_field::utils::RADIUS; const double stellar_radius = mean_field::utils::RADIUS;
const double mass = mean_field::utils::MASS; const double mass = mean_field::utils::MASS;
const double density_value = mass / ((4.0 / 3.0) * M_PI * stellar_radius * stellar_radius * stellar_radius); const double density_value = mass / ((4.0 / 3.0) * M_PI * stellar_radius *
stellar_radius * stellar_radius);
mfem::ParGridFunction displacement(fem.Vec_H1_fes.get()); mfem::ParGridFunction displacement(fem.displacementFes.get());
displacement = 0.0; displacement = 0.0;
fem.mapping->ResetDisplacement(); *fem.displacement = 0.0;
mean_field::physics::update_stiffness_matrix(fem); mfem::GridFunction density(fem.densityFes.get());
mfem::GridFunction density(fem.L2_fes.get());
density = density_value; density = density_value;
zero_vacuum_density(fem, density); zero_vacuum_density(fem, density);
mean_field::analysis::conserve_mass(fem, density, mass); mean_field::analysis::conserve_mass(fem, density, mass);
fem.com = mean_field::analysis::get_com(fem, density); fem.com = mean_field::analysis::get_com(fem, density);
fem.Q = mean_field::physics::compute_quadrupole_moment_tensor(fem, density, fem.com); fem.Q = mean_field::physics::compute_quadrupole_moment_tensor(fem, density,
fem.com);
const mean_field::physics::GravitySolution solution = const mean_field::physics::GravitySolution solution =
mean_field::physics::grav_potential_new(fem, args, density, displacement); mean_field::physics::solve_gravity_field(fem, args, density,
displacement);
auto analytic_potential = [mass, stellar_radius](const mfem::Vector& position) { auto analytic_potential = [mass,
stellar_radius](const mfem::Vector &position) {
const double radius = position.Norml2(); const double radius = position.Norml2();
if (radius >= stellar_radius) { if (radius >= stellar_radius) {
return -mean_field::utils::G * mass / radius; return -mean_field::utils::G * mass / radius;
@@ -522,30 +604,19 @@ static void run_monopole_case(
(3.0 * stellar_radius * stellar_radius - radius * radius) / (3.0 * stellar_radius * stellar_radius - radius * radius) /
(2.0 * stellar_radius * stellar_radius * stellar_radius); (2.0 * stellar_radius * stellar_radius * stellar_radius);
}; };
mean_field::mapping::PhysicalPositionFunctionCoefficient potential_coefficient( mean_field::mapping::PhysicalPositionFunctionCoefficient
*fem.mapping, potential_coefficient(*fem.domainMapperStateless, *fem.displacement,
analytic_potential *fem.compactificationCoordinate,
); analytic_potential);
mfem::ParGridFunction projected_potential(fem.L2_fes.get()); mfem::ParGridFunction projected_potential(fem.gravityPotentialFes.get());
projected_potential.ProjectCoefficient(potential_coefficient); projected_potential.ProjectCoefficient(potential_coefficient);
const mfem::Vector projected_gradient = project_monopole_gradient( const mfem::Vector projected_gradient =
fem, project_monopole_gradient(fem, displacement, mass, stellar_radius);
displacement,
mass,
stellar_radius
);
const AccuracyBudgetMetrics metrics = measure_monopole_accuracy( const AccuracyBudgetMetrics metrics = measure_monopole_accuracy(
fem, fem, density, displacement, solution, projected_potential,
density, projected_gradient, mass, stellar_radius);
displacement,
solution,
projected_potential,
projected_gradient,
mass,
stellar_radius
);
REQUIRE(std::isfinite(metrics.direct_relative_residual)); REQUIRE(std::isfinite(metrics.direct_relative_residual));
REQUIRE(std::isfinite(metrics.gradient_relative_error)); REQUIRE(std::isfinite(metrics.gradient_relative_error));
@@ -553,64 +624,51 @@ static void run_monopole_case(
REQUIRE(std::isfinite(metrics.virial_consistency_error)); REQUIRE(std::isfinite(metrics.virial_consistency_error));
record_experiment_result( record_experiment_result(
sweep_name, sweep_name, case_name,
case_name, {{"solver_rtol", std::to_string(solver_tolerance)},
{
{"solver_rtol", std::to_string(solver_tolerance)},
{"quadrature_global_boost", std::to_string(quadrature_boost)}, {"quadrature_global_boost", std::to_string(quadrature_boost)},
{"mesh_file", args.mesh_file} {"mesh_file", args.mesh_file}},
}, {{"direct_relative_residual", metrics.direct_relative_residual},
{
{"direct_relative_residual", metrics.direct_relative_residual},
{"gradient_relative_error", metrics.gradient_relative_error}, {"gradient_relative_error", metrics.gradient_relative_error},
{"gradient_projection_relative_error", metrics.gradient_projection_relative_error}, {"gradient_projection_relative_error",
{"gradient_solution_projection_gap", metrics.gradient_solution_projection_gap}, metrics.gradient_projection_relative_error},
{"gradient_solution_projection_gap",
metrics.gradient_solution_projection_gap},
{"potential_relative_error", metrics.potential_relative_error}, {"potential_relative_error", metrics.potential_relative_error},
{"potential_projection_relative_error", metrics.potential_projection_relative_error}, {"potential_projection_relative_error",
{"potential_solution_projection_gap", metrics.potential_solution_projection_gap}, metrics.potential_projection_relative_error},
{"potential_solution_projection_gap",
metrics.potential_solution_projection_gap},
{"binding_relative_error", metrics.binding_relative_error}, {"binding_relative_error", metrics.binding_relative_error},
{"virial_relative_error", metrics.virial_relative_error}, {"virial_relative_error", metrics.virial_relative_error},
{"virial_consistency_error", metrics.virial_consistency_error} {"virial_consistency_error", metrics.virial_consistency_error}});
}
);
} }
TEST_CASE("Uniform Monopole Accuracy Budget: Solver Tolerance", tags::gravity & tags::accuracy & tags::integration) { TEST_CASE("Uniform Monopole Accuracy Budget: Solver Tolerance",
tags::gravity_analytic_accuracy) {
const mean_field::utils::Args args = test_utils::setup_args(); const mean_field::utils::Args args = test_utils::setup_args();
constexpr std::array<double, 4> solver_tolerances{1.0e-8, 1.0e-10, 1.0e-12, 1.0e-14}; constexpr std::array<double, 4> solver_tolerances{1.0e-8, 1.0e-10, 1.0e-12,
1.0e-14};
for (const double solver_tolerance : solver_tolerances) { for (const double solver_tolerance : solver_tolerances) {
run_monopole_case( run_monopole_case("solver_tolerance", "uniform_monopole", args,
"solver_tolerance", solver_tolerance, 0);
"uniform_monopole",
args,
solver_tolerance,
0
);
} }
} }
TEST_CASE("Uniform Monopole Accuracy Budget: Quadrature", tags::gravity & tags::accuracy & tags::integration) { TEST_CASE("Uniform Monopole Accuracy Budget: Quadrature",
tags::gravity_analytic_accuracy) {
const mean_field::utils::Args args = test_utils::setup_args(); const mean_field::utils::Args args = test_utils::setup_args();
constexpr std::array<int, 3> quadrature_boosts{0, 4, 8}; constexpr std::array<int, 3> quadrature_boosts{0, 4, 8};
for (const int quadrature_boost : quadrature_boosts) { for (const int quadrature_boost : quadrature_boosts) {
run_monopole_case( run_monopole_case("quadrature", "uniform_monopole", args, 1.0e-13,
"quadrature", quadrature_boost);
"uniform_monopole",
args,
1.0e-13,
quadrature_boost
);
} }
} }
TEST_CASE("Uniform Monopole Accuracy Budget: Projection Decomposition", tags::gravity & tags::accuracy & tags::integration) { TEST_CASE("Uniform Monopole Accuracy Budget: Projection Decomposition",
run_monopole_case( tags::gravity_analytic_accuracy) {
"projection_decomposition", run_monopole_case("projection_decomposition", "uniform_monopole",
"uniform_monopole", test_utils::setup_args(), 1.0e-13, 0);
test_utils::setup_args(),
1.0e-13,
0
);
} }

View File

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

View File

@@ -21,11 +21,8 @@ import :utils.misc;
import :utils.user; import :utils.user;
namespace mean_field::fem { namespace mean_field::fem {
FEM setup_fem( FEM setup_fem(const std::string &filename, const utils::Args &args,
const std::string &filename, const int extraRefine) {
const utils::Args &args,
const int extraRefine
) {
FEM fem; FEM fem;
using GravityPotential = field::Gravity::Potential; using GravityPotential = field::Gravity::Potential;
@@ -33,6 +30,7 @@ namespace mean_field::fem {
using DisplacementVector = field::Displacement::Vector; using DisplacementVector = field::Displacement::Vector;
using DensityScalar = field::Density::Scalar; using DensityScalar = field::Density::Scalar;
using EnthalpyScalar = field::Enthalpy::Scalar; using EnthalpyScalar = field::Enthalpy::Scalar;
using DomainSchema = utils::domain::CoreEnvelopeVacuumDomainSchema;
// ===================================================================== // =====================================================================
// Section 1: Mesh construction // Section 1: Mesh construction
@@ -47,9 +45,11 @@ namespace mean_field::fem {
int mpiSize = 1; int mpiSize = 1;
MPI_Comm_size(MPI_COMM_WORLD, &mpiSize); MPI_Comm_size(MPI_COMM_WORLD, &mpiSize);
const std::unique_ptr<int[]> meshPartitioning(fem.smesh.mesh->GeneratePartitioning(mpiSize, 1)); const std::unique_ptr<int[]> meshPartitioning(
fem.smesh.mesh->GeneratePartitioning(mpiSize, 1));
fem.mesh = std::make_unique<mfem::ParMesh>(MPI_COMM_WORLD, *fem.smesh.mesh, meshPartitioning.get(), 1); fem.mesh = std::make_unique<mfem::ParMesh>(MPI_COMM_WORLD, *fem.smesh.mesh,
meshPartitioning.get(), 1);
fem.mesh->EnsureNodes(); fem.mesh->EnsureNodes();
@@ -69,22 +69,22 @@ namespace mean_field::fem {
throw std::runtime_error("Values for exterior coordinate not set."); 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) { if (serialCoordinate.FESpace() != &serialCoordinateSpace) {
throw std::runtime_error( throw std::runtime_error(
"Exterior coordinate values are not associated with the " "Exterior coordinate values are not associated with the "
"supplied finite-element space." "supplied finite-element space.");
);
} }
if (serialCoordinateSpace.GetMesh() != fem.smesh.mesh.get()) { if (serialCoordinateSpace.GetMesh() != fem.smesh.mesh.get()) {
throw std::runtime_error( throw std::runtime_error(
"Exterior coordinate space is not associated with the " "Exterior coordinate space is not associated with the "
"loaded STROID mesh." "loaded STROID mesh.");
);
} }
if (serialCoordinateSpace.GetVDim() != 1) { if (serialCoordinateSpace.GetVDim() != 1) {
@@ -94,29 +94,30 @@ namespace mean_field::fem {
if (serialCoordinate.Size() != serialCoordinateSpace.GetVSize()) { if (serialCoordinate.Size() != serialCoordinateSpace.GetVSize()) {
throw std::runtime_error( throw std::runtime_error(
"Exterior coordinate value count does not match its " "Exterior coordinate value count does not match its "
"finite-element space." "finite-element space.");
);
} }
const int compactificationOrder = serialCoordinateSpace.GetMaxElementOrder(); const int compactificationOrder = serialCoordinateSpace.GetMaxElementOrder();
const int dimension = fem.mesh->Dimension(); const int dimension = fem.mesh->Dimension();
fem.compactificationFec = std::make_unique<mfem::H1_FECollection>(compactificationOrder, dimension); fem.compactificationFec =
std::make_unique<mfem::H1_FECollection>(compactificationOrder, dimension);
fem.compactificationFes = fem.compactificationFes = std::make_unique<mfem::ParFiniteElementSpace>(
std::make_unique<mfem::ParFiniteElementSpace>(fem.mesh.get(), fem.compactificationFec.get()); 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( throw std::runtime_error(
"Distributed exterior coordinate does not match the " "Distributed exterior coordinate does not match the "
"constructed parallel finite-element space." "constructed parallel finite-element space.");
);
} }
fem.compactificationCoordinate = std::make_unique<mfem::ParGridFunction>(fem.compactificationFes.get()); fem.compactificationCoordinate =
std::make_unique<mfem::ParGridFunction>(fem.compactificationFes.get());
*fem.compactificationCoordinate = distributedCoordinate; *fem.compactificationCoordinate = distributedCoordinate;
@@ -128,7 +129,8 @@ namespace mean_field::fem {
const double value = (*fem.compactificationCoordinate)(index); const double value = (*fem.compactificationCoordinate)(index);
if (!std::isfinite(value)) { 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); localMinimum = std::min(localMinimum, value);
@@ -139,17 +141,18 @@ namespace mean_field::fem {
double globalMinimum = 0.0; double globalMinimum = 0.0;
double globalMaximum = 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; constexpr double coordinateTolerance = 1.0e-12;
if (globalMinimum < -coordinateTolerance || globalMaximum > 1.0 + coordinateTolerance) { if (globalMinimum < -coordinateTolerance ||
throw std::runtime_error( globalMaximum > 1.0 + coordinateTolerance) {
"Exterior coordinate lies outside the expected " throw std::runtime_error("Exterior coordinate lies outside the expected "
"interval [0, 1]." "interval [0, 1].");
);
} }
// ===================================================================== // =====================================================================
@@ -162,7 +165,8 @@ namespace mean_field::fem {
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. // Gravity flux: H(div)/RT. Basis choices are encoded by field.mfem.
@@ -170,17 +174,21 @@ namespace mean_field::fem {
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. // 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;
@@ -190,7 +198,8 @@ namespace mean_field::fem {
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 // Specific enthalpy: scalar continuous H1
@@ -198,50 +207,11 @@ namespace mean_field::fem {
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 // Section 4: Multipole data
// =====================================================================
auto [stellarRadiusReference, infinityRadiusReference] =
utils::discover_bounds(fem.mesh.get(), 3)
.or_else([](const boundary::BoundsError &) -> std::expected<boundary::Bounds, boundary::BoundsError> {
throw std::runtime_error(
"Unable to determine vacuum-domain reference "
"boundaries."
);
})
.value();
fem.mapping =
std::make_unique<mapping::DomainMapper>(*fem.displacement, stellarRadiusReference, infinityRadiusReference);
// =====================================================================
// Section 5: Block offsets
//
// Legacy layouts only. New coupled operators use :utils.blocks forms.
//
// Main system: [Displacement | Density]
// Gravity system: [Flux | Potential]
// =====================================================================
fem.blockTrueOffsets.SetSize(3);
fem.blockTrueOffsets[0] = 0;
fem.blockTrueOffsets[1] = fem.displacementFes->GetTrueVSize();
fem.blockTrueOffsets[2] = fem.blockTrueOffsets[1] + fem.densityFes->GetTrueVSize();
fem.gravityBlockTrueOffsets.SetSize(3);
fem.gravityBlockTrueOffsets[0] = 0;
fem.gravityBlockTrueOffsets[1] = fem.gravityFluxFes->GetTrueVSize();
fem.gravityBlockTrueOffsets[2] = fem.gravityBlockTrueOffsets[1] + fem.gravityPotentialFes->GetTrueVSize();
// =====================================================================
// Section 6: Multipole data
// ===================================================================== // =====================================================================
fem.com.SetSize(dimension); fem.com.SetSize(dimension);
@@ -251,13 +221,9 @@ namespace mean_field::fem {
fem.Q = 0.0; fem.Q = 0.0;
// ===================================================================== // =====================================================================
// Section 7: Essential boundaries and domain masks // Section 5: Boundary markers
// ===================================================================== // =====================================================================
fem.essentialDisplacementTdofs.SetSize(0);
populate_element_mask(fem.mesh.get(), utils::DOMAINS::STELLAR, fem.gravityContext.stellar_mask);
const int boundaryAttributeCount = fem.mesh->bdr_attributes.Max(); const int boundaryAttributeCount = fem.mesh->bdr_attributes.Max();
fem.boundaryContext.inf_bounds.SetSize(boundaryAttributeCount); fem.boundaryContext.inf_bounds.SetSize(boundaryAttributeCount);
@@ -267,76 +233,47 @@ namespace mean_field::fem {
fem.boundaryContext.inf_bounds = 0; fem.boundaryContext.inf_bounds = 0;
fem.boundaryContext.stellar_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 // Section 7: Quadrature policy
// =====================================================================
fem.gravityContext.minres = std::make_unique<mfem::MINRESSolver>(fem.mesh->GetComm());
fem.gravityContext.minres->SetRelTol(1.0e-12);
fem.gravityContext.minres->SetAbsTol(1.0e-12);
fem.gravityContext.minres->SetMaxIter(1000);
fem.gravityContext.minres->SetPrintLevel(0);
fem.gravityContext.prec_Phi = std::make_unique<mfem::HypreBoomerAMG>();
fem.gravityContext.prec_Phi->SetPrintLevel(0);
fem.gravityContext.block_prec =
std::make_unique<mfem::BlockDiagonalPreconditioner>(fem.gravityBlockTrueOffsets);
fem.gravityContext.minres->SetPreconditioner(*fem.gravityContext.block_prec);
// =====================================================================
// Section 9: Vacuum true-DOF masks
// =====================================================================
{
mfem::Array<int> vacuumMask;
utils::populate_element_mask(fem.mesh.get(), utils::DOMAINS::VACUUM, vacuumMask);
utils::populate_domain_tdofs(fem.displacementFes.get(), vacuumMask, fem.vacuumDisplacementTdofs);
utils::populate_domain_tdofs(fem.densityFes.get(), vacuumMask, fem.vacuumDensityTdofs);
utils::populate_domain_tdofs(fem.enthalpyFes.get(), vacuumMask, fem.vacuumEnthalpyTdofs);
}
// =====================================================================
// Section 10: Quadrature policy
// ===================================================================== // =====================================================================
const quadrature::QuadratureOptions &quadratureOptions = args.quadrature; const quadrature::QuadratureOptions &quadratureOptions = args.quadrature;
if (quadratureOptions.validation.reject_negative_boosts && quadratureOptions.global_boost < 0) { if (quadratureOptions.validation.reject_negative_boosts &&
quadratureOptions.global_boost < 0) {
throw std::invalid_argument("Global quadrature boost cannot be negative."); throw std::invalid_argument("Global quadrature boost cannot be negative.");
} }
quadrature::RuleSet quadratureRuleSet = quadrature::RuleSet quadratureRuleSet = quadrature::make_rule_set(
quadrature::make_rule_set(quadratureOptions.mode, quadratureOptions.global_boost); quadratureOptions.mode, quadratureOptions.global_boost);
if (quadratureOptions.fallback_fixed_order.has_value()) { if (quadratureOptions.fallback_fixed_order.has_value()) {
if (*quadratureOptions.fallback_fixed_order < 0) { 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]( auto apply_quadrature_options = [&quadratureOptions](
quadrature::RuleControl &ruleControl, quadrature::RuleControl &ruleControl,
const quadrature::QuadratureTermOptions &termOptions const quadrature::QuadratureTermOptions
) { &termOptions) {
if (termOptions.fixed_order.has_value() && *termOptions.fixed_order < 0) { if (termOptions.fixed_order.has_value() && *termOptions.fixed_order < 0) {
throw std::invalid_argument("Fixed quadrature order cannot be negative."); throw std::invalid_argument("Fixed quadrature order cannot be negative.");
} }
if (quadratureOptions.validation.reject_negative_boosts && termOptions.additional_boost < 0) { if (quadratureOptions.validation.reject_negative_boosts &&
termOptions.additional_boost < 0) {
throw std::invalid_argument("Term quadrature boost cannot be negative."); throw std::invalid_argument("Term quadrature boost cannot be negative.");
} }
@@ -347,66 +284,97 @@ namespace mean_field::fem {
} }
}; };
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_force, quadratureOptions.gravity_force); apply_quadrature_options(quadratureRuleSet.gravity_force,
quadratureOptions.gravity_force);
apply_quadrature_options(quadratureRuleSet.gravity_boundary, quadratureOptions.gravity_boundary); apply_quadrature_options(quadratureRuleSet.gravity_boundary,
quadratureOptions.gravity_boundary);
apply_quadrature_options(quadratureRuleSet.centrifugal, quadratureOptions.centrifugal); apply_quadrature_options(quadratureRuleSet.centrifugal,
quadratureOptions.centrifugal);
apply_quadrature_options(quadratureRuleSet.density_projection, quadratureOptions.density_projection); apply_quadrature_options(quadratureRuleSet.density_projection,
quadratureOptions.density_projection);
apply_quadrature_options(quadratureRuleSet.eos_closure, quadratureOptions.eos_closure); apply_quadrature_options(quadratureRuleSet.eos_closure,
quadratureOptions.eos_closure);
apply_quadrature_options(quadratureRuleSet.hydrostatic_equilibrium, quadratureOptions.hydrostatic_equilibrium); apply_quadrature_options(quadratureRuleSet.hydrostatic_equilibrium,
quadratureOptions.hydrostatic_equilibrium);
apply_quadrature_options(quadratureRuleSet.isobaric_surface, quadratureOptions.isobaric_surface); apply_quadrature_options(quadratureRuleSet.isobaric_surface,
quadratureOptions.isobaric_surface);
apply_quadrature_options(quadratureRuleSet.mesh_extension, quadratureOptions.mesh_extension); apply_quadrature_options(quadratureRuleSet.mesh_extension,
quadratureOptions.mesh_extension);
apply_quadrature_options(quadratureRuleSet.mass_conservation, quadratureOptions.mass_conservation); apply_quadrature_options(quadratureRuleSet.mass_conservation,
quadratureOptions.mass_conservation);
apply_quadrature_options(quadratureRuleSet.mass_normalization, quadratureOptions.mass_normalization); apply_quadrature_options(quadratureRuleSet.mass_normalization,
quadratureOptions.mass_normalization);
apply_quadrature_options(quadratureRuleSet.center_of_mass, quadratureOptions.center_of_mass); apply_quadrature_options(quadratureRuleSet.center_of_mass,
quadratureOptions.center_of_mass);
apply_quadrature_options(quadratureRuleSet.quadrupole, quadratureOptions.quadrupole); apply_quadrature_options(quadratureRuleSet.quadrupole,
quadratureOptions.quadrupole);
apply_quadrature_options(quadratureRuleSet.gravitational_energy, quadratureOptions.gravitational_energy); apply_quadrature_options(quadratureRuleSet.gravitational_energy,
quadratureOptions.gravitational_energy);
apply_quadrature_options(quadratureRuleSet.pressure_integral, quadratureOptions.pressure_integral); apply_quadrature_options(quadratureRuleSet.pressure_integral,
quadratureOptions.pressure_integral);
apply_quadrature_options(quadratureRuleSet.pressure_force, quadratureOptions.pressure_force); apply_quadrature_options(quadratureRuleSet.pressure_force,
quadratureOptions.pressure_force);
apply_quadrature_options(quadratureRuleSet.virial, quadratureOptions.virial); apply_quadrature_options(quadratureRuleSet.virial, quadratureOptions.virial);
apply_quadrature_options(quadratureRuleSet.error_norm, quadratureOptions.error_norm); apply_quadrature_options(quadratureRuleSet.error_norm,
quadratureOptions.error_norm);
apply_quadrature_options(quadratureRuleSet.roles.discretization, quadratureOptions.roles.discretization); apply_quadrature_options(quadratureRuleSet.roles.discretization,
quadratureOptions.roles.discretization);
apply_quadrature_options(quadratureRuleSet.roles.preconditioner, quadratureOptions.roles.preconditioner); apply_quadrature_options(quadratureRuleSet.roles.preconditioner,
quadratureOptions.roles.preconditioner);
apply_quadrature_options(quadratureRuleSet.roles.diagnostic, quadratureOptions.roles.diagnostic); apply_quadrature_options(quadratureRuleSet.roles.diagnostic,
quadratureOptions.roles.diagnostic);
apply_quadrature_options(quadratureRuleSet.roles.projection, quadratureOptions.roles.projection); apply_quadrature_options(quadratureRuleSet.roles.projection,
quadratureOptions.roles.projection);
fem.quadratureFactory = fem.quadratureFactory = std::make_unique<quadrature::RuleFactory>(
std::make_unique<quadrature::RuleFactory>(quadrature::Policy(std::move(quadratureRuleSet))); quadrature::Policy(std::move(quadratureRuleSet)));
// ===================================================================== // =====================================================================
// Section 11: Stateless domain mapper // Section 11: Stateless domain mapper
// ===================================================================== // =====================================================================
auto exteriorDomain = auto exteriorDomain =
std::make_unique<const mapping::compactification::KelvinCompactification>(args.kelvin_options); std::make_unique<const mapping::compactification::KelvinCompactification>(
args.kelvin_options);
fem.domainMapperStateless = MFEM_VERIFY(
std::make_unique<mapping::DomainMapperStateless>(args.domain_mapper_options, std::move(exteriorDomain)); args.domain_mapper_options.vacuum_element_attribute ==
DomainSchema::template material_attribute<utils::domain::Vacuum>(),
"The domain-mapper compactification attribute must match the vacuum "
"material registered by the "
"production domain schema.");
fem.domainMapperStateless = std::make_unique<mapping::DomainMapper>(
args.domain_mapper_options, std::move(exteriorDomain));
return fem; return fem;
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

@@ -11,21 +11,30 @@ module mean_field;
import :operators.kernels.gravity_displacement_force; import :operators.kernels.gravity_displacement_force;
namespace { namespace {
enum class GravityDisplacementForceAction { residual, density, gravityGradient, displacement, complete }; using DomainSchema = mean_field::utils::domain::CoreEnvelopeVacuumDomainSchema;
void true_to_local( [[nodiscard]] bool is_vacuum_attribute(const int attribute) {
const mfem::ParFiniteElementSpace &finiteElementSpace, return DomainSchema::template attribute_belongs_to<
const mfem::Vector &trueVector, mean_field::utils::domain::Vacuum>(attribute);
mfem::Vector &localVector }
) {
MFEM_VERIFY( enum class GravityDisplacementForceAction {
trueVector.Size() == finiteElementSpace.GetTrueVSize(), residual,
"The gravity-displacement-force true vector has the wrong size." 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()); localVector.SetSize(finiteElementSpace.GetVSize());
const mfem::Operator *prolongation = finiteElementSpace.GetProlongationMatrix(); const mfem::Operator *prolongation =
finiteElementSpace.GetProlongationMatrix();
if (prolongation != nullptr) { if (prolongation != nullptr) {
prolongation->Mult(trueVector, localVector); prolongation->Mult(trueVector, localVector);
@@ -34,20 +43,17 @@ namespace {
} }
} }
void local_to_true( void local_to_true(const mfem::ParFiniteElementSpace &finiteElementSpace,
const mfem::ParFiniteElementSpace &finiteElementSpace, const mfem::Vector &localVector, mfem::Vector &trueVector) {
const mfem::Vector &localVector,
mfem::Vector &trueVector
) {
MFEM_VERIFY( MFEM_VERIFY(
localVector.Size() == finiteElementSpace.GetVSize(), localVector.Size() == finiteElementSpace.GetVSize(),
"The gravity-displacement-force local vector has the wrong size." "The gravity-displacement-force local vector has the wrong size.");
);
trueVector.SetSize(finiteElementSpace.GetTrueVSize()); 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) { if (prolongation != nullptr) {
prolongation->MultTranspose(localVector, trueVector); prolongation->MultTranspose(localVector, trueVector);
@@ -56,13 +62,10 @@ namespace {
} }
} }
[[nodiscard]] int vector_dof_index( [[nodiscard]] int vector_dof_index(const mfem::Ordering::Type ordering,
const mfem::Ordering::Type ordering, const int scalarDof, const int component,
const int scalarDof,
const int component,
const int scalarDofCount, const int scalarDofCount,
const int dimension const int dimension) {
) {
if (ordering == mfem::Ordering::byNODES) { if (ordering == mfem::Ordering::byNODES) {
return scalarDof + component * scalarDofCount; return scalarDof + component * scalarDofCount;
} }
@@ -71,61 +74,53 @@ namespace {
return scalarDof * dimension + component; return scalarDof * dimension + component;
} }
MFEM_ABORT( MFEM_ABORT("The gravity-displacement-force test space uses an unsupported "
"The gravity-displacement-force test space uses an unsupported " "ordering.");
"ordering."
);
return -1; return -1;
} }
[[nodiscard]] const mfem::IntegrationRule &get_gravity_force_rule( [[nodiscard]] const mfem::IntegrationRule &
const mean_field::fem::FEM &f, get_gravity_force_rule(const mean_field::fem::FEM &f,
const mfem::FiniteElement &densityElement, const mfem::FiniteElement &densityElement,
const mfem::FiniteElement &gravityGradientElement, const mfem::FiniteElement &gravityGradientElement,
const mfem::FiniteElement &displacementElement, const mfem::FiniteElement &displacementElement,
const mfem::ElementTransformation &transformation const mfem::ElementTransformation &transformation) {
) { using DisplacementField =
using DisplacementField = mean_field::field::Field<mean_field::field::Displacement>; mean_field::field::Field<mean_field::field::Displacement>;
MFEM_VERIFY( MFEM_VERIFY(densityElement.GetOrder() ==
densityElement.GetOrder() == mean_field::field::Density::Scalar::familyOrder, mean_field::field::Density::Scalar::familyOrder,
"The gravity-displacement-force density element does not match " "The gravity-displacement-force density element does not match "
"the registered density field." "the registered density field.");
);
MFEM_VERIFY( MFEM_VERIFY(gravityGradientElement.GetOrder() ==
gravityGradientElement.GetOrder() == mean_field::field::Gravity::Flux::familyOrder + 1, mean_field::field::Gravity::Flux::familyOrder + 1,
"The gravity-displacement-force RT element does not match the " "The gravity-displacement-force RT element does not match the "
"registered gravity-gradient field." "registered gravity-gradient field.");
);
MFEM_VERIFY( MFEM_VERIFY(displacementElement.GetOrder() ==
displacementElement.GetOrder() == mean_field::field::Displacement::Vector::familyOrder, mean_field::field::Displacement::Vector::familyOrder,
"The gravity-displacement-force test element does not match the " "The gravity-displacement-force test element does not match the "
"registered displacement field." "registered displacement field.");
);
const mean_field::quadrature::Query query = const mean_field::quadrature::Query query = DisplacementField::make_query<
DisplacementField::make_query<mean_field::field::Displacement::Form::GravityForce>( mean_field::field::Displacement::Form::GravityForce>(
mean_field::quadrature::QuadratureRole::discretization, transformation.OrderW(), {}, mean_field::quadrature::QuadratureRole::discretization,
mean_field::utils::DOMAINS::STELLAR, mean_field::quadrature::MappingKind::general transformation.OrderW(), {}, 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( MFEM_VERIFY(rule.integration_rule != nullptr,
rule.integration_rule != nullptr, "The quadrature policy did not return a gravity-displacement-" "The quadrature policy did not return a gravity-displacement-"
"force integration rule." "force integration rule.");
);
return *rule.integration_rule; return *rule.integration_rule;
} }
void validate_finite_vector( void validate_finite_vector(const mfem::Vector &vector, const char *message) {
const mfem::Vector &vector,
const char *message
) {
for (int index = 0; index < vector.Size(); ++index) { for (int index = 0; index < vector.Size(); ++index) {
MFEM_VERIFY(std::isfinite(vector(index)), message); MFEM_VERIFY(std::isfinite(vector(index)), message);
} }
@@ -133,173 +128,153 @@ namespace {
void validate_common_inputs( void validate_common_inputs(
const mean_field::fem::FEM &f, const mean_field::fem::FEM &f,
const mean_field::mapping::DomainMapperStateless &domainMapper, const mean_field::mapping::DomainMapper &domainMapper,
const mfem::Vector &displacementTrue const mfem::Vector &displacementTrue) {
) { MFEM_VERIFY(f.mesh != nullptr,
MFEM_VERIFY(f.mesh != nullptr, "The gravity-displacement-force kernel requires a mesh."); "The gravity-displacement-force kernel requires a mesh.");
MFEM_VERIFY( MFEM_VERIFY(f.densityFes != nullptr,
f.densityFes != nullptr, "The gravity-displacement-force kernel requires the density " "The gravity-displacement-force kernel requires the density "
"finite-element space." "finite-element space.");
);
MFEM_VERIFY( MFEM_VERIFY(f.gravityFluxFes != nullptr,
f.gravityFluxFes != nullptr, "The gravity-displacement-force kernel requires the gravity-" "The gravity-displacement-force kernel requires the gravity-"
"gradient finite-element space." "gradient finite-element space.");
);
MFEM_VERIFY( MFEM_VERIFY(f.displacementFes != nullptr,
f.displacementFes != nullptr, "The gravity-displacement-force kernel requires the displacement " "The gravity-displacement-force kernel requires the displacement "
"finite-element space." "finite-element space.");
);
MFEM_VERIFY( MFEM_VERIFY(f.compactificationFes != nullptr &&
f.compactificationFes != nullptr && f.compactificationCoordinate != nullptr, f.compactificationCoordinate != nullptr,
"The gravity-displacement-force kernel requires the " "The gravity-displacement-force kernel requires the "
"compactification coordinate." "compactification coordinate.");
);
MFEM_VERIFY( MFEM_VERIFY(f.quadratureFactory != nullptr,
f.quadratureFactory != nullptr, "The gravity-displacement-force kernel requires the quadrature " "The gravity-displacement-force kernel requires the quadrature "
"rule factory." "rule factory.");
);
MFEM_VERIFY( MFEM_VERIFY(displacementTrue.Size() == f.displacementFes->GetTrueVSize(),
displacementTrue.Size() == f.displacementFes->GetTrueVSize(),
"The gravity-displacement-force displacement vector has the " "The gravity-displacement-force displacement vector has the "
"wrong size." "wrong size.");
);
MFEM_VERIFY( MFEM_VERIFY(domainMapper.GetDimension() == f.mesh->Dimension(),
domainMapper.GetDimension() == f.mesh->Dimension(),
"The gravity-displacement-force mapper dimension does not match " "The gravity-displacement-force mapper dimension does not match "
"the mesh dimension." "the mesh dimension.");
);
MFEM_VERIFY( MFEM_VERIFY(f.displacementFes->GetVDim() == f.mesh->Dimension(),
f.displacementFes->GetVDim() == f.mesh->Dimension(),
"The gravity-displacement-force displacement dimension does not " "The gravity-displacement-force displacement dimension does not "
"match the mesh dimension." "match the mesh dimension.");
);
validate_finite_vector( validate_finite_vector(
displacementTrue, "The gravity-displacement-force displacement contains a " displacementTrue,
"non-finite value." "The gravity-displacement-force displacement contains a "
); "non-finite value.");
} }
void validate_density( void validate_density(const mean_field::fem::FEM &f,
const mean_field::fem::FEM &f, const mfem::Vector &density, const char *message) {
const mfem::Vector &density,
const char *message
) {
MFEM_VERIFY(density.Size() == f.densityFes->GetTrueVSize(), message); MFEM_VERIFY(density.Size() == f.densityFes->GetTrueVSize(), message);
validate_finite_vector(density, message); validate_finite_vector(density, message);
} }
void validate_gravity_gradient( void validate_gravity_gradient(const mean_field::fem::FEM &f,
const mean_field::fem::FEM &f,
const mfem::Vector &gravityGradient, const mfem::Vector &gravityGradient,
const char *message const char *message) {
) { MFEM_VERIFY(gravityGradient.Size() == f.gravityFluxFes->GetTrueVSize(),
MFEM_VERIFY(gravityGradient.Size() == f.gravityFluxFes->GetTrueVSize(), message); message);
validate_finite_vector(gravityGradient, message); validate_finite_vector(gravityGradient, message);
} }
void apply_gravity_displacement_force_action( void apply_gravity_displacement_force_action(
const mean_field::fem::FEM &f, const mean_field::fem::FEM &f,
const mean_field::mapping::DomainMapperStateless &domainMapper, const mean_field::mapping::DomainMapper &domainMapper,
const GravityDisplacementForceAction requestedAction, const GravityDisplacementForceAction requestedAction,
const mfem::Vector *baseDensityTrue, const mfem::Vector *baseDensityTrue,
const mfem::Vector *densityVariationTrue, const mfem::Vector *densityVariationTrue,
const mfem::Vector *baseGravityGradientTrue, const mfem::Vector *baseGravityGradientTrue,
const mfem::Vector *gravityGradientVariationTrue, const mfem::Vector *gravityGradientVariationTrue,
const mfem::Vector *displacementVariationTrue, const mfem::Vector *displacementVariationTrue,
const mfem::Vector &displacementTrue, const mfem::Vector &displacementTrue, mfem::Vector &actionTrue) {
mfem::Vector &actionTrue
) {
validate_common_inputs(f, domainMapper, displacementTrue); validate_common_inputs(f, domainMapper, displacementTrue);
const bool needsBaseDensity = requestedAction == GravityDisplacementForceAction::residual || const bool needsBaseDensity =
requestedAction == GravityDisplacementForceAction::residual ||
requestedAction == GravityDisplacementForceAction::gravityGradient || requestedAction == GravityDisplacementForceAction::gravityGradient ||
requestedAction == GravityDisplacementForceAction::displacement || requestedAction == GravityDisplacementForceAction::displacement ||
requestedAction == GravityDisplacementForceAction::complete; requestedAction == GravityDisplacementForceAction::complete;
const bool needsDensityVariation = requestedAction == GravityDisplacementForceAction::density || const bool needsDensityVariation =
requestedAction == GravityDisplacementForceAction::density ||
requestedAction == GravityDisplacementForceAction::complete; requestedAction == GravityDisplacementForceAction::complete;
const bool needsBaseGravityGradient = requestedAction == GravityDisplacementForceAction::residual || const bool needsBaseGravityGradient =
requestedAction == GravityDisplacementForceAction::residual ||
requestedAction == GravityDisplacementForceAction::density || requestedAction == GravityDisplacementForceAction::density ||
requestedAction == GravityDisplacementForceAction::displacement || requestedAction == GravityDisplacementForceAction::displacement ||
requestedAction == GravityDisplacementForceAction::complete; requestedAction == GravityDisplacementForceAction::complete;
const bool needsGravityGradientVariation = requestedAction == GravityDisplacementForceAction::gravityGradient || const bool needsGravityGradientVariation =
requestedAction == GravityDisplacementForceAction::gravityGradient ||
requestedAction == GravityDisplacementForceAction::complete; requestedAction == GravityDisplacementForceAction::complete;
const bool needsDisplacementVariation = requestedAction == GravityDisplacementForceAction::displacement || const bool needsDisplacementVariation =
requestedAction == GravityDisplacementForceAction::displacement ||
requestedAction == GravityDisplacementForceAction::complete; requestedAction == GravityDisplacementForceAction::complete;
if (needsBaseDensity) { if (needsBaseDensity) {
MFEM_VERIFY( MFEM_VERIFY(baseDensityTrue != nullptr,
baseDensityTrue != nullptr, "The gravity-displacement-force action requires a base " "The gravity-displacement-force action requires a base "
"density." "density.");
);
validate_density(f, *baseDensityTrue, "The gravity-displacement-force base density is invalid."); validate_density(f, *baseDensityTrue,
"The gravity-displacement-force base density is invalid.");
} }
if (needsDensityVariation) { if (needsDensityVariation) {
MFEM_VERIFY( MFEM_VERIFY(densityVariationTrue != nullptr,
densityVariationTrue != nullptr, "The gravity-displacement-force action requires a density " "The gravity-displacement-force action requires a density "
"variation." "variation.");
);
validate_density( validate_density(f, *densityVariationTrue,
f, *densityVariationTrue,
"The gravity-displacement-force density variation is " "The gravity-displacement-force density variation is "
"invalid." "invalid.");
);
} }
if (needsBaseGravityGradient) { if (needsBaseGravityGradient) {
MFEM_VERIFY( MFEM_VERIFY(baseGravityGradientTrue != nullptr,
baseGravityGradientTrue != nullptr, "The gravity-displacement-force action requires a base " "The gravity-displacement-force action requires a base "
"gravity gradient." "gravity gradient.");
);
validate_gravity_gradient( validate_gravity_gradient(
f, *baseGravityGradientTrue, f, *baseGravityGradientTrue,
"The gravity-displacement-force base gravity gradient is " "The gravity-displacement-force base gravity gradient is "
"invalid." "invalid.");
);
} }
if (needsGravityGradientVariation) { if (needsGravityGradientVariation) {
MFEM_VERIFY( MFEM_VERIFY(gravityGradientVariationTrue != nullptr,
gravityGradientVariationTrue != nullptr, "The gravity-displacement-force action requires a gravity-" "The gravity-displacement-force action requires a gravity-"
"gradient variation." "gradient variation.");
);
validate_gravity_gradient( validate_gravity_gradient(
f, *gravityGradientVariationTrue, f, *gravityGradientVariationTrue,
"The gravity-displacement-force gravity-gradient variation " "The gravity-displacement-force gravity-gradient variation "
"is invalid." "is invalid.");
);
} }
if (needsDisplacementVariation) { if (needsDisplacementVariation) {
MFEM_VERIFY( MFEM_VERIFY(displacementVariationTrue != nullptr &&
displacementVariationTrue != nullptr && displacementVariationTrue->Size() ==
displacementVariationTrue->Size() == f.displacementFes->GetTrueVSize(), f.displacementFes->GetTrueVSize(),
"The gravity-displacement-force displacement variation is " "The gravity-displacement-force displacement variation is "
"invalid." "invalid.");
);
validate_finite_vector( validate_finite_vector(
*displacementVariationTrue, "The gravity-displacement-force displacement variation " *displacementVariationTrue,
"contains a non-finite value." "The gravity-displacement-force displacement variation "
); "contains a non-finite value.");
} }
mfem::Vector baseDensityLocal; mfem::Vector baseDensityLocal;
@@ -318,23 +293,27 @@ namespace {
} }
if (needsBaseGravityGradient) { if (needsBaseGravityGradient) {
true_to_local(*f.gravityFluxFes, *baseGravityGradientTrue, baseGravityGradientLocal); true_to_local(*f.gravityFluxFes, *baseGravityGradientTrue,
baseGravityGradientLocal);
} }
if (needsGravityGradientVariation) { if (needsGravityGradientVariation) {
true_to_local(*f.gravityFluxFes, *gravityGradientVariationTrue, gravityGradientVariationLocal); true_to_local(*f.gravityFluxFes, *gravityGradientVariationTrue,
gravityGradientVariationLocal);
} }
true_to_local(*f.displacementFes, displacementTrue, displacementLocal); true_to_local(*f.displacementFes, displacementTrue, displacementLocal);
if (needsDisplacementVariation) { if (needsDisplacementVariation) {
true_to_local(*f.displacementFes, *displacementVariationTrue, displacementVariationLocal); true_to_local(*f.displacementFes, *displacementVariationTrue,
displacementVariationLocal);
} }
mfem::Vector localAction(f.displacementFes->GetVSize()); mfem::Vector localAction(f.displacementFes->GetVSize());
localAction = 0.0; localAction = 0.0;
mean_field::mapping::DomainMapperStateless::Workspace workspace(f.mesh->Dimension()); mean_field::mapping::DomainMapper::Workspace workspace(
f.mesh->Dimension());
mfem::Array<int> densityDofs; mfem::Array<int> densityDofs;
mfem::Array<int> gravityGradientDofs; mfem::Array<int> gravityGradientDofs;
@@ -365,31 +344,35 @@ namespace {
mean_field::mapping::VolumeMappingVariation mappingVariation; mean_field::mapping::VolumeMappingVariation mappingVariation;
const int dimension = f.mesh->Dimension(); 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) { 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( MFEM_VERIFY(transformation != nullptr,
transformation != nullptr, "The gravity-displacement-force kernel received a null " "The gravity-displacement-force kernel received a null "
"element transformation." "element transformation.");
);
if (transformation->Attribute == vacuumAttribute) { if (is_vacuum_attribute(transformation->Attribute)) {
continue; continue;
} }
const mfem::FiniteElement &densityElement = *f.densityFes->GetFE(elementId); const mfem::FiniteElement &densityElement = *f.densityFes->GetFE(elementId);
const mfem::FiniteElement &gravityGradientElement = *f.gravityFluxFes->GetFE(elementId); const mfem::FiniteElement &gravityGradientElement =
*f.gravityFluxFes->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 *gravityGradientDofTransformation = mfem::DofTransformation *gravityGradientDofTransformation =
f.gravityFluxFes->GetElementVDofs(elementId, gravityGradientDofs); f.gravityFluxFes->GetElementVDofs(elementId, gravityGradientDofs);
@@ -409,20 +392,24 @@ namespace {
} }
if (needsBaseGravityGradient) { if (needsBaseGravityGradient) {
baseGravityGradientLocal.GetSubVector(gravityGradientDofs, elementBaseGravityGradient); baseGravityGradientLocal.GetSubVector(gravityGradientDofs,
elementBaseGravityGradient);
} }
if (needsGravityGradientVariation) { if (needsGravityGradientVariation) {
gravityGradientVariationLocal.GetSubVector(gravityGradientDofs, elementGravityGradientVariation); gravityGradientVariationLocal.GetSubVector(
gravityGradientDofs, elementGravityGradientVariation);
} }
displacementLocal.GetSubVector(displacementDofs, elementDisplacement); displacementLocal.GetSubVector(displacementDofs, elementDisplacement);
if (needsDisplacementVariation) { if (needsDisplacementVariation) {
displacementVariationLocal.GetSubVector(displacementDofs, elementDisplacementVariation); displacementVariationLocal.GetSubVector(displacementDofs,
elementDisplacementVariation);
} }
f.compactificationCoordinate->GetSubVector(compactificationDofs, elementCompactification); f.compactificationCoordinate->GetSubVector(compactificationDofs,
elementCompactification);
if (densityDofTransformation != nullptr) { if (densityDofTransformation != nullptr) {
if (needsBaseDensity) { if (needsBaseDensity) {
@@ -436,11 +423,13 @@ namespace {
if (gravityGradientDofTransformation != nullptr) { if (gravityGradientDofTransformation != nullptr) {
if (needsBaseGravityGradient) { if (needsBaseGravityGradient) {
gravityGradientDofTransformation->InvTransformPrimal(elementBaseGravityGradient); gravityGradientDofTransformation->InvTransformPrimal(
elementBaseGravityGradient);
} }
if (needsGravityGradientVariation) { if (needsGravityGradientVariation) {
gravityGradientDofTransformation->InvTransformPrimal(elementGravityGradientVariation); gravityGradientDofTransformation->InvTransformPrimal(
elementGravityGradientVariation);
} }
} }
@@ -448,42 +437,42 @@ namespace {
displacementDofTransformation->InvTransformPrimal(elementDisplacement); displacementDofTransformation->InvTransformPrimal(elementDisplacement);
if (needsDisplacementVariation) { if (needsDisplacementVariation) {
displacementDofTransformation->InvTransformPrimal(elementDisplacementVariation); displacementDofTransformation->InvTransformPrimal(
elementDisplacementVariation);
} }
} }
if (compactificationDofTransformation != nullptr) { if (compactificationDofTransformation != nullptr) {
compactificationDofTransformation->InvTransformPrimal(elementCompactification); compactificationDofTransformation->InvTransformPrimal(
elementCompactification);
} }
const mean_field::mapping::ElementDisplacementData displacementData = const mean_field::mapping::ElementDisplacementData displacementData =
mean_field::mapping::ElementDisplacementDataFromElementVDofs(displacementElement, elementDisplacement); mean_field::mapping::ElementDisplacementDataFromElementVDofs(
displacementElement, elementDisplacement);
const mean_field::mapping::ElementCompactificationData compactificationData( const mean_field::mapping::ElementCompactificationData compactificationData(
compactificationElement, elementCompactification compactificationElement, elementCompactification);
);
const mean_field::mapping::ElementMappingData mappingData{ 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 (needsDisplacementVariation) { if (needsDisplacementVariation) {
displacementVariationData.emplace( displacementVariationData.emplace(
mean_field::mapping::ElementDisplacementDataFromElementVDofs( mean_field::mapping::ElementDisplacementDataFromElementVDofs(
displacementElement, elementDisplacementVariation displacementElement, elementDisplacementVariation));
)
);
} }
const int scalarDisplacementDofCount = displacementElement.GetDof(); const int scalarDisplacementDofCount = displacementElement.GetDof();
MFEM_VERIFY( MFEM_VERIFY(displacementDofs.Size() ==
displacementDofs.Size() == scalarDisplacementDofCount * dimension, scalarDisplacementDofCount * dimension,
"The gravity-displacement-force element displacement vector " "The gravity-displacement-force element displacement vector "
"has the wrong size." "has the wrong size.");
);
densityShape.SetSize(densityElement.GetDof()); densityShape.SetSize(densityElement.GetDof());
displacementShape.SetSize(scalarDisplacementDofCount); displacementShape.SetSize(scalarDisplacementDofCount);
@@ -500,38 +489,42 @@ namespace {
elementAction = 0.0; elementAction = 0.0;
const mfem::IntegrationRule &integrationRule = const mfem::IntegrationRule &integrationRule =
get_gravity_force_rule(f, densityElement, gravityGradientElement, displacementElement, *transformation); get_gravity_force_rule(f, densityElement, gravityGradientElement,
displacementElement, *transformation);
for (int quadratureIndex = 0; quadratureIndex < integrationRule.GetNPoints(); ++quadratureIndex) { for (int quadratureIndex = 0;
const mfem::IntegrationPoint &integrationPoint = integrationRule.IntPoint(quadratureIndex); quadratureIndex < integrationRule.GetNPoints(); ++quadratureIndex) {
const mfem::IntegrationPoint &integrationPoint =
integrationRule.IntPoint(quadratureIndex);
transformation->SetIntPoint(&integrationPoint); transformation->SetIntPoint(&integrationPoint);
const mean_field::mapping::MappingStatus mappingStatus = domainMapper.EvaluateVolume( const mean_field::mapping::MappingStatus mappingStatus =
mappingData, *transformation, integrationPoint, workspace, mappingContext domainMapper.EvaluateVolume(mappingData, *transformation,
); integrationPoint, workspace,
mappingContext);
MFEM_VERIFY( MFEM_VERIFY(mappingStatus == mean_field::mapping::MappingStatus::valid,
mappingStatus == mean_field::mapping::MappingStatus::valid,
"Stateless mapping failed in the gravity-displacement-" "Stateless mapping failed in the gravity-displacement-"
"force kernel. Element: " "force kernel. Element: "
<< elementId << ", attribute: " << transformation->Attribute << elementId
<< ", quadrature point: " << quadratureIndex << ", status: " << static_cast<int>(mappingStatus) << ", attribute: " << transformation->Attribute
); << ", quadrature point: " << quadratureIndex
<< ", status: " << static_cast<int>(mappingStatus));
if (needsDisplacementVariation) { if (needsDisplacementVariation) {
const mean_field::mapping::MappingStatus variationStatus = domainMapper.EvaluateVolumeVariation( const mean_field::mapping::MappingStatus variationStatus =
mappingData, *displacementVariationData, *transformation, integrationPoint, mappingContext, domainMapper.EvaluateVolumeVariation(
workspace, mappingVariation mappingData, *displacementVariationData, *transformation,
); integrationPoint, mappingContext, workspace, mappingVariation);
MFEM_VERIFY( MFEM_VERIFY(
variationStatus == mean_field::mapping::MappingStatus::valid, variationStatus == mean_field::mapping::MappingStatus::valid,
"Stateless mapping variation failed in the gravity-" "Stateless mapping variation failed in the gravity-"
"displacement-force kernel. Element: " "displacement-force kernel. Element: "
<< elementId << ", attribute: " << transformation->Attribute << ", quadrature point: " << elementId << ", attribute: " << transformation->Attribute
<< quadratureIndex << ", status: " << static_cast<int>(variationStatus) << ", quadrature point: " << quadratureIndex
); << ", status: " << static_cast<int>(variationStatus));
} }
densityElement.CalcShape(integrationPoint, densityShape); densityElement.CalcShape(integrationPoint, densityShape);
@@ -552,27 +545,28 @@ namespace {
} }
if (needsBaseGravityGradient) { if (needsBaseGravityGradient) {
gravityGradientShape.MultTranspose(elementBaseGravityGradient, baseGravityReferenceValue); gravityGradientShape.MultTranspose(elementBaseGravityGradient,
baseGravityReferenceValue);
mappingContext.mapping.mapping_jacobian.Mult(baseGravityReferenceValue, mappedBaseGravity); mappingContext.mapping.mapping_jacobian.Mult(baseGravityReferenceValue,
mappedBaseGravity);
} else { } else {
mappedBaseGravity = 0.0; mappedBaseGravity = 0.0;
} }
if (needsGravityGradientVariation) { if (needsGravityGradientVariation) {
gravityGradientShape.MultTranspose(elementGravityGradientVariation, gravityVariationReferenceValue); gravityGradientShape.MultTranspose(elementGravityGradientVariation,
gravityVariationReferenceValue);
mappingContext.mapping.mapping_jacobian.Mult( mappingContext.mapping.mapping_jacobian.Mult(
gravityVariationReferenceValue, mappedGravityVariation gravityVariationReferenceValue, mappedGravityVariation);
);
} else { } else {
mappedGravityVariation = 0.0; mappedGravityVariation = 0.0;
} }
if (needsDisplacementVariation) { if (needsDisplacementVariation) {
mappingVariation.mapping.mapping_jacobian_variation.Mult( mappingVariation.mapping.mapping_jacobian_variation.Mult(
baseGravityReferenceValue, mappedGeometryVariation baseGravityReferenceValue, mappedGeometryVariation);
);
} else { } else {
mappedGeometryVariation = 0.0; mappedGeometryVariation = 0.0;
} }
@@ -607,22 +601,24 @@ namespace {
* differentiating the Piola map and physical volume weight, * differentiating the Piola map and physical volume weight,
* but avoids a numerically pointless cancellation. * but avoids a numerically pointless cancellation.
*/ */
const double referenceWeight = integrationPoint.weight * transformation->Weight(); const double referenceWeight =
integrationPoint.weight * transformation->Weight();
forceValue *= referenceWeight; forceValue *= referenceWeight;
for (int scalarDof = 0; scalarDof < scalarDisplacementDofCount; ++scalarDof) { for (int scalarDof = 0; scalarDof < scalarDisplacementDofCount;
++scalarDof) {
for (int component = 0; component < dimension; ++component) { for (int component = 0; component < dimension; ++component) {
const int vectorDof = vector_dof_index( const int vectorDof =
displacementOrdering, scalarDof, component, scalarDisplacementDofCount, dimension vector_dof_index(displacementOrdering, scalarDof, component,
); scalarDisplacementDofCount, dimension);
const double contribution = displacementShape(scalarDof) * forceValue(component); const double contribution =
displacementShape(scalarDof) * forceValue(component);
MFEM_VERIFY( MFEM_VERIFY(std::isfinite(contribution),
std::isfinite(contribution), "The gravity-displacement-force kernel " "The gravity-displacement-force kernel "
"encountered a non-finite contribution." "encountered a non-finite contribution.");
);
elementAction(vectorDof) += contribution; elementAction(vectorDof) += contribution;
} }
@@ -642,77 +638,61 @@ namespace {
namespace mean_field::operators::kernels { namespace mean_field::operators::kernels {
void apply_gravity_displacement_force_residual( void apply_gravity_displacement_force_residual(
const fem::FEM &f, const fem::FEM &f, const mapping::DomainMapper &domainMapper,
const mapping::DomainMapperStateless &domainMapper, const mfem::Vector &densityTrue, const mfem::Vector &gravityGradientTrue,
const mfem::Vector &densityTrue, const mfem::Vector &displacementTrue, mfem::Vector &residualTrue) {
const mfem::Vector &gravityGradientTrue,
const mfem::Vector &displacementTrue,
mfem::Vector &residualTrue
) {
apply_gravity_displacement_force_action( apply_gravity_displacement_force_action(
f, domainMapper, GravityDisplacementForceAction::residual, &densityTrue, nullptr, &gravityGradientTrue, f, domainMapper, GravityDisplacementForceAction::residual, &densityTrue,
nullptr, nullptr, displacementTrue, residualTrue nullptr, &gravityGradientTrue, nullptr, nullptr, displacementTrue,
); residualTrue);
} }
void apply_gravity_displacement_force_density_action( void apply_gravity_displacement_force_density_action(
const fem::FEM &f, const fem::FEM &f, const mapping::DomainMapper &domainMapper,
const mapping::DomainMapperStateless &domainMapper,
const mfem::Vector &densityVariationTrue, const mfem::Vector &densityVariationTrue,
const mfem::Vector &baseGravityGradientTrue, const mfem::Vector &baseGravityGradientTrue,
const mfem::Vector &displacementTrue, const mfem::Vector &displacementTrue, mfem::Vector &actionTrue) {
mfem::Vector &actionTrue
) {
apply_gravity_displacement_force_action( apply_gravity_displacement_force_action(
f, domainMapper, GravityDisplacementForceAction::density, nullptr, &densityVariationTrue, f, domainMapper, GravityDisplacementForceAction::density, nullptr,
&baseGravityGradientTrue, nullptr, nullptr, displacementTrue, actionTrue &densityVariationTrue, &baseGravityGradientTrue, nullptr, nullptr,
); displacementTrue, actionTrue);
} }
void apply_gravity_displacement_force_gradient_action( void apply_gravity_displacement_force_gradient_action(
const fem::FEM &f, const fem::FEM &f, const mapping::DomainMapper &domainMapper,
const mapping::DomainMapperStateless &domainMapper,
const mfem::Vector &baseDensityTrue, const mfem::Vector &baseDensityTrue,
const mfem::Vector &gravityGradientVariationTrue, const mfem::Vector &gravityGradientVariationTrue,
const mfem::Vector &displacementTrue, const mfem::Vector &displacementTrue, mfem::Vector &actionTrue) {
mfem::Vector &actionTrue
) {
apply_gravity_displacement_force_action( apply_gravity_displacement_force_action(
f, domainMapper, GravityDisplacementForceAction::gravityGradient, &baseDensityTrue, nullptr, nullptr, f, domainMapper, GravityDisplacementForceAction::gravityGradient,
&gravityGradientVariationTrue, nullptr, displacementTrue, actionTrue &baseDensityTrue, nullptr, nullptr, &gravityGradientVariationTrue,
); nullptr, displacementTrue, actionTrue);
} }
void apply_gravity_displacement_force_displacement_action( void apply_gravity_displacement_force_displacement_action(
const fem::FEM &f, const fem::FEM &f, const mapping::DomainMapper &domainMapper,
const mapping::DomainMapperStateless &domainMapper,
const mfem::Vector &baseDensityTrue, const mfem::Vector &baseDensityTrue,
const mfem::Vector &baseGravityGradientTrue, const mfem::Vector &baseGravityGradientTrue,
const mfem::Vector &displacementVariationTrue, const mfem::Vector &displacementVariationTrue,
const mfem::Vector &displacementTrue, const mfem::Vector &displacementTrue, mfem::Vector &actionTrue) {
mfem::Vector &actionTrue
) {
apply_gravity_displacement_force_action( apply_gravity_displacement_force_action(
f, domainMapper, GravityDisplacementForceAction::displacement, &baseDensityTrue, nullptr, f, domainMapper, GravityDisplacementForceAction::displacement,
&baseGravityGradientTrue, nullptr, &displacementVariationTrue, displacementTrue, actionTrue &baseDensityTrue, nullptr, &baseGravityGradientTrue, nullptr,
); &displacementVariationTrue, displacementTrue, actionTrue);
} }
void apply_gravity_displacement_force_complete_action( void apply_gravity_displacement_force_complete_action(
const fem::FEM &f, const fem::FEM &f, const mapping::DomainMapper &domainMapper,
const mapping::DomainMapperStateless &domainMapper,
const mfem::Vector &baseDensityTrue, const mfem::Vector &baseDensityTrue,
const mfem::Vector &densityVariationTrue, const mfem::Vector &densityVariationTrue,
const mfem::Vector &baseGravityGradientTrue, const mfem::Vector &baseGravityGradientTrue,
const mfem::Vector &gravityGradientVariationTrue, const mfem::Vector &gravityGradientVariationTrue,
const mfem::Vector &displacementVariationTrue, const mfem::Vector &displacementVariationTrue,
const mfem::Vector &displacementTrue, const mfem::Vector &displacementTrue, mfem::Vector &actionTrue) {
mfem::Vector &actionTrue
) {
apply_gravity_displacement_force_action( apply_gravity_displacement_force_action(
f, domainMapper, GravityDisplacementForceAction::complete, &baseDensityTrue, &densityVariationTrue, f, domainMapper, GravityDisplacementForceAction::complete,
&baseGravityGradientTrue, &gravityGradientVariationTrue, &displacementVariationTrue, displacementTrue, &baseDensityTrue, &densityVariationTrue, &baseGravityGradientTrue,
actionTrue &gravityGradientVariationTrue, &displacementVariationTrue,
); displacementTrue, actionTrue);
} }
} // namespace mean_field::operators::kernels } // namespace mean_field::operators::kernels

File diff suppressed because it is too large Load Diff

View File

@@ -11,16 +11,22 @@ module mean_field;
import :operators.kernels.hydrostatic_equilibrium; import :operators.kernels.hydrostatic_equilibrium;
namespace { namespace {
void true_to_local( using DomainSchema = mean_field::utils::domain::CoreEnvelopeVacuumDomainSchema;
const mfem::ParFiniteElementSpace &finiteElementSpace,
const mfem::Vector &trueVector, [[nodiscard]] bool is_vacuum_attribute(const int attribute) {
mfem::Vector &localVector return DomainSchema::template attribute_belongs_to<
) { mean_field::utils::domain::Vacuum>(attribute);
MFEM_VERIFY(trueVector.Size() == finiteElementSpace.GetTrueVSize(), "True vector has the wrong size."); }
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()); localVector.SetSize(finiteElementSpace.GetVSize());
const mfem::Operator *prolongation = finiteElementSpace.GetProlongationMatrix(); const mfem::Operator *prolongation =
finiteElementSpace.GetProlongationMatrix();
if (prolongation != nullptr) { if (prolongation != nullptr) {
prolongation->Mult(trueVector, localVector); prolongation->Mult(trueVector, localVector);
@@ -29,18 +35,17 @@ namespace {
} }
} }
void local_to_true( void local_to_true(const mfem::ParFiniteElementSpace &finiteElementSpace,
const mfem::ParFiniteElementSpace &finiteElementSpace, const mfem::Vector &localVector, mfem::Vector &trueVector) {
const mfem::Vector &localVector, MFEM_VERIFY(localVector.Size() == finiteElementSpace.GetVSize(),
mfem::Vector &trueVector "Local vector has the wrong size.");
) {
MFEM_VERIFY(localVector.Size() == finiteElementSpace.GetVSize(), "Local vector has the wrong size.");
trueVector.SetSize(finiteElementSpace.GetTrueVSize()); 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) { if (prolongation != nullptr) {
prolongation->MultTranspose(localVector, trueVector); prolongation->MultTranspose(localVector, trueVector);
@@ -51,100 +56,93 @@ namespace {
void validate_fem( void validate_fem(
const mean_field::fem::FEM &f, const mean_field::fem::FEM &f,
const mean_field::mapping::DomainMapperStateless &domainMapper const mean_field::mapping::DomainMapper &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( MFEM_VERIFY(f.enthalpyFes != nullptr, "The hydrostatic kernel requires the "
f.enthalpyFes != nullptr, "The hydrostatic kernel requires the " "enthalpy finite-element space.");
"enthalpy finite-element space."
);
MFEM_VERIFY( MFEM_VERIFY(f.gravityPotentialFes != nullptr,
f.gravityPotentialFes != nullptr, "The hydrostatic kernel requires the " "The hydrostatic kernel requires the "
"gravity-potential finite-element space." "gravity-potential finite-element space.");
);
MFEM_VERIFY( MFEM_VERIFY(f.displacementFes != nullptr,
f.displacementFes != nullptr, "The hydrostatic kernel requires the " "The hydrostatic kernel requires the "
"displacement finite-element space." "displacement finite-element space.");
);
MFEM_VERIFY( MFEM_VERIFY(f.compactificationFes != nullptr,
f.compactificationFes != nullptr, "The hydrostatic kernel requires the " "The hydrostatic kernel requires the "
"compactification finite-element space." "compactification finite-element space.");
);
MFEM_VERIFY( MFEM_VERIFY(f.compactificationCoordinate != nullptr,
f.compactificationCoordinate != nullptr, "The hydrostatic kernel requires the " "The hydrostatic kernel requires the "
"compactification coordinate." "compactification coordinate.");
);
MFEM_VERIFY( MFEM_VERIFY(f.quadratureFactory != nullptr,
f.quadratureFactory != nullptr, "The hydrostatic kernel requires the " "The hydrostatic kernel requires the "
"quadrature-rule factory." "quadrature-rule factory.");
);
MFEM_VERIFY( MFEM_VERIFY(f.mesh->Dimension() == 3,
f.mesh->Dimension() == 3, "The rigid-rotation hydrostatic kernel " "The rigid-rotation hydrostatic kernel "
"currently requires a three-dimensional mesh." "currently requires a three-dimensional mesh.");
);
MFEM_VERIFY( MFEM_VERIFY(domainMapper.GetDimension() == f.mesh->Dimension(),
domainMapper.GetDimension() == f.mesh->Dimension(), "The domain-mapper dimension does not match " "The domain-mapper dimension does not match "
"the mesh dimension." "the mesh dimension.");
);
} }
const mfem::IntegrationRule &get_hydrostatic_rule( const mfem::IntegrationRule &
const mean_field::fem::FEM &f, get_hydrostatic_rule(const mean_field::fem::FEM &f,
const mfem::FiniteElement &enthalpyElement, const mfem::FiniteElement &enthalpyElement,
const mfem::FiniteElement &potentialElement, const mfem::FiniteElement &potentialElement,
const mfem::ElementTransformation &transformation 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( MFEM_VERIFY(enthalpyElement.GetOrder() ==
enthalpyElement.GetOrder() == mean_field::field::Enthalpy::Scalar::familyOrder, mean_field::field::Enthalpy::Scalar::familyOrder,
"The hydrostatic test element does not match " "The hydrostatic test element does not match "
"the registered enthalpy field." "the registered enthalpy field.");
);
MFEM_VERIFY( MFEM_VERIFY(potentialElement.GetOrder() ==
potentialElement.GetOrder() == mean_field::field::Gravity::Potential::familyOrder, mean_field::field::Gravity::Potential::familyOrder,
"The hydrostatic potential element does not " "The hydrostatic potential element does not "
"match the registered gravity-potential field." "match the registered gravity-potential field.");
);
const auto enthalpyQuery = EnthalpyField::make_query<mean_field::field::Enthalpy::Form::EquilibriumEnthalpy>( const auto enthalpyQuery = EnthalpyField::make_query<
mean_field::quadrature::QuadratureRole::discretization, transformation.OrderW(), {}, mean_field::field::Enthalpy::Form::EquilibriumEnthalpy>(
mean_field::utils::DOMAINS::STELLAR, mean_field::quadrature::MappingKind::general 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>( const auto gravityQuery = EnthalpyField::make_query<
mean_field::quadrature::QuadratureRole::discretization, transformation.OrderW(), {}, mean_field::field::Enthalpy::Form::EquilibriumGravity>(
mean_field::utils::DOMAINS::STELLAR, mean_field::quadrature::MappingKind::general 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>( const auto rotationQuery = EnthalpyField::make_query<
mean_field::quadrature::QuadratureRole::discretization, transformation.OrderW(), std::array<int, 1>{2}, mean_field::field::Enthalpy::Form::EquilibriumRotation>(
mean_field::utils::DOMAINS::STELLAR, mean_field::quadrature::MappingKind::general 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>( const auto constantQuery = EnthalpyField::make_query<
mean_field::quadrature::QuadratureRole::discretization, transformation.OrderW(), {}, mean_field::field::Enthalpy::Form::EquilibriumConstant>(
mean_field::utils::DOMAINS::STELLAR, mean_field::quadrature::MappingKind::general mean_field::quadrature::QuadratureRole::discretization,
); transformation.OrderW(), {}, mean_field::utils::DOMAINS::STELLAR,
mean_field::quadrature::MappingKind::general);
int integrationOrder = 0; int integrationOrder = 0;
const auto update_order = [&f, &transformation, &integrationOrder](const mean_field::quadrature::Query &query) { const auto update_order = [&f, &transformation, &integrationOrder](
const auto rule = f.quadratureFactory->get(query, transformation.GetGeometryType()); const mean_field::quadrature::Query &query) {
const auto rule =
f.quadratureFactory->get(query, transformation.GetGeometryType());
MFEM_VERIFY( MFEM_VERIFY(rule.integration_rule != nullptr,
rule.integration_rule != nullptr, "The quadrature policy did not return " "The quadrature policy did not return "
"a hydrostatic-equilibrium rule." "a hydrostatic-equilibrium rule.");
);
integrationOrder = std::max(integrationOrder, rule.resolution.order); integrationOrder = std::max(integrationOrder, rule.resolution.order);
}; };
@@ -175,74 +173,66 @@ namespace {
void assemble_hydrostatic_form( void assemble_hydrostatic_form(
const mean_field::fem::FEM &f, const mean_field::fem::FEM &f,
const mean_field::mapping::DomainMapperStateless &domainMapper, const mean_field::mapping::DomainMapper &domainMapper,
const mfem::Vector &displacementTrue, const mfem::Vector &displacementTrue,
const HydrostaticAssemblyRequest &request, const HydrostaticAssemblyRequest &request, mfem::Vector &result) {
mfem::Vector &result
) {
validate_fem(f, domainMapper); validate_fem(f, domainMapper);
MFEM_VERIFY( MFEM_VERIFY(displacementTrue.Size() == f.displacementFes->GetTrueVSize(),
displacementTrue.Size() == f.displacementFes->GetTrueVSize(), "The hydrostatic displacement vector has " "The hydrostatic displacement vector has "
"the wrong size." "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) { if (requiresBaseState) {
MFEM_VERIFY( MFEM_VERIFY(request.rotation != nullptr,
request.rotation != nullptr, "The hydrostatic residual or geometry " "The hydrostatic residual or geometry "
"action requires the rotation model." "action requires the rotation model.");
);
MFEM_VERIFY( MFEM_VERIFY(request.baseEnthalpyTrue != nullptr,
request.baseEnthalpyTrue != nullptr, "The hydrostatic residual or geometry " "The hydrostatic residual or geometry "
"action requires the base enthalpy." "action requires the base enthalpy.");
);
MFEM_VERIFY( MFEM_VERIFY(request.basePotentialTrue != nullptr,
request.basePotentialTrue != nullptr, "The hydrostatic residual or geometry " "The hydrostatic residual or geometry "
"action requires the base potential." "action requires the base potential.");
);
} }
if (request.baseEnthalpyTrue != nullptr) { if (request.baseEnthalpyTrue != nullptr) {
MFEM_VERIFY( MFEM_VERIFY(request.baseEnthalpyTrue->Size() ==
request.baseEnthalpyTrue->Size() == f.enthalpyFes->GetTrueVSize(), f.enthalpyFes->GetTrueVSize(),
"The base enthalpy vector has the wrong size." "The base enthalpy vector has the wrong size.");
);
} }
if (request.basePotentialTrue != nullptr) { if (request.basePotentialTrue != nullptr) {
MFEM_VERIFY( MFEM_VERIFY(request.basePotentialTrue->Size() ==
request.basePotentialTrue->Size() == f.gravityPotentialFes->GetTrueVSize(), f.gravityPotentialFes->GetTrueVSize(),
"The base potential vector has the wrong size." "The base potential vector has the wrong size.");
);
} }
if (request.enthalpyVariationTrue != nullptr) { if (request.enthalpyVariationTrue != nullptr) {
MFEM_VERIFY( MFEM_VERIFY(request.enthalpyVariationTrue->Size() ==
request.enthalpyVariationTrue->Size() == f.enthalpyFes->GetTrueVSize(), f.enthalpyFes->GetTrueVSize(),
"The enthalpy variation has the wrong size." "The enthalpy variation has the wrong size.");
);
} }
if (request.potentialVariationTrue != nullptr) { if (request.potentialVariationTrue != nullptr) {
MFEM_VERIFY( MFEM_VERIFY(request.potentialVariationTrue->Size() ==
request.potentialVariationTrue->Size() == f.gravityPotentialFes->GetTrueVSize(), f.gravityPotentialFes->GetTrueVSize(),
"The potential variation has the wrong size." "The potential variation has the wrong size.");
);
} }
if (request.displacementVariationTrue != nullptr) { if (request.displacementVariationTrue != nullptr) {
MFEM_VERIFY( MFEM_VERIFY(request.displacementVariationTrue->Size() ==
request.displacementVariationTrue->Size() == f.displacementFes->GetTrueVSize(), f.displacementFes->GetTrueVSize(),
"The displacement variation has the wrong size." "The displacement variation has the wrong size.");
);
} }
mfem::Vector displacementLocal; mfem::Vector displacementLocal;
@@ -259,26 +249,31 @@ namespace {
} }
if (request.basePotentialTrue != nullptr) { if (request.basePotentialTrue != nullptr) {
true_to_local(*f.gravityPotentialFes, *request.basePotentialTrue, basePotentialLocal); true_to_local(*f.gravityPotentialFes, *request.basePotentialTrue,
basePotentialLocal);
} }
if (request.enthalpyVariationTrue != nullptr) { if (request.enthalpyVariationTrue != nullptr) {
true_to_local(*f.enthalpyFes, *request.enthalpyVariationTrue, enthalpyVariationLocal); true_to_local(*f.enthalpyFes, *request.enthalpyVariationTrue,
enthalpyVariationLocal);
} }
if (request.potentialVariationTrue != nullptr) { if (request.potentialVariationTrue != nullptr) {
true_to_local(*f.gravityPotentialFes, *request.potentialVariationTrue, potentialVariationLocal); true_to_local(*f.gravityPotentialFes, *request.potentialVariationTrue,
potentialVariationLocal);
} }
if (request.displacementVariationTrue != nullptr) { 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()); mfem::Vector localResult(f.enthalpyFes->GetVSize());
localResult = 0.0; localResult = 0.0;
mean_field::mapping::DomainMapperStateless::Workspace workspace(f.mesh->Dimension()); mean_field::mapping::DomainMapper::Workspace workspace(
f.mesh->Dimension());
mfem::Array<int> enthalpyDofs; mfem::Array<int> enthalpyDofs;
mfem::Array<int> potentialDofs; mfem::Array<int> potentialDofs;
@@ -297,29 +292,32 @@ namespace {
mfem::Vector enthalpyShape; mfem::Vector enthalpyShape;
mfem::Vector potentialShape; mfem::Vector potentialShape;
const int vacuumAttribute = domainMapper.GetVacuumElementAttribute();
for (int elementId = 0; elementId < f.mesh->GetNE(); ++elementId) { 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( MFEM_VERIFY(transformation != nullptr,
transformation != nullptr, "The hydrostatic kernel received a null " "The hydrostatic kernel received a null "
"element transformation." "element transformation.");
);
if (transformation->Attribute == vacuumAttribute) { if (is_vacuum_attribute(transformation->Attribute)) {
continue; 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 = mfem::DofTransformation *potentialDofTransformation =
f.gravityPotentialFes->GetElementDofs(elementId, potentialDofs); f.gravityPotentialFes->GetElementDofs(elementId, potentialDofs);
@@ -332,7 +330,8 @@ namespace {
displacementLocal.GetSubVector(displacementDofs, elementDisplacement); displacementLocal.GetSubVector(displacementDofs, elementDisplacement);
f.compactificationCoordinate->GetSubVector(compactificationDofs, elementCompactification); f.compactificationCoordinate->GetSubVector(compactificationDofs,
elementCompactification);
if (request.baseEnthalpyTrue != nullptr) { if (request.baseEnthalpyTrue != nullptr) {
baseEnthalpyLocal.GetSubVector(enthalpyDofs, elementBaseEnthalpy); baseEnthalpyLocal.GetSubVector(enthalpyDofs, elementBaseEnthalpy);
@@ -343,15 +342,18 @@ namespace {
} }
if (request.enthalpyVariationTrue != nullptr) { if (request.enthalpyVariationTrue != nullptr) {
enthalpyVariationLocal.GetSubVector(enthalpyDofs, elementEnthalpyVariation); enthalpyVariationLocal.GetSubVector(enthalpyDofs,
elementEnthalpyVariation);
} }
if (request.potentialVariationTrue != nullptr) { if (request.potentialVariationTrue != nullptr) {
potentialVariationLocal.GetSubVector(potentialDofs, elementPotentialVariation); potentialVariationLocal.GetSubVector(potentialDofs,
elementPotentialVariation);
} }
if (request.displacementVariationTrue != nullptr) { if (request.displacementVariationTrue != nullptr) {
displacementVariationLocal.GetSubVector(displacementDofs, elementDisplacementVariation); displacementVariationLocal.GetSubVector(displacementDofs,
elementDisplacementVariation);
} }
if (enthalpyDofTransformation != nullptr) { if (enthalpyDofTransformation != nullptr) {
@@ -370,7 +372,8 @@ namespace {
} }
if (request.potentialVariationTrue != nullptr) { if (request.potentialVariationTrue != nullptr) {
potentialDofTransformation->InvTransformPrimal(elementPotentialVariation); potentialDofTransformation->InvTransformPrimal(
elementPotentialVariation);
} }
} }
@@ -378,33 +381,34 @@ namespace {
displacementDofTransformation->InvTransformPrimal(elementDisplacement); displacementDofTransformation->InvTransformPrimal(elementDisplacement);
if (request.displacementVariationTrue != nullptr) { if (request.displacementVariationTrue != nullptr) {
displacementDofTransformation->InvTransformPrimal(elementDisplacementVariation); displacementDofTransformation->InvTransformPrimal(
elementDisplacementVariation);
} }
} }
if (compactificationDofTransformation != nullptr) { if (compactificationDofTransformation != nullptr) {
compactificationDofTransformation->InvTransformPrimal(elementCompactification); compactificationDofTransformation->InvTransformPrimal(
elementCompactification);
} }
const mean_field::mapping::ElementDisplacementData displacementData = const mean_field::mapping::ElementDisplacementData displacementData =
mean_field::mapping::ElementDisplacementDataFromElementVDofs(displacementElement, elementDisplacement); mean_field::mapping::ElementDisplacementDataFromElementVDofs(
displacementElement, elementDisplacement);
const mean_field::mapping::ElementCompactificationData compactificationData( const mean_field::mapping::ElementCompactificationData compactificationData(
compactificationElement, elementCompactification compactificationElement, elementCompactification);
);
const mean_field::mapping::ElementMappingData mappingData{ 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) { if (request.displacementVariationTrue != nullptr) {
displacementVariationData.emplace( displacementVariationData.emplace(
mean_field::mapping::ElementDisplacementDataFromElementVDofs( mean_field::mapping::ElementDisplacementDataFromElementVDofs(
displacementElement, elementDisplacementVariation displacementElement, elementDisplacementVariation));
)
);
} }
elementResult.SetSize(enthalpyElement.GetDof()); elementResult.SetSize(enthalpyElement.GetDof());
@@ -415,27 +419,28 @@ namespace {
potentialShape.SetSize(potentialElement.GetDof()); potentialShape.SetSize(potentialElement.GetDof());
const mfem::IntegrationRule &integrationRule = const mfem::IntegrationRule &integrationRule = get_hydrostatic_rule(
get_hydrostatic_rule(f, enthalpyElement, potentialElement, *transformation); f, enthalpyElement, potentialElement, *transformation);
for (int quadraturePoint = 0; quadraturePoint < integrationRule.GetNPoints(); ++quadraturePoint) { for (int quadraturePoint = 0;
const mfem::IntegrationPoint &integrationPoint = integrationRule.IntPoint(quadraturePoint); quadraturePoint < integrationRule.GetNPoints(); ++quadraturePoint) {
const mfem::IntegrationPoint &integrationPoint =
integrationRule.IntPoint(quadraturePoint);
transformation->SetIntPoint(&integrationPoint); transformation->SetIntPoint(&integrationPoint);
mean_field::mapping::VolumeMappingContext mappingContext; mean_field::mapping::VolumeMappingContext mappingContext;
const mean_field::mapping::MappingStatus mappingStatus = domainMapper.EvaluateVolume( const mean_field::mapping::MappingStatus mappingStatus =
mappingData, *transformation, integrationPoint, workspace, mappingContext domainMapper.EvaluateVolume(mappingData, *transformation,
); integrationPoint, workspace,
mappingContext);
MFEM_VERIFY( MFEM_VERIFY(mappingStatus == mean_field::mapping::MappingStatus::valid,
mappingStatus == mean_field::mapping::MappingStatus::valid,
"The base mapping is invalid in the " "The base mapping is invalid in the "
"hydrostatic kernel. Element: " "hydrostatic kernel. Element: "
<< elementId << ", quadrature point: " << quadraturePoint << elementId << ", quadrature point: " << quadraturePoint
<< ", status: " << static_cast<int>(mappingStatus) << ", status: " << static_cast<int>(mappingStatus));
);
enthalpyElement.CalcShape(integrationPoint, enthalpyShape); enthalpyElement.CalcShape(integrationPoint, enthalpyShape);
@@ -448,14 +453,16 @@ namespace {
const double potentialValue = elementBasePotential * potentialShape; const double potentialValue = elementBasePotential * potentialShape;
const double rotationPotential = const double rotationPotential = request.rotation->potential(
request.rotation->potential(mappingContext.mapping.physical_position); mappingContext.mapping.physical_position);
baseIntegrand = enthalpyValue + potentialValue - rotationPotential - request.bernoulliConstant; baseIntegrand = enthalpyValue + potentialValue - rotationPotential -
request.bernoulliConstant;
} }
if (request.buildResidual) { if (request.buildResidual) {
elementResult.Add(mappingContext.quadrature.weight * baseIntegrand, enthalpyShape); elementResult.Add(mappingContext.quadrature.weight * baseIntegrand,
enthalpyShape);
continue; continue;
} }
@@ -470,27 +477,29 @@ namespace {
materialVariation += elementPotentialVariation * potentialShape; materialVariation += elementPotentialVariation * potentialShape;
} }
double weightedVariation = mappingContext.quadrature.weight * materialVariation; double weightedVariation =
mappingContext.quadrature.weight * materialVariation;
if (request.displacementVariationTrue != nullptr) { if (request.displacementVariationTrue != nullptr) {
mean_field::mapping::VolumeMappingVariation mappingVariation; mean_field::mapping::VolumeMappingVariation mappingVariation;
const mean_field::mapping::MappingStatus variationStatus = domainMapper.EvaluateVolumeVariation( const mean_field::mapping::MappingStatus variationStatus =
mappingData, *displacementVariationData, *transformation, integrationPoint, mappingContext, domainMapper.EvaluateVolumeVariation(
workspace, mappingVariation mappingData, *displacementVariationData, *transformation,
); integrationPoint, mappingContext, workspace, mappingVariation);
MFEM_VERIFY( MFEM_VERIFY(variationStatus ==
variationStatus == mean_field::mapping::MappingStatus::valid, mean_field::mapping::MappingStatus::valid,
"The mapping variation is invalid " "The mapping variation is invalid "
"in the hydrostatic kernel." "in the hydrostatic kernel.");
);
const double rotationVariation = request.rotation->potential_directional_derivative( const double rotationVariation =
mappingContext.mapping.physical_position, mappingVariation.mapping.physical_position_variation request.rotation->potential_directional_derivative(
); mappingContext.mapping.physical_position,
mappingVariation.mapping.physical_position_variation);
weightedVariation += baseIntegrand * mappingVariation.weight_variation - weightedVariation +=
baseIntegrand * mappingVariation.weight_variation -
rotationVariation * mappingContext.quadrature.weight; rotationVariation * mappingContext.quadrature.weight;
} }
@@ -510,15 +519,10 @@ namespace {
namespace mean_field::operators::kernels { namespace mean_field::operators::kernels {
void apply_hydrostatic_equilibrium( void apply_hydrostatic_equilibrium(
const fem::FEM &f, const fem::FEM &f, const mapping::DomainMapper &domainMapper,
const mapping::DomainMapperStateless &domainMapper, const physics::RigidRotation &rotation, const mfem::Vector &enthalpyTrue,
const physics::RigidRotation &rotation, const mfem::Vector &potentialTrue, const mfem::Vector &displacementTrue,
const mfem::Vector &enthalpyTrue, const double bernoulliConstant, mfem::Vector &residual) {
const mfem::Vector &potentialTrue,
const mfem::Vector &displacementTrue,
const double bernoulliConstant,
mfem::Vector &residual
) {
HydrostaticAssemblyRequest request; HydrostaticAssemblyRequest request;
request.rotation = &rotation; request.rotation = &rotation;
@@ -527,16 +531,14 @@ namespace mean_field::operators::kernels {
request.bernoulliConstant = bernoulliConstant; request.bernoulliConstant = bernoulliConstant;
request.buildResidual = true; 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( void apply_hydrostatic_equilibrium_enthalpy_action(
const fem::FEM &f, const fem::FEM &f, const mapping::DomainMapper &domainMapper,
const mapping::DomainMapperStateless &domainMapper,
const mfem::Vector &enthalpyVariationTrue, const mfem::Vector &enthalpyVariationTrue,
const mfem::Vector &displacementTrue, const mfem::Vector &displacementTrue, mfem::Vector &action) {
mfem::Vector &action
) {
HydrostaticAssemblyRequest request; HydrostaticAssemblyRequest request;
request.enthalpyVariationTrue = &enthalpyVariationTrue; request.enthalpyVariationTrue = &enthalpyVariationTrue;
@@ -545,12 +547,9 @@ namespace mean_field::operators::kernels {
} }
void apply_hydrostatic_equilibrium_potential_action( void apply_hydrostatic_equilibrium_potential_action(
const fem::FEM &f, const fem::FEM &f, const mapping::DomainMapper &domainMapper,
const mapping::DomainMapperStateless &domainMapper,
const mfem::Vector &potentialVariationTrue, const mfem::Vector &potentialVariationTrue,
const mfem::Vector &displacementTrue, const mfem::Vector &displacementTrue, mfem::Vector &action) {
mfem::Vector &action
) {
HydrostaticAssemblyRequest request; HydrostaticAssemblyRequest request;
request.potentialVariationTrue = &potentialVariationTrue; request.potentialVariationTrue = &potentialVariationTrue;
@@ -559,12 +558,9 @@ namespace mean_field::operators::kernels {
} }
void apply_hydrostatic_equilibrium_constant_action( void apply_hydrostatic_equilibrium_constant_action(
const fem::FEM &f, const fem::FEM &f, const mapping::DomainMapper &domainMapper,
const mapping::DomainMapperStateless &domainMapper, const double constantVariation, const mfem::Vector &displacementTrue,
const double constantVariation, mfem::Vector &action) {
const mfem::Vector &displacementTrue,
mfem::Vector &action
) {
HydrostaticAssemblyRequest request; HydrostaticAssemblyRequest request;
request.constantVariation = constantVariation; request.constantVariation = constantVariation;
@@ -573,16 +569,12 @@ namespace mean_field::operators::kernels {
} }
void apply_hydrostatic_equilibrium_displacement_action( void apply_hydrostatic_equilibrium_displacement_action(
const fem::FEM &f, const fem::FEM &f, const mapping::DomainMapper &domainMapper,
const mapping::DomainMapperStateless &domainMapper,
const physics::RigidRotation &rotation, const physics::RigidRotation &rotation,
const mfem::Vector &baseEnthalpyTrue, const mfem::Vector &baseEnthalpyTrue, const mfem::Vector &basePotentialTrue,
const mfem::Vector &basePotentialTrue,
const mfem::Vector &baseDisplacementTrue, const mfem::Vector &baseDisplacementTrue,
const double baseBernoulliConstant, const double baseBernoulliConstant,
const mfem::Vector &displacementVariationTrue, const mfem::Vector &displacementVariationTrue, mfem::Vector &action) {
mfem::Vector &action
) {
HydrostaticAssemblyRequest request; HydrostaticAssemblyRequest request;
request.rotation = &rotation; request.rotation = &rotation;
@@ -591,23 +583,19 @@ namespace mean_field::operators::kernels {
request.displacementVariationTrue = &displacementVariationTrue; request.displacementVariationTrue = &displacementVariationTrue;
request.bernoulliConstant = baseBernoulliConstant; request.bernoulliConstant = baseBernoulliConstant;
assemble_hydrostatic_form(f, domainMapper, baseDisplacementTrue, request, action); assemble_hydrostatic_form(f, domainMapper, baseDisplacementTrue, request,
action);
} }
void apply_hydrostatic_equilibrium_action( void apply_hydrostatic_equilibrium_action(
const fem::FEM &f, const fem::FEM &f, const mapping::DomainMapper &domainMapper,
const mapping::DomainMapperStateless &domainMapper,
const physics::RigidRotation &rotation, const physics::RigidRotation &rotation,
const mfem::Vector &baseEnthalpyTrue, const mfem::Vector &baseEnthalpyTrue, const mfem::Vector &basePotentialTrue,
const mfem::Vector &basePotentialTrue,
const mfem::Vector &baseDisplacementTrue, const mfem::Vector &baseDisplacementTrue,
const double baseBernoulliConstant, const double baseBernoulliConstant,
const mfem::Vector &enthalpyVariationTrue, const mfem::Vector &enthalpyVariationTrue,
const mfem::Vector &potentialVariationTrue, const mfem::Vector &potentialVariationTrue, const double constantVariation,
const double constantVariation, const mfem::Vector &displacementVariationTrue, mfem::Vector &action) {
const mfem::Vector &displacementVariationTrue,
mfem::Vector &action
) {
HydrostaticAssemblyRequest request; HydrostaticAssemblyRequest request;
request.rotation = &rotation; request.rotation = &rotation;
@@ -619,6 +607,7 @@ namespace mean_field::operators::kernels {
request.bernoulliConstant = baseBernoulliConstant; request.bernoulliConstant = baseBernoulliConstant;
request.constantVariation = constantVariation; request.constantVariation = constantVariation;
assemble_hydrostatic_form(f, domainMapper, baseDisplacementTrue, request, action); assemble_hydrostatic_form(f, domainMapper, baseDisplacementTrue, request,
action);
} }
} // namespace mean_field::operators::kernels } // namespace mean_field::operators::kernels

View File

@@ -12,20 +12,24 @@ module mean_field;
import :operators.kernels.pressure_force; import :operators.kernels.pressure_force;
namespace { namespace {
using DomainSchema = mean_field::utils::domain::CoreEnvelopeVacuumDomainSchema;
[[nodiscard]] bool is_vacuum_attribute(const int attribute) {
return DomainSchema::template attribute_belongs_to<
mean_field::utils::domain::Vacuum>(attribute);
}
enum class PressureForceAction { residual, enthalpy, displacement }; enum class PressureForceAction { residual, enthalpy, displacement };
void true_to_local( void true_to_local(const mfem::ParFiniteElementSpace &finiteElementSpace,
const mfem::ParFiniteElementSpace &finiteElementSpace, const mfem::Vector &trueVector, mfem::Vector &localVector) {
const mfem::Vector &trueVector, MFEM_VERIFY(trueVector.Size() == finiteElementSpace.GetTrueVSize(),
mfem::Vector &localVector "The pressure-force true vector has the wrong size.");
) {
MFEM_VERIFY(
trueVector.Size() == finiteElementSpace.GetTrueVSize(), "The pressure-force true vector has the wrong size."
);
localVector.SetSize(finiteElementSpace.GetVSize()); localVector.SetSize(finiteElementSpace.GetVSize());
const mfem::Operator *prolongation = finiteElementSpace.GetProlongationMatrix(); const mfem::Operator *prolongation =
finiteElementSpace.GetProlongationMatrix();
if (prolongation != nullptr) { if (prolongation != nullptr) {
prolongation->Mult(trueVector, localVector); prolongation->Mult(trueVector, localVector);
@@ -34,19 +38,16 @@ namespace {
} }
} }
void local_to_true( void local_to_true(const mfem::ParFiniteElementSpace &finiteElementSpace,
const mfem::ParFiniteElementSpace &finiteElementSpace, const mfem::Vector &localVector, mfem::Vector &trueVector) {
const mfem::Vector &localVector, MFEM_VERIFY(localVector.Size() == finiteElementSpace.GetVSize(),
mfem::Vector &trueVector "The pressure-force local vector has the wrong size.");
) {
MFEM_VERIFY(
localVector.Size() == finiteElementSpace.GetVSize(), "The pressure-force local vector has the wrong size."
);
trueVector.SetSize(finiteElementSpace.GetTrueVSize()); 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) { if (prolongation != nullptr) {
prolongation->MultTranspose(localVector, trueVector); prolongation->MultTranspose(localVector, trueVector);
@@ -55,13 +56,10 @@ namespace {
} }
} }
[[nodiscard]] int vector_dof_index( [[nodiscard]] int vector_dof_index(const mfem::Ordering::Type ordering,
const mfem::Ordering::Type ordering, const int scalarDof, const int component,
const int scalarDof,
const int component,
const int scalarDofCount, const int scalarDofCount,
const int dimension const int dimension) {
) {
if (ordering == mfem::Ordering::byNODES) { if (ordering == mfem::Ordering::byNODES) {
return scalarDof + component * scalarDofCount; return scalarDof + component * scalarDofCount;
} }
@@ -74,7 +72,8 @@ namespace {
return -1; return -1;
} }
[[nodiscard]] int get_pressure_extra_order(const mean_field::eos::Polytrope &barotrope) { [[nodiscard]] int
get_pressure_extra_order(const mean_field::eos::Polytrope &barotrope) {
/* /*
* Pressure has the enthalpy dependence * Pressure has the enthalpy dependence
* *
@@ -85,108 +84,92 @@ namespace {
* contribution is therefore n times that order. * contribution is therefore n times that order.
*/ */
const double extraOrder = 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( MFEM_VERIFY(std::isfinite(extraOrder) && extraOrder >= 0.0 &&
std::isfinite(extraOrder) && extraOrder >= 0.0 && extraOrder <=
extraOrder <= static_cast<double>(std::numeric_limits<int>::max()), static_cast<double>(std::numeric_limits<int>::max()),
"The pressure EOS effective polynomial order is invalid." "The pressure EOS effective polynomial order is invalid.");
);
return static_cast<int>(std::ceil(extraOrder)); return static_cast<int>(std::ceil(extraOrder));
} }
[[nodiscard]] const mfem::IntegrationRule &get_pressure_force_rule( [[nodiscard]] const mfem::IntegrationRule &
const mean_field::fem::FEM &f, get_pressure_force_rule(const mean_field::fem::FEM &f,
const mean_field::eos::Polytrope &barotrope, const mean_field::eos::Polytrope &barotrope,
const mfem::FiniteElement &enthalpyElement, const mfem::FiniteElement &enthalpyElement,
const mfem::FiniteElement &displacementElement, const mfem::FiniteElement &displacementElement,
const mfem::ElementTransformation &transformation 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( MFEM_VERIFY(enthalpyElement.GetOrder() ==
enthalpyElement.GetOrder() == mean_field::field::Enthalpy::Scalar::familyOrder, mean_field::field::Enthalpy::Scalar::familyOrder,
"The pressure-force enthalpy element does not match the " "The pressure-force enthalpy element does not match the "
"registered enthalpy field." "registered enthalpy field.");
);
MFEM_VERIFY( MFEM_VERIFY(displacementElement.GetOrder() ==
displacementElement.GetOrder() == mean_field::field::Displacement::Vector::familyOrder, mean_field::field::Displacement::Vector::familyOrder,
"The pressure-force test element does not match the " "The pressure-force test element does not match the "
"registered displacement field." "registered displacement field.");
);
const mean_field::quadrature::Query query = const mean_field::quadrature::Query query = EnthalpyField::make_query<
EnthalpyField::make_query<mean_field::field::Enthalpy::Form::PressureForce>( mean_field::field::Enthalpy::Form::PressureForce>(
mean_field::quadrature::QuadratureRole::discretization, transformation.OrderW(), mean_field::quadrature::QuadratureRole::discretization,
std::array<int, 1>{get_pressure_extra_order(barotrope)}, mean_field::utils::DOMAINS::STELLAR, transformation.OrderW(),
mean_field::quadrature::MappingKind::general 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( MFEM_VERIFY(rule.integration_rule != nullptr,
rule.integration_rule != nullptr, "The quadrature policy did not return a pressure-force " "The quadrature policy did not return a pressure-force "
"integration rule." "integration rule.");
);
return *rule.integration_rule; return *rule.integration_rule;
} }
void validate_inputs( void validate_inputs(
const mean_field::fem::FEM &f, const mean_field::fem::FEM &f,
const mean_field::mapping::DomainMapperStateless &domainMapper, const mean_field::mapping::DomainMapper &domainMapper,
const mfem::Vector &enthalpyTrue, const mfem::Vector &enthalpyTrue, const mfem::Vector &displacementTrue) {
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.");
MFEM_VERIFY( MFEM_VERIFY(f.enthalpyFes != nullptr,
f.enthalpyFes != nullptr, "The pressure-force kernel requires the enthalpy " "The pressure-force kernel requires the enthalpy "
"finite-element space." "finite-element space.");
);
MFEM_VERIFY( MFEM_VERIFY(f.displacementFes != nullptr,
f.displacementFes != nullptr, "The pressure-force kernel requires the displacement " "The pressure-force kernel requires the displacement "
"finite-element space." "finite-element space.");
);
MFEM_VERIFY( MFEM_VERIFY(f.compactificationFes != nullptr,
f.compactificationFes != nullptr, "The pressure-force kernel requires the compactification " "The pressure-force kernel requires the compactification "
"finite-element space." "finite-element space.");
);
MFEM_VERIFY( MFEM_VERIFY(f.compactificationCoordinate != nullptr,
f.compactificationCoordinate != nullptr, "The pressure-force kernel requires the compactification " "The pressure-force kernel requires the compactification "
"coordinate." "coordinate.");
);
MFEM_VERIFY( MFEM_VERIFY(f.quadratureFactory != nullptr,
f.quadratureFactory != nullptr, "The pressure-force kernel requires the quadrature " "The pressure-force kernel requires the quadrature "
"rule factory." "rule factory.");
);
MFEM_VERIFY( MFEM_VERIFY(enthalpyTrue.Size() == f.enthalpyFes->GetTrueVSize(),
enthalpyTrue.Size() == f.enthalpyFes->GetTrueVSize(), "The pressure-force enthalpy vector has the wrong size.");
"The pressure-force enthalpy vector has the wrong size."
);
MFEM_VERIFY( MFEM_VERIFY(displacementTrue.Size() == f.displacementFes->GetTrueVSize(),
displacementTrue.Size() == f.displacementFes->GetTrueVSize(), "The pressure-force displacement vector has the wrong size.");
"The pressure-force displacement vector has the wrong size."
);
MFEM_VERIFY( MFEM_VERIFY(domainMapper.GetDimension() == f.mesh->Dimension(),
domainMapper.GetDimension() == f.mesh->Dimension(),
"The pressure-force domain-mapper dimension does not match " "The pressure-force domain-mapper dimension does not match "
"the mesh dimension." "the mesh dimension.");
);
MFEM_VERIFY( MFEM_VERIFY(f.displacementFes->GetVDim() == f.mesh->Dimension(),
f.displacementFes->GetVDim() == f.mesh->Dimension(), "The displacement vector dimension does not match the " "The displacement vector dimension does not match the "
"mesh dimension." "mesh dimension.");
);
/* /*
* ElementDisplacementDataFromElementVDofs currently consumes the * ElementDisplacementDataFromElementVDofs currently consumes the
@@ -194,40 +177,35 @@ namespace {
* registry change fails immediately rather than silently * registry change fails immediately rather than silently
* corrupting the geometry. * corrupting the geometry.
*/ */
MFEM_VERIFY( MFEM_VERIFY(f.displacementFes->GetOrdering() == mfem::Ordering::byNODES,
f.displacementFes->GetOrdering() == mfem::Ordering::byNODES,
"The pressure-force kernel requires the registered byNODES " "The pressure-force kernel requires the registered byNODES "
"displacement ordering." "displacement ordering.");
);
} }
void apply_pressure_force_action( void apply_pressure_force_action(
const mean_field::fem::FEM &f, const mean_field::fem::FEM &f,
const mean_field::mapping::DomainMapperStateless &domainMapper, const mean_field::mapping::DomainMapper &domainMapper,
const mean_field::eos::Polytrope &barotrope, const mean_field::eos::Polytrope &barotrope,
const PressureForceAction pressureForceAction, const PressureForceAction pressureForceAction,
const mfem::Vector &baseEnthalpyTrue, const mfem::Vector &baseEnthalpyTrue,
const mfem::Vector *enthalpyVariationTrue, const mfem::Vector *enthalpyVariationTrue,
const mfem::Vector *displacementVariationTrue, const mfem::Vector *displacementVariationTrue,
const mfem::Vector &displacementTrue, const mfem::Vector &displacementTrue, mfem::Vector &actionTrue) {
mfem::Vector &actionTrue
) {
validate_inputs(f, domainMapper, baseEnthalpyTrue, displacementTrue); validate_inputs(f, domainMapper, baseEnthalpyTrue, displacementTrue);
if (pressureForceAction == PressureForceAction::enthalpy) { if (pressureForceAction == PressureForceAction::enthalpy) {
MFEM_VERIFY( MFEM_VERIFY(enthalpyVariationTrue != nullptr &&
enthalpyVariationTrue != nullptr && enthalpyVariationTrue->Size() == f.enthalpyFes->GetTrueVSize(), enthalpyVariationTrue->Size() ==
"The pressure-force enthalpy variation has the wrong size." f.enthalpyFes->GetTrueVSize(),
); "The pressure-force enthalpy variation has the wrong size.");
} }
if (pressureForceAction == PressureForceAction::displacement) { if (pressureForceAction == PressureForceAction::displacement) {
MFEM_VERIFY( MFEM_VERIFY(displacementVariationTrue != nullptr &&
displacementVariationTrue != nullptr && displacementVariationTrue->Size() ==
displacementVariationTrue->Size() == f.displacementFes->GetTrueVSize(), f.displacementFes->GetTrueVSize(),
"The pressure-force displacement variation has the wrong " "The pressure-force displacement variation has the wrong "
"size." "size.");
);
} }
mfem::Vector baseEnthalpyLocal; mfem::Vector baseEnthalpyLocal;
@@ -238,19 +216,22 @@ namespace {
true_to_local(*f.enthalpyFes, baseEnthalpyTrue, baseEnthalpyLocal); true_to_local(*f.enthalpyFes, baseEnthalpyTrue, baseEnthalpyLocal);
if (enthalpyVariationTrue != nullptr) { if (enthalpyVariationTrue != nullptr) {
true_to_local(*f.enthalpyFes, *enthalpyVariationTrue, enthalpyVariationLocal); true_to_local(*f.enthalpyFes, *enthalpyVariationTrue,
enthalpyVariationLocal);
} }
true_to_local(*f.displacementFes, displacementTrue, displacementLocal); true_to_local(*f.displacementFes, displacementTrue, displacementLocal);
if (displacementVariationTrue != nullptr) { if (displacementVariationTrue != nullptr) {
true_to_local(*f.displacementFes, *displacementVariationTrue, displacementVariationLocal); true_to_local(*f.displacementFes, *displacementVariationTrue,
displacementVariationLocal);
} }
mfem::Vector localAction(f.displacementFes->GetVSize()); mfem::Vector localAction(f.displacementFes->GetVSize());
localAction = 0.0; localAction = 0.0;
mean_field::mapping::DomainMapperStateless::Workspace workspace(f.mesh->Dimension()); mean_field::mapping::DomainMapper::Workspace workspace(
f.mesh->Dimension());
mfem::Array<int> enthalpyDofsofs; mfem::Array<int> enthalpyDofsofs;
mfem::Array<int> displacementDofs; mfem::Array<int> displacementDofs;
@@ -273,33 +254,37 @@ namespace {
mean_field::mapping::VolumeMappingContext mappingContext; mean_field::mapping::VolumeMappingContext mappingContext;
const int dimension = f.mesh->Dimension(); 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) { 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( MFEM_VERIFY(transformation != nullptr,
transformation != nullptr, "The pressure-force kernel received a null element " "The pressure-force kernel received a null element "
"transformation." "transformation.");
);
/* /*
* Skip vacuum before constructing or evaluating any mapping * Skip vacuum before constructing or evaluating any mapping
* data for the element. * data for the element.
*/ */
if (transformation->Attribute == vacuumAttribute) { if (is_vacuum_attribute(transformation->Attribute)) {
continue; 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 = mfem::DofTransformation *displacementDofTransformation =
f.displacementFes->GetElementVDofs(elementId, displacementDofs); f.displacementFes->GetElementVDofs(elementId, displacementDofs);
@@ -310,16 +295,19 @@ namespace {
baseEnthalpyLocal.GetSubVector(enthalpyDofs, elementBaseEnthalpy); baseEnthalpyLocal.GetSubVector(enthalpyDofs, elementBaseEnthalpy);
if (enthalpyVariationTrue != nullptr) { if (enthalpyVariationTrue != nullptr) {
enthalpyVariationLocal.GetSubVector(enthalpyDofs, elementEnthalpyVariation); enthalpyVariationLocal.GetSubVector(enthalpyDofs,
elementEnthalpyVariation);
} }
displacementLocal.GetSubVector(displacementDofs, elementDisplacement); displacementLocal.GetSubVector(displacementDofs, elementDisplacement);
if (displacementVariationTrue != nullptr) { if (displacementVariationTrue != nullptr) {
displacementVariationLocal.GetSubVector(displacementDofs, elementDisplacementVariation); displacementVariationLocal.GetSubVector(displacementDofs,
elementDisplacementVariation);
} }
f.compactificationCoordinate->GetSubVector(compactificationDofs, elementCompactification); f.compactificationCoordinate->GetSubVector(compactificationDofs,
elementCompactification);
if (enthalpyDofTransformation != nullptr) { if (enthalpyDofTransformation != nullptr) {
enthalpyDofTransformation->InvTransformPrimal(elementBaseEnthalpy); enthalpyDofTransformation->InvTransformPrimal(elementBaseEnthalpy);
@@ -333,42 +321,42 @@ namespace {
displacementDofTransformation->InvTransformPrimal(elementDisplacement); displacementDofTransformation->InvTransformPrimal(elementDisplacement);
if (displacementVariationTrue != nullptr) { if (displacementVariationTrue != nullptr) {
displacementDofTransformation->InvTransformPrimal(elementDisplacementVariation); displacementDofTransformation->InvTransformPrimal(
elementDisplacementVariation);
} }
} }
if (compactificationDofTransformation != nullptr) { if (compactificationDofTransformation != nullptr) {
compactificationDofTransformation->InvTransformPrimal(elementCompactification); compactificationDofTransformation->InvTransformPrimal(
elementCompactification);
} }
const mean_field::mapping::ElementDisplacementData displacementData = const mean_field::mapping::ElementDisplacementData displacementData =
mean_field::mapping::ElementDisplacementDataFromElementVDofs(displacementElement, elementDisplacement); mean_field::mapping::ElementDisplacementDataFromElementVDofs(
displacementElement, elementDisplacement);
const mean_field::mapping::ElementCompactificationData compactificationData( const mean_field::mapping::ElementCompactificationData compactificationData(
compactificationElement, elementCompactification compactificationElement, elementCompactification);
);
const mean_field::mapping::ElementMappingData mappingData{ 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 (displacementVariationTrue != nullptr) { if (displacementVariationTrue != nullptr) {
displacementVariationData.emplace( displacementVariationData.emplace(
mean_field::mapping::ElementDisplacementDataFromElementVDofs( mean_field::mapping::ElementDisplacementDataFromElementVDofs(
displacementElement, elementDisplacementVariation displacementElement, elementDisplacementVariation));
)
);
} }
const int scalarDisplacementDofCount = displacementElement.GetDof(); const int scalarDisplacementDofCount = displacementElement.GetDof();
MFEM_VERIFY( MFEM_VERIFY(displacementDofs.Size() ==
displacementDofs.Size() == scalarDisplacementDofCount * dimension, scalarDisplacementDofCount * dimension,
"The pressure-force element displacement vector has " "The pressure-force element displacement vector has "
"the wrong size." "the wrong size.");
);
enthalpyShape.SetSize(enthalpyElement.GetDof()); enthalpyShape.SetSize(enthalpyElement.GetDof());
@@ -376,30 +364,34 @@ namespace {
displacementDShapePhysical.SetSize(scalarDisplacementDofCount, dimension); displacementDShapePhysical.SetSize(scalarDisplacementDofCount, dimension);
displacementDShapePhysicalVariation.SetSize(scalarDisplacementDofCount, dimension); displacementDShapePhysicalVariation.SetSize(scalarDisplacementDofCount,
dimension);
elementAction.SetSize(displacementDofs.Size()); elementAction.SetSize(displacementDofs.Size());
elementAction = 0.0; elementAction = 0.0;
const mfem::IntegrationRule &integrationRule = const mfem::IntegrationRule &integrationRule = get_pressure_force_rule(
get_pressure_force_rule(f, barotrope, enthalpyElement, displacementElement, *transformation); f, barotrope, enthalpyElement, displacementElement, *transformation);
for (int quadratureIndex = 0; quadratureIndex < integrationRule.GetNPoints(); ++quadratureIndex) { for (int quadratureIndex = 0;
const mfem::IntegrationPoint &integrationPoint = integrationRule.IntPoint(quadratureIndex); quadratureIndex < integrationRule.GetNPoints(); ++quadratureIndex) {
const mfem::IntegrationPoint &integrationPoint =
integrationRule.IntPoint(quadratureIndex);
transformation->SetIntPoint(&integrationPoint); transformation->SetIntPoint(&integrationPoint);
const mean_field::mapping::MappingStatus mappingStatus = domainMapper.EvaluateVolume( const mean_field::mapping::MappingStatus mappingStatus =
mappingData, *transformation, integrationPoint, workspace, mappingContext domainMapper.EvaluateVolume(mappingData, *transformation,
); integrationPoint, workspace,
mappingContext);
MFEM_VERIFY( MFEM_VERIFY(mappingStatus == mean_field::mapping::MappingStatus::valid,
mappingStatus == mean_field::mapping::MappingStatus::valid,
"Stateless mapping failed in the pressure-force " "Stateless mapping failed in the pressure-force "
"kernel. Element: " "kernel. Element: "
<< elementId << ", attribute: " << transformation->Attribute << elementId
<< ", quadrature point: " << quadratureIndex << ", status: " << static_cast<int>(mappingStatus) << ", attribute: " << transformation->Attribute
); << ", quadrature point: " << quadratureIndex
<< ", status: " << static_cast<int>(mappingStatus));
enthalpyElement.CalcShape(integrationPoint, enthalpyShape); enthalpyElement.CalcShape(integrationPoint, enthalpyShape);
@@ -411,13 +403,16 @@ namespace {
pressureForceAction == PressureForceAction::displacement) { pressureForceAction == PressureForceAction::displacement) {
pressureFactor = barotrope.pressure_from_enthalpy(enthalpyValue); pressureFactor = barotrope.pressure_from_enthalpy(enthalpyValue);
} else { } else {
const double enthalpyVariationValue = elementEnthalpyVariation * enthalpyShape; const double enthalpyVariationValue =
elementEnthalpyVariation * enthalpyShape;
pressureFactor = pressureFactor =
barotrope.pressure_derivative_from_enthalpy(enthalpyValue) * enthalpyVariationValue; barotrope.pressure_derivative_from_enthalpy(enthalpyValue) *
enthalpyVariationValue;
} }
displacementElement.CalcDShape(integrationPoint, displacementDShapeReference); displacementElement.CalcDShape(integrationPoint,
displacementDShapeReference);
/* /*
* Row i of DShape is grad_reference(N_i). Multiplication * Row i of DShape is grad_reference(N_i). Multiplication
@@ -426,25 +421,27 @@ namespace {
* grad_physical(N_i) * grad_physical(N_i)
* = grad_reference(N_i) J^{-1}. * = grad_reference(N_i) J^{-1}.
*/ */
mfem::Mult(displacementDShapeReference, mappingContext.quadrature.J_inv, displacementDShapePhysical); mfem::Mult(displacementDShapeReference, mappingContext.quadrature.J_inv,
displacementDShapePhysical);
std::optional<mean_field::mapping::VolumeMappingVariation> mappingVariation; std::optional<mean_field::mapping::VolumeMappingVariation>
mappingVariation;
if (pressureForceAction == PressureForceAction::displacement) { if (pressureForceAction == PressureForceAction::displacement) {
mappingVariation.emplace(); mappingVariation.emplace();
const mean_field::mapping::MappingStatus variationStatus = domainMapper.EvaluateVolumeVariation( const mean_field::mapping::MappingStatus variationStatus =
mappingData, *displacementVariationData, *transformation, integrationPoint, mappingContext, domainMapper.EvaluateVolumeVariation(
workspace, *mappingVariation mappingData, *displacementVariationData, *transformation,
); integrationPoint, mappingContext, workspace, *mappingVariation);
MFEM_VERIFY( MFEM_VERIFY(
variationStatus == mean_field::mapping::MappingStatus::valid, variationStatus == mean_field::mapping::MappingStatus::valid,
"Stateless mapping variation failed in the " "Stateless mapping variation failed in the "
"pressure-force kernel. Element: " "pressure-force kernel. Element: "
<< elementId << ", attribute: " << transformation->Attribute << ", quadrature point: " << elementId << ", attribute: " << transformation->Attribute
<< quadratureIndex << ", status: " << static_cast<int>(variationStatus) << ", quadrature point: " << quadratureIndex
); << ", status: " << static_cast<int>(variationStatus));
/* /*
* Differentiating * Differentiating
@@ -455,19 +452,18 @@ namespace {
* test-gradient variation used by the geometric * test-gradient variation used by the geometric
* pressure block. * pressure block.
*/ */
mfem::Mult( mfem::Mult(displacementDShapeReference,
displacementDShapeReference, mappingVariation->inverse_element_jacobian_variation, mappingVariation->inverse_element_jacobian_variation,
displacementDShapePhysicalVariation displacementDShapePhysicalVariation);
);
} }
const double weightedPressureFactor = pressureFactor * mappingContext.quadrature.weight; const double weightedPressureFactor =
pressureFactor * mappingContext.quadrature.weight;
MFEM_VERIFY( MFEM_VERIFY(std::isfinite(pressureFactor) &&
std::isfinite(pressureFactor) && std::isfinite(weightedPressureFactor), std::isfinite(weightedPressureFactor),
"The pressure-force kernel encountered a non-finite " "The pressure-force kernel encountered a non-finite "
"quadrature value." "quadrature value.");
);
/* /*
* For the vector basis N_i e_c, * For the vector basis N_i e_c,
@@ -479,11 +475,12 @@ namespace {
* R_(i,c) * R_(i,c)
* = -integral P partial_c N_i dV. * = -integral P partial_c N_i dV.
*/ */
for (int scalarDof = 0; scalarDof < scalarDisplacementDofCount; ++scalarDof) { for (int scalarDof = 0; scalarDof < scalarDisplacementDofCount;
++scalarDof) {
for (int component = 0; component < dimension; ++component) { for (int component = 0; component < dimension; ++component) {
const int vectorDof = vector_dof_index( const int vectorDof =
displacementOrdering, scalarDof, component, scalarDisplacementDofCount, dimension vector_dof_index(displacementOrdering, scalarDof, component,
); scalarDisplacementDofCount, dimension);
if (pressureForceAction == PressureForceAction::displacement) { if (pressureForceAction == PressureForceAction::displacement) {
/* /*
@@ -497,20 +494,22 @@ namespace {
const double gradientWeightVariation = const double gradientWeightVariation =
mappingContext.quadrature.weight * mappingContext.quadrature.weight *
displacementDShapePhysicalVariation(scalarDof, component) + displacementDShapePhysicalVariation(scalarDof, component) +
mappingVariation->weight_variation * displacementDShapePhysical(scalarDof, component); mappingVariation->weight_variation *
displacementDShapePhysical(scalarDof, component);
const double contribution = pressureFactor * gradientWeightVariation; const double contribution =
pressureFactor * gradientWeightVariation;
MFEM_VERIFY( MFEM_VERIFY(std::isfinite(gradientWeightVariation) &&
std::isfinite(gradientWeightVariation) && std::isfinite(contribution), std::isfinite(contribution),
"The pressure-force geometry action " "The pressure-force geometry action "
"encountered a non-finite contribution." "encountered a non-finite contribution.");
);
elementAction(vectorDof) -= contribution; elementAction(vectorDof) -= contribution;
} else { } else {
elementAction(vectorDof) -= elementAction(vectorDof) -=
weightedPressureFactor * displacementDShapePhysical(scalarDof, component); weightedPressureFactor *
displacementDShapePhysical(scalarDof, component);
} }
} }
} }
@@ -529,46 +528,33 @@ namespace {
namespace mean_field::operators::kernels { namespace mean_field::operators::kernels {
void apply_pressure_force_residual( void apply_pressure_force_residual(
const fem::FEM &f, const fem::FEM &f, const mapping::DomainMapper &domainMapper,
const mapping::DomainMapperStateless &domainMapper, const eos::Polytrope &barotrope, const mfem::Vector &enthalpyTrue,
const eos::Polytrope &barotrope, const mfem::Vector &displacementTrue, mfem::Vector &residualTrue) {
const mfem::Vector &enthalpyTrue, apply_pressure_force_action(f, domainMapper, barotrope,
const mfem::Vector &displacementTrue, PressureForceAction::residual, enthalpyTrue,
mfem::Vector &residualTrue nullptr, nullptr, displacementTrue, residualTrue);
) {
apply_pressure_force_action(
f, domainMapper, barotrope, PressureForceAction::residual, enthalpyTrue, nullptr, nullptr, displacementTrue,
residualTrue
);
} }
void apply_pressure_force_enthalpy_action( void apply_pressure_force_enthalpy_action(
const fem::FEM &f, const fem::FEM &f, const mapping::DomainMapper &domainMapper,
const mapping::DomainMapperStateless &domainMapper, const eos::Polytrope &barotrope, const mfem::Vector &baseEnthalpyTrue,
const eos::Polytrope &barotrope,
const mfem::Vector &baseEnthalpyTrue,
const mfem::Vector &enthalpyVariationTrue, const mfem::Vector &enthalpyVariationTrue,
const mfem::Vector &displacementTrue, const mfem::Vector &displacementTrue, mfem::Vector &actionTrue) {
mfem::Vector &actionTrue apply_pressure_force_action(f, domainMapper, barotrope,
) { PressureForceAction::enthalpy, baseEnthalpyTrue,
apply_pressure_force_action( &enthalpyVariationTrue, nullptr, displacementTrue,
f, domainMapper, barotrope, PressureForceAction::enthalpy, baseEnthalpyTrue, &enthalpyVariationTrue, actionTrue);
nullptr, displacementTrue, actionTrue
);
} }
void apply_pressure_force_displacement_action( void apply_pressure_force_displacement_action(
const fem::FEM &f, const fem::FEM &f, const mapping::DomainMapper &domainMapper,
const mapping::DomainMapperStateless &domainMapper, const eos::Polytrope &barotrope, const mfem::Vector &baseEnthalpyTrue,
const eos::Polytrope &barotrope,
const mfem::Vector &baseEnthalpyTrue,
const mfem::Vector &displacementVariationTrue, const mfem::Vector &displacementVariationTrue,
const mfem::Vector &displacementTrue, const mfem::Vector &displacementTrue, mfem::Vector &actionTrue) {
mfem::Vector &actionTrue
) {
apply_pressure_force_action( apply_pressure_force_action(
f, domainMapper, barotrope, PressureForceAction::displacement, baseEnthalpyTrue, nullptr, f, domainMapper, barotrope, PressureForceAction::displacement,
&displacementVariationTrue, displacementTrue, actionTrue baseEnthalpyTrue, nullptr, &displacementVariationTrue, displacementTrue,
); actionTrue);
} }
} // namespace mean_field::operators::kernels } // namespace mean_field::operators::kernels

View File

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

View File

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

View File

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

View File

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

View File

@@ -12,32 +12,32 @@ namespace {
using DomainSchema = mean_field::utils::domain::CoreEnvelopeVacuumDomainSchema; using DomainSchema = mean_field::utils::domain::CoreEnvelopeVacuumDomainSchema;
int get_operator_height(const mean_field::fem::FEM &f) { int get_operator_height(const mean_field::fem::FEM &f) {
MFEM_VERIFY( MFEM_VERIFY(f.gravityPotentialFes != nullptr,
f.gravityPotentialFes != nullptr, "PreparedMappedGravitySourceOperator requires the " "PreparedMappedGravitySourceOperator requires the "
"gravity-potential " "gravity-potential "
"finite-element space." "finite-element space.");
); return mean_field::field::make_field_dof_map<mean_field::field::Gravity,
return mean_field::field::make_field_dof_map<mean_field::field::Gravity, DomainSchema>(*f.gravityPotentialFes) DomainSchema>(
*f.gravityPotentialFes)
.reduced_size(); .reduced_size();
} }
int get_operator_width(const mean_field::fem::FEM &f) { int get_operator_width(const mean_field::fem::FEM &f) {
MFEM_VERIFY( MFEM_VERIFY(f.densityFes != nullptr,
f.densityFes != nullptr, "PreparedMappedGravitySourceOperator requires the density " "PreparedMappedGravitySourceOperator requires the density "
"finite-element space." "finite-element space.");
); return mean_field::field::make_field_dof_map<mean_field::field::Density,
return mean_field::field::make_field_dof_map<mean_field::field::Density, DomainSchema>(*f.densityFes) DomainSchema>(*f.densityFes)
.reduced_size(); .reduced_size();
} }
void true_to_local( void true_to_local(const mfem::ParFiniteElementSpace &finite_element_space,
const mfem::ParFiniteElementSpace &finite_element_space,
const mfem::Vector &true_vector, const mfem::Vector &true_vector,
mfem::Vector &local_vector mfem::Vector &local_vector) {
) {
local_vector.SetSize(finite_element_space.GetVSize()); 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) { if (prolongation != nullptr) {
prolongation->Mult(true_vector, local_vector); prolongation->Mult(true_vector, local_vector);
@@ -46,17 +46,17 @@ namespace {
} }
} }
void local_to_true( void local_to_true(const mfem::ParFiniteElementSpace &finite_element_space,
const mfem::ParFiniteElementSpace &finite_element_space,
const mfem::Vector &local_vector, const mfem::Vector &local_vector,
mfem::Vector &true_vector mfem::Vector &true_vector) {
) { MFEM_VERIFY(local_vector.Size() == finite_element_space.GetVSize(),
MFEM_VERIFY(local_vector.Size() == finite_element_space.GetVSize(), "Local vector has the wrong size."); "Local vector has the wrong size.");
true_vector.SetSize(finite_element_space.GetTrueVSize()); 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) { if (prolongation != nullptr) {
prolongation->MultTranspose(local_vector, true_vector); prolongation->MultTranspose(local_vector, true_vector);
@@ -65,75 +65,72 @@ namespace {
} }
} }
const mfem::IntegrationRule &get_source_rule( const mfem::IntegrationRule &
const mean_field::fem::FEM &f, get_source_rule(const mean_field::fem::FEM &f,
const mfem::FiniteElement &density_element, const mfem::FiniteElement &density_element,
const mfem::FiniteElement &potential_element, const mfem::FiniteElement &potential_element,
const mfem::ElementTransformation &transformation 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( MFEM_VERIFY(density_element.GetOrder() ==
density_element.GetOrder() == mean_field::field::Density::Scalar::familyOrder, mean_field::field::Density::Scalar::familyOrder,
"The prepared source trial element does not match the registered " "The prepared source trial element does not match the registered "
"density field." "density field.");
); MFEM_VERIFY(potential_element.GetOrder() ==
MFEM_VERIFY( 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 " "The prepared source test element does not match the registered "
"gravity potential." "gravity potential.");
); const mean_field::quadrature::Query query = GravityField::make_query<
const mean_field::quadrature::Query query = mean_field::field::Gravity::Form::SourceProjection>(
GravityField::make_query<mean_field::field::Gravity::Form::SourceProjection>( mean_field::quadrature::QuadratureRole::discretization,
mean_field::quadrature::QuadratureRole::discretization, transformation.OrderW(), {}, transformation.OrderW(), {}, mean_field::utils::DOMAINS::STELLAR,
mean_field::utils::DOMAINS::STELLAR, mean_field::quadrature::MappingKind::general 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: public:
FrozenMappedGravitySourceCoefficient( FrozenMappedGravitySourceCoefficient(
const mean_field::fem::FEM &f, const mean_field::fem::FEM &f,
const mean_field::mapping::DomainMapperStateless &domain_mapper, const mean_field::mapping::DomainMapper &domain_mapper,
const mfem::Vector &displacement_true const mfem::Vector &displacement_true)
) : m_fem(f), m_domain_mapper(domain_mapper),
: m_fem(f),
m_domain_mapper(domain_mapper),
m_workspace(domain_mapper.GetDimension()) { 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( double Eval(mfem::ElementTransformation &transformation,
mfem::ElementTransformation &transformation, const mfem::IntegrationPoint &integration_point) override {
const mfem::IntegrationPoint &integration_point
) override {
transformation.SetIntPoint(&integration_point); transformation.SetIntPoint(&integration_point);
const int element_id = transformation.ElementNo; const int element_id = transformation.ElementNo;
MFEM_VERIFY( MFEM_VERIFY(element_id >= 0 && element_id < m_fem.mesh->GetNE(),
element_id >= 0 && element_id < m_fem.mesh->GetNE(),
"Mapped gravity source coefficient received an invalid element " "Mapped gravity source coefficient received an invalid element "
"ID." "ID.");
); if (DomainSchema::template attribute_belongs_to<
if (transformation.Attribute == m_domain_mapper.GetVacuumElementAttribute()) { mean_field::utils::domain::Vacuum>(transformation.Attribute)) {
return 0.0; return 0.0;
} }
LoadElement(element_id); LoadElement(element_id);
const mean_field::mapping::ElementMappingData mapping_data{ 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; mean_field::mapping::VolumeMappingContext mapping_context;
const mean_field::mapping::MappingStatus status = m_domain_mapper.EvaluateVolume( const mean_field::mapping::MappingStatus status =
mapping_data, transformation, integration_point, m_workspace, mapping_context m_domain_mapper.EvaluateVolume(mapping_data, transformation,
); integration_point, m_workspace,
mapping_context);
if (status != mean_field::mapping::MappingStatus::valid) { if (status != mean_field::mapping::MappingStatus::valid) {
const mfem::FiniteElement &displacement_element = *m_fem.displacementFes->GetFE(element_id); const mfem::FiniteElement &displacement_element =
const mfem::FiniteElement &compactification_element = *m_fem.compactificationFes->GetFE(element_id); *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 displacement_shape(displacement_element.GetDof());
mfem::Vector compactification_shape(compactification_element.GetDof()); mfem::Vector compactification_shape(compactification_element.GetDof());
@@ -141,36 +138,41 @@ namespace {
mfem::Vector displacement_value(m_domain_mapper.GetDimension()); mfem::Vector displacement_value(m_domain_mapper.GetDimension());
displacement_element.CalcShape(integration_point, displacement_shape); displacement_element.CalcShape(integration_point, displacement_shape);
compactification_element.CalcShape(integration_point, compactification_shape); compactification_element.CalcShape(integration_point,
compactification_shape);
transformation.Transform(integration_point, reference_position); 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( MFEM_ABORT(
"Stateless domain mapping failed while preparing the " "Stateless domain mapping failed while preparing the "
"gravity " "gravity "
"source operator." "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 << "\nElement attribute = " << transformation.Attribute
<< "\nIntegration-point index = " << integration_point.index << "\nIntegration point = <" << "\nIntegration-point index = " << integration_point.index
<< integration_point.x << ", " << integration_point.y << ", " << integration_point.z << ">" << "\nIntegration point = <" << integration_point.x << ", "
<< "\nReference position = <" << reference_position(0) << ", " << reference_position(1) << ", " << integration_point.y << ", " << integration_point.z << ">"
<< reference_position(2) << ">" << "\nReference position = <" << reference_position(0) << ", "
<< "\nReference radius = " << reference_position.Norml2() << "\nDisplacement value = <" << reference_position(1) << ", " << reference_position(2) << ">"
<< displacement_value(0) << ", " << displacement_value(1) << ", " << displacement_value(2) << ">" << "\nReference radius = " << reference_position.Norml2()
<< "\nDisplacement value = <" << displacement_value(0) << ", "
<< displacement_value(1) << ", " << displacement_value(2) << ">"
<< "\nDisplacement magnitude = " << displacement_value.Norml2() << "\nDisplacement magnitude = " << displacement_value.Norml2()
<< "\nCompactification coordinate = " << compactification_coordinate << "\nCompactification coordinate = " << compactification_coordinate
<< "\nDisplacement ordering = " << static_cast<int>(m_fem.displacementFes->GetOrdering()) << "\nDisplacement ordering = "
); << static_cast<int>(m_fem.displacementFes->GetOrdering()));
} }
const double mapping_determinant = mapping_context.mapping.mapping_determinant; const double mapping_determinant =
MFEM_VERIFY( mapping_context.mapping.mapping_determinant;
std::isfinite(mapping_determinant) && mapping_determinant > 0.0, MFEM_VERIFY(std::isfinite(mapping_determinant) && mapping_determinant > 0.0,
"Prepared gravity source operator encountered a non-positive " "Prepared gravity source operator encountered a non-positive "
"or " "or "
"non-finite mapping determinant." "non-finite mapping determinant.");
);
return 4.0 * std::numbers::pi * mean_field::utils::G * mapping_determinant; return 4.0 * std::numbers::pi * mean_field::utils::G * mapping_determinant;
} }
@@ -181,40 +183,46 @@ namespace {
return; return;
} }
const mfem::FiniteElement &displacement_element = *m_fem.displacementFes->GetFE(element_id); const mfem::FiniteElement &displacement_element =
const mfem::FiniteElement &compactification_element = *m_fem.compactificationFes->GetFE(element_id); *m_fem.displacementFes->GetFE(element_id);
const mfem::FiniteElement &compactification_element =
*m_fem.compactificationFes->GetFE(element_id);
mfem::DofTransformation *displacement_dof_transformation = 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 = 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_displacement_local.GetSubVector(m_displacement_dofs,
m_fem.compactificationCoordinate->GetSubVector(m_compactification_dofs, m_element_compactification); m_element_displacement);
m_fem.compactificationCoordinate->GetSubVector(m_compactification_dofs,
m_element_compactification);
if (displacement_dof_transformation != nullptr) { if (displacement_dof_transformation != nullptr) {
displacement_dof_transformation->InvTransformPrimal(m_element_displacement); displacement_dof_transformation->InvTransformPrimal(
m_element_displacement);
} }
if (compactification_dof_transformation != nullptr) { 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( mean_field::mapping::ElementDisplacementDataFromElementVDofs(
displacement_element, m_element_displacement displacement_element, m_element_displacement));
)
);
m_compactification_data = std::make_unique<mean_field::mapping::ElementCompactificationData>( m_compactification_data =
compactification_element, m_element_compactification std::make_unique<mean_field::mapping::ElementCompactificationData>(
); compactification_element, m_element_compactification);
m_cached_element_id = element_id; m_cached_element_id = element_id;
} }
const mean_field::fem::FEM &m_fem; const mean_field::fem::FEM &m_fem;
const mean_field::mapping::DomainMapperStateless &m_domain_mapper; const mean_field::mapping::DomainMapper &m_domain_mapper;
mfem::Vector m_displacement_local; mfem::Vector m_displacement_local;
@@ -224,89 +232,71 @@ namespace {
mfem::Vector m_element_displacement; mfem::Vector m_element_displacement;
mfem::Vector m_element_compactification; mfem::Vector m_element_compactification;
std::unique_ptr<mean_field::mapping::ElementDisplacementData> m_displacement_data; std::unique_ptr<mean_field::mapping::ElementDisplacementData>
std::unique_ptr<mean_field::mapping::ElementCompactificationData> m_compactification_data; m_displacement_data;
std::unique_ptr<mean_field::mapping::ElementCompactificationData>
m_compactification_data;
mean_field::mapping::DomainMapperStateless::Workspace m_workspace; mean_field::mapping::DomainMapper::Workspace m_workspace;
int m_cached_element_id{-1}; int m_cached_element_id{-1};
}; };
} // namespace } // namespace
namespace mean_field::operators { namespace mean_field::operators {
PreparedMappedGravitySourceOperator::PreparedMappedGravitySourceOperator( PreparedMappedGravitySourceOperator::PreparedMappedGravitySourceOperator(
const fem::FEM &f, const fem::FEM &f, const mapping::DomainMapper &domain_mapper)
const mapping::DomainMapperStateless &domain_mapper : Operator(get_operator_height(f), get_operator_width(f)), m_fem(f),
)
: Operator(
get_operator_height(f),
get_operator_width(f)
),
m_fem(f),
m_domain_mapper(domain_mapper), m_domain_mapper(domain_mapper),
m_density_map( m_density_map(field::make_field_dof_map<field::Density, DomainSchema>(
field::make_field_dof_map< *f.densityFes)),
field::Density, m_potential_map(field::make_field_dof_map<field::Gravity, DomainSchema>(
DomainSchema>(*f.densityFes) *f.gravityPotentialFes)),
),
m_potential_map(
field::make_field_dof_map<
field::Gravity,
DomainSchema>(*f.gravityPotentialFes)
),
m_displacement_map( m_displacement_map(
field::make_field_dof_map< field::make_field_dof_map<field::Displacement, DomainSchema>(
field::Displacement, *f.displacementFes)) {
DomainSchema>(*f.displacementFes) MFEM_VERIFY(f.mesh != nullptr,
) { "PreparedMappedGravitySourceOperator requires a mesh.");
MFEM_VERIFY(f.mesh != nullptr, "PreparedMappedGravitySourceOperator requires a mesh."); MFEM_VERIFY(f.densityFes != nullptr,
MFEM_VERIFY( "PreparedMappedGravitySourceOperator requires the density "
f.densityFes != nullptr, "PreparedMappedGravitySourceOperator requires the density " "finite-element space.");
"finite-element space." MFEM_VERIFY(f.gravityPotentialFes != nullptr,
); "PreparedMappedGravitySourceOperator requires the "
MFEM_VERIFY(
f.gravityPotentialFes != nullptr, "PreparedMappedGravitySourceOperator requires the "
"gravity-potential " "gravity-potential "
"finite-element space." "finite-element space.");
); MFEM_VERIFY(f.displacementFes != nullptr,
"PreparedMappedGravitySourceOperator requires "
"the displacement finite-element space.");
MFEM_VERIFY( MFEM_VERIFY(
f.displacementFes != nullptr, "PreparedMappedGravitySourceOperator requires " f.compactificationFes != nullptr,
"the displacement finite-element space." "PreparedMappedGravitySourceOperator requires the compactification "
); "finite-element space.");
MFEM_VERIFY(
f.compactificationFes != nullptr, "PreparedMappedGravitySourceOperator requires the compactification "
"finite-element space."
);
MFEM_VERIFY( MFEM_VERIFY(
f.compactificationCoordinate != nullptr, f.compactificationCoordinate != nullptr,
"PreparedMappedGravitySourceOperator requires the compactification " "PreparedMappedGravitySourceOperator requires the compactification "
"coordinate." "coordinate.");
); MFEM_VERIFY(f.quadratureFactory != nullptr,
MFEM_VERIFY( "PreparedMappedGravitySourceOperator "
f.quadratureFactory != nullptr, "PreparedMappedGravitySourceOperator " "requires the quadrature-rule factory.");
"requires the quadrature-rule factory." MFEM_VERIFY(domain_mapper.GetDimension() == f.mesh->Dimension(),
);
MFEM_VERIFY(
domain_mapper.GetDimension() == f.mesh->Dimension(),
"The stateless domain-mapper dimension does not match the mesh " "The stateless domain-mapper dimension does not match the mesh "
"dimension." "dimension.");
);
utils::populate_element_mask(f.mesh.get(), utils::DOMAINS::STELLAR, m_stellar_marker); m_stellar_marker =
utils::domain::make_attribute_marker<utils::domain::Stellar,
DomainSchema>(*f.mesh);
} }
void PreparedMappedGravitySourceOperator::Prepare(const mfem::Vector &displacement) { void PreparedMappedGravitySourceOperator::Prepare(
MFEM_VERIFY( const mfem::Vector &displacement) {
displacement.Size() == m_displacement_map.reduced_size(), MFEM_VERIFY(displacement.Size() == m_displacement_map.reduced_size(),
"PreparedMappedGravitySourceOperator received a displacement " "PreparedMappedGravitySourceOperator received a displacement "
"vector " "vector "
"with the wrong size." "with the wrong size.");
);
for (int i = 0; i < displacement.Size(); ++i) { for (int i = 0; i < displacement.Size(); ++i) {
MFEM_VERIFY( MFEM_VERIFY(std::isfinite(displacement(i)),
std::isfinite(displacement(i)), "PreparedMappedGravitySourceOperator received a non-finite " "PreparedMappedGravitySourceOperator received a non-finite "
"displacement value." "displacement value.");
);
} }
m_is_prepared = false; m_is_prepared = false;
@@ -315,12 +305,14 @@ namespace mean_field::operators {
m_elements.clear(); m_elements.clear();
m_elements.reserve(m_fem.mesh->GetNE()); m_elements.reserve(m_fem.mesh->GetNE());
FrozenMappedGravitySourceCoefficient source_coefficient(m_fem, m_domain_mapper, m_displacement_true); FrozenMappedGravitySourceCoefficient source_coefficient(
m_fem, m_domain_mapper, m_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); 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; continue;
} }
@@ -329,19 +321,24 @@ namespace mean_field::operators {
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 = 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 = const mfem::IntegrationRule &integration_rule = get_source_rule(
get_source_rule(m_fem, density_element, potential_element, transformation); m_fem, density_element, potential_element, transformation);
const int quadrature_point_count = integration_rule.GetNPoints(); const int quadrature_point_count = integration_rule.GetNPoints();
@@ -358,8 +355,10 @@ namespace mean_field::operators {
mfem::Vector density_shape(density_dof_count); mfem::Vector density_shape(density_dof_count);
mfem::Vector potential_shape(potential_dof_count); mfem::Vector potential_shape(potential_dof_count);
for (int quadrature_point = 0; quadrature_point < quadrature_point_count; ++quadrature_point) { for (int quadrature_point = 0; quadrature_point < quadrature_point_count;
const mfem::IntegrationPoint &integration_point = integration_rule.IntPoint(quadrature_point); ++quadrature_point) {
const mfem::IntegrationPoint &integration_point =
integration_rule.IntPoint(quadrature_point);
transformation.SetIntPoint(&integration_point); transformation.SetIntPoint(&integration_point);
@@ -377,41 +376,40 @@ namespace mean_field::operators {
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); 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( MFEM_VERIFY(std::isfinite(quadrature_value) && quadrature_value > 0.0,
std::isfinite(quadrature_value) && quadrature_value > 0.0,
"Prepared gravity source operator encountered invalid " "Prepared gravity source operator encountered invalid "
"quadrature data on element " "quadrature data on element "
<< element_id << ", quadrature point " << quadrature_point << "." << element_id << ", quadrature point " << quadrature_point
); << ".");
data.quadrature_data(quadrature_point) = quadrature_value; 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_is_prepared = true;
++m_preparation_count; ++m_preparation_count;
} }
void PreparedMappedGravitySourceOperator::Mult( void PreparedMappedGravitySourceOperator::Mult(const mfem::Vector &density,
const mfem::Vector &density, mfem::Vector &action) const {
mfem::Vector &action MFEM_VERIFY(m_is_prepared,
) const { "PreparedMappedGravitySourceOperator must be prepared before "
MFEM_VERIFY( "Mult is called.");
m_is_prepared, "PreparedMappedGravitySourceOperator must be prepared before "
"Mult is called."
);
MFEM_VERIFY( MFEM_VERIFY(density.Size() == Width(),
density.Size() == Width(), "PreparedMappedGravitySourceOperator received a density vector " "PreparedMappedGravitySourceOperator received a density vector "
"with the wrong size." "with the wrong size.");
);
m_density_true.SetSize(m_density_map.full_size()); m_density_true.SetSize(m_density_map.full_size());
m_density_map.scatter(density, m_density_true); m_density_map.scatter(density, m_density_true);
@@ -462,18 +460,14 @@ namespace mean_field::operators {
} }
void PreparedMappedGravitySourceOperator::MultTranspose( void PreparedMappedGravitySourceOperator::MultTranspose(
const mfem::Vector &potential, const mfem::Vector &potential, mfem::Vector &action) const {
mfem::Vector &action MFEM_VERIFY(m_is_prepared,
) const { "PreparedMappedGravitySourceOperator must be prepared before "
MFEM_VERIFY( "MultTranspose is called.");
m_is_prepared, "PreparedMappedGravitySourceOperator must be prepared before "
"MultTranspose is called."
);
MFEM_VERIFY( MFEM_VERIFY(potential.Size() == Height(),
potential.Size() == Height(), "PreparedMappedGravitySourceOperator received a potential vector " "PreparedMappedGravitySourceOperator received a potential vector "
"with the wrong size." "with the wrong size.");
);
m_potential_true.SetSize(m_potential_map.full_size()); m_potential_true.SetSize(m_potential_map.full_size());
m_potential_map.scatter(potential, m_potential_true); m_potential_map.scatter(potential, m_potential_true);
@@ -523,19 +517,23 @@ namespace mean_field::operators {
return m_is_prepared; return m_is_prepared;
} }
std::uint64_t PreparedMappedGravitySourceOperator::GetPreparationCount() const noexcept { std::uint64_t
PreparedMappedGravitySourceOperator::GetPreparationCount() const noexcept {
return m_preparation_count; return m_preparation_count;
} }
const field::FieldDofMap &PreparedMappedGravitySourceOperator::GetDensityMap() const noexcept { const field::FieldDofMap &
PreparedMappedGravitySourceOperator::GetDensityMap() const noexcept {
return m_density_map; return m_density_map;
} }
const field::FieldDofMap &PreparedMappedGravitySourceOperator::GetPotentialMap() const noexcept { const field::FieldDofMap &
PreparedMappedGravitySourceOperator::GetPotentialMap() const noexcept {
return m_potential_map; return m_potential_map;
} }
const field::FieldDofMap &PreparedMappedGravitySourceOperator::GetDisplacementMap() const noexcept { const field::FieldDofMap &
PreparedMappedGravitySourceOperator::GetDisplacementMap() const noexcept {
return m_displacement_map; return m_displacement_map;
} }
} // namespace mean_field::operators } // namespace mean_field::operators

View File

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

View File

@@ -9,25 +9,28 @@ module mean_field;
import :operators.prepared_mass_normalization; import :operators.prepared_mass_normalization;
namespace { namespace {
void validate_finite_vector( using DomainSchema = mean_field::utils::domain::CoreEnvelopeVacuumDomainSchema;
const mfem::Vector &vector,
const char *message [[nodiscard]] bool is_vacuum_attribute(const int attribute) {
) { return DomainSchema::template attribute_belongs_to<
mean_field::utils::domain::Vacuum>(attribute);
}
void validate_finite_vector(const mfem::Vector &vector, const char *message) {
for (int index = 0; index < vector.Size(); ++index) { for (int index = 0; index < vector.Size(); ++index) {
MFEM_VERIFY(std::isfinite(vector(index)), message); MFEM_VERIFY(std::isfinite(vector(index)), message);
} }
} }
void true_to_local( void true_to_local(const mfem::ParFiniteElementSpace &finiteElementSpace,
const mfem::ParFiniteElementSpace &finiteElementSpace, const mfem::Vector &trueVector, mfem::Vector &localVector) {
const mfem::Vector &trueVector, MFEM_VERIFY(trueVector.Size() == finiteElementSpace.GetTrueVSize(),
mfem::Vector &localVector "True vector has the wrong size.");
) {
MFEM_VERIFY(trueVector.Size() == finiteElementSpace.GetTrueVSize(), "True vector has the wrong size.");
localVector.SetSize(finiteElementSpace.GetVSize()); localVector.SetSize(finiteElementSpace.GetVSize());
const mfem::Operator *prolongation = finiteElementSpace.GetProlongationMatrix(); const mfem::Operator *prolongation =
finiteElementSpace.GetProlongationMatrix();
if (prolongation != nullptr) { if (prolongation != nullptr) {
prolongation->Mult(trueVector, localVector); prolongation->Mult(trueVector, localVector);
@@ -36,42 +39,41 @@ namespace {
} }
} }
const mfem::IntegrationRule &get_mass_normalization_rule( const mfem::IntegrationRule &
const mean_field::fem::FEM &f, get_mass_normalization_rule(const mean_field::fem::FEM &f,
const mfem::FiniteElement &densityElement, const mfem::FiniteElement &densityElement,
const mfem::ElementTransformation &transformation const mfem::ElementTransformation &transformation) {
) {
using DensityField = mean_field::field::Field<mean_field::field::Density>; using DensityField = mean_field::field::Field<mean_field::field::Density>;
MFEM_VERIFY( MFEM_VERIFY(densityElement.GetOrder() ==
densityElement.GetOrder() == mean_field::field::Density::Scalar::familyOrder, mean_field::field::Density::Scalar::familyOrder,
"The mass-normalization element does not match the registered " "The mass-normalization element does not match the registered "
"density field." "density field.");
);
const mean_field::quadrature::Query query = const mean_field::quadrature::Query query = DensityField::make_query<
DensityField::make_query<mean_field::field::Density::Form::MassNormalization>( mean_field::field::Density::Form::MassNormalization>(
mean_field::quadrature::QuadratureRole::discretization, transformation.OrderW(), std::array<int, 0>{}, mean_field::quadrature::QuadratureRole::discretization,
mean_field::utils::DOMAINS::STELLAR, mean_field::quadrature::MappingKind::general 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()); const auto resolution =
f.quadratureFactory->get(query, transformation.GetGeometryType());
MFEM_VERIFY( MFEM_VERIFY(
resolution.integration_rule != nullptr, "The quadrature policy did not return a mass-normalization rule." resolution.integration_rule != nullptr,
); "The quadrature policy did not return a mass-normalization rule.");
return *resolution.integration_rule; return *resolution.integration_rule;
} }
void validate_shared_gravity_revisions( void validate_shared_gravity_revisions(
const mean_field::operators::context::gravity_field::GravityFieldLinearizationContext &gravityContext, const mean_field::operators::context::gravity_field::
const mean_field::operators::MassNormalizationDependencies &dependencies GravityFieldLinearizationContext &gravityContext,
) { const mean_field::operators::MassNormalizationDependencies &dependencies) {
MFEM_VERIFY( MFEM_VERIFY(gravityContext.IsPrepared(),
gravityContext.IsPrepared(), "PreparedMassNormalizationOperator requires the shared gravity " "PreparedMassNormalizationOperator requires the shared gravity "
"linearization context to be prepared first." "linearization context to be prepared first.");
);
const auto &revisions = gravityContext.GetRevisions(); const auto &revisions = gravityContext.GetRevisions();
@@ -80,60 +82,55 @@ namespace {
revisions.density.value == dependencies.density.revision && revisions.density.value == dependencies.density.revision &&
revisions.displacement.value == dependencies.displacement.revision, revisions.displacement.value == dependencies.displacement.revision,
"PreparedMassNormalizationOperator received dependency revisions " "PreparedMassNormalizationOperator received dependency revisions "
"that do not match the shared gravity context." "that do not match the shared gravity context.");
);
} }
void validate_shared_identity_transition( void validate_shared_identity_transition(
const mean_field::operators::MassNormalizationDependencyStamp &prepared, const mean_field::operators::MassNormalizationDependencyStamp &prepared,
const mean_field::operators::MassNormalizationDependencyStamp &requested, const mean_field::operators::MassNormalizationDependencyStamp &requested,
const char *message const char *message) {
) { MFEM_VERIFY(prepared.identity == requested.identity ||
MFEM_VERIFY(prepared.identity == requested.identity || prepared.revision != requested.revision, message); prepared.revision != requested.revision,
message);
} }
} // namespace } // namespace
namespace mean_field::operators { namespace mean_field::operators {
PreparedMassNormalizationOperator::PreparedMassNormalizationOperator( PreparedMassNormalizationOperator::PreparedMassNormalizationOperator(
const fem::FEM &f, const fem::FEM &f, const mapping::DomainMapper &domainMapper,
const mapping::DomainMapperStateless &domainMapper, const context::gravity_field::GravityFieldLinearizationContext
const context::gravity_field::GravityFieldLinearizationContext &gravityContext &gravityContext)
) : m_fem(f), m_domainMapper(domainMapper), m_gravityContext(gravityContext) {
: m_fem(f), MFEM_VERIFY(m_fem.mesh != nullptr,
m_domainMapper(domainMapper), "PreparedMassNormalizationOperator requires a mesh.");
m_gravityContext(gravityContext) { MFEM_VERIFY(m_fem.densityFes != nullptr && m_fem.displacementFes != nullptr &&
MFEM_VERIFY(m_fem.mesh != nullptr, "PreparedMassNormalizationOperator requires a mesh."); m_fem.compactificationFes != nullptr &&
MFEM_VERIFY( m_fem.compactificationCoordinate != nullptr &&
m_fem.densityFes != nullptr && m_fem.displacementFes != nullptr && m_fem.compactificationFes != nullptr && m_fem.quadratureFactory != nullptr,
m_fem.compactificationCoordinate != nullptr && m_fem.quadratureFactory != nullptr,
"PreparedMassNormalizationOperator requires density, " "PreparedMassNormalizationOperator requires density, "
"displacement, compactification, and quadrature data." "displacement, compactification, and quadrature data.");
); MFEM_VERIFY(m_domainMapper.GetDimension() == m_fem.mesh->Dimension(),
MFEM_VERIFY(
m_domainMapper.GetDimension() == m_fem.mesh->Dimension(),
"PreparedMassNormalizationOperator received a mapper with the " "PreparedMassNormalizationOperator received a mapper with the "
"wrong dimension." "wrong dimension.");
);
MFEM_VERIFY( MFEM_VERIFY(m_gravityContext.GetDensityMap().full_size() ==
m_gravityContext.GetDensityMap().full_size() == m_fem.densityFes->GetTrueVSize() && m_fem.densityFes->GetTrueVSize() &&
m_gravityContext.GetDisplacementMap().full_size() == m_fem.displacementFes->GetTrueVSize(), m_gravityContext.GetDisplacementMap().full_size() ==
"PreparedMassNormalizationOperator received incompatible shared FieldDof maps." m_fem.displacementFes->GetTrueVSize(),
); "PreparedMassNormalizationOperator received incompatible shared "
"FieldDof maps.");
m_densityVariationTrue.SetSize(m_gravityContext.GetDensityMap().full_size()); m_densityVariationTrue.SetSize(m_gravityContext.GetDensityMap().full_size());
m_displacementVariationTrue.SetSize(m_gravityContext.GetDisplacementMap().full_size()); m_displacementVariationTrue.SetSize(
m_gravityContext.GetDisplacementMap().full_size());
} }
PreparedMassNormalizationReport PreparedMassNormalizationOperator::Prepare( PreparedMassNormalizationReport PreparedMassNormalizationOperator::Prepare(
const MassNormalizationStateView &state, const MassNormalizationStateView &state,
const MassNormalizationDependencies &dependencies const MassNormalizationDependencies &dependencies) {
) { MFEM_VERIFY(std::isfinite(state.targetMass) && state.targetMass > 0.0,
MFEM_VERIFY(
std::isfinite(state.targetMass) && state.targetMass > 0.0,
"PreparedMassNormalizationOperator requires a finite, positive " "PreparedMassNormalizationOperator requires a finite, positive "
"target mass." "target mass.");
);
validate_shared_gravity_revisions(m_gravityContext, dependencies); validate_shared_gravity_revisions(m_gravityContext, dependencies);
@@ -141,29 +138,32 @@ namespace mean_field::operators {
validate_shared_identity_transition( validate_shared_identity_transition(
m_preparedDependencies.discretization, dependencies.discretization, m_preparedDependencies.discretization, dependencies.discretization,
"A new mass-normalization discretization identity must also " "A new mass-normalization discretization identity must also "
"change the shared gravity revision." "change the shared gravity revision.");
);
validate_shared_identity_transition( validate_shared_identity_transition(
m_preparedDependencies.density, dependencies.density, m_preparedDependencies.density, dependencies.density,
"A new mass-normalization density identity must also change " "A new mass-normalization density identity must also change "
"the shared gravity revision." "the shared gravity revision.");
);
validate_shared_identity_transition( validate_shared_identity_transition(
m_preparedDependencies.displacement, dependencies.displacement, m_preparedDependencies.displacement, dependencies.displacement,
"A new mass-normalization displacement identity must also " "A new mass-normalization displacement identity must also "
"change the shared gravity revision." "change the shared gravity revision.");
);
} }
const bool rebuildStaticPlan = const bool rebuildStaticPlan =
!m_isPrepared || dependencies.discretization != m_preparedDependencies.discretization; !m_isPrepared ||
dependencies.discretization != m_preparedDependencies.discretization;
const bool refreshGeometry = const bool refreshGeometry =
rebuildStaticPlan || dependencies.displacement != m_preparedDependencies.displacement; rebuildStaticPlan ||
dependencies.displacement != m_preparedDependencies.displacement;
const bool refreshDensity = rebuildStaticPlan || dependencies.density != m_preparedDependencies.density; const bool refreshDensity =
rebuildStaticPlan ||
dependencies.density != m_preparedDependencies.density;
const bool updateTargetMass = !m_isPrepared || dependencies.targetMass != m_preparedDependencies.targetMass || const bool updateTargetMass =
!m_isPrepared ||
dependencies.targetMass != m_preparedDependencies.targetMass ||
state.targetMass != m_targetMass; state.targetMass != m_targetMass;
m_isPrepared = false; m_isPrepared = false;
@@ -176,7 +176,8 @@ namespace mean_field::operators {
} }
if (refreshGeometry) { if (refreshGeometry) {
RefreshGeometry(m_gravityContext.GetGeometryContext().GetDisplacementTrue()); RefreshGeometry(
m_gravityContext.GetGeometryContext().GetDisplacementTrue());
report.refreshedGeometry = true; report.refreshedGeometry = true;
} }
@@ -209,19 +210,17 @@ namespace mean_field::operators {
m_elements.clear(); m_elements.clear();
m_elements.reserve(m_fem.mesh->GetNE()); m_elements.reserve(m_fem.mesh->GetNE());
const int vacuumAttribute = m_domainMapper.GetVacuumElementAttribute();
int localStellarElementCount = 0; int localStellarElementCount = 0;
for (int elementId = 0; elementId < m_fem.mesh->GetNE(); ++elementId) { 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( MFEM_VERIFY(transformation != nullptr,
transformation != nullptr, "PreparedMassNormalizationOperator received a null element " "PreparedMassNormalizationOperator received a null element "
"transformation." "transformation.");
);
if (transformation->Attribute == vacuumAttribute) { if (is_vacuum_attribute(transformation->Attribute)) {
continue; continue;
} }
@@ -230,22 +229,26 @@ namespace mean_field::operators {
ElementPAData &data = m_elements.back(); ElementPAData &data = m_elements.back();
data.elementId = elementId; data.elementId = elementId;
data.densityDofTransformation = m_fem.densityFes->GetElementDofs(elementId, data.densityDofs); data.densityDofTransformation =
m_fem.densityFes->GetElementDofs(elementId, data.densityDofs);
data.displacementDofTransformation = data.displacementDofTransformation = m_fem.displacementFes->GetElementVDofs(
m_fem.displacementFes->GetElementVDofs(elementId, data.displacementDofs); elementId, data.displacementDofs);
data.compactificationDofTransformation = data.compactificationDofTransformation =
m_fem.compactificationFes->GetElementDofs(elementId, data.compactificationDofs); m_fem.compactificationFes->GetElementDofs(elementId,
data.compactificationDofs);
const mfem::FiniteElement &densityElement = *m_fem.densityFes->GetFE(elementId); const mfem::FiniteElement &densityElement =
*m_fem.densityFes->GetFE(elementId);
const mfem::IntegrationRule &integrationRule = const mfem::IntegrationRule &integrationRule =
get_mass_normalization_rule(m_fem, densityElement, *transformation); get_mass_normalization_rule(m_fem, densityElement, *transformation);
data.quadraturePoints.resize(integrationRule.GetNPoints()); data.quadraturePoints.resize(integrationRule.GetNPoints());
for (int quadraturePoint = 0; quadraturePoint < integrationRule.GetNPoints(); ++quadraturePoint) { for (int quadraturePoint = 0;
quadraturePoint < integrationRule.GetNPoints(); ++quadraturePoint) {
QuadraturePointData &point = data.quadraturePoints[quadraturePoint]; QuadraturePointData &point = data.quadraturePoints[quadraturePoint];
point.integrationPoint = integrationRule.IntPoint(quadraturePoint); point.integrationPoint = integrationRule.IntPoint(quadraturePoint);
@@ -256,85 +259,88 @@ namespace mean_field::operators {
} }
int globalStellarElementCount = 0; int globalStellarElementCount = 0;
MPI_Allreduce( MPI_Allreduce(&localStellarElementCount, &globalStellarElementCount, 1,
&localStellarElementCount, &globalStellarElementCount, 1, MPI_INT, MPI_SUM, m_fem.mesh->GetComm() MPI_INT, MPI_SUM, m_fem.mesh->GetComm());
);
MFEM_VERIFY(globalStellarElementCount > 0, "PreparedMassNormalizationOperator found no stellar elements."); MFEM_VERIFY(globalStellarElementCount > 0,
"PreparedMassNormalizationOperator found no stellar elements.");
} }
void PreparedMassNormalizationOperator::RefreshGeometry(const mfem::Vector &displacement) { void PreparedMassNormalizationOperator::RefreshGeometry(
MFEM_VERIFY( const mfem::Vector &displacement) {
displacement.Size() == m_fem.displacementFes->GetTrueVSize(), MFEM_VERIFY(displacement.Size() == m_fem.displacementFes->GetTrueVSize(),
"PreparedMassNormalizationOperator received a displacement " "PreparedMassNormalizationOperator received a displacement "
"vector with the wrong size." "vector with the wrong size.");
);
validate_finite_vector( validate_finite_vector(
displacement, "PreparedMassNormalizationOperator received a non-finite " displacement, "PreparedMassNormalizationOperator received a non-finite "
"displacement value." "displacement value.");
);
mfem::Vector displacementLocal; mfem::Vector displacementLocal;
true_to_local(*m_fem.displacementFes, displacement, displacementLocal); true_to_local(*m_fem.displacementFes, displacement, displacementLocal);
mapping::DomainMapperStateless::Workspace workspace(m_fem.mesh->Dimension()); mapping::DomainMapper::Workspace workspace(m_fem.mesh->Dimension());
for (ElementPAData &data : m_elements) { for (ElementPAData &data : m_elements) {
displacementLocal.GetSubVector(data.displacementDofs, data.baseDisplacement); displacementLocal.GetSubVector(data.displacementDofs,
data.baseDisplacement);
m_fem.compactificationCoordinate->GetSubVector(data.compactificationDofs, data.compactification); m_fem.compactificationCoordinate->GetSubVector(data.compactificationDofs,
data.compactification);
if (data.displacementDofTransformation != nullptr) { if (data.displacementDofTransformation != nullptr) {
data.displacementDofTransformation->InvTransformPrimal(data.baseDisplacement); data.displacementDofTransformation->InvTransformPrimal(
data.baseDisplacement);
} }
if (data.compactificationDofTransformation != nullptr) { if (data.compactificationDofTransformation != nullptr) {
data.compactificationDofTransformation->InvTransformPrimal(data.compactification); data.compactificationDofTransformation->InvTransformPrimal(
data.compactification);
} }
const mfem::FiniteElement &displacementElement = *m_fem.displacementFes->GetFE(data.elementId); const mfem::FiniteElement &displacementElement =
*m_fem.displacementFes->GetFE(data.elementId);
const mfem::FiniteElement &compactificationElement = *m_fem.compactificationFes->GetFE(data.elementId); const mfem::FiniteElement &compactificationElement =
*m_fem.compactificationFes->GetFE(data.elementId);
const mapping::ElementDisplacementData displacementData = const mapping::ElementDisplacementData displacementData =
mapping::ElementDisplacementDataFromElementVDofs(displacementElement, data.baseDisplacement); mapping::ElementDisplacementDataFromElementVDofs(displacementElement,
data.baseDisplacement);
const mapping::ElementCompactificationData compactificationData( const mapping::ElementCompactificationData compactificationData(
compactificationElement, data.compactification compactificationElement, data.compactification);
);
const mapping::ElementMappingData mappingData{ const mapping::ElementMappingData mappingData{
.displacement = displacementData, .compactification = compactificationData .displacement = displacementData,
}; .compactification = compactificationData};
mfem::ElementTransformation *transformation = m_fem.mesh->GetElementTransformation(data.elementId); mfem::ElementTransformation *transformation =
m_fem.mesh->GetElementTransformation(data.elementId);
for (QuadraturePointData &point : data.quadraturePoints) { for (QuadraturePointData &point : data.quadraturePoints) {
const mapping::MappingStatus status = m_domainMapper.EvaluateVolume( const mapping::MappingStatus status = m_domainMapper.EvaluateVolume(
mappingData, *transformation, point.integrationPoint, workspace, point.mappingContext mappingData, *transformation, point.integrationPoint, workspace,
); point.mappingContext);
MFEM_VERIFY( MFEM_VERIFY(status == mapping::MappingStatus::valid,
status == mapping::MappingStatus::valid, "Stateless mapping failed while preparing mass " "Stateless mapping failed while preparing mass "
"normalization. Element: " "normalization. Element: "
<< data.elementId << data.elementId
<< ", attribute: " << transformation->Attribute << ", attribute: " << transformation->Attribute
<< ", status: " << static_cast<int>(status) << ", status: " << static_cast<int>(status));
);
} }
} }
} }
void PreparedMassNormalizationOperator::RefreshDensity(const mfem::Vector &density) { void PreparedMassNormalizationOperator::RefreshDensity(
MFEM_VERIFY( const mfem::Vector &density) {
density.Size() == m_fem.densityFes->GetTrueVSize(), MFEM_VERIFY(density.Size() == m_fem.densityFes->GetTrueVSize(),
"PreparedMassNormalizationOperator received a density vector " "PreparedMassNormalizationOperator received a density vector "
"with the wrong size." "with the wrong size.");
);
validate_finite_vector( validate_finite_vector(
density, "PreparedMassNormalizationOperator received a non-finite density " density,
"value." "PreparedMassNormalizationOperator received a non-finite density "
); "value.");
mfem::Vector densityLocal; mfem::Vector densityLocal;
true_to_local(*m_fem.densityFes, density, densityLocal); true_to_local(*m_fem.densityFes, density, densityLocal);
@@ -350,10 +356,9 @@ namespace mean_field::operators {
for (QuadraturePointData &point : data.quadraturePoints) { for (QuadraturePointData &point : data.quadraturePoints) {
point.density = elementDensity * point.densityShape; point.density = elementDensity * point.densityShape;
MFEM_VERIFY( MFEM_VERIFY(std::isfinite(point.density),
std::isfinite(point.density), "PreparedMassNormalizationOperator produced a non-finite " "PreparedMassNormalizationOperator produced a non-finite "
"quadrature density." "quadrature density.");
);
} }
} }
} }
@@ -368,26 +373,29 @@ namespace mean_field::operators {
} }
m_currentMass = GlobalSum(localMass); m_currentMass = GlobalSum(localMass);
MFEM_VERIFY(std::isfinite(m_currentMass), "PreparedMassNormalizationOperator assembled a non-finite mass."); MFEM_VERIFY(std::isfinite(m_currentMass),
"PreparedMassNormalizationOperator assembled a non-finite mass.");
m_cachedResidual.SetSize(1); m_cachedResidual.SetSize(1);
m_cachedResidual(0) = m_currentMass - m_targetMass; m_cachedResidual(0) = m_currentMass - m_targetMass;
++m_preparationCount; ++m_preparationCount;
} }
void PreparedMassNormalizationOperator::BuildResidual(mfem::Vector &residual) const { void PreparedMassNormalizationOperator::BuildResidual(
mfem::Vector &residual) const {
VerifyPrepared(); VerifyPrepared();
residual = m_cachedResidual; residual = m_cachedResidual;
++m_residualApplicationCount; ++m_residualApplicationCount;
} }
double PreparedMassNormalizationOperator::EvaluateDensityActionLocal(const mfem::Vector &densityVariation) const { double PreparedMassNormalizationOperator::EvaluateDensityActionLocal(
MFEM_VERIFY( const mfem::Vector &densityVariation) const {
densityVariation.Size() == m_fem.densityFes->GetTrueVSize(), MFEM_VERIFY(densityVariation.Size() == m_fem.densityFes->GetTrueVSize(),
"Mass-normalization density action received a vector with the " "Mass-normalization density action received a vector with the "
"wrong size." "wrong size.");
); validate_finite_vector(
validate_finite_vector(densityVariation, "Mass-normalization density action received a non-finite value."); densityVariation,
"Mass-normalization density action received a non-finite value.");
mfem::Vector densityVariationLocal; mfem::Vector densityVariationLocal;
true_to_local(*m_fem.densityFes, densityVariation, densityVariationLocal); true_to_local(*m_fem.densityFes, densityVariation, densityVariationLocal);
@@ -396,14 +404,17 @@ namespace mean_field::operators {
double localAction = 0.0; double localAction = 0.0;
for (const ElementPAData &data : m_elements) { for (const ElementPAData &data : m_elements) {
densityVariationLocal.GetSubVector(data.densityDofs, elementDensityVariation); densityVariationLocal.GetSubVector(data.densityDofs,
elementDensityVariation);
if (data.densityDofTransformation != nullptr) { if (data.densityDofTransformation != nullptr) {
data.densityDofTransformation->InvTransformPrimal(elementDensityVariation); data.densityDofTransformation->InvTransformPrimal(
elementDensityVariation);
} }
for (const QuadraturePointData &point : data.quadraturePoints) { for (const QuadraturePointData &point : data.quadraturePoints) {
localAction += (elementDensityVariation * point.densityShape) * point.mappingContext.quadrature.weight; localAction += (elementDensityVariation * point.densityShape) *
point.mappingContext.quadrature.weight;
} }
} }
@@ -411,67 +422,72 @@ namespace mean_field::operators {
} }
double PreparedMassNormalizationOperator::EvaluateDisplacementActionLocal( double PreparedMassNormalizationOperator::EvaluateDisplacementActionLocal(
const mfem::Vector &displacementVariation const mfem::Vector &displacementVariation) const {
) const { MFEM_VERIFY(displacementVariation.Size() ==
MFEM_VERIFY( m_fem.displacementFes->GetTrueVSize(),
displacementVariation.Size() == m_fem.displacementFes->GetTrueVSize(),
"Mass-normalization displacement action received a vector with " "Mass-normalization displacement action received a vector with "
"the wrong size." "the wrong size.");
);
validate_finite_vector( validate_finite_vector(
displacementVariation, "Mass-normalization displacement action received a non-finite " displacementVariation,
"value." "Mass-normalization displacement action received a non-finite "
); "value.");
mfem::Vector displacementVariationLocal; mfem::Vector displacementVariationLocal;
true_to_local(*m_fem.displacementFes, displacementVariation, displacementVariationLocal); true_to_local(*m_fem.displacementFes, displacementVariation,
displacementVariationLocal);
mapping::DomainMapperStateless::Workspace workspace(m_fem.mesh->Dimension()); mapping::DomainMapper::Workspace workspace(m_fem.mesh->Dimension());
mfem::Vector elementDisplacementVariation; mfem::Vector elementDisplacementVariation;
double localAction = 0.0; double localAction = 0.0;
for (const ElementPAData &data : m_elements) { for (const ElementPAData &data : m_elements) {
displacementVariationLocal.GetSubVector(data.displacementDofs, elementDisplacementVariation); displacementVariationLocal.GetSubVector(data.displacementDofs,
elementDisplacementVariation);
if (data.displacementDofTransformation != nullptr) { if (data.displacementDofTransformation != nullptr) {
data.displacementDofTransformation->InvTransformPrimal(elementDisplacementVariation); data.displacementDofTransformation->InvTransformPrimal(
elementDisplacementVariation);
} }
const mfem::FiniteElement &displacementElement = *m_fem.displacementFes->GetFE(data.elementId); const mfem::FiniteElement &displacementElement =
*m_fem.displacementFes->GetFE(data.elementId);
const mfem::FiniteElement &compactificationElement = *m_fem.compactificationFes->GetFE(data.elementId); const mfem::FiniteElement &compactificationElement =
*m_fem.compactificationFes->GetFE(data.elementId);
const mapping::ElementDisplacementData baseDisplacementData = const mapping::ElementDisplacementData baseDisplacementData =
mapping::ElementDisplacementDataFromElementVDofs(displacementElement, data.baseDisplacement); mapping::ElementDisplacementDataFromElementVDofs(displacementElement,
data.baseDisplacement);
const mapping::ElementDisplacementData directionData = const mapping::ElementDisplacementData directionData =
mapping::ElementDisplacementDataFromElementVDofs(displacementElement, elementDisplacementVariation); mapping::ElementDisplacementDataFromElementVDofs(
displacementElement, elementDisplacementVariation);
const mapping::ElementCompactificationData compactificationData( const mapping::ElementCompactificationData compactificationData(
compactificationElement, data.compactification compactificationElement, data.compactification);
);
const mapping::ElementMappingData mappingData{ const mapping::ElementMappingData mappingData{
.displacement = baseDisplacementData, .compactification = compactificationData .displacement = baseDisplacementData,
}; .compactification = compactificationData};
mfem::ElementTransformation *transformation = m_fem.mesh->GetElementTransformation(data.elementId); mfem::ElementTransformation *transformation =
m_fem.mesh->GetElementTransformation(data.elementId);
for (const QuadraturePointData &point : data.quadraturePoints) { for (const QuadraturePointData &point : data.quadraturePoints) {
mapping::VolumeMappingVariation variation; mapping::VolumeMappingVariation variation;
const mapping::MappingStatus status = m_domainMapper.EvaluateVolumeVariation( const mapping::MappingStatus status =
mappingData, directionData, *transformation, point.integrationPoint, point.mappingContext, m_domainMapper.EvaluateVolumeVariation(
workspace, variation mappingData, directionData, *transformation,
); point.integrationPoint, point.mappingContext, workspace,
variation);
MFEM_VERIFY( MFEM_VERIFY(status == mapping::MappingStatus::valid,
status == mapping::MappingStatus::valid, "Stateless mapping variation failed in the " "Stateless mapping variation failed in the "
"mass-normalization displacement action. Element: " "mass-normalization displacement action. Element: "
<< data.elementId << data.elementId
<< ", status: " << static_cast<int>(status) << ", status: " << static_cast<int>(status));
);
localAction += point.density * variation.weight_variation; localAction += point.density * variation.weight_variation;
} }
@@ -481,17 +497,18 @@ namespace mean_field::operators {
} }
void PreparedMassNormalizationOperator::ApplyDensityJacobianAction( void PreparedMassNormalizationOperator::ApplyDensityJacobianAction(
const mfem::Vector &densityVariation, const mfem::Vector &densityVariation, mfem::Vector &action) const {
mfem::Vector &action
) const {
VerifyPrepared(); VerifyPrepared();
MFEM_VERIFY( MFEM_VERIFY(densityVariation.Size() ==
densityVariation.Size() == m_gravityContext.GetDensityMap().reduced_size(), m_gravityContext.GetDensityMap().reduced_size(),
"Mass-normalization density action received a supported vector with the wrong size." "Mass-normalization density action received a supported vector "
); "with the wrong size.");
validate_finite_vector(densityVariation, "Mass-normalization density action received a non-finite value."); validate_finite_vector(
m_gravityContext.GetDensityMap().scatter(densityVariation, m_densityVariationTrue); densityVariation,
"Mass-normalization density action received a non-finite value.");
m_gravityContext.GetDensityMap().scatter(densityVariation,
m_densityVariationTrue);
action.SetSize(1); action.SetSize(1);
action(0) = GlobalSum(EvaluateDensityActionLocal(m_densityVariationTrue)); action(0) = GlobalSum(EvaluateDensityActionLocal(m_densityVariationTrue));
@@ -499,47 +516,49 @@ namespace mean_field::operators {
} }
void PreparedMassNormalizationOperator::ApplyDisplacementJacobianAction( void PreparedMassNormalizationOperator::ApplyDisplacementJacobianAction(
const mfem::Vector &displacementVariation, const mfem::Vector &displacementVariation, mfem::Vector &action) const {
mfem::Vector &action
) const {
VerifyPrepared(); VerifyPrepared();
MFEM_VERIFY( MFEM_VERIFY(displacementVariation.Size() ==
displacementVariation.Size() == m_gravityContext.GetDisplacementMap().reduced_size(), m_gravityContext.GetDisplacementMap().reduced_size(),
"Mass-normalization displacement action received a supported vector with the wrong size." "Mass-normalization displacement action received a supported "
); "vector with the wrong size.");
validate_finite_vector( validate_finite_vector(
displacementVariation, "Mass-normalization displacement action received a non-finite value." displacementVariation,
); "Mass-normalization displacement action received a non-finite value.");
m_gravityContext.GetDisplacementMap().scatter(displacementVariation, m_displacementVariationTrue); m_gravityContext.GetDisplacementMap().scatter(displacementVariation,
m_displacementVariationTrue);
action.SetSize(1); action.SetSize(1);
action(0) = GlobalSum(EvaluateDisplacementActionLocal(m_displacementVariationTrue)); action(0) =
GlobalSum(EvaluateDisplacementActionLocal(m_displacementVariationTrue));
++m_actionStatistics.displacementApplications; ++m_actionStatistics.displacementApplications;
} }
void PreparedMassNormalizationOperator::ApplyCompleteJacobianAction( void PreparedMassNormalizationOperator::ApplyCompleteJacobianAction(
const mfem::Vector &densityVariation, const mfem::Vector &densityVariation,
const mfem::Vector &displacementVariation, const mfem::Vector &displacementVariation, mfem::Vector &action) const {
mfem::Vector &action
) const {
VerifyPrepared(); VerifyPrepared();
MFEM_VERIFY( MFEM_VERIFY(densityVariation.Size() ==
densityVariation.Size() == m_gravityContext.GetDensityMap().reduced_size(), m_gravityContext.GetDensityMap().reduced_size(),
"Mass-normalization complete action received a supported density vector with the wrong size." "Mass-normalization complete action received a supported density "
); "vector with the wrong size.");
MFEM_VERIFY( MFEM_VERIFY(displacementVariation.Size() ==
displacementVariation.Size() == m_gravityContext.GetDisplacementMap().reduced_size(), m_gravityContext.GetDisplacementMap().reduced_size(),
"Mass-normalization complete action received a supported displacement vector with the wrong size." "Mass-normalization complete action received a supported "
); "displacement vector with the wrong size.");
validate_finite_vector(densityVariation, "Mass-normalization complete action received a non-finite density.");
validate_finite_vector( validate_finite_vector(
displacementVariation, "Mass-normalization complete action received a non-finite displacement." densityVariation,
); "Mass-normalization complete action received a non-finite density.");
validate_finite_vector(
displacementVariation,
"Mass-normalization complete action received a non-finite displacement.");
m_gravityContext.GetDensityMap().scatter(densityVariation, m_densityVariationTrue); m_gravityContext.GetDensityMap().scatter(densityVariation,
m_gravityContext.GetDisplacementMap().scatter(displacementVariation, m_displacementVariationTrue); m_densityVariationTrue);
m_gravityContext.GetDisplacementMap().scatter(displacementVariation,
m_displacementVariationTrue);
const double localAction = const double localAction =
EvaluateDensityActionLocal(m_densityVariationTrue) + EvaluateDensityActionLocal(m_densityVariationTrue) +
@@ -550,9 +569,11 @@ namespace mean_field::operators {
++m_actionStatistics.completeApplications; ++m_actionStatistics.completeApplications;
} }
double PreparedMassNormalizationOperator::GlobalSum(const double localValue) const { double
PreparedMassNormalizationOperator::GlobalSum(const double localValue) const {
double globalValue = 0.0; double globalValue = 0.0;
MPI_Allreduce(&localValue, &globalValue, 1, MPI_DOUBLE, MPI_SUM, m_fem.mesh->GetComm()); MPI_Allreduce(&localValue, &globalValue, 1, MPI_DOUBLE, MPI_SUM,
m_fem.mesh->GetComm());
return globalValue; return globalValue;
} }
@@ -562,9 +583,11 @@ namespace mean_field::operators {
} }
const auto &revisions = m_gravityContext.GetRevisions(); const auto &revisions = m_gravityContext.GetRevisions();
return revisions.discretization.value == m_preparedDependencies.discretization.revision && return revisions.discretization.value ==
m_preparedDependencies.discretization.revision &&
revisions.density.value == m_preparedDependencies.density.revision && revisions.density.value == m_preparedDependencies.density.revision &&
revisions.displacement.value == m_preparedDependencies.displacement.revision; revisions.displacement.value ==
m_preparedDependencies.displacement.revision;
} }
double PreparedMassNormalizationOperator::GetCurrentMass() const { double PreparedMassNormalizationOperator::GetCurrentMass() const {
@@ -577,11 +600,13 @@ namespace mean_field::operators {
return m_targetMass; return m_targetMass;
} }
std::uint64_t PreparedMassNormalizationOperator::GetPreparationCount() const noexcept { std::uint64_t
PreparedMassNormalizationOperator::GetPreparationCount() const noexcept {
return m_preparationCount; return m_preparationCount;
} }
std::uint64_t PreparedMassNormalizationOperator::GetResidualApplicationCount() const noexcept { std::uint64_t PreparedMassNormalizationOperator::GetResidualApplicationCount()
const noexcept {
return m_residualApplicationCount; return m_residualApplicationCount;
} }
@@ -600,56 +625,54 @@ namespace mean_field::operators {
} }
void PreparedMassNormalizationOperator::VerifyPrepared() const { void PreparedMassNormalizationOperator::VerifyPrepared() const {
MFEM_VERIFY( MFEM_VERIFY(IsPrepared(),
IsPrepared(), "PreparedMassNormalizationOperator must be prepared for the " "PreparedMassNormalizationOperator must be prepared for the "
"current shared gravity-context revisions." "current shared gravity-context revisions.");
);
} }
PreparedMassNormalizationJacobianOperator::PreparedMassNormalizationJacobianOperator( PreparedMassNormalizationJacobianOperator::
PreparedMassNormalizationJacobianOperator(
const MassNormalizationLayout &layout, const MassNormalizationLayout &layout,
const PreparedMassNormalizationOperator &preparedOperator const PreparedMassNormalizationOperator &preparedOperator)
) : mfem::Operator(layout.residual_offsets().Last(),
: mfem::Operator( layout.value_offsets().Last()),
layout.residual_offsets().Last(), m_layout(layout), m_preparedOperator(preparedOperator) {
layout.value_offsets().Last()
),
m_layout(layout),
m_preparedOperator(preparedOperator) {
const fem::FEM &f = m_preparedOperator.GetFEM(); const fem::FEM &f = m_preparedOperator.GetFEM();
MFEM_VERIFY( MFEM_VERIFY(f.densityFes != nullptr && f.displacementFes != nullptr &&
f.densityFes != nullptr && f.displacementFes != nullptr && f.gravityFluxFes != nullptr && f.gravityFluxFes != nullptr &&
f.gravityPotentialFes != nullptr && f.enthalpyFes != nullptr, f.gravityPotentialFes != nullptr && f.enthalpyFes != nullptr,
"Prepared mass-normalization MFEM adapter requires every " "Prepared mass-normalization MFEM adapter requires every "
"finite-element space in the barotropic equilibrium layout." "finite-element space in the barotropic equilibrium layout.");
);
using Form = utils::blocks::barotropic_equilibrium_form; using Form = utils::blocks::barotropic_equilibrium_form;
constexpr auto densityValue = utils::blocks::get_value_block<Form>(utils::blocks::density_field.mass_term); constexpr auto densityValue = utils::blocks::get_value_block<Form>(
constexpr auto displacementValue = utils::blocks::density_field.mass_term);
utils::blocks::get_value_block<Form>(utils::blocks::displacement_field.geometry_term); constexpr auto displacementValue = utils::blocks::get_value_block<Form>(
constexpr auto gravityGradientValue = utils::blocks::displacement_field.geometry_term);
utils::blocks::get_value_block<Form>(utils::blocks::gravity_field.gradient_term); constexpr auto gravityGradientValue = utils::blocks::get_value_block<Form>(
constexpr auto gravityPotentialValue = utils::blocks::gravity_field.gradient_term);
utils::blocks::get_value_block<Form>(utils::blocks::gravity_field.poisson_term); constexpr auto gravityPotentialValue = utils::blocks::get_value_block<Form>(
constexpr auto enthalpyValue = utils::blocks::gravity_field.poisson_term);
utils::blocks::get_value_block<Form>(utils::blocks::enthalpy_field.specific_term); constexpr auto enthalpyValue = utils::blocks::get_value_block<Form>(
constexpr auto barotropicConstantValue = utils::blocks::enthalpy_field.specific_term);
utils::blocks::get_value_block<Form>(utils::blocks::barotropic_constant_field.mass_normalization_term); constexpr auto barotropicConstantValue = utils::blocks::get_value_block<Form>(
utils::blocks::barotropic_constant_field.mass_normalization_term);
constexpr auto gravityGradientResidual = constexpr auto gravityGradientResidual =
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 gravityPotentialResidual = constexpr auto gravityPotentialResidual =
utils::blocks::get_residual_block<Form>(utils::blocks::gravity_field.poisson_term); utils::blocks::get_residual_block<Form>(
constexpr auto densityResidual = utils::blocks::gravity_field.poisson_term);
utils::blocks::get_residual_block<Form>(utils::blocks::density_field.mass_term); constexpr auto densityResidual = utils::blocks::get_residual_block<Form>(
constexpr auto displacementResidual = utils::blocks::density_field.mass_term);
utils::blocks::get_residual_block<Form>(utils::blocks::displacement_field.geometry_term); constexpr auto displacementResidual = utils::blocks::get_residual_block<Form>(
constexpr auto enthalpyResidual = utils::blocks::displacement_field.geometry_term);
utils::blocks::get_residual_block<Form>(utils::blocks::enthalpy_field.specific_term); constexpr auto enthalpyResidual = utils::blocks::get_residual_block<Form>(
constexpr auto massResidual = utils::blocks::enthalpy_field.specific_term);
utils::blocks::get_residual_block<Form>(utils::blocks::barotropic_constant_field.mass_normalization_term); constexpr auto massResidual = utils::blocks::get_residual_block<Form>(
utils::blocks::barotropic_constant_field.mass_normalization_term);
using DomainSchema = utils::domain::CoreEnvelopeVacuumDomainSchema; using DomainSchema = utils::domain::CoreEnvelopeVacuumDomainSchema;
@@ -658,62 +681,70 @@ namespace mean_field::operators {
const field::FieldDofMap enthalpyMap = const field::FieldDofMap enthalpyMap =
field::make_field_dof_map<field::Enthalpy, DomainSchema>(*f.enthalpyFes); field::make_field_dof_map<field::Enthalpy, DomainSchema>(*f.enthalpyFes);
MFEM_VERIFY( MFEM_VERIFY(m_layout.size(densityValue) ==
m_layout.size(densityValue) == gravityContext.GetDensityMap().reduced_size() && gravityContext.GetDensityMap().reduced_size() &&
m_layout.size(displacementValue) == gravityContext.GetDisplacementMap().reduced_size() && m_layout.size(displacementValue) ==
m_layout.size(gravityGradientValue) == gravityContext.GetGravityGradientMap().reduced_size() && gravityContext.GetDisplacementMap().reduced_size() &&
m_layout.size(gravityPotentialValue) == gravityContext.GetGravityPotentialMap().reduced_size() && m_layout.size(gravityGradientValue) ==
gravityContext.GetGravityGradientMap().reduced_size() &&
m_layout.size(gravityPotentialValue) ==
gravityContext.GetGravityPotentialMap().reduced_size() &&
m_layout.size(enthalpyValue) == enthalpyMap.reduced_size() && m_layout.size(enthalpyValue) == enthalpyMap.reduced_size() &&
m_layout.size(barotropicConstantValue) == 1 && m_layout.size(barotropicConstantValue) == 1 &&
m_layout.size(gravityGradientResidual) == gravityContext.GetGravityGradientMap().reduced_size() && m_layout.size(gravityGradientResidual) ==
m_layout.size(gravityPotentialResidual) == gravityContext.GetGravityPotentialMap().reduced_size() && gravityContext.GetGravityGradientMap().reduced_size() &&
m_layout.size(densityResidual) == gravityContext.GetDensityMap().reduced_size() && m_layout.size(gravityPotentialResidual) ==
m_layout.size(displacementResidual) == gravityContext.GetDisplacementMap().reduced_size() && gravityContext.GetGravityPotentialMap().reduced_size() &&
m_layout.size(enthalpyResidual) == enthalpyMap.reduced_size() && m_layout.size(massResidual) == 1, m_layout.size(densityResidual) ==
gravityContext.GetDensityMap().reduced_size() &&
m_layout.size(displacementResidual) ==
gravityContext.GetDisplacementMap().reduced_size() &&
m_layout.size(enthalpyResidual) ==
enthalpyMap.reduced_size() &&
m_layout.size(massResidual) == 1,
"Prepared mass-normalization MFEM adapter received incompatible " "Prepared mass-normalization MFEM adapter received incompatible "
"barotropic block sizes." "barotropic block sizes.");
);
} }
void PreparedMassNormalizationJacobianOperator::Mult( void PreparedMassNormalizationJacobianOperator::Mult(
const mfem::Vector &direction, const mfem::Vector &direction, mfem::Vector &action) const {
mfem::Vector &action MFEM_VERIFY(m_preparedOperator.IsPrepared(),
) const { "Prepared mass-normalization MFEM adapter requires a prepared "
MFEM_VERIFY( "row operator.");
m_preparedOperator.IsPrepared(), "Prepared mass-normalization MFEM adapter requires a prepared " MFEM_VERIFY(direction.Size() == Width(),
"row operator." "Prepared mass-normalization MFEM adapter received a direction "
); "with the wrong size.");
MFEM_VERIFY(
direction.Size() == Width(), "Prepared mass-normalization MFEM adapter received a direction "
"with the wrong size."
);
using Form = utils::blocks::barotropic_equilibrium_form; using Form = utils::blocks::barotropic_equilibrium_form;
constexpr auto densityValue = utils::blocks::get_value_block<Form>(utils::blocks::density_field.mass_term); constexpr auto densityValue = utils::blocks::get_value_block<Form>(
constexpr auto displacementValue = utils::blocks::density_field.mass_term);
utils::blocks::get_value_block<Form>(utils::blocks::displacement_field.geometry_term); constexpr auto displacementValue = utils::blocks::get_value_block<Form>(
constexpr auto massResidual = utils::blocks::displacement_field.geometry_term);
utils::blocks::get_residual_block<Form>(utils::blocks::barotropic_constant_field.mass_normalization_term); constexpr auto massResidual = utils::blocks::get_residual_block<Form>(
utils::blocks::barotropic_constant_field.mass_normalization_term);
const mfem::Vector densityVariation( const mfem::Vector densityVariation(
const_cast<mfem::real_t *>(direction.GetData()) + m_layout.offset(densityValue), m_layout.size(densityValue) const_cast<mfem::real_t *>(direction.GetData()) +
); m_layout.offset(densityValue),
m_layout.size(densityValue));
const mfem::Vector displacementVariation( const mfem::Vector displacementVariation(
const_cast<mfem::real_t *>(direction.GetData()) + m_layout.offset(displacementValue), const_cast<mfem::real_t *>(direction.GetData()) +
m_layout.size(displacementValue) m_layout.offset(displacementValue),
); m_layout.size(displacementValue));
mfem::Vector massAction; mfem::Vector massAction;
m_preparedOperator.ApplyCompleteJacobianAction(densityVariation, displacementVariation, massAction); m_preparedOperator.ApplyCompleteJacobianAction(
densityVariation, displacementVariation, massAction);
action.SetSize(Height()); action.SetSize(Height());
action = 0.0; action = 0.0;
action(m_layout.offset(massResidual)) = massAction(0); action(m_layout.offset(massResidual)) = massAction(0);
} }
const MassNormalizationLayout &PreparedMassNormalizationJacobianOperator::GetLayout() const noexcept { const MassNormalizationLayout &
PreparedMassNormalizationJacobianOperator::GetLayout() const noexcept {
return m_layout; return m_layout;
} }
} // namespace mean_field::operators } // namespace mean_field::operators

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -45,7 +45,8 @@ export namespace mean_field::utils::domain {
template <typename T> constexpr bool is_domain_set_v = false; template <typename T> constexpr bool is_domain_set_v = false;
template <IsDomain... DomainTs> constexpr bool is_domain_set_v<DomainSet<DomainTs...>> = true; template <IsDomain... DomainTs>
constexpr bool is_domain_set_v<DomainSet<DomainTs...>> = true;
template <typename T> template <typename T>
concept IsDomainSet = is_domain_set_v<T>; concept IsDomainSet = is_domain_set_v<T>;
@@ -58,7 +59,8 @@ export namespace mean_field::utils::domain {
struct DomainRelation {}; struct DomainRelation {};
template <IsDomainOrSet A, IsDomainOrSet B> struct Inscribed final : public DomainRelation { template <IsDomainOrSet A, IsDomainOrSet B>
struct Inscribed final : public DomainRelation {
using inner_type = A; using inner_type = A;
using outer_type = B; using outer_type = B;
@@ -88,14 +90,16 @@ export namespace mean_field::utils::domain {
template <typename T> constexpr bool is_material_v = false; template <typename T> constexpr bool is_material_v = false;
template <IsDomain D, int Id> constexpr bool is_material_v<Material<D, Id>> = true; template <IsDomain D, int Id>
constexpr bool is_material_v<Material<D, Id>> = true;
template <typename T> template <typename T>
concept IsMaterial = is_material_v<T>; concept IsMaterial = is_material_v<T>;
template <typename T> constexpr bool is_boundary_attr_v = false; template <typename T> constexpr bool is_boundary_attr_v = false;
template <IsBoundary B, int Id> constexpr bool is_boundary_attr_v<BoundaryAttribute<B, Id>> = true; template <IsBoundary B, int Id>
constexpr bool is_boundary_attr_v<BoundaryAttribute<B, Id>> = true;
template <typename T> template <typename T>
concept IsBoundaryAttr = is_boundary_attr_v<T>; concept IsBoundaryAttr = is_boundary_attr_v<T>;
@@ -113,10 +117,13 @@ export namespace mean_field::utils::domain {
template <IsMaterial... MaterialTs> template <IsMaterial... MaterialTs>
[[nodiscard]] [[nodiscard]]
consteval bool material_ids_are_unique() noexcept { consteval bool material_ids_are_unique() noexcept {
constexpr std::array<int, sizeof...(MaterialTs)> materialIds{MaterialTs::id...}; constexpr std::array<int, sizeof...(MaterialTs)> materialIds{
MaterialTs::id...};
for (std::size_t firstIndex = 0; firstIndex < materialIds.size(); ++firstIndex) { for (std::size_t firstIndex = 0; firstIndex < materialIds.size();
for (std::size_t secondIndex = firstIndex + 1; secondIndex < materialIds.size(); ++secondIndex) { ++firstIndex) {
for (std::size_t secondIndex = firstIndex + 1;
secondIndex < materialIds.size(); ++secondIndex) {
if (materialIds[firstIndex] == materialIds[secondIndex]) { if (materialIds[firstIndex] == materialIds[secondIndex]) {
return false; return false;
} }
@@ -129,10 +136,13 @@ export namespace mean_field::utils::domain {
template <IsBoundaryAttr... BoundaryTs> template <IsBoundaryAttr... BoundaryTs>
[[nodiscard]] [[nodiscard]]
consteval bool boundary_ids_are_unique() noexcept { consteval bool boundary_ids_are_unique() noexcept {
constexpr std::array<int, sizeof...(BoundaryTs)> boundaryIds{BoundaryTs::id...}; constexpr std::array<int, sizeof...(BoundaryTs)> boundaryIds{
BoundaryTs::id...};
for (std::size_t firstIndex = 0; firstIndex < boundaryIds.size(); ++firstIndex) { for (std::size_t firstIndex = 0; firstIndex < boundaryIds.size();
for (std::size_t secondIndex = firstIndex + 1; secondIndex < boundaryIds.size(); ++secondIndex) { ++firstIndex) {
for (std::size_t secondIndex = firstIndex + 1;
secondIndex < boundaryIds.size(); ++secondIndex) {
if (boundaryIds[firstIndex] == boundaryIds[secondIndex]) { if (boundaryIds[firstIndex] == boundaryIds[secondIndex]) {
return false; return false;
} }
@@ -146,12 +156,14 @@ export namespace mean_field::utils::domain {
template <> struct MaterialDomainsAreUnique<> : std::true_type {}; template <> struct MaterialDomainsAreUnique<> : std::true_type {};
template <typename MaterialT> struct MaterialDomainsAreUnique<MaterialT> : std::true_type { }; template <typename MaterialT>
struct MaterialDomainsAreUnique<MaterialT> : std::true_type {};
template <typename FirstMaterialT, typename... RemainingMaterialTs> template <typename FirstMaterialT, typename... RemainingMaterialTs>
struct MaterialDomainsAreUnique<FirstMaterialT, RemainingMaterialTs...> struct MaterialDomainsAreUnique<FirstMaterialT, RemainingMaterialTs...>
: std::bool_constant< : std::bool_constant<
(!std::is_same_v<typename FirstMaterialT::domain_type, typename RemainingMaterialTs::domain_type> && (!std::is_same_v<typename FirstMaterialT::domain_type,
typename RemainingMaterialTs::domain_type> &&
...) && ...) &&
MaterialDomainsAreUnique<RemainingMaterialTs...>::value> {}; MaterialDomainsAreUnique<RemainingMaterialTs...>::value> {};
@@ -159,12 +171,14 @@ export namespace mean_field::utils::domain {
template <> struct BoundaryTypesAreUnique<> : std::true_type {}; template <> struct BoundaryTypesAreUnique<> : std::true_type {};
template <typename BoundaryT> struct BoundaryTypesAreUnique<BoundaryT> : std::true_type { }; template <typename BoundaryT>
struct BoundaryTypesAreUnique<BoundaryT> : std::true_type {};
template <typename FirstBoundaryT, typename... RemainingBoundaryTs> template <typename FirstBoundaryT, typename... RemainingBoundaryTs>
struct BoundaryTypesAreUnique<FirstBoundaryT, RemainingBoundaryTs...> struct BoundaryTypesAreUnique<FirstBoundaryT, RemainingBoundaryTs...>
: std::bool_constant< : std::bool_constant<
(!std::is_same_v<typename FirstBoundaryT::boundary_type, typename RemainingBoundaryTs::boundary_type> && (!std::is_same_v<typename FirstBoundaryT::boundary_type,
typename RemainingBoundaryTs::boundary_type> &&
...) && ...) &&
BoundaryTypesAreUnique<RemainingBoundaryTs...>::value> {}; BoundaryTypesAreUnique<RemainingBoundaryTs...>::value> {};
@@ -172,7 +186,8 @@ export namespace mean_field::utils::domain {
concept HaveUniqueMaterialIds = material_ids_are_unique<MaterialTs...>(); concept HaveUniqueMaterialIds = material_ids_are_unique<MaterialTs...>();
template <typename... MaterialTs> template <typename... MaterialTs>
concept HaveUniqueMaterialDomains = MaterialDomainsAreUnique<MaterialTs...>::value; concept HaveUniqueMaterialDomains =
MaterialDomainsAreUnique<MaterialTs...>::value;
template <typename... BoundaryTs> template <typename... BoundaryTs>
concept HaveUniqueBoundaryIds = boundary_ids_are_unique<BoundaryTs...>(); concept HaveUniqueBoundaryIds = boundary_ids_are_unique<BoundaryTs...>();
@@ -181,79 +196,104 @@ export namespace mean_field::utils::domain {
concept HaveUniqueBoundaryTypes = BoundaryTypesAreUnique<BoundaryTs...>::value; concept HaveUniqueBoundaryTypes = BoundaryTypesAreUnique<BoundaryTs...>::value;
template <IsMaterial... MaterialTs> template <IsMaterial... MaterialTs>
requires(HaveUniqueMaterialIds<MaterialTs...> && HaveUniqueMaterialDomains<MaterialTs...>) requires(HaveUniqueMaterialIds<MaterialTs...> &&
HaveUniqueMaterialDomains<MaterialTs...>)
struct MaterialList { struct MaterialList {
static constexpr std::size_t count = sizeof...(MaterialTs); static constexpr std::size_t count = sizeof...(MaterialTs);
[[nodiscard]] [[nodiscard]]
static constexpr std::array< static constexpr std::array<MaterialDescriptor, count>
MaterialDescriptor, descriptors() noexcept {
count> descriptors() noexcept { return {MaterialDescriptor{.name = MaterialTs::domain_type::name,
return {MaterialDescriptor{.name = MaterialTs::domain_type::name, .id = MaterialTs::id}...}; .id = MaterialTs::id}...};
} }
}; };
template <IsBoundaryAttr... BoundaryTs> template <IsBoundaryAttr... BoundaryTs>
requires(HaveUniqueBoundaryIds<BoundaryTs...> && HaveUniqueBoundaryTypes<BoundaryTs...>) requires(HaveUniqueBoundaryIds<BoundaryTs...> &&
HaveUniqueBoundaryTypes<BoundaryTs...>)
struct BoundaryList { struct BoundaryList {
static constexpr std::size_t count = sizeof...(BoundaryTs); static constexpr std::size_t count = sizeof...(BoundaryTs);
[[nodiscard]] [[nodiscard]]
static constexpr std::array< static constexpr std::array<BoundaryDescriptor, count>
BoundaryDescriptor, descriptors() noexcept {
count> descriptors() noexcept { return {BoundaryDescriptor{.name = BoundaryTs::boundary_type::name,
return {BoundaryDescriptor{.name = BoundaryTs::boundary_type::name, .id = BoundaryTs::id}...}; .id = BoundaryTs::id}...};
} }
}; };
template <typename DomainT, typename MaterialListT> struct DomainMaterialResolver; template <typename DomainT, typename MaterialListT>
struct DomainMaterialResolver;
template <IsDomain DomainT, IsMaterial... MaterialTs> template <IsDomain DomainT, IsMaterial... MaterialTs>
struct DomainMaterialResolver<DomainT, MaterialList<MaterialTs...>> { struct DomainMaterialResolver<DomainT, MaterialList<MaterialTs...>> {
static constexpr bool registered = (std::is_same_v<DomainT, typename MaterialTs::domain_type> || ...);
[[nodiscard]]
static constexpr bool contains_attribute(int materialId) noexcept {
return ((std::is_same_v<DomainT, typename MaterialTs::domain_type> && MaterialTs::id == materialId) || ...);
}
};
template <IsDomain... DomainTs, IsMaterial... MaterialTs>
struct DomainMaterialResolver<DomainSet<DomainTs...>, MaterialList<MaterialTs...>> {
static constexpr bool registered = static constexpr bool registered =
(DomainMaterialResolver<DomainTs, MaterialList<MaterialTs...>>::registered && ...); (std::is_same_v<DomainT, typename MaterialTs::domain_type> || ...);
[[nodiscard]] [[nodiscard]]
static constexpr bool contains_attribute(int materialId) noexcept { static constexpr bool contains_attribute(int materialId) noexcept {
return ( return ((std::is_same_v<DomainT, typename MaterialTs::domain_type> &&
DomainMaterialResolver<DomainTs, MaterialList<MaterialTs...>>::contains_attribute(materialId) || ... MaterialTs::id == materialId) ||
); ...);
}
};
template <typename BoundaryT, typename BoundaryListT> struct BoundaryAttributeResolver;
template <IsBoundary BoundaryT, IsBoundaryAttr... BoundaryTs>
struct BoundaryAttributeResolver<BoundaryT, BoundaryList<BoundaryTs...>> {
static constexpr bool registered = (std::is_same_v<BoundaryT, typename BoundaryTs::boundary_type> || ...);
[[nodiscard]]
static constexpr bool matches_attribute(int boundaryId) noexcept {
return (
(std::is_same_v<BoundaryT, typename BoundaryTs::boundary_type> && BoundaryTs::id == boundaryId) || ...
);
} }
[[nodiscard]] [[nodiscard]]
static consteval int attribute() { static consteval int attribute() {
static_assert( static_assert(registered,
registered, "Requested boundary is not registered " "Requested domain is not registered in this schema.");
"in this schema."
); int result = 0;
((std::is_same_v<DomainT, typename MaterialTs::domain_type>
? result = MaterialTs::id
: result),
...);
return result;
}
};
template <IsDomain... DomainTs, IsMaterial... MaterialTs>
struct DomainMaterialResolver<DomainSet<DomainTs...>,
MaterialList<MaterialTs...>> {
static constexpr bool registered =
(DomainMaterialResolver<DomainTs,
MaterialList<MaterialTs...>>::registered &&
...);
[[nodiscard]]
static constexpr bool contains_attribute(int materialId) noexcept {
return (DomainMaterialResolver<DomainTs, MaterialList<MaterialTs...>>::
contains_attribute(materialId) ||
...);
}
};
template <typename BoundaryT, typename BoundaryListT>
struct BoundaryAttributeResolver;
template <IsBoundary BoundaryT, IsBoundaryAttr... BoundaryTs>
struct BoundaryAttributeResolver<BoundaryT, BoundaryList<BoundaryTs...>> {
static constexpr bool registered =
(std::is_same_v<BoundaryT, typename BoundaryTs::boundary_type> || ...);
[[nodiscard]]
static constexpr bool matches_attribute(int boundaryId) noexcept {
return ((std::is_same_v<BoundaryT, typename BoundaryTs::boundary_type> &&
BoundaryTs::id == boundaryId) ||
...);
}
[[nodiscard]]
static consteval int attribute() {
static_assert(registered, "Requested boundary is not registered "
"in this schema.");
int result = 0; int result = 0;
((std::is_same_v<BoundaryT, typename BoundaryTs::boundary_type> ? result = BoundaryTs::id : result), ...); ((std::is_same_v<BoundaryT, typename BoundaryTs::boundary_type>
? result = BoundaryTs::id
: result),
...);
return result; return result;
} }
@@ -261,14 +301,16 @@ export namespace mean_field::utils::domain {
template <typename T> constexpr bool is_material_list_v = false; template <typename T> constexpr bool is_material_list_v = false;
template <IsMaterial... MaterialTs> constexpr bool is_material_list_v<MaterialList<MaterialTs...>> = true; template <IsMaterial... MaterialTs>
constexpr bool is_material_list_v<MaterialList<MaterialTs...>> = true;
template <typename T> template <typename T>
concept IsMaterialList = is_material_list_v<T>; concept IsMaterialList = is_material_list_v<T>;
template <typename T> constexpr bool is_boundary_list_v = false; template <typename T> constexpr bool is_boundary_list_v = false;
template <IsBoundaryAttr... BoundaryTs> constexpr bool is_boundary_list_v<BoundaryList<BoundaryTs...>> = true; template <IsBoundaryAttr... BoundaryTs>
constexpr bool is_boundary_list_v<BoundaryList<BoundaryTs...>> = true;
template <typename T> template <typename T>
concept IsBoundaryList = is_boundary_list_v<T>; concept IsBoundaryList = is_boundary_list_v<T>;
@@ -295,7 +337,8 @@ export namespace mean_field::utils::domain {
template <typename T> constexpr bool is_relation_list_v = false; template <typename T> constexpr bool is_relation_list_v = false;
template <IsRelation... RelationTs> constexpr bool is_relation_list_v<RelationList<RelationTs...>> = true; template <IsRelation... RelationTs>
constexpr bool is_relation_list_v<RelationList<RelationTs...>> = true;
template <typename T> template <typename T>
concept IsRelationList = is_relation_list_v<T>; concept IsRelationList = is_relation_list_v<T>;
@@ -308,36 +351,51 @@ export namespace mean_field::utils::domain {
* DomainBoundary references both a boundary and one or * DomainBoundary references both a boundary and one or
* two domains. * two domains.
*/ */
template <IsRelation RelationT, IsMaterialList MaterialsT, IsBoundaryList BoundariesT> template <IsRelation RelationT, IsMaterialList MaterialsT,
IsBoundaryList BoundariesT>
struct RelationUsesRegisteredEntities; struct RelationUsesRegisteredEntities;
template <IsDomainOrSet DomainT, IsMaterialList MaterialsT, IsBoundaryList BoundariesT> template <IsDomainOrSet DomainT, IsMaterialList MaterialsT,
struct RelationUsesRegisteredEntities<Connected<DomainT>, MaterialsT, BoundariesT> IsBoundaryList BoundariesT>
: std::bool_constant<DomainMaterialResolver<DomainT, MaterialsT>::registered> { }; struct RelationUsesRegisteredEntities<Connected<DomainT>, MaterialsT,
BoundariesT>
: std::bool_constant<
DomainMaterialResolver<DomainT, MaterialsT>::registered> {};
template <IsDomainOrSet InnerT, IsDomainOrSet OuterT, IsMaterialList MaterialsT, IsBoundaryList BoundariesT> template <IsDomainOrSet InnerT, IsDomainOrSet OuterT, IsMaterialList MaterialsT,
struct RelationUsesRegisteredEntities<Inscribed<InnerT, OuterT>, MaterialsT, BoundariesT> IsBoundaryList BoundariesT>
struct RelationUsesRegisteredEntities<Inscribed<InnerT, OuterT>, MaterialsT,
BoundariesT>
: std::bool_constant< : std::bool_constant<
DomainMaterialResolver<InnerT, MaterialsT>::registered && DomainMaterialResolver<InnerT, MaterialsT>::registered &&
DomainMaterialResolver<OuterT, MaterialsT>::registered> {}; DomainMaterialResolver<OuterT, MaterialsT>::registered> {};
template <IsBoundary BoundaryT, IsDomainOrSet... DomainTs, IsMaterialList MaterialsT, IsBoundaryList BoundariesT> template <IsBoundary BoundaryT, IsDomainOrSet... DomainTs,
struct RelationUsesRegisteredEntities<DomainBoundary<BoundaryT, DomainTs...>, MaterialsT, BoundariesT> IsMaterialList MaterialsT, IsBoundaryList BoundariesT>
struct RelationUsesRegisteredEntities<DomainBoundary<BoundaryT, DomainTs...>,
MaterialsT, BoundariesT>
: std::bool_constant< : std::bool_constant<
BoundaryAttributeResolver<BoundaryT, BoundariesT>::registered && BoundaryAttributeResolver<BoundaryT, BoundariesT>::registered &&
(DomainMaterialResolver<DomainTs, MaterialsT>::registered && ...)> {}; (DomainMaterialResolver<DomainTs, MaterialsT>::registered && ...)> {};
template <IsMaterialList MaterialsT, IsBoundaryList BoundariesT, IsRelationList RelationsT> template <IsMaterialList MaterialsT, IsBoundaryList BoundariesT,
IsRelationList RelationsT>
struct RelationsUseRegisteredEntities; struct RelationsUseRegisteredEntities;
template <IsMaterialList MaterialsT, IsBoundaryList BoundariesT, IsRelation... RelationTs> template <IsMaterialList MaterialsT, IsBoundaryList BoundariesT,
struct RelationsUseRegisteredEntities<MaterialsT, BoundariesT, RelationList<RelationTs...>> IsRelation... RelationTs>
: std::bool_constant<(RelationUsesRegisteredEntities<RelationTs, MaterialsT, BoundariesT>::value && ...)> { }; struct RelationsUseRegisteredEntities<MaterialsT, BoundariesT,
RelationList<RelationTs...>>
: std::bool_constant<(RelationUsesRegisteredEntities<RelationTs, MaterialsT,
BoundariesT>::value &&
...)> {};
template <typename MaterialsT, typename BoundariesT, typename RelationsT> template <typename MaterialsT, typename BoundariesT, typename RelationsT>
concept HaveValidRelationEntities = RelationsUseRegisteredEntities<MaterialsT, BoundariesT, RelationsT>::value; concept HaveValidRelationEntities =
RelationsUseRegisteredEntities<MaterialsT, BoundariesT, RelationsT>::value;
template <IsMaterialList Materials, IsBoundaryList Boundaries, IsRelationList Relations> template <IsMaterialList Materials, IsBoundaryList Boundaries,
IsRelationList Relations>
requires HaveValidRelationEntities<Materials, Boundaries, Relations> requires HaveValidRelationEntities<Materials, Boundaries, Relations>
struct DomainSchema { struct DomainSchema {
using materials_type = Materials; using materials_type = Materials;
@@ -369,12 +427,20 @@ export namespace mean_field::utils::domain {
template <IsDomainOrSet DomainT> template <IsDomainOrSet DomainT>
[[nodiscard]] [[nodiscard]]
static constexpr bool attribute_belongs_to(int materialId) noexcept { static constexpr bool attribute_belongs_to(int materialId) noexcept {
static_assert( static_assert(contains_domain<DomainT>(),
contains_domain<DomainT>(), "Requested domain is not completely " "Requested domain is not completely "
"registered in this schema." "registered in this schema.");
);
return DomainMaterialResolver<DomainT, Materials>::contains_attribute(materialId); return DomainMaterialResolver<DomainT, Materials>::contains_attribute(
materialId);
}
template <IsDomain DomainT>
[[nodiscard]]
static consteval int material_attribute() noexcept {
static_assert(contains_domain<DomainT>(),
"Requested domain is not registered in this schema.");
return DomainMaterialResolver<DomainT, Materials>::attribute();
} }
template <IsBoundary BoundaryT> template <IsBoundary BoundaryT>
@@ -392,19 +458,21 @@ export namespace mean_field::utils::domain {
template <IsBoundary BoundaryT> template <IsBoundary BoundaryT>
[[nodiscard]] [[nodiscard]]
static constexpr bool boundary_attribute_matches(int boundaryId) noexcept { static constexpr bool boundary_attribute_matches(int boundaryId) noexcept {
static_assert( static_assert(contains_boundary<BoundaryT>(),
contains_boundary<BoundaryT>(), "Requested boundary is not registered " "Requested boundary is not registered "
"in this schema." "in this schema.");
);
return BoundaryAttributeResolver<BoundaryT, Boundaries>::matches_attribute(boundaryId); return BoundaryAttributeResolver<BoundaryT, Boundaries>::matches_attribute(
boundaryId);
} }
}; };
template <typename S> constexpr bool is_schema_v = false; template <typename S> constexpr bool is_schema_v = false;
template <IsMaterialList Materials, IsBoundaryList Boundaries, IsRelationList Relations> template <IsMaterialList Materials, IsBoundaryList Boundaries,
constexpr bool is_schema_v<DomainSchema<Materials, Boundaries, Relations>> = true; IsRelationList Relations>
constexpr bool is_schema_v<DomainSchema<Materials, Boundaries, Relations>> =
true;
template <typename T> template <typename T>
concept IsSchema = is_schema_v<T>; concept IsSchema = is_schema_v<T>;
@@ -465,7 +533,8 @@ export namespace mean_field::utils::domain {
std::optional<int> secondMaterialId = std::nullopt; std::optional<int> secondMaterialId = std::nullopt;
}; };
std::optional<DomainBoundaryDiagnostics> domainBoundaryDiagnostics = std::nullopt; std::optional<DomainBoundaryDiagnostics> domainBoundaryDiagnostics =
std::nullopt;
[[nodiscard]] [[nodiscard]]
bool valid() const noexcept { bool valid() const noexcept {
@@ -480,19 +549,18 @@ export namespace mean_field::utils::domain {
template <IsRelation RelationT> struct RelationValidator; template <IsRelation RelationT> struct RelationValidator;
template <IsDomainOrSet InnerT, IsDomainOrSet OuterT> struct RelationValidator<Inscribed<InnerT, OuterT>> { template <IsDomainOrSet InnerT, IsDomainOrSet OuterT>
struct RelationValidator<Inscribed<InnerT, OuterT>> {
template <IsSchema SchemaT> template <IsSchema SchemaT>
[[nodiscard]] [[nodiscard]]
static RelationValidationResult validate(const mfem::Mesh &mesh) { static RelationValidationResult validate(const mfem::Mesh &mesh) {
static_assert( static_assert(SchemaT::template contains_domain<InnerT>(),
SchemaT::template contains_domain<InnerT>(), "The inner domain of Inscribed is not " "The inner domain of Inscribed is not "
"registered in the supplied schema." "registered in the supplied schema.");
);
static_assert( static_assert(SchemaT::template contains_domain<OuterT>(),
SchemaT::template contains_domain<OuterT>(), "The outer domain of Inscribed is not " "The outer domain of Inscribed is not "
"registered in the supplied schema." "registered in the supplied schema.");
);
bool foundInnerElement = false; bool foundInnerElement = false;
bool foundOuterElement = false; bool foundOuterElement = false;
@@ -501,9 +569,13 @@ export namespace mean_field::utils::domain {
for (int elementId = 0; elementId < mesh.GetNE(); ++elementId) { for (int elementId = 0; elementId < mesh.GetNE(); ++elementId) {
const int materialId = mesh.GetAttribute(elementId); const int materialId = mesh.GetAttribute(elementId);
foundInnerElement = foundInnerElement || SchemaT::template attribute_belongs_to<InnerT>(materialId); foundInnerElement =
foundInnerElement ||
SchemaT::template attribute_belongs_to<InnerT>(materialId);
foundOuterElement = foundOuterElement || SchemaT::template attribute_belongs_to<OuterT>(materialId); foundOuterElement =
foundOuterElement ||
SchemaT::template attribute_belongs_to<OuterT>(materialId);
} }
if (!foundInnerElement) { if (!foundInnerElement) {
@@ -521,12 +593,12 @@ export namespace mean_field::utils::domain {
mesh.GetFaceElements(faceId, &firstElementId, &secondElementId); mesh.GetFaceElements(faceId, &firstElementId, &secondElementId);
const bool firstIsInner = const bool firstIsInner =
firstElementId >= 0 && firstElementId >= 0 && SchemaT::template attribute_belongs_to<InnerT>(
SchemaT::template attribute_belongs_to<InnerT>(mesh.GetAttribute(firstElementId)); mesh.GetAttribute(firstElementId));
const bool secondIsInner = const bool secondIsInner = secondElementId >= 0 &&
secondElementId >= 0 && SchemaT::template attribute_belongs_to<InnerT>(
SchemaT::template attribute_belongs_to<InnerT>(mesh.GetAttribute(secondElementId)); mesh.GetAttribute(secondElementId));
if (firstIsInner == secondIsInner) { if (firstIsInner == secondIsInner) {
continue; continue;
@@ -534,31 +606,32 @@ export namespace mean_field::utils::domain {
foundInnerBoundary = true; foundInnerBoundary = true;
const int innerElementId = firstIsInner ? firstElementId : secondElementId; const int innerElementId =
firstIsInner ? firstElementId : secondElementId;
const int adjacentElementId = firstIsInner ? secondElementId : firstElementId; const int adjacentElementId =
firstIsInner ? secondElementId : firstElementId;
if (adjacentElementId < 0) { if (adjacentElementId < 0) {
return { return {.failure =
.failure = RelationValidationFailure::InnerDomainTouchesMeshBoundary, RelationValidationFailure::InnerDomainTouchesMeshBoundary,
.inscribedDiagnostics = std::make_optional<RelationValidationResult::InscribedDiagnostics>( .inscribedDiagnostics = std::make_optional<
{.faceId = faceId, .innerElementId = innerElementId} RelationValidationResult::InscribedDiagnostics>(
) {.faceId = faceId, .innerElementId = innerElementId})};
};
} }
const int adjacentMaterialId = mesh.GetAttribute(adjacentElementId); const int adjacentMaterialId = mesh.GetAttribute(adjacentElementId);
if (!SchemaT::template attribute_belongs_to<OuterT>(adjacentMaterialId)) { if (!SchemaT::template attribute_belongs_to<OuterT>(adjacentMaterialId)) {
return { return {
.failure = RelationValidationFailure::InnerDomainTouchesUnexpectedMaterial, .failure =
.inscribedDiagnostics = std::make_optional<RelationValidationResult::InscribedDiagnostics>( RelationValidationFailure::InnerDomainTouchesUnexpectedMaterial,
.inscribedDiagnostics = std::make_optional<
RelationValidationResult::InscribedDiagnostics>(
{.faceId = faceId, {.faceId = faceId,
.innerElementId = innerElementId, .innerElementId = innerElementId,
.adjacentElementId = adjacentElementId, .adjacentElementId = adjacentElementId,
.adjacentMaterialId = adjacentMaterialId} .adjacentMaterialId = adjacentMaterialId})};
)
};
} }
} }
@@ -574,13 +647,13 @@ export namespace mean_field::utils::domain {
template <IsSchema SchemaT> template <IsSchema SchemaT>
[[nodiscard]] [[nodiscard]]
static RelationValidationResult validate(const mfem::Mesh &mesh) { static RelationValidationResult validate(const mfem::Mesh &mesh) {
static_assert( static_assert(SchemaT::template contains_domain<DomainT>(),
SchemaT::template contains_domain<DomainT>(), "Connected refers to a domain which is " "Connected refers to a domain which is "
"not completely registered in the " "not completely registered in the "
"supplied DomainSchema." "supplied DomainSchema.");
);
std::vector<bool> belongsToDomain(static_cast<std::size_t>(mesh.GetNE()), false); std::vector<bool> belongsToDomain(static_cast<std::size_t>(mesh.GetNE()),
false);
int domainElementCount = 0; int domainElementCount = 0;
int firstDomainElement = -1; int firstDomainElement = -1;
@@ -588,7 +661,8 @@ export namespace mean_field::utils::domain {
for (int elementId = 0; elementId < mesh.GetNE(); ++elementId) { for (int elementId = 0; elementId < mesh.GetNE(); ++elementId) {
const int materialId = mesh.GetAttribute(elementId); const int materialId = mesh.GetAttribute(elementId);
const bool belongs = SchemaT::template attribute_belongs_to<DomainT>(materialId); const bool belongs =
SchemaT::template attribute_belongs_to<DomainT>(materialId);
belongsToDomain[static_cast<std::size_t>(elementId)] = belongs; belongsToDomain[static_cast<std::size_t>(elementId)] = belongs;
@@ -604,15 +678,14 @@ export namespace mean_field::utils::domain {
} }
if (domainElementCount == 0) { if (domainElementCount == 0) {
return { return {.failure = RelationValidationFailure::DomainAbsent,
.failure = RelationValidationFailure::DomainAbsent, .connectedDiagnostics = std::make_optional<
.connectedDiagnostics = std::make_optional<RelationValidationResult::ConnectedDiagnostics>( RelationValidationResult::ConnectedDiagnostics>(
{.domainElementCount = 0, .visitedElementCount = 0} {.domainElementCount = 0, .visitedElementCount = 0})};
)
};
} }
std::vector<std::vector<int>> adjacency(static_cast<std::size_t>(mesh.GetNE())); std::vector<std::vector<int>> adjacency(
static_cast<std::size_t>(mesh.GetNE()));
for (int faceId = 0; faceId < mesh.GetNumFaces(); ++faceId) { for (int faceId = 0; faceId < mesh.GetNumFaces(); ++faceId) {
int firstElementId = -1; int firstElementId = -1;
@@ -624,17 +697,21 @@ export namespace mean_field::utils::domain {
continue; continue;
} }
const bool firstBelongs = belongsToDomain[static_cast<std::size_t>(firstElementId)]; const bool firstBelongs =
belongsToDomain[static_cast<std::size_t>(firstElementId)];
const bool secondBelongs = belongsToDomain[static_cast<std::size_t>(secondElementId)]; const bool secondBelongs =
belongsToDomain[static_cast<std::size_t>(secondElementId)];
if (!(firstBelongs && secondBelongs)) { if (!(firstBelongs && secondBelongs)) {
continue; continue;
} }
adjacency[static_cast<std::size_t>(firstElementId)].push_back(secondElementId); adjacency[static_cast<std::size_t>(firstElementId)].push_back(
secondElementId);
adjacency[static_cast<std::size_t>(secondElementId)].push_back(firstElementId); adjacency[static_cast<std::size_t>(secondElementId)].push_back(
firstElementId);
} }
std::vector<bool> visited(static_cast<std::size_t>(mesh.GetNE()), false); std::vector<bool> visited(static_cast<std::size_t>(mesh.GetNE()), false);
@@ -660,7 +737,8 @@ export namespace mean_field::utils::domain {
++visitedElementCount; ++visitedElementCount;
for (const int neighborElementId : adjacency[static_cast<std::size_t>(elementId)]) { for (const int neighborElementId :
adjacency[static_cast<std::size_t>(elementId)]) {
if (!visited[static_cast<std::size_t>(neighborElementId)]) { if (!visited[static_cast<std::size_t>(neighborElementId)]) {
pending.push_back(neighborElementId); pending.push_back(neighborElementId);
} }
@@ -668,11 +746,10 @@ export namespace mean_field::utils::domain {
} }
if (visitedElementCount == domainElementCount) { if (visitedElementCount == domainElementCount) {
return { return {.connectedDiagnostics = std::make_optional<
.connectedDiagnostics = std::make_optional<RelationValidationResult::ConnectedDiagnostics>( RelationValidationResult::ConnectedDiagnostics>(
{.domainElementCount = domainElementCount, .visitedElementCount = visitedElementCount} {.domainElementCount = domainElementCount,
) .visitedElementCount = visitedElementCount})};
};
} }
int disconnectedElementId = -1; int disconnectedElementId = -1;
@@ -689,12 +766,11 @@ export namespace mean_field::utils::domain {
return { return {
.failure = RelationValidationFailure::DomainDisconnected, .failure = RelationValidationFailure::DomainDisconnected,
.connectedDiagnostics = std::make_optional<RelationValidationResult::ConnectedDiagnostics>( .connectedDiagnostics =
std::make_optional<RelationValidationResult::ConnectedDiagnostics>(
{.elementId = disconnectedElementId, {.elementId = disconnectedElementId,
.domainElementCount = domainElementCount, .domainElementCount = domainElementCount,
.visitedElementCount = visitedElementCount} .visitedElementCount = visitedElementCount})};
)
};
} }
}; };
@@ -703,23 +779,19 @@ export namespace mean_field::utils::domain {
template <IsSchema SchemaT> template <IsSchema SchemaT>
[[nodiscard]] [[nodiscard]]
static RelationValidationResult validate(const mfem::Mesh &mesh) { static RelationValidationResult validate(const mfem::Mesh &mesh) {
static_assert( static_assert(sizeof...(DomainTs) == 1 || sizeof...(DomainTs) == 2,
sizeof...(DomainTs) == 1 || sizeof...(DomainTs) == 2, "DomainBoundary requires exactly one or two domains.");
"DomainBoundary requires exactly one or two domains."
);
static_assert( static_assert(SchemaT::template contains_boundary<BoundaryT>(),
SchemaT::template contains_boundary<BoundaryT>(), "DomainBoundary refers to a boundary which is not " "DomainBoundary refers to a boundary which is not "
"registered in the supplied DomainSchema." "registered in the supplied DomainSchema.");
);
static_assert( static_assert((SchemaT::template contains_domain<DomainTs>() && ...),
(SchemaT::template contains_domain<DomainTs>() && ...),
"DomainBoundary refers to a domain which is not " "DomainBoundary refers to a domain which is not "
"completely registered in the supplied DomainSchema." "completely registered in the supplied DomainSchema.");
);
constexpr int expectedBoundaryAttribute = SchemaT::template boundary_attribute<BoundaryT>(); constexpr int expectedBoundaryAttribute =
SchemaT::template boundary_attribute<BoundaryT>();
using DomainsTuple = std::tuple<DomainTs...>; using DomainsTuple = std::tuple<DomainTs...>;
@@ -732,42 +804,47 @@ export namespace mean_field::utils::domain {
* one here; instead, every boundary element on an expected * one here; instead, every boundary element on an expected
* face must carry the expected semantic boundary attribute. * face must carry the expected semantic boundary attribute.
*/ */
std::vector<std::vector<int>> boundaryElementsByFace(static_cast<std::size_t>(mesh.GetNumFaces())); std::vector<std::vector<int>> boundaryElementsByFace(
static_cast<std::size_t>(mesh.GetNumFaces()));
for (int boundaryElementId = 0; boundaryElementId < mesh.GetNBE(); ++boundaryElementId) { for (int boundaryElementId = 0; boundaryElementId < mesh.GetNBE();
++boundaryElementId) {
const int faceId = mesh.GetBdrElementFaceIndex(boundaryElementId); const int faceId = mesh.GetBdrElementFaceIndex(boundaryElementId);
if (faceId >= 0 && faceId < mesh.GetNumFaces()) { if (faceId >= 0 && faceId < mesh.GetNumFaces()) {
boundaryElementsByFace[static_cast<std::size_t>(faceId)].push_back(boundaryElementId); boundaryElementsByFace[static_cast<std::size_t>(faceId)].push_back(
boundaryElementId);
} }
} }
/* /*
* Build detailed diagnostics for one face. * Build detailed diagnostics for one face.
*/ */
const auto make_diagnostics = [&mesh, expectedBoundaryAttribute]( const auto make_diagnostics =
[&mesh, expectedBoundaryAttribute](
int faceId, int boundaryElementId, int faceId, int boundaryElementId,
std::optional<int> actualBoundaryAttribute std::optional<int> actualBoundaryAttribute) {
) {
RelationValidationResult::DomainBoundaryDiagnostics diagnostics{ RelationValidationResult::DomainBoundaryDiagnostics diagnostics{
.faceId = faceId, .faceId = faceId,
.boundaryElementId = boundaryElementId, .boundaryElementId = boundaryElementId,
.expectedBoundaryAttribute = expectedBoundaryAttribute, .expectedBoundaryAttribute = expectedBoundaryAttribute,
.actualBoundaryAttribute = actualBoundaryAttribute .actualBoundaryAttribute = actualBoundaryAttribute};
};
if (faceId < 0 || faceId >= mesh.GetNumFaces()) { if (faceId < 0 || faceId >= mesh.GetNumFaces()) {
return diagnostics; return diagnostics;
} }
mesh.GetFaceElements(faceId, &diagnostics.firstElementId, &diagnostics.secondElementId); mesh.GetFaceElements(faceId, &diagnostics.firstElementId,
&diagnostics.secondElementId);
if (diagnostics.firstElementId >= 0) { if (diagnostics.firstElementId >= 0) {
diagnostics.firstMaterialId = mesh.GetAttribute(diagnostics.firstElementId); diagnostics.firstMaterialId =
mesh.GetAttribute(diagnostics.firstElementId);
} }
if (diagnostics.secondElementId >= 0) { if (diagnostics.secondElementId >= 0) {
diagnostics.secondMaterialId = mesh.GetAttribute(diagnostics.secondElementId); diagnostics.secondMaterialId =
mesh.GetAttribute(diagnostics.secondElementId);
} }
return diagnostics; return diagnostics;
@@ -789,7 +866,8 @@ export namespace mean_field::utils::domain {
* *
* Both adjacent volume elements must exist. * Both adjacent volume elements must exist.
*/ */
const auto has_required_topology = [](int firstElementId, int secondElementId) { const auto has_required_topology = [](int firstElementId,
int secondElementId) {
if constexpr (sizeof...(DomainTs) == 1) { if constexpr (sizeof...(DomainTs) == 1) {
const bool firstExists = firstElementId >= 0; const bool firstExists = firstElementId >= 0;
@@ -807,7 +885,8 @@ export namespace mean_field::utils::domain {
* *
* For two domains, ordering is intentionally irrelevant. * For two domains, ordering is intentionally irrelevant.
*/ */
const auto face_matches_domains = [&mesh](int firstElementId, int secondElementId) { const auto face_matches_domains = [&mesh](int firstElementId,
int secondElementId) {
if constexpr (sizeof...(DomainTs) == 1) { if constexpr (sizeof...(DomainTs) == 1) {
using DomainT = std::tuple_element_t<0, DomainsTuple>; using DomainT = std::tuple_element_t<0, DomainsTuple>;
@@ -837,11 +916,17 @@ export namespace mean_field::utils::domain {
const int secondMaterialId = mesh.GetAttribute(secondElementId); const int secondMaterialId = mesh.GetAttribute(secondElementId);
const bool forwardMatch = SchemaT::template attribute_belongs_to<FirstDomainT>(firstMaterialId) && const bool forwardMatch =
SchemaT::template attribute_belongs_to<SecondDomainT>(secondMaterialId); SchemaT::template attribute_belongs_to<FirstDomainT>(
firstMaterialId) &&
SchemaT::template attribute_belongs_to<SecondDomainT>(
secondMaterialId);
const bool reverseMatch = SchemaT::template attribute_belongs_to<SecondDomainT>(firstMaterialId) && const bool reverseMatch =
SchemaT::template attribute_belongs_to<FirstDomainT>(secondMaterialId); SchemaT::template attribute_belongs_to<SecondDomainT>(
firstMaterialId) &&
SchemaT::template attribute_belongs_to<FirstDomainT>(
secondMaterialId);
return forwardMatch || reverseMatch; return forwardMatch || reverseMatch;
} }
@@ -856,7 +941,8 @@ export namespace mean_field::utils::domain {
* exactly the topology/material interface declared by * exactly the topology/material interface declared by
* DomainBoundary. * DomainBoundary.
*/ */
for (int boundaryElementId = 0; boundaryElementId < mesh.GetNBE(); ++boundaryElementId) { for (int boundaryElementId = 0; boundaryElementId < mesh.GetNBE();
++boundaryElementId) {
const int boundaryAttribute = mesh.GetBdrAttribute(boundaryElementId); const int boundaryAttribute = mesh.GetBdrAttribute(boundaryElementId);
if (boundaryAttribute != expectedBoundaryAttribute) { if (boundaryAttribute != expectedBoundaryAttribute) {
@@ -873,23 +959,21 @@ export namespace mean_field::utils::domain {
mesh.GetFaceElements(faceId, &firstElementId, &secondElementId); mesh.GetFaceElements(faceId, &firstElementId, &secondElementId);
if (!has_required_topology(firstElementId, secondElementId)) { if (!has_required_topology(firstElementId, secondElementId)) {
return { return {.failure = RelationValidationFailure::
.failure = RelationValidationFailure::DomainBoundaryTaggedFaceHasWrongTopology, DomainBoundaryTaggedFaceHasWrongTopology,
.domainBoundaryDiagnostics = .domainBoundaryDiagnostics = std::make_optional<
std::make_optional<RelationValidationResult::DomainBoundaryDiagnostics>( RelationValidationResult::DomainBoundaryDiagnostics>(
make_diagnostics(faceId, boundaryElementId, boundaryAttribute) make_diagnostics(faceId, boundaryElementId,
) boundaryAttribute))};
};
} }
if (!face_matches_domains(firstElementId, secondElementId)) { if (!face_matches_domains(firstElementId, secondElementId)) {
return { return {.failure = RelationValidationFailure::
.failure = RelationValidationFailure::DomainBoundaryTaggedFaceTouchesUnexpectedMaterial, DomainBoundaryTaggedFaceTouchesUnexpectedMaterial,
.domainBoundaryDiagnostics = .domainBoundaryDiagnostics = std::make_optional<
std::make_optional<RelationValidationResult::DomainBoundaryDiagnostics>( RelationValidationResult::DomainBoundaryDiagnostics>(
make_diagnostics(faceId, boundaryElementId, boundaryAttribute) make_diagnostics(faceId, boundaryElementId,
) boundaryAttribute))};
};
} }
} }
@@ -917,32 +1001,32 @@ export namespace mean_field::utils::domain {
foundExpectedFace = true; foundExpectedFace = true;
const auto &boundaryElementIds = boundaryElementsByFace[static_cast<std::size_t>(faceId)]; const auto &boundaryElementIds =
boundaryElementsByFace[static_cast<std::size_t>(faceId)];
if (boundaryElementIds.empty()) { if (boundaryElementIds.empty()) {
return { return {
.failure = RelationValidationFailure::DomainBoundaryExpectedFaceIsUntagged, .failure =
.domainBoundaryDiagnostics = RelationValidationFailure::DomainBoundaryExpectedFaceIsUntagged,
std::make_optional<RelationValidationResult::DomainBoundaryDiagnostics>( .domainBoundaryDiagnostics = std::make_optional<
make_diagnostics(faceId, -1, std::nullopt) RelationValidationResult::DomainBoundaryDiagnostics>(
) make_diagnostics(faceId, -1, std::nullopt))};
};
} }
for (const int boundaryElementId : boundaryElementIds) { for (const int boundaryElementId : boundaryElementIds) {
const int actualBoundaryAttribute = mesh.GetBdrAttribute(boundaryElementId); const int actualBoundaryAttribute =
mesh.GetBdrAttribute(boundaryElementId);
if (actualBoundaryAttribute == expectedBoundaryAttribute) { if (actualBoundaryAttribute == expectedBoundaryAttribute) {
continue; continue;
} }
return { return {.failure = RelationValidationFailure::
.failure = RelationValidationFailure::DomainBoundaryExpectedFaceHasWrongAttribute, DomainBoundaryExpectedFaceHasWrongAttribute,
.domainBoundaryDiagnostics = .domainBoundaryDiagnostics = std::make_optional<
std::make_optional<RelationValidationResult::DomainBoundaryDiagnostics>( RelationValidationResult::DomainBoundaryDiagnostics>(
make_diagnostics(faceId, boundaryElementId, actualBoundaryAttribute) make_diagnostics(faceId, boundaryElementId,
) actualBoundaryAttribute))};
};
} }
} }
@@ -955,13 +1039,10 @@ export namespace mean_field::utils::domain {
* have returned a more specific diagnostic. * have returned a more specific diagnostic.
*/ */
if (!foundTaggedBoundary || !foundExpectedFace) { if (!foundTaggedBoundary || !foundExpectedFace) {
return { return {.failure = RelationValidationFailure::DomainBoundaryAbsent,
.failure = RelationValidationFailure::DomainBoundaryAbsent, .domainBoundaryDiagnostics = std::make_optional<
.domainBoundaryDiagnostics = RelationValidationResult::DomainBoundaryDiagnostics>(
std::make_optional<RelationValidationResult::DomainBoundaryDiagnostics>( make_diagnostics(-1, -1, std::nullopt))};
make_diagnostics(-1, -1, std::nullopt)
)
};
} }
return {}; return {};
@@ -1028,7 +1109,8 @@ export namespace mean_field::utils::domain {
[[nodiscard]] [[nodiscard]]
std::optional<std::size_t> first_failed_relation_index() const noexcept { std::optional<std::size_t> first_failed_relation_index() const noexcept {
for (std::size_t relationIndex = 0; relationIndex < relationResults.size(); ++relationIndex) { for (std::size_t relationIndex = 0; relationIndex < relationResults.size();
++relationIndex) {
if (!relationResults[relationIndex].valid()) { if (!relationResults[relationIndex].valid()) {
return relationIndex; return relationIndex;
} }
@@ -1038,7 +1120,8 @@ export namespace mean_field::utils::domain {
} }
}; };
template <IsSchema SchemaT, typename RelationListT> struct SchemaRelationValidator; template <IsSchema SchemaT, typename RelationListT>
struct SchemaRelationValidator;
template <IsSchema SchemaT, IsRelation... RelationTs> template <IsSchema SchemaT, IsRelation... RelationTs>
struct SchemaRelationValidator<SchemaT, RelationList<RelationTs...>> { struct SchemaRelationValidator<SchemaT, RelationList<RelationTs...>> {
@@ -1050,13 +1133,11 @@ export namespace mean_field::utils::domain {
std::size_t relationIndex = 0; std::size_t relationIndex = 0;
(schemaResult.relationResults.push_back( (schemaResult.relationResults.push_back(SchemaRelationValidationResult{
SchemaRelationValidationResult{
.relationIndex = relationIndex++, .relationIndex = relationIndex++,
.relationName = RelationTs::name, .relationName = RelationTs::name,
.result = RelationValidator<RelationTs>::template validate<SchemaT>(mesh) .result =
} RelationValidator<RelationTs>::template validate<SchemaT>(mesh)}),
),
...); ...);
return schemaResult; return schemaResult;
@@ -1071,22 +1152,38 @@ export namespace mean_field::utils::domain {
return SchemaRelationValidator<SchemaT, RelationsT>::validate(mesh); return SchemaRelationValidator<SchemaT, RelationsT>::validate(mesh);
} }
template <IsDomainOrSet DomainT, IsSchema SchemaT>
[[nodiscard]]
mfem::Array<int> make_attribute_marker(const mfem::Mesh &mesh) {
static_assert(SchemaT::template contains_domain<DomainT>(),
"Requested marker domain is not completely registered in the "
"supplied DomainSchema.");
mfem::Array<int> marker(mesh.attributes.Max());
for (int attribute = 1; attribute <= marker.Size(); ++attribute) {
marker[attribute - 1] =
SchemaT::template attribute_belongs_to<DomainT>(attribute) ? 1 : 0;
}
return marker;
}
using CoreEnvelopeVacuumDomainSchema = DomainSchema< using CoreEnvelopeVacuumDomainSchema = DomainSchema<
MaterialList<Material<Core, 1>, Material<Envelope, 2>, Material<Vacuum, 3>>, MaterialList<Material<Core, 1>, Material<Envelope, 2>, Material<Vacuum, 3>>,
BoundaryList<BoundaryAttribute<StellarSurface, 1>, BoundaryAttribute<InfinitySurface, 2>>, BoundaryList<BoundaryAttribute<StellarSurface, 1>,
BoundaryAttribute<InfinitySurface, 2>>,
RelationList< RelationList<
// All Domains must be fully connected // All Domains must be fully connected
Connected<Core>, Connected<Core>, Connected<Envelope>, Connected<Vacuum>,
Connected<Envelope>,
Connected<Vacuum>,
// Describe the topology of the mesh (core must be within envelope and the stellar domain (core + envelope) // Describe the topology of the mesh (core must be within envelope and
// must be inscribed within vacuum region // the stellar domain (core + envelope) must be inscribed within vacuum
Inscribed<Core, Envelope>, // region
Inscribed<Stellar, Vacuum>, Inscribed<Core, Envelope>, Inscribed<Stellar, Vacuum>,
// The stellar surface sits between the stellar and vacuum domain and the infinity surface sits at the // The stellar surface sits between the stellar and vacuum domain and
// outside of the vacuum domain // the infinity surface sits at the outside of the vacuum domain
DomainBoundary<StellarSurface, Stellar, Vacuum>, DomainBoundary<StellarSurface, Stellar, Vacuum>,
DomainBoundary<InfinitySurface, Vacuum>>>; DomainBoundary<InfinitySurface, Vacuum>>>;
} // namespace mean_field::utils::domain } // namespace mean_field::utils::domain

View File

@@ -1,5 +1,4 @@
module; module;
#include <expected>
#include <functional> #include <functional>
#include <string_view> #include <string_view>
@@ -9,16 +8,16 @@ module;
#include <XAD/XAD.hpp> #include <XAD/XAD.hpp>
export module mean_field:utils.misc; export module mean_field:utils.misc;
import :boundary.contexts; import :utils.domain;
export namespace mean_field::utils { export namespace mean_field::utils {
constexpr double APPROX_MAX_ACCEPTABLE_POTENTIAL_ERROR_SI_BURNING = 1e-4; constexpr double APPROX_MAX_ACCEPTABLE_POTENTIAL_ERROR_SI_BURNING = 1e-4;
bool is_vacuum( bool is_vacuum(const mfem::ElementTransformation &Tr,
const mfem::ElementTransformation &Tr, mfem::Array<mfem::Vector *> elvec) {
mfem::Array<mfem::Vector *> elvec using Schema = domain::CoreEnvelopeVacuumDomainSchema;
) {
if (Tr.Attribute == 3) { if (Schema::template attribute_belongs_to<domain::Vacuum>(Tr.Attribute)) {
const int size_elvec = elvec.Size(); const int size_elvec = elvec.Size();
for (int i = 0; i < size_elvec; i++) { for (int i = 0; i < size_elvec; i++) {
if (elvec[i]) { if (elvec[i]) {
@@ -30,11 +29,11 @@ export namespace mean_field::utils {
return false; return false;
} }
bool is_vacuum( bool is_vacuum(const mfem::ElementTransformation &Tr,
const mfem::ElementTransformation &Tr, const mfem::Array2D<mfem::DenseMatrix *> &elmats) {
const mfem::Array2D<mfem::DenseMatrix *> &elmats using Schema = domain::CoreEnvelopeVacuumDomainSchema;
) {
if (Tr.Attribute == 3) { if (Schema::template attribute_belongs_to<domain::Vacuum>(Tr.Attribute)) {
const int cols = elmats.NumCols(); const int cols = elmats.NumCols();
const int rows = elmats.NumRows(); const int rows = elmats.NumRows();
for (int rowID = 0; rowID < rows; rowID++) { for (int rowID = 0; rowID < rows; rowID++) {
@@ -66,13 +65,15 @@ export namespace mean_field::utils {
[[maybe_unused]] constexpr int PORT = 19916; [[maybe_unused]] constexpr int PORT = 19916;
template <typename T> template <typename T>
concept is_xad = std::is_same_v<T, xad::AReal<long double>> || std::is_same_v<T, xad::AReal<double>> || concept is_xad = std::is_same_v<T, xad::AReal<long double>> ||
std::is_same_v<T, xad::AReal<double>> ||
std::is_same_v<T, xad::AReal<float>>; std::is_same_v<T, xad::AReal<float>>;
template <typename T> template <typename T>
concept is_real = std::is_floating_point_v<T> || is_xad<T>; concept is_real = std::is_floating_point_v<T> || is_xad<T>;
template <is_real T> using EOS_P = std::function<T(const T &rho, const T &temp)>; template <is_real T>
using EOS_P = std::function<T(const T &rho, const T &temp)>;
enum class DOMAINS : uint8_t { enum class DOMAINS : uint8_t {
CORE = 1 << 0, CORE = 1 << 0,
@@ -82,35 +83,9 @@ export namespace mean_field::utils {
ALL = CORE | ENVELOPE | VACUUM ALL = CORE | ENVELOPE | VACUUM
}; };
DOMAINS operator|( DOMAINS operator|(DOMAINS lhs, DOMAINS rhs);
DOMAINS lhs,
DOMAINS rhs
);
DOMAINS operator&( DOMAINS operator&(DOMAINS lhs, DOMAINS rhs);
DOMAINS lhs,
DOMAINS rhs
);
void populate_element_mask(
const mfem::Mesh *mesh,
DOMAINS domain,
mfem::Array<int> &mask
);
void populate_domain_tdofs(
const mfem::ParFiniteElementSpace *fes,
const mfem::Array<int> &element_mask,
mfem::Array<int> &ess_tdof
);
std::expected<
boundary::Bounds,
boundary::BoundsError>
discover_bounds(
const mfem::Mesh *mesh,
int vacuum_attr
);
int get_mesh_order(const mfem::Mesh &mesh); int get_mesh_order(const mfem::Mesh &mesh);

View File

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

View File

@@ -1,6 +1,7 @@
#include <algorithm> #include <algorithm>
#include <array> #include <array>
#include <catch2/catch_test_macros.hpp> #include <catch2/catch_test_macros.hpp>
#include <cmath>
#include <cstddef> #include <cstddef>
#include <mfem.hpp> #include <mfem.hpp>
#include <mpi.h> #include <mpi.h>
@@ -79,6 +80,11 @@ namespace field_dof_map_test_utils {
concept CanMakeFieldDofMap = concept CanMakeFieldDofMap =
requires(const mfem::ParFiniteElementSpace &space) { field::make_field_dof_map<FieldT, Schema>(space); }; requires(const mfem::ParFiniteElementSpace &space) { field::make_field_dof_map<FieldT, Schema>(space); };
template <typename FieldT>
concept CanMakeFieldDofGridFunctionAdapter = requires(const mfem::ParFiniteElementSpace &space) {
field::make_field_dof_grid_function_adapter<FieldT, Schema>(space);
};
using AlternateSchema = domain::DomainSchema< using AlternateSchema = domain::DomainSchema<
domain::MaterialList< domain::MaterialList<
domain::Material<domain::Core, 11>, domain::Material<domain::Core, 11>,
@@ -90,7 +96,7 @@ namespace field_dof_map_test_utils {
TEST_CASE( TEST_CASE(
"Field DOF Map Preserves Canonical Bidirectional Indexing", "Field DOF Map Preserves Canonical Bidirectional Indexing",
tags::unit &tags::field tags::field_dof_unit
) { ) {
namespace field = mean_field::field; namespace field = mean_field::field;
@@ -166,7 +172,7 @@ TEST_CASE(
TEST_CASE( TEST_CASE(
"Field DOF Map Rejects Invalid Canonical Mappings", "Field DOF Map Rejects Invalid Canonical Mappings",
tags::unit &tags::field tags::field_dof_unit
) { ) {
namespace field = mean_field::field; namespace field = mean_field::field;
@@ -191,7 +197,7 @@ TEST_CASE(
TEST_CASE( TEST_CASE(
"Field DOF Map Rejects Out Of Range Index Queries", "Field DOF Map Rejects Out Of Range Index Queries",
tags::unit &tags::field tags::field_dof_unit
) { ) {
namespace field = mean_field::field; namespace field = mean_field::field;
@@ -212,7 +218,7 @@ TEST_CASE(
TEST_CASE( TEST_CASE(
"Field DOF Map Gather Selects Exactly The Active True DOFs", "Field DOF Map Gather Selects Exactly The Active True DOFs",
tags::unit &tags::field tags::field_dof_unit
) { ) {
namespace field = mean_field::field; namespace field = mean_field::field;
@@ -247,7 +253,7 @@ TEST_CASE(
TEST_CASE( TEST_CASE(
"Field DOF Map Scatter Produces The Canonical Supported Projection", "Field DOF Map Scatter Produces The Canonical Supported Projection",
tags::unit &tags::field tags::field_dof_unit
) { ) {
namespace field = mean_field::field; namespace field = mean_field::field;
@@ -281,7 +287,7 @@ TEST_CASE(
TEST_CASE( TEST_CASE(
"Field DOF Map Gather Scatter Projects A Full Vector Onto Field Support", "Field DOF Map Gather Scatter Projects A Full Vector Onto Field Support",
tags::unit &tags::field tags::field_dof_unit
) { ) {
namespace field = mean_field::field; namespace field = mean_field::field;
@@ -310,7 +316,7 @@ TEST_CASE(
TEST_CASE( TEST_CASE(
"Field DOF Map Scatter Into Preserves Unsupported True DOFs", "Field DOF Map Scatter Into Preserves Unsupported True DOFs",
tags::unit &tags::field tags::field_dof_unit
) { ) {
namespace field = mean_field::field; namespace field = mean_field::field;
@@ -337,7 +343,7 @@ TEST_CASE(
TEST_CASE( TEST_CASE(
"Field DOF Map Scatter Add Accumulates Only Onto Active True DOFs", "Field DOF Map Scatter Add Accumulates Only Onto Active True DOFs",
tags::unit &tags::field tags::field_dof_unit
) { ) {
namespace field = mean_field::field; namespace field = mean_field::field;
@@ -364,7 +370,7 @@ TEST_CASE(
TEST_CASE( TEST_CASE(
"Field DOF Map Operations Support MFEM Vector Views Without Resizing", "Field DOF Map Operations Support MFEM Vector Views Without Resizing",
tags::unit &tags::field tags::field_dof_unit
) { ) {
namespace field = mean_field::field; namespace field = mean_field::field;
@@ -406,7 +412,7 @@ TEST_CASE(
TEST_CASE( TEST_CASE(
"Field DOF Map Operations Reject Incompatible Vector Sizes", "Field DOF Map Operations Reject Incompatible Vector Sizes",
tags::unit &tags::field tags::field_dof_unit
) { ) {
namespace field = mean_field::field; namespace field = mean_field::field;
@@ -433,7 +439,7 @@ TEST_CASE(
TEST_CASE( TEST_CASE(
"Field DOF Map Identity Mapping Is An Exact Vector Identity", "Field DOF Map Identity Mapping Is An Exact Vector Identity",
tags::unit &tags::field tags::field_dof_unit
) { ) {
namespace field = mean_field::field; namespace field = mean_field::field;
@@ -463,7 +469,7 @@ TEST_CASE(
TEST_CASE( TEST_CASE(
"Field DOF Map Validates Field DOF Support Consistency", "Field DOF Map Validates Field DOF Support Consistency",
tags::unit &tags::field tags::field_dof_unit
) { ) {
namespace field = mean_field::field; namespace field = mean_field::field;
@@ -495,7 +501,7 @@ TEST_CASE(
TEST_CASE( TEST_CASE(
"Field DOF Map Factory Is Available Only For Spatial Registered Fields", "Field DOF Map Factory Is Available Only For Spatial Registered Fields",
tags::unit &tags::field tags::field_dof_unit
) { ) {
namespace field = mean_field::field; namespace field = mean_field::field;
@@ -509,12 +515,24 @@ TEST_CASE(
STATIC_REQUIRE_FALSE(field_dof_map_test_utils::CanMakeFieldDofMap<field::BarotropicConstant>); STATIC_REQUIRE_FALSE(field_dof_map_test_utils::CanMakeFieldDofMap<field::BarotropicConstant>);
STATIC_REQUIRE(field_dof_map_test_utils::CanMakeFieldDofGridFunctionAdapter<field::Density>);
STATIC_REQUIRE(field_dof_map_test_utils::CanMakeFieldDofGridFunctionAdapter<field::Enthalpy>);
STATIC_REQUIRE(field_dof_map_test_utils::CanMakeFieldDofGridFunctionAdapter<field::Gravity>);
STATIC_REQUIRE(field_dof_map_test_utils::CanMakeFieldDofGridFunctionAdapter<field::Displacement>);
STATIC_REQUIRE_FALSE(
field_dof_map_test_utils::CanMakeFieldDofGridFunctionAdapter<field::BarotropicConstant>
);
CHECK(true); CHECK(true);
} }
TEST_CASE( TEST_CASE(
"Field DOF Map Factory Exactly Preserves Density Support", "Field DOF Map Factory Exactly Preserves Density Support",
tags::integration &tags::field tags::field_dof_integration
) { ) {
namespace field = mean_field::field; namespace field = mean_field::field;
@@ -567,7 +585,7 @@ TEST_CASE(
TEST_CASE( TEST_CASE(
"Field DOF Map Factory Exactly Preserves H1 Enthalpy Support", "Field DOF Map Factory Exactly Preserves H1 Enthalpy Support",
tags::integration &tags::field tags::field_dof_integration
) { ) {
namespace field = mean_field::field; namespace field = mean_field::field;
@@ -614,7 +632,7 @@ TEST_CASE(
TEST_CASE( TEST_CASE(
"Field DOF Map Factory Produces Identity Maps For All Supported Fields", "Field DOF Map Factory Produces Identity Maps For All Supported Fields",
tags::integration &tags::field tags::field_dof_integration
) { ) {
namespace field = mean_field::field; namespace field = mean_field::field;
@@ -648,7 +666,7 @@ TEST_CASE(
TEST_CASE( TEST_CASE(
"Field DOF Map Factory Uses Schema Material Bindings Rather Than Numeric Conventions", "Field DOF Map Factory Uses Schema Material Bindings Rather Than Numeric Conventions",
tags::integration &tags::field tags::field_dof_integration
) { ) {
namespace field = mean_field::field; namespace field = mean_field::field;
@@ -681,7 +699,7 @@ TEST_CASE(
TEST_CASE( TEST_CASE(
"Field DOF Map Reduced Vectors Round Trip Through Real Field Support", "Field DOF Map Reduced Vectors Round Trip Through Real Field Support",
tags::integration &tags::field tags::field_dof_integration
) { ) {
namespace field = mean_field::field; namespace field = mean_field::field;
@@ -722,3 +740,245 @@ TEST_CASE(
} }
} }
} }
TEST_CASE(
"Field DOF Grid Function Adapter Gathers Exactly The Supported True DOFs",
tags::field_dof_integration
) {
namespace field = mean_field::field;
mfem::Mesh serialMesh = field_dof_map_test_utils::make_split_mesh();
mfem::ParMesh mesh(MPI_COMM_WORLD, serialMesh);
auto fec = field::Field<field::Density>::make_fec<field::Density::Scalar>(2);
auto finiteElementSpace =
field::Field<field::Density>::make_fespace<field::Density::Scalar>(mesh, *fec);
REQUIRE(finiteElementSpace != nullptr);
const field::FieldDofGridFunctionAdapter adapter =
field::make_field_dof_grid_function_adapter<field::Density, field_dof_map_test_utils::Schema>(
*finiteElementSpace
);
mfem::Vector full(adapter.dof_map().full_size());
for (int trueDof = 0; trueDof < full.Size(); ++trueDof) {
full(trueDof) = 1.25 + 0.375 * static_cast<double>(trueDof + 1);
}
mfem::ParGridFunction gridFunction(finiteElementSpace.get());
gridFunction.SetFromTrueDofs(full);
const mfem::Vector expected = adapter.dof_map().gather(full);
const mfem::Vector actual = adapter.gather(gridFunction);
REQUIRE(actual.Size() == expected.Size());
for (int reducedDof = 0; reducedDof < actual.Size(); ++reducedDof) {
CAPTURE(reducedDof);
CHECK(actual(reducedDof) == expected(reducedDof));
}
mfem::Vector output(adapter.dof_map().reduced_size());
adapter.gather(gridFunction, output);
for (int reducedDof = 0; reducedDof < output.Size(); ++reducedDof) {
CAPTURE(reducedDof);
CHECK(output(reducedDof) == expected(reducedDof));
}
}
TEST_CASE(
"Field DOF Grid Function Adapter Scatter Projects And Round Trips Reduced Fields",
tags::field_dof_integration
) {
namespace field = mean_field::field;
mfem::Mesh serialMesh = field_dof_map_test_utils::make_split_mesh();
mfem::ParMesh mesh(MPI_COMM_WORLD, serialMesh);
auto fec = field::Field<field::Enthalpy>::make_fec<field::Enthalpy::Scalar>(2);
auto finiteElementSpace =
field::Field<field::Enthalpy>::make_fespace<field::Enthalpy::Scalar>(mesh, *fec);
REQUIRE(finiteElementSpace != nullptr);
const field::FieldDofGridFunctionAdapter adapter =
field::make_field_dof_grid_function_adapter<field::Enthalpy, field_dof_map_test_utils::Schema>(
*finiteElementSpace
);
mfem::Vector reduced(adapter.dof_map().reduced_size());
for (int reducedDof = 0; reducedDof < reduced.Size(); ++reducedDof) {
reduced(reducedDof) = -0.75 + 0.0625 * static_cast<double>(reducedDof + 1);
}
mfem::ParGridFunction gridFunction(finiteElementSpace.get());
gridFunction = 91.0;
adapter.scatter(reduced, gridFunction);
mfem::Vector actualFull;
gridFunction.GetTrueDofs(actualFull);
const mfem::Vector expectedFull = adapter.dof_map().scatter(reduced);
REQUIRE(actualFull.Size() == expectedFull.Size());
for (int trueDof = 0; trueDof < actualFull.Size(); ++trueDof) {
CAPTURE(trueDof);
CHECK(actualFull(trueDof) == expectedFull(trueDof));
if (!adapter.dof_map().contains_true_dof(trueDof)) {
CHECK(actualFull(trueDof) == 0.0);
}
}
const mfem::Vector recovered = adapter.gather(gridFunction);
REQUIRE(recovered.Size() == reduced.Size());
for (int reducedDof = 0; reducedDof < reduced.Size(); ++reducedDof) {
CAPTURE(reducedDof);
CHECK(recovered(reducedDof) == reduced(reducedDof));
}
}
TEST_CASE(
"Field DOF Grid Function Adapter Scatter Into Preserves Unsupported True DOFs",
tags::field_dof_integration
) {
namespace field = mean_field::field;
mfem::Mesh serialMesh = field_dof_map_test_utils::make_split_mesh();
mfem::ParMesh mesh(MPI_COMM_WORLD, serialMesh);
auto fec = field::Field<field::Density>::make_fec<field::Density::Scalar>(2);
auto finiteElementSpace =
field::Field<field::Density>::make_fespace<field::Density::Scalar>(mesh, *fec);
REQUIRE(finiteElementSpace != nullptr);
const field::FieldDofGridFunctionAdapter adapter =
field::make_field_dof_grid_function_adapter<field::Density, field_dof_map_test_utils::Schema>(
*finiteElementSpace
);
mfem::Vector initialFull(adapter.dof_map().full_size());
for (int trueDof = 0; trueDof < initialFull.Size(); ++trueDof) {
initialFull(trueDof) = 40.0 + static_cast<double>(trueDof);
}
mfem::Vector reduced(adapter.dof_map().reduced_size());
for (int reducedDof = 0; reducedDof < reduced.Size(); ++reducedDof) {
reduced(reducedDof) = -10.0 - static_cast<double>(reducedDof);
}
mfem::ParGridFunction gridFunction(finiteElementSpace.get());
gridFunction.SetFromTrueDofs(initialFull);
adapter.scatter_into(reduced, gridFunction);
mfem::Vector actualFull;
gridFunction.GetTrueDofs(actualFull);
mfem::Vector expectedFull(initialFull);
adapter.dof_map().scatter_into(reduced, expectedFull);
REQUIRE(actualFull.Size() == expectedFull.Size());
for (int trueDof = 0; trueDof < actualFull.Size(); ++trueDof) {
CAPTURE(trueDof);
CHECK(actualFull(trueDof) == expectedFull(trueDof));
if (!adapter.dof_map().contains_true_dof(trueDof)) {
CHECK(actualFull(trueDof) == initialFull(trueDof));
}
}
}
TEST_CASE(
"Field DOF Grid Function Adapter Is Exact For Identity Vector Field Maps",
tags::field_dof_integration
) {
namespace field = mean_field::field;
mfem::Mesh serialMesh = field_dof_map_test_utils::make_split_mesh();
mfem::ParMesh mesh(MPI_COMM_WORLD, serialMesh);
auto fec = field::Field<field::Displacement>::make_fec<field::Displacement::Vector>(2);
auto finiteElementSpace =
field::Field<field::Displacement>::make_fespace<field::Displacement::Vector>(mesh, *fec);
REQUIRE(finiteElementSpace != nullptr);
const field::FieldDofGridFunctionAdapter adapter =
field::make_field_dof_grid_function_adapter<field::Displacement, field_dof_map_test_utils::Schema>(
*finiteElementSpace
);
REQUIRE(adapter.dof_map().is_identity());
mfem::Vector reduced(adapter.dof_map().reduced_size());
for (int dof = 0; dof < reduced.Size(); ++dof) {
reduced(dof) = std::sin(0.23 * static_cast<double>(dof + 1));
}
mfem::ParGridFunction gridFunction(finiteElementSpace.get());
adapter.scatter(reduced, gridFunction);
const mfem::Vector recovered = adapter.gather(gridFunction);
REQUIRE(recovered.Size() == reduced.Size());
for (int dof = 0; dof < reduced.Size(); ++dof) {
CAPTURE(dof);
CHECK(recovered(dof) == reduced(dof));
}
}
TEST_CASE(
"Field DOF Grid Function Adapter Rejects Incompatible Maps Spaces And Vectors",
tags::field_dof_integration
) {
namespace field = mean_field::field;
mfem::Mesh serialMesh = field_dof_map_test_utils::make_split_mesh();
mfem::ParMesh mesh(MPI_COMM_WORLD, serialMesh);
auto fec = field::Field<field::Density>::make_fec<field::Density::Scalar>(2);
auto finiteElementSpace =
field::Field<field::Density>::make_fespace<field::Density::Scalar>(mesh, *fec);
auto otherFec = field::Field<field::Density>::make_fec<field::Density::Scalar>(2);
auto otherFiniteElementSpace =
field::Field<field::Density>::make_fespace<field::Density::Scalar>(mesh, *otherFec);
REQUIRE(finiteElementSpace != nullptr);
REQUIRE(otherFiniteElementSpace != nullptr);
REQUIRE(finiteElementSpace->GetTrueVSize() == otherFiniteElementSpace->GetTrueVSize());
const field::FieldDofGridFunctionAdapter adapter =
field::make_field_dof_grid_function_adapter<field::Density, field_dof_map_test_utils::Schema>(
*finiteElementSpace
);
const mfem::Array<int> empty;
CHECK_THROWS_AS(
(field::FieldDofGridFunctionAdapter(
field::FieldDofMap(finiteElementSpace->GetTrueVSize() + 1, empty),
*finiteElementSpace
)),
std::invalid_argument
);
mfem::ParGridFunction gridFunction(finiteElementSpace.get());
mfem::ParGridFunction otherGridFunction(otherFiniteElementSpace.get());
mfem::Vector reduced(adapter.dof_map().reduced_size());
reduced = 1.0;
mfem::Vector wrongReduced(adapter.dof_map().reduced_size() + 1);
mfem::Vector wrongOutput(adapter.dof_map().reduced_size() + 1);
CHECK_THROWS_AS(adapter.gather(otherGridFunction), std::invalid_argument);
CHECK_THROWS_AS(adapter.scatter(reduced, otherGridFunction), std::invalid_argument);
CHECK_THROWS_AS(adapter.scatter_into(reduced, otherGridFunction), std::invalid_argument);
CHECK_THROWS_AS(adapter.gather(gridFunction, wrongOutput), std::invalid_argument);
CHECK_THROWS_AS(adapter.scatter(wrongReduced, gridFunction), std::invalid_argument);
CHECK_THROWS_AS(adapter.scatter_into(wrongReduced, gridFunction), std::invalid_argument);
}

View File

@@ -9,6 +9,20 @@ import test_helpers;
using namespace mean_field; using namespace mean_field;
namespace { namespace {
struct SerialMappingData {
explicit SerialMappingData(mfem::Mesh &mesh)
: compactification_fes(&mesh, &compactification_fec),
compactification_coordinate(&compactification_fes),
mapper(field_dof_test_utils::make_domain_mapper()) {
compactification_coordinate = 0.0;
}
mfem::H1_FECollection compactification_fec{1, 3};
mfem::FiniteElementSpace compactification_fes;
mfem::GridFunction compactification_coordinate;
mapping::DomainMapper mapper;
};
double compute_roche_surface_scale( double compute_roche_surface_scale(
const double rotation_fraction, const double rotation_fraction,
const double sine_theta_squared const double sine_theta_squared
@@ -29,7 +43,7 @@ namespace {
TEST_CASE( TEST_CASE(
"Centrifugal Integrator Matches Manufactured Cartesian Load", "Centrifugal Integrator Matches Manufactured Cartesian Load",
tags::unit &tags::solver &tags::integrator &tags::centrifugal tags::rotation_integrator_unit
) { ) {
constexpr int dim = 3; constexpr int dim = 3;
constexpr double density = 1.7; constexpr double density = 1.7;
@@ -49,13 +63,15 @@ TEST_CASE(
mfem::GridFunction displacement(&displacement_fes); mfem::GridFunction displacement(&displacement_fes);
displacement = 0.0; displacement = 0.0;
mapping::DomainMapper domain_mapper(displacement, 1.0, 2.0); SerialMappingData mapping_data(mesh);
mfem::Vector omega(dim); mfem::Vector omega(dim);
omega = 0.0; omega = 0.0;
omega(2) = omega_value; omega(2) = omega_value;
integrators::CentrifugalForceIntegrator integrator(domain_mapper, omega); integrators::CentrifugalForceIntegrator integrator(
mapping_data.mapper, displacement, mapping_data.compactification_coordinate, omega
);
const mfem::FiniteElement *velocity_element = velocity_fes.GetFE(0); const mfem::FiniteElement *velocity_element = velocity_fes.GetFE(0);
const mfem::FiniteElement *density_element = density_fes.GetFE(0); const mfem::FiniteElement *density_element = density_fes.GetFE(0);
@@ -66,8 +82,7 @@ TEST_CASE(
quadrature::Policy policy(std::move(rule_set)); quadrature::Policy policy(std::move(rule_set));
quadrature::RuleFactory quadrature_factory(std::move(policy)); quadrature::RuleFactory quadrature_factory(std::move(policy));
const quadrature::MappingKind mapping_kind = const quadrature::MappingKind mapping_kind = quadrature::MappingKind::general;
!domain_mapper.HasDisplacementField() ? quadrature::MappingKind::none : quadrature::MappingKind::general;
const int position_order = displacement_element->GetOrder(); const int position_order = displacement_element->GetOrder();
quadrature_factory.configure_centrifugal( quadrature_factory.configure_centrifugal(
@@ -127,7 +142,7 @@ TEST_CASE(
TEST_CASE( TEST_CASE(
"Centrifugal Integrator Jacobian Matches Residual Linearization", "Centrifugal Integrator Jacobian Matches Residual Linearization",
tags::unit &tags::solver &tags::integrator &tags::centrifugal tags::rotation_integrator_unit
) { ) {
constexpr int dim = 3; constexpr int dim = 3;
constexpr double step = 1.0e-6; constexpr double step = 1.0e-6;
@@ -147,14 +162,16 @@ TEST_CASE(
mfem::GridFunction displacement(&displacement_fes); mfem::GridFunction displacement(&displacement_fes);
displacement = 0.0; displacement = 0.0;
mapping::DomainMapper domain_mapper(displacement, 1.0, 2.0); SerialMappingData mapping_data(mesh);
mfem::Vector omega(dim); mfem::Vector omega(dim);
omega(0) = 0.7; omega(0) = 0.7;
omega(1) = -1.1; omega(1) = -1.1;
omega(2) = 1.6; omega(2) = 1.6;
integrators::CentrifugalForceIntegrator integrator(domain_mapper, omega); integrators::CentrifugalForceIntegrator integrator(
mapping_data.mapper, displacement, mapping_data.compactification_coordinate, omega
);
const mfem::FiniteElement *velocity_element = velocity_fes.GetFE(0); const mfem::FiniteElement *velocity_element = velocity_fes.GetFE(0);
const mfem::FiniteElement *density_element = density_fes.GetFE(0); const mfem::FiniteElement *density_element = density_fes.GetFE(0);
@@ -165,8 +182,7 @@ TEST_CASE(
quadrature::Policy policy(std::move(rule_set)); quadrature::Policy policy(std::move(rule_set));
quadrature::RuleFactory quadrature_factory(std::move(policy)); quadrature::RuleFactory quadrature_factory(std::move(policy));
const quadrature::MappingKind mapping_kind = const quadrature::MappingKind mapping_kind = quadrature::MappingKind::general;
!domain_mapper.HasDisplacementField() ? quadrature::MappingKind::none : quadrature::MappingKind::general;
const int position_order = displacement_element->GetOrder(); const int position_order = displacement_element->GetOrder();
quadrature_factory.configure_centrifugal( quadrature_factory.configure_centrifugal(
@@ -287,7 +303,7 @@ TEST_CASE(
TEST_CASE( TEST_CASE(
"Centrifugal Integrator Preserves Rotation Identities", "Centrifugal Integrator Preserves Rotation Identities",
tags::unit &tags::solver &tags::integrator &tags::centrifugal tags::rotation_integrator_unit
) { ) {
constexpr int dim = 3; constexpr int dim = 3;
constexpr double density = 1.4; constexpr double density = 1.4;
@@ -307,14 +323,16 @@ TEST_CASE(
mfem::GridFunction displacement(&displacement_fes); mfem::GridFunction displacement(&displacement_fes);
displacement = 0.0; displacement = 0.0;
mapping::DomainMapper domain_mapper(displacement, 1.0, 2.0); SerialMappingData mapping_data(mesh);
mfem::Vector omega(dim); mfem::Vector omega(dim);
omega(0) = 0.7; omega(0) = 0.7;
omega(1) = -1.1; omega(1) = -1.1;
omega(2) = 1.6; omega(2) = 1.6;
integrators::CentrifugalForceIntegrator integrator(domain_mapper, omega); integrators::CentrifugalForceIntegrator integrator(
mapping_data.mapper, displacement, mapping_data.compactification_coordinate, omega
);
const mfem::FiniteElement *velocity_element = velocity_fes.GetFE(0); const mfem::FiniteElement *velocity_element = velocity_fes.GetFE(0);
const mfem::FiniteElement *density_element = density_fes.GetFE(0); const mfem::FiniteElement *density_element = density_fes.GetFE(0);
@@ -325,8 +343,7 @@ TEST_CASE(
quadrature::Policy policy(std::move(rule_set)); quadrature::Policy policy(std::move(rule_set));
quadrature::RuleFactory quadrature_factory(std::move(policy)); quadrature::RuleFactory quadrature_factory(std::move(policy));
const quadrature::MappingKind mapping_kind = const quadrature::MappingKind mapping_kind = quadrature::MappingKind::general;
!domain_mapper.HasDisplacementField() ? quadrature::MappingKind::none : quadrature::MappingKind::general;
const int position_order = displacement_element->GetOrder(); const int position_order = displacement_element->GetOrder();
quadrature_factory.configure_centrifugal( quadrature_factory.configure_centrifugal(
@@ -424,7 +441,7 @@ TEST_CASE(
TEST_CASE( TEST_CASE(
"Centrifugal Integrator Matches Rotational Virial On Roche Mappings", "Centrifugal Integrator Matches Rotational Virial On Roche Mappings",
tags::integration &tags::solver &tags::integrator &tags::centrifugal tags::rotation_integrator_integration
) { ) {
auto args = test_utils::setup_args(); auto args = test_utils::setup_args();
fem::FEM f = fem::setup_fem(args.mesh_file, args, 0); fem::FEM f = fem::setup_fem(args.mesh_file, args, 0);
@@ -486,16 +503,20 @@ TEST_CASE(
mfem::VectorFunctionCoefficient displacement_coefficient(dim, rotation_displacement); mfem::VectorFunctionCoefficient displacement_coefficient(dim, rotation_displacement);
displacement.ProjectCoefficient(displacement_coefficient); displacement.ProjectCoefficient(displacement_coefficient);
f.mapping->SetDisplacement(displacement); *f.displacement = displacement;
mapping::GridFunctionMappingEvaluator mapping_evaluator(
*f.domainMapperStateless, *f.displacement, *f.compactificationCoordinate
);
mfem::Vector omega(dim); mfem::Vector omega(dim);
omega = 0.0; omega = 0.0;
omega(2) = rotation_fraction; omega(2) = rotation_fraction;
integrators::CentrifugalForceIntegrator integrator(*f.mapping, omega); integrators::CentrifugalForceIntegrator integrator(
*f.domainMapperStateless, *f.displacement, *f.compactificationCoordinate, omega
);
const quadrature::MappingKind mapping_kind = const quadrature::MappingKind mapping_kind = quadrature::MappingKind::general;
!f.mapping->HasDisplacementField() ? quadrature::MappingKind::none : quadrature::MappingKind::general;
f.quadratureFactory->configure_centrifugal( f.quadratureFactory->configure_centrifugal(
integrator, quadrature::QuadratureRole::discretization, representative_density_element, integrator, quadrature::QuadratureRole::discretization, representative_density_element,
representative_velocity_element, representative_transformation, position_order, utils::DOMAINS::STELLAR, representative_velocity_element, representative_transformation, position_order, utils::DOMAINS::STELLAR,
@@ -560,7 +581,7 @@ TEST_CASE(
for (int i = 0; i < velocity_dofs_count; ++i) { for (int i = 0; i < velocity_dofs_count; ++i) {
const mfem::IntegrationPoint &node = velocity_nodes.IntPoint(i); const mfem::IntegrationPoint &node = velocity_nodes.IntPoint(i);
transformation->SetIntPoint(&node); transformation->SetIntPoint(&node);
f.mapping->GetPhysicalPoint(*transformation, node, x_physical); mapping_evaluator.GetPhysicalPoint(*transformation, node, x_physical);
for (int d = 0; d < dim; ++d) { for (int d = 0; d < dim; ++d) {
position_test_dofs(i + d * velocity_dofs_count) = x_physical(d); position_test_dofs(i + d * velocity_dofs_count) = x_physical(d);
@@ -581,14 +602,14 @@ TEST_CASE(
const mfem::IntegrationPoint &integration_point = reference_rule.IntPoint(q); const mfem::IntegrationPoint &integration_point = reference_rule.IntPoint(q);
transformation->SetIntPoint(&integration_point); transformation->SetIntPoint(&integration_point);
const double signed_map_determinant = f.mapping->ComputeDetJ(*transformation, integration_point);
const mapping::VolumeQuadratureContext context = const mapping::VolumeQuadratureContext context =
f.mapping->GetQuadratureContext(*transformation, integration_point); mapping_evaluator.GetQuadratureContext(*transformation, integration_point);
const double signed_map_determinant = context.detJ;
local_minimum_map_determinant = std::min(local_minimum_map_determinant, signed_map_determinant); local_minimum_map_determinant = std::min(local_minimum_map_determinant, signed_map_determinant);
local_maximum_map_determinant = std::max(local_maximum_map_determinant, signed_map_determinant); local_maximum_map_determinant = std::max(local_maximum_map_determinant, signed_map_determinant);
f.mapping->GetPhysicalPoint(*transformation, integration_point, x_physical); mapping_evaluator.GetPhysicalPoint(*transformation, integration_point, x_physical);
velocity_element->CalcShape(integration_point, velocity_shape); velocity_element->CalcShape(integration_point, velocity_shape);
position_test_value = 0.0; position_test_value = 0.0;
@@ -661,13 +682,13 @@ TEST_CASE(
CHECK_THAT(relative_position_error, Catch::Matchers::WithinAbs(0.0, position_tolerance)); CHECK_THAT(relative_position_error, Catch::Matchers::WithinAbs(0.0, position_tolerance));
} }
f.mapping->ResetDisplacement(); *f.displacement = 0.0;
} }
TEST_CASE( TEST_CASE(
"Centrifugal Virial Position Representation Is Consistent At The " "Centrifugal Virial Position Representation Is Consistent At The "
"Registered Order", "Registered Order",
tags::integration &tags::solver &tags::integrator &tags::centrifugal tags::rotation_integrator_integration
) { ) {
constexpr int dim = 3; constexpr int dim = 3;
constexpr double concentration = 4.0; constexpr double concentration = 4.0;
@@ -728,7 +749,10 @@ TEST_CASE(
mfem::VectorFunctionCoefficient displacement_coefficient(dim, rotation_displacement); mfem::VectorFunctionCoefficient displacement_coefficient(dim, rotation_displacement);
displacement.ProjectCoefficient(displacement_coefficient); displacement.ProjectCoefficient(displacement_coefficient);
f.mapping->SetDisplacement(displacement); *f.displacement = displacement;
mapping::GridFunctionMappingEvaluator mapping_evaluator(
*f.domainMapperStateless, *f.displacement, *f.compactificationCoordinate
);
mfem::Vector omega(dim); mfem::Vector omega(dim);
omega = 0.0; omega = 0.0;
@@ -761,7 +785,7 @@ TEST_CASE(
for (int i = 0; i < velocity_dofs_count; ++i) { for (int i = 0; i < velocity_dofs_count; ++i) {
const mfem::IntegrationPoint &node = velocity_nodes.IntPoint(i); const mfem::IntegrationPoint &node = velocity_nodes.IntPoint(i);
transformation->SetIntPoint(&node); transformation->SetIntPoint(&node);
f.mapping->GetPhysicalPoint(*transformation, node, x_physical); mapping_evaluator.GetPhysicalPoint(*transformation, node, x_physical);
for (int d = 0; d < dim; ++d) { for (int d = 0; d < dim; ++d) {
position_test_dofs(i + d * velocity_dofs_count) = x_physical(d); position_test_dofs(i + d * velocity_dofs_count) = x_physical(d);
@@ -780,13 +804,13 @@ TEST_CASE(
const mfem::IntegrationPoint &integration_point = reference_rule.IntPoint(q); const mfem::IntegrationPoint &integration_point = reference_rule.IntPoint(q);
transformation->SetIntPoint(&integration_point); transformation->SetIntPoint(&integration_point);
const double signed_map_determinant = f.mapping->ComputeDetJ(*transformation, integration_point);
const mapping::VolumeQuadratureContext context = const mapping::VolumeQuadratureContext context =
f.mapping->GetQuadratureContext(*transformation, integration_point); mapping_evaluator.GetQuadratureContext(*transformation, integration_point);
const double signed_map_determinant = context.detJ;
local_minimum_determinant = std::min(local_minimum_determinant, signed_map_determinant); local_minimum_determinant = std::min(local_minimum_determinant, signed_map_determinant);
f.mapping->GetPhysicalPoint(*transformation, integration_point, x_physical); mapping_evaluator.GetPhysicalPoint(*transformation, integration_point, x_physical);
velocity_element->CalcShape(integration_point, velocity_shape); velocity_element->CalcShape(integration_point, velocity_shape);
position_test_value = 0.0; position_test_value = 0.0;
@@ -833,7 +857,7 @@ TEST_CASE(
minimum_determinants[rotation_index][order_index] = global_minimum_determinant; minimum_determinants[rotation_index][order_index] = global_minimum_determinant;
} }
f.mapping->ResetDisplacement(); *f.displacement = 0.0;
} }
for (std::size_t rotation_index = 0; rotation_index < rotation_fractions.size(); ++rotation_index) { for (std::size_t rotation_index = 0; rotation_index < rotation_fractions.size(); ++rotation_index) {
@@ -853,7 +877,7 @@ TEST_CASE(
TEST_CASE( TEST_CASE(
"Centrifugal Virial Position Representation Converges Under H Refinement", "Centrifugal Virial Position Representation Converges Under H Refinement",
tags::integration &tags::solver &tags::integrator &tags::convergence &tags::h_refinement &tags::centrifugal tags::rotation_integrator_convergence
) { ) {
constexpr int dim = 3; constexpr int dim = 3;
constexpr double concentration = 4.0; constexpr double concentration = 4.0;
@@ -916,7 +940,10 @@ TEST_CASE(
mfem::VectorFunctionCoefficient displacement_coefficient(dim, rotation_displacement); mfem::VectorFunctionCoefficient displacement_coefficient(dim, rotation_displacement);
displacement.ProjectCoefficient(displacement_coefficient); displacement.ProjectCoefficient(displacement_coefficient);
f.mapping->SetDisplacement(displacement); *f.displacement = displacement;
mapping::GridFunctionMappingEvaluator mapping_evaluator(
*f.domainMapperStateless, *f.displacement, *f.compactificationCoordinate
);
mfem::Vector omega(dim); mfem::Vector omega(dim);
omega = 0.0; omega = 0.0;
@@ -949,7 +976,7 @@ TEST_CASE(
for (int i = 0; i < velocity_dofs_count; ++i) { for (int i = 0; i < velocity_dofs_count; ++i) {
const mfem::IntegrationPoint &node = velocity_nodes.IntPoint(i); const mfem::IntegrationPoint &node = velocity_nodes.IntPoint(i);
transformation->SetIntPoint(&node); transformation->SetIntPoint(&node);
f.mapping->GetPhysicalPoint(*transformation, node, x_physical); mapping_evaluator.GetPhysicalPoint(*transformation, node, x_physical);
for (int d = 0; d < dim; ++d) { for (int d = 0; d < dim; ++d) {
position_test_dofs(i + d * velocity_dofs_count) = x_physical(d); position_test_dofs(i + d * velocity_dofs_count) = x_physical(d);
@@ -968,13 +995,13 @@ TEST_CASE(
const mfem::IntegrationPoint &integration_point = reference_rule.IntPoint(q); const mfem::IntegrationPoint &integration_point = reference_rule.IntPoint(q);
transformation->SetIntPoint(&integration_point); transformation->SetIntPoint(&integration_point);
const double signed_map_determinant = f.mapping->ComputeDetJ(*transformation, integration_point);
const mapping::VolumeQuadratureContext context = const mapping::VolumeQuadratureContext context =
f.mapping->GetQuadratureContext(*transformation, integration_point); mapping_evaluator.GetQuadratureContext(*transformation, integration_point);
const double signed_map_determinant = context.detJ;
local_minimum_determinant = std::min(local_minimum_determinant, signed_map_determinant); local_minimum_determinant = std::min(local_minimum_determinant, signed_map_determinant);
f.mapping->GetPhysicalPoint(*transformation, integration_point, x_physical); mapping_evaluator.GetPhysicalPoint(*transformation, integration_point, x_physical);
velocity_element->CalcShape(integration_point, velocity_shape); velocity_element->CalcShape(integration_point, velocity_shape);
position_test_value = 0.0; position_test_value = 0.0;
@@ -1021,7 +1048,7 @@ TEST_CASE(
minimum_determinants[rotation_index][refinement_index] = global_minimum_determinant; minimum_determinants[rotation_index][refinement_index] = global_minimum_determinant;
} }
f.mapping->ResetDisplacement(); *f.displacement = 0.0;
} }
for (std::size_t rotation_index = 0; rotation_index < rotation_fractions.size(); ++rotation_index) { for (std::size_t rotation_index = 0; rotation_index < rotation_fractions.size(); ++rotation_index) {

View File

@@ -10,7 +10,7 @@ using namespace mean_field;
TEST_CASE( TEST_CASE(
"Gravity Force Integrator Jacobian Matches Residual Linearization", "Gravity Force Integrator Jacobian Matches Residual Linearization",
tags::unit &tags::solver &tags::integrator &tags::gravity tags::gravity_integrator_unit
) { ) {
constexpr int dim = 3; constexpr int dim = 3;
constexpr double finite_difference_step = 1.0e-3; constexpr double finite_difference_step = 1.0e-3;
@@ -31,21 +31,20 @@ TEST_CASE(
mfem::RT_FECollection gravity_gradient_fec(1, dim); mfem::RT_FECollection gravity_gradient_fec(1, dim);
mfem::L2_FECollection gravity_potential_fec(1, dim); mfem::L2_FECollection gravity_potential_fec(1, dim);
mfem::H1_FECollection displacement_fec(2, dim); mfem::H1_FECollection displacement_fec(2, dim);
mfem::H1_FECollection compactification_fec(1, dim);
mfem::FiniteElementSpace velocity_fes(&mesh, &velocity_fec, dim, mfem::Ordering::byVDIM); mfem::FiniteElementSpace velocity_fes(&mesh, &velocity_fec, dim, mfem::Ordering::byVDIM);
mfem::FiniteElementSpace density_fes(&mesh, &density_fec); mfem::FiniteElementSpace density_fes(&mesh, &density_fec);
mfem::FiniteElementSpace gravity_gradient_fes(&mesh, &gravity_gradient_fec); mfem::FiniteElementSpace gravity_gradient_fes(&mesh, &gravity_gradient_fec);
mfem::FiniteElementSpace gravity_potential_fes(&mesh, &gravity_potential_fec); mfem::FiniteElementSpace gravity_potential_fes(&mesh, &gravity_potential_fec);
mfem::FiniteElementSpace displacement_fes(&mesh, &displacement_fec, dim, mfem::Ordering::byVDIM); mfem::FiniteElementSpace displacement_fes(&mesh, &displacement_fec, dim, mfem::Ordering::byVDIM);
mfem::FiniteElementSpace compactification_fes(&mesh, &compactification_fec);
mfem::GridFunction displacement(&displacement_fes); mfem::GridFunction displacement(&displacement_fes);
displacement = 0.0; displacement = 0.0;
mfem::GridFunction compactification_coordinate(&compactification_fes);
mapping::DomainMapper domain_mapper(displacement, 1.0, 2.0); compactification_coordinate = 0.0;
INFO(std::format("Domain mapping is has displacement field: {}", domain_mapper.HasDisplacementField())); mapping::DomainMapper domain_mapper = field_dof_test_utils::make_domain_mapper();
INFO(std::format("Domain mapping is identity: {}", domain_mapper.CalcIsIdentity()));
REQUIRE(domain_mapper.CalcIsIdentity());
const mfem::FiniteElement *velocity_element = velocity_fes.GetFE(0); const mfem::FiniteElement *velocity_element = velocity_fes.GetFE(0);
const mfem::FiniteElement *density_element = density_fes.GetFE(0); const mfem::FiniteElement *density_element = density_fes.GetFE(0);
@@ -130,7 +129,8 @@ TEST_CASE(
element_residual[displacement_block] = &displacement_residual; element_residual[displacement_block] = &displacement_residual;
integrators::GravityMomentumIntegrator integrator( integrators::GravityMomentumIntegrator integrator(
domain_mapper, integrators::GravityForceJacobianMode::field_coupled domain_mapper, displacement, compactification_coordinate,
integrators::GravityForceJacobianMode::field_coupled
); );
const int maximum_order = std::max( const int maximum_order = std::max(
@@ -268,7 +268,7 @@ TEST_CASE(
TEST_CASE( TEST_CASE(
"Gravity Force Integrator Matches Manufactured Cartesian Load", "Gravity Force Integrator Matches Manufactured Cartesian Load",
tags::unit &tags::solver &tags::integrator &tags::gravity tags::gravity_integrator_unit
) { ) {
constexpr int dim = 3; constexpr int dim = 3;
constexpr double tolerance = 1.0e-12; constexpr double tolerance = 1.0e-12;
@@ -286,18 +286,23 @@ TEST_CASE(
mfem::L2_FECollection density_fec(1, dim); mfem::L2_FECollection density_fec(1, dim);
mfem::RT_FECollection gravity_gradient_fec(0, dim); mfem::RT_FECollection gravity_gradient_fec(0, dim);
mfem::H1_FECollection displacement_fec(1, dim); mfem::H1_FECollection displacement_fec(1, dim);
mfem::H1_FECollection compactification_fec(1, dim);
mfem::FiniteElementSpace velocity_fes(&mesh, &velocity_fec, dim, mfem::Ordering::byVDIM); mfem::FiniteElementSpace velocity_fes(&mesh, &velocity_fec, dim, mfem::Ordering::byVDIM);
mfem::FiniteElementSpace density_fes(&mesh, &density_fec); mfem::FiniteElementSpace density_fes(&mesh, &density_fec);
mfem::FiniteElementSpace gravity_gradient_fes(&mesh, &gravity_gradient_fec); mfem::FiniteElementSpace gravity_gradient_fes(&mesh, &gravity_gradient_fec);
mfem::FiniteElementSpace displacement_fes(&mesh, &displacement_fec, dim, mfem::Ordering::byVDIM); mfem::FiniteElementSpace displacement_fes(&mesh, &displacement_fec, dim, mfem::Ordering::byVDIM);
mfem::FiniteElementSpace compactification_fes(&mesh, &compactification_fec);
mfem::GridFunction displacement(&displacement_fes); mfem::GridFunction displacement(&displacement_fes);
displacement = 0.0; displacement = 0.0;
mfem::GridFunction compactification_coordinate(&compactification_fes);
compactification_coordinate = 0.0;
mapping::DomainMapper domain_mapper = field_dof_test_utils::make_domain_mapper();
mapping::DomainMapper domain_mapper(displacement, 1.0, 2.0); mapping::GridFunctionMappingEvaluator mapping_evaluator(
domain_mapper, displacement, compactification_coordinate
REQUIRE(domain_mapper.CalcIsIdentity()); );
auto reference_density = [](const mfem::Vector &x) { return 1.0 + x(0); }; auto reference_density = [](const mfem::Vector &x) { return 1.0 + x(0); };
@@ -376,7 +381,8 @@ TEST_CASE(
element_residual[displacement_block] = &displacement_residual; element_residual[displacement_block] = &displacement_residual;
integrators::GravityMomentumIntegrator integrator( integrators::GravityMomentumIntegrator integrator(
domain_mapper, integrators::GravityForceJacobianMode::field_coupled domain_mapper, displacement, compactification_coordinate,
integrators::GravityForceJacobianMode::field_coupled
); );
const mfem::IntegrationRule &integration_rule = mfem::IntRules.Get(velocity_element->GetGeomType(), 8); const mfem::IntegrationRule &integration_rule = mfem::IntRules.Get(velocity_element->GetGeomType(), 8);
@@ -395,7 +401,7 @@ TEST_CASE(
for (int i = 0; i < velocity_dofs_count; ++i) { for (int i = 0; i < velocity_dofs_count; ++i) {
const mfem::IntegrationPoint &node = velocity_nodes.IntPoint(i); const mfem::IntegrationPoint &node = velocity_nodes.IntPoint(i);
transformation->SetIntPoint(&node); transformation->SetIntPoint(&node);
domain_mapper.GetPhysicalPoint(*transformation, node, x_physical); mapping_evaluator.GetPhysicalPoint(*transformation, node, x_physical);
test_dofs(i + component * velocity_dofs_count) = test_dofs(i + component * velocity_dofs_count) =
coordinate_weight < 0 ? 1.0 : x_physical(coordinate_weight); coordinate_weight < 0 ? 1.0 : x_physical(coordinate_weight);
} }
@@ -433,7 +439,7 @@ TEST_CASE(
} }
TEST_CASE( TEST_CASE(
"Gravity Force Integrator Preserves Gravity Identities", "Gravity Force Integrator Preserves Gravity Identities",
tags::unit &tags::solver &tags::integrator &tags::gravity tags::gravity_integrator_unit
) { ) {
constexpr int dim = 3; constexpr int dim = 3;
constexpr double density_value = 1.7; constexpr double density_value = 1.7;
@@ -454,18 +460,22 @@ TEST_CASE(
mfem::L2_FECollection density_fec(0, dim); mfem::L2_FECollection density_fec(0, dim);
mfem::RT_FECollection gravity_gradient_fec(0, dim); mfem::RT_FECollection gravity_gradient_fec(0, dim);
mfem::H1_FECollection displacement_fec(1, dim); mfem::H1_FECollection displacement_fec(1, dim);
mfem::H1_FECollection compactification_fec(1, dim);
mfem::FiniteElementSpace velocity_fes(&mesh, &velocity_fec, dim, mfem::Ordering::byVDIM); mfem::FiniteElementSpace velocity_fes(&mesh, &velocity_fec, dim, mfem::Ordering::byVDIM);
mfem::FiniteElementSpace density_fes(&mesh, &density_fec); mfem::FiniteElementSpace density_fes(&mesh, &density_fec);
mfem::FiniteElementSpace gravity_gradient_fes(&mesh, &gravity_gradient_fec); mfem::FiniteElementSpace gravity_gradient_fes(&mesh, &gravity_gradient_fec);
mfem::FiniteElementSpace displacement_fes(&mesh, &displacement_fec, dim, mfem::Ordering::byVDIM); mfem::FiniteElementSpace displacement_fes(&mesh, &displacement_fec, dim, mfem::Ordering::byVDIM);
mfem::FiniteElementSpace compactification_fes(&mesh, &compactification_fec);
mfem::GridFunction displacement(&displacement_fes); mfem::GridFunction displacement(&displacement_fes);
displacement = 0.0; displacement = 0.0;
mfem::GridFunction compactification_coordinate(&compactification_fes);
mapping::DomainMapper domain_mapper(displacement, 1.0, 2.0); compactification_coordinate = 0.0;
mapping::DomainMapper domain_mapper = field_dof_test_utils::make_domain_mapper();
REQUIRE(domain_mapper.HasDisplacementField()); mapping::GridFunctionMappingEvaluator mapping_evaluator(
domain_mapper, displacement, compactification_coordinate
);
auto radial_gravity = [](const mfem::Vector &x, mfem::Vector &gravity) { auto radial_gravity = [](const mfem::Vector &x, mfem::Vector &gravity) {
gravity.SetSize(3); gravity.SetSize(3);
@@ -543,7 +553,8 @@ TEST_CASE(
element_residual[displacement_block] = &displacement_residual; element_residual[displacement_block] = &displacement_residual;
integrators::GravityMomentumIntegrator integrator( integrators::GravityMomentumIntegrator integrator(
domain_mapper, integrators::GravityForceJacobianMode::field_coupled domain_mapper, displacement, compactification_coordinate,
integrators::GravityForceJacobianMode::field_coupled
); );
const mfem::IntegrationRule &integration_rule = mfem::IntRules.Get(velocity_element->GetGeomType(), 8); const mfem::IntegrationRule &integration_rule = mfem::IntRules.Get(velocity_element->GetGeomType(), 8);
@@ -609,7 +620,7 @@ TEST_CASE(
for (int i = 0; i < velocity_dofs_count; ++i) { for (int i = 0; i < velocity_dofs_count; ++i) {
const mfem::IntegrationPoint &node = velocity_nodes.IntPoint(i); const mfem::IntegrationPoint &node = velocity_nodes.IntPoint(i);
transformation->SetIntPoint(&node); transformation->SetIntPoint(&node);
domain_mapper.GetPhysicalPoint(*transformation, node, x_physical); mapping_evaluator.GetPhysicalPoint(*transformation, node, x_physical);
for (int component = 0; component < dim; ++component) { for (int component = 0; component < dim; ++component) {
centered_position(component) = x_physical(component) - 0.5; centered_position(component) = x_physical(component) - 0.5;

File diff suppressed because it is too large Load Diff

View File

@@ -43,11 +43,18 @@ TEST_CASE(
CHECK(initial_report.geometry.reconstructed_operators); CHECK(initial_report.geometry.reconstructed_operators);
CHECK(initial_report.geometry.rebuilt_mass_operator); CHECK(initial_report.geometry.rebuilt_mass_operator);
CHECK(initial_report.geometry.rebuilt_source_operator); CHECK(initial_report.geometry.rebuilt_source_operator);
CHECK(initial_report.geometry.rebuilt_divergence_operator);
CHECK(initial_report.geometry.refreshed_variation_state); CHECK(initial_report.geometry.refreshed_variation_state);
CHECK(initial_report.updated_density); CHECK(initial_report.updated_density);
CHECK(initial_report.updated_gravity_gradient); CHECK(initial_report.updated_gravity_gradient);
CHECK(initial_report.DidAnyWork()); CHECK(initial_report.DidAnyWork());
const auto &geometry_context = context.GetGeometryContext();
CHECK(geometry_context.GetDivergenceOperator().Width() == f.gravityFluxFes->GetTrueVSize());
CHECK(geometry_context.GetDivergenceOperator().Height() == f.gravityPotentialFes->GetTrueVSize());
CHECK(geometry_context.GetTransposeDivergenceOperator().Width() == f.gravityPotentialFes->GetTrueVSize());
CHECK(geometry_context.GetTransposeDivergenceOperator().Height() == f.gravityFluxFes->GetTrueVSize());
const auto initial_mass_preparations = context.GetGeometryContext().GetMassOperator().GetPreparationCount(); const auto initial_mass_preparations = context.GetGeometryContext().GetMassOperator().GetPreparationCount();
const auto initial_source_preparations = context.GetGeometryContext().GetSourceOperator().GetPreparationCount(); const auto initial_source_preparations = context.GetGeometryContext().GetSourceOperator().GetPreparationCount();
@@ -93,6 +100,7 @@ TEST_CASE(
CHECK_FALSE(displacement_report.geometry.reconstructed_operators); CHECK_FALSE(displacement_report.geometry.reconstructed_operators);
CHECK(displacement_report.geometry.rebuilt_mass_operator); CHECK(displacement_report.geometry.rebuilt_mass_operator);
CHECK(displacement_report.geometry.rebuilt_source_operator); CHECK(displacement_report.geometry.rebuilt_source_operator);
CHECK_FALSE(displacement_report.geometry.rebuilt_divergence_operator);
CHECK(displacement_report.geometry.refreshed_variation_state); CHECK(displacement_report.geometry.refreshed_variation_state);
CHECK_FALSE(displacement_report.updated_density); CHECK_FALSE(displacement_report.updated_density);
CHECK_FALSE(displacement_report.updated_gravity_gradient); CHECK_FALSE(displacement_report.updated_gravity_gradient);
@@ -107,6 +115,7 @@ TEST_CASE(
CHECK(discretization_report.geometry.reconstructed_operators); CHECK(discretization_report.geometry.reconstructed_operators);
CHECK(discretization_report.geometry.rebuilt_mass_operator); CHECK(discretization_report.geometry.rebuilt_mass_operator);
CHECK(discretization_report.geometry.rebuilt_source_operator); CHECK(discretization_report.geometry.rebuilt_source_operator);
CHECK(discretization_report.geometry.rebuilt_divergence_operator);
CHECK(discretization_report.updated_density); CHECK(discretization_report.updated_density);
CHECK(discretization_report.updated_gravity_gradient); CHECK(discretization_report.updated_gravity_gradient);
CHECK(context.GetGeometryContext().GetMassOperator().GetPreparationCount() == 1); CHECK(context.GetGeometryContext().GetMassOperator().GetPreparationCount() == 1);

View File

@@ -14,83 +14,95 @@ namespace gravity_displacement_force_test_utils {
using CoupledForm = mean_field::utils::blocks::barotropic_equilibrium_form; using CoupledForm = mean_field::utils::blocks::barotropic_equilibrium_form;
constexpr auto densityValue = constexpr auto densityValue =
mean_field::utils::blocks::get_value_block<CoupledForm>(mean_field::utils::blocks::density_field.mass_term); mean_field::utils::blocks::get_value_block<CoupledForm>(
mean_field::utils::blocks::density_field.mass_term);
constexpr auto displacementValue = mean_field::utils::blocks::get_value_block<CoupledForm>( constexpr auto displacementValue =
mean_field::utils::blocks::displacement_field.geometry_term mean_field::utils::blocks::get_value_block<CoupledForm>(
); mean_field::utils::blocks::displacement_field.geometry_term);
constexpr auto gravityGradientValue = constexpr auto gravityGradientValue =
mean_field::utils::blocks::get_value_block<CoupledForm>(mean_field::utils::blocks::gravity_field.gradient_term); mean_field::utils::blocks::get_value_block<CoupledForm>(
mean_field::utils::blocks::gravity_field.gradient_term);
constexpr auto gravityPotentialValue = constexpr auto gravityPotentialValue =
mean_field::utils::blocks::get_value_block<CoupledForm>(mean_field::utils::blocks::gravity_field.poisson_term); mean_field::utils::blocks::get_value_block<CoupledForm>(
mean_field::utils::blocks::gravity_field.poisson_term);
constexpr auto enthalpyValue = mean_field::utils::blocks::get_value_block<CoupledForm>( constexpr auto enthalpyValue =
mean_field::utils::blocks::enthalpy_field.specific_term mean_field::utils::blocks::get_value_block<CoupledForm>(
); mean_field::utils::blocks::enthalpy_field.specific_term);
constexpr auto barotropicConstantValue = mean_field::utils::blocks::get_value_block<CoupledForm>( constexpr auto barotropicConstantValue =
mean_field::utils::blocks::barotropic_constant_field.mass_normalization_term mean_field::utils::blocks::get_value_block<CoupledForm>(
); mean_field::utils::blocks::barotropic_constant_field
.mass_normalization_term);
constexpr auto gravityGradientResidual = mean_field::utils::blocks::get_residual_block<CoupledForm>( constexpr auto gravityGradientResidual =
mean_field::utils::blocks::gravity_field.gradient_term mean_field::utils::blocks::get_residual_block<CoupledForm>(
); mean_field::utils::blocks::gravity_field.gradient_term);
constexpr auto gravityPotentialResidual = mean_field::utils::blocks::get_residual_block<CoupledForm>( constexpr auto gravityPotentialResidual =
mean_field::utils::blocks::gravity_field.poisson_term mean_field::utils::blocks::get_residual_block<CoupledForm>(
); mean_field::utils::blocks::gravity_field.poisson_term);
constexpr auto densityResidual = constexpr auto densityResidual =
mean_field::utils::blocks::get_residual_block<CoupledForm>(mean_field::utils::blocks::density_field.mass_term); mean_field::utils::blocks::get_residual_block<CoupledForm>(
mean_field::utils::blocks::density_field.mass_term);
constexpr auto displacementResidual = mean_field::utils::blocks::get_residual_block<CoupledForm>( constexpr auto displacementResidual =
mean_field::utils::blocks::displacement_field.geometry_term mean_field::utils::blocks::get_residual_block<CoupledForm>(
); mean_field::utils::blocks::displacement_field.geometry_term);
constexpr auto enthalpyResidual = mean_field::utils::blocks::get_residual_block<CoupledForm>( constexpr auto enthalpyResidual =
mean_field::utils::blocks::enthalpy_field.specific_term mean_field::utils::blocks::get_residual_block<CoupledForm>(
); mean_field::utils::blocks::enthalpy_field.specific_term);
constexpr auto massResidual = mean_field::utils::blocks::get_residual_block<CoupledForm>( constexpr auto massResidual =
mean_field::utils::blocks::barotropic_constant_field.mass_normalization_term mean_field::utils::blocks::get_residual_block<CoupledForm>(
); mean_field::utils::blocks::barotropic_constant_field
.mass_normalization_term);
[[nodiscard]] mean_field::operators::GravityDisplacementForceLayout make_layout(const mean_field::fem::FEM &f) { [[nodiscard]] mean_field::operators::GravityDisplacementForceLayout
make_layout(const mean_field::fem::FEM &f) {
using DomainSchema = gravity_prepared_test_utils::DomainSchema; using DomainSchema = gravity_prepared_test_utils::DomainSchema;
const auto densityMap = gravity_prepared_test_utils::make_field_map<mean_field::field::Density>(f); const auto densityMap =
const auto displacementMap = gravity_prepared_test_utils::make_field_map<mean_field::field::Displacement>(f); gravity_prepared_test_utils::make_field_map<mean_field::field::Density>(
f);
const auto displacementMap = gravity_prepared_test_utils::make_field_map<
mean_field::field::Displacement>(f);
const auto gravityFluxMap = const auto gravityFluxMap =
mean_field::field::make_field_dof_map<mean_field::field::Gravity, DomainSchema>(*f.gravityFluxFes); mean_field::field::make_field_dof_map<mean_field::field::Gravity,
const auto gravityPotentialMap = DomainSchema>(*f.gravityFluxFes);
mean_field::field::make_field_dof_map<mean_field::field::Gravity, DomainSchema>(*f.gravityPotentialFes); const auto gravityPotentialMap = mean_field::field::make_field_dof_map<
mean_field::field::Gravity, DomainSchema>(*f.gravityPotentialFes);
const auto enthalpyMap = const auto enthalpyMap =
mean_field::field::make_field_dof_map<mean_field::field::Enthalpy, DomainSchema>(*f.enthalpyFes); mean_field::field::make_field_dof_map<mean_field::field::Enthalpy,
DomainSchema>(*f.enthalpyFes);
const std::array<int, CoupledForm::value_block_count> valueSizes{ const std::array<int, CoupledForm::value_block_count> valueSizes{
densityMap.reduced_size(), displacementMap.reduced_size(), gravityFluxMap.reduced_size(), densityMap.reduced_size(), displacementMap.reduced_size(),
gravityPotentialMap.reduced_size(), enthalpyMap.reduced_size(), 1 gravityFluxMap.reduced_size(), gravityPotentialMap.reduced_size(),
}; enthalpyMap.reduced_size(), 1};
const std::array<int, CoupledForm::residual_block_count> residualSizes{ const std::array<int, CoupledForm::residual_block_count> residualSizes{
gravityFluxMap.reduced_size(), gravityPotentialMap.reduced_size(), densityMap.reduced_size(), gravityFluxMap.reduced_size(), gravityPotentialMap.reduced_size(),
displacementMap.reduced_size(), enthalpyMap.reduced_size(), 1 densityMap.reduced_size(), displacementMap.reduced_size(),
}; enthalpyMap.reduced_size(), 1};
return {valueSizes, residualSizes}; return {valueSizes, residualSizes};
} }
[[nodiscard]] mfem::Vector make_density( [[nodiscard]] mfem::Vector make_density(const mean_field::fem::FEM &f,
const mean_field::fem::FEM &f, const double phase) {
const double phase
) {
mfem::ParGridFunction densityField(f.densityFes.get()); mfem::ParGridFunction densityField(f.densityFes.get());
mfem::FunctionCoefficient densityCoefficient([phase](const mfem::Vector &position) { mfem::FunctionCoefficient densityCoefficient(
[phase](const mfem::Vector &position) {
return 0.82 + 0.07 * std::sin(0.8 * position(0) + phase) + return 0.82 + 0.07 * std::sin(0.8 * position(0) + phase) +
0.05 * std::cos(0.6 * position(1) - 0.3 * phase) + 0.03 * position(2) * position(2); 0.05 * std::cos(0.6 * position(1) - 0.3 * phase) +
0.03 * position(2) * position(2);
}); });
densityField.ProjectCoefficient(densityCoefficient); densityField.ProjectCoefficient(densityCoefficient);
@@ -100,15 +112,14 @@ namespace gravity_displacement_force_test_utils {
return densityTrue; return densityTrue;
} }
[[nodiscard]] mfem::Vector make_density_direction( [[nodiscard]] mfem::Vector make_density_direction(const mean_field::fem::FEM &f,
const mean_field::fem::FEM &f, const double phase) {
const double phase
) {
mfem::ParGridFunction densityField(f.densityFes.get()); mfem::ParGridFunction densityField(f.densityFes.get());
mfem::FunctionCoefficient densityCoefficient([phase](const mfem::Vector &position) { mfem::FunctionCoefficient densityCoefficient(
return 0.19 * std::sin(0.9 * position(0) + phase) - 0.13 * std::cos(0.7 * position(1) - phase) + [phase](const mfem::Vector &position) {
0.08 * position(2); return 0.19 * std::sin(0.9 * position(0) + phase) -
0.13 * std::cos(0.7 * position(1) - phase) + 0.08 * position(2);
}); });
densityField.ProjectCoefficient(densityCoefficient); densityField.ProjectCoefficient(densityCoefficient);
@@ -118,13 +129,12 @@ namespace gravity_displacement_force_test_utils {
return densityTrue; return densityTrue;
} }
[[nodiscard]] mfem::Vector make_gravity_gradient( [[nodiscard]] mfem::Vector make_gravity_gradient(const mean_field::fem::FEM &f,
const mean_field::fem::FEM &f, const double phase) {
const double phase
) {
mfem::ParGridFunction gravityField(f.gravityFluxFes.get()); mfem::ParGridFunction gravityField(f.gravityFluxFes.get());
auto gravityFunction = [phase](const mfem::Vector &position, mfem::Vector &value) { auto gravityFunction = [phase](const mfem::Vector &position,
mfem::Vector &value) {
value.SetSize(3); value.SetSize(3);
value(0) = 0.31 + 0.08 * position(0) + 0.03 * phase * position(1); value(0) = 0.31 + 0.08 * position(0) + 0.03 * phase * position(1);
@@ -143,13 +153,13 @@ namespace gravity_displacement_force_test_utils {
return gravityTrue; return gravityTrue;
} }
[[nodiscard]] mfem::Vector make_gravity_gradient_direction( [[nodiscard]] mfem::Vector
const mean_field::fem::FEM &f, make_gravity_gradient_direction(const mean_field::fem::FEM &f,
const double phase const double phase) {
) {
mfem::ParGridFunction gravityField(f.gravityFluxFes.get()); mfem::ParGridFunction gravityField(f.gravityFluxFes.get());
auto gravityFunction = [phase](const mfem::Vector &position, mfem::Vector &value) { auto gravityFunction = [phase](const mfem::Vector &position,
mfem::Vector &value) {
value.SetSize(3); value.SetSize(3);
value(0) = 0.14 * std::sin(position(0) + phase) + 0.03 * position(1); value(0) = 0.14 * std::sin(position(0) + phase) + 0.03 * position(1);
@@ -168,26 +178,31 @@ namespace gravity_displacement_force_test_utils {
return gravityTrue; return gravityTrue;
} }
[[nodiscard]] mfem::Vector make_displacement_direction(const mean_field::fem::FEM &f) { [[nodiscard]] mfem::Vector
mfem::Vector direction = gravity_prepared_test_utils::make_displacement(f, 0.83); make_displacement_direction(const mean_field::fem::FEM &f) {
mfem::Vector direction =
gravity_prepared_test_utils::make_displacement(f, 0.83);
const mfem::Vector second = gravity_prepared_test_utils::make_displacement(f, 0.29); const mfem::Vector second =
gravity_prepared_test_utils::make_displacement(f, 0.29);
direction -= second; direction -= second;
return direction; return direction;
} }
[[nodiscard]] mfem::Vector make_vacuum_only_density(const mean_field::fem::FEM &f) { [[nodiscard]] mfem::Vector
make_vacuum_only_density(const mean_field::fem::FEM &f) {
mfem::ParGridFunction densityField(f.densityFes.get()); mfem::ParGridFunction densityField(f.densityFes.get());
densityField = 0.0; densityField = 0.0;
const int vacuumAttribute = f.domainMapperStateless->GetVacuumElementAttribute(); const int vacuumAttribute = field_dof_test_utils::vacuum_material_attribute;
mfem::Array<int> densityDofs; mfem::Array<int> densityDofs;
int localVacuumElements = 0; int localVacuumElements = 0;
for (int elementId = 0; elementId < f.mesh->GetNE(); ++elementId) { for (int elementId = 0; elementId < f.mesh->GetNE(); ++elementId) {
mfem::ElementTransformation *transformation = f.mesh->GetElementTransformation(elementId); mfem::ElementTransformation *transformation =
f.mesh->GetElementTransformation(elementId);
REQUIRE(transformation != nullptr); REQUIRE(transformation != nullptr);
@@ -204,7 +219,8 @@ namespace gravity_displacement_force_test_utils {
} }
int globalVacuumElements = 0; int globalVacuumElements = 0;
MPI_Allreduce(&localVacuumElements, &globalVacuumElements, 1, MPI_INT, MPI_SUM, f.mesh->GetComm()); MPI_Allreduce(&localVacuumElements, &globalVacuumElements, 1, MPI_INT,
MPI_SUM, f.mesh->GetComm());
REQUIRE(globalVacuumElements > 0); REQUIRE(globalVacuumElements > 0);
@@ -213,65 +229,59 @@ namespace gravity_displacement_force_test_utils {
return densityTrue; return densityTrue;
} }
[[nodiscard]] mean_field::operators::context::gravity_field::GravityFieldRevisions make_revisions() { [[nodiscard]] mean_field::operators::context::gravity_field::
return { GravityFieldRevisions
.discretization = {.value = 3}, make_revisions() {
return {.discretization = {.value = 3},
.displacement = {.value = 5}, .displacement = {.value = 5},
.density = {.value = 7}, .density = {.value = 7},
.gravity_gradient = {.value = 11}, .gravity_gradient = {.value = 11},
.gravity_potential = {.value = 13} .gravity_potential = {.value = 13}};
};
} }
void prepare_gravity_context( void prepare_gravity_context(
mean_field::operators::context::gravity_field::GravityFieldLinearizationContext &context, mean_field::operators::context::gravity_field::
const mfem::Vector &density, GravityFieldLinearizationContext &context,
const mfem::Vector &displacement, const mfem::Vector &density, const mfem::Vector &displacement,
const mfem::Vector &gravityGradient, const mfem::Vector &gravityGradient, const mfem::Vector &gravityPotential,
const mfem::Vector &gravityPotential, const mean_field::operators::context::gravity_field::GravityFieldRevisions
const mean_field::operators::context::gravity_field::GravityFieldRevisions &revisions &revisions) {
) {
context.Prepare( context.Prepare(
{.density = context.GetDensityMap().gather(density), {.density = context.GetDensityMap().gather(density),
.displacement = context.GetDisplacementMap().gather(displacement), .displacement = context.GetDisplacementMap().gather(displacement),
.gravity_gradient = context.GetGravityGradientMap().gather(gravityGradient), .gravity_gradient =
.gravity_potential = context.GetGravityPotentialMap().gather(gravityPotential)}, context.GetGravityGradientMap().gather(gravityGradient),
revisions .gravity_potential =
); context.GetGravityPotentialMap().gather(gravityPotential)},
revisions);
} }
[[nodiscard]] double relative_difference( [[nodiscard]] double relative_difference(const mfem::Vector &left,
const mfem::Vector &left,
const mfem::Vector &right, const mfem::Vector &right,
const MPI_Comm communicator const MPI_Comm communicator) {
) { MFEM_VERIFY(left.Size() == right.Size(),
MFEM_VERIFY( "Cannot compare gravity-displacement-force vectors with "
left.Size() == right.Size(), "Cannot compare gravity-displacement-force vectors with " "different sizes.");
"different sizes."
);
mfem::Vector difference(left); mfem::Vector difference(left);
difference -= right; difference -= right;
const double scale = std::max( const double scale =
{gravity_prepared_test_utils::global_norm(left, communicator), std::max({gravity_prepared_test_utils::global_norm(left, communicator),
gravity_prepared_test_utils::global_norm(right, communicator), gravity_prepared_test_utils::global_norm(right, communicator),
100.0 * std::numeric_limits<double>::epsilon()} 100.0 * std::numeric_limits<double>::epsilon()});
);
return gravity_prepared_test_utils::global_norm(difference, communicator) / scale; return gravity_prepared_test_utils::global_norm(difference, communicator) /
scale;
} }
[[nodiscard]] mfem::Vector centered_difference( [[nodiscard]] mfem::Vector centered_difference(
const mean_field::fem::FEM &f, const mean_field::fem::FEM &f, const mfem::Vector &baseDensity,
const mfem::Vector &baseDensity,
const mfem::Vector &densityDirection, const mfem::Vector &densityDirection,
const mfem::Vector &baseGravityGradient, const mfem::Vector &baseGravityGradient,
const mfem::Vector &gravityGradientDirection, const mfem::Vector &gravityGradientDirection,
const mfem::Vector &baseDisplacement, const mfem::Vector &baseDisplacement,
const mfem::Vector &displacementDirection, const mfem::Vector &displacementDirection, const double step) {
const double step
) {
mfem::Vector plusDensity(baseDensity); mfem::Vector plusDensity(baseDensity);
plusDensity.Add(step, densityDirection); plusDensity.Add(step, densityDirection);
@@ -294,12 +304,12 @@ namespace gravity_displacement_force_test_utils {
mfem::Vector minusResidual; mfem::Vector minusResidual;
mean_field::operators::kernels::apply_gravity_displacement_force_residual( mean_field::operators::kernels::apply_gravity_displacement_force_residual(
f, *f.domainMapperStateless, plusDensity, plusGravity, plusDisplacement, plusResidual f, *f.domainMapperStateless, plusDensity, plusGravity, plusDisplacement,
); plusResidual);
mean_field::operators::kernels::apply_gravity_displacement_force_residual( mean_field::operators::kernels::apply_gravity_displacement_force_residual(
f, *f.domainMapperStateless, minusDensity, minusGravity, minusDisplacement, minusResidual f, *f.domainMapperStateless, minusDensity, minusGravity,
); minusDisplacement, minusResidual);
plusResidual -= minusResidual; plusResidual -= minusResidual;
plusResidual /= 2.0 * step; plusResidual /= 2.0 * step;
@@ -310,8 +320,7 @@ namespace gravity_displacement_force_test_utils {
[[nodiscard]] mfem::Vector copy_residual_block( [[nodiscard]] mfem::Vector copy_residual_block(
const mfem::Vector &action, const mfem::Vector &action,
const mean_field::operators::GravityDisplacementForceLayout &layout, const mean_field::operators::GravityDisplacementForceLayout &layout,
const mean_field::utils::blocks::residual_block<index> block const mean_field::utils::blocks::residual_block<index> block) {
) {
mfem::Vector result(layout.size(block)); mfem::Vector result(layout.size(block));
const int offset = layout.offset(block); const int offset = layout.offset(block);
@@ -323,19 +332,18 @@ namespace gravity_displacement_force_test_utils {
} }
} // namespace gravity_displacement_force_test_utils } // namespace gravity_displacement_force_test_utils
TEST_CASE( TEST_CASE("Gravity Displacement Force Query Includes Every Registered Operand",
"Gravity Displacement Force Query Includes Every Registered Operand", tags::gravity_unit) {
tags::gravity_unit using DisplacementField =
) { mean_field::field::Field<mean_field::field::Displacement>;
using DisplacementField = mean_field::field::Field<mean_field::field::Displacement>;
constexpr int geometryWeightOrder = 4; constexpr int geometryWeightOrder = 4;
constexpr mean_field::quadrature::Query query = constexpr mean_field::quadrature::Query query = DisplacementField::make_query<
DisplacementField::make_query<mean_field::field::Displacement::Form::GravityForce>( mean_field::field::Displacement::Form::GravityForce>(
mean_field::quadrature::QuadratureRole::discretization, geometryWeightOrder, {}, mean_field::quadrature::QuadratureRole::discretization,
mean_field::utils::DOMAINS::STELLAR, mean_field::quadrature::MappingKind::general geometryWeightOrder, {}, mean_field::utils::DOMAINS::STELLAR,
); mean_field::quadrature::MappingKind::general);
/* /*
* rho: 2 * rho: 2
@@ -348,7 +356,8 @@ TEST_CASE(
STATIC_REQUIRE(query.term == mean_field::quadrature::Term::gravity_force); STATIC_REQUIRE(query.term == mean_field::quadrature::Term::gravity_force);
STATIC_REQUIRE(query.role == mean_field::quadrature::QuadratureRole::discretization); STATIC_REQUIRE(query.role ==
mean_field::quadrature::QuadratureRole::discretization);
STATIC_REQUIRE(query.domain == mean_field::utils::DOMAINS::STELLAR); STATIC_REQUIRE(query.domain == mean_field::utils::DOMAINS::STELLAR);
@@ -358,11 +367,9 @@ TEST_CASE(
STATIC_REQUIRE(*query.base_order == expectedBaseOrder); STATIC_REQUIRE(*query.base_order == expectedBaseOrder);
} }
TEST_CASE( TEST_CASE("Gravity Displacement Force Uses Positive Grad-Phi Sign And Excludes "
"Gravity Displacement Force Uses Positive Grad-Phi Sign And Excludes "
"Vacuum", "Vacuum",
tags::gravity_kernel_accuracy tags::gravity_kernel_accuracy) {
) {
mean_field::utils::Args args = test_utils::setup_args(); mean_field::utils::Args args = test_utils::setup_args();
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0); mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
@@ -384,7 +391,8 @@ TEST_CASE(
value(0) = 1.0; value(0) = 1.0;
}; };
mfem::VectorFunctionCoefficient gravityCoefficient(3, constantGravityFunction); mfem::VectorFunctionCoefficient gravityCoefficient(3,
constantGravityFunction);
gravityField.ProjectCoefficient(gravityCoefficient); gravityField.ProjectCoefficient(gravityCoefficient);
@@ -397,8 +405,8 @@ TEST_CASE(
mfem::Vector residual; mfem::Vector residual;
mean_field::operators::kernels::apply_gravity_displacement_force_residual( mean_field::operators::kernels::apply_gravity_displacement_force_residual(
f, *f.domainMapperStateless, density, gravityGradient, displacement, residual f, *f.domainMapperStateless, density, gravityGradient, displacement,
); residual);
mfem::ParGridFunction testField(f.displacementFes.get()); mfem::ParGridFunction testField(f.displacementFes.get());
testField.ProjectCoefficient(gravityCoefficient); testField.ProjectCoefficient(gravityCoefficient);
@@ -406,54 +414,57 @@ TEST_CASE(
mfem::Vector testDirection; mfem::Vector testDirection;
testField.GetTrueDofs(testDirection); testField.GetTrueDofs(testDirection);
const double signedWork = gravity_prepared_test_utils::global_dot(residual, testDirection, f.mesh->GetComm()); const double signedWork = gravity_prepared_test_utils::global_dot(
residual, testDirection, f.mesh->GetComm());
INFO("Constant +x gravity-force work = " << signedWork); INFO("Constant +x gravity-force work = " << signedWork);
CHECK(signedWork > 0.0); CHECK(signedWork > 0.0);
const mfem::Vector vacuumDensity = gravity_displacement_force_test_utils::make_vacuum_only_density(f); const mfem::Vector vacuumDensity =
gravity_displacement_force_test_utils::make_vacuum_only_density(f);
mfem::Vector vacuumResidual; mfem::Vector vacuumResidual;
mean_field::operators::kernels::apply_gravity_displacement_force_residual( mean_field::operators::kernels::apply_gravity_displacement_force_residual(
f, *f.domainMapperStateless, vacuumDensity, gravityGradient, displacement, vacuumResidual f, *f.domainMapperStateless, vacuumDensity, gravityGradient, displacement,
); vacuumResidual);
CHECK(gravity_prepared_test_utils::global_norm(vacuumResidual, f.mesh->GetComm()) == 0.0); CHECK(gravity_prepared_test_utils::global_norm(vacuumResidual,
f.mesh->GetComm()) == 0.0);
} }
TEST_CASE( TEST_CASE("Prepared Gravity Displacement Force Reuses Shared Gravity Revisions",
"Prepared Gravity Displacement Force Reuses Shared Gravity Revisions", tags::gravity_prepared) {
tags::gravity_prepared
) {
mean_field::utils::Args args = test_utils::setup_args(); mean_field::utils::Args args = test_utils::setup_args();
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0); mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
REQUIRE(f.okay()); REQUIRE(f.okay());
mfem::Vector density = gravity_displacement_force_test_utils::make_density(f, 0.31); mfem::Vector density =
gravity_displacement_force_test_utils::make_density(f, 0.31);
const mfem::Vector gravityGradient = gravity_displacement_force_test_utils::make_gravity_gradient(f, 0.47); const mfem::Vector gravityGradient =
gravity_displacement_force_test_utils::make_gravity_gradient(f, 0.47);
const mfem::Vector displacement = gravity_prepared_test_utils::make_displacement(f, 0.61); const mfem::Vector displacement =
gravity_prepared_test_utils::make_displacement(f, 0.61);
mfem::Vector gravityPotential(f.gravityPotentialFes->GetTrueVSize()); mfem::Vector gravityPotential(f.gravityPotentialFes->GetTrueVSize());
gravityPotential = 0.0; gravityPotential = 0.0;
auto revisions = gravity_displacement_force_test_utils::make_revisions(); auto revisions = gravity_displacement_force_test_utils::make_revisions();
mean_field::operators::context::gravity_field::GravityFieldLinearizationContext gravityContext( mean_field::operators::context::gravity_field::
f, *f.domainMapperStateless GravityFieldLinearizationContext gravityContext(f,
); *f.domainMapperStateless);
gravity_displacement_force_test_utils::prepare_gravity_context( gravity_displacement_force_test_utils::prepare_gravity_context(
gravityContext, density, displacement, gravityGradient, gravityPotential, revisions gravityContext, density, displacement, gravityGradient, gravityPotential,
); revisions);
mean_field::operators::PreparedGravityDisplacementForceOperator preparedOperator( mean_field::operators::PreparedGravityDisplacementForceOperator
f, *f.domainMapperStateless, gravityContext preparedOperator(f, *f.domainMapperStateless, gravityContext);
);
const auto initialReport = preparedOperator.Prepare(); const auto initialReport = preparedOperator.Prepare();
REQUIRE(initialReport.DidAnyWork()); REQUIRE(initialReport.DidAnyWork());
@@ -465,24 +476,23 @@ TEST_CASE(
preparedOperator.BuildResidual(preparedResidual); preparedOperator.BuildResidual(preparedResidual);
mean_field::operators::kernels::apply_gravity_displacement_force_residual( mean_field::operators::kernels::apply_gravity_displacement_force_residual(
f, *f.domainMapperStateless, density, gravityGradient, displacement, kernelResidual f, *f.domainMapperStateless, density, gravityGradient, displacement,
); kernelResidual);
const mfem::Vector kernelResidualReduced = gravityContext.GetDisplacementMap().gather(kernelResidual); const mfem::Vector kernelResidualReduced =
gravityContext.GetDisplacementMap().gather(kernelResidual);
CHECK( CHECK(gravity_displacement_force_test_utils::relative_difference(
gravity_displacement_force_test_utils::relative_difference( preparedResidual, kernelResidualReduced, f.mesh->GetComm()) <
preparedResidual, kernelResidualReduced, f.mesh->GetComm() 2.0e-12);
) < 2.0e-12
);
CHECK_FALSE(preparedOperator.Prepare().DidAnyWork()); CHECK_FALSE(preparedOperator.Prepare().DidAnyWork());
++revisions.gravity_potential.value; ++revisions.gravity_potential.value;
gravity_displacement_force_test_utils::prepare_gravity_context( gravity_displacement_force_test_utils::prepare_gravity_context(
gravityContext, density, displacement, gravityGradient, gravityPotential, revisions gravityContext, density, displacement, gravityGradient, gravityPotential,
); revisions);
CHECK(preparedOperator.IsPrepared()); CHECK(preparedOperator.IsPrepared());
CHECK_FALSE(preparedOperator.Prepare().DidAnyWork()); CHECK_FALSE(preparedOperator.Prepare().DidAnyWork());
@@ -491,8 +501,8 @@ TEST_CASE(
++revisions.density.value; ++revisions.density.value;
gravity_displacement_force_test_utils::prepare_gravity_context( gravity_displacement_force_test_utils::prepare_gravity_context(
gravityContext, density, displacement, gravityGradient, gravityPotential, revisions gravityContext, density, displacement, gravityGradient, gravityPotential,
); revisions);
CHECK_FALSE(preparedOperator.IsPrepared()); CHECK_FALSE(preparedOperator.IsPrepared());
@@ -506,73 +516,79 @@ TEST_CASE(
TEST_CASE( TEST_CASE(
"Gravity Displacement Force Jacobian Matches All Columns And Centered " "Gravity Displacement Force Jacobian Matches All Columns And Centered "
"Differences", "Differences",
tags::gravity_prepared_jacobian_accuracy tags::gravity_prepared_jacobian_accuracy) {
) {
mean_field::utils::Args args = test_utils::setup_args(); mean_field::utils::Args args = test_utils::setup_args();
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0); mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
REQUIRE(f.okay()); REQUIRE(f.okay());
const mfem::Vector density = gravity_displacement_force_test_utils::make_density(f, 0.37); const mfem::Vector density =
gravity_displacement_force_test_utils::make_density(f, 0.37);
const mfem::Vector densityDirection = gravity_displacement_force_test_utils::make_density_direction(f, 0.53); const mfem::Vector densityDirection =
gravity_displacement_force_test_utils::make_density_direction(f, 0.53);
const mfem::Vector gravityGradient = gravity_displacement_force_test_utils::make_gravity_gradient(f, 0.67); const mfem::Vector gravityGradient =
gravity_displacement_force_test_utils::make_gravity_gradient(f, 0.67);
const mfem::Vector gravityGradientDirection = const mfem::Vector gravityGradientDirection =
gravity_displacement_force_test_utils::make_gravity_gradient_direction(f, 0.71); gravity_displacement_force_test_utils::make_gravity_gradient_direction(
f, 0.71);
const mfem::Vector displacement = gravity_prepared_test_utils::make_displacement(f, 0.59); const mfem::Vector displacement =
gravity_prepared_test_utils::make_displacement(f, 0.59);
const mfem::Vector displacementDirection = gravity_displacement_force_test_utils::make_displacement_direction(f); const mfem::Vector displacementDirection =
gravity_displacement_force_test_utils::make_displacement_direction(f);
mfem::Vector gravityPotential(f.gravityPotentialFes->GetTrueVSize()); mfem::Vector gravityPotential(f.gravityPotentialFes->GetTrueVSize());
gravityPotential = 0.0; gravityPotential = 0.0;
mean_field::operators::context::gravity_field::GravityFieldLinearizationContext gravityContext( mean_field::operators::context::gravity_field::
f, *f.domainMapperStateless GravityFieldLinearizationContext gravityContext(f,
); *f.domainMapperStateless);
gravity_displacement_force_test_utils::prepare_gravity_context( gravity_displacement_force_test_utils::prepare_gravity_context(
gravityContext, density, displacement, gravityGradient, gravityPotential, gravityContext, density, displacement, gravityGradient, gravityPotential,
gravity_displacement_force_test_utils::make_revisions() gravity_displacement_force_test_utils::make_revisions());
);
mean_field::operators::PreparedGravityDisplacementForceOperator preparedOperator( mean_field::operators::PreparedGravityDisplacementForceOperator
f, *f.domainMapperStateless, gravityContext preparedOperator(f, *f.domainMapperStateless, gravityContext);
);
preparedOperator.Prepare(); preparedOperator.Prepare();
const mfem::Vector densityDirectionReduced = gravityContext.GetDensityMap().gather(densityDirection); const mfem::Vector densityDirectionReduced =
gravityContext.GetDensityMap().gather(densityDirection);
const mfem::Vector gravityGradientDirectionReduced = const mfem::Vector gravityGradientDirectionReduced =
gravityContext.GetGravityGradientMap().gather(gravityGradientDirection); gravityContext.GetGravityGradientMap().gather(gravityGradientDirection);
const mfem::Vector displacementDirectionReduced = gravityContext.GetDisplacementMap().gather(displacementDirection); const mfem::Vector displacementDirectionReduced =
gravityContext.GetDisplacementMap().gather(displacementDirection);
mfem::Vector densityAction; mfem::Vector densityAction;
mfem::Vector gravityAction; mfem::Vector gravityAction;
mfem::Vector displacementAction; mfem::Vector displacementAction;
mfem::Vector completeAction; mfem::Vector completeAction;
preparedOperator.ApplyDensityJacobianAction(densityDirectionReduced, densityAction); preparedOperator.ApplyDensityJacobianAction(densityDirectionReduced,
densityAction);
preparedOperator.ApplyGravityGradientJacobianAction(gravityGradientDirectionReduced, gravityAction); preparedOperator.ApplyGravityGradientJacobianAction(
gravityGradientDirectionReduced, gravityAction);
preparedOperator.ApplyDisplacementJacobianAction(displacementDirectionReduced, displacementAction); preparedOperator.ApplyDisplacementJacobianAction(displacementDirectionReduced,
displacementAction);
preparedOperator.ApplyCompleteJacobianAction( preparedOperator.ApplyCompleteJacobianAction(
densityDirectionReduced, displacementDirectionReduced, gravityGradientDirectionReduced, completeAction densityDirectionReduced, displacementDirectionReduced,
); gravityGradientDirectionReduced, completeAction);
mfem::Vector summedColumns(densityAction); mfem::Vector summedColumns(densityAction);
summedColumns += gravityAction; summedColumns += gravityAction;
summedColumns += displacementAction; summedColumns += displacementAction;
CHECK( CHECK(gravity_displacement_force_test_utils::relative_difference(
gravity_displacement_force_test_utils::relative_difference(completeAction, summedColumns, f.mesh->GetComm()) < completeAction, summedColumns, f.mesh->GetComm()) < 2.0e-12);
2.0e-12
);
mfem::Vector zeroDensity(densityDirection.Size()); mfem::Vector zeroDensity(densityDirection.Size());
mfem::Vector zeroGravity(gravityGradientDirection.Size()); mfem::Vector zeroGravity(gravityGradientDirection.Size());
@@ -583,41 +599,50 @@ TEST_CASE(
constexpr double step = 1.0e-5; constexpr double step = 1.0e-5;
const mfem::Vector densityDifferenceTrue = gravity_displacement_force_test_utils::centered_difference( const mfem::Vector densityDifferenceTrue =
f, density, densityDirection, gravityGradient, zeroGravity, displacement, zeroDisplacement, step gravity_displacement_force_test_utils::centered_difference(
); f, density, densityDirection, gravityGradient, zeroGravity,
displacement, zeroDisplacement, step);
const mfem::Vector gravityDifferenceTrue = gravity_displacement_force_test_utils::centered_difference( const mfem::Vector gravityDifferenceTrue =
f, density, zeroDensity, gravityGradient, gravityGradientDirection, displacement, zeroDisplacement, step gravity_displacement_force_test_utils::centered_difference(
); f, density, zeroDensity, gravityGradient, gravityGradientDirection,
displacement, zeroDisplacement, step);
const mfem::Vector displacementDifferenceTrue = gravity_displacement_force_test_utils::centered_difference( const mfem::Vector displacementDifferenceTrue =
f, density, zeroDensity, gravityGradient, zeroGravity, displacement, displacementDirection, step gravity_displacement_force_test_utils::centered_difference(
); f, density, zeroDensity, gravityGradient, zeroGravity, displacement,
displacementDirection, step);
const mfem::Vector completeDifferenceTrue = gravity_displacement_force_test_utils::centered_difference( const mfem::Vector completeDifferenceTrue =
f, density, densityDirection, gravityGradient, gravityGradientDirection, displacement, displacementDirection, gravity_displacement_force_test_utils::centered_difference(
step f, density, densityDirection, gravityGradient,
); gravityGradientDirection, displacement, displacementDirection, step);
const mfem::Vector densityDifference = gravityContext.GetDisplacementMap().gather(densityDifferenceTrue); const mfem::Vector densityDifference =
const mfem::Vector gravityDifference = gravityContext.GetDisplacementMap().gather(gravityDifferenceTrue); gravityContext.GetDisplacementMap().gather(densityDifferenceTrue);
const mfem::Vector displacementDifference = gravityContext.GetDisplacementMap().gather(displacementDifferenceTrue); const mfem::Vector gravityDifference =
const mfem::Vector completeDifference = gravityContext.GetDisplacementMap().gather(completeDifferenceTrue); gravityContext.GetDisplacementMap().gather(gravityDifferenceTrue);
const mfem::Vector displacementDifference =
gravityContext.GetDisplacementMap().gather(displacementDifferenceTrue);
const mfem::Vector completeDifference =
gravityContext.GetDisplacementMap().gather(completeDifferenceTrue);
const double densityError = const double densityError =
gravity_displacement_force_test_utils::relative_difference(densityAction, densityDifference, f.mesh->GetComm()); gravity_displacement_force_test_utils::relative_difference(
densityAction, densityDifference, f.mesh->GetComm());
const double gravityError = const double gravityError =
gravity_displacement_force_test_utils::relative_difference(gravityAction, gravityDifference, f.mesh->GetComm()); gravity_displacement_force_test_utils::relative_difference(
gravityAction, gravityDifference, f.mesh->GetComm());
const double displacementError = gravity_displacement_force_test_utils::relative_difference( const double displacementError =
displacementAction, displacementDifference, f.mesh->GetComm() gravity_displacement_force_test_utils::relative_difference(
); displacementAction, displacementDifference, f.mesh->GetComm());
const double completeError = gravity_displacement_force_test_utils::relative_difference( const double completeError =
completeAction, completeDifference, f.mesh->GetComm() gravity_displacement_force_test_utils::relative_difference(
); completeAction, completeDifference, f.mesh->GetComm());
INFO("Density-column centered-difference error = " << densityError); INFO("Density-column centered-difference error = " << densityError);
INFO("Gravity-column centered-difference error = " << gravityError); INFO("Gravity-column centered-difference error = " << gravityError);
@@ -630,70 +655,82 @@ TEST_CASE(
CHECK(completeError < 3.0e-8); CHECK(completeError < 3.0e-8);
} }
TEST_CASE( TEST_CASE("Prepared Gravity Displacement Force MFEM Adapter Routes Only R-d",
"Prepared Gravity Displacement Force MFEM Adapter Routes Only R-d", tags::gravity_prepared_unit) {
tags::gravity_prepared_unit
) {
mean_field::utils::Args args = test_utils::setup_args(); mean_field::utils::Args args = test_utils::setup_args();
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0); mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
REQUIRE(f.okay()); REQUIRE(f.okay());
const mfem::Vector density = gravity_displacement_force_test_utils::make_density(f, 0.41); const mfem::Vector density =
gravity_displacement_force_test_utils::make_density(f, 0.41);
const mfem::Vector densityDirection = gravity_displacement_force_test_utils::make_density_direction(f, 0.57); const mfem::Vector densityDirection =
gravity_displacement_force_test_utils::make_density_direction(f, 0.57);
const mfem::Vector gravityGradient = gravity_displacement_force_test_utils::make_gravity_gradient(f, 0.63); const mfem::Vector gravityGradient =
gravity_displacement_force_test_utils::make_gravity_gradient(f, 0.63);
const mfem::Vector gravityGradientDirection = const mfem::Vector gravityGradientDirection =
gravity_displacement_force_test_utils::make_gravity_gradient_direction(f, 0.77); gravity_displacement_force_test_utils::make_gravity_gradient_direction(
f, 0.77);
const mfem::Vector displacement = gravity_prepared_test_utils::make_displacement(f, 0.51); const mfem::Vector displacement =
gravity_prepared_test_utils::make_displacement(f, 0.51);
const mfem::Vector displacementDirection = gravity_displacement_force_test_utils::make_displacement_direction(f); const mfem::Vector displacementDirection =
gravity_displacement_force_test_utils::make_displacement_direction(f);
mfem::Vector gravityPotential(f.gravityPotentialFes->GetTrueVSize()); mfem::Vector gravityPotential(f.gravityPotentialFes->GetTrueVSize());
gravityPotential = 0.0; gravityPotential = 0.0;
mean_field::operators::context::gravity_field::GravityFieldLinearizationContext gravityContext( mean_field::operators::context::gravity_field::
f, *f.domainMapperStateless GravityFieldLinearizationContext gravityContext(f,
); *f.domainMapperStateless);
gravity_displacement_force_test_utils::prepare_gravity_context( gravity_displacement_force_test_utils::prepare_gravity_context(
gravityContext, density, displacement, gravityGradient, gravityPotential, gravityContext, density, displacement, gravityGradient, gravityPotential,
gravity_displacement_force_test_utils::make_revisions() gravity_displacement_force_test_utils::make_revisions());
);
mean_field::operators::PreparedGravityDisplacementForceOperator preparedOperator( mean_field::operators::PreparedGravityDisplacementForceOperator
f, *f.domainMapperStateless, gravityContext preparedOperator(f, *f.domainMapperStateless, gravityContext);
);
preparedOperator.Prepare(); preparedOperator.Prepare();
const mfem::Vector densityDirectionReduced = gravityContext.GetDensityMap().gather(densityDirection); const mfem::Vector densityDirectionReduced =
gravityContext.GetDensityMap().gather(densityDirection);
const mfem::Vector gravityGradientDirectionReduced = const mfem::Vector gravityGradientDirectionReduced =
gravityContext.GetGravityGradientMap().gather(gravityGradientDirection); gravityContext.GetGravityGradientMap().gather(gravityGradientDirection);
const mfem::Vector displacementDirectionReduced = gravityContext.GetDisplacementMap().gather(displacementDirection); const mfem::Vector displacementDirectionReduced =
gravityContext.GetDisplacementMap().gather(displacementDirection);
const auto layout = gravity_displacement_force_test_utils::make_layout(f); const auto layout = gravity_displacement_force_test_utils::make_layout(f);
mean_field::operators::PreparedGravityDisplacementForceJacobianOperator adapter(layout, preparedOperator); mean_field::operators::PreparedGravityDisplacementForceJacobianOperator
adapter(layout, preparedOperator);
mfem::BlockVector direction(layout.value_offsets()); mfem::BlockVector direction(layout.value_offsets());
direction = 0.0; direction = 0.0;
direction.GetBlock(gravity_displacement_force_test_utils::densityValue) = densityDirectionReduced; direction.GetBlock(gravity_displacement_force_test_utils::densityValue) =
densityDirectionReduced;
direction.GetBlock(gravity_displacement_force_test_utils::displacementValue) = displacementDirectionReduced; direction.GetBlock(gravity_displacement_force_test_utils::displacementValue) =
displacementDirectionReduced;
direction.GetBlock(gravity_displacement_force_test_utils::gravityGradientValue) = gravityGradientDirectionReduced; direction.GetBlock(
gravity_displacement_force_test_utils::gravityGradientValue) =
gravityGradientDirectionReduced;
direction.GetBlock(gravity_displacement_force_test_utils::gravityPotentialValue) = 0.29; direction.GetBlock(
gravity_displacement_force_test_utils::gravityPotentialValue) = 0.29;
direction.GetBlock(gravity_displacement_force_test_utils::enthalpyValue) = -0.37; direction.GetBlock(gravity_displacement_force_test_utils::enthalpyValue) =
-0.37;
direction.GetBlock(gravity_displacement_force_test_utils::barotropicConstantValue) = 0.43; direction.GetBlock(
gravity_displacement_force_test_utils::barotropicConstantValue) = 0.43;
mfem::Vector action; mfem::Vector action;
adapter.Mult(direction, action); adapter.Mult(direction, action);
@@ -701,39 +738,36 @@ TEST_CASE(
mfem::Vector expectedDisplacementAction; mfem::Vector expectedDisplacementAction;
preparedOperator.ApplyCompleteJacobianAction( preparedOperator.ApplyCompleteJacobianAction(
densityDirectionReduced, displacementDirectionReduced, gravityGradientDirectionReduced, densityDirectionReduced, displacementDirectionReduced,
expectedDisplacementAction gravityGradientDirectionReduced, expectedDisplacementAction);
);
const mfem::Vector actualDisplacementAction = gravity_displacement_force_test_utils::copy_residual_block( const mfem::Vector actualDisplacementAction =
action, layout, gravity_displacement_force_test_utils::displacementResidual gravity_displacement_force_test_utils::copy_residual_block(
); action, layout,
gravity_displacement_force_test_utils::displacementResidual);
CHECK( CHECK(gravity_displacement_force_test_utils::relative_difference(
gravity_displacement_force_test_utils::relative_difference( actualDisplacementAction, expectedDisplacementAction,
actualDisplacementAction, expectedDisplacementAction, f.mesh->GetComm() f.mesh->GetComm()) < 2.0e-12);
) < 2.0e-12
);
const std::array<mfem::Vector, 5> zeroRows{ const std::array<mfem::Vector, 5> zeroRows{
gravity_displacement_force_test_utils::copy_residual_block( gravity_displacement_force_test_utils::copy_residual_block(
action, layout, gravity_displacement_force_test_utils::gravityGradientResidual action, layout,
), gravity_displacement_force_test_utils::gravityGradientResidual),
gravity_displacement_force_test_utils::copy_residual_block( gravity_displacement_force_test_utils::copy_residual_block(
action, layout, gravity_displacement_force_test_utils::gravityPotentialResidual action, layout,
), gravity_displacement_force_test_utils::gravityPotentialResidual),
gravity_displacement_force_test_utils::copy_residual_block( gravity_displacement_force_test_utils::copy_residual_block(
action, layout, gravity_displacement_force_test_utils::densityResidual action, layout,
), gravity_displacement_force_test_utils::densityResidual),
gravity_displacement_force_test_utils::copy_residual_block( gravity_displacement_force_test_utils::copy_residual_block(
action, layout, gravity_displacement_force_test_utils::enthalpyResidual action, layout,
), gravity_displacement_force_test_utils::enthalpyResidual),
gravity_displacement_force_test_utils::copy_residual_block( gravity_displacement_force_test_utils::copy_residual_block(
action, layout, gravity_displacement_force_test_utils::massResidual action, layout, gravity_displacement_force_test_utils::massResidual)};
)
};
for (const mfem::Vector &row : zeroRows) { for (const mfem::Vector &row : zeroRows) {
CHECK(gravity_prepared_test_utils::global_norm(row, f.mesh->GetComm()) == 0.0); CHECK(gravity_prepared_test_utils::global_norm(row, f.mesh->GetComm()) ==
0.0);
} }
} }

View File

@@ -20,25 +20,17 @@ namespace gravity_displacement_force_analytic_test_utils {
return (4.0 / 3.0) * std::numbers::pi * radius * radius * radius; return (4.0 / 3.0) * std::numbers::pi * radius * radius * radius;
} }
[[nodiscard]] double determinant( [[nodiscard]] double determinant(const std::array<double, 3> &scales) {
const std::array<
double,
3> &scales
) {
return scales[0] * scales[1] * scales[2]; return scales[0] * scales[1] * scales[2];
} }
[[nodiscard]] double relative_scalar_error( [[nodiscard]] double relative_scalar_error(const double computed,
const double computed, const double expected) {
const double expected
) {
return std::abs(computed - expected) / std::max(std::abs(expected), 1.0e-30); return std::abs(computed - expected) / std::max(std::abs(expected), 1.0e-30);
} }
[[nodiscard]] mfem::Vector make_constant_density( [[nodiscard]] mfem::Vector make_constant_density(const mean_field::fem::FEM &f,
const mean_field::fem::FEM &f, const double densityValue) {
const double densityValue
) {
mfem::ParGridFunction densityField(f.densityFes.get()); mfem::ParGridFunction densityField(f.densityFes.get());
mfem::ConstantCoefficient densityCoefficient(densityValue); mfem::ConstantCoefficient densityCoefficient(densityValue);
densityField.ProjectCoefficient(densityCoefficient); densityField.ProjectCoefficient(densityCoefficient);
@@ -48,23 +40,21 @@ namespace gravity_displacement_force_analytic_test_utils {
return densityTrue; return densityTrue;
} }
[[nodiscard]] mfem::Vector make_reference_gravity( [[nodiscard]] mfem::Vector
const mean_field::fem::FEM &f, make_reference_gravity(const mean_field::fem::FEM &f,
const std::array< const std::array<double, 3> &referenceGravity) {
double,
3> &referenceGravity
) {
mfem::ParGridFunction gravityField(f.gravityFluxFes.get()); mfem::ParGridFunction gravityField(f.gravityFluxFes.get());
mfem::VectorFunctionCoefficient gravityCoefficient( mfem::VectorFunctionCoefficient gravityCoefficient(
f.mesh->Dimension(), [referenceGravity](const mfem::Vector &, mfem::Vector &value) { f.mesh->Dimension(),
[referenceGravity](const mfem::Vector &, mfem::Vector &value) {
value.SetSize(3); value.SetSize(3);
for (int component = 0; component < 3; ++component) { for (int component = 0; component < 3; ++component) {
value(component) = referenceGravity[static_cast<std::size_t>(component)]; value(component) =
referenceGravity[static_cast<std::size_t>(component)];
} }
} });
);
gravityField.ProjectCoefficient(gravityCoefficient); gravityField.ProjectCoefficient(gravityCoefficient);
@@ -73,21 +63,19 @@ namespace gravity_displacement_force_analytic_test_utils {
return gravityTrue; return gravityTrue;
} }
[[nodiscard]] mfem::Vector make_radial_gravity( [[nodiscard]] mfem::Vector make_radial_gravity(const mean_field::fem::FEM &f,
const mean_field::fem::FEM &f, const double radialCoefficient) {
const double radialCoefficient
) {
mfem::ParGridFunction gravityField(f.gravityFluxFes.get()); mfem::ParGridFunction gravityField(f.gravityFluxFes.get());
mfem::VectorFunctionCoefficient gravityCoefficient( mfem::VectorFunctionCoefficient gravityCoefficient(
f.mesh->Dimension(), [radialCoefficient](const mfem::Vector &position, mfem::Vector &value) { f.mesh->Dimension(),
[radialCoefficient](const mfem::Vector &position, mfem::Vector &value) {
value.SetSize(position.Size()); value.SetSize(position.Size());
for (int component = 0; component < position.Size(); ++component) { for (int component = 0; component < position.Size(); ++component) {
value(component) = radialCoefficient * position(component); value(component) = radialCoefficient * position(component);
} }
} });
);
gravityField.ProjectCoefficient(gravityCoefficient); gravityField.ProjectCoefficient(gravityCoefficient);
@@ -96,23 +84,22 @@ namespace gravity_displacement_force_analytic_test_utils {
return gravityTrue; return gravityTrue;
} }
[[nodiscard]] mfem::Vector make_affine_displacement( [[nodiscard]] mfem::Vector
const mean_field::fem::FEM &f, make_affine_displacement(const mean_field::fem::FEM &f,
const std::array< const std::array<double, 3> &scales) {
double,
3> &scales
) {
mfem::ParGridFunction displacementField(f.displacementFes.get()); mfem::ParGridFunction displacementField(f.displacementFes.get());
mfem::VectorFunctionCoefficient displacementCoefficient( mfem::VectorFunctionCoefficient displacementCoefficient(
f.mesh->Dimension(), [scales](const mfem::Vector &position, mfem::Vector &value) { f.mesh->Dimension(),
[scales](const mfem::Vector &position, mfem::Vector &value) {
value.SetSize(position.Size()); value.SetSize(position.Size());
for (int component = 0; component < position.Size(); ++component) { for (int component = 0; component < position.Size(); ++component) {
value(component) = (scales[static_cast<std::size_t>(component)] - 1.0) * position(component); value(component) =
(scales[static_cast<std::size_t>(component)] - 1.0) *
position(component);
} }
} });
);
displacementField.ProjectCoefficient(displacementCoefficient); displacementField.ProjectCoefficient(displacementCoefficient);
@@ -121,19 +108,18 @@ namespace gravity_displacement_force_analytic_test_utils {
return displacementTrue; return displacementTrue;
} }
[[nodiscard]] mfem::Vector make_constant_test_direction( [[nodiscard]] mfem::Vector
const mean_field::fem::FEM &f, make_constant_test_direction(const mean_field::fem::FEM &f,
const int selectedComponent const int selectedComponent) {
) {
mfem::ParGridFunction testField(f.displacementFes.get()); mfem::ParGridFunction testField(f.displacementFes.get());
mfem::VectorFunctionCoefficient testCoefficient( mfem::VectorFunctionCoefficient testCoefficient(
f.mesh->Dimension(), [selectedComponent](const mfem::Vector &position, mfem::Vector &value) { f.mesh->Dimension(),
[selectedComponent](const mfem::Vector &position, mfem::Vector &value) {
value.SetSize(position.Size()); value.SetSize(position.Size());
value = 0.0; value = 0.0;
value(selectedComponent) = 1.0; value(selectedComponent) = 1.0;
} });
);
testField.ProjectCoefficient(testCoefficient); testField.ProjectCoefficient(testCoefficient);
@@ -142,12 +128,13 @@ namespace gravity_displacement_force_analytic_test_utils {
return testTrue; return testTrue;
} }
[[nodiscard]] mfem::Vector make_dilation_test_direction(const mean_field::fem::FEM &f) { [[nodiscard]] mfem::Vector
make_dilation_test_direction(const mean_field::fem::FEM &f) {
mfem::ParGridFunction testField(f.displacementFes.get()); mfem::ParGridFunction testField(f.displacementFes.get());
mfem::VectorFunctionCoefficient testCoefficient( mfem::VectorFunctionCoefficient testCoefficient(
f.mesh->Dimension(), [](const mfem::Vector &position, mfem::Vector &value) { value = position; } f.mesh->Dimension(), [](const mfem::Vector &position,
); mfem::Vector &value) { value = position; });
testField.ProjectCoefficient(testCoefficient); testField.ProjectCoefficient(testCoefficient);
@@ -156,57 +143,64 @@ namespace gravity_displacement_force_analytic_test_utils {
return testTrue; return testTrue;
} }
void set_mass_normalized_density( void set_mass_normalized_density(mean_field::fem::FEM &f,
mean_field::fem::FEM &f,
const double targetMass, const double targetMass,
mfem::ParGridFunction &densityField mfem::ParGridFunction &densityField) {
) { const mfem::Vector stellarDensityTrue =
const mfem::Vector stellarDensityTrue = gravity_prepared_test_utils::make_domain_supported_density(f, true); gravity_prepared_test_utils::make_domain_supported_density(f, true);
densityField.SetFromTrueDofs(stellarDensityTrue); densityField.SetFromTrueDofs(stellarDensityTrue);
const double unnormalizedMass = const double unnormalizedMass =
mean_field::analysis::domain_integrate_grid_function(f, densityField, mean_field::utils::DOMAINS::STELLAR); mean_field::analysis::domain_integrate_grid_function(
f, densityField, mean_field::utils::DOMAINS::STELLAR);
MFEM_VERIFY(unnormalizedMass > 0.0, "The analytic gravity-force test obtained non-positive mass."); MFEM_VERIFY(unnormalizedMass > 0.0,
"The analytic gravity-force test obtained non-positive mass.");
densityField *= targetMass / unnormalizedMass; densityField *= targetMass / unnormalizedMass;
} }
} // namespace gravity_displacement_force_analytic_test_utils } // namespace gravity_displacement_force_analytic_test_utils
TEST_CASE( TEST_CASE("Gravity Displacement Force Matches Analytic Affine Resultants",
"Gravity Displacement Force Matches Analytic Affine Resultants", tags::gravity &tags::accuracy &tags::analytic_comparison
tags::gravity &tags::accuracy &tags::analytic_comparison &tags::integration &tags::integration) {
) {
mean_field::utils::Args args = test_utils::setup_args(); mean_field::utils::Args args = test_utils::setup_args();
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0); mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
REQUIRE(f.okay()); REQUIRE(f.okay());
REQUIRE(f.domainMapperStateless != nullptr); REQUIRE(f.domainMapperStateless != nullptr);
REQUIRE(f.mapping != nullptr); REQUIRE(f.domainMapperStateless != nullptr);
constexpr double densityValue = 1.37; constexpr double densityValue = 1.37;
constexpr std::array<double, 3> physicalGravity{0.31, -0.47, 0.22}; constexpr std::array<double, 3> physicalGravity{0.31, -0.47, 0.22};
constexpr std::array<gravity_displacement_force_analytic_test_utils::AffineCase, 3> affineCases{ constexpr std::array<
{{.name = "identity geometry", .scales = {1.0, 1.0, 1.0}}, gravity_displacement_force_analytic_test_utils::AffineCase, 3>
{.name = "volume-preserving affine geometry", .scales = {1.14, 0.93, 1.0 / (1.14 * 0.93)}}, affineCases{{{.name = "identity geometry", .scales = {1.0, 1.0, 1.0}},
{.name = "volume-changing affine geometry", .scales = {1.11, 0.96, 1.07}}} {.name = "volume-preserving affine geometry",
}; .scales = {1.14, 0.93, 1.0 / (1.14 * 0.93)}},
{.name = "volume-changing affine geometry",
.scales = {1.11, 0.96, 1.07}}}};
const mfem::Vector density = gravity_displacement_force_analytic_test_utils::make_constant_density(f, densityValue); const mfem::Vector density =
gravity_displacement_force_analytic_test_utils::make_constant_density(
f, densityValue);
const double referenceVolume = const double referenceVolume =
gravity_displacement_force_analytic_test_utils::analytic_sphere_volume(mean_field::utils::RADIUS); gravity_displacement_force_analytic_test_utils::analytic_sphere_volume(
mean_field::utils::RADIUS);
constexpr double relativeTolerance = 5.0e-6; constexpr double relativeTolerance = 5.0e-6;
for (const gravity_displacement_force_analytic_test_utils::AffineCase &affineCase : affineCases) { for (const gravity_displacement_force_analytic_test_utils::AffineCase
&affineCase : affineCases) {
DYNAMIC_SECTION(affineCase.name) { DYNAMIC_SECTION(affineCase.name) {
const double mapDeterminant = const double mapDeterminant =
gravity_displacement_force_analytic_test_utils::determinant(affineCase.scales); gravity_displacement_force_analytic_test_utils::determinant(
affineCase.scales);
REQUIRE(mapDeterminant > 0.0); REQUIRE(mapDeterminant > 0.0);
@@ -222,35 +216,42 @@ TEST_CASE(
*/ */
for (int component = 0; component < 3; ++component) { for (int component = 0; component < 3; ++component) {
referenceGravity[static_cast<std::size_t>(component)] = referenceGravity[static_cast<std::size_t>(component)] =
mapDeterminant * physicalGravity[static_cast<std::size_t>(component)] / mapDeterminant *
physicalGravity[static_cast<std::size_t>(component)] /
affineCase.scales[static_cast<std::size_t>(component)]; affineCase.scales[static_cast<std::size_t>(component)];
} }
const mfem::Vector gravityGradient = const mfem::Vector gravityGradient =
gravity_displacement_force_analytic_test_utils::make_reference_gravity(f, referenceGravity); gravity_displacement_force_analytic_test_utils::
make_reference_gravity(f, referenceGravity);
const mfem::Vector displacement = const mfem::Vector displacement =
gravity_displacement_force_analytic_test_utils::make_affine_displacement(f, affineCase.scales); gravity_displacement_force_analytic_test_utils::
make_affine_displacement(f, affineCase.scales);
mfem::Vector residual; mfem::Vector residual;
mean_field::operators::kernels::apply_gravity_displacement_force_residual( mean_field::operators::kernels::apply_gravity_displacement_force_residual(
f, *f.domainMapperStateless, density, gravityGradient, displacement, residual f, *f.domainMapperStateless, density, gravityGradient, displacement,
); residual);
for (int component = 0; component < 3; ++component) { for (int component = 0; component < 3; ++component) {
const mfem::Vector testDirection = const mfem::Vector testDirection =
gravity_displacement_force_analytic_test_utils::make_constant_test_direction(f, component); gravity_displacement_force_analytic_test_utils::
make_constant_test_direction(f, component);
const double computedResultant = const double computedResultant =
gravity_prepared_test_utils::global_dot(residual, testDirection, f.mesh->GetComm()); gravity_prepared_test_utils::global_dot(residual, testDirection,
f.mesh->GetComm());
const double expectedResultant = densityValue * physicalGravity[static_cast<std::size_t>(component)] * const double expectedResultant =
densityValue *
physicalGravity[static_cast<std::size_t>(component)] *
mapDeterminant * referenceVolume; mapDeterminant * referenceVolume;
const double relativeError = gravity_displacement_force_analytic_test_utils::relative_scalar_error( const double relativeError =
computedResultant, expectedResultant gravity_displacement_force_analytic_test_utils::
); relative_scalar_error(computedResultant, expectedResultant);
CAPTURE(component); CAPTURE(component);
INFO("Map determinant = " << mapDeterminant); INFO("Map determinant = " << mapDeterminant);
@@ -266,8 +267,8 @@ TEST_CASE(
TEST_CASE( TEST_CASE(
"Gravity Displacement Force Reproduces Analytic Homogeneous Sphere Work", "Gravity Displacement Force Reproduces Analytic Homogeneous Sphere Work",
tags::gravity &tags::accuracy &tags::analytic_comparison &tags::integration tags::gravity &tags::accuracy &tags::analytic_comparison
) { &tags::integration) {
mean_field::utils::Args args = test_utils::setup_args(); mean_field::utils::Args args = test_utils::setup_args();
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0); mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
@@ -277,15 +278,21 @@ TEST_CASE(
const double radius = mean_field::utils::RADIUS; const double radius = mean_field::utils::RADIUS;
const double mass = mean_field::utils::MASS; const double mass = mean_field::utils::MASS;
const double volume = gravity_displacement_force_analytic_test_utils::analytic_sphere_volume(radius); const double volume =
gravity_displacement_force_analytic_test_utils::analytic_sphere_volume(
radius);
const double densityValue = mass / volume; const double densityValue = mass / volume;
const double radialGravityCoefficient = mean_field::utils::G * mass / (radius * radius * radius); const double radialGravityCoefficient =
mean_field::utils::G * mass / (radius * radius * radius);
const mfem::Vector density = gravity_displacement_force_analytic_test_utils::make_constant_density(f, densityValue); const mfem::Vector density =
gravity_displacement_force_analytic_test_utils::make_constant_density(
f, densityValue);
const mfem::Vector gravityGradient = const mfem::Vector gravityGradient =
gravity_displacement_force_analytic_test_utils::make_radial_gravity(f, radialGravityCoefficient); gravity_displacement_force_analytic_test_utils::make_radial_gravity(
f, radialGravityCoefficient);
mfem::Vector displacement(f.displacementFes->GetTrueVSize()); mfem::Vector displacement(f.displacementFes->GetTrueVSize());
displacement = 0.0; displacement = 0.0;
@@ -293,18 +300,22 @@ TEST_CASE(
mfem::Vector residual; mfem::Vector residual;
mean_field::operators::kernels::apply_gravity_displacement_force_residual( mean_field::operators::kernels::apply_gravity_displacement_force_residual(
f, *f.domainMapperStateless, density, gravityGradient, displacement, residual f, *f.domainMapperStateless, density, gravityGradient, displacement,
); residual);
const mfem::Vector dilationDirection = const mfem::Vector dilationDirection =
gravity_displacement_force_analytic_test_utils::make_dilation_test_direction(f); gravity_displacement_force_analytic_test_utils::
make_dilation_test_direction(f);
const double computedWork = gravity_prepared_test_utils::global_dot(residual, dilationDirection, f.mesh->GetComm()); const double computedWork = gravity_prepared_test_utils::global_dot(
residual, dilationDirection, f.mesh->GetComm());
const double analyticWork = (3.0 / 5.0) * mean_field::utils::G * mass * mass / radius; const double analyticWork =
(3.0 / 5.0) * mean_field::utils::G * mass * mass / radius;
const double relativeError = const double relativeError =
gravity_displacement_force_analytic_test_utils::relative_scalar_error(computedWork, analyticWork); gravity_displacement_force_analytic_test_utils::relative_scalar_error(
computedWork, analyticWork);
INFO("Computed positive gravity work = " << computedWork); INFO("Computed positive gravity work = " << computedWork);
INFO("Analytic positive gravity work = " << analyticWork); INFO("Analytic positive gravity work = " << analyticWork);
@@ -316,10 +327,9 @@ TEST_CASE(
CHECK(relativeError < 1.0e-5); CHECK(relativeError < 1.0e-5);
} }
TEST_CASE( TEST_CASE("Solved Homogeneous Sphere Gravity Force Matches Analytic Virial",
"Solved Homogeneous Sphere Gravity Force Matches Analytic Virial", tags::gravity &tags::accuracy &tags::analytic_comparison
tags::gravity &tags::accuracy &tags::analytic_comparison &tags::integration &tags::initialization &tags::integration &tags::initialization) {
) {
mean_field::utils::Args args = test_utils::setup_args(); mean_field::utils::Args args = test_utils::setup_args();
args.p.rtol = 1.0e-13; args.p.rtol = 1.0e-13;
args.p.max_iters = std::max(args.p.max_iters, 1000); args.p.max_iters = std::max(args.p.max_iters, 1000);
@@ -332,19 +342,20 @@ TEST_CASE(
mfem::ParGridFunction displacementField(f.displacementFes.get()); mfem::ParGridFunction displacementField(f.displacementFes.get());
displacementField = 0.0; displacementField = 0.0;
REQUIRE(f.mapping != nullptr); REQUIRE(f.domainMapperStateless != nullptr);
f.mapping->ResetDisplacement(); *f.displacement = 0.0;
mean_field::physics::update_stiffness_matrix(f);
const double radius = mean_field::utils::RADIUS; const double radius = mean_field::utils::RADIUS;
const double mass = mean_field::utils::MASS; const double mass = mean_field::utils::MASS;
mfem::ParGridFunction densityField(f.densityFes.get()); mfem::ParGridFunction densityField(f.densityFes.get());
gravity_displacement_force_analytic_test_utils::set_mass_normalized_density(f, mass, densityField); gravity_displacement_force_analytic_test_utils::set_mass_normalized_density(
f, mass, densityField);
const mean_field::physics::GravitySolution gravitySolution = const mean_field::physics::GravitySolution gravitySolution =
mean_field::physics::grav_potential_new(f, args, densityField, displacementField); mean_field::physics::solve_gravity_field(f, args, densityField,
displacementField);
mfem::Vector densityTrue; mfem::Vector densityTrue;
mfem::Vector gravityGradientTrue; mfem::Vector gravityGradientTrue;
@@ -357,18 +368,22 @@ TEST_CASE(
mfem::Vector residual; mfem::Vector residual;
mean_field::operators::kernels::apply_gravity_displacement_force_residual( mean_field::operators::kernels::apply_gravity_displacement_force_residual(
f, *f.domainMapperStateless, densityTrue, gravityGradientTrue, displacementTrue, residual f, *f.domainMapperStateless, densityTrue, gravityGradientTrue,
); displacementTrue, residual);
const mfem::Vector dilationDirection = const mfem::Vector dilationDirection =
gravity_displacement_force_analytic_test_utils::make_dilation_test_direction(f); gravity_displacement_force_analytic_test_utils::
make_dilation_test_direction(f);
const double computedWork = gravity_prepared_test_utils::global_dot(residual, dilationDirection, f.mesh->GetComm()); const double computedWork = gravity_prepared_test_utils::global_dot(
residual, dilationDirection, f.mesh->GetComm());
const double analyticWork = (3.0 / 5.0) * mean_field::utils::G * mass * mass / radius; const double analyticWork =
(3.0 / 5.0) * mean_field::utils::G * mass * mass / radius;
const double relativeError = const double relativeError =
gravity_displacement_force_analytic_test_utils::relative_scalar_error(computedWork, analyticWork); gravity_displacement_force_analytic_test_utils::relative_scalar_error(
computedWork, analyticWork);
INFO("Solved-field positive gravity work = " << computedWork); INFO("Solved-field positive gravity work = " << computedWork);
INFO("Analytic positive gravity work = " << analyticWork); INFO("Analytic positive gravity work = " << analyticWork);

File diff suppressed because it is too large Load Diff

View File

@@ -9,10 +9,8 @@ import mean_field;
import test_helpers; import test_helpers;
namespace hydrostatic_kernel_test_utils { namespace hydrostatic_kernel_test_utils {
mfem::Vector project_scalar( mfem::Vector project_scalar(mfem::ParFiniteElementSpace &finiteElementSpace,
mfem::ParFiniteElementSpace &finiteElementSpace, mfem::Coefficient &coefficient) {
mfem::Coefficient &coefficient
) {
mfem::ParGridFunction field(&finiteElementSpace); mfem::ParGridFunction field(&finiteElementSpace);
field.ProjectCoefficient(coefficient); field.ProjectCoefficient(coefficient);
@@ -23,10 +21,9 @@ namespace hydrostatic_kernel_test_utils {
return trueVector; return trueVector;
} }
mfem::Vector make_constant_field( mfem::Vector
mfem::ParFiniteElementSpace &finiteElementSpace, make_constant_field(mfem::ParFiniteElementSpace &finiteElementSpace,
const double value const double value) {
) {
mfem::ConstantCoefficient coefficient(value); mfem::ConstantCoefficient coefficient(value);
return project_scalar(finiteElementSpace, coefficient); return project_scalar(finiteElementSpace, coefficient);
@@ -34,7 +31,8 @@ namespace hydrostatic_kernel_test_utils {
mfem::Vector make_enthalpy(const mean_field::fem::FEM &f) { mfem::Vector make_enthalpy(const mean_field::fem::FEM &f) {
mfem::FunctionCoefficient coefficient([](const mfem::Vector &position) { mfem::FunctionCoefficient coefficient([](const mfem::Vector &position) {
return 1.10 + 0.035 * position(0) - 0.021 * position(1) + 0.014 * position(2); return 1.10 + 0.035 * position(0) - 0.021 * position(1) +
0.014 * position(2);
}); });
return project_scalar(*f.enthalpyFes, coefficient); return project_scalar(*f.enthalpyFes, coefficient);
@@ -42,7 +40,8 @@ namespace hydrostatic_kernel_test_utils {
mfem::Vector make_potential(const mean_field::fem::FEM &f) { mfem::Vector make_potential(const mean_field::fem::FEM &f) {
mfem::FunctionCoefficient coefficient([](const mfem::Vector &position) { mfem::FunctionCoefficient coefficient([](const mfem::Vector &position) {
return -0.72 + 0.018 * position(0) + 0.011 * position(1) - 0.025 * position(2); return -0.72 + 0.018 * position(0) + 0.011 * position(1) -
0.025 * position(2);
}); });
return project_scalar(*f.gravityPotentialFes, coefficient); return project_scalar(*f.gravityPotentialFes, coefficient);
@@ -74,11 +73,9 @@ namespace hydrostatic_kernel_test_utils {
return mean_field::physics::RigidRotation(angularVelocity, center); return mean_field::physics::RigidRotation(angularVelocity, center);
} }
mfem::Vector centered_difference( mfem::Vector centered_difference(const mfem::Vector &plusResidual,
const mfem::Vector &plusResidual,
const mfem::Vector &minusResidual, const mfem::Vector &minusResidual,
const double epsilon const double epsilon) {
) {
mfem::Vector difference(plusResidual); mfem::Vector difference(plusResidual);
difference -= minusResidual; difference -= minusResidual;
@@ -87,12 +84,10 @@ namespace hydrostatic_kernel_test_utils {
return difference; return difference;
} }
double sum_normalized_error( double sum_normalized_error(const mfem::Vector &computed,
const mfem::Vector &computed,
const mfem::Vector &reference, const mfem::Vector &reference,
const double normalization, const double normalization,
const MPI_Comm communicator const MPI_Comm communicator) {
) {
mfem::Vector difference(computed); mfem::Vector difference(computed);
difference -= reference; difference -= reference;
@@ -105,9 +100,10 @@ namespace hydrostatic_kernel_test_utils {
attributeValues = 0.0; attributeValues = 0.0;
const int vacuumAttribute = f.domainMapperStateless->GetVacuumElementAttribute(); const int vacuumAttribute = field_dof_test_utils::vacuum_material_attribute;
for (int attributeIndex = 0; attributeIndex < f.mesh->attributes.Size(); ++attributeIndex) { for (int attributeIndex = 0; attributeIndex < f.mesh->attributes.Size();
++attributeIndex) {
const int attribute = f.mesh->attributes[attributeIndex]; const int attribute = f.mesh->attributes[attributeIndex];
if (attribute == vacuumAttribute) { if (attribute == vacuumAttribute) {
@@ -124,38 +120,31 @@ namespace hydrostatic_kernel_test_utils {
public: public:
HydrostaticEnthalpyMassOperator( HydrostaticEnthalpyMassOperator(
const mean_field::fem::FEM &f, const mean_field::fem::FEM &f,
const mean_field::mapping::DomainMapperStateless &domainMapper, const mean_field::mapping::DomainMapper &domainMapper,
const mfem::Vector &displacementTrue const mfem::Vector &displacementTrue)
) : mfem::Operator(f.enthalpyFes->GetTrueVSize()), f_(f),
: mfem::Operator(f.enthalpyFes->GetTrueVSize()), domainMapper_(domainMapper), displacementTrue_(displacementTrue) {}
f_(f),
domainMapper_(domainMapper),
displacementTrue_(displacementTrue) {
}
void Mult( void Mult(const mfem::Vector &input, mfem::Vector &output) const override {
const mfem::Vector &input, mean_field::operators::kernels::
mfem::Vector &output apply_hydrostatic_equilibrium_enthalpy_action(
) const override { f_, domainMapper_, input, displacementTrue_, output);
mean_field::operators::kernels::apply_hydrostatic_equilibrium_enthalpy_action(
f_, domainMapper_, input, displacementTrue_, output
);
} }
private: private:
const mean_field::fem::FEM &f_; const mean_field::fem::FEM &f_;
const mean_field::mapping::DomainMapperStateless &domainMapper_; const mean_field::mapping::DomainMapper &domainMapper_;
const mfem::Vector &displacementTrue_; const mfem::Vector &displacementTrue_;
}; };
} // namespace hydrostatic_kernel_test_utils } // namespace hydrostatic_kernel_test_utils
TEST_CASE( TEST_CASE("Rigid Rotation Potential Derivative Matches Centered Differences",
"Rigid Rotation Potential Derivative Matches Centered Differences", tags::barotrope &tags::hydro &tags::jacobian &tags::physics
tags::barotrope &tags::hydro &tags::jacobian &tags::physics &tags::unit &tags::kernels &tags::unit &tags::kernels) {
) { const mean_field::physics::RigidRotation rotation =
const mean_field::physics::RigidRotation rotation = hydrostatic_kernel_test_utils::make_rotation(); hydrostatic_kernel_test_utils::make_rotation();
mfem::Vector position(3); mfem::Vector position(3);
mfem::Vector direction(3); mfem::Vector direction(3);
@@ -177,43 +166,52 @@ TEST_CASE(
minusPosition.Add(-epsilon, direction); minusPosition.Add(-epsilon, direction);
const double centeredDerivative = const double centeredDerivative =
(rotation.potential(plusPosition) - rotation.potential(minusPosition)) / (2.0 * epsilon); (rotation.potential(plusPosition) - rotation.potential(minusPosition)) /
(2.0 * epsilon);
const double analyticDerivative = rotation.potential_directional_derivative(position, direction); const double analyticDerivative =
rotation.potential_directional_derivative(position, direction);
const double relativeError = std::abs(centeredDerivative - analyticDerivative) / const double relativeError =
std::max(std::abs(analyticDerivative), std::numeric_limits<double>::epsilon()); std::abs(centeredDerivative - analyticDerivative) /
std::max(std::abs(analyticDerivative),
std::numeric_limits<double>::epsilon());
INFO("Rigid-rotation derivative error = " << relativeError); INFO("Rigid-rotation derivative error = " << relativeError);
CHECK(relativeError < 2.0e-9); CHECK(relativeError < 2.0e-9);
} }
TEST_CASE( TEST_CASE("Hydrostatic Residual Vanishes For A Manufactured Rotating State",
"Hydrostatic Residual Vanishes For A Manufactured Rotating State", tags::barotrope &tags::hydro &tags::integration &tags::kernels
tags::barotrope &tags::hydro &tags::integration &tags::kernels &tags::physics &tags::residuals &tags::physics &tags::residuals) {
) {
auto args = test_utils::setup_args(); auto args = test_utils::setup_args();
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0); mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
const mean_field::physics::RigidRotation rotation = hydrostatic_kernel_test_utils::make_rotation(); const mean_field::physics::RigidRotation rotation =
hydrostatic_kernel_test_utils::make_rotation();
constexpr double bernoulliConstant = 0.73; constexpr double bernoulliConstant = 0.73;
constexpr double potentialValue = -0.21; constexpr double potentialValue = -0.21;
constexpr double constantOffset = 0.40; constexpr double constantOffset = 0.40;
mfem::FunctionCoefficient enthalpyCoefficient([&rotation](const mfem::Vector &position) { mfem::FunctionCoefficient enthalpyCoefficient(
return bernoulliConstant - potentialValue + rotation.potential(position); [&rotation](const mfem::Vector &position) {
return bernoulliConstant - potentialValue +
rotation.potential(position);
}); });
const mfem::Vector interpolatedEnthalpy = const mfem::Vector interpolatedEnthalpy =
hydrostatic_kernel_test_utils::project_scalar(*f.enthalpyFes, enthalpyCoefficient); hydrostatic_kernel_test_utils::project_scalar(*f.enthalpyFes,
enthalpyCoefficient);
const mfem::Vector potential = const mfem::Vector potential =
hydrostatic_kernel_test_utils::make_constant_field(*f.gravityPotentialFes, potentialValue); hydrostatic_kernel_test_utils::make_constant_field(*f.gravityPotentialFes,
potentialValue);
const mfem::Vector displacement = gravity_prepared_test_utils::make_displacement(f, 0.0); const mfem::Vector displacement =
gravity_prepared_test_utils::make_displacement(f, 0.0);
const MPI_Comm communicator = f.mesh->GetComm(); const MPI_Comm communicator = f.mesh->GetComm();
@@ -228,32 +226,33 @@ TEST_CASE(
mfem::Vector interpolatedReferenceResidual; mfem::Vector interpolatedReferenceResidual;
mean_field::operators::kernels::apply_hydrostatic_equilibrium( mean_field::operators::kernels::apply_hydrostatic_equilibrium(
f, *f.domainMapperStateless, rotation, interpolatedEnthalpy, potential, displacement, bernoulliConstant, f, *f.domainMapperStateless, rotation, interpolatedEnthalpy, potential,
interpolatedResidual displacement, bernoulliConstant, interpolatedResidual);
);
mean_field::operators::kernels::apply_hydrostatic_equilibrium( mean_field::operators::kernels::apply_hydrostatic_equilibrium(
f, *f.domainMapperStateless, rotation, interpolatedEnthalpy, potential, displacement, f, *f.domainMapperStateless, rotation, interpolatedEnthalpy, potential,
bernoulliConstant + constantOffset, interpolatedReferenceResidual displacement, bernoulliConstant + constantOffset,
); interpolatedReferenceResidual);
const double interpolatedResidualNorm = const double interpolatedResidualNorm =
gravity_prepared_test_utils::global_norm(interpolatedResidual, communicator); gravity_prepared_test_utils::global_norm(interpolatedResidual,
communicator);
const double interpolatedReferenceNorm = const double interpolatedReferenceNorm =
gravity_prepared_test_utils::global_norm(interpolatedReferenceResidual, communicator); gravity_prepared_test_utils::global_norm(interpolatedReferenceResidual,
communicator);
REQUIRE(interpolatedReferenceNorm > 1.0e-12); REQUIRE(interpolatedReferenceNorm > 1.0e-12);
const double representationFloor = interpolatedResidualNorm / interpolatedReferenceNorm; const double representationFloor =
interpolatedResidualNorm / interpolatedReferenceNorm;
INFO("Interpolated rotating-state residual norm = " << interpolatedResidualNorm); INFO("Interpolated rotating-state residual norm = "
<< interpolatedResidualNorm);
INFO( INFO("Interpolated rotating-state relative "
"Interpolated rotating-state relative "
"representation floor = " "representation floor = "
<< representationFloor << representationFloor);
);
/* /*
* This remains an independent physical/sign check. A wrong * This remains an independent physical/sign check. A wrong
@@ -276,9 +275,8 @@ TEST_CASE(
* side is in the range of M_h. Starting CG from zero keeps the * side is in the range of M_h. Starting CG from zero keeps the
* iteration in the active stellar subspace. * iteration in the active stellar subspace.
*/ */
hydrostatic_kernel_test_utils::HydrostaticEnthalpyMassOperator enthalpyMassOperator( hydrostatic_kernel_test_utils::HydrostaticEnthalpyMassOperator
f, *f.domainMapperStateless, displacement enthalpyMassOperator(f, *f.domainMapperStateless, displacement);
);
mfem::Vector correctionRightHandSide(interpolatedResidual); mfem::Vector correctionRightHandSide(interpolatedResidual);
@@ -299,11 +297,14 @@ TEST_CASE(
projectionSolver.Mult(correctionRightHandSide, enthalpyCorrection); projectionSolver.Mult(correctionRightHandSide, enthalpyCorrection);
INFO("Discrete-equilibrium projection converged = " << projectionSolver.GetConverged()); INFO("Discrete-equilibrium projection converged = "
<< projectionSolver.GetConverged());
INFO("Discrete-equilibrium projection iterations = " << projectionSolver.GetNumIterations()); INFO("Discrete-equilibrium projection iterations = "
<< projectionSolver.GetNumIterations());
INFO("Discrete-equilibrium projection final norm = " << projectionSolver.GetFinalNorm()); INFO("Discrete-equilibrium projection final norm = "
<< projectionSolver.GetFinalNorm());
REQUIRE(projectionSolver.GetConverged()); REQUIRE(projectionSolver.GetConverged());
@@ -314,15 +315,15 @@ TEST_CASE(
correctionEquationResidual -= correctionRightHandSide; correctionEquationResidual -= correctionRightHandSide;
const double correctionEquationNorm = const double correctionEquationNorm =
gravity_prepared_test_utils::global_norm(correctionEquationResidual, communicator); gravity_prepared_test_utils::global_norm(correctionEquationResidual,
communicator);
INFO( INFO("Discrete-equilibrium correction-equation "
"Discrete-equilibrium correction-equation "
"residual norm = " "residual norm = "
<< correctionEquationNorm << correctionEquationNorm);
);
CHECK(correctionEquationNorm <= std::max(5.0e-12 * interpolatedResidualNorm, 5.0e-15)); CHECK(correctionEquationNorm <=
std::max(5.0e-12 * interpolatedResidualNorm, 5.0e-15));
mfem::Vector discreteEnthalpy(interpolatedEnthalpy); mfem::Vector discreteEnthalpy(interpolatedEnthalpy);
@@ -332,20 +333,21 @@ TEST_CASE(
mfem::Vector referenceResidual; mfem::Vector referenceResidual;
mean_field::operators::kernels::apply_hydrostatic_equilibrium( mean_field::operators::kernels::apply_hydrostatic_equilibrium(
f, *f.domainMapperStateless, rotation, discreteEnthalpy, potential, displacement, bernoulliConstant, f, *f.domainMapperStateless, rotation, discreteEnthalpy, potential,
exactResidual displacement, bernoulliConstant, exactResidual);
);
mean_field::operators::kernels::apply_hydrostatic_equilibrium( mean_field::operators::kernels::apply_hydrostatic_equilibrium(
f, *f.domainMapperStateless, rotation, discreteEnthalpy, potential, displacement, f, *f.domainMapperStateless, rotation, discreteEnthalpy, potential,
bernoulliConstant + constantOffset, referenceResidual displacement, bernoulliConstant + constantOffset, referenceResidual);
);
const double exactNorm = gravity_prepared_test_utils::global_norm(exactResidual, communicator); const double exactNorm =
gravity_prepared_test_utils::global_norm(exactResidual, communicator);
const double referenceNorm = gravity_prepared_test_utils::global_norm(referenceResidual, communicator); const double referenceNorm =
gravity_prepared_test_utils::global_norm(referenceResidual, communicator);
const double correctionNorm = gravity_prepared_test_utils::global_norm(enthalpyCorrection, communicator); const double correctionNorm = gravity_prepared_test_utils::global_norm(
enthalpyCorrection, communicator);
INFO("Enthalpy representation correction norm = " << correctionNorm); INFO("Enthalpy representation correction norm = " << correctionNorm);
@@ -360,31 +362,37 @@ TEST_CASE(
TEST_CASE( TEST_CASE(
"Exact Constant Hydrostatic Equilibrium Remains Zero Under Deformation", "Exact Constant Hydrostatic Equilibrium Remains Zero Under Deformation",
tags::barotrope &tags::hydro &tags::integration &tags::jacobian &tags::kernels &tags::mapping &tags::physics tags::barotrope &tags::hydro &tags::integration &tags::jacobian
) { &tags::kernels &tags::mapping &tags::physics) {
auto args = test_utils::setup_args(); auto args = test_utils::setup_args();
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0); mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
const mean_field::physics::RigidRotation rotation = hydrostatic_kernel_test_utils::make_zero_rotation(); const mean_field::physics::RigidRotation rotation =
hydrostatic_kernel_test_utils::make_zero_rotation();
constexpr double enthalpyValue = 1.20; constexpr double enthalpyValue = 1.20;
constexpr double potentialValue = -0.35; constexpr double potentialValue = -0.35;
constexpr double bernoulliConstant = enthalpyValue + potentialValue; constexpr double bernoulliConstant = enthalpyValue + potentialValue;
const mfem::Vector enthalpy = hydrostatic_kernel_test_utils::make_constant_field(*f.enthalpyFes, enthalpyValue); const mfem::Vector enthalpy =
hydrostatic_kernel_test_utils::make_constant_field(*f.enthalpyFes,
enthalpyValue);
const mfem::Vector potential = const mfem::Vector potential =
hydrostatic_kernel_test_utils::make_constant_field(*f.gravityPotentialFes, potentialValue); hydrostatic_kernel_test_utils::make_constant_field(*f.gravityPotentialFes,
potentialValue);
const mfem::Vector displacementVariation = gravity_prepared_test_utils::make_displacement(f, 0.67); const mfem::Vector displacementVariation =
gravity_prepared_test_utils::make_displacement(f, 0.67);
const MPI_Comm communicator = f.mesh->GetComm(); const MPI_Comm communicator = f.mesh->GetComm();
for (const double deformationScale : {0.0, 0.5, 1.0}) { for (const double deformationScale : {0.0, 0.5, 1.0}) {
DYNAMIC_SECTION("Deformation scale = " << deformationScale) { DYNAMIC_SECTION("Deformation scale = " << deformationScale) {
const mfem::Vector displacement = gravity_prepared_test_utils::make_displacement(f, deformationScale); const mfem::Vector displacement =
gravity_prepared_test_utils::make_displacement(f, deformationScale);
mfem::Vector exactResidual; mfem::Vector exactResidual;
mfem::Vector referenceResidual; mfem::Vector referenceResidual;
@@ -392,35 +400,38 @@ TEST_CASE(
mfem::Vector referenceGeometryAction; mfem::Vector referenceGeometryAction;
mean_field::operators::kernels::apply_hydrostatic_equilibrium( mean_field::operators::kernels::apply_hydrostatic_equilibrium(
f, *f.domainMapperStateless, rotation, enthalpy, potential, displacement, bernoulliConstant, f, *f.domainMapperStateless, rotation, enthalpy, potential,
exactResidual displacement, bernoulliConstant, exactResidual);
);
mean_field::operators::kernels::apply_hydrostatic_equilibrium( mean_field::operators::kernels::apply_hydrostatic_equilibrium(
f, *f.domainMapperStateless, rotation, enthalpy, potential, displacement, bernoulliConstant + 0.50, f, *f.domainMapperStateless, rotation, enthalpy, potential,
referenceResidual displacement, bernoulliConstant + 0.50, referenceResidual);
);
mean_field::operators::kernels::apply_hydrostatic_equilibrium_displacement_action( mean_field::operators::kernels::
f, *f.domainMapperStateless, rotation, enthalpy, potential, displacement, bernoulliConstant, apply_hydrostatic_equilibrium_displacement_action(
displacementVariation, exactGeometryAction f, *f.domainMapperStateless, rotation, enthalpy, potential,
); displacement, bernoulliConstant, displacementVariation,
exactGeometryAction);
mean_field::operators::kernels::apply_hydrostatic_equilibrium_displacement_action( mean_field::operators::kernels::
f, *f.domainMapperStateless, rotation, enthalpy, potential, displacement, bernoulliConstant + 0.50, apply_hydrostatic_equilibrium_displacement_action(
displacementVariation, referenceGeometryAction f, *f.domainMapperStateless, rotation, enthalpy, potential,
); displacement, bernoulliConstant + 0.50, displacementVariation,
referenceGeometryAction);
const double exactResidualNorm = gravity_prepared_test_utils::global_norm(exactResidual, communicator); const double exactResidualNorm =
gravity_prepared_test_utils::global_norm(exactResidual, communicator);
const double referenceResidualNorm = const double referenceResidualNorm =
gravity_prepared_test_utils::global_norm(referenceResidual, communicator); gravity_prepared_test_utils::global_norm(referenceResidual,
communicator);
const double exactGeometryNorm = const double exactGeometryNorm = gravity_prepared_test_utils::global_norm(
gravity_prepared_test_utils::global_norm(exactGeometryAction, communicator); exactGeometryAction, communicator);
const double referenceGeometryNorm = const double referenceGeometryNorm =
gravity_prepared_test_utils::global_norm(referenceGeometryAction, communicator); gravity_prepared_test_utils::global_norm(referenceGeometryAction,
communicator);
REQUIRE(referenceResidualNorm > 1.0e-12); REQUIRE(referenceResidualNorm > 1.0e-12);
@@ -433,51 +444,59 @@ TEST_CASE(
} }
} }
TEST_CASE( TEST_CASE("Hydrostatic Equilibrium Excludes Vacuum Elements",
"Hydrostatic Equilibrium Excludes Vacuum Elements", tags::barotrope &tags::hydro &tags::kernels &tags::mapping
tags::barotrope &tags::hydro &tags::kernels &tags::mapping &tags::physics &tags::unit &tags::physics &tags::unit) {
) {
auto args = test_utils::setup_args(); auto args = test_utils::setup_args();
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0); mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
const mean_field::physics::RigidRotation rotation = hydrostatic_kernel_test_utils::make_zero_rotation(); const mean_field::physics::RigidRotation rotation =
hydrostatic_kernel_test_utils::make_zero_rotation();
const mfem::Vector zeroEnthalpy(f.enthalpyFes->GetTrueVSize()); const mfem::Vector zeroEnthalpy(f.enthalpyFes->GetTrueVSize());
mfem::Vector enthalpy(zeroEnthalpy); mfem::Vector enthalpy(zeroEnthalpy);
enthalpy = 0.0; enthalpy = 0.0;
const mfem::Vector vacuumPotential = hydrostatic_kernel_test_utils::make_vacuum_supported_potential(f); const mfem::Vector vacuumPotential =
hydrostatic_kernel_test_utils::make_vacuum_supported_potential(f);
const mfem::Vector stellarPotential = const mfem::Vector stellarPotential =
hydrostatic_kernel_test_utils::make_constant_field(*f.gravityPotentialFes, 1.0); hydrostatic_kernel_test_utils::make_constant_field(*f.gravityPotentialFes,
1.0);
const mfem::Vector displacement = gravity_prepared_test_utils::make_displacement(f, 1.0); const mfem::Vector displacement =
gravity_prepared_test_utils::make_displacement(f, 1.0);
mfem::Vector residual; mfem::Vector residual;
mfem::Vector vacuumAction; mfem::Vector vacuumAction;
mfem::Vector stellarAction; mfem::Vector stellarAction;
mean_field::operators::kernels::apply_hydrostatic_equilibrium( mean_field::operators::kernels::apply_hydrostatic_equilibrium(
f, *f.domainMapperStateless, rotation, enthalpy, vacuumPotential, displacement, 0.0, residual f, *f.domainMapperStateless, rotation, enthalpy, vacuumPotential,
); displacement, 0.0, residual);
mean_field::operators::kernels::apply_hydrostatic_equilibrium_potential_action( mean_field::operators::kernels::
f, *f.domainMapperStateless, vacuumPotential, displacement, vacuumAction apply_hydrostatic_equilibrium_potential_action(
); f, *f.domainMapperStateless, vacuumPotential, displacement,
vacuumAction);
mean_field::operators::kernels::apply_hydrostatic_equilibrium_potential_action( mean_field::operators::kernels::
f, *f.domainMapperStateless, stellarPotential, displacement, stellarAction apply_hydrostatic_equilibrium_potential_action(
); f, *f.domainMapperStateless, stellarPotential, displacement,
stellarAction);
const MPI_Comm communicator = f.mesh->GetComm(); const MPI_Comm communicator = f.mesh->GetComm();
const double residualNorm = gravity_prepared_test_utils::global_norm(residual, communicator); const double residualNorm =
gravity_prepared_test_utils::global_norm(residual, communicator);
const double vacuumActionNorm = gravity_prepared_test_utils::global_norm(vacuumAction, communicator); const double vacuumActionNorm =
gravity_prepared_test_utils::global_norm(vacuumAction, communicator);
const double stellarActionNorm = gravity_prepared_test_utils::global_norm(stellarAction, communicator); const double stellarActionNorm =
gravity_prepared_test_utils::global_norm(stellarAction, communicator);
REQUIRE(stellarActionNorm > 1.0e-12); REQUIRE(stellarActionNorm > 1.0e-12);
@@ -486,30 +505,35 @@ TEST_CASE(
CHECK(vacuumActionNorm <= 5.0e-13 * stellarActionNorm); CHECK(vacuumActionNorm <= 5.0e-13 * stellarActionNorm);
} }
TEST_CASE( TEST_CASE("Hydrostatic Jacobian Matches Blocks And Centered Differences",
"Hydrostatic Jacobian Matches Blocks And Centered Differences", tags::barotrope &tags::hydro &tags::integration &tags::jacobian
tags::barotrope &tags::hydro &tags::integration &tags::jacobian &tags::kernels &tags::mapping &tags::physics &tags::kernels &tags::mapping &tags::physics) {
) {
auto args = test_utils::setup_args(); auto args = test_utils::setup_args();
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0); mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
const mean_field::physics::RigidRotation rotation = hydrostatic_kernel_test_utils::make_rotation(); const mean_field::physics::RigidRotation rotation =
hydrostatic_kernel_test_utils::make_rotation();
const mfem::Vector enthalpy = hydrostatic_kernel_test_utils::make_enthalpy(f); const mfem::Vector enthalpy = hydrostatic_kernel_test_utils::make_enthalpy(f);
const mfem::Vector potential = hydrostatic_kernel_test_utils::make_potential(f); const mfem::Vector potential =
hydrostatic_kernel_test_utils::make_potential(f);
const mfem::Vector displacement = gravity_prepared_test_utils::make_displacement(f, 1.0); const mfem::Vector displacement =
gravity_prepared_test_utils::make_displacement(f, 1.0);
const mfem::Vector enthalpyVariation = const mfem::Vector enthalpyVariation =
gravity_prepared_test_utils::make_deterministic_vector(f.enthalpyFes->GetTrueVSize(), 0.23); gravity_prepared_test_utils::make_deterministic_vector(
f.enthalpyFes->GetTrueVSize(), 0.23);
const mfem::Vector potentialVariation = const mfem::Vector potentialVariation =
gravity_prepared_test_utils::make_deterministic_vector(f.gravityPotentialFes->GetTrueVSize(), 0.47); gravity_prepared_test_utils::make_deterministic_vector(
f.gravityPotentialFes->GetTrueVSize(), 0.47);
const mfem::Vector displacementVariation = const mfem::Vector displacementVariation =
gravity_prepared_test_utils::make_deterministic_vector(f.displacementFes->GetTrueVSize(), 0.71); gravity_prepared_test_utils::make_deterministic_vector(
f.displacementFes->GetTrueVSize(), 0.71);
constexpr double bernoulliConstant = 0.41; constexpr double bernoulliConstant = 0.41;
constexpr double constantVariation = -0.37; constexpr double constantVariation = -0.37;
@@ -522,26 +546,28 @@ TEST_CASE(
mfem::Vector completeAction; mfem::Vector completeAction;
mean_field::operators::kernels::apply_hydrostatic_equilibrium_enthalpy_action( mean_field::operators::kernels::apply_hydrostatic_equilibrium_enthalpy_action(
f, *f.domainMapperStateless, enthalpyVariation, displacement, enthalpyAction f, *f.domainMapperStateless, enthalpyVariation, displacement,
); enthalpyAction);
mean_field::operators::kernels::apply_hydrostatic_equilibrium_potential_action( mean_field::operators::kernels::
f, *f.domainMapperStateless, potentialVariation, displacement, potentialAction apply_hydrostatic_equilibrium_potential_action(
); f, *f.domainMapperStateless, potentialVariation, displacement,
potentialAction);
mean_field::operators::kernels::apply_hydrostatic_equilibrium_constant_action( mean_field::operators::kernels::apply_hydrostatic_equilibrium_constant_action(
f, *f.domainMapperStateless, constantVariation, displacement, constantAction f, *f.domainMapperStateless, constantVariation, displacement,
); constantAction);
mean_field::operators::kernels::apply_hydrostatic_equilibrium_displacement_action( mean_field::operators::kernels::
f, *f.domainMapperStateless, rotation, enthalpy, potential, displacement, bernoulliConstant, apply_hydrostatic_equilibrium_displacement_action(
displacementVariation, displacementAction f, *f.domainMapperStateless, rotation, enthalpy, potential,
); displacement, bernoulliConstant, displacementVariation,
displacementAction);
mean_field::operators::kernels::apply_hydrostatic_equilibrium_action( mean_field::operators::kernels::apply_hydrostatic_equilibrium_action(
f, *f.domainMapperStateless, rotation, enthalpy, potential, displacement, bernoulliConstant, enthalpyVariation, f, *f.domainMapperStateless, rotation, enthalpy, potential, displacement,
potentialVariation, constantVariation, displacementVariation, completeAction bernoulliConstant, enthalpyVariation, potentialVariation,
); constantVariation, displacementVariation, completeAction);
mfem::Vector blockAction(enthalpyAction); mfem::Vector blockAction(enthalpyAction);
blockAction += potentialAction; blockAction += potentialAction;
@@ -550,22 +576,23 @@ TEST_CASE(
const MPI_Comm communicator = f.mesh->GetComm(); const MPI_Comm communicator = f.mesh->GetComm();
const double blockError = gravity_prepared_test_utils::relative_error(completeAction, blockAction, communicator); const double blockError = gravity_prepared_test_utils::relative_error(
completeAction, blockAction, communicator);
INFO("Hydrostatic block reconstruction error = " << blockError); INFO("Hydrostatic block reconstruction error = " << blockError);
CHECK(blockError < 5.0e-13); CHECK(blockError < 5.0e-13);
auto evaluate_residual = [&f, &rotation]( auto evaluate_residual = [&f,
const mfem::Vector &trialEnthalpy, const mfem::Vector &trialPotential, &rotation](const mfem::Vector &trialEnthalpy,
const mfem::Vector &trialDisplacement, const double trialConstant const mfem::Vector &trialPotential,
) { const mfem::Vector &trialDisplacement,
const double trialConstant) {
mfem::Vector residual; mfem::Vector residual;
mean_field::operators::kernels::apply_hydrostatic_equilibrium( mean_field::operators::kernels::apply_hydrostatic_equilibrium(
f, *f.domainMapperStateless, rotation, trialEnthalpy, trialPotential, trialDisplacement, trialConstant, f, *f.domainMapperStateless, rotation, trialEnthalpy, trialPotential,
residual trialDisplacement, trialConstant, residual);
);
return residual; return residual;
}; };
@@ -577,10 +604,13 @@ TEST_CASE(
minusEnthalpy.Add(-epsilon, enthalpyVariation); minusEnthalpy.Add(-epsilon, enthalpyVariation);
const mfem::Vector enthalpyDifference = hydrostatic_kernel_test_utils::centered_difference( const mfem::Vector enthalpyDifference =
evaluate_residual(plusEnthalpy, potential, displacement, bernoulliConstant), hydrostatic_kernel_test_utils::centered_difference(
evaluate_residual(minusEnthalpy, potential, displacement, bernoulliConstant), epsilon evaluate_residual(plusEnthalpy, potential, displacement,
); bernoulliConstant),
evaluate_residual(minusEnthalpy, potential, displacement,
bernoulliConstant),
epsilon);
mfem::Vector plusPotential(potential); mfem::Vector plusPotential(potential);
mfem::Vector minusPotential(potential); mfem::Vector minusPotential(potential);
@@ -589,15 +619,21 @@ TEST_CASE(
minusPotential.Add(-epsilon, potentialVariation); minusPotential.Add(-epsilon, potentialVariation);
const mfem::Vector potentialDifference = hydrostatic_kernel_test_utils::centered_difference( const mfem::Vector potentialDifference =
evaluate_residual(enthalpy, plusPotential, displacement, bernoulliConstant), hydrostatic_kernel_test_utils::centered_difference(
evaluate_residual(enthalpy, minusPotential, displacement, bernoulliConstant), epsilon evaluate_residual(enthalpy, plusPotential, displacement,
); bernoulliConstant),
evaluate_residual(enthalpy, minusPotential, displacement,
bernoulliConstant),
epsilon);
const mfem::Vector constantDifference = hydrostatic_kernel_test_utils::centered_difference( const mfem::Vector constantDifference =
evaluate_residual(enthalpy, potential, displacement, bernoulliConstant + epsilon * constantVariation), hydrostatic_kernel_test_utils::centered_difference(
evaluate_residual(enthalpy, potential, displacement, bernoulliConstant - epsilon * constantVariation), epsilon evaluate_residual(enthalpy, potential, displacement,
); bernoulliConstant + epsilon * constantVariation),
evaluate_residual(enthalpy, potential, displacement,
bernoulliConstant - epsilon * constantVariation),
epsilon);
mfem::Vector plusDisplacement(displacement); mfem::Vector plusDisplacement(displacement);
mfem::Vector minusDisplacement(displacement); mfem::Vector minusDisplacement(displacement);
@@ -606,22 +642,25 @@ TEST_CASE(
minusDisplacement.Add(-epsilon, displacementVariation); minusDisplacement.Add(-epsilon, displacementVariation);
const mfem::Vector displacementDifference = hydrostatic_kernel_test_utils::centered_difference( const mfem::Vector displacementDifference =
evaluate_residual(enthalpy, potential, plusDisplacement, bernoulliConstant), hydrostatic_kernel_test_utils::centered_difference(
evaluate_residual(enthalpy, potential, minusDisplacement, bernoulliConstant), epsilon evaluate_residual(enthalpy, potential, plusDisplacement,
); bernoulliConstant),
evaluate_residual(enthalpy, potential, minusDisplacement,
bernoulliConstant),
epsilon);
const double enthalpyError = const double enthalpyError = gravity_prepared_test_utils::relative_error(
gravity_prepared_test_utils::relative_error(enthalpyAction, enthalpyDifference, communicator); enthalpyAction, enthalpyDifference, communicator);
const double potentialError = const double potentialError = gravity_prepared_test_utils::relative_error(
gravity_prepared_test_utils::relative_error(potentialAction, potentialDifference, communicator); potentialAction, potentialDifference, communicator);
const double constantError = const double constantError = gravity_prepared_test_utils::relative_error(
gravity_prepared_test_utils::relative_error(constantAction, constantDifference, communicator); constantAction, constantDifference, communicator);
const double displacementError = const double displacementError = gravity_prepared_test_utils::relative_error(
gravity_prepared_test_utils::relative_error(displacementAction, displacementDifference, communicator); displacementAction, displacementDifference, communicator);
INFO("Hydrostatic enthalpy-block error = " << enthalpyError); INFO("Hydrostatic enthalpy-block error = " << enthalpyError);
@@ -655,86 +694,91 @@ TEST_CASE(
combinedMinusDisplacement.Add(-epsilon, displacementVariation); combinedMinusDisplacement.Add(-epsilon, displacementVariation);
const mfem::Vector combinedDifference = hydrostatic_kernel_test_utils::centered_difference( const mfem::Vector combinedDifference =
evaluate_residual( hydrostatic_kernel_test_utils::centered_difference(
combinedPlusEnthalpy, combinedPlusPotential, combinedPlusDisplacement, evaluate_residual(combinedPlusEnthalpy, combinedPlusPotential,
bernoulliConstant + epsilon * constantVariation combinedPlusDisplacement,
), bernoulliConstant + epsilon * constantVariation),
evaluate_residual( evaluate_residual(combinedMinusEnthalpy, combinedMinusPotential,
combinedMinusEnthalpy, combinedMinusPotential, combinedMinusDisplacement, combinedMinusDisplacement,
bernoulliConstant - epsilon * constantVariation bernoulliConstant - epsilon * constantVariation),
), epsilon);
epsilon
);
const double blockNormSum = gravity_prepared_test_utils::global_norm(enthalpyAction, communicator) + const double blockNormSum =
gravity_prepared_test_utils::global_norm(enthalpyAction, communicator) +
gravity_prepared_test_utils::global_norm(potentialAction, communicator) + gravity_prepared_test_utils::global_norm(potentialAction, communicator) +
gravity_prepared_test_utils::global_norm(constantAction, communicator) + gravity_prepared_test_utils::global_norm(constantAction, communicator) +
gravity_prepared_test_utils::global_norm(displacementAction, communicator); gravity_prepared_test_utils::global_norm(displacementAction,
communicator);
const double simultaneousError = hydrostatic_kernel_test_utils::sum_normalized_error( const double simultaneousError =
completeAction, combinedDifference, blockNormSum, communicator hydrostatic_kernel_test_utils::sum_normalized_error(
); completeAction, combinedDifference, blockNormSum, communicator);
INFO("Hydrostatic simultaneous Jacobian error = " << simultaneousError); INFO("Hydrostatic simultaneous Jacobian error = " << simultaneousError);
CHECK(simultaneousError < 2.0e-7); CHECK(simultaneousError < 2.0e-7);
} }
TEST_CASE( TEST_CASE("Hydrostatic Displacement Action Is Linear In Its Direction",
"Hydrostatic Displacement Action Is Linear In Its Direction", tags::barotrope &tags::hydro &tags::integration &tags::jacobian
tags::barotrope &tags::hydro &tags::integration &tags::jacobian &tags::mapping &tags::physics &tags::unit &tags::mapping &tags::physics &tags::unit &tags::kernels) {
&tags::kernels
) {
auto args = test_utils::setup_args(); auto args = test_utils::setup_args();
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0); mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
const mean_field::physics::RigidRotation rotation = hydrostatic_kernel_test_utils::make_rotation(); const mean_field::physics::RigidRotation rotation =
hydrostatic_kernel_test_utils::make_rotation();
const mfem::Vector enthalpy = hydrostatic_kernel_test_utils::make_enthalpy(f); const mfem::Vector enthalpy = hydrostatic_kernel_test_utils::make_enthalpy(f);
const mfem::Vector potential = hydrostatic_kernel_test_utils::make_potential(f); const mfem::Vector potential =
hydrostatic_kernel_test_utils::make_potential(f);
const mfem::Vector displacement = gravity_prepared_test_utils::make_displacement(f, 1.0); const mfem::Vector displacement =
gravity_prepared_test_utils::make_displacement(f, 1.0);
const mfem::Vector firstDirection = const mfem::Vector firstDirection =
gravity_prepared_test_utils::make_deterministic_vector(f.displacementFes->GetTrueVSize(), 0.31); gravity_prepared_test_utils::make_deterministic_vector(
f.displacementFes->GetTrueVSize(), 0.31);
const mfem::Vector secondDirection = const mfem::Vector secondDirection =
gravity_prepared_test_utils::make_deterministic_vector(f.displacementFes->GetTrueVSize(), 0.83); gravity_prepared_test_utils::make_deterministic_vector(
f.displacementFes->GetTrueVSize(), 0.83);
constexpr double firstScale = 0.43; constexpr double firstScale = 0.43;
constexpr double secondScale = -0.29; constexpr double secondScale = -0.29;
constexpr double bernoulliConstant = 0.41; constexpr double bernoulliConstant = 0.41;
const mfem::Vector combinedDirection = const mfem::Vector combinedDirection =
gravity_prepared_test_utils::linear_combination(firstDirection, firstScale, secondDirection, secondScale); gravity_prepared_test_utils::linear_combination(
firstDirection, firstScale, secondDirection, secondScale);
mfem::Vector firstAction; mfem::Vector firstAction;
mfem::Vector secondAction; mfem::Vector secondAction;
mfem::Vector combinedAction; mfem::Vector combinedAction;
mean_field::operators::kernels::apply_hydrostatic_equilibrium_displacement_action( mean_field::operators::kernels::
f, *f.domainMapperStateless, rotation, enthalpy, potential, displacement, bernoulliConstant, firstDirection, apply_hydrostatic_equilibrium_displacement_action(
firstAction f, *f.domainMapperStateless, rotation, enthalpy, potential,
); displacement, bernoulliConstant, firstDirection, firstAction);
mean_field::operators::kernels::apply_hydrostatic_equilibrium_displacement_action( mean_field::operators::kernels::
f, *f.domainMapperStateless, rotation, enthalpy, potential, displacement, bernoulliConstant, secondDirection, apply_hydrostatic_equilibrium_displacement_action(
secondAction f, *f.domainMapperStateless, rotation, enthalpy, potential,
); displacement, bernoulliConstant, secondDirection, secondAction);
mean_field::operators::kernels::apply_hydrostatic_equilibrium_displacement_action( mean_field::operators::kernels::
f, *f.domainMapperStateless, rotation, enthalpy, potential, displacement, bernoulliConstant, combinedDirection, apply_hydrostatic_equilibrium_displacement_action(
combinedAction f, *f.domainMapperStateless, rotation, enthalpy, potential,
); displacement, bernoulliConstant, combinedDirection, combinedAction);
const mfem::Vector expectedAction = const mfem::Vector expectedAction =
gravity_prepared_test_utils::linear_combination(firstAction, firstScale, secondAction, secondScale); gravity_prepared_test_utils::linear_combination(
firstAction, firstScale, secondAction, secondScale);
const double linearityError = const double linearityError = gravity_prepared_test_utils::relative_error(
gravity_prepared_test_utils::relative_error(combinedAction, expectedAction, f.mesh->GetComm()); combinedAction, expectedAction, f.mesh->GetComm());
INFO("Hydrostatic displacement-linearity error = " << linearityError); INFO("Hydrostatic displacement-linearity error = " << linearityError);
@@ -743,8 +787,8 @@ TEST_CASE(
TEST_CASE( TEST_CASE(
"Hydrostatic Residual Is Translationally Invariant On Deformed Geometry", "Hydrostatic Residual Is Translationally Invariant On Deformed Geometry",
tags::barotrope &tags::hydro &tags::integration &tags::kernels &tags::mapping &tags::physics &tags::residuals tags::barotrope &tags::hydro &tags::integration &tags::kernels
) { &tags::mapping &tags::physics &tags::residuals) {
auto args = test_utils::setup_args(); auto args = test_utils::setup_args();
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0); mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
@@ -770,29 +814,33 @@ TEST_CASE(
mfem::Vector translatedCenter(center); mfem::Vector translatedCenter(center);
translatedCenter += translation; translatedCenter += translation;
const mean_field::physics::RigidRotation baseRotation(angularVelocity, center); const mean_field::physics::RigidRotation baseRotation(angularVelocity,
center);
const mean_field::physics::RigidRotation translatedRotation(angularVelocity, translatedCenter); const mean_field::physics::RigidRotation translatedRotation(angularVelocity,
translatedCenter);
const mfem::Vector enthalpy = hydrostatic_kernel_test_utils::make_enthalpy(f); const mfem::Vector enthalpy = hydrostatic_kernel_test_utils::make_enthalpy(f);
const mfem::Vector potential = hydrostatic_kernel_test_utils::make_potential(f); const mfem::Vector potential =
hydrostatic_kernel_test_utils::make_potential(f);
/* /*
* Use a nontrivially deformed base state so this checks rotation * Use a nontrivially deformed base state so this checks rotation
* and mapped geometry simultaneously. The comparison state adds * and mapped geometry simultaneously. The comparison state adds
* an exactly representable rigid translation to that deformation. * an exactly representable rigid translation to that deformation.
*/ */
const mfem::Vector baseDisplacement = gravity_prepared_test_utils::make_displacement(f, 0.73); const mfem::Vector baseDisplacement =
gravity_prepared_test_utils::make_displacement(f, 0.73);
mfem::ParGridFunction translationField(f.displacementFes.get()); mfem::ParGridFunction translationField(f.displacementFes.get());
mfem::VectorFunctionCoefficient translationCoefficient( mfem::VectorFunctionCoefficient translationCoefficient(
f.mesh->Dimension(), [&translation](const mfem::Vector &, mfem::Vector &value) { f.mesh->Dimension(),
[&translation](const mfem::Vector &, mfem::Vector &value) {
value.SetSize(translation.Size()); value.SetSize(translation.Size());
value = translation; value = translation;
} });
);
translationField.ProjectCoefficient(translationCoefficient); translationField.ProjectCoefficient(translationCoefficient);
@@ -810,43 +858,47 @@ TEST_CASE(
mfem::Vector untranslatedCenterResidual; mfem::Vector untranslatedCenterResidual;
mean_field::operators::kernels::apply_hydrostatic_equilibrium( mean_field::operators::kernels::apply_hydrostatic_equilibrium(
f, *f.domainMapperStateless, baseRotation, enthalpy, potential, baseDisplacement, bernoulliConstant, f, *f.domainMapperStateless, baseRotation, enthalpy, potential,
baseResidual baseDisplacement, bernoulliConstant, baseResidual);
);
mean_field::operators::kernels::apply_hydrostatic_equilibrium( mean_field::operators::kernels::apply_hydrostatic_equilibrium(
f, *f.domainMapperStateless, translatedRotation, enthalpy, potential, translatedDisplacement, bernoulliConstant, f, *f.domainMapperStateless, translatedRotation, enthalpy, potential,
translatedResidual translatedDisplacement, bernoulliConstant, translatedResidual);
);
/* /*
* Negative control: translate the geometry but leave the rotation * Negative control: translate the geometry but leave the rotation
* center fixed. This must not agree with the covariant result. * center fixed. This must not agree with the covariant result.
*/ */
mean_field::operators::kernels::apply_hydrostatic_equilibrium( mean_field::operators::kernels::apply_hydrostatic_equilibrium(
f, *f.domainMapperStateless, baseRotation, enthalpy, potential, translatedDisplacement, bernoulliConstant, f, *f.domainMapperStateless, baseRotation, enthalpy, potential,
untranslatedCenterResidual translatedDisplacement, bernoulliConstant, untranslatedCenterResidual);
);
const MPI_Comm communicator = f.mesh->GetComm(); const MPI_Comm communicator = f.mesh->GetComm();
const double baseResidualNorm = gravity_prepared_test_utils::global_norm(baseResidual, communicator); const double baseResidualNorm =
gravity_prepared_test_utils::global_norm(baseResidual, communicator);
const double translatedResidualNorm = gravity_prepared_test_utils::global_norm(translatedResidual, communicator); const double translatedResidualNorm =
gravity_prepared_test_utils::global_norm(translatedResidual,
communicator);
const double translationInvarianceError = const double translationInvarianceError =
gravity_prepared_test_utils::relative_error(translatedResidual, baseResidual, communicator); gravity_prepared_test_utils::relative_error(translatedResidual,
baseResidual, communicator);
const double fixedCenterDifference = const double fixedCenterDifference =
gravity_prepared_test_utils::relative_error(untranslatedCenterResidual, translatedResidual, communicator); gravity_prepared_test_utils::relative_error(
untranslatedCenterResidual, translatedResidual, communicator);
INFO("Base deformed hydrostatic residual norm = " << baseResidualNorm); INFO("Base deformed hydrostatic residual norm = " << baseResidualNorm);
INFO("Translated hydrostatic residual norm = " << translatedResidualNorm); INFO("Translated hydrostatic residual norm = " << translatedResidualNorm);
INFO("Mapped-rotation translation invariance error = " << translationInvarianceError); INFO("Mapped-rotation translation invariance error = "
<< translationInvarianceError);
INFO("Relative change with untranslated rotation center = " << fixedCenterDifference); INFO("Relative change with untranslated rotation center = "
<< fixedCenterDifference);
REQUIRE(baseResidualNorm > 1.0e-12); REQUIRE(baseResidualNorm > 1.0e-12);
REQUIRE(translatedResidualNorm > 1.0e-12); REQUIRE(translatedResidualNorm > 1.0e-12);

View File

@@ -10,52 +10,48 @@ import mean_field;
import test_helpers; import test_helpers;
namespace pressure_force_kernel_test_utils { namespace pressure_force_kernel_test_utils {
[[nodiscard]] mfem::Vector make_deterministic_vector( [[nodiscard]] mfem::Vector make_deterministic_vector(const int size,
const int size, const double phase) {
const double phase
) {
mfem::Vector vector(size); mfem::Vector vector(size);
for (int index = 0; index < size; ++index) { for (int index = 0; index < size; ++index) {
const double position = static_cast<double>(index + 1); const double position = static_cast<double>(index + 1);
vector(index) = vector(index) = 0.71 + 0.19 * std::sin(0.31 * position + phase) +
0.71 + 0.19 * std::sin(0.31 * position + phase) + 0.08 * std::cos(0.17 * position - 0.5 * phase); 0.08 * std::cos(0.17 * position - 0.5 * phase);
} }
return vector; return vector;
} }
[[nodiscard]] mfem::Vector make_zero_displacement(const mean_field::fem::FEM &f) { [[nodiscard]] mfem::Vector
make_zero_displacement(const mean_field::fem::FEM &f) {
mfem::Vector displacementTrue(f.displacementFes->GetTrueVSize()); mfem::Vector displacementTrue(f.displacementFes->GetTrueVSize());
displacementTrue = 0.0; displacementTrue = 0.0;
return displacementTrue; return displacementTrue;
} }
[[nodiscard]] mfem::Vector make_vacuum_only_enthalpy(const mean_field::fem::FEM &f) { [[nodiscard]] mfem::Vector
mfem::Vector enthalpyTrue = make_deterministic_vector(f.enthalpyFes->GetTrueVSize(), 0.43); make_vacuum_only_enthalpy(const mean_field::fem::FEM &f) {
mfem::Vector enthalpyTrue =
make_deterministic_vector(f.enthalpyFes->GetTrueVSize(), 0.43);
mfem::Array<int> stellarElementMask; using DomainSchema =
mean_field::utils::populate_element_mask(f.mesh.get(), mean_field::utils::DOMAINS::STELLAR, stellarElementMask); mean_field::utils::domain::CoreEnvelopeVacuumDomainSchema;
const mean_field::field::FieldDofMap enthalpyMap =
mean_field::field::make_field_dof_map<mean_field::field::Enthalpy,
DomainSchema>(*f.enthalpyFes);
mfem::Array<int> stellarEnthalpyTrueDofs; for (int reducedDof = 0; reducedDof < enthalpyMap.reduced_size();
mean_field::utils::populate_domain_tdofs(f.enthalpyFes.get(), stellarElementMask, stellarEnthalpyTrueDofs); ++reducedDof) {
enthalpyTrue(enthalpyMap.true_dof(reducedDof)) = 0.0;
for (int listIndex = 0; listIndex < stellarEnthalpyTrueDofs.Size(); ++listIndex) {
const int trueDof = stellarEnthalpyTrueDofs[listIndex];
MFEM_VERIFY(
trueDof >= 0 && trueDof < enthalpyTrue.Size(), "The stellar enthalpy true-DOF mask contains an "
"invalid index."
);
enthalpyTrue(trueDof) = 0.0;
} }
return enthalpyTrue; return enthalpyTrue;
} }
[[nodiscard]] mfem::Vector make_positive_asymmetric_enthalpy(const mean_field::fem::FEM &f) { [[nodiscard]] mfem::Vector
make_positive_asymmetric_enthalpy(const mean_field::fem::FEM &f) {
mfem::FunctionCoefficient coefficient([](const mfem::Vector &position) { mfem::FunctionCoefficient coefficient([](const mfem::Vector &position) {
return 1.10 + 0.07 * position(0) - 0.04 * position(1) + 0.03 * position(2); return 1.10 + 0.07 * position(0) - 0.04 * position(1) + 0.03 * position(2);
}); });
@@ -70,16 +66,16 @@ namespace pressure_force_kernel_test_utils {
return enthalpyTrue; return enthalpyTrue;
} }
[[nodiscard]] mfem::Vector make_component_test_field( [[nodiscard]] mfem::Vector
const mean_field::fem::FEM &f, make_component_test_field(const mean_field::fem::FEM &f, const int component,
const int component, const int coordinate) {
const int coordinate
) {
const int dimension = f.mesh->Dimension(); const int dimension = f.mesh->Dimension();
MFEM_VERIFY(component >= 0 && component < dimension, "The requested vector component is invalid."); MFEM_VERIFY(component >= 0 && component < dimension,
"The requested vector component is invalid.");
MFEM_VERIFY(coordinate >= -1 && coordinate < dimension, "The requested coordinate is invalid."); MFEM_VERIFY(coordinate >= -1 && coordinate < dimension,
"The requested coordinate is invalid.");
/* /*
* coordinate == -1 gives the rigid translation e_component. * coordinate == -1 gives the rigid translation e_component.
@@ -89,13 +85,13 @@ namespace pressure_force_kernel_test_utils {
* w = x_coordinate e_component. * w = x_coordinate e_component.
*/ */
mfem::VectorFunctionCoefficient coefficient( mfem::VectorFunctionCoefficient coefficient(
dimension, [component, coordinate, dimension](const mfem::Vector &position, mfem::Vector &value) { dimension, [component, coordinate, dimension](
const mfem::Vector &position, mfem::Vector &value) {
value.SetSize(dimension); value.SetSize(dimension);
value = 0.0; value = 0.0;
value(component) = coordinate < 0 ? 1.0 : position(coordinate); value(component) = coordinate < 0 ? 1.0 : position(coordinate);
} });
);
mfem::ParGridFunction field(f.displacementFes.get()); mfem::ParGridFunction field(f.displacementFes.get());
@@ -107,12 +103,11 @@ namespace pressure_force_kernel_test_utils {
return fieldTrue; return fieldTrue;
} }
[[nodiscard]] double global_dot( [[nodiscard]] double global_dot(const mfem::Vector &left,
const mfem::Vector &left,
const mfem::Vector &right, const mfem::Vector &right,
MPI_Comm communicator MPI_Comm communicator) {
) { MFEM_VERIFY(left.Size() == right.Size(),
MFEM_VERIFY(left.Size() == right.Size(), "The global dot-product vectors have different sizes."); "The global dot-product vectors have different sizes.");
const double localDot = left * right; const double localDot = left * right;
double globalDot = 0.0; double globalDot = 0.0;
@@ -124,24 +119,19 @@ namespace pressure_force_kernel_test_utils {
[[nodiscard]] double integrate_pressure( [[nodiscard]] double integrate_pressure(
const mean_field::fem::FEM &f, const mean_field::fem::FEM &f,
const mean_field::mapping::DomainMapperStateless &domainMapper, const mean_field::mapping::DomainMapper &domainMapper,
const mean_field::eos::Polytrope &barotrope, const mean_field::eos::Polytrope &barotrope,
const mfem::Vector &enthalpyTrue, const mfem::Vector &enthalpyTrue, const mfem::Vector &displacementTrue) {
const mfem::Vector &displacementTrue MFEM_VERIFY(enthalpyTrue.Size() == f.enthalpyFes->GetTrueVSize(),
) { "The pressure-integral enthalpy vector has the wrong size.");
MFEM_VERIFY(
enthalpyTrue.Size() == f.enthalpyFes->GetTrueVSize(),
"The pressure-integral enthalpy vector has the wrong size."
);
MFEM_VERIFY( MFEM_VERIFY(displacementTrue.Size() == f.displacementFes->GetTrueVSize(),
displacementTrue.Size() == f.displacementFes->GetTrueVSize(), "The pressure-integral displacement vector has the wrong size.");
"The pressure-integral displacement vector has the wrong size."
);
mfem::Vector enthalpyLocal(f.enthalpyFes->GetVSize()); mfem::Vector enthalpyLocal(f.enthalpyFes->GetVSize());
const mfem::Operator *enthalpyProlongation = f.enthalpyFes->GetProlongationMatrix(); const mfem::Operator *enthalpyProlongation =
f.enthalpyFes->GetProlongationMatrix();
if (enthalpyProlongation != nullptr) { if (enthalpyProlongation != nullptr) {
enthalpyProlongation->Mult(enthalpyTrue, enthalpyLocal); enthalpyProlongation->Mult(enthalpyTrue, enthalpyLocal);
@@ -151,7 +141,8 @@ namespace pressure_force_kernel_test_utils {
mfem::Vector displacementLocal(f.displacementFes->GetVSize()); mfem::Vector displacementLocal(f.displacementFes->GetVSize());
const mfem::Operator *displacementProlongation = f.displacementFes->GetProlongationMatrix(); const mfem::Operator *displacementProlongation =
f.displacementFes->GetProlongationMatrix();
if (displacementProlongation != nullptr) { if (displacementProlongation != nullptr) {
displacementProlongation->Mult(displacementTrue, displacementLocal); displacementProlongation->Mult(displacementTrue, displacementLocal);
@@ -160,19 +151,22 @@ namespace pressure_force_kernel_test_utils {
} }
const double pressureExtraOrderValue = const double pressureExtraOrderValue =
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( MFEM_VERIFY(std::isfinite(pressureExtraOrderValue) &&
std::isfinite(pressureExtraOrderValue) && pressureExtraOrderValue >= 0.0 && pressureExtraOrderValue >= 0.0 &&
pressureExtraOrderValue <= static_cast<double>(std::numeric_limits<int>::max()), pressureExtraOrderValue <=
"The pressure-integral EOS order is invalid." static_cast<double>(std::numeric_limits<int>::max()),
); "The pressure-integral EOS order is invalid.");
const int pressureExtraOrder = static_cast<int>(std::ceil(pressureExtraOrderValue)); const int pressureExtraOrder =
static_cast<int>(std::ceil(pressureExtraOrderValue));
using EnthalpyField = mean_field::field::Field<mean_field::field::Enthalpy>; using EnthalpyField = mean_field::field::Field<mean_field::field::Enthalpy>;
mean_field::mapping::DomainMapperStateless::Workspace workspace(f.mesh->Dimension()); mean_field::mapping::DomainMapper::Workspace workspace(
f.mesh->Dimension());
mean_field::mapping::VolumeMappingContext mappingContext; mean_field::mapping::VolumeMappingContext mappingContext;
@@ -187,27 +181,31 @@ namespace pressure_force_kernel_test_utils {
double localPressureIntegral = 0.0; double localPressureIntegral = 0.0;
const int vacuumAttribute = domainMapper.GetVacuumElementAttribute(); const int vacuumAttribute = field_dof_test_utils::vacuum_material_attribute;
for (int elementId = 0; elementId < f.mesh->GetNE(); ++elementId) { 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( MFEM_VERIFY(transformation != nullptr,
transformation != nullptr, "The pressure-integral reference received a null " "The pressure-integral reference received a null "
"element transformation." "element transformation.");
);
if (transformation->Attribute == vacuumAttribute) { if (transformation->Attribute == vacuumAttribute) {
continue; 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 = mfem::DofTransformation *displacementDofTransformation =
f.displacementFes->GetElementVDofs(elementId, displacementDofs); f.displacementFes->GetElementVDofs(elementId, displacementDofs);
@@ -219,7 +217,8 @@ namespace pressure_force_kernel_test_utils {
displacementLocal.GetSubVector(displacementDofs, elementDisplacement); displacementLocal.GetSubVector(displacementDofs, elementDisplacement);
f.compactificationCoordinate->GetSubVector(compactificationDofs, elementCompactification); f.compactificationCoordinate->GetSubVector(compactificationDofs,
elementCompactification);
if (enthalpyDofTransformation != nullptr) { if (enthalpyDofTransformation != nullptr) {
enthalpyDofTransformation->InvTransformPrimal(elementEnthalpy); enthalpyDofTransformation->InvTransformPrimal(elementEnthalpy);
@@ -230,63 +229,70 @@ namespace pressure_force_kernel_test_utils {
} }
if (compactificationDofTransformation != nullptr) { if (compactificationDofTransformation != nullptr) {
compactificationDofTransformation->InvTransformPrimal(elementCompactification); compactificationDofTransformation->InvTransformPrimal(
elementCompactification);
} }
const mean_field::mapping::ElementDisplacementData displacementData = const mean_field::mapping::ElementDisplacementData displacementData =
mean_field::mapping::ElementDisplacementDataFromElementVDofs(displacementElement, elementDisplacement); mean_field::mapping::ElementDisplacementDataFromElementVDofs(
displacementElement, elementDisplacement);
const mean_field::mapping::ElementCompactificationData compactificationData( const mean_field::mapping::ElementCompactificationData compactificationData(
compactificationElement, elementCompactification compactificationElement, elementCompactification);
);
const mean_field::mapping::ElementMappingData mappingData{ const mean_field::mapping::ElementMappingData mappingData{
.displacement = displacementData, .compactification = compactificationData .displacement = displacementData,
}; .compactification = compactificationData};
const mean_field::quadrature::Query query = const mean_field::quadrature::Query query = EnthalpyField::make_query<
EnthalpyField::make_query<mean_field::field::Enthalpy::Form::PressureIntegral>( mean_field::field::Enthalpy::Form::PressureIntegral>(
mean_field::quadrature::QuadratureRole::diagnostic, transformation->OrderW(), mean_field::quadrature::QuadratureRole::diagnostic,
std::array<int, 1>{pressureExtraOrder}, mean_field::utils::DOMAINS::STELLAR, transformation->OrderW(), std::array<int, 1>{pressureExtraOrder},
mean_field::quadrature::MappingKind::general mean_field::utils::DOMAINS::STELLAR,
); mean_field::quadrature::MappingKind::general);
const mean_field::quadrature::MfemRule rule = const mean_field::quadrature::MfemRule rule =
f.quadratureFactory->get(query, transformation->GetGeometryType()); f.quadratureFactory->get(query, transformation->GetGeometryType());
MFEM_VERIFY(rule.integration_rule != nullptr, "The pressure-integral quadrature rule is null."); MFEM_VERIFY(rule.integration_rule != nullptr,
"The pressure-integral quadrature rule is null.");
enthalpyShape.SetSize(enthalpyElement.GetDof()); enthalpyShape.SetSize(enthalpyElement.GetDof());
for (int quadratureIndex = 0; quadratureIndex < rule.integration_rule->GetNPoints(); ++quadratureIndex) { for (int quadratureIndex = 0;
const mfem::IntegrationPoint &integrationPoint = rule.integration_rule->IntPoint(quadratureIndex); quadratureIndex < rule.integration_rule->GetNPoints();
++quadratureIndex) {
const mfem::IntegrationPoint &integrationPoint =
rule.integration_rule->IntPoint(quadratureIndex);
transformation->SetIntPoint(&integrationPoint); transformation->SetIntPoint(&integrationPoint);
const mean_field::mapping::MappingStatus mappingStatus = domainMapper.EvaluateVolume( const mean_field::mapping::MappingStatus mappingStatus =
mappingData, *transformation, integrationPoint, workspace, mappingContext domainMapper.EvaluateVolume(mappingData, *transformation,
); integrationPoint, workspace,
mappingContext);
MFEM_VERIFY( MFEM_VERIFY(mappingStatus == mean_field::mapping::MappingStatus::valid,
mappingStatus == mean_field::mapping::MappingStatus::valid,
"Stateless mapping failed in the " "Stateless mapping failed in the "
"independent pressure integral. Element: " "independent pressure integral. Element: "
<< elementId << ", attribute: " << transformation->Attribute << elementId
<< ", quadrature point: " << quadratureIndex << ", status: " << static_cast<int>(mappingStatus) << ", attribute: " << transformation->Attribute
); << ", quadrature point: " << quadratureIndex
<< ", status: " << static_cast<int>(mappingStatus));
enthalpyElement.CalcShape(integrationPoint, enthalpyShape); enthalpyElement.CalcShape(integrationPoint, enthalpyShape);
const double enthalpyValue = elementEnthalpy * enthalpyShape; const double enthalpyValue = elementEnthalpy * enthalpyShape;
const double pressureValue = barotrope.pressure_from_enthalpy(enthalpyValue); const double pressureValue =
barotrope.pressure_from_enthalpy(enthalpyValue);
const double contribution = pressureValue * mappingContext.quadrature.weight; const double contribution =
pressureValue * mappingContext.quadrature.weight;
MFEM_VERIFY( MFEM_VERIFY(std::isfinite(pressureValue) && std::isfinite(contribution),
std::isfinite(pressureValue) && std::isfinite(contribution), "The independent pressure integral " "The independent pressure integral "
"encountered a non-finite value." "encountered a non-finite value.");
);
localPressureIntegral += contribution; localPressureIntegral += contribution;
} }
@@ -294,16 +300,15 @@ namespace pressure_force_kernel_test_utils {
double globalPressureIntegral = 0.0; double globalPressureIntegral = 0.0;
MPI_Allreduce(&localPressureIntegral, &globalPressureIntegral, 1, MPI_DOUBLE, MPI_SUM, f.mesh->GetComm()); MPI_Allreduce(&localPressureIntegral, &globalPressureIntegral, 1, MPI_DOUBLE,
MPI_SUM, f.mesh->GetComm());
return globalPressureIntegral; return globalPressureIntegral;
} }
} // namespace pressure_force_kernel_test_utils } // namespace pressure_force_kernel_test_utils
TEST_CASE( TEST_CASE("Pressure Force Residual Vanishes For Zero Enthalpy",
"Pressure Force Residual Vanishes For Zero Enthalpy", tags::barotrope &tags::pressure &tags::kernels &tags::integration) {
tags::barotrope &tags::pressure &tags::kernels &tags::integration
) {
mean_field::utils::Args args = test_utils::setup_args(); mean_field::utils::Args args = test_utils::setup_args();
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0); mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
@@ -315,25 +320,25 @@ TEST_CASE(
mfem::Vector enthalpyTrue(f.enthalpyFes->GetTrueVSize()); mfem::Vector enthalpyTrue(f.enthalpyFes->GetTrueVSize());
enthalpyTrue = 0.0; enthalpyTrue = 0.0;
const mfem::Vector displacementTrue = pressure_force_kernel_test_utils::make_zero_displacement(f); const mfem::Vector displacementTrue =
pressure_force_kernel_test_utils::make_zero_displacement(f);
mfem::Vector residualTrue; mfem::Vector residualTrue;
mean_field::operators::kernels::apply_pressure_force_residual( mean_field::operators::kernels::apply_pressure_force_residual(
f, *f.domainMapperStateless, barotrope, enthalpyTrue, displacementTrue, residualTrue f, *f.domainMapperStateless, barotrope, enthalpyTrue, displacementTrue,
); residualTrue);
REQUIRE(residualTrue.Size() == f.displacementFes->GetTrueVSize()); REQUIRE(residualTrue.Size() == f.displacementFes->GetTrueVSize());
const double residualNorm = gravity_prepared_test_utils::global_norm(residualTrue, f.mesh->GetComm()); const double residualNorm =
gravity_prepared_test_utils::global_norm(residualTrue, f.mesh->GetComm());
CHECK(residualNorm == 0.0); CHECK(residualNorm == 0.0);
} }
TEST_CASE( TEST_CASE("Pressure Force Residual Excludes Vacuum Enthalpy Exactly",
"Pressure Force Residual Excludes Vacuum Enthalpy Exactly", tags::barotrope &tags::pressure &tags::kernels &tags::integration) {
tags::barotrope &tags::pressure &tags::kernels &tags::integration
) {
mean_field::utils::Args args = test_utils::setup_args(); mean_field::utils::Args args = test_utils::setup_args();
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0); mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
@@ -342,9 +347,11 @@ TEST_CASE(
const mean_field::eos::Polytrope barotrope(3.0, 0.25); const mean_field::eos::Polytrope barotrope(3.0, 0.25);
const mfem::Vector enthalpyTrue = pressure_force_kernel_test_utils::make_vacuum_only_enthalpy(f); const mfem::Vector enthalpyTrue =
pressure_force_kernel_test_utils::make_vacuum_only_enthalpy(f);
const double enthalpyNorm = gravity_prepared_test_utils::global_norm(enthalpyTrue, f.mesh->GetComm()); const double enthalpyNorm =
gravity_prepared_test_utils::global_norm(enthalpyTrue, f.mesh->GetComm());
/* /*
* Ensure this is a real exclusion test rather than another * Ensure this is a real exclusion test rather than another
@@ -352,25 +359,25 @@ TEST_CASE(
*/ */
REQUIRE(enthalpyNorm > 0.0); REQUIRE(enthalpyNorm > 0.0);
const mfem::Vector displacementTrue = pressure_force_kernel_test_utils::make_zero_displacement(f); const mfem::Vector displacementTrue =
pressure_force_kernel_test_utils::make_zero_displacement(f);
mfem::Vector residualTrue; mfem::Vector residualTrue;
mean_field::operators::kernels::apply_pressure_force_residual( mean_field::operators::kernels::apply_pressure_force_residual(
f, *f.domainMapperStateless, barotrope, enthalpyTrue, displacementTrue, residualTrue f, *f.domainMapperStateless, barotrope, enthalpyTrue, displacementTrue,
); residualTrue);
REQUIRE(residualTrue.Size() == f.displacementFes->GetTrueVSize()); REQUIRE(residualTrue.Size() == f.displacementFes->GetTrueVSize());
const double residualNorm = gravity_prepared_test_utils::global_norm(residualTrue, f.mesh->GetComm()); const double residualNorm =
gravity_prepared_test_utils::global_norm(residualTrue, f.mesh->GetComm());
CHECK(residualNorm == 0.0); CHECK(residualNorm == 0.0);
} }
TEST_CASE( TEST_CASE("Pressure Force Residual Is Nonzero For Positive Stellar Pressure",
"Pressure Force Residual Is Nonzero For Positive Stellar Pressure", tags::barotrope &tags::pressure &tags::kernels &tags::integration) {
tags::barotrope &tags::pressure &tags::kernels &tags::integration
) {
mean_field::utils::Args args = test_utils::setup_args(); mean_field::utils::Args args = test_utils::setup_args();
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0); mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
@@ -387,15 +394,17 @@ TEST_CASE(
mfem::Vector enthalpyTrue(f.enthalpyFes->GetTrueVSize()); mfem::Vector enthalpyTrue(f.enthalpyFes->GetTrueVSize());
enthalpyTrue = 1.0; enthalpyTrue = 1.0;
const mfem::Vector displacementTrue = pressure_force_kernel_test_utils::make_zero_displacement(f); const mfem::Vector displacementTrue =
pressure_force_kernel_test_utils::make_zero_displacement(f);
mfem::Vector residualTrue; mfem::Vector residualTrue;
mean_field::operators::kernels::apply_pressure_force_residual( mean_field::operators::kernels::apply_pressure_force_residual(
f, *f.domainMapperStateless, barotrope, enthalpyTrue, displacementTrue, residualTrue f, *f.domainMapperStateless, barotrope, enthalpyTrue, displacementTrue,
); residualTrue);
const double residualNorm = gravity_prepared_test_utils::global_norm(residualTrue, f.mesh->GetComm()); const double residualNorm =
gravity_prepared_test_utils::global_norm(residualTrue, f.mesh->GetComm());
INFO("Positive-pressure residual norm = " << residualNorm); INFO("Positive-pressure residual norm = " << residualNorm);
@@ -404,10 +413,9 @@ TEST_CASE(
CHECK(residualNorm > 100.0 * std::numeric_limits<double>::epsilon()); CHECK(residualNorm > 100.0 * std::numeric_limits<double>::epsilon());
} }
TEST_CASE( TEST_CASE("Pressure Force Residual Does No Work Against Rigid Translations",
"Pressure Force Residual Does No Work Against Rigid Translations", tags::barotrope &tags::pressure &tags::kernels &tags::integration
tags::barotrope &tags::pressure &tags::kernels &tags::integration &tags::accuracy &tags::accuracy) {
) {
mean_field::utils::Args args = test_utils::setup_args(); mean_field::utils::Args args = test_utils::setup_args();
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0); mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
@@ -418,17 +426,20 @@ TEST_CASE(
const mean_field::eos::Polytrope barotrope(3.0, 0.25); const mean_field::eos::Polytrope barotrope(3.0, 0.25);
const mfem::Vector enthalpyTrue = pressure_force_kernel_test_utils::make_positive_asymmetric_enthalpy(f); const mfem::Vector enthalpyTrue =
pressure_force_kernel_test_utils::make_positive_asymmetric_enthalpy(f);
const mfem::Vector displacementTrue = pressure_force_kernel_test_utils::make_zero_displacement(f); const mfem::Vector displacementTrue =
pressure_force_kernel_test_utils::make_zero_displacement(f);
mfem::Vector residualTrue; mfem::Vector residualTrue;
mean_field::operators::kernels::apply_pressure_force_residual( mean_field::operators::kernels::apply_pressure_force_residual(
f, *f.domainMapperStateless, barotrope, enthalpyTrue, displacementTrue, residualTrue f, *f.domainMapperStateless, barotrope, enthalpyTrue, displacementTrue,
); residualTrue);
const double residualNorm = gravity_prepared_test_utils::global_norm(residualTrue, f.mesh->GetComm()); const double residualNorm =
gravity_prepared_test_utils::global_norm(residualTrue, f.mesh->GetComm());
REQUIRE(residualNorm > 0.0); REQUIRE(residualNorm > 0.0);
@@ -436,14 +447,17 @@ TEST_CASE(
for (int component = 0; component < dimension; ++component) { for (int component = 0; component < dimension; ++component) {
const mfem::Vector translationTrue = const mfem::Vector translationTrue =
pressure_force_kernel_test_utils::make_component_test_field(f, component, -1); pressure_force_kernel_test_utils::make_component_test_field(
f, component, -1);
const double translationNorm = gravity_prepared_test_utils::global_norm(translationTrue, f.mesh->GetComm()); const double translationNorm = gravity_prepared_test_utils::global_norm(
translationTrue, f.mesh->GetComm());
const double translationWork = const double translationWork = pressure_force_kernel_test_utils::global_dot(
pressure_force_kernel_test_utils::global_dot(translationTrue, residualTrue, f.mesh->GetComm()); translationTrue, residualTrue, f.mesh->GetComm());
const double dotProductScale = std::fmax(residualNorm * translationNorm, 1.0); const double dotProductScale =
std::fmax(residualNorm * translationNorm, 1.0);
CAPTURE(component, translationWork, dotProductScale); CAPTURE(component, translationWork, dotProductScale);
@@ -451,10 +465,9 @@ TEST_CASE(
} }
} }
TEST_CASE( TEST_CASE("Pressure Force Residual Matches Independent Pressure Integral",
"Pressure Force Residual Matches Independent Pressure Integral", tags::barotrope &tags::pressure &tags::kernels &tags::integration
tags::barotrope &tags::pressure &tags::kernels &tags::integration &tags::accuracy &tags::accuracy) {
) {
mean_field::utils::Args args = test_utils::setup_args(); mean_field::utils::Args args = test_utils::setup_args();
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0); mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
@@ -465,15 +478,17 @@ TEST_CASE(
const mean_field::eos::Polytrope barotrope(3.0, 0.25); const mean_field::eos::Polytrope barotrope(3.0, 0.25);
const mfem::Vector enthalpyTrue = pressure_force_kernel_test_utils::make_positive_asymmetric_enthalpy(f); const mfem::Vector enthalpyTrue =
pressure_force_kernel_test_utils::make_positive_asymmetric_enthalpy(f);
const mfem::Vector displacementTrue = pressure_force_kernel_test_utils::make_zero_displacement(f); const mfem::Vector displacementTrue =
pressure_force_kernel_test_utils::make_zero_displacement(f);
mfem::Vector residualTrue; mfem::Vector residualTrue;
mean_field::operators::kernels::apply_pressure_force_residual( mean_field::operators::kernels::apply_pressure_force_residual(
f, *f.domainMapperStateless, barotrope, enthalpyTrue, displacementTrue, residualTrue f, *f.domainMapperStateless, barotrope, enthalpyTrue, displacementTrue,
); residualTrue);
const int dimension = f.mesh->Dimension(); const int dimension = f.mesh->Dimension();
@@ -484,16 +499,19 @@ TEST_CASE(
for (int component = 0; component < dimension; ++component) { for (int component = 0; component < dimension; ++component) {
for (int coordinate = 0; coordinate < dimension; ++coordinate) { for (int coordinate = 0; coordinate < dimension; ++coordinate) {
const mfem::Vector affineTestTrue = const mfem::Vector affineTestTrue =
pressure_force_kernel_test_utils::make_component_test_field(f, component, coordinate); pressure_force_kernel_test_utils::make_component_test_field(
f, component, coordinate);
virtualWork(component, coordinate) = virtualWork(component, coordinate) =
pressure_force_kernel_test_utils::global_dot(affineTestTrue, residualTrue, f.mesh->GetComm()); pressure_force_kernel_test_utils::global_dot(
affineTestTrue, residualTrue, f.mesh->GetComm());
} }
} }
const double pressureIntegral = pressure_force_kernel_test_utils::integrate_pressure( const double pressureIntegral =
f, *f.domainMapperStateless, barotrope, enthalpyTrue, displacementTrue pressure_force_kernel_test_utils::integrate_pressure(
); f, *f.domainMapperStateless, barotrope, enthalpyTrue,
displacementTrue);
REQUIRE(std::isfinite(pressureIntegral)); REQUIRE(std::isfinite(pressureIntegral));
@@ -527,25 +545,28 @@ TEST_CASE(
for (int coordinate = 0; coordinate < dimension; ++coordinate) { for (int coordinate = 0; coordinate < dimension; ++coordinate) {
const double computedWork = virtualWork(component, coordinate); const double computedWork = virtualWork(component, coordinate);
const double expectedWork = component == coordinate ? -pressureIntegral : 0.0; const double expectedWork =
component == coordinate ? -pressureIntegral : 0.0;
CAPTURE(component, coordinate, computedWork, expectedWork, pressureIntegral, comparisonTolerance); CAPTURE(component, coordinate, computedWork, expectedWork,
pressureIntegral, comparisonTolerance);
CHECK(std::abs(computedWork - expectedWork) <= comparisonTolerance); CHECK(std::abs(computedWork - expectedWork) <= comparisonTolerance);
} }
} }
const double relativeMeanError = std::abs(meanDiagonalWork + pressureIntegral) / std::abs(pressureIntegral); const double relativeMeanError =
std::abs(meanDiagonalWork + pressureIntegral) /
std::abs(pressureIntegral);
INFO("Relative mean diagonal error = " << relativeMeanError); INFO("Relative mean diagonal error = " << relativeMeanError);
CHECK(relativeMeanError <= 1.0e-6); CHECK(relativeMeanError <= 1.0e-6);
} }
TEST_CASE( TEST_CASE("Pressure Force Residual Matches Deformed Pressure Volume Variation",
"Pressure Force Residual Matches Deformed Pressure Volume Variation", tags::barotrope &tags::pressure &tags::kernels &tags::integration
tags::barotrope &tags::pressure &tags::kernels &tags::integration &tags::accuracy &tags::accuracy) {
) {
mean_field::utils::Args args = test_utils::setup_args(); mean_field::utils::Args args = test_utils::setup_args();
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0); mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
@@ -559,14 +580,16 @@ TEST_CASE(
* exercises a genuinely nonuniform pressure distribution. * exercises a genuinely nonuniform pressure distribution.
*/ */
const mfem::Vector enthalpyTrue = const mfem::Vector enthalpyTrue =
pressure_force_kernel_test_utils::make_deterministic_vector(f.enthalpyFes->GetTrueVSize(), 0.37); pressure_force_kernel_test_utils::make_deterministic_vector(
f.enthalpyFes->GetTrueVSize(), 0.37);
/* /*
* make_displacement() contains anisotropic diagonal terms and * make_displacement() contains anisotropic diagonal terms and
* quadratic cross terms. A scale of 0.67 therefore provides a * quadratic cross terms. A scale of 0.67 therefore provides a
* nonzero, nonspherical, valid base geometry. * nonzero, nonspherical, valid base geometry.
*/ */
const mfem::Vector baseDisplacementTrue = gravity_prepared_test_utils::make_displacement(f, 0.67); const mfem::Vector baseDisplacementTrue =
gravity_prepared_test_utils::make_displacement(f, 0.67);
/* /*
* Differentiate along the same smooth deformation family. Thus * Differentiate along the same smooth deformation family. Thus
@@ -576,38 +599,43 @@ TEST_CASE(
* This gives a controlled geometry path while still evaluating * This gives a controlled geometry path while still evaluating
* the derivative at a genuinely deformed base state. * the derivative at a genuinely deformed base state.
*/ */
const mfem::Vector displacementVariationTrue = gravity_prepared_test_utils::make_displacement(f, 1.0); const mfem::Vector displacementVariationTrue =
gravity_prepared_test_utils::make_displacement(f, 1.0);
const double baseDisplacementNorm = const double baseDisplacementNorm = gravity_prepared_test_utils::global_norm(
gravity_prepared_test_utils::global_norm(baseDisplacementTrue, f.mesh->GetComm()); baseDisplacementTrue, f.mesh->GetComm());
const double variationNorm = gravity_prepared_test_utils::global_norm(displacementVariationTrue, f.mesh->GetComm()); const double variationNorm = gravity_prepared_test_utils::global_norm(
displacementVariationTrue, f.mesh->GetComm());
REQUIRE(baseDisplacementNorm > 100.0 * std::numeric_limits<double>::epsilon()); REQUIRE(baseDisplacementNorm >
100.0 * std::numeric_limits<double>::epsilon());
REQUIRE(variationNorm > 100.0 * std::numeric_limits<double>::epsilon()); REQUIRE(variationNorm > 100.0 * std::numeric_limits<double>::epsilon());
mfem::Vector residualTrue; mfem::Vector residualTrue;
mean_field::operators::kernels::apply_pressure_force_residual( mean_field::operators::kernels::apply_pressure_force_residual(
f, *f.domainMapperStateless, barotrope, enthalpyTrue, baseDisplacementTrue, residualTrue f, *f.domainMapperStateless, barotrope, enthalpyTrue,
); baseDisplacementTrue, residualTrue);
REQUIRE(residualTrue.Size() == f.displacementFes->GetTrueVSize()); REQUIRE(residualTrue.Size() == f.displacementFes->GetTrueVSize());
const double residualWork = const double residualWork = pressure_force_kernel_test_utils::global_dot(
pressure_force_kernel_test_utils::global_dot(displacementVariationTrue, residualTrue, f.mesh->GetComm()); displacementVariationTrue, residualTrue, f.mesh->GetComm());
REQUIRE(std::isfinite(residualWork)); REQUIRE(std::isfinite(residualWork));
REQUIRE(std::abs(residualWork) > 100.0 * std::numeric_limits<double>::epsilon()); REQUIRE(std::abs(residualWork) >
100.0 * std::numeric_limits<double>::epsilon());
/* /*
* The relatively broad initial sweep lets us see the expected * The relatively broad initial sweep lets us see the expected
* centered-difference convergence before reaching the quadrature * centered-difference convergence before reaching the quadrature
* and representation plateau. * and representation plateau.
*/ */
constexpr std::array<double, 4> differenceSteps{1.0e-2, 5.0e-3, 2.5e-3, 1.25e-3}; constexpr std::array<double, 4> differenceSteps{1.0e-2, 5.0e-3, 2.5e-3,
1.25e-3};
double bestRelativeDiscrepancy = std::numeric_limits<double>::infinity(); double bestRelativeDiscrepancy = std::numeric_limits<double>::infinity();
@@ -620,18 +648,21 @@ TEST_CASE(
displacementMinus.Add(-differenceStep, displacementVariationTrue); displacementMinus.Add(-differenceStep, displacementVariationTrue);
const double pressureIntegralPlus = pressure_force_kernel_test_utils::integrate_pressure( const double pressureIntegralPlus =
f, *f.domainMapperStateless, barotrope, enthalpyTrue, displacementPlus pressure_force_kernel_test_utils::integrate_pressure(
); f, *f.domainMapperStateless, barotrope, enthalpyTrue,
displacementPlus);
const double pressureIntegralMinus = pressure_force_kernel_test_utils::integrate_pressure( const double pressureIntegralMinus =
f, *f.domainMapperStateless, barotrope, enthalpyTrue, displacementMinus pressure_force_kernel_test_utils::integrate_pressure(
); f, *f.domainMapperStateless, barotrope, enthalpyTrue,
displacementMinus);
REQUIRE(std::isfinite(pressureIntegralPlus)); REQUIRE(std::isfinite(pressureIntegralPlus));
REQUIRE(std::isfinite(pressureIntegralMinus)); REQUIRE(std::isfinite(pressureIntegralMinus));
const double pressureVolumeDerivative = (pressureIntegralPlus - pressureIntegralMinus) / (2.0 * differenceStep); const double pressureVolumeDerivative =
(pressureIntegralPlus - pressureIntegralMinus) / (2.0 * differenceStep);
REQUIRE(std::isfinite(pressureVolumeDerivative)); REQUIRE(std::isfinite(pressureVolumeDerivative));
@@ -643,7 +674,8 @@ TEST_CASE(
REQUIRE(comparisonScale > 100.0 * std::numeric_limits<double>::epsilon()); REQUIRE(comparisonScale > 100.0 * std::numeric_limits<double>::epsilon());
const double absoluteDiscrepancy = std::abs(residualWork + pressureVolumeDerivative); const double absoluteDiscrepancy =
std::abs(residualWork + pressureVolumeDerivative);
const double relativeDiscrepancy = absoluteDiscrepancy / comparisonScale; const double relativeDiscrepancy = absoluteDiscrepancy / comparisonScale;
@@ -657,7 +689,8 @@ TEST_CASE(
INFO("Pressure-volume derivative = " << pressureVolumeDerivative); INFO("Pressure-volume derivative = " << pressureVolumeDerivative);
INFO("Residual work plus derivative = " << residualWork + pressureVolumeDerivative); INFO("Residual work plus derivative = " << residualWork +
pressureVolumeDerivative);
INFO("Relative discrepancy = " << relativeDiscrepancy); INFO("Relative discrepancy = " << relativeDiscrepancy);
@@ -669,7 +702,8 @@ TEST_CASE(
CHECK(residualWork * pressureVolumeDerivative < 0.0); CHECK(residualWork * pressureVolumeDerivative < 0.0);
} }
INFO("Best pressure-volume relative discrepancy = " << bestRelativeDiscrepancy); INFO("Best pressure-volume relative discrepancy = "
<< bestRelativeDiscrepancy);
/* /*
* This is intentionally a provisional but meaningful threshold. * This is intentionally a provisional but meaningful threshold.

View File

@@ -1,5 +1,6 @@
#include <catch2/catch_test_macros.hpp> #include <catch2/catch_test_macros.hpp>
#include <catch2/matchers/catch_matchers_floating_point.hpp> #include <catch2/matchers/catch_matchers_floating_point.hpp>
#include <cmath>
#include <mfem.hpp> #include <mfem.hpp>
import mean_field; import mean_field;
@@ -9,28 +10,33 @@ using namespace mean_field;
using Catch::Matchers::WithinAbs; using Catch::Matchers::WithinAbs;
namespace prepared_test = gravity_prepared_test_utils; namespace prepared_test = gravity_prepared_test_utils;
TEST_CASE( TEST_CASE("Prepared Mapped Hdiv Mass Matches Stateless Kernel",
"Prepared Mapped Hdiv Mass Matches Stateless Kernel", tags::gravity_prepared) {
tags::gravity_prepared
) {
auto args = test_utils::setup_args(); auto args = test_utils::setup_args();
fem::FEM f = fem::setup_fem(args.mesh_file, args, 0); fem::FEM f = fem::setup_fem(args.mesh_file, args, 0);
operators::PreparedMappedHDivMassOperator prepared_operator(f, *f.domainMapperStateless); operators::PreparedMappedHDivMassOperator prepared_operator(
REQUIRE(prepared_operator.Width() == prepared_operator.GetFluxMap().reduced_size()); f, *f.domainMapperStateless);
REQUIRE(prepared_operator.Height() == prepared_operator.GetFluxMap().reduced_size()); REQUIRE(prepared_operator.Width() ==
prepared_operator.GetFluxMap().reduced_size());
REQUIRE(prepared_operator.Height() ==
prepared_operator.GetFluxMap().reduced_size());
const mfem::Vector gravity_gradient_true = const mfem::Vector gravity_gradient_true =
prepared_test::make_deterministic_vector(f.gravityFluxFes->GetTrueVSize(), 0.21); prepared_test::make_deterministic_vector(f.gravityFluxFes->GetTrueVSize(),
const mfem::Vector gravity_gradient = prepared_operator.GetFluxMap().gather(gravity_gradient_true); 0.21);
const mfem::Vector gravity_gradient =
prepared_operator.GetFluxMap().gather(gravity_gradient_true);
const MPI_Comm communicator = f.gravityFluxFes->GetComm(); const MPI_Comm communicator = f.gravityFluxFes->GetComm();
mfem::Vector identity_action; mfem::Vector identity_action;
mfem::Vector deformed_action; mfem::Vector deformed_action;
for (const double deformation_scale : {0.0, 1.0}) { for (const double deformation_scale : {0.0, 1.0}) {
const mfem::Vector displacement_true = prepared_test::make_displacement(f, deformation_scale); const mfem::Vector displacement_true =
const mfem::Vector displacement = prepared_operator.GetDisplacementMap().gather(displacement_true); prepared_test::make_displacement(f, deformation_scale);
const mfem::Vector displacement =
prepared_operator.GetDisplacementMap().gather(displacement_true);
prepared_operator.Prepare(displacement); prepared_operator.Prepare(displacement);
@@ -39,15 +45,19 @@ TEST_CASE(
prepared_operator.Mult(gravity_gradient, prepared_action); prepared_operator.Mult(gravity_gradient, prepared_action);
mfem::Vector reference_action_true; mfem::Vector reference_action_true;
operators::kernels::apply_mapped_hdiv_mass( operators::kernels::apply_mapped_hdiv_mass(
f, *f.domainMapperStateless, gravity_gradient_true, displacement_true, reference_action_true f, *f.domainMapperStateless, gravity_gradient_true, displacement_true,
); reference_action_true);
const mfem::Vector reference_action = prepared_operator.GetFluxMap().gather(reference_action_true); const mfem::Vector reference_action =
prepared_operator.GetFluxMap().gather(reference_action_true);
const double relative_error = prepared_test::relative_error(prepared_action, reference_action, communicator); const double relative_error = prepared_test::relative_error(
prepared_action, reference_action, communicator);
INFO("Deformation scale = " << deformation_scale); INFO("Deformation scale = " << deformation_scale);
INFO("Prepared action norm = " << prepared_test::global_norm(prepared_action, communicator)); INFO("Prepared action norm = "
INFO("Reference action norm = " << prepared_test::global_norm(reference_action, communicator)); << prepared_test::global_norm(prepared_action, communicator));
INFO("Reference action norm = "
<< prepared_test::global_norm(reference_action, communicator));
INFO("Relative prepared-operator error = " << relative_error); INFO("Relative prepared-operator error = " << relative_error);
REQUIRE(prepared_operator.IsPrepared()); REQUIRE(prepared_operator.IsPrepared());
@@ -60,7 +70,8 @@ TEST_CASE(
} }
} }
const double geometry_change = prepared_test::relative_error(deformed_action, identity_action, communicator); const double geometry_change = prepared_test::relative_error(
deformed_action, identity_action, communicator);
INFO("Relative action change under deformation = " << geometry_change); INFO("Relative action change under deformation = " << geometry_change);
@@ -68,27 +79,30 @@ TEST_CASE(
CHECK(geometry_change > 1.0e-5); CHECK(geometry_change > 1.0e-5);
} }
TEST_CASE( TEST_CASE("Prepared Mapped Hdiv Mass Preserves Operator Identities",
"Prepared Mapped Hdiv Mass Preserves Operator Identities", tags::gravity_prepared) {
tags::gravity_prepared
) {
auto args = test_utils::setup_args(); auto args = test_utils::setup_args();
fem::FEM f = fem::setup_fem(args.mesh_file, args, 0); fem::FEM f = fem::setup_fem(args.mesh_file, args, 0);
operators::PreparedMappedHDivMassOperator prepared_operator(f, *f.domainMapperStateless); operators::PreparedMappedHDivMassOperator prepared_operator(
REQUIRE(prepared_operator.Width() == prepared_operator.GetFluxMap().reduced_size()); f, *f.domainMapperStateless);
REQUIRE(prepared_operator.Height() == prepared_operator.GetFluxMap().reduced_size()); REQUIRE(prepared_operator.Width() ==
prepared_operator.GetFluxMap().reduced_size());
REQUIRE(prepared_operator.Height() ==
prepared_operator.GetFluxMap().reduced_size());
const mfem::Vector displacement = const mfem::Vector displacement =
prepared_operator.GetDisplacementMap().gather(prepared_test::make_displacement(f, 1.0)); prepared_operator.GetDisplacementMap().gather(
prepared_test::make_displacement(f, 1.0));
prepared_operator.Prepare(displacement); prepared_operator.Prepare(displacement);
const mfem::Vector first = prepared_operator.GetFluxMap().gather( const mfem::Vector first = prepared_operator.GetFluxMap().gather(
prepared_test::make_deterministic_vector(f.gravityFluxFes->GetTrueVSize(), 0.17) prepared_test::make_deterministic_vector(f.gravityFluxFes->GetTrueVSize(),
); 0.17));
const mfem::Vector second = prepared_operator.GetFluxMap().gather( const mfem::Vector second = prepared_operator.GetFluxMap().gather(
prepared_test::make_deterministic_vector(f.gravityFluxFes->GetTrueVSize(), 0.83) prepared_test::make_deterministic_vector(f.gravityFluxFes->GetTrueVSize(),
); 0.83));
const mfem::Vector combination = prepared_test::linear_combination(first, 1.7, second, -0.4); const mfem::Vector combination =
prepared_test::linear_combination(first, 1.7, second, -0.4);
mfem::Vector first_action; mfem::Vector first_action;
mfem::Vector second_action; mfem::Vector second_action;
@@ -99,7 +113,8 @@ TEST_CASE(
prepared_operator.Mult(second, second_action); prepared_operator.Mult(second, second_action);
prepared_operator.Mult(combination, combination_action); prepared_operator.Mult(combination, combination_action);
mfem::Vector expected_combination = prepared_test::linear_combination(first_action, 1.7, second_action, -0.4); mfem::Vector expected_combination =
prepared_test::linear_combination(first_action, 1.7, second_action, -0.4);
mfem::Vector zero(first.Size()); mfem::Vector zero(first.Size());
zero = 0.0; zero = 0.0;
@@ -107,14 +122,20 @@ TEST_CASE(
const MPI_Comm communicator = f.gravityFluxFes->GetComm(); const MPI_Comm communicator = f.gravityFluxFes->GetComm();
const double first_second_product = prepared_test::global_dot(first, second_action, communicator); const double first_second_product =
const double second_first_product = prepared_test::global_dot(second, first_action, communicator); prepared_test::global_dot(first, second_action, communicator);
const double symmetry_error = prepared_test::relative_scalar_error(first_second_product, second_first_product); const double second_first_product =
const double linearity_error = prepared_test::global_dot(second, first_action, communicator);
prepared_test::relative_error(combination_action, expected_combination, communicator); const double symmetry_error = prepared_test::relative_scalar_error(
const double first_energy = prepared_test::global_dot(first, first_action, communicator); first_second_product, second_first_product);
const double second_energy = prepared_test::global_dot(second, second_action, communicator); const double linearity_error = prepared_test::relative_error(
const std::uint64_t preparation_count = prepared_operator.GetPreparationCount(); combination_action, expected_combination, communicator);
const double first_energy =
prepared_test::global_dot(first, first_action, communicator);
const double second_energy =
prepared_test::global_dot(second, second_action, communicator);
const std::uint64_t preparation_count =
prepared_operator.GetPreparationCount();
mfem::Vector repeated_action; mfem::Vector repeated_action;
prepared_operator.Mult(first, repeated_action); prepared_operator.Mult(first, repeated_action);
@@ -128,9 +149,42 @@ TEST_CASE(
CHECK_THAT(symmetry_error, WithinAbs(0.0, 2.0e-12)); CHECK_THAT(symmetry_error, WithinAbs(0.0, 2.0e-12));
CHECK_THAT(linearity_error, WithinAbs(0.0, 2.0e-12)); CHECK_THAT(linearity_error, WithinAbs(0.0, 2.0e-12));
CHECK_THAT(prepared_test::global_norm(zero_action, communicator), WithinAbs(0.0, 1.0e-14)); CHECK_THAT(prepared_test::global_norm(zero_action, communicator),
WithinAbs(0.0, 1.0e-14));
CHECK(first_energy > 0.0); CHECK(first_energy > 0.0);
CHECK(second_energy > 0.0); CHECK(second_energy > 0.0);
CHECK(prepared_test::relative_error(repeated_action, first_action, communicator) < 2.0e-14); CHECK(prepared_test::relative_error(repeated_action, first_action,
communicator) < 2.0e-14);
CHECK(prepared_operator.GetPreparationCount() == preparation_count); CHECK(prepared_operator.GetPreparationCount() == preparation_count);
} }
TEST_CASE("Prepared Mapped Hdiv Mass Diagonal Is Positive Across Both Domains",
tags::gravity_prepared) {
auto args = test_utils::setup_args();
fem::FEM f = fem::setup_fem(args.mesh_file, args, 0);
operators::PreparedMappedHDivMassOperator prepared_operator(
f, *f.domainMapperStateless);
const mfem::Vector displacement =
prepared_operator.GetDisplacementMap().gather(
prepared_test::make_displacement(f, 1.0));
prepared_operator.Prepare(displacement);
mfem::Vector diagonal;
mfem::Vector true_diagonal;
prepared_operator.AssembleDiagonal(diagonal);
prepared_operator.AssembleTrueDiagonal(true_diagonal);
REQUIRE(diagonal.Size() == prepared_operator.Height());
REQUIRE(true_diagonal.Size() == prepared_operator.GetFluxMap().full_size());
const mfem::Vector gathered_true_diagonal =
prepared_operator.GetFluxMap().gather(true_diagonal);
for (int i = 0; i < diagonal.Size(); ++i) {
REQUIRE(std::isfinite(diagonal(i)));
CHECK(diagonal(i) > 0.0);
CHECK_THAT(diagonal(i), WithinAbs(gathered_true_diagonal(i),
1.0e-14 * std::abs(diagonal(i))));
}
}

View File

@@ -24,52 +24,40 @@ namespace prepared_hydrostatic_analytic_solve_test_utils {
public: public:
EnthalpyJacobianOperator( EnthalpyJacobianOperator(
const int enthalpySize, const int enthalpySize,
const mean_field::operators::PreparedHydrostaticEquilibriumOperator &preparedOperator const mean_field::operators::PreparedHydrostaticEquilibriumOperator
) &preparedOperator)
: mfem::Operator(enthalpySize), : mfem::Operator(enthalpySize), m_preparedOperator(preparedOperator) {}
m_preparedOperator(preparedOperator) {
}
void Mult( void Mult(const mfem::Vector &direction,
const mfem::Vector &direction, mfem::Vector &action) const override {
mfem::Vector &action
) const override {
m_preparedOperator.ApplyEnthalpyJacobianAction(direction, action); m_preparedOperator.ApplyEnthalpyJacobianAction(direction, action);
} }
private: private:
const mean_field::operators::PreparedHydrostaticEquilibriumOperator &m_preparedOperator; const mean_field::operators::PreparedHydrostaticEquilibriumOperator
&m_preparedOperator;
}; };
mean_field::operators::context::hydrostatic::HydrostaticEquilibriumDependencies make_dependencies() { mean_field::operators::context::hydrostatic::HydrostaticEquilibriumDependencies
return { make_dependencies() {
.discretization = {.identity = 701, .revision = 2}, return {.discretization = {.identity = 701, .revision = 2},
.enthalpy = {.identity = 709, .revision = 3}, .enthalpy = {.identity = 709, .revision = 3},
.gravityPotential = {.identity = 719, .revision = 5}, .gravityPotential = {.identity = 719, .revision = 5},
.displacement = {.identity = 727, .revision = 7}, .displacement = {.identity = 727, .revision = 7},
.rotation = {.identity = 733, .revision = 11}, .rotation = {.identity = 733, .revision = 11},
.bernoulliConstant = {.identity = 739, .revision = 13} .bernoulliConstant = {.identity = 739, .revision = 13}};
};
} }
mean_field::operators::context::hydrostatic::HydrostaticEquilibriumStateView make_state( mean_field::operators::context::hydrostatic::HydrostaticEquilibriumStateView
const mfem::Vector &enthalpy, make_state(const mfem::Vector &enthalpy, const mfem::Vector &gravityPotential,
const mfem::Vector &gravityPotential, const mfem::Vector &displacement) {
const mfem::Vector &displacement return {.enthalpy = enthalpy,
) {
return {
.enthalpy = enthalpy,
.gravityPotential = gravityPotential, .gravityPotential = gravityPotential,
.displacement = displacement, .displacement = displacement,
.bernoulliConstant = bernoulliConstant .bernoulliConstant = bernoulliConstant};
};
} }
mfem::Vector make_vector( mfem::Vector make_vector(const std::array<double, 3> &values) {
const std::array<
double,
3> &values
) {
mfem::Vector vector(3); mfem::Vector vector(3);
for (int component = 0; component < 3; ++component) { for (int component = 0; component < 3; ++component) {
@@ -79,22 +67,22 @@ namespace prepared_hydrostatic_analytic_solve_test_utils {
return vector; return vector;
} }
mean_field::physics::RigidRotation make_rotation(const AnalyticCase &analyticCase) { mean_field::physics::RigidRotation
make_rotation(const AnalyticCase &analyticCase) {
return mean_field::physics::RigidRotation( return mean_field::physics::RigidRotation(
make_vector(analyticCase.angularVelocity), make_vector(analyticCase.rotationCenter) make_vector(analyticCase.angularVelocity),
); make_vector(analyticCase.rotationCenter));
} }
void map_to_physical( void map_to_physical(const mfem::Vector &referencePosition,
const mfem::Vector &referencePosition,
const AnalyticCase &analyticCase, const AnalyticCase &analyticCase,
mfem::Vector &physicalPosition mfem::Vector &physicalPosition) {
) {
physicalPosition.SetSize(3); physicalPosition.SetSize(3);
for (int component = 0; component < 3; ++component) { for (int component = 0; component < 3; ++component) {
physicalPosition(component) = physicalPosition(component) =
analyticCase.deformationScale[static_cast<std::size_t>(component)] * referencePosition(component); analyticCase.deformationScale[static_cast<std::size_t>(component)] *
referencePosition(component);
} }
} }
@@ -102,7 +90,8 @@ namespace prepared_hydrostatic_analytic_solve_test_utils {
double normalizedRadiusSquared = 0.0; double normalizedRadiusSquared = 0.0;
for (int component = 0; component < 3; ++component) { for (int component = 0; component < 3; ++component) {
const double normalizedCoordinate = referencePosition(component) / mean_field::utils::RADIUS; const double normalizedCoordinate =
referencePosition(component) / mean_field::utils::RADIUS;
normalizedRadiusSquared += normalizedCoordinate * normalizedCoordinate; normalizedRadiusSquared += normalizedCoordinate * normalizedCoordinate;
} }
@@ -110,11 +99,10 @@ namespace prepared_hydrostatic_analytic_solve_test_utils {
return enthalpyAmplitude * std::max(0.0, 1.0 - normalizedRadiusSquared); return enthalpyAmplitude * std::max(0.0, 1.0 - normalizedRadiusSquared);
} }
double exact_potential_value( double
const mfem::Vector &referencePosition, exact_potential_value(const mfem::Vector &referencePosition,
const AnalyticCase &analyticCase, const AnalyticCase &analyticCase,
const mean_field::physics::RigidRotation &rotation const mean_field::physics::RigidRotation &rotation) {
) {
mfem::Vector physicalPosition; mfem::Vector physicalPosition;
map_to_physical(referencePosition, analyticCase, physicalPosition); map_to_physical(referencePosition, analyticCase, physicalPosition);
@@ -126,26 +114,27 @@ namespace prepared_hydrostatic_analytic_solve_test_utils {
* *
* analytically. * analytically.
*/ */
return bernoulliConstant + rotation.potential(physicalPosition) - exact_enthalpy_value(referencePosition); return bernoulliConstant + rotation.potential(physicalPosition) -
exact_enthalpy_value(referencePosition);
} }
mfem::Array<int> make_stellar_element_marker(const mean_field::fem::FEM &f) { mfem::Array<int> make_stellar_element_marker(const mean_field::fem::FEM &f) {
mfem::Array<int> stellarElementMarker(f.mesh->GetNE()); mfem::Array<int> stellarElementMarker(f.mesh->GetNE());
const int vacuumAttribute = f.domainMapperStateless->GetVacuumElementAttribute(); const int vacuumAttribute = field_dof_test_utils::vacuum_material_attribute;
for (int elementId = 0; elementId < f.mesh->GetNE(); ++elementId) { for (int elementId = 0; elementId < f.mesh->GetNE(); ++elementId) {
stellarElementMarker[elementId] = f.mesh->GetAttribute(elementId) != vacuumAttribute; stellarElementMarker[elementId] =
f.mesh->GetAttribute(elementId) != vacuumAttribute;
} }
return stellarElementMarker; return stellarElementMarker;
} }
} // namespace prepared_hydrostatic_analytic_solve_test_utils } // namespace prepared_hydrostatic_analytic_solve_test_utils
TEST_CASE( TEST_CASE("Prepared Hydrostatic Operator Solves Analytic Bernoulli Equilibria",
"Prepared Hydrostatic Operator Solves Analytic Bernoulli Equilibria", tags::barotrope_hydrostatic_prepared_analytic &tags::convergence
tags::barotrope_hydrostatic_prepared_analytic &tags::convergence &tags::accuracy &tags::accuracy) {
) {
using prepared_hydrostatic_analytic_solve_test_utils::AnalyticCase; using prepared_hydrostatic_analytic_solve_test_utils::AnalyticCase;
constexpr double deformationX = 1.08; constexpr double deformationX = 1.08;
@@ -171,8 +160,7 @@ TEST_CASE(
{.name = "volume-preserving deformed rotating equilibrium", {.name = "volume-preserving deformed rotating equilibrium",
.deformationScale = {deformationX, deformationY, deformationZ}, .deformationScale = {deformationX, deformationY, deformationZ},
.angularVelocity = {0.17, -0.12, 0.43}, .angularVelocity = {0.17, -0.12, 0.43},
.rotationCenter = {0.031, -0.024, 0.018}}} .rotationCenter = {0.031, -0.024, 0.018}}}};
};
auto args = test_utils::setup_args(); auto args = test_utils::setup_args();
@@ -181,51 +169,59 @@ TEST_CASE(
const MPI_Comm communicator = f.mesh->GetComm(); const MPI_Comm communicator = f.mesh->GetComm();
const mean_field::field::FieldDofMap enthalpyMap = const mean_field::field::FieldDofMap enthalpyMap =
field_dof_test_utils::make_map<mean_field::field::Enthalpy>(*f.enthalpyFes); field_dof_test_utils::make_map<mean_field::field::Enthalpy>(
*f.enthalpyFes);
const mean_field::field::FieldDofMap gravityPotentialMap = const mean_field::field::FieldDofMap gravityPotentialMap =
field_dof_test_utils::make_map<mean_field::field::Gravity>(*f.gravityPotentialFes); field_dof_test_utils::make_map<mean_field::field::Gravity>(
*f.gravityPotentialFes);
const mean_field::field::FieldDofMap displacementMap = const mean_field::field::FieldDofMap displacementMap =
field_dof_test_utils::make_map<mean_field::field::Displacement>(*f.displacementFes); field_dof_test_utils::make_map<mean_field::field::Displacement>(
*f.displacementFes);
const mfem::Array<int> stellarElementMarker = const mfem::Array<int> stellarElementMarker =
prepared_hydrostatic_analytic_solve_test_utils::make_stellar_element_marker(f); prepared_hydrostatic_analytic_solve_test_utils::
make_stellar_element_marker(f);
for (const AnalyticCase &analyticCase : analyticCases) { for (const AnalyticCase &analyticCase : analyticCases) {
DYNAMIC_SECTION(analyticCase.name) { DYNAMIC_SECTION(analyticCase.name) {
const double deformationDeterminant = const double deformationDeterminant = analyticCase.deformationScale[0] *
analyticCase.deformationScale[0] * analyticCase.deformationScale[1] * analyticCase.deformationScale[2]; analyticCase.deformationScale[1] *
analyticCase.deformationScale[2];
REQUIRE(std::abs(deformationDeterminant - 1.0) < 2.0e-14); REQUIRE(std::abs(deformationDeterminant - 1.0) < 2.0e-14);
const mean_field::physics::RigidRotation rotation = const mean_field::physics::RigidRotation rotation =
prepared_hydrostatic_analytic_solve_test_utils::make_rotation(analyticCase); prepared_hydrostatic_analytic_solve_test_utils::make_rotation(
analyticCase);
auto displacementFunction = auto displacementFunction =
[&analyticCase](const mfem::Vector &referencePosition, mfem::Vector &displacementValue) { [&analyticCase](const mfem::Vector &referencePosition,
mfem::Vector &displacementValue) {
mfem::Vector physicalPosition; mfem::Vector physicalPosition;
prepared_hydrostatic_analytic_solve_test_utils::map_to_physical( prepared_hydrostatic_analytic_solve_test_utils::map_to_physical(
referencePosition, analyticCase, physicalPosition referencePosition, analyticCase, physicalPosition);
);
displacementValue.SetSize(3); displacementValue.SetSize(3);
displacementValue = physicalPosition; displacementValue = physicalPosition;
displacementValue -= referencePosition; displacementValue -= referencePosition;
}; };
auto potentialFunction = [&analyticCase, &rotation](const mfem::Vector &referencePosition) { auto potentialFunction = [&analyticCase, &rotation](
return prepared_hydrostatic_analytic_solve_test_utils::exact_potential_value( const mfem::Vector &referencePosition) {
referencePosition, analyticCase, rotation return prepared_hydrostatic_analytic_solve_test_utils::
); exact_potential_value(referencePosition, analyticCase, rotation);
}; };
auto enthalpyFunction = [](const mfem::Vector &referencePosition) { auto enthalpyFunction = [](const mfem::Vector &referencePosition) {
return prepared_hydrostatic_analytic_solve_test_utils::exact_enthalpy_value(referencePosition); return prepared_hydrostatic_analytic_solve_test_utils::
exact_enthalpy_value(referencePosition);
}; };
mfem::VectorFunctionCoefficient displacementCoefficient(f.mesh->Dimension(), displacementFunction); mfem::VectorFunctionCoefficient displacementCoefficient(
f.mesh->Dimension(), displacementFunction);
mfem::FunctionCoefficient potentialCoefficient(potentialFunction); mfem::FunctionCoefficient potentialCoefficient(potentialFunction);
@@ -248,8 +244,10 @@ TEST_CASE(
displacementField.GetTrueDofs(displacementTrue); displacementField.GetTrueDofs(displacementTrue);
potentialField.GetTrueDofs(gravityPotentialTrue); potentialField.GetTrueDofs(gravityPotentialTrue);
const mfem::Vector displacement = displacementMap.gather(displacementTrue); const mfem::Vector displacement =
const mfem::Vector gravityPotential = gravityPotentialMap.gather(gravityPotentialTrue); displacementMap.gather(displacementTrue);
const mfem::Vector gravityPotential =
gravityPotentialMap.gather(gravityPotentialTrue);
/* /*
* This projection is not used as the solution. It gives * This projection is not used as the solution. It gives
@@ -264,15 +262,16 @@ TEST_CASE(
zeroEnthalpyField = 0.0; zeroEnthalpyField = 0.0;
const double exactEnthalpyNorm = const double exactEnthalpyNorm = zeroEnthalpyField.ComputeL2Error(
zeroEnthalpyField.ComputeL2Error(exactEnthalpyCoefficient, nullptr, &stellarElementMarker); exactEnthalpyCoefficient, nullptr, &stellarElementMarker);
const double projectionError = const double projectionError = projectedEnthalpyField.ComputeL2Error(
projectedEnthalpyField.ComputeL2Error(exactEnthalpyCoefficient, nullptr, &stellarElementMarker); exactEnthalpyCoefficient, nullptr, &stellarElementMarker);
REQUIRE(exactEnthalpyNorm > 0.0); REQUIRE(exactEnthalpyNorm > 0.0);
const double relativeProjectionError = projectionError / exactEnthalpyNorm; const double relativeProjectionError =
projectionError / exactEnthalpyNorm;
/* /*
* Begin deliberately far from equilibrium. * Begin deliberately far from equilibrium.
@@ -281,14 +280,16 @@ TEST_CASE(
enthalpy = 0.0; enthalpy = 0.0;
auto dependencies = prepared_hydrostatic_analytic_solve_test_utils::make_dependencies(); auto dependencies =
prepared_hydrostatic_analytic_solve_test_utils::make_dependencies();
mean_field::operators::PreparedHydrostaticEquilibriumOperator preparedOperator(f, *f.domainMapperStateless); mean_field::operators::PreparedHydrostaticEquilibriumOperator
preparedOperator(f, *f.domainMapperStateless);
const auto initialReport = preparedOperator.Prepare( const auto initialReport = preparedOperator.Prepare(
prepared_hydrostatic_analytic_solve_test_utils::make_state(enthalpy, gravityPotential, displacement), prepared_hydrostatic_analytic_solve_test_utils::make_state(
dependencies, rotation enthalpy, gravityPotential, displacement),
); dependencies, rotation);
REQUIRE(initialReport.preparedResidual); REQUIRE(initialReport.preparedResidual);
REQUIRE(initialReport.preparedAlgebraicJacobianBlocks); REQUIRE(initialReport.preparedAlgebraicJacobianBlocks);
@@ -297,7 +298,9 @@ TEST_CASE(
preparedOperator.BuildResidual(initialResidual); preparedOperator.BuildResidual(initialResidual);
const double initialResidualNorm = gravity_prepared_test_utils::global_norm(initialResidual, communicator); const double initialResidualNorm =
gravity_prepared_test_utils::global_norm(initialResidual,
communicator);
REQUIRE(initialResidualNorm > 1.0e-12); REQUIRE(initialResidualNorm > 1.0e-12);
@@ -311,9 +314,8 @@ TEST_CASE(
* rotation, and displacement makes this a well-defined * rotation, and displacement makes this a well-defined
* enthalpy solve. * enthalpy solve.
*/ */
prepared_hydrostatic_analytic_solve_test_utils::EnthalpyJacobianOperator enthalpyJacobian( prepared_hydrostatic_analytic_solve_test_utils::EnthalpyJacobianOperator
enthalpyMap.reduced_size(), preparedOperator enthalpyJacobian(enthalpyMap.reduced_size(), preparedOperator);
);
mfem::Vector rightHandSide(initialResidual); mfem::Vector rightHandSide(initialResidual);
rightHandSide *= -1.0; rightHandSide *= -1.0;
@@ -355,9 +357,9 @@ TEST_CASE(
++dependencies.enthalpy.revision; ++dependencies.enthalpy.revision;
const auto solvedReport = preparedOperator.Prepare( const auto solvedReport = preparedOperator.Prepare(
prepared_hydrostatic_analytic_solve_test_utils::make_state(enthalpy, gravityPotential, displacement), prepared_hydrostatic_analytic_solve_test_utils::make_state(
dependencies, rotation enthalpy, gravityPotential, displacement),
); dependencies, rotation);
CHECK(solvedReport.contextReport.updatedEnthalpy); CHECK(solvedReport.contextReport.updatedEnthalpy);
@@ -371,7 +373,9 @@ TEST_CASE(
preparedOperator.BuildResidual(solvedResidual); preparedOperator.BuildResidual(solvedResidual);
const double solvedResidualNorm = gravity_prepared_test_utils::global_norm(solvedResidual, communicator); const double solvedResidualNorm =
gravity_prepared_test_utils::global_norm(solvedResidual,
communicator);
const double residualReduction = solvedResidualNorm / initialResidualNorm; const double residualReduction = solvedResidualNorm / initialResidualNorm;
@@ -389,10 +393,11 @@ TEST_CASE(
enthalpyMap.scatter(enthalpy, enthalpyTrue); enthalpyMap.scatter(enthalpy, enthalpyTrue);
solvedEnthalpyField.SetFromTrueDofs(enthalpyTrue); solvedEnthalpyField.SetFromTrueDofs(enthalpyTrue);
const double solvedAnalyticError = const double solvedAnalyticError = solvedEnthalpyField.ComputeL2Error(
solvedEnthalpyField.ComputeL2Error(exactEnthalpyCoefficient, nullptr, &stellarElementMarker); exactEnthalpyCoefficient, nullptr, &stellarElementMarker);
const double relativeSolvedAnalyticError = solvedAnalyticError / exactEnthalpyNorm; const double relativeSolvedAnalyticError =
solvedAnalyticError / exactEnthalpyNorm;
INFO("Deformation determinant = " << deformationDeterminant); INFO("Deformation determinant = " << deformationDeterminant);
@@ -404,7 +409,8 @@ TEST_CASE(
INFO("Relative analytic projection floor = " << relativeProjectionError); INFO("Relative analytic projection floor = " << relativeProjectionError);
INFO("Relative solved analytic L2 error = " << relativeSolvedAnalyticError); INFO("Relative solved analytic L2 error = "
<< relativeSolvedAnalyticError);
/* /*
* The discrete Bernoulli equation must be solved essentially * The discrete Bernoulli equation must be solved essentially
@@ -419,7 +425,8 @@ TEST_CASE(
* also contains potential-projection and mapped-space * also contains potential-projection and mapped-space
* compatibility errors. * compatibility errors.
*/ */
CHECK(relativeSolvedAnalyticError < std::max(5.0 * relativeProjectionError, 1.25e-4)); CHECK(relativeSolvedAnalyticError <
std::max(5.0 * relativeProjectionError, 1.25e-4));
/* /*
* Record that the analytic error remains within one order of * Record that the analytic error remains within one order of

View File

@@ -24,76 +24,59 @@ namespace prepared_pressure_force_test_utils {
explicit Maps(const mean_field::fem::FEM &f) explicit Maps(const mean_field::fem::FEM &f)
: density( : density(
mean_field::field::make_field_dof_map< mean_field::field::make_field_dof_map<mean_field::field::Density,
mean_field::field::Density, DomainSchema>(*f.densityFes)),
DomainSchema>(*f.densityFes) displacement(mean_field::field::make_field_dof_map<
), mean_field::field::Displacement, DomainSchema>(
displacement( *f.displacementFes)),
mean_field::field::make_field_dof_map<
mean_field::field::Displacement,
DomainSchema>(*f.displacementFes)
),
gravityFlux( gravityFlux(
mean_field::field::make_field_dof_map< mean_field::field::make_field_dof_map<mean_field::field::Gravity,
mean_field::field::Gravity, DomainSchema>(
DomainSchema>(*f.gravityFluxFes) *f.gravityFluxFes)),
),
gravityPotential( gravityPotential(
mean_field::field::make_field_dof_map< mean_field::field::make_field_dof_map<mean_field::field::Gravity,
mean_field::field::Gravity, DomainSchema>(
DomainSchema>(*f.gravityPotentialFes) *f.gravityPotentialFes)),
), enthalpy(mean_field::field::make_field_dof_map<
enthalpy( mean_field::field::Enthalpy, DomainSchema>(*f.enthalpyFes)) {}
mean_field::field::make_field_dof_map<
mean_field::field::Enthalpy,
DomainSchema>(*f.enthalpyFes)
) {
}
}; };
[[nodiscard]] [[nodiscard]]
mfem::Vector make_positive_enthalpy_true( mfem::Vector make_positive_enthalpy_true(const mean_field::fem::FEM &f,
const mean_field::fem::FEM &f, const double phase) {
const double phase
) {
mfem::Vector enthalpy(f.enthalpyFes->GetTrueVSize()); mfem::Vector enthalpy(f.enthalpyFes->GetTrueVSize());
for (int index = 0; index < enthalpy.Size(); ++index) { for (int index = 0; index < enthalpy.Size(); ++index) {
const double position = static_cast<double>(index + 1); const double position = static_cast<double>(index + 1);
enthalpy(index) = enthalpy(index) = 0.93 + 0.09 * std::sin(0.23 * position + phase) +
0.93 + 0.09 * std::sin(0.23 * position + phase) + 0.04 * std::cos(0.17 * position - 0.5 * phase); 0.04 * std::cos(0.17 * position - 0.5 * phase);
} }
return enthalpy; return enthalpy;
} }
[[nodiscard]] [[nodiscard]]
mfem::Vector make_enthalpy_direction_true( mfem::Vector make_enthalpy_direction_true(const mean_field::fem::FEM &f,
const mean_field::fem::FEM &f, const double phase) {
const double phase
) {
mfem::Vector direction(f.enthalpyFes->GetTrueVSize()); mfem::Vector direction(f.enthalpyFes->GetTrueVSize());
for (int index = 0; index < direction.Size(); ++index) { for (int index = 0; index < direction.Size(); ++index) {
const double position = static_cast<double>(index + 1); const double position = static_cast<double>(index + 1);
direction(index) = direction(index) = 0.27 * std::sin(0.19 * position + phase) +
0.27 * std::sin(0.19 * position + phase) + 0.14 * std::cos(0.13 * position - 0.5 * phase); 0.14 * std::cos(0.13 * position - 0.5 * phase);
} }
return direction; return direction;
} }
[[nodiscard]] [[nodiscard]]
mfem::Vector make_displacement_direction_true( mfem::Vector make_displacement_direction_true(const mean_field::fem::FEM &f,
const mean_field::fem::FEM &f, const double phase) {
const double phase MFEM_VERIFY(f.mesh->Dimension() == 3,
) { "The prepared pressure-force test requires a "
MFEM_VERIFY( "three-dimensional mesh.");
f.mesh->Dimension() == 3, "The prepared pressure-force test requires a "
"three-dimensional mesh."
);
mfem::ParGridFunction directionField(f.displacementFes.get()); mfem::ParGridFunction directionField(f.displacementFes.get());
@@ -107,13 +90,15 @@ namespace prepared_pressure_force_test_utils {
value.SetSize(3); value.SetSize(3);
value(0) = 0.019 * x + 0.011 * y * z - 0.006 * z * z + 0.004 * phase * y; value(0) =
0.019 * x + 0.011 * y * z - 0.006 * z * z + 0.004 * phase * y;
value(1) = -0.016 * y + 0.008 * x * z + 0.005 * x * x - 0.003 * phase * z; value(1) =
-0.016 * y + 0.008 * x * z + 0.005 * x * x - 0.003 * phase * z;
value(2) = 0.013 * z - 0.010 * x * y + 0.006 * y * y + 0.004 * phase * x; value(2) =
} 0.013 * z - 0.010 * x * y + 0.006 * y * y + 0.004 * phase * x;
); });
directionField.ProjectCoefficient(directionCoefficient); directionField.ProjectCoefficient(directionCoefficient);
@@ -125,93 +110,95 @@ namespace prepared_pressure_force_test_utils {
} }
[[nodiscard]] [[nodiscard]]
double relative_difference( double relative_difference(const mfem::Vector &left, const mfem::Vector &right,
const mfem::Vector &left, const MPI_Comm communicator) {
const mfem::Vector &right, MFEM_VERIFY(left.Size() == right.Size(),
const MPI_Comm communicator "Cannot compare prepared pressure-force vectors with "
) { "different sizes.");
MFEM_VERIFY(
left.Size() == right.Size(), "Cannot compare prepared pressure-force vectors with "
"different sizes."
);
mfem::Vector difference(left); mfem::Vector difference(left);
difference -= right; difference -= right;
const double scale = std::max( const double scale =
{gravity_prepared_test_utils::global_norm(left, communicator), std::max({gravity_prepared_test_utils::global_norm(left, communicator),
gravity_prepared_test_utils::global_norm(right, communicator), gravity_prepared_test_utils::global_norm(right, communicator),
100.0 * std::numeric_limits<double>::epsilon()} 100.0 * std::numeric_limits<double>::epsilon()});
);
return gravity_prepared_test_utils::global_norm(difference, communicator) / scale; return gravity_prepared_test_utils::global_norm(difference, communicator) /
scale;
} }
[[nodiscard]] [[nodiscard]]
mean_field::operators::context::pressure_force::PressureForceDependencies make_dependencies() { mean_field::operators::context::pressure_force::PressureForceDependencies
return { make_dependencies() {
.discretization = {.identity = 1201, .revision = 3}, return {.discretization = {.identity = 1201, .revision = 3},
.enthalpy = {.identity = 1213, .revision = 5}, .enthalpy = {.identity = 1213, .revision = 5},
.displacement = {.identity = 1217, .revision = 7} .displacement = {.identity = 1217, .revision = 7}};
};
} }
constexpr auto densityValue = constexpr auto densityValue =
mean_field::utils::blocks::get_value_block<CoupledForm>(mean_field::utils::blocks::density_field.mass_term); mean_field::utils::blocks::get_value_block<CoupledForm>(
mean_field::utils::blocks::density_field.mass_term);
constexpr auto displacementValue = mean_field::utils::blocks::get_value_block<CoupledForm>( constexpr auto displacementValue =
mean_field::utils::blocks::displacement_field.geometry_term mean_field::utils::blocks::get_value_block<CoupledForm>(
); mean_field::utils::blocks::displacement_field.geometry_term);
constexpr auto gravityGradientValue = constexpr auto gravityGradientValue =
mean_field::utils::blocks::get_value_block<CoupledForm>(mean_field::utils::blocks::gravity_field.gradient_term); mean_field::utils::blocks::get_value_block<CoupledForm>(
mean_field::utils::blocks::gravity_field.gradient_term);
constexpr auto gravityPotentialValue = constexpr auto gravityPotentialValue =
mean_field::utils::blocks::get_value_block<CoupledForm>(mean_field::utils::blocks::gravity_field.poisson_term); mean_field::utils::blocks::get_value_block<CoupledForm>(
mean_field::utils::blocks::gravity_field.poisson_term);
constexpr auto enthalpyValue = mean_field::utils::blocks::get_value_block<CoupledForm>( constexpr auto enthalpyValue =
mean_field::utils::blocks::enthalpy_field.specific_term mean_field::utils::blocks::get_value_block<CoupledForm>(
); mean_field::utils::blocks::enthalpy_field.specific_term);
constexpr auto barotropicConstantValue = mean_field::utils::blocks::get_value_block<CoupledForm>( constexpr auto barotropicConstantValue =
mean_field::utils::blocks::barotropic_constant_field.mass_normalization_term mean_field::utils::blocks::get_value_block<CoupledForm>(
); mean_field::utils::blocks::barotropic_constant_field
.mass_normalization_term);
constexpr auto gravityGradientResidual = mean_field::utils::blocks::get_residual_block<CoupledForm>( constexpr auto gravityGradientResidual =
mean_field::utils::blocks::gravity_field.gradient_term mean_field::utils::blocks::get_residual_block<CoupledForm>(
); mean_field::utils::blocks::gravity_field.gradient_term);
constexpr auto gravityPotentialResidual = mean_field::utils::blocks::get_residual_block<CoupledForm>( constexpr auto gravityPotentialResidual =
mean_field::utils::blocks::gravity_field.poisson_term mean_field::utils::blocks::get_residual_block<CoupledForm>(
); mean_field::utils::blocks::gravity_field.poisson_term);
constexpr auto densityResidual = constexpr auto densityResidual =
mean_field::utils::blocks::get_residual_block<CoupledForm>(mean_field::utils::blocks::density_field.mass_term); mean_field::utils::blocks::get_residual_block<CoupledForm>(
mean_field::utils::blocks::density_field.mass_term);
constexpr auto displacementResidual = mean_field::utils::blocks::get_residual_block<CoupledForm>( constexpr auto displacementResidual =
mean_field::utils::blocks::displacement_field.geometry_term mean_field::utils::blocks::get_residual_block<CoupledForm>(
); mean_field::utils::blocks::displacement_field.geometry_term);
constexpr auto enthalpyResidual = mean_field::utils::blocks::get_residual_block<CoupledForm>( constexpr auto enthalpyResidual =
mean_field::utils::blocks::enthalpy_field.specific_term mean_field::utils::blocks::get_residual_block<CoupledForm>(
); mean_field::utils::blocks::enthalpy_field.specific_term);
constexpr auto massResidual = mean_field::utils::blocks::get_residual_block<CoupledForm>( constexpr auto massResidual =
mean_field::utils::blocks::barotropic_constant_field.mass_normalization_term mean_field::utils::blocks::get_residual_block<CoupledForm>(
); mean_field::utils::blocks::barotropic_constant_field
.mass_normalization_term);
[[nodiscard]] [[nodiscard]]
mean_field::operators::BarotropicEquilibriumLayout make_coupled_layout(const Maps &maps) { mean_field::operators::BarotropicEquilibriumLayout
make_coupled_layout(const Maps &maps) {
const std::array<int, CoupledForm::value_block_count> valueSizes{ const std::array<int, CoupledForm::value_block_count> valueSizes{
maps.density.reduced_size(), maps.displacement.reduced_size(), maps.gravityFlux.reduced_size(), maps.density.reduced_size(), maps.displacement.reduced_size(),
maps.gravityPotential.reduced_size(), maps.enthalpy.reduced_size(), 1 maps.gravityFlux.reduced_size(), maps.gravityPotential.reduced_size(),
}; maps.enthalpy.reduced_size(), 1};
const std::array<int, CoupledForm::residual_block_count> residualSizes{ const std::array<int, CoupledForm::residual_block_count> residualSizes{
maps.gravityFlux.reduced_size(), maps.gravityPotential.reduced_size(), maps.density.reduced_size(), maps.gravityFlux.reduced_size(), maps.gravityPotential.reduced_size(),
maps.displacement.reduced_size(), maps.enthalpy.reduced_size(), 1 maps.density.reduced_size(), maps.displacement.reduced_size(),
}; maps.enthalpy.reduced_size(), 1};
return {valueSizes, residualSizes}; return {valueSizes, residualSizes};
} }
@@ -221,8 +208,7 @@ namespace prepared_pressure_force_test_utils {
mfem::Vector copy_residual_block( mfem::Vector copy_residual_block(
const mfem::Vector &action, const mfem::Vector &action,
const mean_field::operators::BarotropicEquilibriumLayout &layout, const mean_field::operators::BarotropicEquilibriumLayout &layout,
const mean_field::utils::blocks::residual_block<index> block const mean_field::utils::blocks::residual_block<index> block) {
) {
mfem::Vector result(layout.size(block)); mfem::Vector result(layout.size(block));
const int offset = layout.offset(block); const int offset = layout.offset(block);
@@ -236,9 +222,9 @@ namespace prepared_pressure_force_test_utils {
} // namespace prepared_pressure_force_test_utils } // namespace prepared_pressure_force_test_utils
TEST_CASE( TEST_CASE(
"Prepared Pressure Force Uses FieldDof Supported Dimensions And Owns Its Context", "Prepared Pressure Force Uses FieldDof Supported Dimensions And Owns Its "
tags::barotrope &tags::pressure &tags::prepared &tags::field &tags::unit "Context",
) { tags::barotrope &tags::pressure &tags::prepared &tags::field &tags::unit) {
mean_field::utils::Args args = test_utils::setup_args(); mean_field::utils::Args args = test_utils::setup_args();
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0); mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
@@ -249,7 +235,8 @@ TEST_CASE(
const mean_field::eos::Polytrope equationOfState(3.0, 0.25); const mean_field::eos::Polytrope equationOfState(3.0, 0.25);
mean_field::operators::PreparedPressureForceOperator preparedOperator(f, *f.domainMapperStateless, equationOfState); mean_field::operators::PreparedPressureForceOperator preparedOperator(
f, *f.domainMapperStateless, equationOfState);
REQUIRE(maps.enthalpy.reduced_size() < maps.enthalpy.full_size()); REQUIRE(maps.enthalpy.reduced_size() < maps.enthalpy.full_size());
@@ -257,17 +244,17 @@ TEST_CASE(
CHECK(preparedOperator.GetEnthalpySize() == maps.enthalpy.reduced_size()); CHECK(preparedOperator.GetEnthalpySize() == maps.enthalpy.reduced_size());
CHECK(preparedOperator.GetDisplacementSize() == maps.displacement.reduced_size()); CHECK(preparedOperator.GetDisplacementSize() ==
maps.displacement.reduced_size());
CHECK( CHECK(&preparedOperator.GetContext().GetPreparationStatistics() ==
&preparedOperator.GetContext().GetPreparationStatistics() == &preparedOperator.GetContextPreparationStatistics() &preparedOperator.GetContextPreparationStatistics());
);
} }
TEST_CASE( TEST_CASE("Prepared Pressure Force Jacobian Matches Full Stateless Columns "
"Prepared Pressure Force Jacobian Matches Full Stateless Columns Through FieldDof Restriction", "Through FieldDof Restriction",
tags::barotrope &tags::pressure &tags::prepared &tags::field &tags::integration &tags::accuracy tags::barotrope &tags::pressure &tags::prepared &tags::field
) { &tags::integration &tags::accuracy) {
mean_field::utils::Args args = test_utils::setup_args(); mean_field::utils::Args args = test_utils::setup_args();
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0); mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
@@ -278,86 +265,90 @@ TEST_CASE(
const mean_field::eos::Polytrope equationOfState(3.0, 0.25); const mean_field::eos::Polytrope equationOfState(3.0, 0.25);
const mfem::Vector enthalpy = const mfem::Vector enthalpy = maps.enthalpy.gather(
maps.enthalpy.gather(prepared_pressure_force_test_utils::make_positive_enthalpy_true(f, 0.47)); prepared_pressure_force_test_utils::make_positive_enthalpy_true(f, 0.47));
const mfem::Vector displacement = maps.displacement.gather(gravity_prepared_test_utils::make_displacement(f, 0.69)); const mfem::Vector displacement = maps.displacement.gather(
gravity_prepared_test_utils::make_displacement(f, 0.69));
const mfem::Vector enthalpyDirection = const mfem::Vector enthalpyDirection = maps.enthalpy.gather(
maps.enthalpy.gather(prepared_pressure_force_test_utils::make_enthalpy_direction_true(f, 0.73)); prepared_pressure_force_test_utils::make_enthalpy_direction_true(f,
0.73));
const mfem::Vector displacementDirection = const mfem::Vector displacementDirection = maps.displacement.gather(
maps.displacement.gather(prepared_pressure_force_test_utils::make_displacement_direction_true(f, 0.83)); prepared_pressure_force_test_utils::make_displacement_direction_true(
f, 0.83));
mean_field::operators::PreparedPressureForceOperator preparedOperator(f, *f.domainMapperStateless, equationOfState); mean_field::operators::PreparedPressureForceOperator preparedOperator(
f, *f.domainMapperStateless, equationOfState);
preparedOperator.Prepare( preparedOperator.Prepare(
{.enthalpy = enthalpy, .displacement = displacement}, prepared_pressure_force_test_utils::make_dependencies() {.enthalpy = enthalpy, .displacement = displacement},
); prepared_pressure_force_test_utils::make_dependencies());
const mfem::Vector enthalpyTrue = maps.enthalpy.scatter(enthalpy); const mfem::Vector enthalpyTrue = maps.enthalpy.scatter(enthalpy);
const mfem::Vector displacementTrue = maps.displacement.scatter(displacement); const mfem::Vector displacementTrue = maps.displacement.scatter(displacement);
const mfem::Vector enthalpyDirectionTrue = maps.enthalpy.scatter(enthalpyDirection); const mfem::Vector enthalpyDirectionTrue =
maps.enthalpy.scatter(enthalpyDirection);
const mfem::Vector displacementDirectionTrue = maps.displacement.scatter(displacementDirection); const mfem::Vector displacementDirectionTrue =
maps.displacement.scatter(displacementDirection);
mfem::Vector preparedEnthalpyAction; mfem::Vector preparedEnthalpyAction;
mfem::Vector kernelEnthalpyActionTrue; mfem::Vector kernelEnthalpyActionTrue;
preparedOperator.ApplyEnthalpyJacobianAction(enthalpyDirection, preparedEnthalpyAction); preparedOperator.ApplyEnthalpyJacobianAction(enthalpyDirection,
preparedEnthalpyAction);
mean_field::operators::kernels::apply_pressure_force_enthalpy_action( mean_field::operators::kernels::apply_pressure_force_enthalpy_action(
f, *f.domainMapperStateless, equationOfState, enthalpyTrue, enthalpyDirectionTrue, displacementTrue, f, *f.domainMapperStateless, equationOfState, enthalpyTrue,
kernelEnthalpyActionTrue enthalpyDirectionTrue, displacementTrue, kernelEnthalpyActionTrue);
);
const mfem::Vector kernelEnthalpyAction = maps.displacement.gather(kernelEnthalpyActionTrue); const mfem::Vector kernelEnthalpyAction =
maps.displacement.gather(kernelEnthalpyActionTrue);
CHECK( CHECK(prepared_pressure_force_test_utils::relative_difference(
prepared_pressure_force_test_utils::relative_difference( preparedEnthalpyAction, kernelEnthalpyAction, f.mesh->GetComm()) <
preparedEnthalpyAction, kernelEnthalpyAction, f.mesh->GetComm() 2.0e-12);
) < 2.0e-12
);
mfem::Vector preparedDisplacementAction; mfem::Vector preparedDisplacementAction;
mfem::Vector kernelDisplacementActionTrue; mfem::Vector kernelDisplacementActionTrue;
preparedOperator.ApplyDisplacementJacobianAction(displacementDirection, preparedDisplacementAction); preparedOperator.ApplyDisplacementJacobianAction(displacementDirection,
preparedDisplacementAction);
mean_field::operators::kernels::apply_pressure_force_displacement_action( mean_field::operators::kernels::apply_pressure_force_displacement_action(
f, *f.domainMapperStateless, equationOfState, enthalpyTrue, displacementDirectionTrue, displacementTrue, f, *f.domainMapperStateless, equationOfState, enthalpyTrue,
kernelDisplacementActionTrue displacementDirectionTrue, displacementTrue,
); kernelDisplacementActionTrue);
const mfem::Vector kernelDisplacementAction = maps.displacement.gather(kernelDisplacementActionTrue); const mfem::Vector kernelDisplacementAction =
maps.displacement.gather(kernelDisplacementActionTrue);
CHECK( CHECK(prepared_pressure_force_test_utils::relative_difference(
prepared_pressure_force_test_utils::relative_difference( preparedDisplacementAction, kernelDisplacementAction,
preparedDisplacementAction, kernelDisplacementAction, f.mesh->GetComm() f.mesh->GetComm()) < 2.0e-12);
) < 2.0e-12
);
mfem::Vector fusedAction; mfem::Vector fusedAction;
preparedOperator.ApplyCompleteJacobianAction(enthalpyDirection, displacementDirection, fusedAction); preparedOperator.ApplyCompleteJacobianAction(
enthalpyDirection, displacementDirection, fusedAction);
mfem::Vector expectedFusedAction(kernelEnthalpyAction); mfem::Vector expectedFusedAction(kernelEnthalpyAction);
expectedFusedAction += kernelDisplacementAction; expectedFusedAction += kernelDisplacementAction;
CHECK( CHECK(prepared_pressure_force_test_utils::relative_difference(
prepared_pressure_force_test_utils::relative_difference(fusedAction, expectedFusedAction, f.mesh->GetComm()) < fusedAction, expectedFusedAction, f.mesh->GetComm()) < 2.0e-12);
2.0e-12
);
} }
TEST_CASE( TEST_CASE(
"Prepared Pressure Force MFEM Adapter Routes Reduced Coupled FieldDof Blocks", "Prepared Pressure Force MFEM Adapter Routes Reduced Coupled FieldDof "
tags::barotrope &tags::pressure &tags::prepared &tags::field &tags::integration &tags::jacobian "Blocks",
&tags::mfem_operators &tags::unit tags::barotrope &tags::pressure &tags::prepared &tags::field
) { &tags::integration &tags::jacobian &tags::mfem_operators &tags::unit) {
mean_field::utils::Args args = test_utils::setup_args(); mean_field::utils::Args args = test_utils::setup_args();
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0); mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
@@ -368,31 +359,38 @@ TEST_CASE(
const mean_field::eos::Polytrope equationOfState(3.0, 0.25); const mean_field::eos::Polytrope equationOfState(3.0, 0.25);
const mfem::Vector enthalpy = const mfem::Vector enthalpy = maps.enthalpy.gather(
maps.enthalpy.gather(prepared_pressure_force_test_utils::make_positive_enthalpy_true(f, 0.53)); prepared_pressure_force_test_utils::make_positive_enthalpy_true(f, 0.53));
const mfem::Vector displacement = maps.displacement.gather(gravity_prepared_test_utils::make_displacement(f, 0.71)); const mfem::Vector displacement = maps.displacement.gather(
gravity_prepared_test_utils::make_displacement(f, 0.71));
const mfem::Vector enthalpyDirection = const mfem::Vector enthalpyDirection = maps.enthalpy.gather(
maps.enthalpy.gather(prepared_pressure_force_test_utils::make_enthalpy_direction_true(f, 0.89)); prepared_pressure_force_test_utils::make_enthalpy_direction_true(f,
0.89));
const mfem::Vector displacementDirection = const mfem::Vector displacementDirection = maps.displacement.gather(
maps.displacement.gather(prepared_pressure_force_test_utils::make_displacement_direction_true(f, 0.97)); prepared_pressure_force_test_utils::make_displacement_direction_true(
f, 0.97));
mean_field::operators::PreparedPressureForceOperator preparedOperator(f, *f.domainMapperStateless, equationOfState); mean_field::operators::PreparedPressureForceOperator preparedOperator(
f, *f.domainMapperStateless, equationOfState);
preparedOperator.Prepare( preparedOperator.Prepare(
{.enthalpy = enthalpy, .displacement = displacement}, prepared_pressure_force_test_utils::make_dependencies() {.enthalpy = enthalpy, .displacement = displacement},
); prepared_pressure_force_test_utils::make_dependencies());
const mean_field::operators::BarotropicEquilibriumLayout layout = const mean_field::operators::BarotropicEquilibriumLayout layout =
prepared_pressure_force_test_utils::make_coupled_layout(maps); prepared_pressure_force_test_utils::make_coupled_layout(maps);
mean_field::operators::PreparedPressureForceJacobianOperator adapter(layout, preparedOperator); mean_field::operators::PreparedPressureForceJacobianOperator adapter(
layout, preparedOperator);
CHECK(layout.size(prepared_pressure_force_test_utils::enthalpyValue) == maps.enthalpy.reduced_size()); CHECK(layout.size(prepared_pressure_force_test_utils::enthalpyValue) ==
maps.enthalpy.reduced_size());
CHECK(layout.size(prepared_pressure_force_test_utils::densityValue) == maps.density.reduced_size()); CHECK(layout.size(prepared_pressure_force_test_utils::densityValue) ==
maps.density.reduced_size());
mfem::BlockVector direction(layout.value_offsets()); mfem::BlockVector direction(layout.value_offsets());
@@ -403,74 +401,66 @@ TEST_CASE(
*/ */
direction.GetBlock(prepared_pressure_force_test_utils::densityValue) = 0.37; direction.GetBlock(prepared_pressure_force_test_utils::densityValue) = 0.37;
direction.GetBlock(prepared_pressure_force_test_utils::gravityGradientValue) = -0.41; direction.GetBlock(prepared_pressure_force_test_utils::gravityGradientValue) =
-0.41;
direction.GetBlock(prepared_pressure_force_test_utils::gravityPotentialValue) = 0.59; direction.GetBlock(
prepared_pressure_force_test_utils::gravityPotentialValue) = 0.59;
direction.GetBlock(prepared_pressure_force_test_utils::barotropicConstantValue) = -0.73; direction.GetBlock(
prepared_pressure_force_test_utils::barotropicConstantValue) = -0.73;
direction.GetBlock(prepared_pressure_force_test_utils::displacementValue) = displacementDirection; direction.GetBlock(prepared_pressure_force_test_utils::displacementValue) =
displacementDirection;
direction.GetBlock(prepared_pressure_force_test_utils::enthalpyValue) = enthalpyDirection; direction.GetBlock(prepared_pressure_force_test_utils::enthalpyValue) =
enthalpyDirection;
mfem::Vector expectedDisplacementAction; mfem::Vector expectedDisplacementAction;
preparedOperator.ApplyCompleteJacobianAction(enthalpyDirection, displacementDirection, expectedDisplacementAction); preparedOperator.ApplyCompleteJacobianAction(
enthalpyDirection, displacementDirection, expectedDisplacementAction);
mfem::Vector action; mfem::Vector action;
adapter.Mult(direction, action); adapter.Mult(direction, action);
const mfem::Vector displacementResidualAction = prepared_pressure_force_test_utils::copy_residual_block( const mfem::Vector displacementResidualAction =
action, layout, prepared_pressure_force_test_utils::displacementResidual prepared_pressure_force_test_utils::copy_residual_block(
); action, layout,
prepared_pressure_force_test_utils::displacementResidual);
CHECK( CHECK(prepared_pressure_force_test_utils::relative_difference(
prepared_pressure_force_test_utils::relative_difference( displacementResidualAction, expectedDisplacementAction,
displacementResidualAction, expectedDisplacementAction, f.mesh->GetComm() f.mesh->GetComm()) < 2.0e-14);
) < 2.0e-14
); CHECK(prepared_pressure_force_test_utils::copy_residual_block(
action, layout,
prepared_pressure_force_test_utils::gravityGradientResidual)
.Norml2() == 0.0);
CHECK(prepared_pressure_force_test_utils::copy_residual_block(
action, layout,
prepared_pressure_force_test_utils::gravityPotentialResidual)
.Norml2() == 0.0);
CHECK(prepared_pressure_force_test_utils::copy_residual_block(
action, layout, prepared_pressure_force_test_utils::densityResidual)
.Norml2() == 0.0);
CHECK( CHECK(
prepared_pressure_force_test_utils::copy_residual_block( prepared_pressure_force_test_utils::copy_residual_block(
action, layout, prepared_pressure_force_test_utils::gravityGradientResidual action, layout, prepared_pressure_force_test_utils::enthalpyResidual)
) .Norml2() == 0.0);
.Norml2() == 0.0
);
CHECK( CHECK(prepared_pressure_force_test_utils::copy_residual_block(
prepared_pressure_force_test_utils::copy_residual_block( action, layout, prepared_pressure_force_test_utils::massResidual)
action, layout, prepared_pressure_force_test_utils::gravityPotentialResidual .Norml2() == 0.0);
)
.Norml2() == 0.0
);
CHECK(
prepared_pressure_force_test_utils::copy_residual_block(
action, layout, prepared_pressure_force_test_utils::densityResidual
)
.Norml2() == 0.0
);
CHECK(
prepared_pressure_force_test_utils::copy_residual_block(
action, layout, prepared_pressure_force_test_utils::enthalpyResidual
)
.Norml2() == 0.0
);
CHECK(
prepared_pressure_force_test_utils::copy_residual_block(
action, layout, prepared_pressure_force_test_utils::massResidual
)
.Norml2() == 0.0
);
} }
TEST_CASE( TEST_CASE("Pressure Force Residual Converges To A Manufactured Analytic Force",
"Pressure Force Residual Converges To A Manufactured Analytic Force", tags::barotrope &tags::pressure &tags::kernels &tags::integration
tags::barotrope &tags::pressure &tags::kernels &tags::integration &tags::convergence &tags::h_refinement &tags::convergence &tags::h_refinement &tags::analytic_comparison
&tags::analytic_comparison &tags::accuracy &tags::accuracy) {
) {
constexpr int dimension = 3; constexpr int dimension = 3;
constexpr std::array<int, 2> refinementLevels{0, 1}; constexpr std::array<int, 2> refinementLevels{0, 1};
@@ -484,10 +474,12 @@ TEST_CASE(
std::array<double, refinementLevels.size()> relativeErrors{}; std::array<double, refinementLevels.size()> relativeErrors{};
for (std::size_t levelIndex = 0; levelIndex < refinementLevels.size(); ++levelIndex) { for (std::size_t levelIndex = 0; levelIndex < refinementLevels.size();
++levelIndex) {
mean_field::utils::Args args = test_utils::setup_args(); mean_field::utils::Args args = test_utils::setup_args();
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, refinementLevels[levelIndex]); mean_field::fem::FEM f = mean_field::fem::setup_fem(
args.mesh_file, args, refinementLevels[levelIndex]);
REQUIRE(f.okay()); REQUIRE(f.okay());
REQUIRE(f.mesh->Dimension() == dimension); REQUIRE(f.mesh->Dimension() == dimension);
@@ -495,12 +487,15 @@ TEST_CASE(
const MPI_Comm communicator = f.mesh->GetComm(); const MPI_Comm communicator = f.mesh->GetComm();
constexpr double supportRadius = supportRadiusFraction * mean_field::utils::RADIUS; constexpr double supportRadius =
supportRadiusFraction * mean_field::utils::RADIUS;
constexpr double supportRadiusSquared = supportRadius * supportRadius; constexpr double supportRadiusSquared = supportRadius * supportRadius;
auto analyticEnthalpyFunction = [supportRadiusSquared](const mfem::Vector &position) { auto analyticEnthalpyFunction = [supportRadiusSquared](
const double normalizedRadiusSquared = (position * position) / supportRadiusSquared; const mfem::Vector &position) {
const double normalizedRadiusSquared =
(position * position) / supportRadiusSquared;
if (normalizedRadiusSquared >= 1.0) { if (normalizedRadiusSquared >= 1.0) {
return 0.0; return 0.0;
@@ -508,14 +503,18 @@ TEST_CASE(
const double distanceToSupportBoundary = 1.0 - normalizedRadiusSquared; const double distanceToSupportBoundary = 1.0 - normalizedRadiusSquared;
return amplitude * std::exp(-bumpSharpness * normalizedRadiusSquared / distanceToSupportBoundary); return amplitude * std::exp(-bumpSharpness * normalizedRadiusSquared /
distanceToSupportBoundary);
}; };
auto analyticPressureForceFunction = [supportRadiusSquared](const mfem::Vector &position, mfem::Vector &force) { auto analyticPressureForceFunction = [supportRadiusSquared](
const mfem::Vector &position,
mfem::Vector &force) {
force.SetSize(dimension); force.SetSize(dimension);
force = 0.0; force = 0.0;
const double normalizedRadiusSquared = (position * position) / supportRadiusSquared; const double normalizedRadiusSquared =
(position * position) / supportRadiusSquared;
if (normalizedRadiusSquared >= 1.0) { if (normalizedRadiusSquared >= 1.0) {
return; return;
@@ -524,20 +523,24 @@ TEST_CASE(
const double distanceToSupportBoundary = 1.0 - normalizedRadiusSquared; const double distanceToSupportBoundary = 1.0 - normalizedRadiusSquared;
const double enthalpy = const double enthalpy =
amplitude * std::exp(-bumpSharpness * normalizedRadiusSquared / distanceToSupportBoundary); amplitude * std::exp(-bumpSharpness * normalizedRadiusSquared /
distanceToSupportBoundary);
const double pressureGradientScale = const double pressureGradientScale =
-2.0 * bumpSharpness * std::pow(enthalpy, 4.0) / -2.0 * bumpSharpness * std::pow(enthalpy, 4.0) /
(supportRadiusSquared * distanceToSupportBoundary * distanceToSupportBoundary); (supportRadiusSquared * distanceToSupportBoundary *
distanceToSupportBoundary);
for (int component = 0; component < dimension; ++component) { for (int component = 0; component < dimension; ++component) {
force(component) = pressureGradientScale * position(component); force(component) = pressureGradientScale * position(component);
} }
}; };
mfem::FunctionCoefficient analyticEnthalpyCoefficient(analyticEnthalpyFunction); mfem::FunctionCoefficient analyticEnthalpyCoefficient(
analyticEnthalpyFunction);
mfem::VectorFunctionCoefficient analyticPressureForceCoefficient(dimension, analyticPressureForceFunction); mfem::VectorFunctionCoefficient analyticPressureForceCoefficient(
dimension, analyticPressureForceFunction);
mfem::ParGridFunction discreteEnthalpyField(f.enthalpyFes.get()); mfem::ParGridFunction discreteEnthalpyField(f.enthalpyFes.get());
@@ -555,8 +558,8 @@ TEST_CASE(
mfem::Vector discreteResidual; mfem::Vector discreteResidual;
mean_field::operators::kernels::apply_pressure_force_residual( mean_field::operators::kernels::apply_pressure_force_residual(
f, *f.domainMapperStateless, barotrope, discreteEnthalpyTrue, zeroDisplacement, discreteResidual f, *f.domainMapperStateless, barotrope, discreteEnthalpyTrue,
); zeroDisplacement, discreteResidual);
REQUIRE(discreteResidual.Size() == f.displacementFes->GetTrueVSize()); REQUIRE(discreteResidual.Size() == f.displacementFes->GetTrueVSize());
@@ -564,9 +567,10 @@ TEST_CASE(
stellarMarker = 0; stellarMarker = 0;
const int vacuumAttribute = f.domainMapperStateless->GetVacuumElementAttribute(); const int vacuumAttribute = field_dof_test_utils::vacuum_material_attribute;
for (int attributeIndex = 0; attributeIndex < f.mesh->attributes.Size(); ++attributeIndex) { for (int attributeIndex = 0; attributeIndex < f.mesh->attributes.Size();
++attributeIndex) {
const int attribute = f.mesh->attributes[attributeIndex]; const int attribute = f.mesh->attributes[attributeIndex];
if (attribute != vacuumAttribute) { if (attribute != vacuumAttribute) {
@@ -574,28 +578,34 @@ TEST_CASE(
} }
} }
const mfem::Geometry::Type elementGeometry = f.displacementFes->GetFE(0)->GetGeomType(); const mfem::Geometry::Type elementGeometry =
f.displacementFes->GetFE(0)->GetGeomType();
for (int element = 1; element < f.mesh->GetNE(); ++element) { for (int element = 1; element < f.mesh->GetNE(); ++element) {
REQUIRE(f.displacementFes->GetFE(element)->GetGeomType() == elementGeometry); REQUIRE(f.displacementFes->GetFE(element)->GetGeomType() ==
elementGeometry);
} }
const int referenceQuadratureOrder = 2 * f.displacementFes->GetMaxElementOrder() + 16; const int referenceQuadratureOrder =
2 * f.displacementFes->GetMaxElementOrder() + 16;
const mfem::IntegrationRule &referenceQuadrature = const mfem::IntegrationRule &referenceQuadrature =
mfem::IntRules.Get(elementGeometry, referenceQuadratureOrder); mfem::IntRules.Get(elementGeometry, referenceQuadratureOrder);
auto *analyticForceIntegrator = new mfem::VectorDomainLFIntegrator(analyticPressureForceCoefficient); auto *analyticForceIntegrator =
new mfem::VectorDomainLFIntegrator(analyticPressureForceCoefficient);
analyticForceIntegrator->SetIntRule(&referenceQuadrature); analyticForceIntegrator->SetIntRule(&referenceQuadrature);
mfem::ParLinearForm analyticForceLoad(f.displacementFes.get()); mfem::ParLinearForm analyticForceLoad(f.displacementFes.get());
analyticForceLoad.AddDomainIntegrator(analyticForceIntegrator, stellarMarker); analyticForceLoad.AddDomainIntegrator(analyticForceIntegrator,
stellarMarker);
analyticForceLoad.Assemble(); analyticForceLoad.Assemble();
std::unique_ptr<mfem::HypreParVector> analyticForceHypreVector(analyticForceLoad.ParallelAssemble()); std::unique_ptr<mfem::HypreParVector> analyticForceHypreVector(
analyticForceLoad.ParallelAssemble());
REQUIRE(analyticForceHypreVector != nullptr); REQUIRE(analyticForceHypreVector != nullptr);
@@ -603,7 +613,8 @@ TEST_CASE(
REQUIRE(analyticForceTrue.Size() == discreteResidual.Size()); REQUIRE(analyticForceTrue.Size() == discreteResidual.Size());
const double analyticForceNorm = gravity_prepared_test_utils::global_norm(analyticForceTrue, communicator); const double analyticForceNorm = gravity_prepared_test_utils::global_norm(
analyticForceTrue, communicator);
REQUIRE(std::isfinite(analyticForceNorm)); REQUIRE(std::isfinite(analyticForceNorm));
REQUIRE(analyticForceNorm > 0.0); REQUIRE(analyticForceNorm > 0.0);
@@ -620,7 +631,8 @@ TEST_CASE(
rieszForm.Assemble(); rieszForm.Assemble();
rieszForm.Finalize(); rieszForm.Finalize();
std::unique_ptr<mfem::HypreParMatrix> rieszMatrix(rieszForm.ParallelAssemble()); std::unique_ptr<mfem::HypreParMatrix> rieszMatrix(
rieszForm.ParallelAssemble());
REQUIRE(rieszMatrix != nullptr); REQUIRE(rieszMatrix != nullptr);
REQUIRE(rieszMatrix->Height() == discreteResidual.Size()); REQUIRE(rieszMatrix->Height() == discreteResidual.Size());
@@ -637,30 +649,30 @@ TEST_CASE(
rieszSolver.SetRelTol(1.0e-13); rieszSolver.SetRelTol(1.0e-13);
rieszSolver.SetAbsTol(1.0e-15); rieszSolver.SetAbsTol(1.0e-15);
rieszSolver.SetMaxIter(5000); rieszSolver.SetMaxIter(5000);
rieszSolver.SetPrintLevel(1); rieszSolver.SetPrintLevel(0);
auto calculateDualNorm = [&rieszSolver, communicator](const mfem::Vector &functional) { auto calculateDualNorm = [&rieszSolver,
communicator](const mfem::Vector &functional) {
mfem::Vector rieszRepresentative(functional.Size()); mfem::Vector rieszRepresentative(functional.Size());
rieszRepresentative = 0.0; rieszRepresentative = 0.0;
rieszSolver.Mult(functional, rieszRepresentative); rieszSolver.Mult(functional, rieszRepresentative);
MFEM_VERIFY( MFEM_VERIFY(rieszSolver.GetConverged(),
rieszSolver.GetConverged(), "The pressure-force convergence-test Riesz solve " "The pressure-force convergence-test Riesz solve "
"did not converge." "did not converge.");
);
const double dualNormSquared = const double dualNormSquared = gravity_prepared_test_utils::global_dot(
gravity_prepared_test_utils::global_dot(functional, rieszRepresentative, communicator); functional, rieszRepresentative, communicator);
MFEM_VERIFY(std::isfinite(dualNormSquared), "The pressure-force dual norm is not finite."); MFEM_VERIFY(std::isfinite(dualNormSquared),
"The pressure-force dual norm is not finite.");
MFEM_VERIFY( MFEM_VERIFY(dualNormSquared >=
dualNormSquared >= -100.0 * std::numeric_limits<double>::epsilon(), -100.0 * std::numeric_limits<double>::epsilon(),
"The pressure-force Riesz operator produced a " "The pressure-force Riesz operator produced a "
"negative dual norm." "negative dual norm.");
);
return std::sqrt(std::max(dualNormSquared, 0.0)); return std::sqrt(std::max(dualNormSquared, 0.0));
}; };
@@ -688,9 +700,12 @@ TEST_CASE(
REQUIRE(relativeError > 0.0); REQUIRE(relativeError > 0.0);
} }
static_assert(refinementLevels.size() == 2, "This reduced convergence test expects exactly two refinement levels."); static_assert(
refinementLevels.size() == 2,
"This reduced convergence test expects exactly two refinement levels.");
const double observedRate = std::log(relativeErrors[0] / relativeErrors[1]) / std::log(2.0); const double observedRate =
std::log(relativeErrors[0] / relativeErrors[1]) / std::log(2.0);
INFO("Level 0 pressure-force relative dual error = " << relativeErrors[0]); INFO("Level 0 pressure-force relative dual error = " << relativeErrors[0]);

View File

@@ -14,82 +14,94 @@ namespace rotational_displacement_force_test_utils {
using CoupledForm = mean_field::utils::blocks::barotropic_equilibrium_form; using CoupledForm = mean_field::utils::blocks::barotropic_equilibrium_form;
constexpr auto densityValue = constexpr auto densityValue =
mean_field::utils::blocks::get_value_block<CoupledForm>(mean_field::utils::blocks::density_field.mass_term); mean_field::utils::blocks::get_value_block<CoupledForm>(
mean_field::utils::blocks::density_field.mass_term);
constexpr auto displacementValue = mean_field::utils::blocks::get_value_block<CoupledForm>( constexpr auto displacementValue =
mean_field::utils::blocks::displacement_field.geometry_term mean_field::utils::blocks::get_value_block<CoupledForm>(
); mean_field::utils::blocks::displacement_field.geometry_term);
constexpr auto gravityGradientValue = constexpr auto gravityGradientValue =
mean_field::utils::blocks::get_value_block<CoupledForm>(mean_field::utils::blocks::gravity_field.gradient_term); mean_field::utils::blocks::get_value_block<CoupledForm>(
mean_field::utils::blocks::gravity_field.gradient_term);
constexpr auto gravityPotentialValue = constexpr auto gravityPotentialValue =
mean_field::utils::blocks::get_value_block<CoupledForm>(mean_field::utils::blocks::gravity_field.poisson_term); mean_field::utils::blocks::get_value_block<CoupledForm>(
mean_field::utils::blocks::gravity_field.poisson_term);
constexpr auto enthalpyValue = mean_field::utils::blocks::get_value_block<CoupledForm>( constexpr auto enthalpyValue =
mean_field::utils::blocks::enthalpy_field.specific_term mean_field::utils::blocks::get_value_block<CoupledForm>(
); mean_field::utils::blocks::enthalpy_field.specific_term);
constexpr auto barotropicConstantValue = mean_field::utils::blocks::get_value_block<CoupledForm>( constexpr auto barotropicConstantValue =
mean_field::utils::blocks::barotropic_constant_field.mass_normalization_term mean_field::utils::blocks::get_value_block<CoupledForm>(
); mean_field::utils::blocks::barotropic_constant_field
.mass_normalization_term);
constexpr auto gravityGradientResidual = mean_field::utils::blocks::get_residual_block<CoupledForm>( constexpr auto gravityGradientResidual =
mean_field::utils::blocks::gravity_field.gradient_term mean_field::utils::blocks::get_residual_block<CoupledForm>(
); mean_field::utils::blocks::gravity_field.gradient_term);
constexpr auto gravityPotentialResidual = mean_field::utils::blocks::get_residual_block<CoupledForm>( constexpr auto gravityPotentialResidual =
mean_field::utils::blocks::gravity_field.poisson_term mean_field::utils::blocks::get_residual_block<CoupledForm>(
); mean_field::utils::blocks::gravity_field.poisson_term);
constexpr auto densityResidual = constexpr auto densityResidual =
mean_field::utils::blocks::get_residual_block<CoupledForm>(mean_field::utils::blocks::density_field.mass_term); mean_field::utils::blocks::get_residual_block<CoupledForm>(
mean_field::utils::blocks::density_field.mass_term);
constexpr auto displacementResidual = mean_field::utils::blocks::get_residual_block<CoupledForm>( constexpr auto displacementResidual =
mean_field::utils::blocks::displacement_field.geometry_term mean_field::utils::blocks::get_residual_block<CoupledForm>(
); mean_field::utils::blocks::displacement_field.geometry_term);
constexpr auto enthalpyResidual = mean_field::utils::blocks::get_residual_block<CoupledForm>( constexpr auto enthalpyResidual =
mean_field::utils::blocks::enthalpy_field.specific_term mean_field::utils::blocks::get_residual_block<CoupledForm>(
); mean_field::utils::blocks::enthalpy_field.specific_term);
constexpr auto massResidual = mean_field::utils::blocks::get_residual_block<CoupledForm>( constexpr auto massResidual =
mean_field::utils::blocks::barotropic_constant_field.mass_normalization_term mean_field::utils::blocks::get_residual_block<CoupledForm>(
); mean_field::utils::blocks::barotropic_constant_field
.mass_normalization_term);
[[nodiscard]] mean_field::operators::RotationalDisplacementForceLayout make_layout(const mean_field::fem::FEM &f) { [[nodiscard]] mean_field::operators::RotationalDisplacementForceLayout
make_layout(const mean_field::fem::FEM &f) {
using DomainSchema = gravity_prepared_test_utils::DomainSchema; using DomainSchema = gravity_prepared_test_utils::DomainSchema;
const auto densityMap = gravity_prepared_test_utils::make_field_map<mean_field::field::Density>(f); const auto densityMap =
const auto displacementMap = gravity_prepared_test_utils::make_field_map<mean_field::field::Displacement>(f); gravity_prepared_test_utils::make_field_map<mean_field::field::Density>(
f);
const auto displacementMap = gravity_prepared_test_utils::make_field_map<
mean_field::field::Displacement>(f);
const auto gravityFluxMap = const auto gravityFluxMap =
mean_field::field::make_field_dof_map<mean_field::field::Gravity, DomainSchema>(*f.gravityFluxFes); mean_field::field::make_field_dof_map<mean_field::field::Gravity,
const auto gravityPotentialMap = DomainSchema>(*f.gravityFluxFes);
mean_field::field::make_field_dof_map<mean_field::field::Gravity, DomainSchema>(*f.gravityPotentialFes); const auto gravityPotentialMap = mean_field::field::make_field_dof_map<
mean_field::field::Gravity, DomainSchema>(*f.gravityPotentialFes);
const auto enthalpyMap = const auto enthalpyMap =
mean_field::field::make_field_dof_map<mean_field::field::Enthalpy, DomainSchema>(*f.enthalpyFes); mean_field::field::make_field_dof_map<mean_field::field::Enthalpy,
DomainSchema>(*f.enthalpyFes);
const std::array<int, CoupledForm::value_block_count> valueSizes{ const std::array<int, CoupledForm::value_block_count> valueSizes{
densityMap.reduced_size(), displacementMap.reduced_size(), gravityFluxMap.reduced_size(), densityMap.reduced_size(), displacementMap.reduced_size(),
gravityPotentialMap.reduced_size(), enthalpyMap.reduced_size(), 1 gravityFluxMap.reduced_size(), gravityPotentialMap.reduced_size(),
}; enthalpyMap.reduced_size(), 1};
const std::array<int, CoupledForm::residual_block_count> residualSizes{ const std::array<int, CoupledForm::residual_block_count> residualSizes{
gravityFluxMap.reduced_size(), gravityPotentialMap.reduced_size(), densityMap.reduced_size(), gravityFluxMap.reduced_size(), gravityPotentialMap.reduced_size(),
displacementMap.reduced_size(), enthalpyMap.reduced_size(), 1 densityMap.reduced_size(), displacementMap.reduced_size(),
}; enthalpyMap.reduced_size(), 1};
return {valueSizes, residualSizes}; return {valueSizes, residualSizes};
} }
[[nodiscard]] mfem::Vector make_density( [[nodiscard]] mfem::Vector make_density(const mean_field::fem::FEM &f,
const mean_field::fem::FEM &f, const double phase) {
const double phase
) {
mfem::ParGridFunction densityField(f.densityFes.get()); mfem::ParGridFunction densityField(f.densityFes.get());
mfem::FunctionCoefficient densityCoefficient([phase](const mfem::Vector &position) { mfem::FunctionCoefficient densityCoefficient(
return 0.88 + 0.06 * std::sin(0.7 * position(0) + phase) + 0.04 * std::cos(0.6 * position(1) - phase) + [phase](const mfem::Vector &position) {
return 0.88 + 0.06 * std::sin(0.7 * position(0) + phase) +
0.04 * std::cos(0.6 * position(1) - phase) +
0.025 * position(2) * position(2); 0.025 * position(2) * position(2);
}); });
@@ -100,15 +112,14 @@ namespace rotational_displacement_force_test_utils {
return densityTrue; return densityTrue;
} }
[[nodiscard]] mfem::Vector make_density_direction( [[nodiscard]] mfem::Vector make_density_direction(const mean_field::fem::FEM &f,
const mean_field::fem::FEM &f, const double phase) {
const double phase
) {
mfem::ParGridFunction densityField(f.densityFes.get()); mfem::ParGridFunction densityField(f.densityFes.get());
mfem::FunctionCoefficient densityCoefficient([phase](const mfem::Vector &position) { mfem::FunctionCoefficient densityCoefficient(
return 0.17 * std::sin(0.9 * position(0) + phase) - 0.12 * std::cos(0.8 * position(1) - phase) + [phase](const mfem::Vector &position) {
0.07 * position(2); return 0.17 * std::sin(0.9 * position(0) + phase) -
0.12 * std::cos(0.8 * position(1) - phase) + 0.07 * position(2);
}); });
densityField.ProjectCoefficient(densityCoefficient); densityField.ProjectCoefficient(densityCoefficient);
@@ -118,16 +129,20 @@ namespace rotational_displacement_force_test_utils {
return densityTrue; return densityTrue;
} }
[[nodiscard]] mfem::Vector make_displacement_direction(const mean_field::fem::FEM &f) { [[nodiscard]] mfem::Vector
mfem::Vector direction = gravity_prepared_test_utils::make_displacement(f, 0.91); make_displacement_direction(const mean_field::fem::FEM &f) {
mfem::Vector direction =
gravity_prepared_test_utils::make_displacement(f, 0.91);
const mfem::Vector second = gravity_prepared_test_utils::make_displacement(f, 0.27); const mfem::Vector second =
gravity_prepared_test_utils::make_displacement(f, 0.27);
direction -= second; direction -= second;
return direction; return direction;
} }
[[nodiscard]] mean_field::physics::RigidRotation make_rotation(const double scale = 1.0) { [[nodiscard]] mean_field::physics::RigidRotation
make_rotation(const double scale = 1.0) {
mfem::Vector angularVelocity(3); mfem::Vector angularVelocity(3);
angularVelocity(0) = scale * 0.17; angularVelocity(0) = scale * 0.17;
angularVelocity(1) = scale * -0.09; angularVelocity(1) = scale * -0.09;
@@ -141,27 +156,28 @@ namespace rotational_displacement_force_test_utils {
return mean_field::physics::RigidRotation(angularVelocity, center); return mean_field::physics::RigidRotation(angularVelocity, center);
} }
[[nodiscard]] mean_field::operators::context::rotational_displacement_force::RotationalDisplacementForceDependencies [[nodiscard]] mean_field::operators::context::rotational_displacement_force::
RotationalDisplacementForceDependencies
make_dependencies() { make_dependencies() {
return { return {.discretization = {.identity = 211, .revision = 3},
.discretization = {.identity = 211, .revision = 3},
.density = {.identity = 223, .revision = 5}, .density = {.identity = 223, .revision = 5},
.displacement = {.identity = 227, .revision = 7}, .displacement = {.identity = 227, .revision = 7},
.rotation = {.identity = 229, .revision = 11} .rotation = {.identity = 229, .revision = 11}};
};
} }
[[nodiscard]] mfem::Vector make_vacuum_only_density(const mean_field::fem::FEM &f) { [[nodiscard]] mfem::Vector
make_vacuum_only_density(const mean_field::fem::FEM &f) {
mfem::ParGridFunction densityField(f.densityFes.get()); mfem::ParGridFunction densityField(f.densityFes.get());
densityField = 0.0; densityField = 0.0;
const int vacuumAttribute = f.domainMapperStateless->GetVacuumElementAttribute(); const int vacuumAttribute = field_dof_test_utils::vacuum_material_attribute;
mfem::Array<int> densityDofs; mfem::Array<int> densityDofs;
int localVacuumElements = 0; int localVacuumElements = 0;
for (int elementId = 0; elementId < f.mesh->GetNE(); ++elementId) { for (int elementId = 0; elementId < f.mesh->GetNE(); ++elementId) {
mfem::ElementTransformation *transformation = f.mesh->GetElementTransformation(elementId); mfem::ElementTransformation *transformation =
f.mesh->GetElementTransformation(elementId);
REQUIRE(transformation != nullptr); REQUIRE(transformation != nullptr);
@@ -179,7 +195,8 @@ namespace rotational_displacement_force_test_utils {
int globalVacuumElements = 0; int globalVacuumElements = 0;
MPI_Allreduce(&localVacuumElements, &globalVacuumElements, 1, MPI_INT, MPI_SUM, f.mesh->GetComm()); MPI_Allreduce(&localVacuumElements, &globalVacuumElements, 1, MPI_INT,
MPI_SUM, f.mesh->GetComm());
REQUIRE(globalVacuumElements > 0); REQUIRE(globalVacuumElements > 0);
@@ -188,23 +205,20 @@ namespace rotational_displacement_force_test_utils {
return densityTrue; return densityTrue;
} }
[[nodiscard]] double global_norm( [[nodiscard]] double global_norm(const mfem::Vector &vector,
const mfem::Vector &vector, MPI_Comm communicator) {
MPI_Comm communicator
) {
const double localSquaredNorm = vector * vector; const double localSquaredNorm = vector * vector;
double globalSquaredNorm = 0.0; double globalSquaredNorm = 0.0;
MPI_Allreduce(&localSquaredNorm, &globalSquaredNorm, 1, MPI_DOUBLE, MPI_SUM, communicator); MPI_Allreduce(&localSquaredNorm, &globalSquaredNorm, 1, MPI_DOUBLE, MPI_SUM,
communicator);
return std::sqrt(globalSquaredNorm); return std::sqrt(globalSquaredNorm);
} }
[[nodiscard]] double global_dot( [[nodiscard]] double global_dot(const mfem::Vector &left,
const mfem::Vector &left,
const mfem::Vector &right, const mfem::Vector &right,
MPI_Comm communicator MPI_Comm communicator) {
) {
REQUIRE(left.Size() == right.Size()); REQUIRE(left.Size() == right.Size());
const double localDot = left * right; const double localDot = left * right;
@@ -215,29 +229,25 @@ namespace rotational_displacement_force_test_utils {
return globalDot; return globalDot;
} }
[[nodiscard]] double relative_difference( [[nodiscard]] double relative_difference(const mfem::Vector &computed,
const mfem::Vector &computed,
const mfem::Vector &reference, const mfem::Vector &reference,
MPI_Comm communicator MPI_Comm communicator) {
) {
REQUIRE(computed.Size() == reference.Size()); REQUIRE(computed.Size() == reference.Size());
mfem::Vector difference(computed); mfem::Vector difference(computed);
difference -= reference; difference -= reference;
return global_norm(difference, communicator) / return global_norm(difference, communicator) /
std::max(global_norm(reference, communicator), std::numeric_limits<double>::epsilon()); std::max(global_norm(reference, communicator),
std::numeric_limits<double>::epsilon());
} }
[[nodiscard]] mfem::Vector centered_difference( [[nodiscard]] mfem::Vector centered_difference(
const mean_field::fem::FEM &f, const mean_field::fem::FEM &f,
const mean_field::physics::RigidRotation &rotation, const mean_field::physics::RigidRotation &rotation,
const mfem::Vector &baseDensity, const mfem::Vector &baseDensity, const mfem::Vector &densityDirection,
const mfem::Vector &densityDirection,
const mfem::Vector &baseDisplacement, const mfem::Vector &baseDisplacement,
const mfem::Vector &displacementDirection, const mfem::Vector &displacementDirection, const double step) {
const double step
) {
mfem::Vector plusDensity(baseDensity); mfem::Vector plusDensity(baseDensity);
plusDensity.Add(step, densityDirection); plusDensity.Add(step, densityDirection);
@@ -254,12 +264,12 @@ namespace rotational_displacement_force_test_utils {
mfem::Vector minusResidual; mfem::Vector minusResidual;
mean_field::operators::kernels::apply_rotational_displacement_force_residual( mean_field::operators::kernels::apply_rotational_displacement_force_residual(
f, *f.domainMapperStateless, rotation, plusDensity, plusDisplacement, plusResidual f, *f.domainMapperStateless, rotation, plusDensity, plusDisplacement,
); plusResidual);
mean_field::operators::kernels::apply_rotational_displacement_force_residual( mean_field::operators::kernels::apply_rotational_displacement_force_residual(
f, *f.domainMapperStateless, rotation, minusDensity, minusDisplacement, minusResidual f, *f.domainMapperStateless, rotation, minusDensity, minusDisplacement,
); minusResidual);
plusResidual -= minusResidual; plusResidual -= minusResidual;
plusResidual /= 2.0 * step; plusResidual /= 2.0 * step;
@@ -270,8 +280,7 @@ namespace rotational_displacement_force_test_utils {
[[nodiscard]] mfem::Vector copy_residual_block( [[nodiscard]] mfem::Vector copy_residual_block(
const mfem::Vector &action, const mfem::Vector &action,
const mean_field::operators::RotationalDisplacementForceLayout &layout, const mean_field::operators::RotationalDisplacementForceLayout &layout,
const mean_field::utils::blocks::residual_block<index> block const mean_field::utils::blocks::residual_block<index> block) {
) {
mfem::Vector result(layout.size(block)); mfem::Vector result(layout.size(block));
const int offset = layout.offset(block); const int offset = layout.offset(block);
@@ -286,17 +295,18 @@ namespace rotational_displacement_force_test_utils {
TEST_CASE( TEST_CASE(
"Rotational Displacement Force Query Includes Density Test And Linear " "Rotational Displacement Force Query Includes Density Test And Linear "
"Position", "Position",
tags::rotation_prepared_unit tags::rotation_prepared_unit) {
) { using DisplacementField =
using DisplacementField = mean_field::field::Field<mean_field::field::Displacement>; mean_field::field::Field<mean_field::field::Displacement>;
constexpr int geometryWeightOrder = 4; constexpr int geometryWeightOrder = 4;
constexpr mean_field::quadrature::Query query = constexpr mean_field::quadrature::Query query = DisplacementField::make_query<
DisplacementField::make_query<mean_field::field::Displacement::Form::CentrifugalForce>( mean_field::field::Displacement::Form::CentrifugalForce>(
mean_field::quadrature::QuadratureRole::discretization, geometryWeightOrder, std::array<int, 1>{1}, mean_field::quadrature::QuadratureRole::discretization,
mean_field::utils::DOMAINS::STELLAR, mean_field::quadrature::MappingKind::general geometryWeightOrder, std::array<int, 1>{1},
); mean_field::utils::DOMAINS::STELLAR,
mean_field::quadrature::MappingKind::general);
/* density: 2, displacement test: 3, position: 1, geometry: 4 */ /* density: 2, displacement test: 3, position: 1, geometry: 4 */
constexpr int expectedBaseOrder = 2 + 3 + 1 + 4; constexpr int expectedBaseOrder = 2 + 3 + 1 + 4;
@@ -308,33 +318,33 @@ TEST_CASE(
STATIC_REQUIRE(*query.base_order == expectedBaseOrder); STATIC_REQUIRE(*query.base_order == expectedBaseOrder);
} }
TEST_CASE( TEST_CASE("Rotational Displacement Force Uses Negative Rotation-Potential "
"Rotational Displacement Force Uses Negative Rotation-Potential "
"Gradient And Excludes Vacuum", "Gradient And Excludes Vacuum",
tags::rotation_kernel_accuracy tags::rotation_kernel_accuracy) {
) {
mean_field::utils::Args args = test_utils::setup_args(); mean_field::utils::Args args = test_utils::setup_args();
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0); mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
REQUIRE(f.okay()); REQUIRE(f.okay());
const mfem::Vector density = rotational_displacement_force_test_utils::make_density(f, 0.31); const mfem::Vector density =
rotational_displacement_force_test_utils::make_density(f, 0.31);
mfem::Vector displacement(f.displacementFes->GetTrueVSize()); mfem::Vector displacement(f.displacementFes->GetTrueVSize());
displacement = 0.0; displacement = 0.0;
const mean_field::physics::RigidRotation rotation = rotational_displacement_force_test_utils::make_rotation(); const mean_field::physics::RigidRotation rotation =
rotational_displacement_force_test_utils::make_rotation();
mfem::Vector residual; mfem::Vector residual;
mean_field::operators::kernels::apply_rotational_displacement_force_residual( mean_field::operators::kernels::apply_rotational_displacement_force_residual(
f, *f.domainMapperStateless, rotation, density, displacement, residual f, *f.domainMapperStateless, rotation, density, displacement, residual);
);
mfem::ParGridFunction gradientTestField(f.displacementFes.get()); mfem::ParGridFunction gradientTestField(f.displacementFes.get());
auto gradientFunction = [&rotation](const mfem::Vector &position, mfem::Vector &value) { auto gradientFunction = [&rotation](const mfem::Vector &position,
mfem::Vector &value) {
rotation.potential_gradient(position, value); rotation.potential_gradient(position, value);
}; };
@@ -346,63 +356,73 @@ TEST_CASE(
gradientTestField.GetTrueDofs(gradientTestDirection); gradientTestField.GetTrueDofs(gradientTestDirection);
const double signedWork = const double signedWork =
rotational_displacement_force_test_utils::global_dot(residual, gradientTestDirection, f.mesh->GetComm()); rotational_displacement_force_test_utils::global_dot(
residual, gradientTestDirection, f.mesh->GetComm());
INFO("Rotation-force work against grad(Psi) = " << signedWork); INFO("Rotation-force work against grad(Psi) = " << signedWork);
CHECK(signedWork < 0.0); CHECK(signedWork < 0.0);
const mfem::Vector vacuumDensity = rotational_displacement_force_test_utils::make_vacuum_only_density(f); const mfem::Vector vacuumDensity =
rotational_displacement_force_test_utils::make_vacuum_only_density(f);
mfem::Vector vacuumResidual; mfem::Vector vacuumResidual;
mean_field::operators::kernels::apply_rotational_displacement_force_residual( mean_field::operators::kernels::apply_rotational_displacement_force_residual(
f, *f.domainMapperStateless, rotation, vacuumDensity, displacement, vacuumResidual f, *f.domainMapperStateless, rotation, vacuumDensity, displacement,
); vacuumResidual);
CHECK(rotational_displacement_force_test_utils::global_norm(vacuumResidual, f.mesh->GetComm()) == 0.0); CHECK(rotational_displacement_force_test_utils::global_norm(
vacuumResidual, f.mesh->GetComm()) == 0.0);
mfem::Vector zeroAngularVelocity(3); mfem::Vector zeroAngularVelocity(3);
mfem::Vector zeroCenter(3); mfem::Vector zeroCenter(3);
zeroAngularVelocity = 0.0; zeroAngularVelocity = 0.0;
zeroCenter = 0.0; zeroCenter = 0.0;
const mean_field::physics::RigidRotation zeroRotation(zeroAngularVelocity, zeroCenter); const mean_field::physics::RigidRotation zeroRotation(zeroAngularVelocity,
zeroCenter);
mfem::Vector zeroRotationResidual; mfem::Vector zeroRotationResidual;
mean_field::operators::kernels::apply_rotational_displacement_force_residual( mean_field::operators::kernels::apply_rotational_displacement_force_residual(
f, *f.domainMapperStateless, zeroRotation, density, displacement, zeroRotationResidual f, *f.domainMapperStateless, zeroRotation, density, displacement,
); zeroRotationResidual);
CHECK(rotational_displacement_force_test_utils::global_norm(zeroRotationResidual, f.mesh->GetComm()) == 0.0); CHECK(rotational_displacement_force_test_utils::global_norm(
zeroRotationResidual, f.mesh->GetComm()) == 0.0);
} }
TEST_CASE( TEST_CASE("Prepared Rotational Displacement Force Reprepares Selectively",
"Prepared Rotational Displacement Force Reprepares Selectively", tags::rotation_prepared) {
tags::rotation_prepared
) {
mean_field::utils::Args args = test_utils::setup_args(); mean_field::utils::Args args = test_utils::setup_args();
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0); mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
REQUIRE(f.okay()); REQUIRE(f.okay());
mfem::Vector densityTrue = rotational_displacement_force_test_utils::make_density(f, 0.37); mfem::Vector densityTrue =
rotational_displacement_force_test_utils::make_density(f, 0.37);
const mfem::Vector displacementTrue = gravity_prepared_test_utils::make_displacement(f, 0.53); const mfem::Vector displacementTrue =
gravity_prepared_test_utils::make_displacement(f, 0.53);
mean_field::physics::RigidRotation rotation = rotational_displacement_force_test_utils::make_rotation(0.81); mean_field::physics::RigidRotation rotation =
rotational_displacement_force_test_utils::make_rotation(0.81);
auto dependencies = rotational_displacement_force_test_utils::make_dependencies(); auto dependencies =
rotational_displacement_force_test_utils::make_dependencies();
mean_field::operators::PreparedRotationalDisplacementForceOperator preparedOperator(f, *f.domainMapperStateless); mean_field::operators::PreparedRotationalDisplacementForceOperator
preparedOperator(f, *f.domainMapperStateless);
const auto &context = preparedOperator.GetContext(); const auto &context = preparedOperator.GetContext();
mfem::Vector density = context.GetDensityMap().gather(densityTrue); mfem::Vector density = context.GetDensityMap().gather(densityTrue);
const mfem::Vector displacement = context.GetDisplacementMap().gather(displacementTrue); const mfem::Vector displacement =
context.GetDisplacementMap().gather(displacementTrue);
const auto initialReport = const auto initialReport = preparedOperator.Prepare(
preparedOperator.Prepare({.density = density, .displacement = displacement}, dependencies, rotation); {.density = density, .displacement = displacement}, dependencies,
rotation);
REQUIRE(initialReport.DidAnyWork()); REQUIRE(initialReport.DidAnyWork());
REQUIRE(initialReport.updatedRotation); REQUIRE(initialReport.updatedRotation);
@@ -415,18 +435,19 @@ TEST_CASE(
preparedOperator.BuildResidual(preparedResidual); preparedOperator.BuildResidual(preparedResidual);
mean_field::operators::kernels::apply_rotational_displacement_force_residual( mean_field::operators::kernels::apply_rotational_displacement_force_residual(
f, *f.domainMapperStateless, rotation, densityTrue, displacementTrue, kernelResidual f, *f.domainMapperStateless, rotation, densityTrue, displacementTrue,
); kernelResidual);
const mfem::Vector kernelResidualReduced = context.GetDisplacementMap().gather(kernelResidual); const mfem::Vector kernelResidualReduced =
context.GetDisplacementMap().gather(kernelResidual);
CHECK( CHECK(rotational_displacement_force_test_utils::relative_difference(
rotational_displacement_force_test_utils::relative_difference( preparedResidual, kernelResidualReduced, f.mesh->GetComm()) <
preparedResidual, kernelResidualReduced, f.mesh->GetComm() 2.0e-12);
) < 2.0e-12
);
CHECK_FALSE(preparedOperator.Prepare({.density = density, .displacement = displacement}, dependencies, rotation) CHECK_FALSE(preparedOperator
.Prepare({.density = density, .displacement = displacement},
dependencies, rotation)
.DidAnyWork()); .DidAnyWork());
densityTrue = rotational_displacement_force_test_utils::make_density(f, 0.79); densityTrue = rotational_displacement_force_test_utils::make_density(f, 0.79);
@@ -434,8 +455,9 @@ TEST_CASE(
++dependencies.density.revision; ++dependencies.density.revision;
const auto densityReport = const auto densityReport = preparedOperator.Prepare(
preparedOperator.Prepare({.density = density, .displacement = displacement}, dependencies, rotation); {.density = density, .displacement = displacement}, dependencies,
rotation);
CHECK(densityReport.preparedResidual); CHECK(densityReport.preparedResidual);
CHECK_FALSE(densityReport.updatedRotation); CHECK_FALSE(densityReport.updatedRotation);
@@ -444,8 +466,9 @@ TEST_CASE(
++dependencies.rotation.revision; ++dependencies.rotation.revision;
const auto rotationReport = const auto rotationReport = preparedOperator.Prepare(
preparedOperator.Prepare({.density = density, .displacement = displacement}, dependencies, rotation); {.density = density, .displacement = displacement}, dependencies,
rotation);
CHECK(rotationReport.updatedRotation); CHECK(rotationReport.updatedRotation);
CHECK(rotationReport.preparedResidual); CHECK(rotationReport.preparedResidual);
@@ -453,40 +476,45 @@ TEST_CASE(
CHECK(preparedOperator.GetResidualApplicationCount() == 1); CHECK(preparedOperator.GetResidualApplicationCount() == 1);
} }
TEST_CASE( TEST_CASE("Rotational Displacement Force Jacobian Matches Both Columns And "
"Rotational Displacement Force Jacobian Matches Both Columns And "
"Centered Differences", "Centered Differences",
tags::rotation_prepared_jacobian_accuracy tags::rotation_prepared_jacobian_accuracy) {
) {
mean_field::utils::Args args = test_utils::setup_args(); mean_field::utils::Args args = test_utils::setup_args();
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0); mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
REQUIRE(f.okay()); REQUIRE(f.okay());
const mfem::Vector densityTrue = rotational_displacement_force_test_utils::make_density(f, 0.43); const mfem::Vector densityTrue =
rotational_displacement_force_test_utils::make_density(f, 0.43);
const mfem::Vector densityDirectionTrue = rotational_displacement_force_test_utils::make_density_direction(f, 0.59); const mfem::Vector densityDirectionTrue =
rotational_displacement_force_test_utils::make_density_direction(f, 0.59);
const mfem::Vector displacementTrue = gravity_prepared_test_utils::make_displacement(f, 0.61); const mfem::Vector displacementTrue =
gravity_prepared_test_utils::make_displacement(f, 0.61);
const mfem::Vector displacementDirectionTrue = const mfem::Vector displacementDirectionTrue =
rotational_displacement_force_test_utils::make_displacement_direction(f); rotational_displacement_force_test_utils::make_displacement_direction(f);
const mean_field::physics::RigidRotation rotation = rotational_displacement_force_test_utils::make_rotation(0.93); const mean_field::physics::RigidRotation rotation =
rotational_displacement_force_test_utils::make_rotation(0.93);
mean_field::operators::PreparedRotationalDisplacementForceOperator preparedOperator(f, *f.domainMapperStateless); mean_field::operators::PreparedRotationalDisplacementForceOperator
preparedOperator(f, *f.domainMapperStateless);
const auto &context = preparedOperator.GetContext(); const auto &context = preparedOperator.GetContext();
const mfem::Vector density = context.GetDensityMap().gather(densityTrue); const mfem::Vector density = context.GetDensityMap().gather(densityTrue);
const mfem::Vector densityDirection = context.GetDensityMap().gather(densityDirectionTrue); const mfem::Vector densityDirection =
const mfem::Vector displacement = context.GetDisplacementMap().gather(displacementTrue); context.GetDensityMap().gather(densityDirectionTrue);
const mfem::Vector displacementDirection = context.GetDisplacementMap().gather(displacementDirectionTrue); const mfem::Vector displacement =
context.GetDisplacementMap().gather(displacementTrue);
const mfem::Vector displacementDirection =
context.GetDisplacementMap().gather(displacementDirectionTrue);
preparedOperator.Prepare( preparedOperator.Prepare(
{.density = density, .displacement = displacement}, {.density = density, .displacement = displacement},
rotational_displacement_force_test_utils::make_dependencies(), rotation rotational_displacement_force_test_utils::make_dependencies(), rotation);
);
mfem::Vector densityAction; mfem::Vector densityAction;
mfem::Vector displacementAction; mfem::Vector displacementAction;
@@ -494,18 +522,17 @@ TEST_CASE(
preparedOperator.ApplyDensityJacobianAction(densityDirection, densityAction); preparedOperator.ApplyDensityJacobianAction(densityDirection, densityAction);
preparedOperator.ApplyDisplacementJacobianAction(displacementDirection, displacementAction); preparedOperator.ApplyDisplacementJacobianAction(displacementDirection,
displacementAction);
preparedOperator.ApplyCompleteJacobianAction(densityDirection, displacementDirection, completeAction); preparedOperator.ApplyCompleteJacobianAction(
densityDirection, displacementDirection, completeAction);
mfem::Vector summedColumns(densityAction); mfem::Vector summedColumns(densityAction);
summedColumns += displacementAction; summedColumns += displacementAction;
CHECK( CHECK(rotational_displacement_force_test_utils::relative_difference(
rotational_displacement_force_test_utils::relative_difference( completeAction, summedColumns, f.mesh->GetComm()) < 2.0e-12);
completeAction, summedColumns, f.mesh->GetComm()
) < 2.0e-12
);
mfem::Vector zeroDensityTrue(densityDirectionTrue.Size()); mfem::Vector zeroDensityTrue(densityDirectionTrue.Size());
mfem::Vector zeroDisplacementTrue(displacementDirectionTrue.Size()); mfem::Vector zeroDisplacementTrue(displacementDirectionTrue.Size());
@@ -514,33 +541,39 @@ TEST_CASE(
constexpr double step = 1.0e-5; constexpr double step = 1.0e-5;
const mfem::Vector densityDifferenceTrue = rotational_displacement_force_test_utils::centered_difference( const mfem::Vector densityDifferenceTrue =
f, rotation, densityTrue, densityDirectionTrue, displacementTrue, zeroDisplacementTrue, step rotational_displacement_force_test_utils::centered_difference(
); f, rotation, densityTrue, densityDirectionTrue, displacementTrue,
zeroDisplacementTrue, step);
const mfem::Vector displacementDifferenceTrue = rotational_displacement_force_test_utils::centered_difference( const mfem::Vector displacementDifferenceTrue =
f, rotation, densityTrue, zeroDensityTrue, displacementTrue, displacementDirectionTrue, step rotational_displacement_force_test_utils::centered_difference(
); f, rotation, densityTrue, zeroDensityTrue, displacementTrue,
displacementDirectionTrue, step);
const mfem::Vector completeDifferenceTrue = rotational_displacement_force_test_utils::centered_difference( const mfem::Vector completeDifferenceTrue =
f, rotation, densityTrue, densityDirectionTrue, displacementTrue, displacementDirectionTrue, step rotational_displacement_force_test_utils::centered_difference(
); f, rotation, densityTrue, densityDirectionTrue, displacementTrue,
displacementDirectionTrue, step);
const mfem::Vector densityDifference = context.GetDisplacementMap().gather(densityDifferenceTrue); const mfem::Vector densityDifference =
const mfem::Vector displacementDifference = context.GetDisplacementMap().gather(displacementDifferenceTrue); context.GetDisplacementMap().gather(densityDifferenceTrue);
const mfem::Vector completeDifference = context.GetDisplacementMap().gather(completeDifferenceTrue); const mfem::Vector displacementDifference =
context.GetDisplacementMap().gather(displacementDifferenceTrue);
const mfem::Vector completeDifference =
context.GetDisplacementMap().gather(completeDifferenceTrue);
const double densityError = rotational_displacement_force_test_utils::relative_difference( const double densityError =
densityAction, densityDifference, f.mesh->GetComm() rotational_displacement_force_test_utils::relative_difference(
); densityAction, densityDifference, f.mesh->GetComm());
const double displacementError = rotational_displacement_force_test_utils::relative_difference( const double displacementError =
displacementAction, displacementDifference, f.mesh->GetComm() rotational_displacement_force_test_utils::relative_difference(
); displacementAction, displacementDifference, f.mesh->GetComm());
const double completeError = rotational_displacement_force_test_utils::relative_difference( const double completeError =
completeAction, completeDifference, f.mesh->GetComm() rotational_displacement_force_test_utils::relative_difference(
); completeAction, completeDifference, f.mesh->GetComm());
INFO("Density-column centered-difference error = " << densityError); INFO("Density-column centered-difference error = " << densityError);
INFO("Displacement-column centered-difference error = " << displacementError); INFO("Displacement-column centered-difference error = " << displacementError);
@@ -551,95 +584,109 @@ TEST_CASE(
CHECK(completeError < 4.0e-8); CHECK(completeError < 4.0e-8);
} }
TEST_CASE( TEST_CASE("Prepared Rotational Displacement Force MFEM Adapter Routes Only R-d",
"Prepared Rotational Displacement Force MFEM Adapter Routes Only R-d", tags::rotation_prepared_unit) {
tags::rotation_prepared_unit
) {
mean_field::utils::Args args = test_utils::setup_args(); mean_field::utils::Args args = test_utils::setup_args();
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0); mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
REQUIRE(f.okay()); REQUIRE(f.okay());
const mfem::Vector densityTrue = rotational_displacement_force_test_utils::make_density(f, 0.47); const mfem::Vector densityTrue =
rotational_displacement_force_test_utils::make_density(f, 0.47);
const mfem::Vector densityDirectionTrue = rotational_displacement_force_test_utils::make_density_direction(f, 0.63); const mfem::Vector densityDirectionTrue =
rotational_displacement_force_test_utils::make_density_direction(f, 0.63);
const mfem::Vector displacementTrue = gravity_prepared_test_utils::make_displacement(f, 0.57); const mfem::Vector displacementTrue =
gravity_prepared_test_utils::make_displacement(f, 0.57);
const mfem::Vector displacementDirectionTrue = const mfem::Vector displacementDirectionTrue =
rotational_displacement_force_test_utils::make_displacement_direction(f); rotational_displacement_force_test_utils::make_displacement_direction(f);
const mean_field::physics::RigidRotation rotation = rotational_displacement_force_test_utils::make_rotation(0.87); const mean_field::physics::RigidRotation rotation =
rotational_displacement_force_test_utils::make_rotation(0.87);
mean_field::operators::PreparedRotationalDisplacementForceOperator preparedOperator(f, *f.domainMapperStateless); mean_field::operators::PreparedRotationalDisplacementForceOperator
preparedOperator(f, *f.domainMapperStateless);
const auto &context = preparedOperator.GetContext(); const auto &context = preparedOperator.GetContext();
const mfem::Vector density = context.GetDensityMap().gather(densityTrue); const mfem::Vector density = context.GetDensityMap().gather(densityTrue);
const mfem::Vector densityDirection = context.GetDensityMap().gather(densityDirectionTrue); const mfem::Vector densityDirection =
const mfem::Vector displacement = context.GetDisplacementMap().gather(displacementTrue); context.GetDensityMap().gather(densityDirectionTrue);
const mfem::Vector displacementDirection = context.GetDisplacementMap().gather(displacementDirectionTrue); const mfem::Vector displacement =
context.GetDisplacementMap().gather(displacementTrue);
const mfem::Vector displacementDirection =
context.GetDisplacementMap().gather(displacementDirectionTrue);
preparedOperator.Prepare( preparedOperator.Prepare(
{.density = density, .displacement = displacement}, {.density = density, .displacement = displacement},
rotational_displacement_force_test_utils::make_dependencies(), rotation rotational_displacement_force_test_utils::make_dependencies(), rotation);
);
const auto layout = rotational_displacement_force_test_utils::make_layout(f); const auto layout = rotational_displacement_force_test_utils::make_layout(f);
mean_field::operators::PreparedRotationalDisplacementForceJacobianOperator adapter(layout, preparedOperator); mean_field::operators::PreparedRotationalDisplacementForceJacobianOperator
adapter(layout, preparedOperator);
mfem::BlockVector direction(layout.value_offsets()); mfem::BlockVector direction(layout.value_offsets());
direction = 0.0; direction = 0.0;
direction.GetBlock(rotational_displacement_force_test_utils::densityValue) = densityDirection; direction.GetBlock(rotational_displacement_force_test_utils::densityValue) =
densityDirection;
direction.GetBlock(rotational_displacement_force_test_utils::displacementValue) = displacementDirection; direction.GetBlock(
rotational_displacement_force_test_utils::displacementValue) =
displacementDirection;
direction.GetBlock(rotational_displacement_force_test_utils::gravityGradientValue) = 0.23; direction.GetBlock(
rotational_displacement_force_test_utils::gravityGradientValue) = 0.23;
direction.GetBlock(rotational_displacement_force_test_utils::gravityPotentialValue) = -0.31; direction.GetBlock(
rotational_displacement_force_test_utils::gravityPotentialValue) = -0.31;
direction.GetBlock(rotational_displacement_force_test_utils::enthalpyValue) = 0.37; direction.GetBlock(rotational_displacement_force_test_utils::enthalpyValue) =
0.37;
direction.GetBlock(rotational_displacement_force_test_utils::barotropicConstantValue) = -0.41; direction.GetBlock(
rotational_displacement_force_test_utils::barotropicConstantValue) =
-0.41;
mfem::Vector action; mfem::Vector action;
adapter.Mult(direction, action); adapter.Mult(direction, action);
mfem::Vector expectedDisplacementAction; mfem::Vector expectedDisplacementAction;
preparedOperator.ApplyCompleteJacobianAction(densityDirection, displacementDirection, expectedDisplacementAction); preparedOperator.ApplyCompleteJacobianAction(
densityDirection, displacementDirection, expectedDisplacementAction);
const mfem::Vector actualDisplacementAction = rotational_displacement_force_test_utils::copy_residual_block( const mfem::Vector actualDisplacementAction =
action, layout, rotational_displacement_force_test_utils::displacementResidual rotational_displacement_force_test_utils::copy_residual_block(
); action, layout,
rotational_displacement_force_test_utils::displacementResidual);
CHECK( CHECK(rotational_displacement_force_test_utils::relative_difference(
rotational_displacement_force_test_utils::relative_difference( actualDisplacementAction, expectedDisplacementAction,
actualDisplacementAction, expectedDisplacementAction, f.mesh->GetComm() f.mesh->GetComm()) < 2.0e-12);
) < 2.0e-12
);
const std::array<mfem::Vector, 5> zeroRows{ const std::array<mfem::Vector, 5> zeroRows{
rotational_displacement_force_test_utils::copy_residual_block( rotational_displacement_force_test_utils::copy_residual_block(
action, layout, rotational_displacement_force_test_utils::gravityGradientResidual action, layout,
), rotational_displacement_force_test_utils::gravityGradientResidual),
rotational_displacement_force_test_utils::copy_residual_block( rotational_displacement_force_test_utils::copy_residual_block(
action, layout, rotational_displacement_force_test_utils::gravityPotentialResidual action, layout,
), rotational_displacement_force_test_utils::gravityPotentialResidual),
rotational_displacement_force_test_utils::copy_residual_block( rotational_displacement_force_test_utils::copy_residual_block(
action, layout, rotational_displacement_force_test_utils::densityResidual action, layout,
), rotational_displacement_force_test_utils::densityResidual),
rotational_displacement_force_test_utils::copy_residual_block( rotational_displacement_force_test_utils::copy_residual_block(
action, layout, rotational_displacement_force_test_utils::enthalpyResidual action, layout,
), rotational_displacement_force_test_utils::enthalpyResidual),
rotational_displacement_force_test_utils::copy_residual_block( rotational_displacement_force_test_utils::copy_residual_block(
action, layout, rotational_displacement_force_test_utils::massResidual action, layout,
) rotational_displacement_force_test_utils::massResidual)};
};
for (const mfem::Vector &row : zeroRows) { for (const mfem::Vector &row : zeroRows) {
CHECK(rotational_displacement_force_test_utils::global_norm(row, f.mesh->GetComm()) == 0.0); CHECK(rotational_displacement_force_test_utils::global_norm(
row, f.mesh->GetComm()) == 0.0);
} }
} }

File diff suppressed because it is too large Load Diff

View File

@@ -8,14 +8,13 @@
import mean_field; import mean_field;
import test_helpers; import test_helpers;
TEST_CASE( TEST_CASE("Polytropic EOS Satisfies Its Analytic Identities",
"Polytropic Barotrope Satisfies Its Analytic Identities", tags::barotrope_eos_unit) {
tags::hydro &tags::unit &tags::barotrope
) {
constexpr double polytropic_index = 3.0; constexpr double polytropic_index = 3.0;
constexpr double polytropic_constant = 1.5; constexpr double polytropic_constant = 1.5;
const mean_field::physics::PolytropicBarotrope barotrope(polytropic_index, polytropic_constant); const mean_field::eos::Polytrope barotrope(polytropic_index,
polytropic_constant);
const std::array<double, 5> densities{1.0e-6, 1.0e-3, 0.1, 0.7, 2.0}; const std::array<double, 5> densities{1.0e-6, 1.0e-3, 0.1, 0.7, 2.0};
@@ -24,30 +23,40 @@ TEST_CASE(
const double enthalpy = barotrope.enthalpy_from_density(density); const double enthalpy = barotrope.enthalpy_from_density(density);
const double reconstructed_density = barotrope.density_from_enthalpy(enthalpy); const double reconstructed_density =
barotrope.density_from_enthalpy(enthalpy);
const double reconstructed_pressure = barotrope.pressure_from_enthalpy(enthalpy); const double reconstructed_pressure =
barotrope.pressure_from_enthalpy(enthalpy);
CHECK_THAT(reconstructed_density, Catch::Matchers::WithinRel(density, 2.0e-14)); const double reconstructed_enthalpy =
barotrope.enthalpy_from_pressure(pressure);
CHECK_THAT(reconstructed_pressure, Catch::Matchers::WithinRel(pressure, 2.0e-14)); CHECK_THAT(reconstructed_density,
Catch::Matchers::WithinRel(density, 2.0e-14));
CHECK_THAT(pressure, Catch::Matchers::WithinRel(density * enthalpy / (polytropic_index + 1.0), 2.0e-14)); CHECK_THAT(reconstructed_pressure,
Catch::Matchers::WithinRel(pressure, 2.0e-14));
CHECK_THAT(barotrope.pressure_derivative_from_enthalpy(enthalpy), Catch::Matchers::WithinRel(density, 2.0e-14)); CHECK_THAT(reconstructed_enthalpy,
Catch::Matchers::WithinRel(enthalpy, 2.0e-14));
CHECK_THAT(pressure,
Catch::Matchers::WithinRel(
density * enthalpy / (polytropic_index + 1.0), 2.0e-14));
CHECK_THAT(barotrope.pressure_derivative_from_enthalpy(enthalpy),
Catch::Matchers::WithinRel(density, 2.0e-14));
CHECK_THAT( CHECK_THAT(
barotrope.pressure_derivative_from_density(density), barotrope.pressure_derivative_from_density(density),
Catch::Matchers::WithinRel(enthalpy / polytropic_index, 2.0e-14) Catch::Matchers::WithinRel(enthalpy / polytropic_index, 2.0e-14));
);
} }
} }
TEST_CASE( TEST_CASE("Polytropic EOS Derivatives Match Centered Differences",
"Polytropic Barotrope Derivatives Match Centered Differences", tags::barotrope_eos_jacobian) {
tags::hydro &tags::jacobian &tags::unit &tags::barotrope const mean_field::eos::Polytrope barotrope(3.0, 1.5);
) {
const mean_field::physics::PolytropicBarotrope barotrope(3.0, 1.5);
const std::array<double, 4> enthalpies{0.05, 0.2, 0.7, 1.4}; const std::array<double, 4> enthalpies{0.05, 0.2, 0.7, 1.4};
@@ -55,30 +64,30 @@ TEST_CASE(
const double step = 1.0e-6 * std::max(1.0, enthalpy); const double step = 1.0e-6 * std::max(1.0, enthalpy);
const double density_difference = const double density_difference =
(barotrope.density_from_enthalpy(enthalpy + step) - barotrope.density_from_enthalpy(enthalpy - step)) / (barotrope.density_from_enthalpy(enthalpy + step) -
barotrope.density_from_enthalpy(enthalpy - step)) /
(2.0 * step); (2.0 * step);
const double pressure_difference = const double pressure_difference =
(barotrope.pressure_from_enthalpy(enthalpy + step) - barotrope.pressure_from_enthalpy(enthalpy - step)) / (barotrope.pressure_from_enthalpy(enthalpy + step) -
barotrope.pressure_from_enthalpy(enthalpy - step)) /
(2.0 * step); (2.0 * step);
CHECK_THAT( CHECK_THAT(
density_difference, density_difference,
Catch::Matchers::WithinRel(barotrope.density_derivative_from_enthalpy(enthalpy), 5.0e-10) Catch::Matchers::WithinRel(
); barotrope.density_derivative_from_enthalpy(enthalpy), 5.0e-10));
CHECK_THAT( CHECK_THAT(
pressure_difference, pressure_difference,
Catch::Matchers::WithinRel(barotrope.pressure_derivative_from_enthalpy(enthalpy), 5.0e-10) Catch::Matchers::WithinRel(
); barotrope.pressure_derivative_from_enthalpy(enthalpy), 5.0e-10));
} }
} }
TEST_CASE( TEST_CASE("Polytropic EOS Has An Exact Zero Density Surface",
"Polytropic Barotrope Has An Exact Zero Density Surface", tags::barotrope_eos_unit) {
tags::hydro &tags::unit &tags::barotrope const mean_field::eos::Polytrope barotrope(3.0, 1.5);
) {
const mean_field::physics::PolytropicBarotrope barotrope(3.0, 1.5);
CHECK(barotrope.density_from_enthalpy(-1.0) == 0.0); CHECK(barotrope.density_from_enthalpy(-1.0) == 0.0);
CHECK(barotrope.density_from_enthalpy(0.0) == 0.0); CHECK(barotrope.density_from_enthalpy(0.0) == 0.0);
@@ -93,21 +102,21 @@ TEST_CASE(
CHECK(barotrope.pressure_derivative_from_enthalpy(0.0) == 0.0); CHECK(barotrope.pressure_derivative_from_enthalpy(0.0) == 0.0);
} }
TEST_CASE( TEST_CASE("Polytropic EOS Rejects Invalid Material Parameters",
"Polytropic Barotrope Rejects Invalid Material Parameters", tags::barotrope_eos_unit) {
tags::hydro &tags::unit CHECK_THROWS_AS(mean_field::eos::Polytrope(0.5, 1.0), std::invalid_argument);
) {
CHECK_THROWS_AS(mean_field::physics::PolytropicBarotrope(0.5, 1.0), std::invalid_argument);
CHECK_THROWS_AS(mean_field::physics::PolytropicBarotrope(3.0, 0.0), std::invalid_argument); CHECK_THROWS_AS(mean_field::eos::Polytrope(3.0, 0.0), std::invalid_argument);
CHECK_THROWS_AS( CHECK_THROWS_AS(
mean_field::physics::PolytropicBarotrope(std::numeric_limits<double>::infinity(), 1.0), std::invalid_argument mean_field::eos::Polytrope(std::numeric_limits<double>::infinity(), 1.0),
); std::invalid_argument);
const mean_field::physics::PolytropicBarotrope barotrope(3.0, 1.0); const mean_field::eos::Polytrope barotrope(3.0, 1.0);
CHECK_THROWS_AS(barotrope.pressure_from_density(-1.0), std::domain_error); CHECK_THROWS_AS(barotrope.pressure_from_density(-1.0), std::domain_error);
CHECK_THROWS_AS(barotrope.enthalpy_from_density(-1.0), std::domain_error); CHECK_THROWS_AS(barotrope.enthalpy_from_density(-1.0), std::domain_error);
CHECK_THROWS_AS(barotrope.enthalpy_from_pressure(-1.0), std::domain_error);
} }

View File

@@ -13,37 +13,32 @@
import mean_field; import mean_field;
import test_helpers; import test_helpers;
namespace polytropic_barotrope_test_utils { namespace polytropic_eos_test_utils {
template <typename Function> template <typename Function>
double centered_derivative( double centered_derivative(Function &&function, const double position,
Function &&function, const double step) {
const double position,
const double step
) {
return (function(position + step) - function(position - step)) / (2.0 * step); return (function(position + step) - function(position - step)) / (2.0 * step);
} }
template <typename Integrand> template <typename Integrand>
double integrate_cube( double integrate_cube(const mfem::IntegrationRule &integrationRule,
const mfem::IntegrationRule &integrationRule, Integrand &&integrand) {
Integrand &&integrand
) {
double integral = 0.0; double integral = 0.0;
for (int pointIndex = 0; pointIndex < integrationRule.GetNPoints(); ++pointIndex) { for (int pointIndex = 0; pointIndex < integrationRule.GetNPoints();
const mfem::IntegrationPoint &integrationPoint = integrationRule.IntPoint(pointIndex); ++pointIndex) {
const mfem::IntegrationPoint &integrationPoint =
integrationRule.IntPoint(pointIndex);
integral += integrationPoint.weight * integrand(integrationPoint); integral += integrationPoint.weight * integrand(integrationPoint);
} }
return integral; return integral;
} }
} // namespace polytropic_barotrope_test_utils } // namespace polytropic_eos_test_utils
TEST_CASE( TEST_CASE("Polytropic EOS Satisfies Its Thermodynamic Identities",
"Polytropic Barotrope Satisfies Its Thermodynamic Identities", tags::barotrope_eos_unit) {
tags::barotrope &tags::physics &tags::unit
) {
constexpr std::array<double, 3> polytropicIndices{1.0, 1.5, 3.0}; constexpr std::array<double, 3> polytropicIndices{1.0, 1.5, 3.0};
constexpr std::array<double, 4> densities{1.0e-4, 0.02, 0.37, 2.4}; constexpr std::array<double, 4> densities{1.0e-4, 0.02, 0.37, 2.4};
@@ -52,9 +47,11 @@ TEST_CASE(
for (const double polytropicIndex : polytropicIndices) { for (const double polytropicIndex : polytropicIndices) {
DYNAMIC_SECTION("polytropic index n = " << polytropicIndex) { DYNAMIC_SECTION("polytropic index n = " << polytropicIndex) {
const mean_field::physics::PolytropicBarotrope barotrope(polytropicIndex, polytropicConstant); const mean_field::eos::Polytrope barotrope(polytropicIndex,
polytropicConstant);
const double expectedEnthalpyScale = (polytropicIndex + 1.0) * polytropicConstant; const double expectedEnthalpyScale =
(polytropicIndex + 1.0) * polytropicConstant;
CHECK(barotrope.polytropic_index() == polytropicIndex); CHECK(barotrope.polytropic_index() == polytropicIndex);
@@ -65,35 +62,45 @@ TEST_CASE(
for (const double density : densities) { for (const double density : densities) {
CAPTURE(polytropicIndex, polytropicConstant, density); CAPTURE(polytropicIndex, polytropicConstant, density);
const double expectedPressure = polytropicConstant * std::pow(density, 1.0 + 1.0 / polytropicIndex); const double expectedPressure =
polytropicConstant * std::pow(density, 1.0 + 1.0 / polytropicIndex);
const double expectedEnthalpy = expectedEnthalpyScale * std::pow(density, 1.0 / polytropicIndex); const double expectedEnthalpy =
expectedEnthalpyScale * std::pow(density, 1.0 / polytropicIndex);
const double pressureFromDensity = barotrope.pressure_from_density(density); const double pressureFromDensity =
barotrope.pressure_from_density(density);
const double enthalpyFromDensity = barotrope.enthalpy_from_density(density); const double enthalpyFromDensity =
barotrope.enthalpy_from_density(density);
const double recoveredDensity = barotrope.density_from_enthalpy(enthalpyFromDensity); const double recoveredDensity =
barotrope.density_from_enthalpy(enthalpyFromDensity);
const double pressureFromEnthalpy = barotrope.pressure_from_enthalpy(enthalpyFromDensity); const double pressureFromEnthalpy =
barotrope.pressure_from_enthalpy(enthalpyFromDensity);
CHECK_THAT(pressureFromDensity, Catch::Matchers::WithinRel(expectedPressure, 2.0e-13)); CHECK_THAT(pressureFromDensity,
Catch::Matchers::WithinRel(expectedPressure, 2.0e-13));
CHECK_THAT(enthalpyFromDensity, Catch::Matchers::WithinRel(expectedEnthalpy, 2.0e-13)); CHECK_THAT(enthalpyFromDensity,
Catch::Matchers::WithinRel(expectedEnthalpy, 2.0e-13));
CHECK_THAT(recoveredDensity, Catch::Matchers::WithinRel(density, 5.0e-13)); CHECK_THAT(recoveredDensity,
Catch::Matchers::WithinRel(density, 5.0e-13));
CHECK_THAT(pressureFromEnthalpy, Catch::Matchers::WithinRel(expectedPressure, 5.0e-13)); CHECK_THAT(pressureFromEnthalpy,
Catch::Matchers::WithinRel(expectedPressure, 5.0e-13));
/* /*
* Polytropic identity: * Polytropic identity:
* *
* P = rho h / (n + 1). * P = rho h / (n + 1).
*/ */
CHECK_THAT( CHECK_THAT(pressureFromEnthalpy,
pressureFromEnthalpy, Catch::Matchers::WithinRel(density * enthalpyFromDensity /
Catch::Matchers::WithinRel(density * enthalpyFromDensity / (polytropicIndex + 1.0), 5.0e-13) (polytropicIndex + 1.0),
); 5.0e-13));
/* /*
* Polytropic identity: * Polytropic identity:
@@ -105,8 +112,7 @@ TEST_CASE(
*/ */
CHECK( CHECK(
barotrope.pressure_derivative_from_enthalpy(enthalpyFromDensity) == barotrope.pressure_derivative_from_enthalpy(enthalpyFromDensity) ==
barotrope.density_from_enthalpy(enthalpyFromDensity) barotrope.density_from_enthalpy(enthalpyFromDensity));
);
/* /*
* Since * Since
@@ -117,19 +123,16 @@ TEST_CASE(
* *
* dP / d rho = h / n. * dP / d rho = h / n.
*/ */
CHECK_THAT( CHECK_THAT(barotrope.pressure_derivative_from_density(density),
barotrope.pressure_derivative_from_density(density), Catch::Matchers::WithinRel(
Catch::Matchers::WithinRel(enthalpyFromDensity / polytropicIndex, 5.0e-13) enthalpyFromDensity / polytropicIndex, 5.0e-13));
);
} }
} }
} }
} }
TEST_CASE( TEST_CASE("Polytropic EOS Pressure Derivatives Match Centered Differences",
"Polytropic Barotrope Pressure Derivatives Match Centered Differences", tags::barotrope_eos_jacobian) {
tags::barotrope &tags::physics &tags::unit &tags::jacobian &tags::pressure
) {
constexpr std::array<double, 3> polytropicIndices{1.0, 1.5, 3.0}; constexpr std::array<double, 3> polytropicIndices{1.0, 1.5, 3.0};
constexpr std::array<double, 3> positiveValues{0.2, 0.73, 1.8}; constexpr std::array<double, 3> positiveValues{0.2, 0.73, 1.8};
@@ -137,50 +140,55 @@ TEST_CASE(
constexpr double polytropicConstant = 0.61; constexpr double polytropicConstant = 0.61;
for (const double polytropicIndex : polytropicIndices) { for (const double polytropicIndex : polytropicIndices) {
const mean_field::physics::PolytropicBarotrope barotrope(polytropicIndex, polytropicConstant); const mean_field::eos::Polytrope barotrope(polytropicIndex,
polytropicConstant);
DYNAMIC_SECTION("polytropic index n = " << polytropicIndex) { DYNAMIC_SECTION("polytropic index n = " << polytropicIndex) {
for (const double enthalpy : positiveValues) { for (const double enthalpy : positiveValues) {
const double step = 2.0e-6 * std::max(1.0, std::abs(enthalpy)); const double step = 2.0e-6 * std::max(1.0, std::abs(enthalpy));
const double numericalDerivative = polytropic_barotrope_test_utils::centered_derivative( const double numericalDerivative =
polytropic_eos_test_utils::centered_derivative(
[&barotrope](const double perturbedEnthalpy) { [&barotrope](const double perturbedEnthalpy) {
return barotrope.pressure_from_enthalpy(perturbedEnthalpy); return barotrope.pressure_from_enthalpy(perturbedEnthalpy);
}, },
enthalpy, step enthalpy, step);
);
const double analyticDerivative = barotrope.pressure_derivative_from_enthalpy(enthalpy); const double analyticDerivative =
barotrope.pressure_derivative_from_enthalpy(enthalpy);
CAPTURE(polytropicIndex, enthalpy, step, numericalDerivative, analyticDerivative); CAPTURE(polytropicIndex, enthalpy, step, numericalDerivative,
analyticDerivative);
CHECK_THAT(numericalDerivative, Catch::Matchers::WithinRel(analyticDerivative, 5.0e-8)); CHECK_THAT(numericalDerivative,
Catch::Matchers::WithinRel(analyticDerivative, 5.0e-8));
} }
for (const double density : positiveValues) { for (const double density : positiveValues) {
const double step = 2.0e-6 * std::max(1.0, std::abs(density)); const double step = 2.0e-6 * std::max(1.0, std::abs(density));
const double numericalDerivative = polytropic_barotrope_test_utils::centered_derivative( const double numericalDerivative =
polytropic_eos_test_utils::centered_derivative(
[&barotrope](const double perturbedDensity) { [&barotrope](const double perturbedDensity) {
return barotrope.pressure_from_density(perturbedDensity); return barotrope.pressure_from_density(perturbedDensity);
}, },
density, step density, step);
);
const double analyticDerivative = barotrope.pressure_derivative_from_density(density); const double analyticDerivative =
barotrope.pressure_derivative_from_density(density);
CAPTURE(polytropicIndex, density, step, numericalDerivative, analyticDerivative); CAPTURE(polytropicIndex, density, step, numericalDerivative,
analyticDerivative);
CHECK_THAT(numericalDerivative, Catch::Matchers::WithinRel(analyticDerivative, 5.0e-8)); CHECK_THAT(numericalDerivative,
Catch::Matchers::WithinRel(analyticDerivative, 5.0e-8));
} }
} }
} }
} }
TEST_CASE( TEST_CASE("Polytropic EOS Density Derivative Matches Centered Differences",
"Polytropic Barotrope Density Derivative Matches Centered Differences", tags::barotrope_eos_jacobian) {
tags::barotrope &tags::physics &tags::unit &tags::jacobian &tags::pressure
) {
constexpr std::array<double, 3> polytropicIndices{1.0, 1.5, 3.0}; constexpr std::array<double, 3> polytropicIndices{1.0, 1.5, 3.0};
constexpr std::array<double, 3> enthalpies{0.2, 0.73, 1.8}; constexpr std::array<double, 3> enthalpies{0.2, 0.73, 1.8};
@@ -188,40 +196,43 @@ TEST_CASE(
constexpr double polytropicConstant = 0.61; constexpr double polytropicConstant = 0.61;
for (const double polytropicIndex : polytropicIndices) { for (const double polytropicIndex : polytropicIndices) {
const mean_field::physics::PolytropicBarotrope barotrope(polytropicIndex, polytropicConstant); const mean_field::eos::Polytrope barotrope(polytropicIndex,
polytropicConstant);
DYNAMIC_SECTION("polytropic index n = " << polytropicIndex) { DYNAMIC_SECTION("polytropic index n = " << polytropicIndex) {
for (const double enthalpy : enthalpies) { for (const double enthalpy : enthalpies) {
const double step = 2.0e-6 * std::max(1.0, std::abs(enthalpy)); const double step = 2.0e-6 * std::max(1.0, std::abs(enthalpy));
const double numericalDerivative = polytropic_barotrope_test_utils::centered_derivative( const double numericalDerivative =
polytropic_eos_test_utils::centered_derivative(
[&barotrope](const double perturbedEnthalpy) { [&barotrope](const double perturbedEnthalpy) {
return barotrope.density_from_enthalpy(perturbedEnthalpy); return barotrope.density_from_enthalpy(perturbedEnthalpy);
}, },
enthalpy, step enthalpy, step);
);
const double analyticDerivative = barotrope.density_derivative_from_enthalpy(enthalpy); const double analyticDerivative =
barotrope.density_derivative_from_enthalpy(enthalpy);
CAPTURE(polytropicIndex, enthalpy, step, numericalDerivative, analyticDerivative); CAPTURE(polytropicIndex, enthalpy, step, numericalDerivative,
analyticDerivative);
CHECK_THAT(numericalDerivative, Catch::Matchers::WithinRel(analyticDerivative, 5.0e-8)); CHECK_THAT(numericalDerivative,
Catch::Matchers::WithinRel(analyticDerivative, 5.0e-8));
} }
} }
} }
} }
TEST_CASE( TEST_CASE("Polytropic EOS Defines Consistent Surface And Exterior Behavior",
"Polytropic Barotrope Defines Consistent Surface And Exterior Behavior", tags::barotrope_eos_unit) {
tags::barotrope &tags::physics &tags::unit &tags::pressure
) {
constexpr std::array<double, 3> polytropicIndices{1.0, 1.5, 3.0}; constexpr std::array<double, 3> polytropicIndices{1.0, 1.5, 3.0};
constexpr double polytropicConstant = 0.47; constexpr double polytropicConstant = 0.47;
constexpr double exteriorEnthalpy = -0.3; constexpr double exteriorEnthalpy = -0.3;
for (const double polytropicIndex : polytropicIndices) { for (const double polytropicIndex : polytropicIndices) {
const mean_field::physics::PolytropicBarotrope barotrope(polytropicIndex, polytropicConstant); const mean_field::eos::Polytrope barotrope(polytropicIndex,
polytropicConstant);
DYNAMIC_SECTION("polytropic index n = " << polytropicIndex) { DYNAMIC_SECTION("polytropic index n = " << polytropicIndex) {
/* /*
@@ -246,9 +257,11 @@ TEST_CASE(
CHECK(barotrope.pressure_from_enthalpy(exteriorEnthalpy) == 0.0); CHECK(barotrope.pressure_from_enthalpy(exteriorEnthalpy) == 0.0);
CHECK(barotrope.density_derivative_from_enthalpy(exteriorEnthalpy) == 0.0); CHECK(barotrope.density_derivative_from_enthalpy(exteriorEnthalpy) ==
0.0);
CHECK(barotrope.pressure_derivative_from_enthalpy(exteriorEnthalpy) == 0.0); CHECK(barotrope.pressure_derivative_from_enthalpy(exteriorEnthalpy) ==
0.0);
/* /*
* At h = 0, rho(h) has a nonzero right * At h = 0, rho(h) has a nonzero right
@@ -257,55 +270,58 @@ TEST_CASE(
const double expectedSurfaceDensityDerivative = const double expectedSurfaceDensityDerivative =
polytropicIndex == 1.0 ? 1.0 / barotrope.enthalpy_scale() : 0.0; polytropicIndex == 1.0 ? 1.0 / barotrope.enthalpy_scale() : 0.0;
CHECK(barotrope.density_derivative_from_enthalpy(0.0) == expectedSurfaceDensityDerivative); CHECK(barotrope.density_derivative_from_enthalpy(0.0) ==
expectedSurfaceDensityDerivative);
} }
} }
} }
TEST_CASE( TEST_CASE("Polytropic EOS Rejects Invalid Physical Inputs",
"Polytropic Barotrope Rejects Invalid Physical Inputs", tags::barotrope_eos_unit) {
tags::barotrope &tags::physics &tags::unit &tags::pressure CHECK_THROWS_AS(mean_field::eos::Polytrope(0.999, 1.0),
) { std::invalid_argument);
CHECK_THROWS_AS(mean_field::physics::PolytropicBarotrope(0.999, 1.0), std::invalid_argument);
CHECK_THROWS_AS( CHECK_THROWS_AS(
mean_field::physics::PolytropicBarotrope(std::numeric_limits<double>::infinity(), 1.0), std::invalid_argument mean_field::eos::Polytrope(std::numeric_limits<double>::infinity(), 1.0),
); std::invalid_argument);
CHECK_THROWS_AS(mean_field::physics::PolytropicBarotrope(3.0, 0.0), std::invalid_argument); CHECK_THROWS_AS(mean_field::eos::Polytrope(3.0, 0.0), std::invalid_argument);
CHECK_THROWS_AS(mean_field::physics::PolytropicBarotrope(3.0, -1.0), std::invalid_argument); CHECK_THROWS_AS(mean_field::eos::Polytrope(3.0, -1.0), std::invalid_argument);
const mean_field::physics::PolytropicBarotrope barotrope(3.0, 0.75); const mean_field::eos::Polytrope barotrope(3.0, 0.75);
CHECK_THROWS_AS(barotrope.pressure_from_density(-0.1), std::domain_error); CHECK_THROWS_AS(barotrope.pressure_from_density(-0.1), std::domain_error);
CHECK_THROWS_AS(barotrope.enthalpy_from_density(-0.1), std::domain_error); CHECK_THROWS_AS(barotrope.enthalpy_from_density(-0.1), std::domain_error);
CHECK_THROWS_AS(barotrope.pressure_derivative_from_density(-0.1), std::domain_error); CHECK_THROWS_AS(barotrope.pressure_derivative_from_density(-0.1),
std::domain_error);
constexpr std::array<double, 3> nonfiniteValues{ constexpr std::array<double, 3> nonfiniteValues{
std::numeric_limits<double>::infinity(), -std::numeric_limits<double>::infinity(), std::numeric_limits<double>::infinity(),
std::numeric_limits<double>::quiet_NaN() -std::numeric_limits<double>::infinity(),
}; std::numeric_limits<double>::quiet_NaN()};
for (const double nonfiniteValue : nonfiniteValues) { for (const double nonfiniteValue : nonfiniteValues) {
CAPTURE(nonfiniteValue); CAPTURE(nonfiniteValue);
CHECK_THROWS_AS(barotrope.density_from_enthalpy(nonfiniteValue), std::domain_error); CHECK_THROWS_AS(barotrope.density_from_enthalpy(nonfiniteValue),
std::domain_error);
CHECK_THROWS_AS(barotrope.pressure_from_enthalpy(nonfiniteValue), std::domain_error); CHECK_THROWS_AS(barotrope.pressure_from_enthalpy(nonfiniteValue),
std::domain_error);
CHECK_THROWS_AS(barotrope.density_derivative_from_enthalpy(nonfiniteValue), std::domain_error); CHECK_THROWS_AS(barotrope.density_derivative_from_enthalpy(nonfiniteValue),
std::domain_error);
CHECK_THROWS_AS(barotrope.pressure_derivative_from_enthalpy(nonfiniteValue), std::domain_error); CHECK_THROWS_AS(barotrope.pressure_derivative_from_enthalpy(nonfiniteValue),
std::domain_error);
} }
} }
TEST_CASE( TEST_CASE("Pressure Force And Pressure Integral Have Distinct Registered Forms",
"Pressure Force And Pressure Integral Have Distinct Registered Forms", tags::barotrope_pressure_quadrature_unit) {
tags::barotrope &tags::pressure &tags::pressure_gradient &tags::quadrature &tags::unit
) {
using EnthalpyField = mean_field::field::Field<mean_field::field::Enthalpy>; using EnthalpyField = mean_field::field::Field<mean_field::field::Enthalpy>;
/* /*
@@ -320,34 +336,38 @@ TEST_CASE(
* *
* beyond the registered enthalpy operand. * beyond the registered enthalpy operand.
*/ */
constexpr int enthalpyOrder = mean_field::field::Enthalpy::Scalar::familyOrder; constexpr int enthalpyOrder =
mean_field::field::Enthalpy::Scalar::familyOrder;
constexpr int pressureExtraOrder = 3 * enthalpyOrder; constexpr int pressureExtraOrder = 3 * enthalpyOrder;
constexpr int geometryWeightOrder = 2; constexpr int geometryWeightOrder = 2;
constexpr mean_field::quadrature::Query pressureIntegralQuery = constexpr mean_field::quadrature::Query pressureIntegralQuery =
EnthalpyField::make_query<mean_field::field::Enthalpy::Form::PressureIntegral>( EnthalpyField::make_query<
mean_field::quadrature::QuadratureRole::diagnostic, geometryWeightOrder, mean_field::field::Enthalpy::Form::PressureIntegral>(
std::array<int, 1>{pressureExtraOrder}, mean_field::utils::DOMAINS::STELLAR, mean_field::quadrature::QuadratureRole::diagnostic,
mean_field::quadrature::MappingKind::general geometryWeightOrder, std::array<int, 1>{pressureExtraOrder},
); mean_field::utils::DOMAINS::STELLAR,
mean_field::quadrature::MappingKind::general);
constexpr mean_field::quadrature::Query pressureForceQuery = constexpr mean_field::quadrature::Query pressureForceQuery =
EnthalpyField::make_query<mean_field::field::Enthalpy::Form::PressureForce>( EnthalpyField::make_query<
mean_field::quadrature::QuadratureRole::discretization, geometryWeightOrder, mean_field::field::Enthalpy::Form::PressureForce>(
std::array<int, 1>{pressureExtraOrder}, mean_field::utils::DOMAINS::STELLAR, mean_field::quadrature::QuadratureRole::discretization,
mean_field::quadrature::MappingKind::general geometryWeightOrder, std::array<int, 1>{pressureExtraOrder},
); mean_field::utils::DOMAINS::STELLAR,
mean_field::quadrature::MappingKind::general);
STATIC_CHECK(mean_field::field::Enthalpy::Form::PressureIntegral::dynamicOrderCount == 1);
STATIC_CHECK(mean_field::field::Enthalpy::Form::PressureForce::dynamicOrderCount == 1);
STATIC_CHECK( STATIC_CHECK(
mean_field::field::Enthalpy::Form::PressureIntegral::policyKey != mean_field::field::Enthalpy::Form::PressureIntegral::dynamicOrderCount ==
mean_field::field::Enthalpy::Form::PressureForce::policyKey 1);
);
STATIC_CHECK(
mean_field::field::Enthalpy::Form::PressureForce::dynamicOrderCount == 1);
STATIC_CHECK(mean_field::field::Enthalpy::Form::PressureIntegral::policyKey !=
mean_field::field::Enthalpy::Form::PressureForce::policyKey);
REQUIRE(pressureIntegralQuery.base_order.has_value()); REQUIRE(pressureIntegralQuery.base_order.has_value());
@@ -374,13 +394,17 @@ TEST_CASE(
*/ */
CHECK(*pressureForceQuery.base_order == 16); CHECK(*pressureForceQuery.base_order == 16);
CHECK(pressureIntegralQuery.term == mean_field::quadrature::Term::pressure_integral); CHECK(pressureIntegralQuery.term ==
mean_field::quadrature::Term::pressure_integral);
CHECK(pressureForceQuery.term == mean_field::quadrature::Term::pressure_force); CHECK(pressureForceQuery.term ==
mean_field::quadrature::Term::pressure_force);
CHECK(pressureIntegralQuery.role == mean_field::quadrature::QuadratureRole::diagnostic); CHECK(pressureIntegralQuery.role ==
mean_field::quadrature::QuadratureRole::diagnostic);
CHECK(pressureForceQuery.role == mean_field::quadrature::QuadratureRole::discretization); CHECK(pressureForceQuery.role ==
mean_field::quadrature::QuadratureRole::discretization);
CHECK(pressureIntegralQuery.domain == mean_field::utils::DOMAINS::STELLAR); CHECK(pressureIntegralQuery.domain == mean_field::utils::DOMAINS::STELLAR);
@@ -391,16 +415,19 @@ TEST_CASE(
* controls. * controls.
*/ */
mean_field::quadrature::RuleSet ruleSet = mean_field::quadrature::RuleSet ruleSet =
mean_field::quadrature::make_rule_set(mean_field::quadrature::Mode::production); mean_field::quadrature::make_rule_set(
mean_field::quadrature::Mode::production);
ruleSet.pressure_integral.boost = 3; ruleSet.pressure_integral.boost = 3;
ruleSet.pressure_force.boost = 5; ruleSet.pressure_force.boost = 5;
const mean_field::quadrature::Policy policy(std::move(ruleSet)); const mean_field::quadrature::Policy policy(std::move(ruleSet));
const mean_field::quadrature::Resolution pressureIntegralResolution = policy.resolve(pressureIntegralQuery); const mean_field::quadrature::Resolution pressureIntegralResolution =
policy.resolve(pressureIntegralQuery);
const mean_field::quadrature::Resolution pressureForceResolution = policy.resolve(pressureForceQuery); const mean_field::quadrature::Resolution pressureForceResolution =
policy.resolve(pressureForceQuery);
CHECK(pressureIntegralResolution.base_order == 14); CHECK(pressureIntegralResolution.base_order == 14);
@@ -415,13 +442,12 @@ TEST_CASE(
CHECK(pressureForceResolution.order == 21); CHECK(pressureForceResolution.order == 21);
} }
TEST_CASE( TEST_CASE("Pressure Quadrature Exactly Integrates An N Three Polynomial",
"Pressure Quadrature Exactly Integrates An N Three Polynomial", tags::barotrope_pressure_quadrature_accuracy) {
tags::barotrope &tags::pressure &tags::pressure_gradient &tags::quadrature &tags::accuracy
) {
using EnthalpyField = mean_field::field::Field<mean_field::field::Enthalpy>; using EnthalpyField = mean_field::field::Field<mean_field::field::Enthalpy>;
constexpr int enthalpyOrder = mean_field::field::Enthalpy::Scalar::familyOrder; constexpr int enthalpyOrder =
mean_field::field::Enthalpy::Scalar::familyOrder;
constexpr int pressureExtraOrder = 3 * enthalpyOrder; constexpr int pressureExtraOrder = 3 * enthalpyOrder;
@@ -432,23 +458,27 @@ TEST_CASE(
* rho(h) = h^3, * rho(h) = h^3,
* P(h) = h^4 / 4. * P(h) = h^4 / 4.
*/ */
const mean_field::physics::PolytropicBarotrope barotrope(3.0, 0.25); const mean_field::eos::Polytrope barotrope(3.0, 0.25);
constexpr mean_field::quadrature::Query pressureIntegralQuery = constexpr mean_field::quadrature::Query pressureIntegralQuery =
EnthalpyField::make_query<mean_field::field::Enthalpy::Form::PressureIntegral>( EnthalpyField::make_query<
mean_field::quadrature::QuadratureRole::diagnostic, 0, std::array<int, 1>{pressureExtraOrder}, mean_field::field::Enthalpy::Form::PressureIntegral>(
mean_field::utils::DOMAINS::STELLAR, mean_field::quadrature::MappingKind::affine mean_field::quadrature::QuadratureRole::diagnostic, 0,
); std::array<int, 1>{pressureExtraOrder},
mean_field::utils::DOMAINS::STELLAR,
mean_field::quadrature::MappingKind::affine);
constexpr mean_field::quadrature::Query pressureForceQuery = constexpr mean_field::quadrature::Query pressureForceQuery =
EnthalpyField::make_query<mean_field::field::Enthalpy::Form::PressureForce>( EnthalpyField::make_query<
mean_field::quadrature::QuadratureRole::discretization, 0, std::array<int, 1>{pressureExtraOrder}, mean_field::field::Enthalpy::Form::PressureForce>(
mean_field::utils::DOMAINS::STELLAR, mean_field::quadrature::MappingKind::affine mean_field::quadrature::QuadratureRole::discretization, 0,
); std::array<int, 1>{pressureExtraOrder},
mean_field::utils::DOMAINS::STELLAR,
mean_field::quadrature::MappingKind::affine);
const mean_field::quadrature::RuleFactory ruleFactory{ const mean_field::quadrature::RuleFactory ruleFactory{
mean_field::quadrature::Policy(mean_field::quadrature::make_rule_set(mean_field::quadrature::Mode::production)) mean_field::quadrature::Policy(mean_field::quadrature::make_rule_set(
}; mean_field::quadrature::Mode::production))};
const mean_field::quadrature::MfemRule pressureIntegralRule = const mean_field::quadrature::MfemRule pressureIntegralRule =
ruleFactory.get(pressureIntegralQuery, mfem::Geometry::CUBE); ruleFactory.get(pressureIntegralQuery, mfem::Geometry::CUBE);
@@ -466,15 +496,17 @@ TEST_CASE(
* *
* P = x^12 y^12 z^12 / 4. * P = x^12 y^12 z^12 / 4.
*/ */
const double numericalPressureIntegral = polytropic_barotrope_test_utils::integrate_cube( const double numericalPressureIntegral =
*pressureIntegralRule.integration_rule, [&barotrope](const mfem::IntegrationPoint &integrationPoint) { polytropic_eos_test_utils::integrate_cube(
const double coordinateProduct = integrationPoint.x * integrationPoint.y * integrationPoint.z; *pressureIntegralRule.integration_rule,
[&barotrope](const mfem::IntegrationPoint &integrationPoint) {
const double coordinateProduct =
integrationPoint.x * integrationPoint.y * integrationPoint.z;
const double enthalpy = std::pow(coordinateProduct, 3.0); const double enthalpy = std::pow(coordinateProduct, 3.0);
return barotrope.pressure_from_enthalpy(enthalpy); return barotrope.pressure_from_enthalpy(enthalpy);
} });
);
const double analyticPressureIntegral = 0.25 / std::pow(13.0, 3.0); const double analyticPressureIntegral = 0.25 / std::pow(13.0, 3.0);
@@ -489,32 +521,38 @@ TEST_CASE(
* -P div(w) * -P div(w)
* = -x^14 y^14 z^14 / 4. * = -x^14 y^14 z^14 / 4.
*/ */
const double numericalPressureForceIntegral = polytropic_barotrope_test_utils::integrate_cube( const double numericalPressureForceIntegral =
*pressureForceRule.integration_rule, [&barotrope](const mfem::IntegrationPoint &integrationPoint) { polytropic_eos_test_utils::integrate_cube(
const double coordinateProduct = integrationPoint.x * integrationPoint.y * integrationPoint.z; *pressureForceRule.integration_rule,
[&barotrope](const mfem::IntegrationPoint &integrationPoint) {
const double coordinateProduct =
integrationPoint.x * integrationPoint.y * integrationPoint.z;
const double enthalpy = std::pow(coordinateProduct, 3.0); const double enthalpy = std::pow(coordinateProduct, 3.0);
const double pressure = barotrope.pressure_from_enthalpy(enthalpy); const double pressure = barotrope.pressure_from_enthalpy(enthalpy);
const double testDivergence = integrationPoint.x * integrationPoint.x * integrationPoint.y * const double testDivergence =
integrationPoint.x * integrationPoint.x * integrationPoint.y *
integrationPoint.y * integrationPoint.z * integrationPoint.z; integrationPoint.y * integrationPoint.z * integrationPoint.z;
return -pressure * testDivergence; return -pressure * testDivergence;
} });
);
const double analyticPressureForceIntegral = -0.25 / std::pow(15.0, 3.0); const double analyticPressureForceIntegral = -0.25 / std::pow(15.0, 3.0);
INFO("Pressure-integral quadrature order = " << pressureIntegralRule.resolution.order); INFO("Pressure-integral quadrature order = "
<< pressureIntegralRule.resolution.order);
INFO("Pressure-force quadrature order = " << pressureForceRule.resolution.order); INFO("Pressure-force quadrature order = "
<< pressureForceRule.resolution.order);
INFO("Numerical pressure integral = " << numericalPressureIntegral); INFO("Numerical pressure integral = " << numericalPressureIntegral);
INFO("Analytic pressure integral = " << analyticPressureIntegral); INFO("Analytic pressure integral = " << analyticPressureIntegral);
INFO("Numerical pressure-force integral = " << numericalPressureForceIntegral); INFO(
"Numerical pressure-force integral = " << numericalPressureForceIntegral);
INFO("Analytic pressure-force integral = " << analyticPressureForceIntegral); INFO("Analytic pressure-force integral = " << analyticPressureForceIntegral);
@@ -526,7 +564,10 @@ TEST_CASE(
CHECK(pressureForceRule.resolution.order == 14); CHECK(pressureForceRule.resolution.order == 14);
CHECK_THAT(numericalPressureIntegral, Catch::Matchers::WithinAbs(analyticPressureIntegral, 5.0e-14)); CHECK_THAT(numericalPressureIntegral,
Catch::Matchers::WithinAbs(analyticPressureIntegral, 5.0e-14));
CHECK_THAT(numericalPressureForceIntegral, Catch::Matchers::WithinAbs(analyticPressureForceIntegral, 5.0e-14)); CHECK_THAT(
numericalPressureForceIntegral,
Catch::Matchers::WithinAbs(analyticPressureForceIntegral, 5.0e-14));
} }

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More