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/fem.cpp
libmeanfield/impl/mapping/coefficients.cpp
libmeanfield/impl/mapping/domain_mapper.cpp
libmeanfield/impl/mapping/compactification/kelvin.cpp
libmeanfield/impl/physics/gravity.cpp
libmeanfield/impl/physics/solid.cpp
@@ -56,7 +55,7 @@ target_sources(mean_field
libmeanfield/impl/integrators/gravity.cpp
libmeanfield/impl/integrators/mass_continuity.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/operators/gravity_field.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/kelvin.cppm
libmeanfield/interface/mapping/compactification/options.cppm
libmeanfield/interface/physics/context.cppm
libmeanfield/interface/physics/gravity.cppm
libmeanfield/interface/physics/solid.cppm
libmeanfield/interface/utils/domain.cppm
@@ -126,7 +124,6 @@ target_sources(mean_field
libmeanfield/interface/field/field_base.cppm
libmeanfield/interface/field/field_registry.cppm
libmeanfield/interface/field/field_mfem.cppm
libmeanfield/interface/physics/barotrope.cppm
libmeanfield/interface/operators/prepared_barotropic_closure_operator.cppm
libmeanfield/interface/operators/contexts/barotropic_closure_linearization_context.cppm
libmeanfield/interface/physics/rigid_rotation.cppm

View File

@@ -10,7 +10,6 @@
#include <mfem.hpp>
#include <mpi.h>
import mean_field;
import test_helpers;
import experiment;
@@ -38,47 +37,66 @@ struct AccuracyBudgetMetrics {
static double global_norm(const mfem::Vector &vector, MPI_Comm communicator) {
const double local_norm_squared = vector * vector;
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);
}
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;
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;
}
static void zero_vacuum_density(const mean_field::fem::FEM& fem, mfem::GridFunction& density) {
for (int index = 0; index < fem.vacuum_tdof_rho.Size(); ++index) {
density(fem.vacuum_tdof_rho[index]) = 0.0;
}
static void zero_vacuum_density(const mean_field::fem::FEM &fem,
mfem::GridFunction &density) {
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) {
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(
mean_field::fem::FEM& fem,
const mfem::GridFunction& displacement,
const double mass,
const double stellar_radius
) {
static_cast<void>(displacement);
mean_field::fem::FEM &fem, const mfem::GridFunction &displacement,
const double mass, const double stellar_radius) {
*fem.displacement = displacement;
mfem::Vector local_rhs(fem.RT_fes->GetVSize());
mfem::Vector local_rhs(fem.gravityFluxFes->GetVSize());
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);
mean_field::mapping::GridFunctionMappingEvaluator mapping_evaluator(
*fem.domainMapperStateless, *fem.displacement,
*fem.compactificationCoordinate);
for (int element_id = 0; element_id < fem.mesh->GetNE(); ++element_id) {
const mfem::FiniteElement& gravity_element = *fem.RT_fes->GetFE(element_id);
mfem::ElementTransformation* transformation = fem.mesh->GetElementTransformation(element_id);
const mfem::FiniteElement &gravity_element =
*fem.gravityFluxFes->GetFE(element_id);
mfem::ElementTransformation *transformation =
fem.mesh->GetElementTransformation(element_id);
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 dimension = transformation->GetSpaceDim();
@@ -86,32 +104,38 @@ static mfem::Vector assemble_monopole_projection_rhs(
mfem::Vector physical_position(dimension);
mfem::Vector analytic_field(dimension);
mfem::Vector pulled_field(dimension);
mfem::DenseMatrix mapping_jacobian(dimension);
mfem::DenseMatrix vector_shape(dof_count, dimension);
element_rhs = 0.0;
const mfem::IntegrationRule& rule = mfem::IntRules.Get(
transformation->GetGeometryType(),
quadrature_order
);
const mfem::IntegrationRule &rule =
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);
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();
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;
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 {
analytic_field *= mean_field::utils::G * mass /
(stellar_radius * stellar_radius * stellar_radius);
}
fem.mapping->ComputeJacobian(*transformation, mapping_jacobian);
mapping_jacobian.MultTranspose(analytic_field, pulled_field);
mapping_context.mapping_jacobian.MultTranspose(analytic_field,
pulled_field);
transformation->SetIntPoint(&point);
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 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);
}
mfem::Vector true_rhs(fem.RT_fes->GetTrueVSize());
mfem::Vector true_rhs(fem.gravityFluxFes->GetTrueVSize());
true_rhs = 0.0;
const mfem::Operator* prolongation = fem.RT_fes->GetProlongationMatrix();
const mfem::Operator *prolongation =
fem.gravityFluxFes->GetProlongationMatrix();
if (prolongation != nullptr) {
prolongation->MultTranspose(local_rhs, true_rhs);
} else {
@@ -142,120 +168,134 @@ static mfem::Vector assemble_monopole_projection_rhs(
return true_rhs;
}
static mfem::Vector project_monopole_gradient(
mean_field::fem::FEM& fem,
static mfem::Vector
project_monopole_gradient(mean_field::fem::FEM &fem,
const mfem::GridFunction &displacement,
const double mass,
const double stellar_radius
) {
const double mass, const double stellar_radius) {
mfem::Vector displacement_true;
displacement.GetTrueDofs(displacement_true);
const mfem::Vector projection_rhs = assemble_monopole_projection_rhs(
fem,
displacement,
mass,
stellar_radius
);
const mfem::Vector projection_rhs_true =
assemble_monopole_projection_rhs(fem, displacement, mass, stellar_radius);
mean_field::operators::PreparedMappedHDivMassOperator mass_operator(
fem,
*fem.domain_mapper_stateless
);
mass_operator.Prepare(displacement_true);
fem, *fem.domainMapperStateless);
mass_operator.Prepare(
mass_operator.GetDisplacementMap().gather(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.SetRelTol(1.0e-11);
solver.SetAbsTol(1.0e-13);
solver.SetMaxIter(4000);
solver.SetPrintLevel(0);
mfem::Vector projected_gradient(fem.RT_fes->GetTrueVSize());
projected_gradient = 0.0;
solver.Mult(projection_rhs, projected_gradient);
mfem::Vector projected_gradient_reduced(
mass_operator.GetFluxMap().reduced_size());
projected_gradient_reduced = 0.0;
solver.Mult(projection_rhs, projected_gradient_reduced);
mfem::Vector residual;
mass_operator.Mult(projected_gradient, residual);
mass_operator.Mult(projected_gradient_reduced, residual);
residual -= projection_rhs;
const double relative_residual = global_norm(residual, fem.RT_fes->GetComm()) /
std::max(global_norm(projection_rhs, fem.RT_fes->GetComm()), std::numeric_limits<double>::epsilon());
const double relative_residual =
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(relative_residual < 1.0e-8);
return projected_gradient;
return mass_operator.GetFluxMap().scatter(projected_gradient_reduced);
}
static double mapped_hdiv_relative_gap(
mean_field::fem::FEM& fem,
static double mapped_hdiv_relative_gap(mean_field::fem::FEM &fem,
const mfem::GridFunction &displacement,
const mfem::Vector &calculated,
const mfem::Vector& reference
) {
const mfem::Vector &reference) {
mfem::Vector displacement_true;
displacement.GetTrueDofs(displacement_true);
mean_field::operators::PreparedMappedHDivMassOperator mass_operator(
fem,
*fem.domain_mapper_stateless
);
mass_operator.Prepare(displacement_true);
fem, *fem.domainMapperStateless);
mass_operator.Prepare(
mass_operator.GetDisplacementMap().gather(displacement_true));
mfem::Vector difference(calculated);
difference -= reference;
mfem::Vector difference_action;
mfem::Vector reference_action;
mass_operator.Mult(difference, difference_action);
mass_operator.Mult(reference, reference_action);
const mfem::Vector reduced_difference =
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 reference_energy = global_dot(reference, reference_action, fem.RT_fes->GetComm());
MFEM_VERIFY(reference_energy > 0.0, "Projected monopole field has zero mapped H(div) norm.");
const double difference_energy = global_dot(
reduced_difference, difference_action, fem.gravityFluxFes->GetComm());
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);
}
static AccuracyBudgetEnergies measure_stellar_energies(
mean_field::fem::FEM& fem,
static AccuracyBudgetEnergies
measure_stellar_energies(mean_field::fem::FEM &fem,
const mfem::GridFunction &density,
const mean_field::physics::GravitySolution& solution
) {
const int vacuum_attribute = fem.domain_mapper_stateless->GetVacuumElementAttribute();
const mean_field::physics::GravitySolution &solution) {
const int vacuum_attribute = field_dof_test_utils::vacuum_material_attribute;
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_virial = 0.0;
mfem::Vector physical_position(3);
mfem::Vector reference_field(3);
mfem::Vector physical_field(3);
mfem::DenseMatrix mapping_jacobian(3);
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) {
continue;
}
const mfem::IntegrationRule& rule = mfem::IntRules.Get(
transformation->GetGeometryType(),
quadrature_order
);
const mfem::IntegrationRule &rule =
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);
transformation->SetIntPoint(&point);
fem.mapping->GetPhysicalPoint(*transformation, point, physical_position);
fem.mapping->ComputeJacobian(*transformation, mapping_jacobian);
const double mapping_determinant = mapping_jacobian.Det();
MFEM_VERIFY(mapping_determinant > 0.0, "Non-positive mapping determinant in energy diagnostic.");
mean_field::mapping::MappingPointContext mapping_context;
MFEM_VERIFY(
mapping_evaluator.EvaluatePoint(*transformation, point,
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);
mapping_jacobian.Mult(reference_field, physical_field);
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 phi = solution.phi.GetValue(element_id, point);
local_binding += 0.5 * rho * phi * weight;
@@ -264,34 +304,49 @@ static AccuracyBudgetEnergies measure_stellar_energies(
}
AccuracyBudgetEnergies energies;
MPI_Allreduce(&local_binding, &energies.binding, 1, MPI_DOUBLE, MPI_SUM, fem.L2_fes->GetComm());
MPI_Allreduce(&local_virial, &energies.virial, 1, MPI_DOUBLE, MPI_SUM, fem.L2_fes->GetComm());
MPI_Allreduce(&local_binding, &energies.binding, 1, MPI_DOUBLE, MPI_SUM,
fem.densityFes->GetComm());
MPI_Allreduce(&local_virial, &energies.virial, 1, MPI_DOUBLE, MPI_SUM,
fem.densityFes->GetComm());
return energies;
}
static double reduced_gravity_relative_residual(
mean_field::fem::FEM& fem,
const mfem::GridFunction& density,
mean_field::fem::FEM &fem, const mfem::GridFunction &density,
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;
constexpr auto gradient_block = 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>(
mean_field::utils::blocks::gravity_field.poisson_term
);
constexpr auto gradient_block =
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>(
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{
fem.L2_fes->GetTrueVSize(), fem.Vec_H1_fes->GetTrueVSize(),
fem.RT_fes->GetTrueVSize(), fem.L2_fes->GetTrueVSize()
};
density_map.reduced_size(), displacement_map.reduced_size(),
gravity_flux_map.reduced_size(), gravity_potential_map.reduced_size()};
const std::array<int, GravityFieldForm::residual_block_count> residual_sizes{
fem.RT_fes->GetTrueVSize(), fem.L2_fes->GetTrueVSize()
};
const mean_field::utils::blocks::form_layout<GravityFieldForm> layout(value_sizes, residual_sizes);
gravity_flux_map.reduced_size(), gravity_potential_map.reduced_size()};
const mean_field::utils::blocks::form_layout<GravityFieldForm> layout(
value_sizes, residual_sizes);
mfem::Vector density_true;
mfem::Vector displacement_true;
@@ -302,60 +357,47 @@ static double reduced_gravity_relative_residual(
solution.gradPhi.GetTrueDofs(gradient_true);
solution.phi.GetTrueDofs(potential_true);
mean_field::operators::context::gravity_field::GravityFieldLinearizationContext linearization_context(
fem,
*fem.domain_mapper_stateless
);
mean_field::operators::context::gravity_field::
GravityFieldLinearizationContext linearization_context(
fem, *fem.domainMapperStateless);
mean_field::operators::GravityFieldJacobianOperator jacobian(
fem,
*fem.domain_mapper_stateless,
linearization_context,
layout.value_offsets(),
layout.residual_offsets()
);
fem, *fem.domainMapperStateless, linearization_context,
layout.value_offsets(), layout.residual_offsets());
mean_field::operators::GravityFieldOperator field_operator(
fem,
*fem.domain_mapper_stateless,
linearization_context,
layout.value_offsets(),
jacobian
);
mean_field::operators::context::gravity_field::GravityFieldGeometryContext geometry_context(
fem,
*fem.domain_mapper_stateless
);
fem, *fem.domainMapperStateless, linearization_context,
layout.value_offsets(), jacobian);
mean_field::operators::context::gravity_field::GravityFieldGeometryContext
geometry_context(fem, *fem.domainMapperStateless);
mean_field::operators::ReducedGravityFieldOperator reduced_operator(
field_operator,
geometry_context,
displacement_true
);
field_operator, geometry_context,
displacement_map.gather(displacement_true));
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());
state = 0.0;
state.GetBlock(gradient_block) = gradient_true;
state.GetBlock(poisson_block) = potential_true;
state.GetBlock(gradient_block) = gravity_flux_map.gather(gradient_true);
state.GetBlock(poisson_block) = gravity_potential_map.gather(potential_true);
mfem::Vector residual;
reduced_operator.Mult(state, residual);
residual -= right_hand_side;
return global_norm(residual, fem.L2_fes->GetComm()) /
std::max(global_norm(right_hand_side, fem.L2_fes->GetComm()), std::numeric_limits<double>::epsilon());
return global_norm(residual, fem.mesh->GetComm()) /
std::max(global_norm(right_hand_side, fem.mesh->GetComm()),
std::numeric_limits<double>::epsilon());
}
static AccuracyBudgetMetrics measure_monopole_accuracy(
mean_field::fem::FEM& fem,
static AccuracyBudgetMetrics
measure_monopole_accuracy(mean_field::fem::FEM &fem,
const mfem::GridFunction &density,
const mfem::GridFunction &displacement,
const mean_field::physics::GravitySolution &solution,
const mfem::ParGridFunction &projected_potential,
const mfem::Vector &projected_gradient,
const double mass,
const double stellar_radius
) {
const double mass, const double stellar_radius) {
mfem::Vector solution_gradient;
solution.gradPhi.GetTrueDofs(solution_gradient);
@@ -364,7 +406,8 @@ static AccuracyBudgetMetrics measure_monopole_accuracy(
solution.phi.GetTrueDofs(solution_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);
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_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);
mean_field::mapping::GridFunctionMappingEvaluator mapping_evaluator(
*fem.domainMapperStateless, *fem.displacement,
*fem.compactificationCoordinate);
mfem::Vector physical_position(3);
mfem::Vector analytic_gradient(3);
mfem::Vector solution_reference_gradient(3);
mfem::Vector projection_reference_gradient(3);
mfem::Vector solution_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) {
mfem::ElementTransformation* transformation = fem.mesh->GetElementTransformation(element_id);
const mfem::IntegrationRule& rule = mfem::IntRules.Get(
transformation->GetGeometryType(),
quadrature_order
);
mfem::ElementTransformation *transformation =
fem.mesh->GetElementTransformation(element_id);
const mfem::IntegrationRule &rule =
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);
transformation->SetIntPoint(&point);
fem.mapping->GetPhysicalPoint(*transformation, point, physical_position);
fem.mapping->ComputeJacobian(*transformation, mapping_jacobian);
const double mapping_determinant = mapping_jacobian.Det();
MFEM_VERIFY(mapping_determinant > 0.0, "Non-positive mapping determinant in accuracy diagnostic.");
mean_field::mapping::MappingPointContext mapping_context;
MFEM_VERIFY(
mapping_evaluator.EvaluatePoint(*transformation, point,
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();
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;
double analytic_potential = 0.0;
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;
} else {
analytic_gradient *= mean_field::utils::G * mass /
(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) /
(2.0 * stellar_radius * stellar_radius * stellar_radius);
}
solution.gradPhi.GetVectorValue(element_id, point, solution_reference_gradient);
mapping_jacobian.Mult(solution_reference_gradient, solution_physical_gradient);
solution.gradPhi.GetVectorValue(element_id, point,
solution_reference_gradient);
mapping_jacobian.Mult(solution_reference_gradient,
solution_physical_gradient);
solution_physical_gradient /= mapping_determinant;
projected_gradient_grid_function.GetVectorValue(element_id, point, projection_reference_gradient);
mapping_jacobian.Mult(projection_reference_gradient, projection_physical_gradient);
projected_gradient_grid_function.GetVectorValue(
element_id, point, projection_reference_gradient);
mapping_jacobian.Mult(projection_reference_gradient,
projection_physical_gradient);
projection_physical_gradient /= mapping_determinant;
const double solution_potential_value = solution.phi.GetValue(element_id, point);
const double projection_potential_value = projected_potential.GetValue(element_id, point);
const double weight = point.weight * transformation->Weight() * mapping_determinant;
const double solution_potential_value =
solution.phi.GetValue(element_id, point);
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;
projection_physical_gradient -= analytic_gradient;
local_solution_gradient_error += weight * (solution_physical_gradient * solution_physical_gradient);
local_projection_gradient_error += weight * (projection_physical_gradient * projection_physical_gradient);
local_solution_gradient_error +=
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_solution_potential_error += weight *
(solution_potential_value - analytic_potential) * (solution_potential_value - analytic_potential);
local_projection_potential_error += weight *
(projection_potential_value - analytic_potential) * (projection_potential_value - analytic_potential);
local_solution_potential_error +=
weight * (solution_potential_value - analytic_potential) *
(solution_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;
}
}
const std::array<double, 6> local_values{
local_solution_gradient_error, local_projection_gradient_error, local_gradient_norm,
local_solution_potential_error, local_projection_potential_error, local_potential_norm
};
const std::array<double, 6> local_values{local_solution_gradient_error,
local_projection_gradient_error,
local_gradient_norm,
local_solution_potential_error,
local_projection_potential_error,
local_potential_norm};
std::array<double, 6> global_values{};
MPI_Allreduce(
local_values.data(), global_values.data(), static_cast<int>(local_values.size()),
MPI_DOUBLE, MPI_SUM, fem.L2_fes->GetComm()
);
MPI_Allreduce(local_values.data(), global_values.data(),
static_cast<int>(local_values.size()), MPI_DOUBLE, MPI_SUM,
fem.mesh->GetComm());
const AccuracyBudgetEnergies energies = measure_stellar_energies(fem, density, solution);
const double analytic_energy = -3.0 * mean_field::utils::G * mass * mass / (5.0 * stellar_radius);
const AccuracyBudgetEnergies energies =
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[5] > 0.0);
AccuracyBudgetMetrics metrics;
metrics.direct_relative_residual = reduced_gravity_relative_residual(fem, density, displacement, solution);
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.direct_relative_residual =
reduced_gravity_relative_residual(fem, density, displacement, solution);
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(
fem, displacement, solution_gradient, projected_gradient
);
metrics.potential_relative_error = std::sqrt(global_values[3] / global_values[5]);
metrics.potential_projection_relative_error = std::sqrt(global_values[4] / global_values[5]);
fem, displacement, solution_gradient, projected_gradient);
metrics.potential_relative_error =
std::sqrt(global_values[3] / global_values[5]);
metrics.potential_projection_relative_error =
std::sqrt(global_values[4] / global_values[5]);
mfem::Vector potential_difference(solution_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);
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;
metrics.binding_relative_error = std::abs(energies.binding - analytic_energy) / std::abs(analytic_energy);
metrics.virial_relative_error = 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());
metrics.binding_relative_error =
std::abs(energies.binding - analytic_energy) / std::abs(analytic_energy);
metrics.virial_relative_error =
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;
}
static void run_monopole_case(
const std::string& sweep_name,
static void run_monopole_case(const std::string &sweep_name,
const std::string &case_name,
mean_field::utils::Args args,
const double solver_tolerance,
const int quadrature_boost
) {
const int quadrature_boost) {
args.p.rtol = solver_tolerance;
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.quadrature.global_boost = quadrature_boost;
mean_field::fem::FEM fem = mean_field::fem::setup_fem(args.mesh_file, args, 0);
REQUIRE(fem.mapping != nullptr);
REQUIRE(fem.domain_mapper_stateless != nullptr);
mean_field::fem::FEM fem =
mean_field::fem::setup_fem(args.mesh_file, args, 0);
REQUIRE(fem.domainMapperStateless != nullptr);
const double stellar_radius = mean_field::utils::RADIUS;
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;
fem.mapping->ResetDisplacement();
mean_field::physics::update_stiffness_matrix(fem);
mfem::GridFunction density(fem.L2_fes.get());
*fem.displacement = 0.0;
mfem::GridFunction density(fem.densityFes.get());
density = density_value;
zero_vacuum_density(fem, density);
mean_field::analysis::conserve_mass(fem, density, mass);
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 =
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();
if (radius >= stellar_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) /
(2.0 * stellar_radius * stellar_radius * stellar_radius);
};
mean_field::mapping::PhysicalPositionFunctionCoefficient potential_coefficient(
*fem.mapping,
analytic_potential
);
mfem::ParGridFunction projected_potential(fem.L2_fes.get());
mean_field::mapping::PhysicalPositionFunctionCoefficient
potential_coefficient(*fem.domainMapperStateless, *fem.displacement,
*fem.compactificationCoordinate,
analytic_potential);
mfem::ParGridFunction projected_potential(fem.gravityPotentialFes.get());
projected_potential.ProjectCoefficient(potential_coefficient);
const mfem::Vector projected_gradient = project_monopole_gradient(
fem,
displacement,
mass,
stellar_radius
);
const mfem::Vector projected_gradient =
project_monopole_gradient(fem, displacement, mass, stellar_radius);
const AccuracyBudgetMetrics metrics = measure_monopole_accuracy(
fem,
density,
displacement,
solution,
projected_potential,
projected_gradient,
mass,
stellar_radius
);
fem, density, displacement, solution, projected_potential,
projected_gradient, mass, stellar_radius);
REQUIRE(std::isfinite(metrics.direct_relative_residual));
REQUIRE(std::isfinite(metrics.gradient_relative_error));
@@ -553,64 +624,51 @@ static void run_monopole_case(
REQUIRE(std::isfinite(metrics.virial_consistency_error));
record_experiment_result(
sweep_name,
case_name,
{
{"solver_rtol", std::to_string(solver_tolerance)},
sweep_name, case_name,
{{"solver_rtol", std::to_string(solver_tolerance)},
{"quadrature_global_boost", std::to_string(quadrature_boost)},
{"mesh_file", args.mesh_file}
},
{
{"direct_relative_residual", metrics.direct_relative_residual},
{"mesh_file", args.mesh_file}},
{{"direct_relative_residual", metrics.direct_relative_residual},
{"gradient_relative_error", metrics.gradient_relative_error},
{"gradient_projection_relative_error", metrics.gradient_projection_relative_error},
{"gradient_solution_projection_gap", metrics.gradient_solution_projection_gap},
{"gradient_projection_relative_error",
metrics.gradient_projection_relative_error},
{"gradient_solution_projection_gap",
metrics.gradient_solution_projection_gap},
{"potential_relative_error", metrics.potential_relative_error},
{"potential_projection_relative_error", metrics.potential_projection_relative_error},
{"potential_solution_projection_gap", metrics.potential_solution_projection_gap},
{"potential_projection_relative_error",
metrics.potential_projection_relative_error},
{"potential_solution_projection_gap",
metrics.potential_solution_projection_gap},
{"binding_relative_error", metrics.binding_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();
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) {
run_monopole_case(
"solver_tolerance",
"uniform_monopole",
args,
solver_tolerance,
0
);
run_monopole_case("solver_tolerance", "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();
constexpr std::array<int, 3> quadrature_boosts{0, 4, 8};
for (const int quadrature_boost : quadrature_boosts) {
run_monopole_case(
"quadrature",
"uniform_monopole",
args,
1.0e-13,
quadrature_boost
);
run_monopole_case("quadrature", "uniform_monopole", args, 1.0e-13,
quadrature_boost);
}
}
TEST_CASE("Uniform Monopole Accuracy Budget: Projection Decomposition", tags::gravity & tags::accuracy & tags::integration) {
run_monopole_case(
"projection_decomposition",
"uniform_monopole",
test_utils::setup_args(),
1.0e-13,
0
);
TEST_CASE("Uniform Monopole Accuracy Budget: Projection Decomposition",
tags::gravity_analytic_accuracy) {
run_monopole_case("projection_decomposition", "uniform_monopole",
test_utils::setup_args(), 1.0e-13, 0);
}

View File

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

View File

@@ -21,11 +21,8 @@ import :utils.misc;
import :utils.user;
namespace mean_field::fem {
FEM setup_fem(
const std::string &filename,
const utils::Args &args,
const int extraRefine
) {
FEM setup_fem(const std::string &filename, const utils::Args &args,
const int extraRefine) {
FEM fem;
using GravityPotential = field::Gravity::Potential;
@@ -33,6 +30,7 @@ namespace mean_field::fem {
using DisplacementVector = field::Displacement::Vector;
using DensityScalar = field::Density::Scalar;
using EnthalpyScalar = field::Enthalpy::Scalar;
using DomainSchema = utils::domain::CoreEnvelopeVacuumDomainSchema;
// =====================================================================
// Section 1: Mesh construction
@@ -47,9 +45,11 @@ namespace mean_field::fem {
int mpiSize = 1;
MPI_Comm_size(MPI_COMM_WORLD, &mpiSize);
const std::unique_ptr<int[]> meshPartitioning(fem.smesh.mesh->GeneratePartitioning(mpiSize, 1));
const std::unique_ptr<int[]> meshPartitioning(
fem.smesh.mesh->GeneratePartitioning(mpiSize, 1));
fem.mesh = std::make_unique<mfem::ParMesh>(MPI_COMM_WORLD, *fem.smesh.mesh, meshPartitioning.get(), 1);
fem.mesh = std::make_unique<mfem::ParMesh>(MPI_COMM_WORLD, *fem.smesh.mesh,
meshPartitioning.get(), 1);
fem.mesh->EnsureNodes();
@@ -69,22 +69,22 @@ namespace mean_field::fem {
throw std::runtime_error("Values for exterior coordinate not set.");
}
const mfem::FiniteElementSpace &serialCoordinateSpace = *fem.smesh.exterior_coordinate->space;
const mfem::FiniteElementSpace &serialCoordinateSpace =
*fem.smesh.exterior_coordinate->space;
const mfem::GridFunction &serialCoordinate = *fem.smesh.exterior_coordinate->values;
const mfem::GridFunction &serialCoordinate =
*fem.smesh.exterior_coordinate->values;
if (serialCoordinate.FESpace() != &serialCoordinateSpace) {
throw std::runtime_error(
"Exterior coordinate values are not associated with the "
"supplied finite-element space."
);
"supplied finite-element space.");
}
if (serialCoordinateSpace.GetMesh() != fem.smesh.mesh.get()) {
throw std::runtime_error(
"Exterior coordinate space is not associated with the "
"loaded STROID mesh."
);
"loaded STROID mesh.");
}
if (serialCoordinateSpace.GetVDim() != 1) {
@@ -94,29 +94,30 @@ namespace mean_field::fem {
if (serialCoordinate.Size() != serialCoordinateSpace.GetVSize()) {
throw std::runtime_error(
"Exterior coordinate value count does not match its "
"finite-element space."
);
"finite-element space.");
}
const int compactificationOrder = serialCoordinateSpace.GetMaxElementOrder();
const int dimension = fem.mesh->Dimension();
fem.compactificationFec = std::make_unique<mfem::H1_FECollection>(compactificationOrder, dimension);
fem.compactificationFec =
std::make_unique<mfem::H1_FECollection>(compactificationOrder, dimension);
fem.compactificationFes =
std::make_unique<mfem::ParFiniteElementSpace>(fem.mesh.get(), fem.compactificationFec.get());
fem.compactificationFes = std::make_unique<mfem::ParFiniteElementSpace>(
fem.mesh.get(), fem.compactificationFec.get());
mfem::ParGridFunction distributedCoordinate(fem.mesh.get(), &serialCoordinate, meshPartitioning.get());
mfem::ParGridFunction distributedCoordinate(fem.mesh.get(), &serialCoordinate,
meshPartitioning.get());
if (distributedCoordinate.Size() != fem.compactificationFes->GetVSize()) {
throw std::runtime_error(
"Distributed exterior coordinate does not match the "
"constructed parallel finite-element space."
);
"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;
@@ -128,7 +129,8 @@ namespace mean_field::fem {
const double value = (*fem.compactificationCoordinate)(index);
if (!std::isfinite(value)) {
throw std::runtime_error("Exterior coordinate contains a non-finite value.");
throw std::runtime_error(
"Exterior coordinate contains a non-finite value.");
}
localMinimum = std::min(localMinimum, value);
@@ -139,17 +141,18 @@ namespace mean_field::fem {
double globalMinimum = 0.0;
double globalMaximum = 0.0;
MPI_Allreduce(&localMinimum, &globalMinimum, 1, MPI_DOUBLE, MPI_MIN, MPI_COMM_WORLD);
MPI_Allreduce(&localMinimum, &globalMinimum, 1, MPI_DOUBLE, MPI_MIN,
MPI_COMM_WORLD);
MPI_Allreduce(&localMaximum, &globalMaximum, 1, MPI_DOUBLE, MPI_MAX, MPI_COMM_WORLD);
MPI_Allreduce(&localMaximum, &globalMaximum, 1, MPI_DOUBLE, MPI_MAX,
MPI_COMM_WORLD);
constexpr double coordinateTolerance = 1.0e-12;
if (globalMinimum < -coordinateTolerance || globalMaximum > 1.0 + coordinateTolerance) {
throw std::runtime_error(
"Exterior coordinate lies outside the expected "
"interval [0, 1]."
);
if (globalMinimum < -coordinateTolerance ||
globalMaximum > 1.0 + coordinateTolerance) {
throw std::runtime_error("Exterior coordinate lies outside the expected "
"interval [0, 1].");
}
// =====================================================================
@@ -162,7 +165,8 @@ namespace mean_field::fem {
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.
@@ -170,17 +174,21 @@ namespace mean_field::fem {
fem.gravityFluxFec = GravityField::make_fec<GravityFlux>(dimension);
fem.gravityFluxFes = GravityField::make_fespace<GravityFlux>(*fem.mesh, *fem.gravityFluxFec);
fem.gravityFluxFes =
GravityField::make_fespace<GravityFlux>(*fem.mesh, *fem.gravityFluxFec);
// ---------------------------------------------------------------------
// Displacement: vector H1. Ordering is encoded by field.mfem.
// ---------------------------------------------------------------------
fem.displacementFec = DisplacementField::make_fec<DisplacementVector>(dimension);
fem.displacementFec =
DisplacementField::make_fec<DisplacementVector>(dimension);
fem.displacementFes = DisplacementField::make_fespace<DisplacementVector>(*fem.mesh, *fem.displacementFec);
fem.displacementFes = DisplacementField::make_fespace<DisplacementVector>(
*fem.mesh, *fem.displacementFec);
fem.displacement = std::make_unique<mfem::ParGridFunction>(fem.displacementFes.get());
fem.displacement =
std::make_unique<mfem::ParGridFunction>(fem.displacementFes.get());
*fem.displacement = 0.0;
@@ -190,7 +198,8 @@ namespace mean_field::fem {
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
@@ -198,50 +207,11 @@ namespace mean_field::fem {
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
// =====================================================================
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
// Section 4: Multipole data
// =====================================================================
fem.com.SetSize(dimension);
@@ -251,13 +221,9 @@ namespace mean_field::fem {
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();
fem.boundaryContext.inf_bounds.SetSize(boundaryAttributeCount);
@@ -267,76 +233,47 @@ namespace mean_field::fem {
fem.boundaryContext.inf_bounds = 0;
fem.boundaryContext.stellar_bounds = 0;
fem.boundaryContext.inf_bounds[static_cast<int>(boundary::Boundaries::INF_SURFACE) - 1] = 1;
fem.boundaryContext
.inf_bounds[static_cast<int>(boundary::Boundaries::INF_SURFACE) - 1] = 1;
fem.boundaryContext.stellar_bounds[static_cast<int>(boundary::Boundaries::STELLAR_SURFACE) - 1] = 1;
fem.boundaryContext
.stellar_bounds[static_cast<int>(boundary::Boundaries::STELLAR_SURFACE) -
1] = 1;
// =====================================================================
// Section 8: Gravity solver context
// =====================================================================
fem.gravityContext.minres = std::make_unique<mfem::MINRESSolver>(fem.mesh->GetComm());
fem.gravityContext.minres->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
// Section 7: Quadrature policy
// =====================================================================
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.");
}
quadrature::RuleSet quadratureRuleSet =
quadrature::make_rule_set(quadratureOptions.mode, quadratureOptions.global_boost);
quadrature::RuleSet quadratureRuleSet = quadrature::make_rule_set(
quadratureOptions.mode, quadratureOptions.global_boost);
if (quadratureOptions.fallback_fixed_order.has_value()) {
if (*quadratureOptions.fallback_fixed_order < 0) {
throw std::invalid_argument("Fallback quadrature order cannot be negative.");
throw std::invalid_argument(
"Fallback quadrature order cannot be negative.");
}
quadratureRuleSet.fallback.fixed_order = quadratureOptions.fallback_fixed_order;
quadratureRuleSet.fallback.fixed_order =
quadratureOptions.fallback_fixed_order;
}
auto apply_quadrature_options = [&quadratureOptions](
quadrature::RuleControl &ruleControl,
const quadrature::QuadratureTermOptions &termOptions
) {
const quadrature::QuadratureTermOptions
&termOptions) {
if (termOptions.fixed_order.has_value() && *termOptions.fixed_order < 0) {
throw std::invalid_argument("Fixed quadrature order cannot be negative.");
}
if (quadratureOptions.validation.reject_negative_boosts && termOptions.additional_boost < 0) {
if (quadratureOptions.validation.reject_negative_boosts &&
termOptions.additional_boost < 0) {
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.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 =
std::make_unique<quadrature::RuleFactory>(quadrature::Policy(std::move(quadratureRuleSet)));
fem.quadratureFactory = std::make_unique<quadrature::RuleFactory>(
quadrature::Policy(std::move(quadratureRuleSet)));
// =====================================================================
// Section 11: Stateless domain mapper
// =====================================================================
auto exteriorDomain =
std::make_unique<const mapping::compactification::KelvinCompactification>(args.kelvin_options);
std::make_unique<const mapping::compactification::KelvinCompactification>(
args.kelvin_options);
fem.domainMapperStateless =
std::make_unique<mapping::DomainMapperStateless>(args.domain_mapper_options, std::move(exteriorDomain));
MFEM_VERIFY(
args.domain_mapper_options.vacuum_element_attribute ==
DomainSchema::template material_attribute<utils::domain::Vacuum>(),
"The domain-mapper compactification attribute must match the vacuum "
"material registered by the "
"production domain schema.");
fem.domainMapperStateless = std::make_unique<mapping::DomainMapper>(
args.domain_mapper_options, std::move(exteriorDomain));
return fem;
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,116 +1,17 @@
module;
#include <expected>
#include <mfem.hpp>
module mean_field;
import :boundary.contexts;
namespace mean_field::utils {
DOMAINS operator|(
DOMAINS lhs,
DOMAINS rhs
) {
return static_cast<DOMAINS>(static_cast<uint8_t>(lhs) | static_cast<uint8_t>(rhs));
DOMAINS operator|(DOMAINS lhs, DOMAINS rhs) {
return static_cast<DOMAINS>(static_cast<uint8_t>(lhs) |
static_cast<uint8_t>(rhs));
}
DOMAINS operator&(
DOMAINS lhs,
DOMAINS rhs
) {
return static_cast<DOMAINS>(static_cast<uint8_t>(lhs) & static_cast<uint8_t>(rhs));
}
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);
DOMAINS operator&(DOMAINS lhs, DOMAINS rhs) {
return static_cast<DOMAINS>(static_cast<uint8_t>(lhs) &
static_cast<uint8_t>(rhs));
}
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);
}
[[nodiscard]] double enthalpy_from_pressure(double pressure) const override {
[[nodiscard]] double enthalpy_from_pressure(const double pressure) const override {
validate_nonnegativity(pressure, "pressure");
const double np1 = m_polytropic_index + 1;
return np1 * std::pow(m_polytropic_constant, m_polytropic_index / np1) * std::pow(pressure, 1.0 / np1);
@@ -159,10 +159,6 @@ export namespace mean_field::eos {
);
}
}
public:
private:
double m_polytropic_index;
double m_polytropic_constant;
double m_enthalpy_scale;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,6 +1,7 @@
#include <algorithm>
#include <array>
#include <catch2/catch_test_macros.hpp>
#include <cmath>
#include <cstddef>
#include <mfem.hpp>
#include <mpi.h>
@@ -79,6 +80,11 @@ namespace field_dof_map_test_utils {
concept CanMakeFieldDofMap =
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<
domain::MaterialList<
domain::Material<domain::Core, 11>,
@@ -90,7 +96,7 @@ namespace field_dof_map_test_utils {
TEST_CASE(
"Field DOF Map Preserves Canonical Bidirectional Indexing",
tags::unit &tags::field
tags::field_dof_unit
) {
namespace field = mean_field::field;
@@ -166,7 +172,7 @@ TEST_CASE(
TEST_CASE(
"Field DOF Map Rejects Invalid Canonical Mappings",
tags::unit &tags::field
tags::field_dof_unit
) {
namespace field = mean_field::field;
@@ -191,7 +197,7 @@ TEST_CASE(
TEST_CASE(
"Field DOF Map Rejects Out Of Range Index Queries",
tags::unit &tags::field
tags::field_dof_unit
) {
namespace field = mean_field::field;
@@ -212,7 +218,7 @@ TEST_CASE(
TEST_CASE(
"Field DOF Map Gather Selects Exactly The Active True DOFs",
tags::unit &tags::field
tags::field_dof_unit
) {
namespace field = mean_field::field;
@@ -247,7 +253,7 @@ TEST_CASE(
TEST_CASE(
"Field DOF Map Scatter Produces The Canonical Supported Projection",
tags::unit &tags::field
tags::field_dof_unit
) {
namespace field = mean_field::field;
@@ -281,7 +287,7 @@ TEST_CASE(
TEST_CASE(
"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;
@@ -310,7 +316,7 @@ TEST_CASE(
TEST_CASE(
"Field DOF Map Scatter Into Preserves Unsupported True DOFs",
tags::unit &tags::field
tags::field_dof_unit
) {
namespace field = mean_field::field;
@@ -337,7 +343,7 @@ TEST_CASE(
TEST_CASE(
"Field DOF Map Scatter Add Accumulates Only Onto Active True DOFs",
tags::unit &tags::field
tags::field_dof_unit
) {
namespace field = mean_field::field;
@@ -364,7 +370,7 @@ TEST_CASE(
TEST_CASE(
"Field DOF Map Operations Support MFEM Vector Views Without Resizing",
tags::unit &tags::field
tags::field_dof_unit
) {
namespace field = mean_field::field;
@@ -406,7 +412,7 @@ TEST_CASE(
TEST_CASE(
"Field DOF Map Operations Reject Incompatible Vector Sizes",
tags::unit &tags::field
tags::field_dof_unit
) {
namespace field = mean_field::field;
@@ -433,7 +439,7 @@ TEST_CASE(
TEST_CASE(
"Field DOF Map Identity Mapping Is An Exact Vector Identity",
tags::unit &tags::field
tags::field_dof_unit
) {
namespace field = mean_field::field;
@@ -463,7 +469,7 @@ TEST_CASE(
TEST_CASE(
"Field DOF Map Validates Field DOF Support Consistency",
tags::unit &tags::field
tags::field_dof_unit
) {
namespace field = mean_field::field;
@@ -495,7 +501,7 @@ TEST_CASE(
TEST_CASE(
"Field DOF Map Factory Is Available Only For Spatial Registered Fields",
tags::unit &tags::field
tags::field_dof_unit
) {
namespace field = mean_field::field;
@@ -509,12 +515,24 @@ TEST_CASE(
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);
}
TEST_CASE(
"Field DOF Map Factory Exactly Preserves Density Support",
tags::integration &tags::field
tags::field_dof_integration
) {
namespace field = mean_field::field;
@@ -567,7 +585,7 @@ TEST_CASE(
TEST_CASE(
"Field DOF Map Factory Exactly Preserves H1 Enthalpy Support",
tags::integration &tags::field
tags::field_dof_integration
) {
namespace field = mean_field::field;
@@ -614,7 +632,7 @@ TEST_CASE(
TEST_CASE(
"Field DOF Map Factory Produces Identity Maps For All Supported Fields",
tags::integration &tags::field
tags::field_dof_integration
) {
namespace field = mean_field::field;
@@ -648,7 +666,7 @@ TEST_CASE(
TEST_CASE(
"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;
@@ -681,7 +699,7 @@ TEST_CASE(
TEST_CASE(
"Field DOF Map Reduced Vectors Round Trip Through Real Field Support",
tags::integration &tags::field
tags::field_dof_integration
) {
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;
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(
const double rotation_fraction,
const double sine_theta_squared
@@ -29,7 +43,7 @@ namespace {
TEST_CASE(
"Centrifugal Integrator Matches Manufactured Cartesian Load",
tags::unit &tags::solver &tags::integrator &tags::centrifugal
tags::rotation_integrator_unit
) {
constexpr int dim = 3;
constexpr double density = 1.7;
@@ -49,13 +63,15 @@ TEST_CASE(
mfem::GridFunction displacement(&displacement_fes);
displacement = 0.0;
mapping::DomainMapper domain_mapper(displacement, 1.0, 2.0);
SerialMappingData mapping_data(mesh);
mfem::Vector omega(dim);
omega = 0.0;
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 *density_element = density_fes.GetFE(0);
@@ -66,8 +82,7 @@ TEST_CASE(
quadrature::Policy policy(std::move(rule_set));
quadrature::RuleFactory quadrature_factory(std::move(policy));
const quadrature::MappingKind mapping_kind =
!domain_mapper.HasDisplacementField() ? quadrature::MappingKind::none : quadrature::MappingKind::general;
const quadrature::MappingKind mapping_kind = quadrature::MappingKind::general;
const int position_order = displacement_element->GetOrder();
quadrature_factory.configure_centrifugal(
@@ -127,7 +142,7 @@ TEST_CASE(
TEST_CASE(
"Centrifugal Integrator Jacobian Matches Residual Linearization",
tags::unit &tags::solver &tags::integrator &tags::centrifugal
tags::rotation_integrator_unit
) {
constexpr int dim = 3;
constexpr double step = 1.0e-6;
@@ -147,14 +162,16 @@ TEST_CASE(
mfem::GridFunction displacement(&displacement_fes);
displacement = 0.0;
mapping::DomainMapper domain_mapper(displacement, 1.0, 2.0);
SerialMappingData mapping_data(mesh);
mfem::Vector omega(dim);
omega(0) = 0.7;
omega(1) = -1.1;
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 *density_element = density_fes.GetFE(0);
@@ -165,8 +182,7 @@ TEST_CASE(
quadrature::Policy policy(std::move(rule_set));
quadrature::RuleFactory quadrature_factory(std::move(policy));
const quadrature::MappingKind mapping_kind =
!domain_mapper.HasDisplacementField() ? quadrature::MappingKind::none : quadrature::MappingKind::general;
const quadrature::MappingKind mapping_kind = quadrature::MappingKind::general;
const int position_order = displacement_element->GetOrder();
quadrature_factory.configure_centrifugal(
@@ -287,7 +303,7 @@ TEST_CASE(
TEST_CASE(
"Centrifugal Integrator Preserves Rotation Identities",
tags::unit &tags::solver &tags::integrator &tags::centrifugal
tags::rotation_integrator_unit
) {
constexpr int dim = 3;
constexpr double density = 1.4;
@@ -307,14 +323,16 @@ TEST_CASE(
mfem::GridFunction displacement(&displacement_fes);
displacement = 0.0;
mapping::DomainMapper domain_mapper(displacement, 1.0, 2.0);
SerialMappingData mapping_data(mesh);
mfem::Vector omega(dim);
omega(0) = 0.7;
omega(1) = -1.1;
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 *density_element = density_fes.GetFE(0);
@@ -325,8 +343,7 @@ TEST_CASE(
quadrature::Policy policy(std::move(rule_set));
quadrature::RuleFactory quadrature_factory(std::move(policy));
const quadrature::MappingKind mapping_kind =
!domain_mapper.HasDisplacementField() ? quadrature::MappingKind::none : quadrature::MappingKind::general;
const quadrature::MappingKind mapping_kind = quadrature::MappingKind::general;
const int position_order = displacement_element->GetOrder();
quadrature_factory.configure_centrifugal(
@@ -424,7 +441,7 @@ TEST_CASE(
TEST_CASE(
"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();
fem::FEM f = fem::setup_fem(args.mesh_file, args, 0);
@@ -486,16 +503,20 @@ TEST_CASE(
mfem::VectorFunctionCoefficient displacement_coefficient(dim, rotation_displacement);
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);
omega = 0.0;
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 =
!f.mapping->HasDisplacementField() ? quadrature::MappingKind::none : quadrature::MappingKind::general;
const quadrature::MappingKind mapping_kind = quadrature::MappingKind::general;
f.quadratureFactory->configure_centrifugal(
integrator, quadrature::QuadratureRole::discretization, representative_density_element,
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) {
const mfem::IntegrationPoint &node = velocity_nodes.IntPoint(i);
transformation->SetIntPoint(&node);
f.mapping->GetPhysicalPoint(*transformation, node, x_physical);
mapping_evaluator.GetPhysicalPoint(*transformation, node, x_physical);
for (int d = 0; d < dim; ++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);
transformation->SetIntPoint(&integration_point);
const double signed_map_determinant = f.mapping->ComputeDetJ(*transformation, integration_point);
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_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);
position_test_value = 0.0;
@@ -661,13 +682,13 @@ TEST_CASE(
CHECK_THAT(relative_position_error, Catch::Matchers::WithinAbs(0.0, position_tolerance));
}
f.mapping->ResetDisplacement();
*f.displacement = 0.0;
}
TEST_CASE(
"Centrifugal Virial Position Representation Is Consistent At The "
"Registered Order",
tags::integration &tags::solver &tags::integrator &tags::centrifugal
tags::rotation_integrator_integration
) {
constexpr int dim = 3;
constexpr double concentration = 4.0;
@@ -728,7 +749,10 @@ TEST_CASE(
mfem::VectorFunctionCoefficient displacement_coefficient(dim, rotation_displacement);
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);
omega = 0.0;
@@ -761,7 +785,7 @@ TEST_CASE(
for (int i = 0; i < velocity_dofs_count; ++i) {
const mfem::IntegrationPoint &node = velocity_nodes.IntPoint(i);
transformation->SetIntPoint(&node);
f.mapping->GetPhysicalPoint(*transformation, node, x_physical);
mapping_evaluator.GetPhysicalPoint(*transformation, node, x_physical);
for (int d = 0; d < dim; ++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);
transformation->SetIntPoint(&integration_point);
const double signed_map_determinant = f.mapping->ComputeDetJ(*transformation, integration_point);
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);
f.mapping->GetPhysicalPoint(*transformation, integration_point, x_physical);
mapping_evaluator.GetPhysicalPoint(*transformation, integration_point, x_physical);
velocity_element->CalcShape(integration_point, velocity_shape);
position_test_value = 0.0;
@@ -833,7 +857,7 @@ TEST_CASE(
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) {
@@ -853,7 +877,7 @@ TEST_CASE(
TEST_CASE(
"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 double concentration = 4.0;
@@ -916,7 +940,10 @@ TEST_CASE(
mfem::VectorFunctionCoefficient displacement_coefficient(dim, rotation_displacement);
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);
omega = 0.0;
@@ -949,7 +976,7 @@ TEST_CASE(
for (int i = 0; i < velocity_dofs_count; ++i) {
const mfem::IntegrationPoint &node = velocity_nodes.IntPoint(i);
transformation->SetIntPoint(&node);
f.mapping->GetPhysicalPoint(*transformation, node, x_physical);
mapping_evaluator.GetPhysicalPoint(*transformation, node, x_physical);
for (int d = 0; d < dim; ++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);
transformation->SetIntPoint(&integration_point);
const double signed_map_determinant = f.mapping->ComputeDetJ(*transformation, integration_point);
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);
f.mapping->GetPhysicalPoint(*transformation, integration_point, x_physical);
mapping_evaluator.GetPhysicalPoint(*transformation, integration_point, x_physical);
velocity_element->CalcShape(integration_point, velocity_shape);
position_test_value = 0.0;
@@ -1021,7 +1048,7 @@ TEST_CASE(
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) {

View File

@@ -10,7 +10,7 @@ using namespace mean_field;
TEST_CASE(
"Gravity Force Integrator Jacobian Matches Residual Linearization",
tags::unit &tags::solver &tags::integrator &tags::gravity
tags::gravity_integrator_unit
) {
constexpr int dim = 3;
constexpr double finite_difference_step = 1.0e-3;
@@ -31,21 +31,20 @@ TEST_CASE(
mfem::RT_FECollection gravity_gradient_fec(1, dim);
mfem::L2_FECollection gravity_potential_fec(1, 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 density_fes(&mesh, &density_fec);
mfem::FiniteElementSpace gravity_gradient_fes(&mesh, &gravity_gradient_fec);
mfem::FiniteElementSpace gravity_potential_fes(&mesh, &gravity_potential_fec);
mfem::FiniteElementSpace displacement_fes(&mesh, &displacement_fec, dim, mfem::Ordering::byVDIM);
mfem::FiniteElementSpace compactification_fes(&mesh, &compactification_fec);
mfem::GridFunction displacement(&displacement_fes);
displacement = 0.0;
mapping::DomainMapper domain_mapper(displacement, 1.0, 2.0);
INFO(std::format("Domain mapping is has displacement field: {}", domain_mapper.HasDisplacementField()));
INFO(std::format("Domain mapping is identity: {}", domain_mapper.CalcIsIdentity()));
REQUIRE(domain_mapper.CalcIsIdentity());
mfem::GridFunction compactification_coordinate(&compactification_fes);
compactification_coordinate = 0.0;
mapping::DomainMapper domain_mapper = field_dof_test_utils::make_domain_mapper();
const mfem::FiniteElement *velocity_element = velocity_fes.GetFE(0);
const mfem::FiniteElement *density_element = density_fes.GetFE(0);
@@ -130,7 +129,8 @@ TEST_CASE(
element_residual[displacement_block] = &displacement_residual;
integrators::GravityMomentumIntegrator integrator(
domain_mapper, integrators::GravityForceJacobianMode::field_coupled
domain_mapper, displacement, compactification_coordinate,
integrators::GravityForceJacobianMode::field_coupled
);
const int maximum_order = std::max(
@@ -268,7 +268,7 @@ TEST_CASE(
TEST_CASE(
"Gravity Force Integrator Matches Manufactured Cartesian Load",
tags::unit &tags::solver &tags::integrator &tags::gravity
tags::gravity_integrator_unit
) {
constexpr int dim = 3;
constexpr double tolerance = 1.0e-12;
@@ -286,18 +286,23 @@ TEST_CASE(
mfem::L2_FECollection density_fec(1, dim);
mfem::RT_FECollection gravity_gradient_fec(0, 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 density_fes(&mesh, &density_fec);
mfem::FiniteElementSpace gravity_gradient_fes(&mesh, &gravity_gradient_fec);
mfem::FiniteElementSpace displacement_fes(&mesh, &displacement_fec, dim, mfem::Ordering::byVDIM);
mfem::FiniteElementSpace compactification_fes(&mesh, &compactification_fec);
mfem::GridFunction displacement(&displacement_fes);
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);
REQUIRE(domain_mapper.CalcIsIdentity());
mapping::GridFunctionMappingEvaluator mapping_evaluator(
domain_mapper, displacement, compactification_coordinate
);
auto reference_density = [](const mfem::Vector &x) { return 1.0 + x(0); };
@@ -376,7 +381,8 @@ TEST_CASE(
element_residual[displacement_block] = &displacement_residual;
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);
@@ -395,7 +401,7 @@ TEST_CASE(
for (int i = 0; i < velocity_dofs_count; ++i) {
const mfem::IntegrationPoint &node = velocity_nodes.IntPoint(i);
transformation->SetIntPoint(&node);
domain_mapper.GetPhysicalPoint(*transformation, node, x_physical);
mapping_evaluator.GetPhysicalPoint(*transformation, node, x_physical);
test_dofs(i + component * velocity_dofs_count) =
coordinate_weight < 0 ? 1.0 : x_physical(coordinate_weight);
}
@@ -433,7 +439,7 @@ TEST_CASE(
}
TEST_CASE(
"Gravity Force Integrator Preserves Gravity Identities",
tags::unit &tags::solver &tags::integrator &tags::gravity
tags::gravity_integrator_unit
) {
constexpr int dim = 3;
constexpr double density_value = 1.7;
@@ -454,18 +460,22 @@ TEST_CASE(
mfem::L2_FECollection density_fec(0, dim);
mfem::RT_FECollection gravity_gradient_fec(0, 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 density_fes(&mesh, &density_fec);
mfem::FiniteElementSpace gravity_gradient_fes(&mesh, &gravity_gradient_fec);
mfem::FiniteElementSpace displacement_fes(&mesh, &displacement_fec, dim, mfem::Ordering::byVDIM);
mfem::FiniteElementSpace compactification_fes(&mesh, &compactification_fec);
mfem::GridFunction displacement(&displacement_fes);
displacement = 0.0;
mapping::DomainMapper domain_mapper(displacement, 1.0, 2.0);
REQUIRE(domain_mapper.HasDisplacementField());
mfem::GridFunction compactification_coordinate(&compactification_fes);
compactification_coordinate = 0.0;
mapping::DomainMapper domain_mapper = field_dof_test_utils::make_domain_mapper();
mapping::GridFunctionMappingEvaluator mapping_evaluator(
domain_mapper, displacement, compactification_coordinate
);
auto radial_gravity = [](const mfem::Vector &x, mfem::Vector &gravity) {
gravity.SetSize(3);
@@ -543,7 +553,8 @@ TEST_CASE(
element_residual[displacement_block] = &displacement_residual;
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);
@@ -609,7 +620,7 @@ TEST_CASE(
for (int i = 0; i < velocity_dofs_count; ++i) {
const mfem::IntegrationPoint &node = velocity_nodes.IntPoint(i);
transformation->SetIntPoint(&node);
domain_mapper.GetPhysicalPoint(*transformation, node, x_physical);
mapping_evaluator.GetPhysicalPoint(*transformation, node, x_physical);
for (int component = 0; component < dim; ++component) {
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.rebuilt_mass_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.updated_density);
CHECK(initial_report.updated_gravity_gradient);
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_source_preparations = context.GetGeometryContext().GetSourceOperator().GetPreparationCount();
@@ -93,6 +100,7 @@ TEST_CASE(
CHECK_FALSE(displacement_report.geometry.reconstructed_operators);
CHECK(displacement_report.geometry.rebuilt_mass_operator);
CHECK(displacement_report.geometry.rebuilt_source_operator);
CHECK_FALSE(displacement_report.geometry.rebuilt_divergence_operator);
CHECK(displacement_report.geometry.refreshed_variation_state);
CHECK_FALSE(displacement_report.updated_density);
CHECK_FALSE(displacement_report.updated_gravity_gradient);
@@ -107,6 +115,7 @@ TEST_CASE(
CHECK(discretization_report.geometry.reconstructed_operators);
CHECK(discretization_report.geometry.rebuilt_mass_operator);
CHECK(discretization_report.geometry.rebuilt_source_operator);
CHECK(discretization_report.geometry.rebuilt_divergence_operator);
CHECK(discretization_report.updated_density);
CHECK(discretization_report.updated_gravity_gradient);
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;
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>(
mean_field::utils::blocks::displacement_field.geometry_term
);
constexpr auto displacementValue =
mean_field::utils::blocks::get_value_block<CoupledForm>(
mean_field::utils::blocks::displacement_field.geometry_term);
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 =
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>(
mean_field::utils::blocks::enthalpy_field.specific_term
);
constexpr auto enthalpyValue =
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>(
mean_field::utils::blocks::barotropic_constant_field.mass_normalization_term
);
constexpr auto barotropicConstantValue =
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>(
mean_field::utils::blocks::gravity_field.gradient_term
);
constexpr auto gravityGradientResidual =
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>(
mean_field::utils::blocks::gravity_field.poisson_term
);
constexpr auto gravityPotentialResidual =
mean_field::utils::blocks::get_residual_block<CoupledForm>(
mean_field::utils::blocks::gravity_field.poisson_term);
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>(
mean_field::utils::blocks::displacement_field.geometry_term
);
constexpr auto displacementResidual =
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>(
mean_field::utils::blocks::enthalpy_field.specific_term
);
constexpr auto enthalpyResidual =
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>(
mean_field::utils::blocks::barotropic_constant_field.mass_normalization_term
);
constexpr auto massResidual =
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;
const auto densityMap = 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 densityMap =
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 =
mean_field::field::make_field_dof_map<mean_field::field::Gravity, DomainSchema>(*f.gravityFluxFes);
const auto gravityPotentialMap =
mean_field::field::make_field_dof_map<mean_field::field::Gravity, DomainSchema>(*f.gravityPotentialFes);
mean_field::field::make_field_dof_map<mean_field::field::Gravity,
DomainSchema>(*f.gravityFluxFes);
const auto gravityPotentialMap = mean_field::field::make_field_dof_map<
mean_field::field::Gravity, DomainSchema>(*f.gravityPotentialFes);
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{
densityMap.reduced_size(), displacementMap.reduced_size(), gravityFluxMap.reduced_size(),
gravityPotentialMap.reduced_size(), enthalpyMap.reduced_size(), 1
};
densityMap.reduced_size(), displacementMap.reduced_size(),
gravityFluxMap.reduced_size(), gravityPotentialMap.reduced_size(),
enthalpyMap.reduced_size(), 1};
const std::array<int, CoupledForm::residual_block_count> residualSizes{
gravityFluxMap.reduced_size(), gravityPotentialMap.reduced_size(), densityMap.reduced_size(),
displacementMap.reduced_size(), enthalpyMap.reduced_size(), 1
};
gravityFluxMap.reduced_size(), gravityPotentialMap.reduced_size(),
densityMap.reduced_size(), displacementMap.reduced_size(),
enthalpyMap.reduced_size(), 1};
return {valueSizes, residualSizes};
}
[[nodiscard]] mfem::Vector make_density(
const mean_field::fem::FEM &f,
const double phase
) {
[[nodiscard]] mfem::Vector make_density(const mean_field::fem::FEM &f,
const double phase) {
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) +
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);
@@ -100,15 +112,14 @@ namespace gravity_displacement_force_test_utils {
return densityTrue;
}
[[nodiscard]] mfem::Vector make_density_direction(
const mean_field::fem::FEM &f,
const double phase
) {
[[nodiscard]] mfem::Vector make_density_direction(const mean_field::fem::FEM &f,
const double phase) {
mfem::ParGridFunction densityField(f.densityFes.get());
mfem::FunctionCoefficient densityCoefficient([phase](const mfem::Vector &position) {
return 0.19 * std::sin(0.9 * position(0) + phase) - 0.13 * std::cos(0.7 * position(1) - phase) +
0.08 * position(2);
mfem::FunctionCoefficient densityCoefficient(
[phase](const mfem::Vector &position) {
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);
@@ -118,13 +129,12 @@ namespace gravity_displacement_force_test_utils {
return densityTrue;
}
[[nodiscard]] mfem::Vector make_gravity_gradient(
const mean_field::fem::FEM &f,
const double phase
) {
[[nodiscard]] mfem::Vector make_gravity_gradient(const mean_field::fem::FEM &f,
const double phase) {
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(0) = 0.31 + 0.08 * position(0) + 0.03 * phase * position(1);
@@ -143,13 +153,13 @@ namespace gravity_displacement_force_test_utils {
return gravityTrue;
}
[[nodiscard]] mfem::Vector make_gravity_gradient_direction(
const mean_field::fem::FEM &f,
const double phase
) {
[[nodiscard]] mfem::Vector
make_gravity_gradient_direction(const mean_field::fem::FEM &f,
const double phase) {
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(0) = 0.14 * std::sin(position(0) + phase) + 0.03 * position(1);
@@ -168,26 +178,31 @@ namespace gravity_displacement_force_test_utils {
return gravityTrue;
}
[[nodiscard]] mfem::Vector make_displacement_direction(const mean_field::fem::FEM &f) {
mfem::Vector direction = gravity_prepared_test_utils::make_displacement(f, 0.83);
[[nodiscard]] mfem::Vector
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;
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());
densityField = 0.0;
const int vacuumAttribute = f.domainMapperStateless->GetVacuumElementAttribute();
const int vacuumAttribute = field_dof_test_utils::vacuum_material_attribute;
mfem::Array<int> densityDofs;
int localVacuumElements = 0;
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);
@@ -204,7 +219,8 @@ namespace gravity_displacement_force_test_utils {
}
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);
@@ -213,65 +229,59 @@ namespace gravity_displacement_force_test_utils {
return densityTrue;
}
[[nodiscard]] mean_field::operators::context::gravity_field::GravityFieldRevisions make_revisions() {
return {
.discretization = {.value = 3},
[[nodiscard]] mean_field::operators::context::gravity_field::
GravityFieldRevisions
make_revisions() {
return {.discretization = {.value = 3},
.displacement = {.value = 5},
.density = {.value = 7},
.gravity_gradient = {.value = 11},
.gravity_potential = {.value = 13}
};
.gravity_potential = {.value = 13}};
}
void prepare_gravity_context(
mean_field::operators::context::gravity_field::GravityFieldLinearizationContext &context,
const mfem::Vector &density,
const mfem::Vector &displacement,
const mfem::Vector &gravityGradient,
const mfem::Vector &gravityPotential,
const mean_field::operators::context::gravity_field::GravityFieldRevisions &revisions
) {
mean_field::operators::context::gravity_field::
GravityFieldLinearizationContext &context,
const mfem::Vector &density, const mfem::Vector &displacement,
const mfem::Vector &gravityGradient, const mfem::Vector &gravityPotential,
const mean_field::operators::context::gravity_field::GravityFieldRevisions
&revisions) {
context.Prepare(
{.density = context.GetDensityMap().gather(density),
.displacement = context.GetDisplacementMap().gather(displacement),
.gravity_gradient = context.GetGravityGradientMap().gather(gravityGradient),
.gravity_potential = context.GetGravityPotentialMap().gather(gravityPotential)},
revisions
);
.gravity_gradient =
context.GetGravityGradientMap().gather(gravityGradient),
.gravity_potential =
context.GetGravityPotentialMap().gather(gravityPotential)},
revisions);
}
[[nodiscard]] double relative_difference(
const mfem::Vector &left,
[[nodiscard]] double relative_difference(const mfem::Vector &left,
const mfem::Vector &right,
const MPI_Comm communicator
) {
MFEM_VERIFY(
left.Size() == right.Size(), "Cannot compare gravity-displacement-force vectors with "
"different sizes."
);
const MPI_Comm communicator) {
MFEM_VERIFY(left.Size() == right.Size(),
"Cannot compare gravity-displacement-force vectors with "
"different sizes.");
mfem::Vector difference(left);
difference -= right;
const double scale = std::max(
{gravity_prepared_test_utils::global_norm(left, communicator),
const double scale =
std::max({gravity_prepared_test_utils::global_norm(left, 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(
const mean_field::fem::FEM &f,
const mfem::Vector &baseDensity,
const mean_field::fem::FEM &f, const mfem::Vector &baseDensity,
const mfem::Vector &densityDirection,
const mfem::Vector &baseGravityGradient,
const mfem::Vector &gravityGradientDirection,
const mfem::Vector &baseDisplacement,
const mfem::Vector &displacementDirection,
const double step
) {
const mfem::Vector &displacementDirection, const double step) {
mfem::Vector plusDensity(baseDensity);
plusDensity.Add(step, densityDirection);
@@ -294,12 +304,12 @@ namespace gravity_displacement_force_test_utils {
mfem::Vector minusResidual;
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(
f, *f.domainMapperStateless, minusDensity, minusGravity, minusDisplacement, minusResidual
);
f, *f.domainMapperStateless, minusDensity, minusGravity,
minusDisplacement, minusResidual);
plusResidual -= minusResidual;
plusResidual /= 2.0 * step;
@@ -310,8 +320,7 @@ namespace gravity_displacement_force_test_utils {
[[nodiscard]] mfem::Vector copy_residual_block(
const mfem::Vector &action,
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));
const int offset = layout.offset(block);
@@ -323,19 +332,18 @@ namespace gravity_displacement_force_test_utils {
}
} // namespace gravity_displacement_force_test_utils
TEST_CASE(
"Gravity Displacement Force Query Includes Every Registered Operand",
tags::gravity_unit
) {
using DisplacementField = mean_field::field::Field<mean_field::field::Displacement>;
TEST_CASE("Gravity Displacement Force Query Includes Every Registered Operand",
tags::gravity_unit) {
using DisplacementField =
mean_field::field::Field<mean_field::field::Displacement>;
constexpr int geometryWeightOrder = 4;
constexpr mean_field::quadrature::Query query =
DisplacementField::make_query<mean_field::field::Displacement::Form::GravityForce>(
mean_field::quadrature::QuadratureRole::discretization, geometryWeightOrder, {},
mean_field::utils::DOMAINS::STELLAR, mean_field::quadrature::MappingKind::general
);
constexpr mean_field::quadrature::Query query = DisplacementField::make_query<
mean_field::field::Displacement::Form::GravityForce>(
mean_field::quadrature::QuadratureRole::discretization,
geometryWeightOrder, {}, mean_field::utils::DOMAINS::STELLAR,
mean_field::quadrature::MappingKind::general);
/*
* rho: 2
@@ -348,7 +356,8 @@ TEST_CASE(
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);
@@ -358,11 +367,9 @@ TEST_CASE(
STATIC_REQUIRE(*query.base_order == expectedBaseOrder);
}
TEST_CASE(
"Gravity Displacement Force Uses Positive Grad-Phi Sign And Excludes "
TEST_CASE("Gravity Displacement Force Uses Positive Grad-Phi Sign And Excludes "
"Vacuum",
tags::gravity_kernel_accuracy
) {
tags::gravity_kernel_accuracy) {
mean_field::utils::Args args = test_utils::setup_args();
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;
};
mfem::VectorFunctionCoefficient gravityCoefficient(3, constantGravityFunction);
mfem::VectorFunctionCoefficient gravityCoefficient(3,
constantGravityFunction);
gravityField.ProjectCoefficient(gravityCoefficient);
@@ -397,8 +405,8 @@ TEST_CASE(
mfem::Vector 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());
testField.ProjectCoefficient(gravityCoefficient);
@@ -406,54 +414,57 @@ TEST_CASE(
mfem::Vector 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);
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;
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(
"Prepared Gravity Displacement Force Reuses Shared Gravity Revisions",
tags::gravity_prepared
) {
TEST_CASE("Prepared Gravity Displacement Force Reuses Shared Gravity Revisions",
tags::gravity_prepared) {
mean_field::utils::Args args = test_utils::setup_args();
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
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());
gravityPotential = 0.0;
auto revisions = gravity_displacement_force_test_utils::make_revisions();
mean_field::operators::context::gravity_field::GravityFieldLinearizationContext gravityContext(
f, *f.domainMapperStateless
);
mean_field::operators::context::gravity_field::
GravityFieldLinearizationContext gravityContext(f,
*f.domainMapperStateless);
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(
f, *f.domainMapperStateless, gravityContext
);
mean_field::operators::PreparedGravityDisplacementForceOperator
preparedOperator(f, *f.domainMapperStateless, gravityContext);
const auto initialReport = preparedOperator.Prepare();
REQUIRE(initialReport.DidAnyWork());
@@ -465,24 +476,23 @@ TEST_CASE(
preparedOperator.BuildResidual(preparedResidual);
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(
gravity_displacement_force_test_utils::relative_difference(
preparedResidual, kernelResidualReduced, f.mesh->GetComm()
) < 2.0e-12
);
CHECK(gravity_displacement_force_test_utils::relative_difference(
preparedResidual, kernelResidualReduced, f.mesh->GetComm()) <
2.0e-12);
CHECK_FALSE(preparedOperator.Prepare().DidAnyWork());
++revisions.gravity_potential.value;
gravity_displacement_force_test_utils::prepare_gravity_context(
gravityContext, density, displacement, gravityGradient, gravityPotential, revisions
);
gravityContext, density, displacement, gravityGradient, gravityPotential,
revisions);
CHECK(preparedOperator.IsPrepared());
CHECK_FALSE(preparedOperator.Prepare().DidAnyWork());
@@ -491,8 +501,8 @@ TEST_CASE(
++revisions.density.value;
gravity_displacement_force_test_utils::prepare_gravity_context(
gravityContext, density, displacement, gravityGradient, gravityPotential, revisions
);
gravityContext, density, displacement, gravityGradient, gravityPotential,
revisions);
CHECK_FALSE(preparedOperator.IsPrepared());
@@ -506,73 +516,79 @@ TEST_CASE(
TEST_CASE(
"Gravity Displacement Force Jacobian Matches All Columns And Centered "
"Differences",
tags::gravity_prepared_jacobian_accuracy
) {
tags::gravity_prepared_jacobian_accuracy) {
mean_field::utils::Args args = test_utils::setup_args();
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
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 =
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());
gravityPotential = 0.0;
mean_field::operators::context::gravity_field::GravityFieldLinearizationContext gravityContext(
f, *f.domainMapperStateless
);
mean_field::operators::context::gravity_field::
GravityFieldLinearizationContext gravityContext(f,
*f.domainMapperStateless);
gravity_displacement_force_test_utils::prepare_gravity_context(
gravityContext, density, displacement, gravityGradient, gravityPotential,
gravity_displacement_force_test_utils::make_revisions()
);
gravity_displacement_force_test_utils::make_revisions());
mean_field::operators::PreparedGravityDisplacementForceOperator preparedOperator(
f, *f.domainMapperStateless, gravityContext
);
mean_field::operators::PreparedGravityDisplacementForceOperator
preparedOperator(f, *f.domainMapperStateless, gravityContext);
preparedOperator.Prepare();
const mfem::Vector densityDirectionReduced = gravityContext.GetDensityMap().gather(densityDirection);
const mfem::Vector densityDirectionReduced =
gravityContext.GetDensityMap().gather(densityDirection);
const mfem::Vector gravityGradientDirectionReduced =
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 gravityAction;
mfem::Vector displacementAction;
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(
densityDirectionReduced, displacementDirectionReduced, gravityGradientDirectionReduced, completeAction
);
densityDirectionReduced, displacementDirectionReduced,
gravityGradientDirectionReduced, completeAction);
mfem::Vector summedColumns(densityAction);
summedColumns += gravityAction;
summedColumns += displacementAction;
CHECK(
gravity_displacement_force_test_utils::relative_difference(completeAction, summedColumns, f.mesh->GetComm()) <
2.0e-12
);
CHECK(gravity_displacement_force_test_utils::relative_difference(
completeAction, summedColumns, f.mesh->GetComm()) < 2.0e-12);
mfem::Vector zeroDensity(densityDirection.Size());
mfem::Vector zeroGravity(gravityGradientDirection.Size());
@@ -583,41 +599,50 @@ TEST_CASE(
constexpr double step = 1.0e-5;
const mfem::Vector densityDifferenceTrue = gravity_displacement_force_test_utils::centered_difference(
f, density, densityDirection, gravityGradient, zeroGravity, displacement, zeroDisplacement, step
);
const mfem::Vector densityDifferenceTrue =
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(
f, density, zeroDensity, gravityGradient, gravityGradientDirection, displacement, zeroDisplacement, step
);
const mfem::Vector gravityDifferenceTrue =
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(
f, density, zeroDensity, gravityGradient, zeroGravity, displacement, displacementDirection, step
);
const mfem::Vector displacementDifferenceTrue =
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(
f, density, densityDirection, gravityGradient, gravityGradientDirection, displacement, displacementDirection,
step
);
const mfem::Vector completeDifferenceTrue =
gravity_displacement_force_test_utils::centered_difference(
f, density, densityDirection, gravityGradient,
gravityGradientDirection, displacement, displacementDirection, step);
const mfem::Vector densityDifference = gravityContext.GetDisplacementMap().gather(densityDifferenceTrue);
const mfem::Vector gravityDifference = gravityContext.GetDisplacementMap().gather(gravityDifferenceTrue);
const mfem::Vector displacementDifference = gravityContext.GetDisplacementMap().gather(displacementDifferenceTrue);
const mfem::Vector completeDifference = gravityContext.GetDisplacementMap().gather(completeDifferenceTrue);
const mfem::Vector densityDifference =
gravityContext.GetDisplacementMap().gather(densityDifferenceTrue);
const mfem::Vector gravityDifference =
gravityContext.GetDisplacementMap().gather(gravityDifferenceTrue);
const mfem::Vector displacementDifference =
gravityContext.GetDisplacementMap().gather(displacementDifferenceTrue);
const mfem::Vector completeDifference =
gravityContext.GetDisplacementMap().gather(completeDifferenceTrue);
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 =
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(
displacementAction, displacementDifference, f.mesh->GetComm()
);
const double displacementError =
gravity_displacement_force_test_utils::relative_difference(
displacementAction, displacementDifference, f.mesh->GetComm());
const double completeError = gravity_displacement_force_test_utils::relative_difference(
completeAction, completeDifference, f.mesh->GetComm()
);
const double completeError =
gravity_displacement_force_test_utils::relative_difference(
completeAction, completeDifference, f.mesh->GetComm());
INFO("Density-column centered-difference error = " << densityError);
INFO("Gravity-column centered-difference error = " << gravityError);
@@ -630,70 +655,82 @@ TEST_CASE(
CHECK(completeError < 3.0e-8);
}
TEST_CASE(
"Prepared Gravity Displacement Force MFEM Adapter Routes Only R-d",
tags::gravity_prepared_unit
) {
TEST_CASE("Prepared Gravity Displacement Force MFEM Adapter Routes Only R-d",
tags::gravity_prepared_unit) {
mean_field::utils::Args args = test_utils::setup_args();
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
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 =
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());
gravityPotential = 0.0;
mean_field::operators::context::gravity_field::GravityFieldLinearizationContext gravityContext(
f, *f.domainMapperStateless
);
mean_field::operators::context::gravity_field::
GravityFieldLinearizationContext gravityContext(f,
*f.domainMapperStateless);
gravity_displacement_force_test_utils::prepare_gravity_context(
gravityContext, density, displacement, gravityGradient, gravityPotential,
gravity_displacement_force_test_utils::make_revisions()
);
gravity_displacement_force_test_utils::make_revisions());
mean_field::operators::PreparedGravityDisplacementForceOperator preparedOperator(
f, *f.domainMapperStateless, gravityContext
);
mean_field::operators::PreparedGravityDisplacementForceOperator
preparedOperator(f, *f.domainMapperStateless, gravityContext);
preparedOperator.Prepare();
const mfem::Vector densityDirectionReduced = gravityContext.GetDensityMap().gather(densityDirection);
const mfem::Vector densityDirectionReduced =
gravityContext.GetDensityMap().gather(densityDirection);
const mfem::Vector gravityGradientDirectionReduced =
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);
mean_field::operators::PreparedGravityDisplacementForceJacobianOperator adapter(layout, preparedOperator);
mean_field::operators::PreparedGravityDisplacementForceJacobianOperator
adapter(layout, preparedOperator);
mfem::BlockVector direction(layout.value_offsets());
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;
adapter.Mult(direction, action);
@@ -701,39 +738,36 @@ TEST_CASE(
mfem::Vector expectedDisplacementAction;
preparedOperator.ApplyCompleteJacobianAction(
densityDirectionReduced, displacementDirectionReduced, gravityGradientDirectionReduced,
expectedDisplacementAction
);
densityDirectionReduced, displacementDirectionReduced,
gravityGradientDirectionReduced, expectedDisplacementAction);
const mfem::Vector actualDisplacementAction = gravity_displacement_force_test_utils::copy_residual_block(
action, layout, gravity_displacement_force_test_utils::displacementResidual
);
const mfem::Vector actualDisplacementAction =
gravity_displacement_force_test_utils::copy_residual_block(
action, layout,
gravity_displacement_force_test_utils::displacementResidual);
CHECK(
gravity_displacement_force_test_utils::relative_difference(
actualDisplacementAction, expectedDisplacementAction, f.mesh->GetComm()
) < 2.0e-12
);
CHECK(gravity_displacement_force_test_utils::relative_difference(
actualDisplacementAction, expectedDisplacementAction,
f.mesh->GetComm()) < 2.0e-12);
const std::array<mfem::Vector, 5> zeroRows{
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(
action, layout, gravity_displacement_force_test_utils::gravityPotentialResidual
),
action, layout,
gravity_displacement_force_test_utils::gravityPotentialResidual),
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(
action, layout, gravity_displacement_force_test_utils::enthalpyResidual
),
action, layout,
gravity_displacement_force_test_utils::enthalpyResidual),
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) {
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;
}
[[nodiscard]] double determinant(
const std::array<
double,
3> &scales
) {
[[nodiscard]] double determinant(const std::array<double, 3> &scales) {
return scales[0] * scales[1] * scales[2];
}
[[nodiscard]] double relative_scalar_error(
const double computed,
const double expected
) {
[[nodiscard]] double relative_scalar_error(const double computed,
const double expected) {
return std::abs(computed - expected) / std::max(std::abs(expected), 1.0e-30);
}
[[nodiscard]] mfem::Vector make_constant_density(
const mean_field::fem::FEM &f,
const double densityValue
) {
[[nodiscard]] mfem::Vector make_constant_density(const mean_field::fem::FEM &f,
const double densityValue) {
mfem::ParGridFunction densityField(f.densityFes.get());
mfem::ConstantCoefficient densityCoefficient(densityValue);
densityField.ProjectCoefficient(densityCoefficient);
@@ -48,23 +40,21 @@ namespace gravity_displacement_force_analytic_test_utils {
return densityTrue;
}
[[nodiscard]] mfem::Vector make_reference_gravity(
const mean_field::fem::FEM &f,
const std::array<
double,
3> &referenceGravity
) {
[[nodiscard]] mfem::Vector
make_reference_gravity(const mean_field::fem::FEM &f,
const std::array<double, 3> &referenceGravity) {
mfem::ParGridFunction gravityField(f.gravityFluxFes.get());
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);
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);
@@ -73,21 +63,19 @@ namespace gravity_displacement_force_analytic_test_utils {
return gravityTrue;
}
[[nodiscard]] mfem::Vector make_radial_gravity(
const mean_field::fem::FEM &f,
const double radialCoefficient
) {
[[nodiscard]] mfem::Vector make_radial_gravity(const mean_field::fem::FEM &f,
const double radialCoefficient) {
mfem::ParGridFunction gravityField(f.gravityFluxFes.get());
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());
for (int component = 0; component < position.Size(); ++component) {
value(component) = radialCoefficient * position(component);
}
}
);
});
gravityField.ProjectCoefficient(gravityCoefficient);
@@ -96,23 +84,22 @@ namespace gravity_displacement_force_analytic_test_utils {
return gravityTrue;
}
[[nodiscard]] mfem::Vector make_affine_displacement(
const mean_field::fem::FEM &f,
const std::array<
double,
3> &scales
) {
[[nodiscard]] mfem::Vector
make_affine_displacement(const mean_field::fem::FEM &f,
const std::array<double, 3> &scales) {
mfem::ParGridFunction displacementField(f.displacementFes.get());
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());
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);
@@ -121,19 +108,18 @@ namespace gravity_displacement_force_analytic_test_utils {
return displacementTrue;
}
[[nodiscard]] mfem::Vector make_constant_test_direction(
const mean_field::fem::FEM &f,
const int selectedComponent
) {
[[nodiscard]] mfem::Vector
make_constant_test_direction(const mean_field::fem::FEM &f,
const int selectedComponent) {
mfem::ParGridFunction testField(f.displacementFes.get());
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 = 0.0;
value(selectedComponent) = 1.0;
}
);
});
testField.ProjectCoefficient(testCoefficient);
@@ -142,12 +128,13 @@ namespace gravity_displacement_force_analytic_test_utils {
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::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);
@@ -156,57 +143,64 @@ namespace gravity_displacement_force_analytic_test_utils {
return testTrue;
}
void set_mass_normalized_density(
mean_field::fem::FEM &f,
void set_mass_normalized_density(mean_field::fem::FEM &f,
const double targetMass,
mfem::ParGridFunction &densityField
) {
const mfem::Vector stellarDensityTrue = gravity_prepared_test_utils::make_domain_supported_density(f, true);
mfem::ParGridFunction &densityField) {
const mfem::Vector stellarDensityTrue =
gravity_prepared_test_utils::make_domain_supported_density(f, true);
densityField.SetFromTrueDofs(stellarDensityTrue);
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;
}
} // namespace gravity_displacement_force_analytic_test_utils
TEST_CASE(
"Gravity Displacement Force Matches Analytic Affine Resultants",
tags::gravity &tags::accuracy &tags::analytic_comparison &tags::integration
) {
TEST_CASE("Gravity Displacement Force Matches Analytic Affine Resultants",
tags::gravity &tags::accuracy &tags::analytic_comparison
&tags::integration) {
mean_field::utils::Args args = test_utils::setup_args();
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
REQUIRE(f.okay());
REQUIRE(f.domainMapperStateless != nullptr);
REQUIRE(f.mapping != nullptr);
REQUIRE(f.domainMapperStateless != nullptr);
constexpr double densityValue = 1.37;
constexpr std::array<double, 3> physicalGravity{0.31, -0.47, 0.22};
constexpr std::array<gravity_displacement_force_analytic_test_utils::AffineCase, 3> affineCases{
{{.name = "identity geometry", .scales = {1.0, 1.0, 1.0}},
{.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}}}
};
constexpr std::array<
gravity_displacement_force_analytic_test_utils::AffineCase, 3>
affineCases{{{.name = "identity geometry", .scales = {1.0, 1.0, 1.0}},
{.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 =
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;
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) {
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);
@@ -222,35 +216,42 @@ TEST_CASE(
*/
for (int component = 0; component < 3; ++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)];
}
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 =
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;
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) {
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 =
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;
const double relativeError = gravity_displacement_force_analytic_test_utils::relative_scalar_error(
computedResultant, expectedResultant
);
const double relativeError =
gravity_displacement_force_analytic_test_utils::
relative_scalar_error(computedResultant, expectedResultant);
CAPTURE(component);
INFO("Map determinant = " << mapDeterminant);
@@ -266,8 +267,8 @@ TEST_CASE(
TEST_CASE(
"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::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 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 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 =
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());
displacement = 0.0;
@@ -293,18 +300,22 @@ TEST_CASE(
mfem::Vector 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 =
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 =
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("Analytic positive gravity work = " << analyticWork);
@@ -316,10 +327,9 @@ TEST_CASE(
CHECK(relativeError < 1.0e-5);
}
TEST_CASE(
"Solved Homogeneous Sphere Gravity Force Matches Analytic Virial",
tags::gravity &tags::accuracy &tags::analytic_comparison &tags::integration &tags::initialization
) {
TEST_CASE("Solved Homogeneous Sphere Gravity Force Matches Analytic Virial",
tags::gravity &tags::accuracy &tags::analytic_comparison
&tags::integration &tags::initialization) {
mean_field::utils::Args args = test_utils::setup_args();
args.p.rtol = 1.0e-13;
args.p.max_iters = std::max(args.p.max_iters, 1000);
@@ -332,19 +342,20 @@ TEST_CASE(
mfem::ParGridFunction displacementField(f.displacementFes.get());
displacementField = 0.0;
REQUIRE(f.mapping != nullptr);
f.mapping->ResetDisplacement();
mean_field::physics::update_stiffness_matrix(f);
REQUIRE(f.domainMapperStateless != nullptr);
*f.displacement = 0.0;
const double radius = mean_field::utils::RADIUS;
const double mass = mean_field::utils::MASS;
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 =
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 gravityGradientTrue;
@@ -357,18 +368,22 @@ TEST_CASE(
mfem::Vector 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 =
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 =
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("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;
namespace hydrostatic_kernel_test_utils {
mfem::Vector project_scalar(
mfem::ParFiniteElementSpace &finiteElementSpace,
mfem::Coefficient &coefficient
) {
mfem::Vector project_scalar(mfem::ParFiniteElementSpace &finiteElementSpace,
mfem::Coefficient &coefficient) {
mfem::ParGridFunction field(&finiteElementSpace);
field.ProjectCoefficient(coefficient);
@@ -23,10 +21,9 @@ namespace hydrostatic_kernel_test_utils {
return trueVector;
}
mfem::Vector make_constant_field(
mfem::ParFiniteElementSpace &finiteElementSpace,
const double value
) {
mfem::Vector
make_constant_field(mfem::ParFiniteElementSpace &finiteElementSpace,
const double value) {
mfem::ConstantCoefficient coefficient(value);
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::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);
@@ -42,7 +40,8 @@ namespace hydrostatic_kernel_test_utils {
mfem::Vector make_potential(const mean_field::fem::FEM &f) {
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);
@@ -74,11 +73,9 @@ namespace hydrostatic_kernel_test_utils {
return mean_field::physics::RigidRotation(angularVelocity, center);
}
mfem::Vector centered_difference(
const mfem::Vector &plusResidual,
mfem::Vector centered_difference(const mfem::Vector &plusResidual,
const mfem::Vector &minusResidual,
const double epsilon
) {
const double epsilon) {
mfem::Vector difference(plusResidual);
difference -= minusResidual;
@@ -87,12 +84,10 @@ namespace hydrostatic_kernel_test_utils {
return difference;
}
double sum_normalized_error(
const mfem::Vector &computed,
double sum_normalized_error(const mfem::Vector &computed,
const mfem::Vector &reference,
const double normalization,
const MPI_Comm communicator
) {
const MPI_Comm communicator) {
mfem::Vector difference(computed);
difference -= reference;
@@ -105,9 +100,10 @@ namespace hydrostatic_kernel_test_utils {
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];
if (attribute == vacuumAttribute) {
@@ -124,38 +120,31 @@ namespace hydrostatic_kernel_test_utils {
public:
HydrostaticEnthalpyMassOperator(
const mean_field::fem::FEM &f,
const mean_field::mapping::DomainMapperStateless &domainMapper,
const mfem::Vector &displacementTrue
)
: mfem::Operator(f.enthalpyFes->GetTrueVSize()),
f_(f),
domainMapper_(domainMapper),
displacementTrue_(displacementTrue) {
}
const mean_field::mapping::DomainMapper &domainMapper,
const mfem::Vector &displacementTrue)
: mfem::Operator(f.enthalpyFes->GetTrueVSize()), f_(f),
domainMapper_(domainMapper), displacementTrue_(displacementTrue) {}
void Mult(
const mfem::Vector &input,
mfem::Vector &output
) const override {
mean_field::operators::kernels::apply_hydrostatic_equilibrium_enthalpy_action(
f_, domainMapper_, input, displacementTrue_, output
);
void Mult(const mfem::Vector &input, mfem::Vector &output) const override {
mean_field::operators::kernels::
apply_hydrostatic_equilibrium_enthalpy_action(
f_, domainMapper_, input, displacementTrue_, output);
}
private:
const mean_field::fem::FEM &f_;
const mean_field::mapping::DomainMapperStateless &domainMapper_;
const mean_field::mapping::DomainMapper &domainMapper_;
const mfem::Vector &displacementTrue_;
};
} // namespace hydrostatic_kernel_test_utils
TEST_CASE(
"Rigid Rotation Potential Derivative Matches Centered Differences",
tags::barotrope &tags::hydro &tags::jacobian &tags::physics &tags::unit &tags::kernels
) {
const mean_field::physics::RigidRotation rotation = hydrostatic_kernel_test_utils::make_rotation();
TEST_CASE("Rigid Rotation Potential Derivative Matches Centered Differences",
tags::barotrope &tags::hydro &tags::jacobian &tags::physics
&tags::unit &tags::kernels) {
const mean_field::physics::RigidRotation rotation =
hydrostatic_kernel_test_utils::make_rotation();
mfem::Vector position(3);
mfem::Vector direction(3);
@@ -177,43 +166,52 @@ TEST_CASE(
minusPosition.Add(-epsilon, direction);
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) /
std::max(std::abs(analyticDerivative), std::numeric_limits<double>::epsilon());
const double relativeError =
std::abs(centeredDerivative - analyticDerivative) /
std::max(std::abs(analyticDerivative),
std::numeric_limits<double>::epsilon());
INFO("Rigid-rotation derivative error = " << relativeError);
CHECK(relativeError < 2.0e-9);
}
TEST_CASE(
"Hydrostatic Residual Vanishes For A Manufactured Rotating State",
tags::barotrope &tags::hydro &tags::integration &tags::kernels &tags::physics &tags::residuals
) {
TEST_CASE("Hydrostatic Residual Vanishes For A Manufactured Rotating State",
tags::barotrope &tags::hydro &tags::integration &tags::kernels
&tags::physics &tags::residuals) {
auto args = test_utils::setup_args();
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 potentialValue = -0.21;
constexpr double constantOffset = 0.40;
mfem::FunctionCoefficient enthalpyCoefficient([&rotation](const mfem::Vector &position) {
return bernoulliConstant - potentialValue + rotation.potential(position);
mfem::FunctionCoefficient enthalpyCoefficient(
[&rotation](const mfem::Vector &position) {
return bernoulliConstant - potentialValue +
rotation.potential(position);
});
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 =
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();
@@ -228,32 +226,33 @@ TEST_CASE(
mfem::Vector interpolatedReferenceResidual;
mean_field::operators::kernels::apply_hydrostatic_equilibrium(
f, *f.domainMapperStateless, rotation, interpolatedEnthalpy, potential, displacement, bernoulliConstant,
interpolatedResidual
);
f, *f.domainMapperStateless, rotation, interpolatedEnthalpy, potential,
displacement, bernoulliConstant, interpolatedResidual);
mean_field::operators::kernels::apply_hydrostatic_equilibrium(
f, *f.domainMapperStateless, rotation, interpolatedEnthalpy, potential, displacement,
bernoulliConstant + constantOffset, interpolatedReferenceResidual
);
f, *f.domainMapperStateless, rotation, interpolatedEnthalpy, potential,
displacement, bernoulliConstant + constantOffset,
interpolatedReferenceResidual);
const double interpolatedResidualNorm =
gravity_prepared_test_utils::global_norm(interpolatedResidual, communicator);
gravity_prepared_test_utils::global_norm(interpolatedResidual,
communicator);
const double interpolatedReferenceNorm =
gravity_prepared_test_utils::global_norm(interpolatedReferenceResidual, communicator);
gravity_prepared_test_utils::global_norm(interpolatedReferenceResidual,
communicator);
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(
"Interpolated rotating-state relative "
INFO("Interpolated rotating-state relative "
"representation floor = "
<< representationFloor
);
<< representationFloor);
/*
* 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
* iteration in the active stellar subspace.
*/
hydrostatic_kernel_test_utils::HydrostaticEnthalpyMassOperator enthalpyMassOperator(
f, *f.domainMapperStateless, displacement
);
hydrostatic_kernel_test_utils::HydrostaticEnthalpyMassOperator
enthalpyMassOperator(f, *f.domainMapperStateless, displacement);
mfem::Vector correctionRightHandSide(interpolatedResidual);
@@ -299,11 +297,14 @@ TEST_CASE(
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());
@@ -314,15 +315,15 @@ TEST_CASE(
correctionEquationResidual -= correctionRightHandSide;
const double correctionEquationNorm =
gravity_prepared_test_utils::global_norm(correctionEquationResidual, communicator);
gravity_prepared_test_utils::global_norm(correctionEquationResidual,
communicator);
INFO(
"Discrete-equilibrium correction-equation "
INFO("Discrete-equilibrium correction-equation "
"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);
@@ -332,20 +333,21 @@ TEST_CASE(
mfem::Vector referenceResidual;
mean_field::operators::kernels::apply_hydrostatic_equilibrium(
f, *f.domainMapperStateless, rotation, discreteEnthalpy, potential, displacement, bernoulliConstant,
exactResidual
);
f, *f.domainMapperStateless, rotation, discreteEnthalpy, potential,
displacement, bernoulliConstant, exactResidual);
mean_field::operators::kernels::apply_hydrostatic_equilibrium(
f, *f.domainMapperStateless, rotation, discreteEnthalpy, potential, displacement,
bernoulliConstant + constantOffset, referenceResidual
);
f, *f.domainMapperStateless, rotation, discreteEnthalpy, potential,
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);
@@ -360,31 +362,37 @@ TEST_CASE(
TEST_CASE(
"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();
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 potentialValue = -0.35;
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 =
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();
for (const double deformationScale : {0.0, 0.5, 1.0}) {
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 referenceResidual;
@@ -392,35 +400,38 @@ TEST_CASE(
mfem::Vector referenceGeometryAction;
mean_field::operators::kernels::apply_hydrostatic_equilibrium(
f, *f.domainMapperStateless, rotation, enthalpy, potential, displacement, bernoulliConstant,
exactResidual
);
f, *f.domainMapperStateless, rotation, enthalpy, potential,
displacement, bernoulliConstant, exactResidual);
mean_field::operators::kernels::apply_hydrostatic_equilibrium(
f, *f.domainMapperStateless, rotation, enthalpy, potential, displacement, bernoulliConstant + 0.50,
referenceResidual
);
f, *f.domainMapperStateless, rotation, enthalpy, potential,
displacement, bernoulliConstant + 0.50, referenceResidual);
mean_field::operators::kernels::apply_hydrostatic_equilibrium_displacement_action(
f, *f.domainMapperStateless, rotation, enthalpy, potential, displacement, bernoulliConstant,
displacementVariation, exactGeometryAction
);
mean_field::operators::kernels::
apply_hydrostatic_equilibrium_displacement_action(
f, *f.domainMapperStateless, rotation, enthalpy, potential,
displacement, bernoulliConstant, displacementVariation,
exactGeometryAction);
mean_field::operators::kernels::apply_hydrostatic_equilibrium_displacement_action(
f, *f.domainMapperStateless, rotation, enthalpy, potential, displacement, bernoulliConstant + 0.50,
displacementVariation, referenceGeometryAction
);
mean_field::operators::kernels::
apply_hydrostatic_equilibrium_displacement_action(
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 =
gravity_prepared_test_utils::global_norm(referenceResidual, communicator);
gravity_prepared_test_utils::global_norm(referenceResidual,
communicator);
const double exactGeometryNorm =
gravity_prepared_test_utils::global_norm(exactGeometryAction, communicator);
const double exactGeometryNorm = gravity_prepared_test_utils::global_norm(
exactGeometryAction, communicator);
const double referenceGeometryNorm =
gravity_prepared_test_utils::global_norm(referenceGeometryAction, communicator);
gravity_prepared_test_utils::global_norm(referenceGeometryAction,
communicator);
REQUIRE(referenceResidualNorm > 1.0e-12);
@@ -433,51 +444,59 @@ TEST_CASE(
}
}
TEST_CASE(
"Hydrostatic Equilibrium Excludes Vacuum Elements",
tags::barotrope &tags::hydro &tags::kernels &tags::mapping &tags::physics &tags::unit
) {
TEST_CASE("Hydrostatic Equilibrium Excludes Vacuum Elements",
tags::barotrope &tags::hydro &tags::kernels &tags::mapping
&tags::physics &tags::unit) {
auto args = test_utils::setup_args();
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());
mfem::Vector enthalpy(zeroEnthalpy);
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 =
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 vacuumAction;
mfem::Vector stellarAction;
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(
f, *f.domainMapperStateless, vacuumPotential, displacement, vacuumAction
);
mean_field::operators::kernels::
apply_hydrostatic_equilibrium_potential_action(
f, *f.domainMapperStateless, vacuumPotential, displacement,
vacuumAction);
mean_field::operators::kernels::apply_hydrostatic_equilibrium_potential_action(
f, *f.domainMapperStateless, stellarPotential, displacement, stellarAction
);
mean_field::operators::kernels::
apply_hydrostatic_equilibrium_potential_action(
f, *f.domainMapperStateless, stellarPotential, displacement,
stellarAction);
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);
@@ -486,30 +505,35 @@ TEST_CASE(
CHECK(vacuumActionNorm <= 5.0e-13 * stellarActionNorm);
}
TEST_CASE(
"Hydrostatic Jacobian Matches Blocks And Centered Differences",
tags::barotrope &tags::hydro &tags::integration &tags::jacobian &tags::kernels &tags::mapping &tags::physics
) {
TEST_CASE("Hydrostatic Jacobian Matches Blocks And Centered Differences",
tags::barotrope &tags::hydro &tags::integration &tags::jacobian
&tags::kernels &tags::mapping &tags::physics) {
auto args = test_utils::setup_args();
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 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 =
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 =
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 =
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 constantVariation = -0.37;
@@ -522,26 +546,28 @@ TEST_CASE(
mfem::Vector completeAction;
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(
f, *f.domainMapperStateless, potentialVariation, displacement, potentialAction
);
mean_field::operators::kernels::
apply_hydrostatic_equilibrium_potential_action(
f, *f.domainMapperStateless, potentialVariation, displacement,
potentialAction);
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(
f, *f.domainMapperStateless, rotation, enthalpy, potential, displacement, bernoulliConstant,
displacementVariation, displacementAction
);
mean_field::operators::kernels::
apply_hydrostatic_equilibrium_displacement_action(
f, *f.domainMapperStateless, rotation, enthalpy, potential,
displacement, bernoulliConstant, displacementVariation,
displacementAction);
mean_field::operators::kernels::apply_hydrostatic_equilibrium_action(
f, *f.domainMapperStateless, rotation, enthalpy, potential, displacement, bernoulliConstant, enthalpyVariation,
potentialVariation, constantVariation, displacementVariation, completeAction
);
f, *f.domainMapperStateless, rotation, enthalpy, potential, displacement,
bernoulliConstant, enthalpyVariation, potentialVariation,
constantVariation, displacementVariation, completeAction);
mfem::Vector blockAction(enthalpyAction);
blockAction += potentialAction;
@@ -550,22 +576,23 @@ TEST_CASE(
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);
CHECK(blockError < 5.0e-13);
auto evaluate_residual = [&f, &rotation](
const mfem::Vector &trialEnthalpy, const mfem::Vector &trialPotential,
const mfem::Vector &trialDisplacement, const double trialConstant
) {
auto evaluate_residual = [&f,
&rotation](const mfem::Vector &trialEnthalpy,
const mfem::Vector &trialPotential,
const mfem::Vector &trialDisplacement,
const double trialConstant) {
mfem::Vector residual;
mean_field::operators::kernels::apply_hydrostatic_equilibrium(
f, *f.domainMapperStateless, rotation, trialEnthalpy, trialPotential, trialDisplacement, trialConstant,
residual
);
f, *f.domainMapperStateless, rotation, trialEnthalpy, trialPotential,
trialDisplacement, trialConstant, residual);
return residual;
};
@@ -577,10 +604,13 @@ TEST_CASE(
minusEnthalpy.Add(-epsilon, enthalpyVariation);
const mfem::Vector enthalpyDifference = hydrostatic_kernel_test_utils::centered_difference(
evaluate_residual(plusEnthalpy, potential, displacement, bernoulliConstant),
evaluate_residual(minusEnthalpy, potential, displacement, bernoulliConstant), epsilon
);
const mfem::Vector enthalpyDifference =
hydrostatic_kernel_test_utils::centered_difference(
evaluate_residual(plusEnthalpy, potential, displacement,
bernoulliConstant),
evaluate_residual(minusEnthalpy, potential, displacement,
bernoulliConstant),
epsilon);
mfem::Vector plusPotential(potential);
mfem::Vector minusPotential(potential);
@@ -589,15 +619,21 @@ TEST_CASE(
minusPotential.Add(-epsilon, potentialVariation);
const mfem::Vector potentialDifference = hydrostatic_kernel_test_utils::centered_difference(
evaluate_residual(enthalpy, plusPotential, displacement, bernoulliConstant),
evaluate_residual(enthalpy, minusPotential, displacement, bernoulliConstant), epsilon
);
const mfem::Vector potentialDifference =
hydrostatic_kernel_test_utils::centered_difference(
evaluate_residual(enthalpy, plusPotential, displacement,
bernoulliConstant),
evaluate_residual(enthalpy, minusPotential, displacement,
bernoulliConstant),
epsilon);
const mfem::Vector constantDifference = hydrostatic_kernel_test_utils::centered_difference(
evaluate_residual(enthalpy, potential, displacement, bernoulliConstant + epsilon * constantVariation),
evaluate_residual(enthalpy, potential, displacement, bernoulliConstant - epsilon * constantVariation), epsilon
);
const mfem::Vector constantDifference =
hydrostatic_kernel_test_utils::centered_difference(
evaluate_residual(enthalpy, potential, displacement,
bernoulliConstant + epsilon * constantVariation),
evaluate_residual(enthalpy, potential, displacement,
bernoulliConstant - epsilon * constantVariation),
epsilon);
mfem::Vector plusDisplacement(displacement);
mfem::Vector minusDisplacement(displacement);
@@ -606,22 +642,25 @@ TEST_CASE(
minusDisplacement.Add(-epsilon, displacementVariation);
const mfem::Vector displacementDifference = hydrostatic_kernel_test_utils::centered_difference(
evaluate_residual(enthalpy, potential, plusDisplacement, bernoulliConstant),
evaluate_residual(enthalpy, potential, minusDisplacement, bernoulliConstant), epsilon
);
const mfem::Vector displacementDifference =
hydrostatic_kernel_test_utils::centered_difference(
evaluate_residual(enthalpy, potential, plusDisplacement,
bernoulliConstant),
evaluate_residual(enthalpy, potential, minusDisplacement,
bernoulliConstant),
epsilon);
const double enthalpyError =
gravity_prepared_test_utils::relative_error(enthalpyAction, enthalpyDifference, communicator);
const double enthalpyError = gravity_prepared_test_utils::relative_error(
enthalpyAction, enthalpyDifference, communicator);
const double potentialError =
gravity_prepared_test_utils::relative_error(potentialAction, potentialDifference, communicator);
const double potentialError = gravity_prepared_test_utils::relative_error(
potentialAction, potentialDifference, communicator);
const double constantError =
gravity_prepared_test_utils::relative_error(constantAction, constantDifference, communicator);
const double constantError = gravity_prepared_test_utils::relative_error(
constantAction, constantDifference, communicator);
const double displacementError =
gravity_prepared_test_utils::relative_error(displacementAction, displacementDifference, communicator);
const double displacementError = gravity_prepared_test_utils::relative_error(
displacementAction, displacementDifference, communicator);
INFO("Hydrostatic enthalpy-block error = " << enthalpyError);
@@ -655,86 +694,91 @@ TEST_CASE(
combinedMinusDisplacement.Add(-epsilon, displacementVariation);
const mfem::Vector combinedDifference = hydrostatic_kernel_test_utils::centered_difference(
evaluate_residual(
combinedPlusEnthalpy, combinedPlusPotential, combinedPlusDisplacement,
bernoulliConstant + epsilon * constantVariation
),
evaluate_residual(
combinedMinusEnthalpy, combinedMinusPotential, combinedMinusDisplacement,
bernoulliConstant - epsilon * constantVariation
),
epsilon
);
const mfem::Vector combinedDifference =
hydrostatic_kernel_test_utils::centered_difference(
evaluate_residual(combinedPlusEnthalpy, combinedPlusPotential,
combinedPlusDisplacement,
bernoulliConstant + epsilon * constantVariation),
evaluate_residual(combinedMinusEnthalpy, combinedMinusPotential,
combinedMinusDisplacement,
bernoulliConstant - epsilon * constantVariation),
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(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(
completeAction, combinedDifference, blockNormSum, communicator
);
const double simultaneousError =
hydrostatic_kernel_test_utils::sum_normalized_error(
completeAction, combinedDifference, blockNormSum, communicator);
INFO("Hydrostatic simultaneous Jacobian error = " << simultaneousError);
CHECK(simultaneousError < 2.0e-7);
}
TEST_CASE(
"Hydrostatic Displacement Action Is Linear In Its Direction",
tags::barotrope &tags::hydro &tags::integration &tags::jacobian &tags::mapping &tags::physics &tags::unit
&tags::kernels
) {
TEST_CASE("Hydrostatic Displacement Action Is Linear In Its Direction",
tags::barotrope &tags::hydro &tags::integration &tags::jacobian
&tags::mapping &tags::physics &tags::unit &tags::kernels) {
auto args = test_utils::setup_args();
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 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 =
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 =
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 secondScale = -0.29;
constexpr double bernoulliConstant = 0.41;
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 secondAction;
mfem::Vector combinedAction;
mean_field::operators::kernels::apply_hydrostatic_equilibrium_displacement_action(
f, *f.domainMapperStateless, rotation, enthalpy, potential, displacement, bernoulliConstant, firstDirection,
firstAction
);
mean_field::operators::kernels::
apply_hydrostatic_equilibrium_displacement_action(
f, *f.domainMapperStateless, rotation, enthalpy, potential,
displacement, bernoulliConstant, firstDirection, firstAction);
mean_field::operators::kernels::apply_hydrostatic_equilibrium_displacement_action(
f, *f.domainMapperStateless, rotation, enthalpy, potential, displacement, bernoulliConstant, secondDirection,
secondAction
);
mean_field::operators::kernels::
apply_hydrostatic_equilibrium_displacement_action(
f, *f.domainMapperStateless, rotation, enthalpy, potential,
displacement, bernoulliConstant, secondDirection, secondAction);
mean_field::operators::kernels::apply_hydrostatic_equilibrium_displacement_action(
f, *f.domainMapperStateless, rotation, enthalpy, potential, displacement, bernoulliConstant, combinedDirection,
combinedAction
);
mean_field::operators::kernels::
apply_hydrostatic_equilibrium_displacement_action(
f, *f.domainMapperStateless, rotation, enthalpy, potential,
displacement, bernoulliConstant, combinedDirection, combinedAction);
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 =
gravity_prepared_test_utils::relative_error(combinedAction, expectedAction, f.mesh->GetComm());
const double linearityError = gravity_prepared_test_utils::relative_error(
combinedAction, expectedAction, f.mesh->GetComm());
INFO("Hydrostatic displacement-linearity error = " << linearityError);
@@ -743,8 +787,8 @@ TEST_CASE(
TEST_CASE(
"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();
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);
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 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
* and mapped geometry simultaneously. The comparison state adds
* 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::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 = translation;
}
);
});
translationField.ProjectCoefficient(translationCoefficient);
@@ -810,43 +858,47 @@ TEST_CASE(
mfem::Vector untranslatedCenterResidual;
mean_field::operators::kernels::apply_hydrostatic_equilibrium(
f, *f.domainMapperStateless, baseRotation, enthalpy, potential, baseDisplacement, bernoulliConstant,
baseResidual
);
f, *f.domainMapperStateless, baseRotation, enthalpy, potential,
baseDisplacement, bernoulliConstant, baseResidual);
mean_field::operators::kernels::apply_hydrostatic_equilibrium(
f, *f.domainMapperStateless, translatedRotation, enthalpy, potential, translatedDisplacement, bernoulliConstant,
translatedResidual
);
f, *f.domainMapperStateless, translatedRotation, enthalpy, potential,
translatedDisplacement, bernoulliConstant, translatedResidual);
/*
* Negative control: translate the geometry but leave the rotation
* center fixed. This must not agree with the covariant result.
*/
mean_field::operators::kernels::apply_hydrostatic_equilibrium(
f, *f.domainMapperStateless, baseRotation, enthalpy, potential, translatedDisplacement, bernoulliConstant,
untranslatedCenterResidual
);
f, *f.domainMapperStateless, baseRotation, enthalpy, potential,
translatedDisplacement, bernoulliConstant, untranslatedCenterResidual);
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 =
gravity_prepared_test_utils::relative_error(translatedResidual, baseResidual, communicator);
gravity_prepared_test_utils::relative_error(translatedResidual,
baseResidual, communicator);
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("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(translatedResidualNorm > 1.0e-12);

View File

@@ -10,52 +10,48 @@ import mean_field;
import test_helpers;
namespace pressure_force_kernel_test_utils {
[[nodiscard]] mfem::Vector make_deterministic_vector(
const int size,
const double phase
) {
[[nodiscard]] mfem::Vector make_deterministic_vector(const int size,
const double phase) {
mfem::Vector vector(size);
for (int index = 0; index < size; ++index) {
const double position = static_cast<double>(index + 1);
vector(index) =
0.71 + 0.19 * std::sin(0.31 * position + phase) + 0.08 * std::cos(0.17 * position - 0.5 * phase);
vector(index) = 0.71 + 0.19 * std::sin(0.31 * position + phase) +
0.08 * std::cos(0.17 * position - 0.5 * phase);
}
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());
displacementTrue = 0.0;
return displacementTrue;
}
[[nodiscard]] mfem::Vector make_vacuum_only_enthalpy(const mean_field::fem::FEM &f) {
mfem::Vector enthalpyTrue = make_deterministic_vector(f.enthalpyFes->GetTrueVSize(), 0.43);
[[nodiscard]] mfem::Vector
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;
mean_field::utils::populate_element_mask(f.mesh.get(), mean_field::utils::DOMAINS::STELLAR, stellarElementMask);
using DomainSchema =
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;
mean_field::utils::populate_domain_tdofs(f.enthalpyFes.get(), stellarElementMask, stellarEnthalpyTrueDofs);
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;
for (int reducedDof = 0; reducedDof < enthalpyMap.reduced_size();
++reducedDof) {
enthalpyTrue(enthalpyMap.true_dof(reducedDof)) = 0.0;
}
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) {
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;
}
[[nodiscard]] mfem::Vector make_component_test_field(
const mean_field::fem::FEM &f,
const int component,
const int coordinate
) {
[[nodiscard]] mfem::Vector
make_component_test_field(const mean_field::fem::FEM &f, const int component,
const int coordinate) {
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.
@@ -89,13 +85,13 @@ namespace pressure_force_kernel_test_utils {
* w = x_coordinate e_component.
*/
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 = 0.0;
value(component) = coordinate < 0 ? 1.0 : position(coordinate);
}
);
});
mfem::ParGridFunction field(f.displacementFes.get());
@@ -107,12 +103,11 @@ namespace pressure_force_kernel_test_utils {
return fieldTrue;
}
[[nodiscard]] double global_dot(
const mfem::Vector &left,
[[nodiscard]] double global_dot(const mfem::Vector &left,
const mfem::Vector &right,
MPI_Comm communicator
) {
MFEM_VERIFY(left.Size() == right.Size(), "The global dot-product vectors have different sizes.");
MPI_Comm communicator) {
MFEM_VERIFY(left.Size() == right.Size(),
"The global dot-product vectors have different sizes.");
const double localDot = left * right;
double globalDot = 0.0;
@@ -124,24 +119,19 @@ namespace pressure_force_kernel_test_utils {
[[nodiscard]] double integrate_pressure(
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 mfem::Vector &enthalpyTrue,
const mfem::Vector &displacementTrue
) {
MFEM_VERIFY(
enthalpyTrue.Size() == f.enthalpyFes->GetTrueVSize(),
"The pressure-integral enthalpy vector has the wrong size."
);
const mfem::Vector &enthalpyTrue, const mfem::Vector &displacementTrue) {
MFEM_VERIFY(enthalpyTrue.Size() == f.enthalpyFes->GetTrueVSize(),
"The pressure-integral enthalpy vector has the wrong size.");
MFEM_VERIFY(
displacementTrue.Size() == f.displacementFes->GetTrueVSize(),
"The pressure-integral displacement vector has the wrong size."
);
MFEM_VERIFY(displacementTrue.Size() == f.displacementFes->GetTrueVSize(),
"The pressure-integral displacement vector has the wrong size.");
mfem::Vector enthalpyLocal(f.enthalpyFes->GetVSize());
const mfem::Operator *enthalpyProlongation = f.enthalpyFes->GetProlongationMatrix();
const mfem::Operator *enthalpyProlongation =
f.enthalpyFes->GetProlongationMatrix();
if (enthalpyProlongation != nullptr) {
enthalpyProlongation->Mult(enthalpyTrue, enthalpyLocal);
@@ -151,7 +141,8 @@ namespace pressure_force_kernel_test_utils {
mfem::Vector displacementLocal(f.displacementFes->GetVSize());
const mfem::Operator *displacementProlongation = f.displacementFes->GetProlongationMatrix();
const mfem::Operator *displacementProlongation =
f.displacementFes->GetProlongationMatrix();
if (displacementProlongation != nullptr) {
displacementProlongation->Mult(displacementTrue, displacementLocal);
@@ -160,19 +151,22 @@ namespace pressure_force_kernel_test_utils {
}
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(
std::isfinite(pressureExtraOrderValue) && pressureExtraOrderValue >= 0.0 &&
pressureExtraOrderValue <= static_cast<double>(std::numeric_limits<int>::max()),
"The pressure-integral EOS order is invalid."
);
MFEM_VERIFY(std::isfinite(pressureExtraOrderValue) &&
pressureExtraOrderValue >= 0.0 &&
pressureExtraOrderValue <=
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>;
mean_field::mapping::DomainMapperStateless::Workspace workspace(f.mesh->Dimension());
mean_field::mapping::DomainMapper::Workspace workspace(
f.mesh->Dimension());
mean_field::mapping::VolumeMappingContext mappingContext;
@@ -187,27 +181,31 @@ namespace pressure_force_kernel_test_utils {
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) {
mfem::ElementTransformation *transformation = f.mesh->GetElementTransformation(elementId);
mfem::ElementTransformation *transformation =
f.mesh->GetElementTransformation(elementId);
MFEM_VERIFY(
transformation != nullptr, "The pressure-integral reference received a null "
"element transformation."
);
MFEM_VERIFY(transformation != nullptr,
"The pressure-integral reference received a null "
"element transformation.");
if (transformation->Attribute == vacuumAttribute) {
continue;
}
const mfem::FiniteElement &enthalpyElement = *f.enthalpyFes->GetFE(elementId);
const mfem::FiniteElement &enthalpyElement =
*f.enthalpyFes->GetFE(elementId);
const mfem::FiniteElement &displacementElement = *f.displacementFes->GetFE(elementId);
const mfem::FiniteElement &displacementElement =
*f.displacementFes->GetFE(elementId);
const mfem::FiniteElement &compactificationElement = *f.compactificationFes->GetFE(elementId);
const mfem::FiniteElement &compactificationElement =
*f.compactificationFes->GetFE(elementId);
mfem::DofTransformation *enthalpyDofTransformation = f.enthalpyFes->GetElementDofs(elementId, enthalpyDofs);
mfem::DofTransformation *enthalpyDofTransformation =
f.enthalpyFes->GetElementDofs(elementId, enthalpyDofs);
mfem::DofTransformation *displacementDofTransformation =
f.displacementFes->GetElementVDofs(elementId, displacementDofs);
@@ -219,7 +217,8 @@ namespace pressure_force_kernel_test_utils {
displacementLocal.GetSubVector(displacementDofs, elementDisplacement);
f.compactificationCoordinate->GetSubVector(compactificationDofs, elementCompactification);
f.compactificationCoordinate->GetSubVector(compactificationDofs,
elementCompactification);
if (enthalpyDofTransformation != nullptr) {
enthalpyDofTransformation->InvTransformPrimal(elementEnthalpy);
@@ -230,63 +229,70 @@ namespace pressure_force_kernel_test_utils {
}
if (compactificationDofTransformation != nullptr) {
compactificationDofTransformation->InvTransformPrimal(elementCompactification);
compactificationDofTransformation->InvTransformPrimal(
elementCompactification);
}
const mean_field::mapping::ElementDisplacementData displacementData =
mean_field::mapping::ElementDisplacementDataFromElementVDofs(displacementElement, elementDisplacement);
mean_field::mapping::ElementDisplacementDataFromElementVDofs(
displacementElement, elementDisplacement);
const mean_field::mapping::ElementCompactificationData compactificationData(
compactificationElement, elementCompactification
);
compactificationElement, elementCompactification);
const mean_field::mapping::ElementMappingData mappingData{
.displacement = displacementData, .compactification = compactificationData
};
.displacement = displacementData,
.compactification = compactificationData};
const mean_field::quadrature::Query query =
EnthalpyField::make_query<mean_field::field::Enthalpy::Form::PressureIntegral>(
mean_field::quadrature::QuadratureRole::diagnostic, transformation->OrderW(),
std::array<int, 1>{pressureExtraOrder}, mean_field::utils::DOMAINS::STELLAR,
mean_field::quadrature::MappingKind::general
);
const mean_field::quadrature::Query query = EnthalpyField::make_query<
mean_field::field::Enthalpy::Form::PressureIntegral>(
mean_field::quadrature::QuadratureRole::diagnostic,
transformation->OrderW(), std::array<int, 1>{pressureExtraOrder},
mean_field::utils::DOMAINS::STELLAR,
mean_field::quadrature::MappingKind::general);
const mean_field::quadrature::MfemRule rule =
f.quadratureFactory->get(query, transformation->GetGeometryType());
MFEM_VERIFY(rule.integration_rule != nullptr, "The pressure-integral quadrature rule is null.");
MFEM_VERIFY(rule.integration_rule != nullptr,
"The pressure-integral quadrature rule is null.");
enthalpyShape.SetSize(enthalpyElement.GetDof());
for (int quadratureIndex = 0; quadratureIndex < rule.integration_rule->GetNPoints(); ++quadratureIndex) {
const mfem::IntegrationPoint &integrationPoint = rule.integration_rule->IntPoint(quadratureIndex);
for (int quadratureIndex = 0;
quadratureIndex < rule.integration_rule->GetNPoints();
++quadratureIndex) {
const mfem::IntegrationPoint &integrationPoint =
rule.integration_rule->IntPoint(quadratureIndex);
transformation->SetIntPoint(&integrationPoint);
const mean_field::mapping::MappingStatus mappingStatus = domainMapper.EvaluateVolume(
mappingData, *transformation, integrationPoint, workspace, mappingContext
);
const mean_field::mapping::MappingStatus mappingStatus =
domainMapper.EvaluateVolume(mappingData, *transformation,
integrationPoint, workspace,
mappingContext);
MFEM_VERIFY(
mappingStatus == mean_field::mapping::MappingStatus::valid,
MFEM_VERIFY(mappingStatus == mean_field::mapping::MappingStatus::valid,
"Stateless mapping failed in the "
"independent pressure integral. Element: "
<< elementId << ", attribute: " << transformation->Attribute
<< ", quadrature point: " << quadratureIndex << ", status: " << static_cast<int>(mappingStatus)
);
<< elementId
<< ", attribute: " << transformation->Attribute
<< ", quadrature point: " << quadratureIndex
<< ", status: " << static_cast<int>(mappingStatus));
enthalpyElement.CalcShape(integrationPoint, enthalpyShape);
const double enthalpyValue = elementEnthalpy * enthalpyShape;
const double 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(
std::isfinite(pressureValue) && std::isfinite(contribution), "The independent pressure integral "
"encountered a non-finite value."
);
MFEM_VERIFY(std::isfinite(pressureValue) && std::isfinite(contribution),
"The independent pressure integral "
"encountered a non-finite value.");
localPressureIntegral += contribution;
}
@@ -294,16 +300,15 @@ namespace pressure_force_kernel_test_utils {
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;
}
} // namespace pressure_force_kernel_test_utils
TEST_CASE(
"Pressure Force Residual Vanishes For Zero Enthalpy",
tags::barotrope &tags::pressure &tags::kernels &tags::integration
) {
TEST_CASE("Pressure Force Residual Vanishes For Zero Enthalpy",
tags::barotrope &tags::pressure &tags::kernels &tags::integration) {
mean_field::utils::Args args = test_utils::setup_args();
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());
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;
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());
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);
}
TEST_CASE(
"Pressure Force Residual Excludes Vacuum Enthalpy Exactly",
tags::barotrope &tags::pressure &tags::kernels &tags::integration
) {
TEST_CASE("Pressure Force Residual Excludes Vacuum Enthalpy Exactly",
tags::barotrope &tags::pressure &tags::kernels &tags::integration) {
mean_field::utils::Args args = test_utils::setup_args();
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 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
@@ -352,25 +359,25 @@ TEST_CASE(
*/
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;
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());
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);
}
TEST_CASE(
"Pressure Force Residual Is Nonzero For Positive Stellar Pressure",
tags::barotrope &tags::pressure &tags::kernels &tags::integration
) {
TEST_CASE("Pressure Force Residual Is Nonzero For Positive Stellar Pressure",
tags::barotrope &tags::pressure &tags::kernels &tags::integration) {
mean_field::utils::Args args = test_utils::setup_args();
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());
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;
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);
@@ -404,10 +413,9 @@ TEST_CASE(
CHECK(residualNorm > 100.0 * std::numeric_limits<double>::epsilon());
}
TEST_CASE(
"Pressure Force Residual Does No Work Against Rigid Translations",
tags::barotrope &tags::pressure &tags::kernels &tags::integration &tags::accuracy
) {
TEST_CASE("Pressure Force Residual Does No Work Against Rigid Translations",
tags::barotrope &tags::pressure &tags::kernels &tags::integration
&tags::accuracy) {
mean_field::utils::Args args = test_utils::setup_args();
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 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;
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);
@@ -436,14 +447,17 @@ TEST_CASE(
for (int component = 0; component < dimension; ++component) {
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 =
pressure_force_kernel_test_utils::global_dot(translationTrue, residualTrue, f.mesh->GetComm());
const double translationWork = pressure_force_kernel_test_utils::global_dot(
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);
@@ -451,10 +465,9 @@ TEST_CASE(
}
}
TEST_CASE(
"Pressure Force Residual Matches Independent Pressure Integral",
tags::barotrope &tags::pressure &tags::kernels &tags::integration &tags::accuracy
) {
TEST_CASE("Pressure Force Residual Matches Independent Pressure Integral",
tags::barotrope &tags::pressure &tags::kernels &tags::integration
&tags::accuracy) {
mean_field::utils::Args args = test_utils::setup_args();
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 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;
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();
@@ -484,16 +499,19 @@ TEST_CASE(
for (int component = 0; component < dimension; ++component) {
for (int coordinate = 0; coordinate < dimension; ++coordinate) {
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) =
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(
f, *f.domainMapperStateless, barotrope, enthalpyTrue, displacementTrue
);
const double pressureIntegral =
pressure_force_kernel_test_utils::integrate_pressure(
f, *f.domainMapperStateless, barotrope, enthalpyTrue,
displacementTrue);
REQUIRE(std::isfinite(pressureIntegral));
@@ -527,25 +545,28 @@ TEST_CASE(
for (int coordinate = 0; coordinate < dimension; ++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);
}
}
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);
CHECK(relativeMeanError <= 1.0e-6);
}
TEST_CASE(
"Pressure Force Residual Matches Deformed Pressure Volume Variation",
tags::barotrope &tags::pressure &tags::kernels &tags::integration &tags::accuracy
) {
TEST_CASE("Pressure Force Residual Matches Deformed Pressure Volume Variation",
tags::barotrope &tags::pressure &tags::kernels &tags::integration
&tags::accuracy) {
mean_field::utils::Args args = test_utils::setup_args();
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.
*/
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
* quadratic cross terms. A scale of 0.67 therefore provides a
* 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
@@ -576,38 +599,43 @@ TEST_CASE(
* This gives a controlled geometry path while still evaluating
* 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 =
gravity_prepared_test_utils::global_norm(baseDisplacementTrue, f.mesh->GetComm());
const double baseDisplacementNorm = gravity_prepared_test_utils::global_norm(
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());
mfem::Vector residualTrue;
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());
const double residualWork =
pressure_force_kernel_test_utils::global_dot(displacementVariationTrue, residualTrue, f.mesh->GetComm());
const double residualWork = pressure_force_kernel_test_utils::global_dot(
displacementVariationTrue, residualTrue, f.mesh->GetComm());
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
* centered-difference convergence before reaching the quadrature
* 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();
@@ -620,18 +648,21 @@ TEST_CASE(
displacementMinus.Add(-differenceStep, displacementVariationTrue);
const double pressureIntegralPlus = pressure_force_kernel_test_utils::integrate_pressure(
f, *f.domainMapperStateless, barotrope, enthalpyTrue, displacementPlus
);
const double pressureIntegralPlus =
pressure_force_kernel_test_utils::integrate_pressure(
f, *f.domainMapperStateless, barotrope, enthalpyTrue,
displacementPlus);
const double pressureIntegralMinus = pressure_force_kernel_test_utils::integrate_pressure(
f, *f.domainMapperStateless, barotrope, enthalpyTrue, displacementMinus
);
const double pressureIntegralMinus =
pressure_force_kernel_test_utils::integrate_pressure(
f, *f.domainMapperStateless, barotrope, enthalpyTrue,
displacementMinus);
REQUIRE(std::isfinite(pressureIntegralPlus));
REQUIRE(std::isfinite(pressureIntegralMinus));
const double pressureVolumeDerivative = (pressureIntegralPlus - pressureIntegralMinus) / (2.0 * differenceStep);
const double pressureVolumeDerivative =
(pressureIntegralPlus - pressureIntegralMinus) / (2.0 * differenceStep);
REQUIRE(std::isfinite(pressureVolumeDerivative));
@@ -643,7 +674,8 @@ TEST_CASE(
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;
@@ -657,7 +689,8 @@ TEST_CASE(
INFO("Pressure-volume derivative = " << pressureVolumeDerivative);
INFO("Residual work plus derivative = " << residualWork + pressureVolumeDerivative);
INFO("Residual work plus derivative = " << residualWork +
pressureVolumeDerivative);
INFO("Relative discrepancy = " << relativeDiscrepancy);
@@ -669,7 +702,8 @@ TEST_CASE(
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.

View File

@@ -1,5 +1,6 @@
#include <catch2/catch_test_macros.hpp>
#include <catch2/matchers/catch_matchers_floating_point.hpp>
#include <cmath>
#include <mfem.hpp>
import mean_field;
@@ -9,28 +10,33 @@ using namespace mean_field;
using Catch::Matchers::WithinAbs;
namespace prepared_test = gravity_prepared_test_utils;
TEST_CASE(
"Prepared Mapped Hdiv Mass Matches Stateless Kernel",
tags::gravity_prepared
) {
TEST_CASE("Prepared Mapped Hdiv Mass Matches Stateless Kernel",
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);
REQUIRE(prepared_operator.Width() == prepared_operator.GetFluxMap().reduced_size());
REQUIRE(prepared_operator.Height() == prepared_operator.GetFluxMap().reduced_size());
operators::PreparedMappedHDivMassOperator prepared_operator(
f, *f.domainMapperStateless);
REQUIRE(prepared_operator.Width() ==
prepared_operator.GetFluxMap().reduced_size());
REQUIRE(prepared_operator.Height() ==
prepared_operator.GetFluxMap().reduced_size());
const mfem::Vector gravity_gradient_true =
prepared_test::make_deterministic_vector(f.gravityFluxFes->GetTrueVSize(), 0.21);
const mfem::Vector gravity_gradient = prepared_operator.GetFluxMap().gather(gravity_gradient_true);
prepared_test::make_deterministic_vector(f.gravityFluxFes->GetTrueVSize(),
0.21);
const mfem::Vector gravity_gradient =
prepared_operator.GetFluxMap().gather(gravity_gradient_true);
const MPI_Comm communicator = f.gravityFluxFes->GetComm();
mfem::Vector identity_action;
mfem::Vector deformed_action;
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 = prepared_operator.GetDisplacementMap().gather(displacement_true);
const mfem::Vector displacement_true =
prepared_test::make_displacement(f, deformation_scale);
const mfem::Vector displacement =
prepared_operator.GetDisplacementMap().gather(displacement_true);
prepared_operator.Prepare(displacement);
@@ -39,15 +45,19 @@ TEST_CASE(
prepared_operator.Mult(gravity_gradient, prepared_action);
mfem::Vector reference_action_true;
operators::kernels::apply_mapped_hdiv_mass(
f, *f.domainMapperStateless, gravity_gradient_true, displacement_true, reference_action_true
);
const mfem::Vector reference_action = prepared_operator.GetFluxMap().gather(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 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("Prepared action norm = " << prepared_test::global_norm(prepared_action, communicator));
INFO("Reference action norm = " << prepared_test::global_norm(reference_action, communicator));
INFO("Prepared action norm = "
<< 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);
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);
@@ -68,27 +79,30 @@ TEST_CASE(
CHECK(geometry_change > 1.0e-5);
}
TEST_CASE(
"Prepared Mapped Hdiv Mass Preserves Operator Identities",
tags::gravity_prepared
) {
TEST_CASE("Prepared Mapped Hdiv Mass Preserves Operator Identities",
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);
REQUIRE(prepared_operator.Width() == prepared_operator.GetFluxMap().reduced_size());
REQUIRE(prepared_operator.Height() == prepared_operator.GetFluxMap().reduced_size());
operators::PreparedMappedHDivMassOperator prepared_operator(
f, *f.domainMapperStateless);
REQUIRE(prepared_operator.Width() ==
prepared_operator.GetFluxMap().reduced_size());
REQUIRE(prepared_operator.Height() ==
prepared_operator.GetFluxMap().reduced_size());
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);
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(
prepared_test::make_deterministic_vector(f.gravityFluxFes->GetTrueVSize(), 0.83)
);
const mfem::Vector combination = prepared_test::linear_combination(first, 1.7, second, -0.4);
prepared_test::make_deterministic_vector(f.gravityFluxFes->GetTrueVSize(),
0.83));
const mfem::Vector combination =
prepared_test::linear_combination(first, 1.7, second, -0.4);
mfem::Vector first_action;
mfem::Vector second_action;
@@ -99,7 +113,8 @@ TEST_CASE(
prepared_operator.Mult(second, second_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());
zero = 0.0;
@@ -107,14 +122,20 @@ TEST_CASE(
const MPI_Comm communicator = f.gravityFluxFes->GetComm();
const double first_second_product = prepared_test::global_dot(first, second_action, communicator);
const double second_first_product = prepared_test::global_dot(second, first_action, communicator);
const double symmetry_error = prepared_test::relative_scalar_error(first_second_product, second_first_product);
const double linearity_error =
prepared_test::relative_error(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();
const double first_second_product =
prepared_test::global_dot(first, second_action, communicator);
const double second_first_product =
prepared_test::global_dot(second, first_action, communicator);
const double symmetry_error = prepared_test::relative_scalar_error(
first_second_product, second_first_product);
const double linearity_error = prepared_test::relative_error(
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;
prepared_operator.Mult(first, repeated_action);
@@ -128,9 +149,42 @@ TEST_CASE(
CHECK_THAT(symmetry_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(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);
}
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:
EnthalpyJacobianOperator(
const int enthalpySize,
const mean_field::operators::PreparedHydrostaticEquilibriumOperator &preparedOperator
)
: mfem::Operator(enthalpySize),
m_preparedOperator(preparedOperator) {
}
const mean_field::operators::PreparedHydrostaticEquilibriumOperator
&preparedOperator)
: mfem::Operator(enthalpySize), m_preparedOperator(preparedOperator) {}
void Mult(
const mfem::Vector &direction,
mfem::Vector &action
) const override {
void Mult(const mfem::Vector &direction,
mfem::Vector &action) const override {
m_preparedOperator.ApplyEnthalpyJacobianAction(direction, action);
}
private:
const mean_field::operators::PreparedHydrostaticEquilibriumOperator &m_preparedOperator;
const mean_field::operators::PreparedHydrostaticEquilibriumOperator
&m_preparedOperator;
};
mean_field::operators::context::hydrostatic::HydrostaticEquilibriumDependencies make_dependencies() {
return {
.discretization = {.identity = 701, .revision = 2},
mean_field::operators::context::hydrostatic::HydrostaticEquilibriumDependencies
make_dependencies() {
return {.discretization = {.identity = 701, .revision = 2},
.enthalpy = {.identity = 709, .revision = 3},
.gravityPotential = {.identity = 719, .revision = 5},
.displacement = {.identity = 727, .revision = 7},
.rotation = {.identity = 733, .revision = 11},
.bernoulliConstant = {.identity = 739, .revision = 13}
};
.bernoulliConstant = {.identity = 739, .revision = 13}};
}
mean_field::operators::context::hydrostatic::HydrostaticEquilibriumStateView make_state(
const mfem::Vector &enthalpy,
const mfem::Vector &gravityPotential,
const mfem::Vector &displacement
) {
return {
.enthalpy = enthalpy,
mean_field::operators::context::hydrostatic::HydrostaticEquilibriumStateView
make_state(const mfem::Vector &enthalpy, const mfem::Vector &gravityPotential,
const mfem::Vector &displacement) {
return {.enthalpy = enthalpy,
.gravityPotential = gravityPotential,
.displacement = displacement,
.bernoulliConstant = bernoulliConstant
};
.bernoulliConstant = bernoulliConstant};
}
mfem::Vector make_vector(
const std::array<
double,
3> &values
) {
mfem::Vector make_vector(const std::array<double, 3> &values) {
mfem::Vector vector(3);
for (int component = 0; component < 3; ++component) {
@@ -79,22 +67,22 @@ namespace prepared_hydrostatic_analytic_solve_test_utils {
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(
make_vector(analyticCase.angularVelocity), make_vector(analyticCase.rotationCenter)
);
make_vector(analyticCase.angularVelocity),
make_vector(analyticCase.rotationCenter));
}
void map_to_physical(
const mfem::Vector &referencePosition,
void map_to_physical(const mfem::Vector &referencePosition,
const AnalyticCase &analyticCase,
mfem::Vector &physicalPosition
) {
mfem::Vector &physicalPosition) {
physicalPosition.SetSize(3);
for (int component = 0; component < 3; ++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;
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;
}
@@ -110,11 +99,10 @@ namespace prepared_hydrostatic_analytic_solve_test_utils {
return enthalpyAmplitude * std::max(0.0, 1.0 - normalizedRadiusSquared);
}
double exact_potential_value(
const mfem::Vector &referencePosition,
double
exact_potential_value(const mfem::Vector &referencePosition,
const AnalyticCase &analyticCase,
const mean_field::physics::RigidRotation &rotation
) {
const mean_field::physics::RigidRotation &rotation) {
mfem::Vector physicalPosition;
map_to_physical(referencePosition, analyticCase, physicalPosition);
@@ -126,26 +114,27 @@ namespace prepared_hydrostatic_analytic_solve_test_utils {
*
* 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> 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) {
stellarElementMarker[elementId] = f.mesh->GetAttribute(elementId) != vacuumAttribute;
stellarElementMarker[elementId] =
f.mesh->GetAttribute(elementId) != vacuumAttribute;
}
return stellarElementMarker;
}
} // namespace prepared_hydrostatic_analytic_solve_test_utils
TEST_CASE(
"Prepared Hydrostatic Operator Solves Analytic Bernoulli Equilibria",
tags::barotrope_hydrostatic_prepared_analytic &tags::convergence &tags::accuracy
) {
TEST_CASE("Prepared Hydrostatic Operator Solves Analytic Bernoulli Equilibria",
tags::barotrope_hydrostatic_prepared_analytic &tags::convergence
&tags::accuracy) {
using prepared_hydrostatic_analytic_solve_test_utils::AnalyticCase;
constexpr double deformationX = 1.08;
@@ -171,8 +160,7 @@ TEST_CASE(
{.name = "volume-preserving deformed rotating equilibrium",
.deformationScale = {deformationX, deformationY, deformationZ},
.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();
@@ -181,51 +169,59 @@ TEST_CASE(
const MPI_Comm communicator = f.mesh->GetComm();
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 =
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 =
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 =
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) {
DYNAMIC_SECTION(analyticCase.name) {
const double deformationDeterminant =
analyticCase.deformationScale[0] * analyticCase.deformationScale[1] * analyticCase.deformationScale[2];
const double deformationDeterminant = analyticCase.deformationScale[0] *
analyticCase.deformationScale[1] *
analyticCase.deformationScale[2];
REQUIRE(std::abs(deformationDeterminant - 1.0) < 2.0e-14);
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 =
[&analyticCase](const mfem::Vector &referencePosition, mfem::Vector &displacementValue) {
[&analyticCase](const mfem::Vector &referencePosition,
mfem::Vector &displacementValue) {
mfem::Vector physicalPosition;
prepared_hydrostatic_analytic_solve_test_utils::map_to_physical(
referencePosition, analyticCase, physicalPosition
);
referencePosition, analyticCase, physicalPosition);
displacementValue.SetSize(3);
displacementValue = physicalPosition;
displacementValue -= referencePosition;
};
auto potentialFunction = [&analyticCase, &rotation](const mfem::Vector &referencePosition) {
return prepared_hydrostatic_analytic_solve_test_utils::exact_potential_value(
referencePosition, analyticCase, rotation
);
auto potentialFunction = [&analyticCase, &rotation](
const mfem::Vector &referencePosition) {
return prepared_hydrostatic_analytic_solve_test_utils::
exact_potential_value(referencePosition, analyticCase, rotation);
};
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);
@@ -248,8 +244,10 @@ TEST_CASE(
displacementField.GetTrueDofs(displacementTrue);
potentialField.GetTrueDofs(gravityPotentialTrue);
const mfem::Vector displacement = displacementMap.gather(displacementTrue);
const mfem::Vector gravityPotential = gravityPotentialMap.gather(gravityPotentialTrue);
const mfem::Vector displacement =
displacementMap.gather(displacementTrue);
const mfem::Vector gravityPotential =
gravityPotentialMap.gather(gravityPotentialTrue);
/*
* This projection is not used as the solution. It gives
@@ -264,15 +262,16 @@ TEST_CASE(
zeroEnthalpyField = 0.0;
const double exactEnthalpyNorm =
zeroEnthalpyField.ComputeL2Error(exactEnthalpyCoefficient, nullptr, &stellarElementMarker);
const double exactEnthalpyNorm = zeroEnthalpyField.ComputeL2Error(
exactEnthalpyCoefficient, nullptr, &stellarElementMarker);
const double projectionError =
projectedEnthalpyField.ComputeL2Error(exactEnthalpyCoefficient, nullptr, &stellarElementMarker);
const double projectionError = projectedEnthalpyField.ComputeL2Error(
exactEnthalpyCoefficient, nullptr, &stellarElementMarker);
REQUIRE(exactEnthalpyNorm > 0.0);
const double relativeProjectionError = projectionError / exactEnthalpyNorm;
const double relativeProjectionError =
projectionError / exactEnthalpyNorm;
/*
* Begin deliberately far from equilibrium.
@@ -281,14 +280,16 @@ TEST_CASE(
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(
prepared_hydrostatic_analytic_solve_test_utils::make_state(enthalpy, gravityPotential, displacement),
dependencies, rotation
);
prepared_hydrostatic_analytic_solve_test_utils::make_state(
enthalpy, gravityPotential, displacement),
dependencies, rotation);
REQUIRE(initialReport.preparedResidual);
REQUIRE(initialReport.preparedAlgebraicJacobianBlocks);
@@ -297,7 +298,9 @@ TEST_CASE(
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);
@@ -311,9 +314,8 @@ TEST_CASE(
* rotation, and displacement makes this a well-defined
* enthalpy solve.
*/
prepared_hydrostatic_analytic_solve_test_utils::EnthalpyJacobianOperator enthalpyJacobian(
enthalpyMap.reduced_size(), preparedOperator
);
prepared_hydrostatic_analytic_solve_test_utils::EnthalpyJacobianOperator
enthalpyJacobian(enthalpyMap.reduced_size(), preparedOperator);
mfem::Vector rightHandSide(initialResidual);
rightHandSide *= -1.0;
@@ -355,9 +357,9 @@ TEST_CASE(
++dependencies.enthalpy.revision;
const auto solvedReport = preparedOperator.Prepare(
prepared_hydrostatic_analytic_solve_test_utils::make_state(enthalpy, gravityPotential, displacement),
dependencies, rotation
);
prepared_hydrostatic_analytic_solve_test_utils::make_state(
enthalpy, gravityPotential, displacement),
dependencies, rotation);
CHECK(solvedReport.contextReport.updatedEnthalpy);
@@ -371,7 +373,9 @@ TEST_CASE(
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;
@@ -389,10 +393,11 @@ TEST_CASE(
enthalpyMap.scatter(enthalpy, enthalpyTrue);
solvedEnthalpyField.SetFromTrueDofs(enthalpyTrue);
const double solvedAnalyticError =
solvedEnthalpyField.ComputeL2Error(exactEnthalpyCoefficient, nullptr, &stellarElementMarker);
const double solvedAnalyticError = solvedEnthalpyField.ComputeL2Error(
exactEnthalpyCoefficient, nullptr, &stellarElementMarker);
const double relativeSolvedAnalyticError = solvedAnalyticError / exactEnthalpyNorm;
const double relativeSolvedAnalyticError =
solvedAnalyticError / exactEnthalpyNorm;
INFO("Deformation determinant = " << deformationDeterminant);
@@ -404,7 +409,8 @@ TEST_CASE(
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
@@ -419,7 +425,8 @@ TEST_CASE(
* also contains potential-projection and mapped-space
* 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

View File

@@ -24,76 +24,59 @@ namespace prepared_pressure_force_test_utils {
explicit Maps(const mean_field::fem::FEM &f)
: density(
mean_field::field::make_field_dof_map<
mean_field::field::Density,
DomainSchema>(*f.densityFes)
),
displacement(
mean_field::field::make_field_dof_map<
mean_field::field::Displacement,
DomainSchema>(*f.displacementFes)
),
mean_field::field::make_field_dof_map<mean_field::field::Density,
DomainSchema>(*f.densityFes)),
displacement(mean_field::field::make_field_dof_map<
mean_field::field::Displacement, DomainSchema>(
*f.displacementFes)),
gravityFlux(
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,
DomainSchema>(
*f.gravityFluxFes)),
gravityPotential(
mean_field::field::make_field_dof_map<
mean_field::field::Gravity,
DomainSchema>(*f.gravityPotentialFes)
),
enthalpy(
mean_field::field::make_field_dof_map<
mean_field::field::Enthalpy,
DomainSchema>(*f.enthalpyFes)
) {
}
mean_field::field::make_field_dof_map<mean_field::field::Gravity,
DomainSchema>(
*f.gravityPotentialFes)),
enthalpy(mean_field::field::make_field_dof_map<
mean_field::field::Enthalpy, DomainSchema>(*f.enthalpyFes)) {}
};
[[nodiscard]]
mfem::Vector make_positive_enthalpy_true(
const mean_field::fem::FEM &f,
const double phase
) {
mfem::Vector make_positive_enthalpy_true(const mean_field::fem::FEM &f,
const double phase) {
mfem::Vector enthalpy(f.enthalpyFes->GetTrueVSize());
for (int index = 0; index < enthalpy.Size(); ++index) {
const double position = static_cast<double>(index + 1);
enthalpy(index) =
0.93 + 0.09 * std::sin(0.23 * position + phase) + 0.04 * std::cos(0.17 * position - 0.5 * phase);
enthalpy(index) = 0.93 + 0.09 * std::sin(0.23 * position + phase) +
0.04 * std::cos(0.17 * position - 0.5 * phase);
}
return enthalpy;
}
[[nodiscard]]
mfem::Vector make_enthalpy_direction_true(
const mean_field::fem::FEM &f,
const double phase
) {
mfem::Vector make_enthalpy_direction_true(const mean_field::fem::FEM &f,
const double phase) {
mfem::Vector direction(f.enthalpyFes->GetTrueVSize());
for (int index = 0; index < direction.Size(); ++index) {
const double position = static_cast<double>(index + 1);
direction(index) =
0.27 * std::sin(0.19 * position + phase) + 0.14 * std::cos(0.13 * position - 0.5 * phase);
direction(index) = 0.27 * std::sin(0.19 * position + phase) +
0.14 * std::cos(0.13 * position - 0.5 * phase);
}
return direction;
}
[[nodiscard]]
mfem::Vector make_displacement_direction_true(
const mean_field::fem::FEM &f,
const double phase
) {
MFEM_VERIFY(
f.mesh->Dimension() == 3, "The prepared pressure-force test requires a "
"three-dimensional mesh."
);
mfem::Vector make_displacement_direction_true(const mean_field::fem::FEM &f,
const double phase) {
MFEM_VERIFY(f.mesh->Dimension() == 3,
"The prepared pressure-force test requires a "
"three-dimensional mesh.");
mfem::ParGridFunction directionField(f.displacementFes.get());
@@ -107,13 +90,15 @@ namespace prepared_pressure_force_test_utils {
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);
@@ -125,93 +110,95 @@ namespace prepared_pressure_force_test_utils {
}
[[nodiscard]]
double relative_difference(
const mfem::Vector &left,
const mfem::Vector &right,
const MPI_Comm communicator
) {
MFEM_VERIFY(
left.Size() == right.Size(), "Cannot compare prepared pressure-force vectors with "
"different sizes."
);
double relative_difference(const mfem::Vector &left, const mfem::Vector &right,
const MPI_Comm communicator) {
MFEM_VERIFY(left.Size() == right.Size(),
"Cannot compare prepared pressure-force vectors with "
"different sizes.");
mfem::Vector difference(left);
difference -= right;
const double scale = std::max(
{gravity_prepared_test_utils::global_norm(left, communicator),
const double scale =
std::max({gravity_prepared_test_utils::global_norm(left, 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]]
mean_field::operators::context::pressure_force::PressureForceDependencies make_dependencies() {
return {
.discretization = {.identity = 1201, .revision = 3},
mean_field::operators::context::pressure_force::PressureForceDependencies
make_dependencies() {
return {.discretization = {.identity = 1201, .revision = 3},
.enthalpy = {.identity = 1213, .revision = 5},
.displacement = {.identity = 1217, .revision = 7}
};
.displacement = {.identity = 1217, .revision = 7}};
}
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>(
mean_field::utils::blocks::displacement_field.geometry_term
);
constexpr auto displacementValue =
mean_field::utils::blocks::get_value_block<CoupledForm>(
mean_field::utils::blocks::displacement_field.geometry_term);
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 =
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>(
mean_field::utils::blocks::enthalpy_field.specific_term
);
constexpr auto enthalpyValue =
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>(
mean_field::utils::blocks::barotropic_constant_field.mass_normalization_term
);
constexpr auto barotropicConstantValue =
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>(
mean_field::utils::blocks::gravity_field.gradient_term
);
constexpr auto gravityGradientResidual =
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>(
mean_field::utils::blocks::gravity_field.poisson_term
);
constexpr auto gravityPotentialResidual =
mean_field::utils::blocks::get_residual_block<CoupledForm>(
mean_field::utils::blocks::gravity_field.poisson_term);
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>(
mean_field::utils::blocks::displacement_field.geometry_term
);
constexpr auto displacementResidual =
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>(
mean_field::utils::blocks::enthalpy_field.specific_term
);
constexpr auto enthalpyResidual =
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>(
mean_field::utils::blocks::barotropic_constant_field.mass_normalization_term
);
constexpr auto massResidual =
mean_field::utils::blocks::get_residual_block<CoupledForm>(
mean_field::utils::blocks::barotropic_constant_field
.mass_normalization_term);
[[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{
maps.density.reduced_size(), maps.displacement.reduced_size(), maps.gravityFlux.reduced_size(),
maps.gravityPotential.reduced_size(), maps.enthalpy.reduced_size(), 1
};
maps.density.reduced_size(), maps.displacement.reduced_size(),
maps.gravityFlux.reduced_size(), maps.gravityPotential.reduced_size(),
maps.enthalpy.reduced_size(), 1};
const std::array<int, CoupledForm::residual_block_count> residualSizes{
maps.gravityFlux.reduced_size(), maps.gravityPotential.reduced_size(), maps.density.reduced_size(),
maps.displacement.reduced_size(), maps.enthalpy.reduced_size(), 1
};
maps.gravityFlux.reduced_size(), maps.gravityPotential.reduced_size(),
maps.density.reduced_size(), maps.displacement.reduced_size(),
maps.enthalpy.reduced_size(), 1};
return {valueSizes, residualSizes};
}
@@ -221,8 +208,7 @@ namespace prepared_pressure_force_test_utils {
mfem::Vector copy_residual_block(
const mfem::Vector &action,
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));
const int offset = layout.offset(block);
@@ -236,9 +222,9 @@ namespace prepared_pressure_force_test_utils {
} // namespace prepared_pressure_force_test_utils
TEST_CASE(
"Prepared Pressure Force Uses FieldDof Supported Dimensions And Owns Its Context",
tags::barotrope &tags::pressure &tags::prepared &tags::field &tags::unit
) {
"Prepared Pressure Force Uses FieldDof Supported Dimensions And Owns Its "
"Context",
tags::barotrope &tags::pressure &tags::prepared &tags::field &tags::unit) {
mean_field::utils::Args args = test_utils::setup_args();
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);
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());
@@ -257,17 +244,17 @@ TEST_CASE(
CHECK(preparedOperator.GetEnthalpySize() == maps.enthalpy.reduced_size());
CHECK(preparedOperator.GetDisplacementSize() == maps.displacement.reduced_size());
CHECK(preparedOperator.GetDisplacementSize() ==
maps.displacement.reduced_size());
CHECK(
&preparedOperator.GetContext().GetPreparationStatistics() == &preparedOperator.GetContextPreparationStatistics()
);
CHECK(&preparedOperator.GetContext().GetPreparationStatistics() ==
&preparedOperator.GetContextPreparationStatistics());
}
TEST_CASE(
"Prepared Pressure Force Jacobian Matches Full Stateless Columns Through FieldDof Restriction",
tags::barotrope &tags::pressure &tags::prepared &tags::field &tags::integration &tags::accuracy
) {
TEST_CASE("Prepared Pressure Force Jacobian Matches Full Stateless Columns "
"Through FieldDof Restriction",
tags::barotrope &tags::pressure &tags::prepared &tags::field
&tags::integration &tags::accuracy) {
mean_field::utils::Args args = test_utils::setup_args();
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 mfem::Vector enthalpy =
maps.enthalpy.gather(prepared_pressure_force_test_utils::make_positive_enthalpy_true(f, 0.47));
const mfem::Vector enthalpy = maps.enthalpy.gather(
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 =
maps.enthalpy.gather(prepared_pressure_force_test_utils::make_enthalpy_direction_true(f, 0.73));
const mfem::Vector enthalpyDirection = maps.enthalpy.gather(
prepared_pressure_force_test_utils::make_enthalpy_direction_true(f,
0.73));
const mfem::Vector displacementDirection =
maps.displacement.gather(prepared_pressure_force_test_utils::make_displacement_direction_true(f, 0.83));
const mfem::Vector displacementDirection = maps.displacement.gather(
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(
{.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 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 kernelEnthalpyActionTrue;
preparedOperator.ApplyEnthalpyJacobianAction(enthalpyDirection, preparedEnthalpyAction);
preparedOperator.ApplyEnthalpyJacobianAction(enthalpyDirection,
preparedEnthalpyAction);
mean_field::operators::kernels::apply_pressure_force_enthalpy_action(
f, *f.domainMapperStateless, equationOfState, enthalpyTrue, enthalpyDirectionTrue, displacementTrue,
kernelEnthalpyActionTrue
);
f, *f.domainMapperStateless, equationOfState, enthalpyTrue,
enthalpyDirectionTrue, displacementTrue, kernelEnthalpyActionTrue);
const mfem::Vector kernelEnthalpyAction = maps.displacement.gather(kernelEnthalpyActionTrue);
const mfem::Vector kernelEnthalpyAction =
maps.displacement.gather(kernelEnthalpyActionTrue);
CHECK(
prepared_pressure_force_test_utils::relative_difference(
preparedEnthalpyAction, kernelEnthalpyAction, f.mesh->GetComm()
) < 2.0e-12
);
CHECK(prepared_pressure_force_test_utils::relative_difference(
preparedEnthalpyAction, kernelEnthalpyAction, f.mesh->GetComm()) <
2.0e-12);
mfem::Vector preparedDisplacementAction;
mfem::Vector kernelDisplacementActionTrue;
preparedOperator.ApplyDisplacementJacobianAction(displacementDirection, preparedDisplacementAction);
preparedOperator.ApplyDisplacementJacobianAction(displacementDirection,
preparedDisplacementAction);
mean_field::operators::kernels::apply_pressure_force_displacement_action(
f, *f.domainMapperStateless, equationOfState, enthalpyTrue, displacementDirectionTrue, displacementTrue,
kernelDisplacementActionTrue
);
f, *f.domainMapperStateless, equationOfState, enthalpyTrue,
displacementDirectionTrue, displacementTrue,
kernelDisplacementActionTrue);
const mfem::Vector kernelDisplacementAction = maps.displacement.gather(kernelDisplacementActionTrue);
const mfem::Vector kernelDisplacementAction =
maps.displacement.gather(kernelDisplacementActionTrue);
CHECK(
prepared_pressure_force_test_utils::relative_difference(
preparedDisplacementAction, kernelDisplacementAction, f.mesh->GetComm()
) < 2.0e-12
);
CHECK(prepared_pressure_force_test_utils::relative_difference(
preparedDisplacementAction, kernelDisplacementAction,
f.mesh->GetComm()) < 2.0e-12);
mfem::Vector fusedAction;
preparedOperator.ApplyCompleteJacobianAction(enthalpyDirection, displacementDirection, fusedAction);
preparedOperator.ApplyCompleteJacobianAction(
enthalpyDirection, displacementDirection, fusedAction);
mfem::Vector expectedFusedAction(kernelEnthalpyAction);
expectedFusedAction += kernelDisplacementAction;
CHECK(
prepared_pressure_force_test_utils::relative_difference(fusedAction, expectedFusedAction, f.mesh->GetComm()) <
2.0e-12
);
CHECK(prepared_pressure_force_test_utils::relative_difference(
fusedAction, expectedFusedAction, f.mesh->GetComm()) < 2.0e-12);
}
TEST_CASE(
"Prepared Pressure Force MFEM Adapter Routes Reduced Coupled FieldDof Blocks",
tags::barotrope &tags::pressure &tags::prepared &tags::field &tags::integration &tags::jacobian
&tags::mfem_operators &tags::unit
) {
"Prepared Pressure Force MFEM Adapter Routes Reduced Coupled FieldDof "
"Blocks",
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::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 mfem::Vector enthalpy =
maps.enthalpy.gather(prepared_pressure_force_test_utils::make_positive_enthalpy_true(f, 0.53));
const mfem::Vector enthalpy = maps.enthalpy.gather(
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 =
maps.enthalpy.gather(prepared_pressure_force_test_utils::make_enthalpy_direction_true(f, 0.89));
const mfem::Vector enthalpyDirection = maps.enthalpy.gather(
prepared_pressure_force_test_utils::make_enthalpy_direction_true(f,
0.89));
const mfem::Vector displacementDirection =
maps.displacement.gather(prepared_pressure_force_test_utils::make_displacement_direction_true(f, 0.97));
const mfem::Vector displacementDirection = maps.displacement.gather(
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(
{.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 =
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());
@@ -403,74 +401,66 @@ TEST_CASE(
*/
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;
preparedOperator.ApplyCompleteJacobianAction(enthalpyDirection, displacementDirection, expectedDisplacementAction);
preparedOperator.ApplyCompleteJacobianAction(
enthalpyDirection, displacementDirection, expectedDisplacementAction);
mfem::Vector action;
adapter.Mult(direction, action);
const mfem::Vector displacementResidualAction = prepared_pressure_force_test_utils::copy_residual_block(
action, layout, prepared_pressure_force_test_utils::displacementResidual
);
const mfem::Vector displacementResidualAction =
prepared_pressure_force_test_utils::copy_residual_block(
action, layout,
prepared_pressure_force_test_utils::displacementResidual);
CHECK(
prepared_pressure_force_test_utils::relative_difference(
displacementResidualAction, expectedDisplacementAction, f.mesh->GetComm()
) < 2.0e-14
);
CHECK(prepared_pressure_force_test_utils::relative_difference(
displacementResidualAction, expectedDisplacementAction,
f.mesh->GetComm()) < 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(
prepared_pressure_force_test_utils::copy_residual_block(
action, layout, prepared_pressure_force_test_utils::gravityGradientResidual
)
.Norml2() == 0.0
);
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::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(
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
);
CHECK(prepared_pressure_force_test_utils::copy_residual_block(
action, layout, prepared_pressure_force_test_utils::massResidual)
.Norml2() == 0.0);
}
TEST_CASE(
"Pressure Force Residual Converges To A Manufactured Analytic Force",
tags::barotrope &tags::pressure &tags::kernels &tags::integration &tags::convergence &tags::h_refinement
&tags::analytic_comparison &tags::accuracy
) {
TEST_CASE("Pressure Force Residual Converges To A Manufactured Analytic Force",
tags::barotrope &tags::pressure &tags::kernels &tags::integration
&tags::convergence &tags::h_refinement &tags::analytic_comparison
&tags::accuracy) {
constexpr int dimension = 3;
constexpr std::array<int, 2> refinementLevels{0, 1};
@@ -484,10 +474,12 @@ TEST_CASE(
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::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.mesh->Dimension() == dimension);
@@ -495,12 +487,15 @@ TEST_CASE(
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;
auto analyticEnthalpyFunction = [supportRadiusSquared](const mfem::Vector &position) {
const double normalizedRadiusSquared = (position * position) / supportRadiusSquared;
auto analyticEnthalpyFunction = [supportRadiusSquared](
const mfem::Vector &position) {
const double normalizedRadiusSquared =
(position * position) / supportRadiusSquared;
if (normalizedRadiusSquared >= 1.0) {
return 0.0;
@@ -508,14 +503,18 @@ TEST_CASE(
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 = 0.0;
const double normalizedRadiusSquared = (position * position) / supportRadiusSquared;
const double normalizedRadiusSquared =
(position * position) / supportRadiusSquared;
if (normalizedRadiusSquared >= 1.0) {
return;
@@ -524,20 +523,24 @@ TEST_CASE(
const double distanceToSupportBoundary = 1.0 - normalizedRadiusSquared;
const double enthalpy =
amplitude * std::exp(-bumpSharpness * normalizedRadiusSquared / distanceToSupportBoundary);
amplitude * std::exp(-bumpSharpness * normalizedRadiusSquared /
distanceToSupportBoundary);
const double pressureGradientScale =
-2.0 * bumpSharpness * std::pow(enthalpy, 4.0) /
(supportRadiusSquared * distanceToSupportBoundary * distanceToSupportBoundary);
(supportRadiusSquared * distanceToSupportBoundary *
distanceToSupportBoundary);
for (int component = 0; component < dimension; ++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());
@@ -555,8 +558,8 @@ TEST_CASE(
mfem::Vector discreteResidual;
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());
@@ -564,9 +567,10 @@ TEST_CASE(
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];
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) {
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 =
mfem::IntRules.Get(elementGeometry, referenceQuadratureOrder);
auto *analyticForceIntegrator = new mfem::VectorDomainLFIntegrator(analyticPressureForceCoefficient);
auto *analyticForceIntegrator =
new mfem::VectorDomainLFIntegrator(analyticPressureForceCoefficient);
analyticForceIntegrator->SetIntRule(&referenceQuadrature);
mfem::ParLinearForm analyticForceLoad(f.displacementFes.get());
analyticForceLoad.AddDomainIntegrator(analyticForceIntegrator, stellarMarker);
analyticForceLoad.AddDomainIntegrator(analyticForceIntegrator,
stellarMarker);
analyticForceLoad.Assemble();
std::unique_ptr<mfem::HypreParVector> analyticForceHypreVector(analyticForceLoad.ParallelAssemble());
std::unique_ptr<mfem::HypreParVector> analyticForceHypreVector(
analyticForceLoad.ParallelAssemble());
REQUIRE(analyticForceHypreVector != nullptr);
@@ -603,7 +613,8 @@ TEST_CASE(
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(analyticForceNorm > 0.0);
@@ -620,7 +631,8 @@ TEST_CASE(
rieszForm.Assemble();
rieszForm.Finalize();
std::unique_ptr<mfem::HypreParMatrix> rieszMatrix(rieszForm.ParallelAssemble());
std::unique_ptr<mfem::HypreParMatrix> rieszMatrix(
rieszForm.ParallelAssemble());
REQUIRE(rieszMatrix != nullptr);
REQUIRE(rieszMatrix->Height() == discreteResidual.Size());
@@ -637,30 +649,30 @@ TEST_CASE(
rieszSolver.SetRelTol(1.0e-13);
rieszSolver.SetAbsTol(1.0e-15);
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());
rieszRepresentative = 0.0;
rieszSolver.Mult(functional, rieszRepresentative);
MFEM_VERIFY(
rieszSolver.GetConverged(), "The pressure-force convergence-test Riesz solve "
"did not converge."
);
MFEM_VERIFY(rieszSolver.GetConverged(),
"The pressure-force convergence-test Riesz solve "
"did not converge.");
const double dualNormSquared =
gravity_prepared_test_utils::global_dot(functional, rieszRepresentative, communicator);
const double dualNormSquared = gravity_prepared_test_utils::global_dot(
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(
dualNormSquared >= -100.0 * std::numeric_limits<double>::epsilon(),
MFEM_VERIFY(dualNormSquared >=
-100.0 * std::numeric_limits<double>::epsilon(),
"The pressure-force Riesz operator produced a "
"negative dual norm."
);
"negative dual norm.");
return std::sqrt(std::max(dualNormSquared, 0.0));
};
@@ -688,9 +700,12 @@ TEST_CASE(
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]);

View File

@@ -14,82 +14,94 @@ namespace rotational_displacement_force_test_utils {
using CoupledForm = mean_field::utils::blocks::barotropic_equilibrium_form;
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>(
mean_field::utils::blocks::displacement_field.geometry_term
);
constexpr auto displacementValue =
mean_field::utils::blocks::get_value_block<CoupledForm>(
mean_field::utils::blocks::displacement_field.geometry_term);
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 =
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>(
mean_field::utils::blocks::enthalpy_field.specific_term
);
constexpr auto enthalpyValue =
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>(
mean_field::utils::blocks::barotropic_constant_field.mass_normalization_term
);
constexpr auto barotropicConstantValue =
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>(
mean_field::utils::blocks::gravity_field.gradient_term
);
constexpr auto gravityGradientResidual =
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>(
mean_field::utils::blocks::gravity_field.poisson_term
);
constexpr auto gravityPotentialResidual =
mean_field::utils::blocks::get_residual_block<CoupledForm>(
mean_field::utils::blocks::gravity_field.poisson_term);
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>(
mean_field::utils::blocks::displacement_field.geometry_term
);
constexpr auto displacementResidual =
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>(
mean_field::utils::blocks::enthalpy_field.specific_term
);
constexpr auto enthalpyResidual =
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>(
mean_field::utils::blocks::barotropic_constant_field.mass_normalization_term
);
constexpr auto massResidual =
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;
const auto densityMap = 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 densityMap =
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 =
mean_field::field::make_field_dof_map<mean_field::field::Gravity, DomainSchema>(*f.gravityFluxFes);
const auto gravityPotentialMap =
mean_field::field::make_field_dof_map<mean_field::field::Gravity, DomainSchema>(*f.gravityPotentialFes);
mean_field::field::make_field_dof_map<mean_field::field::Gravity,
DomainSchema>(*f.gravityFluxFes);
const auto gravityPotentialMap = mean_field::field::make_field_dof_map<
mean_field::field::Gravity, DomainSchema>(*f.gravityPotentialFes);
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{
densityMap.reduced_size(), displacementMap.reduced_size(), gravityFluxMap.reduced_size(),
gravityPotentialMap.reduced_size(), enthalpyMap.reduced_size(), 1
};
densityMap.reduced_size(), displacementMap.reduced_size(),
gravityFluxMap.reduced_size(), gravityPotentialMap.reduced_size(),
enthalpyMap.reduced_size(), 1};
const std::array<int, CoupledForm::residual_block_count> residualSizes{
gravityFluxMap.reduced_size(), gravityPotentialMap.reduced_size(), densityMap.reduced_size(),
displacementMap.reduced_size(), enthalpyMap.reduced_size(), 1
};
gravityFluxMap.reduced_size(), gravityPotentialMap.reduced_size(),
densityMap.reduced_size(), displacementMap.reduced_size(),
enthalpyMap.reduced_size(), 1};
return {valueSizes, residualSizes};
}
[[nodiscard]] mfem::Vector make_density(
const mean_field::fem::FEM &f,
const double phase
) {
[[nodiscard]] mfem::Vector make_density(const mean_field::fem::FEM &f,
const double phase) {
mfem::ParGridFunction densityField(f.densityFes.get());
mfem::FunctionCoefficient densityCoefficient([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) +
mfem::FunctionCoefficient densityCoefficient(
[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);
});
@@ -100,15 +112,14 @@ namespace rotational_displacement_force_test_utils {
return densityTrue;
}
[[nodiscard]] mfem::Vector make_density_direction(
const mean_field::fem::FEM &f,
const double phase
) {
[[nodiscard]] mfem::Vector make_density_direction(const mean_field::fem::FEM &f,
const double phase) {
mfem::ParGridFunction densityField(f.densityFes.get());
mfem::FunctionCoefficient densityCoefficient([phase](const mfem::Vector &position) {
return 0.17 * std::sin(0.9 * position(0) + phase) - 0.12 * std::cos(0.8 * position(1) - phase) +
0.07 * position(2);
mfem::FunctionCoefficient densityCoefficient(
[phase](const mfem::Vector &position) {
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);
@@ -118,16 +129,20 @@ namespace rotational_displacement_force_test_utils {
return densityTrue;
}
[[nodiscard]] mfem::Vector make_displacement_direction(const mean_field::fem::FEM &f) {
mfem::Vector direction = gravity_prepared_test_utils::make_displacement(f, 0.91);
[[nodiscard]] mfem::Vector
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;
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);
angularVelocity(0) = scale * 0.17;
angularVelocity(1) = scale * -0.09;
@@ -141,27 +156,28 @@ namespace rotational_displacement_force_test_utils {
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() {
return {
.discretization = {.identity = 211, .revision = 3},
return {.discretization = {.identity = 211, .revision = 3},
.density = {.identity = 223, .revision = 5},
.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());
densityField = 0.0;
const int vacuumAttribute = f.domainMapperStateless->GetVacuumElementAttribute();
const int vacuumAttribute = field_dof_test_utils::vacuum_material_attribute;
mfem::Array<int> densityDofs;
int localVacuumElements = 0;
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);
@@ -179,7 +195,8 @@ namespace rotational_displacement_force_test_utils {
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);
@@ -188,23 +205,20 @@ namespace rotational_displacement_force_test_utils {
return densityTrue;
}
[[nodiscard]] double global_norm(
const mfem::Vector &vector,
MPI_Comm communicator
) {
[[nodiscard]] double global_norm(const mfem::Vector &vector,
MPI_Comm communicator) {
const double localSquaredNorm = vector * vector;
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);
}
[[nodiscard]] double global_dot(
const mfem::Vector &left,
[[nodiscard]] double global_dot(const mfem::Vector &left,
const mfem::Vector &right,
MPI_Comm communicator
) {
MPI_Comm communicator) {
REQUIRE(left.Size() == right.Size());
const double localDot = left * right;
@@ -215,29 +229,25 @@ namespace rotational_displacement_force_test_utils {
return globalDot;
}
[[nodiscard]] double relative_difference(
const mfem::Vector &computed,
[[nodiscard]] double relative_difference(const mfem::Vector &computed,
const mfem::Vector &reference,
MPI_Comm communicator
) {
MPI_Comm communicator) {
REQUIRE(computed.Size() == reference.Size());
mfem::Vector difference(computed);
difference -= reference;
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(
const mean_field::fem::FEM &f,
const mean_field::physics::RigidRotation &rotation,
const mfem::Vector &baseDensity,
const mfem::Vector &densityDirection,
const mfem::Vector &baseDensity, const mfem::Vector &densityDirection,
const mfem::Vector &baseDisplacement,
const mfem::Vector &displacementDirection,
const double step
) {
const mfem::Vector &displacementDirection, const double step) {
mfem::Vector plusDensity(baseDensity);
plusDensity.Add(step, densityDirection);
@@ -254,12 +264,12 @@ namespace rotational_displacement_force_test_utils {
mfem::Vector minusResidual;
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(
f, *f.domainMapperStateless, rotation, minusDensity, minusDisplacement, minusResidual
);
f, *f.domainMapperStateless, rotation, minusDensity, minusDisplacement,
minusResidual);
plusResidual -= minusResidual;
plusResidual /= 2.0 * step;
@@ -270,8 +280,7 @@ namespace rotational_displacement_force_test_utils {
[[nodiscard]] mfem::Vector copy_residual_block(
const mfem::Vector &action,
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));
const int offset = layout.offset(block);
@@ -286,17 +295,18 @@ namespace rotational_displacement_force_test_utils {
TEST_CASE(
"Rotational Displacement Force Query Includes Density Test And Linear "
"Position",
tags::rotation_prepared_unit
) {
using DisplacementField = mean_field::field::Field<mean_field::field::Displacement>;
tags::rotation_prepared_unit) {
using DisplacementField =
mean_field::field::Field<mean_field::field::Displacement>;
constexpr int geometryWeightOrder = 4;
constexpr mean_field::quadrature::Query query =
DisplacementField::make_query<mean_field::field::Displacement::Form::CentrifugalForce>(
mean_field::quadrature::QuadratureRole::discretization, geometryWeightOrder, std::array<int, 1>{1},
mean_field::utils::DOMAINS::STELLAR, mean_field::quadrature::MappingKind::general
);
constexpr mean_field::quadrature::Query query = DisplacementField::make_query<
mean_field::field::Displacement::Form::CentrifugalForce>(
mean_field::quadrature::QuadratureRole::discretization,
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 */
constexpr int expectedBaseOrder = 2 + 3 + 1 + 4;
@@ -308,33 +318,33 @@ TEST_CASE(
STATIC_REQUIRE(*query.base_order == expectedBaseOrder);
}
TEST_CASE(
"Rotational Displacement Force Uses Negative Rotation-Potential "
TEST_CASE("Rotational Displacement Force Uses Negative Rotation-Potential "
"Gradient And Excludes Vacuum",
tags::rotation_kernel_accuracy
) {
tags::rotation_kernel_accuracy) {
mean_field::utils::Args args = test_utils::setup_args();
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
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());
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;
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());
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);
};
@@ -346,63 +356,73 @@ TEST_CASE(
gradientTestField.GetTrueDofs(gradientTestDirection);
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);
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;
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 zeroCenter(3);
zeroAngularVelocity = 0.0;
zeroCenter = 0.0;
const mean_field::physics::RigidRotation zeroRotation(zeroAngularVelocity, zeroCenter);
const mean_field::physics::RigidRotation zeroRotation(zeroAngularVelocity,
zeroCenter);
mfem::Vector zeroRotationResidual;
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(
"Prepared Rotational Displacement Force Reprepares Selectively",
tags::rotation_prepared
) {
TEST_CASE("Prepared Rotational Displacement Force Reprepares Selectively",
tags::rotation_prepared) {
mean_field::utils::Args args = test_utils::setup_args();
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
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();
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 =
preparedOperator.Prepare({.density = density, .displacement = displacement}, dependencies, rotation);
const auto initialReport = preparedOperator.Prepare(
{.density = density, .displacement = displacement}, dependencies,
rotation);
REQUIRE(initialReport.DidAnyWork());
REQUIRE(initialReport.updatedRotation);
@@ -415,18 +435,19 @@ TEST_CASE(
preparedOperator.BuildResidual(preparedResidual);
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(
rotational_displacement_force_test_utils::relative_difference(
preparedResidual, kernelResidualReduced, f.mesh->GetComm()
) < 2.0e-12
);
CHECK(rotational_displacement_force_test_utils::relative_difference(
preparedResidual, kernelResidualReduced, f.mesh->GetComm()) <
2.0e-12);
CHECK_FALSE(preparedOperator.Prepare({.density = density, .displacement = displacement}, dependencies, rotation)
CHECK_FALSE(preparedOperator
.Prepare({.density = density, .displacement = displacement},
dependencies, rotation)
.DidAnyWork());
densityTrue = rotational_displacement_force_test_utils::make_density(f, 0.79);
@@ -434,8 +455,9 @@ TEST_CASE(
++dependencies.density.revision;
const auto densityReport =
preparedOperator.Prepare({.density = density, .displacement = displacement}, dependencies, rotation);
const auto densityReport = preparedOperator.Prepare(
{.density = density, .displacement = displacement}, dependencies,
rotation);
CHECK(densityReport.preparedResidual);
CHECK_FALSE(densityReport.updatedRotation);
@@ -444,8 +466,9 @@ TEST_CASE(
++dependencies.rotation.revision;
const auto rotationReport =
preparedOperator.Prepare({.density = density, .displacement = displacement}, dependencies, rotation);
const auto rotationReport = preparedOperator.Prepare(
{.density = density, .displacement = displacement}, dependencies,
rotation);
CHECK(rotationReport.updatedRotation);
CHECK(rotationReport.preparedResidual);
@@ -453,40 +476,45 @@ TEST_CASE(
CHECK(preparedOperator.GetResidualApplicationCount() == 1);
}
TEST_CASE(
"Rotational Displacement Force Jacobian Matches Both Columns And "
TEST_CASE("Rotational Displacement Force Jacobian Matches Both Columns And "
"Centered Differences",
tags::rotation_prepared_jacobian_accuracy
) {
tags::rotation_prepared_jacobian_accuracy) {
mean_field::utils::Args args = test_utils::setup_args();
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
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 =
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 mfem::Vector density = context.GetDensityMap().gather(densityTrue);
const mfem::Vector densityDirection = context.GetDensityMap().gather(densityDirectionTrue);
const mfem::Vector displacement = context.GetDisplacementMap().gather(displacementTrue);
const mfem::Vector displacementDirection = context.GetDisplacementMap().gather(displacementDirectionTrue);
const mfem::Vector densityDirection =
context.GetDensityMap().gather(densityDirectionTrue);
const mfem::Vector displacement =
context.GetDisplacementMap().gather(displacementTrue);
const mfem::Vector displacementDirection =
context.GetDisplacementMap().gather(displacementDirectionTrue);
preparedOperator.Prepare(
{.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 displacementAction;
@@ -494,18 +522,17 @@ TEST_CASE(
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);
summedColumns += displacementAction;
CHECK(
rotational_displacement_force_test_utils::relative_difference(
completeAction, summedColumns, f.mesh->GetComm()
) < 2.0e-12
);
CHECK(rotational_displacement_force_test_utils::relative_difference(
completeAction, summedColumns, f.mesh->GetComm()) < 2.0e-12);
mfem::Vector zeroDensityTrue(densityDirectionTrue.Size());
mfem::Vector zeroDisplacementTrue(displacementDirectionTrue.Size());
@@ -514,33 +541,39 @@ TEST_CASE(
constexpr double step = 1.0e-5;
const mfem::Vector densityDifferenceTrue = rotational_displacement_force_test_utils::centered_difference(
f, rotation, densityTrue, densityDirectionTrue, displacementTrue, zeroDisplacementTrue, step
);
const mfem::Vector densityDifferenceTrue =
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(
f, rotation, densityTrue, zeroDensityTrue, displacementTrue, displacementDirectionTrue, step
);
const mfem::Vector displacementDifferenceTrue =
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(
f, rotation, densityTrue, densityDirectionTrue, displacementTrue, displacementDirectionTrue, step
);
const mfem::Vector completeDifferenceTrue =
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 displacementDifference = context.GetDisplacementMap().gather(displacementDifferenceTrue);
const mfem::Vector completeDifference = context.GetDisplacementMap().gather(completeDifferenceTrue);
const mfem::Vector densityDifference =
context.GetDisplacementMap().gather(densityDifferenceTrue);
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(
densityAction, densityDifference, f.mesh->GetComm()
);
const double densityError =
rotational_displacement_force_test_utils::relative_difference(
densityAction, densityDifference, f.mesh->GetComm());
const double displacementError = rotational_displacement_force_test_utils::relative_difference(
displacementAction, displacementDifference, f.mesh->GetComm()
);
const double displacementError =
rotational_displacement_force_test_utils::relative_difference(
displacementAction, displacementDifference, f.mesh->GetComm());
const double completeError = rotational_displacement_force_test_utils::relative_difference(
completeAction, completeDifference, f.mesh->GetComm()
);
const double completeError =
rotational_displacement_force_test_utils::relative_difference(
completeAction, completeDifference, f.mesh->GetComm());
INFO("Density-column centered-difference error = " << densityError);
INFO("Displacement-column centered-difference error = " << displacementError);
@@ -551,95 +584,109 @@ TEST_CASE(
CHECK(completeError < 4.0e-8);
}
TEST_CASE(
"Prepared Rotational Displacement Force MFEM Adapter Routes Only R-d",
tags::rotation_prepared_unit
) {
TEST_CASE("Prepared Rotational Displacement Force MFEM Adapter Routes Only R-d",
tags::rotation_prepared_unit) {
mean_field::utils::Args args = test_utils::setup_args();
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
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 =
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 mfem::Vector density = context.GetDensityMap().gather(densityTrue);
const mfem::Vector densityDirection = context.GetDensityMap().gather(densityDirectionTrue);
const mfem::Vector displacement = context.GetDisplacementMap().gather(displacementTrue);
const mfem::Vector displacementDirection = context.GetDisplacementMap().gather(displacementDirectionTrue);
const mfem::Vector densityDirection =
context.GetDensityMap().gather(densityDirectionTrue);
const mfem::Vector displacement =
context.GetDisplacementMap().gather(displacementTrue);
const mfem::Vector displacementDirection =
context.GetDisplacementMap().gather(displacementDirectionTrue);
preparedOperator.Prepare(
{.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);
mean_field::operators::PreparedRotationalDisplacementForceJacobianOperator adapter(layout, preparedOperator);
mean_field::operators::PreparedRotationalDisplacementForceJacobianOperator
adapter(layout, preparedOperator);
mfem::BlockVector direction(layout.value_offsets());
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;
adapter.Mult(direction, action);
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(
action, layout, rotational_displacement_force_test_utils::displacementResidual
);
const mfem::Vector actualDisplacementAction =
rotational_displacement_force_test_utils::copy_residual_block(
action, layout,
rotational_displacement_force_test_utils::displacementResidual);
CHECK(
rotational_displacement_force_test_utils::relative_difference(
actualDisplacementAction, expectedDisplacementAction, f.mesh->GetComm()
) < 2.0e-12
);
CHECK(rotational_displacement_force_test_utils::relative_difference(
actualDisplacementAction, expectedDisplacementAction,
f.mesh->GetComm()) < 2.0e-12);
const std::array<mfem::Vector, 5> zeroRows{
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(
action, layout, rotational_displacement_force_test_utils::gravityPotentialResidual
),
action, layout,
rotational_displacement_force_test_utils::gravityPotentialResidual),
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(
action, layout, rotational_displacement_force_test_utils::enthalpyResidual
),
action, layout,
rotational_displacement_force_test_utils::enthalpyResidual),
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) {
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 test_helpers;
TEST_CASE(
"Polytropic Barotrope Satisfies Its Analytic Identities",
tags::hydro &tags::unit &tags::barotrope
) {
TEST_CASE("Polytropic EOS Satisfies Its Analytic Identities",
tags::barotrope_eos_unit) {
constexpr double polytropic_index = 3.0;
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};
@@ -24,30 +23,40 @@ TEST_CASE(
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(
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(
"Polytropic Barotrope Derivatives Match Centered Differences",
tags::hydro &tags::jacobian &tags::unit &tags::barotrope
) {
const mean_field::physics::PolytropicBarotrope barotrope(3.0, 1.5);
TEST_CASE("Polytropic EOS Derivatives Match Centered Differences",
tags::barotrope_eos_jacobian) {
const mean_field::eos::Polytrope barotrope(3.0, 1.5);
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 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);
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);
CHECK_THAT(
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(
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(
"Polytropic Barotrope Has An Exact Zero Density Surface",
tags::hydro &tags::unit &tags::barotrope
) {
const mean_field::physics::PolytropicBarotrope barotrope(3.0, 1.5);
TEST_CASE("Polytropic EOS Has An Exact Zero Density Surface",
tags::barotrope_eos_unit) {
const mean_field::eos::Polytrope barotrope(3.0, 1.5);
CHECK(barotrope.density_from_enthalpy(-1.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);
}
TEST_CASE(
"Polytropic Barotrope Rejects Invalid Material Parameters",
tags::hydro &tags::unit
) {
CHECK_THROWS_AS(mean_field::physics::PolytropicBarotrope(0.5, 1.0), std::invalid_argument);
TEST_CASE("Polytropic EOS Rejects Invalid Material Parameters",
tags::barotrope_eos_unit) {
CHECK_THROWS_AS(mean_field::eos::Polytrope(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(
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.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 test_helpers;
namespace polytropic_barotrope_test_utils {
namespace polytropic_eos_test_utils {
template <typename Function>
double centered_derivative(
Function &&function,
const double position,
const double step
) {
double centered_derivative(Function &&function, const double position,
const double step) {
return (function(position + step) - function(position - step)) / (2.0 * step);
}
template <typename Integrand>
double integrate_cube(
const mfem::IntegrationRule &integrationRule,
Integrand &&integrand
) {
double integrate_cube(const mfem::IntegrationRule &integrationRule,
Integrand &&integrand) {
double integral = 0.0;
for (int pointIndex = 0; pointIndex < integrationRule.GetNPoints(); ++pointIndex) {
const mfem::IntegrationPoint &integrationPoint = integrationRule.IntPoint(pointIndex);
for (int pointIndex = 0; pointIndex < integrationRule.GetNPoints();
++pointIndex) {
const mfem::IntegrationPoint &integrationPoint =
integrationRule.IntPoint(pointIndex);
integral += integrationPoint.weight * integrand(integrationPoint);
}
return integral;
}
} // namespace polytropic_barotrope_test_utils
} // namespace polytropic_eos_test_utils
TEST_CASE(
"Polytropic Barotrope Satisfies Its Thermodynamic Identities",
tags::barotrope &tags::physics &tags::unit
) {
TEST_CASE("Polytropic EOS Satisfies Its Thermodynamic Identities",
tags::barotrope_eos_unit) {
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};
@@ -52,9 +47,11 @@ TEST_CASE(
for (const double polytropicIndex : polytropicIndices) {
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);
@@ -65,35 +62,45 @@ TEST_CASE(
for (const double density : densities) {
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:
*
* P = rho h / (n + 1).
*/
CHECK_THAT(
pressureFromEnthalpy,
Catch::Matchers::WithinRel(density * enthalpyFromDensity / (polytropicIndex + 1.0), 5.0e-13)
);
CHECK_THAT(pressureFromEnthalpy,
Catch::Matchers::WithinRel(density * enthalpyFromDensity /
(polytropicIndex + 1.0),
5.0e-13));
/*
* Polytropic identity:
@@ -105,8 +112,7 @@ TEST_CASE(
*/
CHECK(
barotrope.pressure_derivative_from_enthalpy(enthalpyFromDensity) ==
barotrope.density_from_enthalpy(enthalpyFromDensity)
);
barotrope.density_from_enthalpy(enthalpyFromDensity));
/*
* Since
@@ -117,19 +123,16 @@ TEST_CASE(
*
* dP / d rho = h / n.
*/
CHECK_THAT(
barotrope.pressure_derivative_from_density(density),
Catch::Matchers::WithinRel(enthalpyFromDensity / polytropicIndex, 5.0e-13)
);
CHECK_THAT(barotrope.pressure_derivative_from_density(density),
Catch::Matchers::WithinRel(
enthalpyFromDensity / polytropicIndex, 5.0e-13));
}
}
}
}
TEST_CASE(
"Polytropic Barotrope Pressure Derivatives Match Centered Differences",
tags::barotrope &tags::physics &tags::unit &tags::jacobian &tags::pressure
) {
TEST_CASE("Polytropic EOS Pressure Derivatives Match Centered Differences",
tags::barotrope_eos_jacobian) {
constexpr std::array<double, 3> polytropicIndices{1.0, 1.5, 3.0};
constexpr std::array<double, 3> positiveValues{0.2, 0.73, 1.8};
@@ -137,50 +140,55 @@ TEST_CASE(
constexpr double polytropicConstant = 0.61;
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) {
for (const double enthalpy : positiveValues) {
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) {
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) {
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) {
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(
"Polytropic Barotrope Density Derivative Matches Centered Differences",
tags::barotrope &tags::physics &tags::unit &tags::jacobian &tags::pressure
) {
TEST_CASE("Polytropic EOS Density Derivative Matches Centered Differences",
tags::barotrope_eos_jacobian) {
constexpr std::array<double, 3> polytropicIndices{1.0, 1.5, 3.0};
constexpr std::array<double, 3> enthalpies{0.2, 0.73, 1.8};
@@ -188,40 +196,43 @@ TEST_CASE(
constexpr double polytropicConstant = 0.61;
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) {
for (const double enthalpy : enthalpies) {
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) {
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(
"Polytropic Barotrope Defines Consistent Surface And Exterior Behavior",
tags::barotrope &tags::physics &tags::unit &tags::pressure
) {
TEST_CASE("Polytropic EOS Defines Consistent Surface And Exterior Behavior",
tags::barotrope_eos_unit) {
constexpr std::array<double, 3> polytropicIndices{1.0, 1.5, 3.0};
constexpr double polytropicConstant = 0.47;
constexpr double exteriorEnthalpy = -0.3;
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) {
/*
@@ -246,9 +257,11 @@ TEST_CASE(
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
@@ -257,55 +270,58 @@ TEST_CASE(
const double expectedSurfaceDensityDerivative =
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(
"Polytropic Barotrope Rejects Invalid Physical Inputs",
tags::barotrope &tags::physics &tags::unit &tags::pressure
) {
CHECK_THROWS_AS(mean_field::physics::PolytropicBarotrope(0.999, 1.0), std::invalid_argument);
TEST_CASE("Polytropic EOS Rejects Invalid Physical Inputs",
tags::barotrope_eos_unit) {
CHECK_THROWS_AS(mean_field::eos::Polytrope(0.999, 1.0),
std::invalid_argument);
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.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{
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>::infinity(),
std::numeric_limits<double>::quiet_NaN()};
for (const double nonfiniteValue : nonfiniteValues) {
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(
"Pressure Force And Pressure Integral Have Distinct Registered Forms",
tags::barotrope &tags::pressure &tags::pressure_gradient &tags::quadrature &tags::unit
) {
TEST_CASE("Pressure Force And Pressure Integral Have Distinct Registered Forms",
tags::barotrope_pressure_quadrature_unit) {
using EnthalpyField = mean_field::field::Field<mean_field::field::Enthalpy>;
/*
@@ -320,34 +336,38 @@ TEST_CASE(
*
* 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 geometryWeightOrder = 2;
constexpr mean_field::quadrature::Query pressureIntegralQuery =
EnthalpyField::make_query<mean_field::field::Enthalpy::Form::PressureIntegral>(
mean_field::quadrature::QuadratureRole::diagnostic, geometryWeightOrder,
std::array<int, 1>{pressureExtraOrder}, mean_field::utils::DOMAINS::STELLAR,
mean_field::quadrature::MappingKind::general
);
EnthalpyField::make_query<
mean_field::field::Enthalpy::Form::PressureIntegral>(
mean_field::quadrature::QuadratureRole::diagnostic,
geometryWeightOrder, std::array<int, 1>{pressureExtraOrder},
mean_field::utils::DOMAINS::STELLAR,
mean_field::quadrature::MappingKind::general);
constexpr mean_field::quadrature::Query pressureForceQuery =
EnthalpyField::make_query<mean_field::field::Enthalpy::Form::PressureForce>(
mean_field::quadrature::QuadratureRole::discretization, 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);
EnthalpyField::make_query<
mean_field::field::Enthalpy::Form::PressureForce>(
mean_field::quadrature::QuadratureRole::discretization,
geometryWeightOrder, std::array<int, 1>{pressureExtraOrder},
mean_field::utils::DOMAINS::STELLAR,
mean_field::quadrature::MappingKind::general);
STATIC_CHECK(
mean_field::field::Enthalpy::Form::PressureIntegral::policyKey !=
mean_field::field::Enthalpy::Form::PressureForce::policyKey
);
mean_field::field::Enthalpy::Form::PressureIntegral::dynamicOrderCount ==
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());
@@ -374,13 +394,17 @@ TEST_CASE(
*/
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);
@@ -391,16 +415,19 @@ TEST_CASE(
* controls.
*/
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_force.boost = 5;
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);
@@ -415,13 +442,12 @@ TEST_CASE(
CHECK(pressureForceResolution.order == 21);
}
TEST_CASE(
"Pressure Quadrature Exactly Integrates An N Three Polynomial",
tags::barotrope &tags::pressure &tags::pressure_gradient &tags::quadrature &tags::accuracy
) {
TEST_CASE("Pressure Quadrature Exactly Integrates An N Three Polynomial",
tags::barotrope_pressure_quadrature_accuracy) {
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;
@@ -432,23 +458,27 @@ TEST_CASE(
* rho(h) = h^3,
* 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 =
EnthalpyField::make_query<mean_field::field::Enthalpy::Form::PressureIntegral>(
mean_field::quadrature::QuadratureRole::diagnostic, 0, std::array<int, 1>{pressureExtraOrder},
mean_field::utils::DOMAINS::STELLAR, mean_field::quadrature::MappingKind::affine
);
EnthalpyField::make_query<
mean_field::field::Enthalpy::Form::PressureIntegral>(
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 =
EnthalpyField::make_query<mean_field::field::Enthalpy::Form::PressureForce>(
mean_field::quadrature::QuadratureRole::discretization, 0, std::array<int, 1>{pressureExtraOrder},
mean_field::utils::DOMAINS::STELLAR, mean_field::quadrature::MappingKind::affine
);
EnthalpyField::make_query<
mean_field::field::Enthalpy::Form::PressureForce>(
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{
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 =
ruleFactory.get(pressureIntegralQuery, mfem::Geometry::CUBE);
@@ -466,15 +496,17 @@ TEST_CASE(
*
* P = x^12 y^12 z^12 / 4.
*/
const double numericalPressureIntegral = polytropic_barotrope_test_utils::integrate_cube(
*pressureIntegralRule.integration_rule, [&barotrope](const mfem::IntegrationPoint &integrationPoint) {
const double coordinateProduct = integrationPoint.x * integrationPoint.y * integrationPoint.z;
const double numericalPressureIntegral =
polytropic_eos_test_utils::integrate_cube(
*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);
return barotrope.pressure_from_enthalpy(enthalpy);
}
);
});
const double analyticPressureIntegral = 0.25 / std::pow(13.0, 3.0);
@@ -489,32 +521,38 @@ TEST_CASE(
* -P div(w)
* = -x^14 y^14 z^14 / 4.
*/
const double numericalPressureForceIntegral = polytropic_barotrope_test_utils::integrate_cube(
*pressureForceRule.integration_rule, [&barotrope](const mfem::IntegrationPoint &integrationPoint) {
const double coordinateProduct = integrationPoint.x * integrationPoint.y * integrationPoint.z;
const double numericalPressureForceIntegral =
polytropic_eos_test_utils::integrate_cube(
*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 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;
return -pressure * testDivergence;
}
);
});
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("Analytic pressure integral = " << analyticPressureIntegral);
INFO("Numerical pressure-force integral = " << numericalPressureForceIntegral);
INFO(
"Numerical pressure-force integral = " << numericalPressureForceIntegral);
INFO("Analytic pressure-force integral = " << analyticPressureForceIntegral);
@@ -526,7 +564,10 @@ TEST_CASE(
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