feat(field-support): added field support system, mid migration
currently the barotope and the pressure force operator are migrated to the new support system
This commit is contained in:
@@ -71,6 +71,17 @@ target_sources(mean_field
|
||||
libmeanfield/impl/operators/contexts/hydrostatic_equilibrium_context.cpp
|
||||
libmeanfield/impl/operators/prepared_hydrostatic_equilibrium.cpp
|
||||
libmeanfield/impl/operators/kernels/pressure_force_kernels.cpp
|
||||
libmeanfield/impl/operators/contexts/pressure_force_context.cpp
|
||||
libmeanfield/impl/operators/prepared_pressure_force.cpp
|
||||
libmeanfield/impl/operators/kernels/gravity_displacement_force_kernels.cpp
|
||||
libmeanfield/impl/operators/prepared_gravity_displacement_force.cpp
|
||||
libmeanfield/impl/operators/contexts/rotation_displacement_force_context.cpp
|
||||
libmeanfield/impl/operators/kernels/rotation_displacement_force_kernels.cpp
|
||||
libmeanfield/impl/operators/prepared_rotation_displacement_force.cpp
|
||||
libmeanfield/impl/operators/prepared_displacement_operator.cpp
|
||||
libmeanfield/impl/models/polytropic.cpp
|
||||
libmeanfield/impl/operators/prepared_mass_normalization.cpp
|
||||
libmeanfield/impl/operators/prepared_stellar_equilibrium.cpp
|
||||
)
|
||||
|
||||
target_sources(mean_field
|
||||
@@ -122,6 +133,24 @@ target_sources(mean_field
|
||||
libmeanfield/interface/operators/kernels/hydrostatic_equilibrium_kernels.cppm
|
||||
libmeanfield/interface/operators/contexts/hydrostatic_equilibrium_context.cppm
|
||||
libmeanfield/interface/operators/kernels/pressure_force_kernels.cppm
|
||||
libmeanfield/interface/operators/contexts/pressure_force_context.cppm
|
||||
libmeanfield/interface/operators/prepared_pressure_force.cppm
|
||||
libmeanfield/interface/operators/kernels/gravity_displacement_force_kernels.cppm
|
||||
libmeanfield/interface/operators/prepared_gravity_displacement_force.cppm
|
||||
libmeanfield/interface/operators/contexts/rotation_displacement_force_context.cppm
|
||||
libmeanfield/interface/operators/kernels/rotation_displacement_force_kernels.cppm
|
||||
libmeanfield/interface/operators/prepared_rotation_displacement_force.cppm
|
||||
libmeanfield/interface/operators/prepared_displacement_operator.cppm
|
||||
libmeanfield/interface/eos/eos_base.cppm
|
||||
libmeanfield/interface/eos/polytropic.cppm
|
||||
libmeanfield/interface/models/structure/structure_base.cppm
|
||||
libmeanfield/interface/models/structure/polytropic.cppm
|
||||
libmeanfield/interface/models/structure_profile.cppm
|
||||
libmeanfield/interface/surface/surface_base.cppm
|
||||
libmeanfield/interface/surface/isobaric.cppm
|
||||
libmeanfield/interface/models/stellar_model.cppm
|
||||
libmeanfield/interface/operators/prepared_mass_normalization.cppm
|
||||
libmeanfield/interface/operators/prepared_stellar_equilibrium.cppm
|
||||
)
|
||||
|
||||
|
||||
@@ -180,7 +209,24 @@ add_executable(tests
|
||||
tests/operators/prepared_hydrostatic_equilibrium_analytic_accuracy.cpp
|
||||
tests/physics/barotrope_pressure.cpp
|
||||
tests/operators/kernels/pressure_force_kernels.cpp
|
||||
|
||||
tests/operators/contexts/pressure_force_context.cpp
|
||||
tests/operators/prepared_pressure_force.cpp
|
||||
tests/operators/gravity_displacement_force.cpp
|
||||
tests/operators/gravity_displacement_force_analytic_comparisons.cpp
|
||||
tests/operators/contexts/rotation_displacement_force_context.cpp
|
||||
tests/operators/prepared_rotation_displacement_force.cpp
|
||||
tests/operators/prepared_rotation_displacement_force_analytic.cpp
|
||||
tests/operators/prepared_rotation_displacement_force_affine_deformation.cpp
|
||||
tests/operators/prepared_displacement_operator.cpp
|
||||
tests/surface/isobaric.cpp
|
||||
tests/models/stellar_model.cpp
|
||||
tests/operators/prepared_mass_normalization.cpp
|
||||
tests/operators/prepared_stellar_equilibrium.cpp
|
||||
tests/utils/domain.cpp
|
||||
tests/field/field_base.cpp
|
||||
tests/field/field_registry.cpp
|
||||
tests/field/field_mfem.cpp
|
||||
tests/field/field_dof_map.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(tests PRIVATE mean_field test_mod Catch2::Catch2 Boost::boost)
|
||||
|
||||
@@ -128,7 +128,7 @@ BreakFunctionDefinitionParameters: false
|
||||
BreakInheritanceList: BeforeColon
|
||||
BreakStringLiterals: true
|
||||
BreakTemplateDeclarations: MultiLine
|
||||
ColumnLimit: 80
|
||||
ColumnLimit: 120
|
||||
CommentPragmas: "^ IWYU pragma:"
|
||||
CompactNamespaces: false
|
||||
ConstructorInitializerIndentWidth: 4
|
||||
|
||||
@@ -13,23 +13,16 @@ namespace {
|
||||
const std::array<
|
||||
int,
|
||||
FormT::dynamicOrderCount> &dynamic_orders = {},
|
||||
const mean_field::utils::DOMAINS domain =
|
||||
mean_field::utils::DOMAINS::ALL
|
||||
const mean_field::utils::DOMAINS domain = mean_field::utils::DOMAINS::ALL
|
||||
) {
|
||||
using DensityField =
|
||||
mean_field::field::Field<mean_field::field::Density>;
|
||||
using DensityField = mean_field::field::Field<mean_field::field::Density>;
|
||||
|
||||
const mean_field::quadrature::Query query =
|
||||
DensityField::make_query<FormT>(
|
||||
mean_field::quadrature::QuadratureRole::diagnostic,
|
||||
transformation.OrderW(), dynamic_orders, domain,
|
||||
fem.has_mapping() ? mean_field::quadrature::MappingKind::general
|
||||
: mean_field::quadrature::MappingKind::none
|
||||
);
|
||||
const mean_field::quadrature::Query query = DensityField::make_query<FormT>(
|
||||
mean_field::quadrature::QuadratureRole::diagnostic, transformation.OrderW(), dynamic_orders, domain,
|
||||
fem.has_mapping() ? mean_field::quadrature::MappingKind::general : mean_field::quadrature::MappingKind::none
|
||||
);
|
||||
|
||||
return *fem.quadratureFactory
|
||||
->get(query, transformation.GetGeometryType())
|
||||
.integration_rule;
|
||||
return *fem.quadratureFactory->get(query, transformation.GetGeometryType()).integration_rule;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
@@ -45,15 +38,11 @@ namespace mean_field::analysis {
|
||||
double local_integral;
|
||||
mfem::Array<int> elem_markers;
|
||||
populate_element_mask(fem.mesh.get(), domain, elem_markers);
|
||||
const mfem::ElementTransformation &representative_transformation =
|
||||
*fem.mesh->GetElementTransformation(0);
|
||||
const mfem::ElementTransformation &representative_transformation = *fem.mesh->GetElementTransformation(0);
|
||||
const mfem::IntegrationRule &integration_rule =
|
||||
get_density_rule<field::Density::Form::MassConservation>(
|
||||
fem, representative_transformation, {}, domain
|
||||
);
|
||||
get_density_rule<field::Density::Form::MassConservation>(fem, representative_transformation, {}, domain);
|
||||
|
||||
if (fem.has_mapping() &&
|
||||
coord_space == mapping::COORDINATE_SPACE::PHYSICAL) {
|
||||
if (fem.has_mapping() && coord_space == mapping::COORDINATE_SPACE::PHYSICAL) {
|
||||
mapping::MappedScalarCoefficient mapped_gf_c(*fem.mapping, gf_c);
|
||||
|
||||
// ReSharper disable once CppDFAMemoryLeak // Disabled because MFEM
|
||||
@@ -80,10 +69,7 @@ namespace mean_field::analysis {
|
||||
}
|
||||
|
||||
double global_integral = 0.0;
|
||||
MPI_Allreduce(
|
||||
&local_integral, &global_integral, 1, MPI_DOUBLE, MPI_SUM,
|
||||
fem.mesh->GetComm()
|
||||
);
|
||||
MPI_Allreduce(&local_integral, &global_integral, 1, MPI_DOUBLE, MPI_SUM, fem.mesh->GetComm());
|
||||
return global_integral;
|
||||
}
|
||||
|
||||
@@ -99,12 +85,10 @@ namespace mean_field::analysis {
|
||||
for (int i = 0; i < fem.mesh->GetNE(); ++i) {
|
||||
if (fem.mesh->GetAttribute(i) == 3)
|
||||
continue;
|
||||
mfem::ElementTransformation *trans =
|
||||
fem.mesh->GetElementTransformation(i);
|
||||
const mfem::IntegrationRule &ir =
|
||||
get_density_rule<field::Density::Form::CenterOfMass>(
|
||||
fem, *trans, std::array<int, 1>{1}, utils::DOMAINS::STELLAR
|
||||
);
|
||||
mfem::ElementTransformation *trans = fem.mesh->GetElementTransformation(i);
|
||||
const mfem::IntegrationRule &ir = get_density_rule<field::Density::Form::CenterOfMass>(
|
||||
fem, *trans, std::array<int, 1>{1}, utils::DOMAINS::STELLAR
|
||||
);
|
||||
|
||||
for (int j = 0; j < ir.GetNPoints(); ++j) {
|
||||
const mfem::IntegrationPoint &ip = ir.IntPoint(j);
|
||||
@@ -138,10 +122,7 @@ namespace mean_field::analysis {
|
||||
|
||||
MPI_Allreduce(&local_mass, &global_mass, 1, MPI_DOUBLE, MPI_SUM, comm);
|
||||
|
||||
MPI_Allreduce(
|
||||
local_com.GetData(), global_com.GetData(), dim, MPI_DOUBLE, MPI_SUM,
|
||||
comm
|
||||
);
|
||||
MPI_Allreduce(local_com.GetData(), global_com.GetData(), dim, MPI_DOUBLE, MPI_SUM, comm);
|
||||
|
||||
if (global_mass > 1e-18) {
|
||||
global_com /= global_mass;
|
||||
@@ -157,9 +138,7 @@ namespace mean_field::analysis {
|
||||
mfem::GridFunction &rho,
|
||||
const double target_mass
|
||||
) {
|
||||
if (const double current_mass = domain_integrate_grid_function(
|
||||
fem, rho, utils::DOMAINS::STELLAR
|
||||
);
|
||||
if (const double current_mass = domain_integrate_grid_function(fem, rho, utils::DOMAINS::STELLAR);
|
||||
current_mass > 1e-15)
|
||||
rho *= (target_mass / current_mass);
|
||||
}
|
||||
@@ -168,16 +147,11 @@ namespace mean_field::analysis {
|
||||
const fem::FEM &fem,
|
||||
const mfem::GridFunction &rho
|
||||
) {
|
||||
auto s2_func = [](const mfem::Vector &x) {
|
||||
return std::pow(x(0), 2) + std::pow(x(1), 2);
|
||||
};
|
||||
auto s2_func = [](const mfem::Vector &x) { return std::pow(x(0), 2) + std::pow(x(1), 2); };
|
||||
|
||||
std::unique_ptr<mfem::Coefficient> s2_coeff;
|
||||
if (fem.has_mapping()) {
|
||||
s2_coeff =
|
||||
std::make_unique<mapping::PhysicalPositionFunctionCoefficient>(
|
||||
*fem.mapping, s2_func
|
||||
);
|
||||
s2_coeff = std::make_unique<mapping::PhysicalPositionFunctionCoefficient>(*fem.mapping, s2_func);
|
||||
} else {
|
||||
s2_coeff = std::make_unique<mfem::FunctionCoefficient>(s2_func);
|
||||
}
|
||||
@@ -186,23 +160,16 @@ namespace mean_field::analysis {
|
||||
mfem::ProductCoefficient I_integrand(rho_coeff, *s2_coeff);
|
||||
|
||||
mfem::LinearForm I_lf(fem.densityFes.get());
|
||||
const mfem::ElementTransformation &representative_transformation =
|
||||
*fem.mesh->GetElementTransformation(0);
|
||||
const mfem::IntegrationRule &integration_rule =
|
||||
get_density_rule<field::Density::Form::Quadrupole>(
|
||||
fem, representative_transformation, std::array<int, 1>{2},
|
||||
utils::DOMAINS::STELLAR
|
||||
);
|
||||
mfem::Array<int> stellar_markers;
|
||||
populate_element_mask(
|
||||
fem.mesh.get(), utils::DOMAINS::STELLAR, stellar_markers
|
||||
const mfem::ElementTransformation &representative_transformation = *fem.mesh->GetElementTransformation(0);
|
||||
const mfem::IntegrationRule &integration_rule = get_density_rule<field::Density::Form::Quadrupole>(
|
||||
fem, representative_transformation, std::array<int, 1>{2}, utils::DOMAINS::STELLAR
|
||||
);
|
||||
mfem::Array<int> stellar_markers;
|
||||
populate_element_mask(fem.mesh.get(), utils::DOMAINS::STELLAR, stellar_markers);
|
||||
|
||||
double local_I = 0.0;
|
||||
if (fem.has_mapping()) {
|
||||
mapping::MappedScalarCoefficient mapped_integrand(
|
||||
*fem.mapping, I_integrand
|
||||
);
|
||||
mapping::MappedScalarCoefficient mapped_integrand(*fem.mapping, I_integrand);
|
||||
auto *integrator = new mfem::DomainLFIntegrator(mapped_integrand);
|
||||
integrator->SetIntRule(&integration_rule);
|
||||
I_lf.AddDomainIntegrator(integrator, stellar_markers);
|
||||
@@ -217,9 +184,7 @@ namespace mean_field::analysis {
|
||||
}
|
||||
|
||||
double global_I = 0.0;
|
||||
MPI_Allreduce(
|
||||
&local_I, &global_I, 1, MPI_DOUBLE, MPI_SUM, fem.mesh->GetComm()
|
||||
);
|
||||
MPI_Allreduce(&local_I, &global_I, 1, MPI_DOUBLE, MPI_SUM, fem.mesh->GetComm());
|
||||
return global_I;
|
||||
}
|
||||
|
||||
@@ -229,13 +194,10 @@ namespace mean_field::analysis {
|
||||
const utils::DOMAINS domain
|
||||
) {
|
||||
mfem::ParMesh &mesh = *fem.mesh;
|
||||
const bool physical =
|
||||
(coordinate_space == mapping::COORDINATE_SPACE::PHYSICAL);
|
||||
const bool physical = (coordinate_space == mapping::COORDINATE_SPACE::PHYSICAL);
|
||||
|
||||
if (physical && !fem.has_mapping()) {
|
||||
MFEM_ABORT(
|
||||
"Physical volume requested but no domain mapping is available."
|
||||
);
|
||||
MFEM_ABORT("Physical volume requested but no domain mapping is available.");
|
||||
}
|
||||
|
||||
double local_volume = 0.0;
|
||||
@@ -258,9 +220,7 @@ namespace mean_field::analysis {
|
||||
}
|
||||
mfem::ElementTransformation *T = mesh.GetElementTransformation(e);
|
||||
const mfem::IntegrationRule &ir =
|
||||
get_density_rule<field::Density::Form::MassConservation>(
|
||||
fem, *T, {}, domain
|
||||
);
|
||||
get_density_rule<field::Density::Form::MassConservation>(fem, *T, {}, domain);
|
||||
|
||||
for (int q = 0; q < ir.GetNPoints(); ++q) {
|
||||
const mfem::IntegrationPoint &ip = ir.IntPoint(q);
|
||||
@@ -277,10 +237,7 @@ namespace mean_field::analysis {
|
||||
}
|
||||
|
||||
double global_volume = 0.0;
|
||||
MPI_Allreduce(
|
||||
&local_volume, &global_volume, 1, MPI_DOUBLE, MPI_SUM,
|
||||
mesh.GetComm()
|
||||
);
|
||||
MPI_Allreduce(&local_volume, &global_volume, 1, MPI_DOUBLE, MPI_SUM, mesh.GetComm());
|
||||
return global_volume;
|
||||
}
|
||||
} // namespace mean_field::analysis
|
||||
|
||||
@@ -47,13 +47,9 @@ namespace mean_field::fem {
|
||||
int mpiSize = 1;
|
||||
MPI_Comm_size(MPI_COMM_WORLD, &mpiSize);
|
||||
|
||||
const std::unique_ptr<int[]> meshPartitioning(
|
||||
fem.smesh.mesh->GeneratePartitioning(mpiSize, 1)
|
||||
);
|
||||
const std::unique_ptr<int[]> meshPartitioning(fem.smesh.mesh->GeneratePartitioning(mpiSize, 1));
|
||||
|
||||
fem.mesh = std::make_unique<mfem::ParMesh>(
|
||||
MPI_COMM_WORLD, *fem.smesh.mesh, meshPartitioning.get(), 1
|
||||
);
|
||||
fem.mesh = std::make_unique<mfem::ParMesh>(MPI_COMM_WORLD, *fem.smesh.mesh, meshPartitioning.get(), 1);
|
||||
|
||||
fem.mesh->EnsureNodes();
|
||||
|
||||
@@ -73,11 +69,9 @@ namespace mean_field::fem {
|
||||
throw std::runtime_error("Values for exterior coordinate not set.");
|
||||
}
|
||||
|
||||
const mfem::FiniteElementSpace &serialCoordinateSpace =
|
||||
*fem.smesh.exterior_coordinate->space;
|
||||
const mfem::FiniteElementSpace &serialCoordinateSpace = *fem.smesh.exterior_coordinate->space;
|
||||
|
||||
const mfem::GridFunction &serialCoordinate =
|
||||
*fem.smesh.exterior_coordinate->values;
|
||||
const mfem::GridFunction &serialCoordinate = *fem.smesh.exterior_coordinate->values;
|
||||
|
||||
if (serialCoordinate.FESpace() != &serialCoordinateSpace) {
|
||||
throw std::runtime_error(
|
||||
@@ -94,9 +88,7 @@ namespace mean_field::fem {
|
||||
}
|
||||
|
||||
if (serialCoordinateSpace.GetVDim() != 1) {
|
||||
throw std::runtime_error(
|
||||
"Exterior coordinate must be a scalar field."
|
||||
);
|
||||
throw std::runtime_error("Exterior coordinate must be a scalar field.");
|
||||
}
|
||||
|
||||
if (serialCoordinate.Size() != serialCoordinateSpace.GetVSize()) {
|
||||
@@ -106,50 +98,37 @@ namespace mean_field::fem {
|
||||
);
|
||||
}
|
||||
|
||||
const int compactificationOrder =
|
||||
serialCoordinateSpace.GetMaxElementOrder();
|
||||
const int compactificationOrder = serialCoordinateSpace.GetMaxElementOrder();
|
||||
|
||||
const int dimension = fem.mesh->Dimension();
|
||||
const int dimension = fem.mesh->Dimension();
|
||||
|
||||
fem.compactificationFec = std::make_unique<mfem::H1_FECollection>(
|
||||
compactificationOrder, dimension
|
||||
);
|
||||
fem.compactificationFec = std::make_unique<mfem::H1_FECollection>(compactificationOrder, dimension);
|
||||
|
||||
fem.compactificationFes = std::make_unique<mfem::ParFiniteElementSpace>(
|
||||
fem.mesh.get(), fem.compactificationFec.get()
|
||||
);
|
||||
fem.compactificationFes =
|
||||
std::make_unique<mfem::ParFiniteElementSpace>(fem.mesh.get(), fem.compactificationFec.get());
|
||||
|
||||
mfem::ParGridFunction distributedCoordinate(
|
||||
fem.mesh.get(), &serialCoordinate, meshPartitioning.get()
|
||||
);
|
||||
mfem::ParGridFunction distributedCoordinate(fem.mesh.get(), &serialCoordinate, meshPartitioning.get());
|
||||
|
||||
if (distributedCoordinate.Size() !=
|
||||
fem.compactificationFes->GetVSize()) {
|
||||
if (distributedCoordinate.Size() != fem.compactificationFes->GetVSize()) {
|
||||
throw std::runtime_error(
|
||||
"Distributed exterior coordinate does not match the "
|
||||
"constructed parallel finite-element space."
|
||||
);
|
||||
}
|
||||
|
||||
fem.compactificationCoordinate =
|
||||
std::make_unique<mfem::ParGridFunction>(
|
||||
fem.compactificationFes.get()
|
||||
);
|
||||
fem.compactificationCoordinate = std::make_unique<mfem::ParGridFunction>(fem.compactificationFes.get());
|
||||
|
||||
*fem.compactificationCoordinate = distributedCoordinate;
|
||||
|
||||
double localMinimum = std::numeric_limits<double>::infinity();
|
||||
double localMinimum = std::numeric_limits<double>::infinity();
|
||||
|
||||
double localMaximum = -std::numeric_limits<double>::infinity();
|
||||
double localMaximum = -std::numeric_limits<double>::infinity();
|
||||
|
||||
for (int index = 0; index < fem.compactificationCoordinate->Size();
|
||||
++index) {
|
||||
for (int index = 0; index < fem.compactificationCoordinate->Size(); ++index) {
|
||||
const double value = (*fem.compactificationCoordinate)(index);
|
||||
|
||||
if (!std::isfinite(value)) {
|
||||
throw std::runtime_error(
|
||||
"Exterior coordinate contains a non-finite value."
|
||||
);
|
||||
throw std::runtime_error("Exterior coordinate contains a non-finite value.");
|
||||
}
|
||||
|
||||
localMinimum = std::min(localMinimum, value);
|
||||
@@ -160,20 +139,13 @@ namespace mean_field::fem {
|
||||
double globalMinimum = 0.0;
|
||||
double globalMaximum = 0.0;
|
||||
|
||||
MPI_Allreduce(
|
||||
&localMinimum, &globalMinimum, 1, MPI_DOUBLE, MPI_MIN,
|
||||
MPI_COMM_WORLD
|
||||
);
|
||||
MPI_Allreduce(&localMinimum, &globalMinimum, 1, MPI_DOUBLE, MPI_MIN, MPI_COMM_WORLD);
|
||||
|
||||
MPI_Allreduce(
|
||||
&localMaximum, &globalMaximum, 1, MPI_DOUBLE, MPI_MAX,
|
||||
MPI_COMM_WORLD
|
||||
);
|
||||
MPI_Allreduce(&localMaximum, &globalMaximum, 1, MPI_DOUBLE, MPI_MAX, MPI_COMM_WORLD);
|
||||
|
||||
constexpr double coordinateTolerance = 1.0e-12;
|
||||
|
||||
if (globalMinimum < -coordinateTolerance ||
|
||||
globalMaximum > 1.0 + coordinateTolerance) {
|
||||
if (globalMinimum < -coordinateTolerance || globalMaximum > 1.0 + coordinateTolerance) {
|
||||
throw std::runtime_error(
|
||||
"Exterior coordinate lies outside the expected "
|
||||
"interval [0, 1]."
|
||||
@@ -188,59 +160,45 @@ namespace mean_field::fem {
|
||||
// Gravity potential: scalar L2
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
fem.gravityPotentialFec =
|
||||
GravityField::make_fec<GravityPotential>(dimension);
|
||||
fem.gravityPotentialFec = GravityField::make_fec<GravityPotential>(dimension);
|
||||
|
||||
fem.gravityPotentialFes = GravityField::make_fespace<GravityPotential>(
|
||||
*fem.mesh, *fem.gravityPotentialFec
|
||||
);
|
||||
fem.gravityPotentialFes = GravityField::make_fespace<GravityPotential>(*fem.mesh, *fem.gravityPotentialFec);
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Gravity flux: H(div)/RT. Basis choices are encoded by field.mfem.
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
fem.gravityFluxFec = GravityField::make_fec<GravityFlux>(dimension);
|
||||
fem.gravityFluxFec = GravityField::make_fec<GravityFlux>(dimension);
|
||||
|
||||
fem.gravityFluxFes = GravityField::make_fespace<GravityFlux>(
|
||||
*fem.mesh, *fem.gravityFluxFec
|
||||
);
|
||||
fem.gravityFluxFes = GravityField::make_fespace<GravityFlux>(*fem.mesh, *fem.gravityFluxFec);
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Displacement: vector H1. Ordering is encoded by field.mfem.
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
fem.displacementFec =
|
||||
DisplacementField::make_fec<DisplacementVector>(dimension);
|
||||
fem.displacementFec = DisplacementField::make_fec<DisplacementVector>(dimension);
|
||||
|
||||
fem.displacementFes =
|
||||
DisplacementField::make_fespace<DisplacementVector>(
|
||||
*fem.mesh, *fem.displacementFec
|
||||
);
|
||||
fem.displacementFes = DisplacementField::make_fespace<DisplacementVector>(*fem.mesh, *fem.displacementFec);
|
||||
|
||||
fem.displacement =
|
||||
std::make_unique<mfem::ParGridFunction>(fem.displacementFes.get());
|
||||
fem.displacement = std::make_unique<mfem::ParGridFunction>(fem.displacementFes.get());
|
||||
|
||||
*fem.displacement = 0.0;
|
||||
*fem.displacement = 0.0;
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Density: scalar discontinuous L2
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
fem.densityFec = DensityField::make_fec<DensityScalar>(dimension);
|
||||
fem.densityFec = DensityField::make_fec<DensityScalar>(dimension);
|
||||
|
||||
fem.densityFes = DensityField::make_fespace<DensityScalar>(
|
||||
*fem.mesh, *fem.densityFec
|
||||
);
|
||||
fem.densityFes = DensityField::make_fespace<DensityScalar>(*fem.mesh, *fem.densityFec);
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Specific enthalpy: scalar continuous H1
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
fem.enthalpyFec = EnthalpyField::make_fec<EnthalpyScalar>(dimension);
|
||||
fem.enthalpyFec = EnthalpyField::make_fec<EnthalpyScalar>(dimension);
|
||||
|
||||
fem.enthalpyFes = EnthalpyField::make_fespace<EnthalpyScalar>(
|
||||
*fem.mesh, *fem.enthalpyFec
|
||||
);
|
||||
fem.enthalpyFes = EnthalpyField::make_fespace<EnthalpyScalar>(*fem.mesh, *fem.enthalpyFec);
|
||||
|
||||
// =====================================================================
|
||||
// Section 4: Domain mapping
|
||||
@@ -248,21 +206,16 @@ namespace mean_field::fem {
|
||||
|
||||
auto [stellarRadiusReference, infinityRadiusReference] =
|
||||
utils::discover_bounds(fem.mesh.get(), 3)
|
||||
.or_else(
|
||||
[](const boundary::BoundsError &)
|
||||
-> std::expected<
|
||||
boundary::Bounds, boundary::BoundsError> {
|
||||
throw std::runtime_error(
|
||||
"Unable to determine vacuum-domain reference "
|
||||
"boundaries."
|
||||
);
|
||||
}
|
||||
)
|
||||
.or_else([](const boundary::BoundsError &) -> std::expected<boundary::Bounds, boundary::BoundsError> {
|
||||
throw std::runtime_error(
|
||||
"Unable to determine vacuum-domain reference "
|
||||
"boundaries."
|
||||
);
|
||||
})
|
||||
.value();
|
||||
|
||||
fem.mapping = std::make_unique<mapping::DomainMapper>(
|
||||
*fem.displacement, stellarRadiusReference, infinityRadiusReference
|
||||
);
|
||||
fem.mapping =
|
||||
std::make_unique<mapping::DomainMapper>(*fem.displacement, stellarRadiusReference, infinityRadiusReference);
|
||||
|
||||
// =====================================================================
|
||||
// Section 5: Block offsets
|
||||
@@ -278,17 +231,14 @@ namespace mean_field::fem {
|
||||
|
||||
fem.blockTrueOffsets[1] = fem.displacementFes->GetTrueVSize();
|
||||
|
||||
fem.blockTrueOffsets[2] =
|
||||
fem.blockTrueOffsets[1] + fem.densityFes->GetTrueVSize();
|
||||
fem.blockTrueOffsets[2] = fem.blockTrueOffsets[1] + fem.densityFes->GetTrueVSize();
|
||||
|
||||
fem.gravityBlockTrueOffsets.SetSize(3);
|
||||
fem.gravityBlockTrueOffsets[0] = 0;
|
||||
|
||||
fem.gravityBlockTrueOffsets[1] = fem.gravityFluxFes->GetTrueVSize();
|
||||
|
||||
fem.gravityBlockTrueOffsets[2] =
|
||||
fem.gravityBlockTrueOffsets[1] +
|
||||
fem.gravityPotentialFes->GetTrueVSize();
|
||||
fem.gravityBlockTrueOffsets[2] = fem.gravityBlockTrueOffsets[1] + fem.gravityPotentialFes->GetTrueVSize();
|
||||
|
||||
// =====================================================================
|
||||
// Section 6: Multipole data
|
||||
@@ -306,10 +256,7 @@ namespace mean_field::fem {
|
||||
|
||||
fem.essentialDisplacementTdofs.SetSize(0);
|
||||
|
||||
populate_element_mask(
|
||||
fem.mesh.get(), utils::DOMAINS::STELLAR,
|
||||
fem.gravityContext.stellar_mask
|
||||
);
|
||||
populate_element_mask(fem.mesh.get(), utils::DOMAINS::STELLAR, fem.gravityContext.stellar_mask);
|
||||
|
||||
const int boundaryAttributeCount = fem.mesh->bdr_attributes.Max();
|
||||
|
||||
@@ -317,21 +264,18 @@ namespace mean_field::fem {
|
||||
|
||||
fem.boundaryContext.stellar_bounds.SetSize(boundaryAttributeCount);
|
||||
|
||||
fem.boundaryContext.inf_bounds = 0;
|
||||
fem.boundaryContext.stellar_bounds = 0;
|
||||
fem.boundaryContext.inf_bounds = 0;
|
||||
fem.boundaryContext.stellar_bounds = 0;
|
||||
|
||||
fem.boundaryContext.inf_bounds
|
||||
[static_cast<int>(boundary::Boundaries::INF_SURFACE) - 1] = 1;
|
||||
fem.boundaryContext.inf_bounds[static_cast<int>(boundary::Boundaries::INF_SURFACE) - 1] = 1;
|
||||
|
||||
fem.boundaryContext.stellar_bounds
|
||||
[static_cast<int>(boundary::Boundaries::STELLAR_SURFACE) - 1] = 1;
|
||||
fem.boundaryContext.stellar_bounds[static_cast<int>(boundary::Boundaries::STELLAR_SURFACE) - 1] = 1;
|
||||
|
||||
// =====================================================================
|
||||
// Section 8: Gravity solver context
|
||||
// =====================================================================
|
||||
|
||||
fem.gravityContext.minres =
|
||||
std::make_unique<mfem::MINRESSolver>(fem.mesh->GetComm());
|
||||
fem.gravityContext.minres = std::make_unique<mfem::MINRESSolver>(fem.mesh->GetComm());
|
||||
|
||||
fem.gravityContext.minres->SetRelTol(1.0e-12);
|
||||
fem.gravityContext.minres->SetAbsTol(1.0e-12);
|
||||
@@ -343,13 +287,9 @@ namespace mean_field::fem {
|
||||
fem.gravityContext.prec_Phi->SetPrintLevel(0);
|
||||
|
||||
fem.gravityContext.block_prec =
|
||||
std::make_unique<mfem::BlockDiagonalPreconditioner>(
|
||||
fem.gravityBlockTrueOffsets
|
||||
);
|
||||
std::make_unique<mfem::BlockDiagonalPreconditioner>(fem.gravityBlockTrueOffsets);
|
||||
|
||||
fem.gravityContext.minres->SetPreconditioner(
|
||||
*fem.gravityContext.block_prec
|
||||
);
|
||||
fem.gravityContext.minres->SetPreconditioner(*fem.gravityContext.block_prec);
|
||||
|
||||
// =====================================================================
|
||||
// Section 9: Vacuum true-DOF masks
|
||||
@@ -358,202 +298,115 @@ namespace mean_field::fem {
|
||||
{
|
||||
mfem::Array<int> vacuumMask;
|
||||
|
||||
utils::populate_element_mask(
|
||||
fem.mesh.get(), utils::DOMAINS::VACUUM, vacuumMask
|
||||
);
|
||||
utils::populate_element_mask(fem.mesh.get(), utils::DOMAINS::VACUUM, vacuumMask);
|
||||
|
||||
utils::populate_domain_tdofs(
|
||||
fem.displacementFes.get(), vacuumMask,
|
||||
fem.vacuumDisplacementTdofs
|
||||
);
|
||||
utils::populate_domain_tdofs(fem.displacementFes.get(), vacuumMask, fem.vacuumDisplacementTdofs);
|
||||
|
||||
utils::populate_domain_tdofs(
|
||||
fem.densityFes.get(), vacuumMask, fem.vacuumDensityTdofs
|
||||
);
|
||||
utils::populate_domain_tdofs(fem.densityFes.get(), vacuumMask, fem.vacuumDensityTdofs);
|
||||
|
||||
utils::populate_domain_tdofs(
|
||||
fem.enthalpyFes.get(), vacuumMask, fem.vacuumEnthalpyTdofs
|
||||
);
|
||||
utils::populate_domain_tdofs(fem.enthalpyFes.get(), vacuumMask, fem.vacuumEnthalpyTdofs);
|
||||
}
|
||||
|
||||
// =====================================================================
|
||||
// Section 10: Quadrature policy
|
||||
// =====================================================================
|
||||
|
||||
const quadrature::QuadratureOptions &quadratureOptions =
|
||||
args.quadrature;
|
||||
const quadrature::QuadratureOptions &quadratureOptions = args.quadrature;
|
||||
|
||||
if (quadratureOptions.validation.reject_negative_boosts &&
|
||||
quadratureOptions.global_boost < 0) {
|
||||
throw std::invalid_argument(
|
||||
"Global quadrature boost cannot be negative."
|
||||
);
|
||||
if (quadratureOptions.validation.reject_negative_boosts && quadratureOptions.global_boost < 0) {
|
||||
throw std::invalid_argument("Global quadrature boost cannot be negative.");
|
||||
}
|
||||
|
||||
quadrature::RuleSet quadratureRuleSet = quadrature::make_rule_set(
|
||||
quadratureOptions.mode, quadratureOptions.global_boost
|
||||
);
|
||||
quadrature::RuleSet quadratureRuleSet =
|
||||
quadrature::make_rule_set(quadratureOptions.mode, quadratureOptions.global_boost);
|
||||
|
||||
if (quadratureOptions.fallback_fixed_order.has_value()) {
|
||||
if (*quadratureOptions.fallback_fixed_order < 0) {
|
||||
throw std::invalid_argument(
|
||||
"Fallback quadrature order cannot be negative."
|
||||
);
|
||||
throw std::invalid_argument("Fallback quadrature order cannot be negative.");
|
||||
}
|
||||
|
||||
quadratureRuleSet.fallback.fixed_order =
|
||||
quadratureOptions.fallback_fixed_order;
|
||||
quadratureRuleSet.fallback.fixed_order = quadratureOptions.fallback_fixed_order;
|
||||
}
|
||||
|
||||
auto apply_quadrature_options =
|
||||
[&quadratureOptions](
|
||||
quadrature::RuleControl &ruleControl,
|
||||
const quadrature::QuadratureTermOptions &termOptions
|
||||
) {
|
||||
if (termOptions.fixed_order.has_value() &&
|
||||
*termOptions.fixed_order < 0) {
|
||||
throw std::invalid_argument(
|
||||
"Fixed quadrature order cannot be negative."
|
||||
);
|
||||
}
|
||||
auto apply_quadrature_options = [&quadratureOptions](
|
||||
quadrature::RuleControl &ruleControl,
|
||||
const quadrature::QuadratureTermOptions &termOptions
|
||||
) {
|
||||
if (termOptions.fixed_order.has_value() && *termOptions.fixed_order < 0) {
|
||||
throw std::invalid_argument("Fixed quadrature order cannot be negative.");
|
||||
}
|
||||
|
||||
if (quadratureOptions.validation.reject_negative_boosts &&
|
||||
termOptions.additional_boost < 0) {
|
||||
throw std::invalid_argument(
|
||||
"Term quadrature boost cannot be negative."
|
||||
);
|
||||
}
|
||||
if (quadratureOptions.validation.reject_negative_boosts && termOptions.additional_boost < 0) {
|
||||
throw std::invalid_argument("Term quadrature boost cannot be negative.");
|
||||
}
|
||||
|
||||
ruleControl.boost += termOptions.additional_boost;
|
||||
ruleControl.boost += termOptions.additional_boost;
|
||||
|
||||
if (termOptions.fixed_order.has_value()) {
|
||||
ruleControl.fixed_order = termOptions.fixed_order;
|
||||
}
|
||||
};
|
||||
if (termOptions.fixed_order.has_value()) {
|
||||
ruleControl.fixed_order = termOptions.fixed_order;
|
||||
}
|
||||
};
|
||||
|
||||
apply_quadrature_options(
|
||||
quadratureRuleSet.gravity_hdiv_mass,
|
||||
quadratureOptions.gravity_hdiv_mass
|
||||
);
|
||||
apply_quadrature_options(quadratureRuleSet.gravity_hdiv_mass, quadratureOptions.gravity_hdiv_mass);
|
||||
|
||||
apply_quadrature_options(
|
||||
quadratureRuleSet.gravity_divergence,
|
||||
quadratureOptions.gravity_divergence
|
||||
);
|
||||
apply_quadrature_options(quadratureRuleSet.gravity_divergence, quadratureOptions.gravity_divergence);
|
||||
|
||||
apply_quadrature_options(
|
||||
quadratureRuleSet.gravity_source, quadratureOptions.gravity_source
|
||||
);
|
||||
apply_quadrature_options(quadratureRuleSet.gravity_source, quadratureOptions.gravity_source);
|
||||
|
||||
apply_quadrature_options(
|
||||
quadratureRuleSet.gravity_boundary,
|
||||
quadratureOptions.gravity_boundary
|
||||
);
|
||||
apply_quadrature_options(quadratureRuleSet.gravity_force, quadratureOptions.gravity_force);
|
||||
|
||||
apply_quadrature_options(
|
||||
quadratureRuleSet.centrifugal, quadratureOptions.centrifugal
|
||||
);
|
||||
apply_quadrature_options(quadratureRuleSet.gravity_boundary, quadratureOptions.gravity_boundary);
|
||||
|
||||
apply_quadrature_options(
|
||||
quadratureRuleSet.density_projection,
|
||||
quadratureOptions.density_projection
|
||||
);
|
||||
apply_quadrature_options(quadratureRuleSet.centrifugal, quadratureOptions.centrifugal);
|
||||
|
||||
apply_quadrature_options(
|
||||
quadratureRuleSet.eos_closure, quadratureOptions.eos_closure
|
||||
);
|
||||
apply_quadrature_options(quadratureRuleSet.density_projection, quadratureOptions.density_projection);
|
||||
|
||||
apply_quadrature_options(
|
||||
quadratureRuleSet.hydrostatic_equilibrium,
|
||||
quadratureOptions.hydrostatic_equilibrium
|
||||
);
|
||||
apply_quadrature_options(quadratureRuleSet.eos_closure, quadratureOptions.eos_closure);
|
||||
|
||||
apply_quadrature_options(
|
||||
quadratureRuleSet.isobaric_surface,
|
||||
quadratureOptions.isobaric_surface
|
||||
);
|
||||
apply_quadrature_options(quadratureRuleSet.hydrostatic_equilibrium, quadratureOptions.hydrostatic_equilibrium);
|
||||
|
||||
apply_quadrature_options(
|
||||
quadratureRuleSet.mesh_extension, quadratureOptions.mesh_extension
|
||||
);
|
||||
apply_quadrature_options(quadratureRuleSet.isobaric_surface, quadratureOptions.isobaric_surface);
|
||||
|
||||
apply_quadrature_options(
|
||||
quadratureRuleSet.mass_conservation,
|
||||
quadratureOptions.mass_conservation
|
||||
);
|
||||
apply_quadrature_options(quadratureRuleSet.mesh_extension, quadratureOptions.mesh_extension);
|
||||
|
||||
apply_quadrature_options(
|
||||
quadratureRuleSet.mass_normalization,
|
||||
quadratureOptions.mass_normalization
|
||||
);
|
||||
apply_quadrature_options(quadratureRuleSet.mass_conservation, quadratureOptions.mass_conservation);
|
||||
|
||||
apply_quadrature_options(
|
||||
quadratureRuleSet.center_of_mass, quadratureOptions.center_of_mass
|
||||
);
|
||||
apply_quadrature_options(quadratureRuleSet.mass_normalization, quadratureOptions.mass_normalization);
|
||||
|
||||
apply_quadrature_options(
|
||||
quadratureRuleSet.quadrupole, quadratureOptions.quadrupole
|
||||
);
|
||||
apply_quadrature_options(quadratureRuleSet.center_of_mass, quadratureOptions.center_of_mass);
|
||||
|
||||
apply_quadrature_options(
|
||||
quadratureRuleSet.gravitational_energy,
|
||||
quadratureOptions.gravitational_energy
|
||||
);
|
||||
apply_quadrature_options(quadratureRuleSet.quadrupole, quadratureOptions.quadrupole);
|
||||
|
||||
apply_quadrature_options(
|
||||
quadratureRuleSet.pressure_integral,
|
||||
quadratureOptions.pressure_integral
|
||||
);
|
||||
apply_quadrature_options(quadratureRuleSet.gravitational_energy, quadratureOptions.gravitational_energy);
|
||||
|
||||
apply_quadrature_options(
|
||||
quadratureRuleSet.pressure_force, quadratureOptions.pressure_force
|
||||
);
|
||||
apply_quadrature_options(quadratureRuleSet.pressure_integral, quadratureOptions.pressure_integral);
|
||||
|
||||
apply_quadrature_options(
|
||||
quadratureRuleSet.virial, quadratureOptions.virial
|
||||
);
|
||||
apply_quadrature_options(quadratureRuleSet.pressure_force, quadratureOptions.pressure_force);
|
||||
|
||||
apply_quadrature_options(
|
||||
quadratureRuleSet.error_norm, quadratureOptions.error_norm
|
||||
);
|
||||
apply_quadrature_options(quadratureRuleSet.virial, quadratureOptions.virial);
|
||||
|
||||
apply_quadrature_options(
|
||||
quadratureRuleSet.roles.discretization,
|
||||
quadratureOptions.roles.discretization
|
||||
);
|
||||
apply_quadrature_options(quadratureRuleSet.error_norm, quadratureOptions.error_norm);
|
||||
|
||||
apply_quadrature_options(
|
||||
quadratureRuleSet.roles.preconditioner,
|
||||
quadratureOptions.roles.preconditioner
|
||||
);
|
||||
apply_quadrature_options(quadratureRuleSet.roles.discretization, quadratureOptions.roles.discretization);
|
||||
|
||||
apply_quadrature_options(
|
||||
quadratureRuleSet.roles.diagnostic,
|
||||
quadratureOptions.roles.diagnostic
|
||||
);
|
||||
apply_quadrature_options(quadratureRuleSet.roles.preconditioner, quadratureOptions.roles.preconditioner);
|
||||
|
||||
apply_quadrature_options(
|
||||
quadratureRuleSet.roles.projection,
|
||||
quadratureOptions.roles.projection
|
||||
);
|
||||
apply_quadrature_options(quadratureRuleSet.roles.diagnostic, quadratureOptions.roles.diagnostic);
|
||||
|
||||
fem.quadratureFactory = std::make_unique<quadrature::RuleFactory>(
|
||||
quadrature::Policy(std::move(quadratureRuleSet))
|
||||
);
|
||||
apply_quadrature_options(quadratureRuleSet.roles.projection, quadratureOptions.roles.projection);
|
||||
|
||||
fem.quadratureFactory =
|
||||
std::make_unique<quadrature::RuleFactory>(quadrature::Policy(std::move(quadratureRuleSet)));
|
||||
|
||||
// =====================================================================
|
||||
// Section 11: Stateless domain mapper
|
||||
// =====================================================================
|
||||
|
||||
auto exteriorDomain = std::make_unique<
|
||||
const mapping::compactification::KelvinCompactification>(
|
||||
args.kelvin_options
|
||||
);
|
||||
auto exteriorDomain =
|
||||
std::make_unique<const mapping::compactification::KelvinCompactification>(args.kelvin_options);
|
||||
|
||||
fem.domainMapperStateless =
|
||||
std::make_unique<mapping::DomainMapperStateless>(
|
||||
args.domain_mapper_options, std::move(exteriorDomain)
|
||||
);
|
||||
std::make_unique<mapping::DomainMapperStateless>(args.domain_mapper_options, std::move(exteriorDomain));
|
||||
|
||||
return fem;
|
||||
}
|
||||
|
||||
@@ -4,8 +4,7 @@ module;
|
||||
module mean_field;
|
||||
|
||||
namespace mean_field::integrators {
|
||||
AdvectionIntegrator::AdvectionIntegrator(const mapping::DomainMapper &map)
|
||||
: m_map(map) {
|
||||
AdvectionIntegrator::AdvectionIntegrator(const mapping::DomainMapper &map) : m_map(map) {
|
||||
}
|
||||
|
||||
void AdvectionIntegrator::AssembleElementVector(
|
||||
@@ -39,8 +38,7 @@ namespace mean_field::integrators {
|
||||
mfem::Vector shape_v(dof_v), shape_rho(dof_rho);
|
||||
mfem::DenseMatrix dshape_v_ref(dof_v, dim), dshape_v_phys(dof_v, dim);
|
||||
|
||||
const mfem::IntegrationRule *ir =
|
||||
&mfem::IntRules.Get(fe_v->GetGeomType(), 2 * fe_v->GetOrder() + 1);
|
||||
const mfem::IntegrationRule *ir = &mfem::IntRules.Get(fe_v->GetGeomType(), 2 * fe_v->GetOrder() + 1);
|
||||
|
||||
for (int q = 0; q < ir->GetNPoints(); q++) {
|
||||
const mfem::IntegrationPoint &ip = ir->IntPoint(q);
|
||||
@@ -83,8 +81,7 @@ namespace mean_field::integrators {
|
||||
|
||||
for (int i = 0; i < dof_v; ++i) {
|
||||
for (int c = 0; c < dim; ++c) {
|
||||
r_v(i + c * dof_v) +=
|
||||
shape_v(i) * rho_val * adv_val(c) * weight;
|
||||
r_v(i + c * dof_v) += shape_v(i) * rho_val * adv_val(c) * weight;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -117,8 +114,7 @@ namespace mean_field::integrators {
|
||||
mfem::Vector shape_v(dof_v), shape_rho(dof_rho);
|
||||
mfem::DenseMatrix dshape_v_ref(dof_v, dim), dshape_v_phys(dof_v, dim);
|
||||
|
||||
const mfem::IntegrationRule *ir =
|
||||
&mfem::IntRules.Get(fe_v->GetGeomType(), 2 * fe_v->GetOrder() + 1);
|
||||
const mfem::IntegrationRule *ir = &mfem::IntRules.Get(fe_v->GetGeomType(), 2 * fe_v->GetOrder() + 1);
|
||||
|
||||
for (int q = 0; q < ir->GetNPoints(); q++) {
|
||||
const mfem::IntegrationPoint &ip = ir->IntPoint(q);
|
||||
@@ -171,8 +167,7 @@ namespace mean_field::integrators {
|
||||
double v_dot_grad_phi_j = 0.0;
|
||||
|
||||
for (int k = 0; k < dim; ++k) {
|
||||
v_dot_grad_phi_j +=
|
||||
v_val(k) * dshape_v_phys(j, k);
|
||||
v_dot_grad_phi_j += v_val(k) * dshape_v_phys(j, k);
|
||||
}
|
||||
|
||||
for (int d = 0; d < dim; ++d) {
|
||||
@@ -187,11 +182,9 @@ namespace mean_field::integrators {
|
||||
// \rho(\vec{v} \cdot \nabla \delta \vec{v})
|
||||
// Only non-zero when the advected component
|
||||
// matches the test component
|
||||
double termB =
|
||||
(c == d) ? v_dot_grad_phi_j : 0.0;
|
||||
double termB = (c == d) ? v_dot_grad_phi_j : 0.0;
|
||||
|
||||
(*dv_dv)(row, col) += shape_v(i) * rho_val *
|
||||
(termA + termB) * weight;
|
||||
(*dv_dv)(row, col) += shape_v(i) * rho_val * (termA + termB) * weight;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,9 +18,7 @@ namespace mean_field::integrators {
|
||||
m_omega = omega;
|
||||
}
|
||||
|
||||
void CentrifugalForceIntegrator::SetIntegrationRule(
|
||||
const mfem::IntegrationRule &ir
|
||||
) {
|
||||
void CentrifugalForceIntegrator::SetIntegrationRule(const mfem::IntegrationRule &ir) {
|
||||
m_ir = &ir;
|
||||
}
|
||||
|
||||
@@ -130,8 +128,7 @@ namespace mean_field::integrators {
|
||||
mfem::Vector x_phys(dim);
|
||||
mfem::Vector a(dim), b(dim);
|
||||
|
||||
const mfem::IntegrationRule *ir =
|
||||
&mfem::IntRules.Get(fe_v->GetGeomType(), 2 * fe_v->GetOrder());
|
||||
const mfem::IntegrationRule *ir = &mfem::IntRules.Get(fe_v->GetGeomType(), 2 * fe_v->GetOrder());
|
||||
|
||||
for (int q = 0; q < ir->GetNPoints(); ++q) {
|
||||
const mfem::IntegrationPoint &ip = ir->IntPoint(q);
|
||||
@@ -159,8 +156,7 @@ namespace mean_field::integrators {
|
||||
for (int c = 0; c < dim; ++c) {
|
||||
const int row = i + c * dof_v;
|
||||
for (int j = 0; j < dof_rho; ++j) {
|
||||
(*dv_drho)(row, j) +=
|
||||
shape_v(i) * shape_rho(j) * b(c) * weight;
|
||||
(*dv_drho)(row, j) += shape_v(i) * shape_rho(j) * b(c) * weight;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,8 +49,7 @@ namespace mean_field::integrators {
|
||||
}
|
||||
|
||||
mfem::Vector shape_v(dof_v), shape_rho(dof_rho);
|
||||
const mfem::IntegrationRule *ir =
|
||||
&mfem::IntRules.Get(fe_v->GetGeomType(), 2 * fe_v->GetOrder());
|
||||
const mfem::IntegrationRule *ir = &mfem::IntRules.Get(fe_v->GetGeomType(), 2 * fe_v->GetOrder());
|
||||
|
||||
for (int q = 0; q < ir->GetNPoints(); ++q) {
|
||||
const mfem::IntegrationPoint &ip = ir->IntPoint(q);
|
||||
@@ -78,8 +77,7 @@ namespace mean_field::integrators {
|
||||
|
||||
for (int i = 0; i < dof_v; ++i) {
|
||||
for (int c = 0; c < dim; ++c) {
|
||||
r_v(i + c * dof_v) +=
|
||||
shape_v(i) * rho_val * F_coriolis(c) * weight;
|
||||
r_v(i + c * dof_v) += shape_v(i) * rho_val * F_coriolis(c) * weight;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -111,8 +109,7 @@ namespace mean_field::integrators {
|
||||
*dv_drho = 0.0;
|
||||
|
||||
mfem::Vector shape_v(dof_v), shape_rho(dof_rho);
|
||||
const mfem::IntegrationRule *ir =
|
||||
&mfem::IntRules.Get(fe_v->GetGeomType(), 2 * fe_v->GetOrder());
|
||||
const mfem::IntegrationRule *ir = &mfem::IntRules.Get(fe_v->GetGeomType(), 2 * fe_v->GetOrder());
|
||||
|
||||
for (int q = 0; q < ir->GetNPoints(); ++q) {
|
||||
const mfem::IntegrationPoint &ip = ir->IntPoint(q);
|
||||
@@ -146,9 +143,7 @@ namespace mean_field::integrators {
|
||||
for (int d = 0; d < dim; ++d) {
|
||||
int col = j + d * dof_v;
|
||||
double coupling = m_omega_mat(c, d);
|
||||
(*dv_dv)(row, col) += shape_v(i) * shape_v(j) *
|
||||
2.0 * rho_val * coupling *
|
||||
weight;
|
||||
(*dv_dv)(row, col) += shape_v(i) * shape_v(j) * 2.0 * rho_val * coupling * weight;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -161,8 +156,7 @@ namespace mean_field::integrators {
|
||||
int row = i + c * dof_v;
|
||||
for (int j = 0; j < dof_rho; ++j) {
|
||||
int col = j;
|
||||
(*dv_drho)(row, col) += shape_v(i) * shape_rho(j) *
|
||||
F_coriolis(c) * weight;
|
||||
(*dv_drho)(row, col) += shape_v(i) * shape_rho(j) * F_coriolis(c) * weight;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,14 +6,10 @@ import :solver.fields;
|
||||
namespace {
|
||||
using namespace mean_field;
|
||||
|
||||
constexpr int velocity_block =
|
||||
solver::block_index(solver::FieldBlock::velocity);
|
||||
constexpr int density_block =
|
||||
solver::block_index(solver::FieldBlock::density);
|
||||
constexpr int gravity_gradient_block =
|
||||
solver::block_index(solver::FieldBlock::gravity_gradient);
|
||||
constexpr int displacement_block =
|
||||
solver::block_index(solver::FieldBlock::displacement);
|
||||
constexpr int velocity_block = solver::block_index(solver::FieldBlock::velocity);
|
||||
constexpr int density_block = solver::block_index(solver::FieldBlock::density);
|
||||
constexpr int gravity_gradient_block = solver::block_index(solver::FieldBlock::gravity_gradient);
|
||||
constexpr int displacement_block = solver::block_index(solver::FieldBlock::displacement);
|
||||
} // namespace
|
||||
|
||||
namespace mean_field::integrators {
|
||||
@@ -25,20 +21,15 @@ namespace mean_field::integrators {
|
||||
m_jacobian_mode(jacobian_mode) {
|
||||
}
|
||||
|
||||
void GravityMomentumIntegrator::SetJacobianMode(
|
||||
const GravityForceJacobianMode jacobian_mode
|
||||
) {
|
||||
void GravityMomentumIntegrator::SetJacobianMode(const GravityForceJacobianMode jacobian_mode) {
|
||||
m_jacobian_mode = jacobian_mode;
|
||||
}
|
||||
|
||||
void GravityMomentumIntegrator::SetIntegrationRule(
|
||||
const mfem::IntegrationRule &integration_rule
|
||||
) {
|
||||
void GravityMomentumIntegrator::SetIntegrationRule(const mfem::IntegrationRule &integration_rule) {
|
||||
m_integration_rule = &integration_rule;
|
||||
}
|
||||
|
||||
GravityForceJacobianMode
|
||||
GravityMomentumIntegrator::GetJacobianMode() const {
|
||||
GravityForceJacobianMode GravityMomentumIntegrator::GetJacobianMode() const {
|
||||
return m_jacobian_mode;
|
||||
}
|
||||
|
||||
@@ -53,27 +44,23 @@ namespace mean_field::integrators {
|
||||
}
|
||||
|
||||
MFEM_VERIFY(
|
||||
m_integration_rule,
|
||||
"GravityForceIntegrator must be configured with an "
|
||||
"integration rule before assembly."
|
||||
m_integration_rule, "GravityForceIntegrator must be configured with an "
|
||||
"integration rule before assembly."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
el.Size() > gravity_gradient_block,
|
||||
"GravityForceIntegrator requires velocity, density, and "
|
||||
"gravity-gradient finite elements."
|
||||
el.Size() > gravity_gradient_block, "GravityForceIntegrator requires velocity, density, and "
|
||||
"gravity-gradient finite elements."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
elfun.Size() > gravity_gradient_block,
|
||||
"GravityForceIntegrator requires velocity, density, and "
|
||||
"gravity-gradient element states."
|
||||
elfun.Size() > gravity_gradient_block, "GravityForceIntegrator requires velocity, density, and "
|
||||
"gravity-gradient element states."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
elvec.Size() > velocity_block && elvec[velocity_block],
|
||||
"GravityForceIntegrator requires a velocity residual block."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
el[velocity_block] && el[density_block] &&
|
||||
el[gravity_gradient_block],
|
||||
el[velocity_block] && el[density_block] && el[gravity_gradient_block],
|
||||
"GravityForceIntegrator received a null finite element."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
@@ -81,25 +68,21 @@ namespace mean_field::integrators {
|
||||
"GravityForceIntegrator received a null element state."
|
||||
);
|
||||
|
||||
const mfem::FiniteElement *velocity_element = el[velocity_block];
|
||||
const mfem::FiniteElement *density_element = el[density_block];
|
||||
const mfem::FiniteElement *gravity_gradient_element =
|
||||
el[gravity_gradient_block];
|
||||
const mfem::FiniteElement *velocity_element = el[velocity_block];
|
||||
const mfem::FiniteElement *density_element = el[density_block];
|
||||
const mfem::FiniteElement *gravity_gradient_element = el[gravity_gradient_block];
|
||||
|
||||
const int velocity_dofs_count = velocity_element->GetDof();
|
||||
const int density_dofs_count = density_element->GetDof();
|
||||
const int gravity_gradient_dofs_count =
|
||||
gravity_gradient_element->GetDof();
|
||||
const int dim = Tr.GetSpaceDim();
|
||||
const int velocity_dofs_count = velocity_element->GetDof();
|
||||
const int density_dofs_count = density_element->GetDof();
|
||||
const int gravity_gradient_dofs_count = gravity_gradient_element->GetDof();
|
||||
const int dim = Tr.GetSpaceDim();
|
||||
|
||||
const mfem::Vector &density_dofs = *elfun[density_block];
|
||||
const mfem::Vector &gravity_gradient_dofs =
|
||||
*elfun[gravity_gradient_block];
|
||||
const mfem::Vector &density_dofs = *elfun[density_block];
|
||||
const mfem::Vector &gravity_gradient_dofs = *elfun[gravity_gradient_block];
|
||||
|
||||
MFEM_VERIFY(
|
||||
density_dofs.Size() == density_dofs_count,
|
||||
"GravityForceIntegrator received an incorrectly sized density "
|
||||
"state."
|
||||
density_dofs.Size() == density_dofs_count, "GravityForceIntegrator received an incorrectly sized density "
|
||||
"state."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
gravity_gradient_dofs.Size() == gravity_gradient_dofs_count,
|
||||
@@ -123,40 +106,31 @@ namespace mean_field::integrators {
|
||||
*elvec[density_block] = 0.0;
|
||||
}
|
||||
|
||||
if (elvec.Size() > gravity_gradient_block &&
|
||||
elvec[gravity_gradient_block]) {
|
||||
if (elvec.Size() > gravity_gradient_block && elvec[gravity_gradient_block]) {
|
||||
elvec[gravity_gradient_block]->SetSize(gravity_gradient_dofs_count);
|
||||
*elvec[gravity_gradient_block] = 0.0;
|
||||
}
|
||||
|
||||
mfem::Vector velocity_shape(velocity_dofs_count);
|
||||
mfem::Vector density_shape(density_dofs_count);
|
||||
mfem::DenseMatrix gravity_gradient_shape(
|
||||
gravity_gradient_dofs_count, dim
|
||||
);
|
||||
mfem::DenseMatrix gravity_gradient_shape(gravity_gradient_dofs_count, dim);
|
||||
mfem::Vector gravity_gradient_element_value(dim);
|
||||
mfem::Vector gravity_gradient_physical_value(dim);
|
||||
|
||||
const mfem::IntegrationRule &integration_rule = *m_integration_rule;
|
||||
|
||||
for (int q = 0; q < integration_rule.GetNPoints(); ++q) {
|
||||
const mfem::IntegrationPoint &integration_point =
|
||||
integration_rule.IntPoint(q);
|
||||
const mfem::IntegrationPoint &integration_point = integration_rule.IntPoint(q);
|
||||
Tr.SetIntPoint(&integration_point);
|
||||
|
||||
const mapping::VolumeQuadratureContext context =
|
||||
m_map.GetQuadratureContext(Tr, integration_point);
|
||||
const mapping::VolumeQuadratureContext context = m_map.GetQuadratureContext(Tr, integration_point);
|
||||
|
||||
velocity_element->CalcShape(integration_point, velocity_shape);
|
||||
density_element->CalcShape(integration_point, density_shape);
|
||||
gravity_gradient_element->CalcVShape(Tr, gravity_gradient_shape);
|
||||
|
||||
gravity_gradient_shape.MultTranspose(
|
||||
gravity_gradient_dofs, gravity_gradient_element_value
|
||||
);
|
||||
context.J_inv.MultTranspose(
|
||||
gravity_gradient_element_value, gravity_gradient_physical_value
|
||||
);
|
||||
gravity_gradient_shape.MultTranspose(gravity_gradient_dofs, gravity_gradient_element_value);
|
||||
context.J_inv.MultTranspose(gravity_gradient_element_value, gravity_gradient_physical_value);
|
||||
|
||||
double density_value = 0.0;
|
||||
for (int i = 0; i < density_dofs_count; ++i) {
|
||||
@@ -166,9 +140,7 @@ namespace mean_field::integrators {
|
||||
for (int i = 0; i < velocity_dofs_count; ++i) {
|
||||
for (int component = 0; component < dim; ++component) {
|
||||
velocity_residual(i + component * velocity_dofs_count) +=
|
||||
velocity_shape(i) * density_value *
|
||||
gravity_gradient_physical_value(component) *
|
||||
context.weight;
|
||||
velocity_shape(i) * density_value * gravity_gradient_physical_value(component) * context.weight;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -185,23 +157,19 @@ namespace mean_field::integrators {
|
||||
}
|
||||
|
||||
MFEM_VERIFY(
|
||||
m_integration_rule,
|
||||
"GravityForceIntegrator must be configured with an "
|
||||
"integration rule before assembly."
|
||||
m_integration_rule, "GravityForceIntegrator must be configured with an "
|
||||
"integration rule before assembly."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
el.Size() > gravity_gradient_block,
|
||||
"GravityForceIntegrator requires velocity, density, and "
|
||||
"gravity-gradient finite elements."
|
||||
el.Size() > gravity_gradient_block, "GravityForceIntegrator requires velocity, density, and "
|
||||
"gravity-gradient finite elements."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
elfun.Size() > gravity_gradient_block,
|
||||
"GravityForceIntegrator requires velocity, density, and "
|
||||
"gravity-gradient element states."
|
||||
elfun.Size() > gravity_gradient_block, "GravityForceIntegrator requires velocity, density, and "
|
||||
"gravity-gradient element states."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
el[velocity_block] && el[density_block] &&
|
||||
el[gravity_gradient_block],
|
||||
el[velocity_block] && el[density_block] && el[gravity_gradient_block],
|
||||
"GravityForceIntegrator received a null finite element."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
@@ -226,25 +194,21 @@ namespace mean_field::integrators {
|
||||
);
|
||||
}
|
||||
|
||||
const mfem::FiniteElement *velocity_element = el[velocity_block];
|
||||
const mfem::FiniteElement *density_element = el[density_block];
|
||||
const mfem::FiniteElement *gravity_gradient_element =
|
||||
el[gravity_gradient_block];
|
||||
const mfem::FiniteElement *velocity_element = el[velocity_block];
|
||||
const mfem::FiniteElement *density_element = el[density_block];
|
||||
const mfem::FiniteElement *gravity_gradient_element = el[gravity_gradient_block];
|
||||
|
||||
const int velocity_dofs_count = velocity_element->GetDof();
|
||||
const int density_dofs_count = density_element->GetDof();
|
||||
const int gravity_gradient_dofs_count =
|
||||
gravity_gradient_element->GetDof();
|
||||
const int dim = Tr.GetSpaceDim();
|
||||
const int velocity_dofs_count = velocity_element->GetDof();
|
||||
const int density_dofs_count = density_element->GetDof();
|
||||
const int gravity_gradient_dofs_count = gravity_gradient_element->GetDof();
|
||||
const int dim = Tr.GetSpaceDim();
|
||||
|
||||
const mfem::Vector &density_dofs = *elfun[density_block];
|
||||
const mfem::Vector &gravity_gradient_dofs =
|
||||
*elfun[gravity_gradient_block];
|
||||
const mfem::Vector &density_dofs = *elfun[density_block];
|
||||
const mfem::Vector &gravity_gradient_dofs = *elfun[gravity_gradient_block];
|
||||
|
||||
MFEM_VERIFY(
|
||||
density_dofs.Size() == density_dofs_count,
|
||||
"GravityForceIntegrator received an incorrectly sized density "
|
||||
"state."
|
||||
density_dofs.Size() == density_dofs_count, "GravityForceIntegrator received an incorrectly sized density "
|
||||
"state."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
gravity_gradient_dofs.Size() == gravity_gradient_dofs_count,
|
||||
@@ -253,11 +217,10 @@ namespace mean_field::integrators {
|
||||
"state."
|
||||
);
|
||||
|
||||
mfem::DenseMatrix *dv_drho = elmats(velocity_block, density_block);
|
||||
mfem::DenseMatrix *dv_dgrad_phi =
|
||||
m_jacobian_mode == GravityForceJacobianMode::field_coupled
|
||||
? elmats(velocity_block, gravity_gradient_block)
|
||||
: nullptr;
|
||||
mfem::DenseMatrix *dv_drho = elmats(velocity_block, density_block);
|
||||
mfem::DenseMatrix *dv_dgrad_phi = m_jacobian_mode == GravityForceJacobianMode::field_coupled
|
||||
? elmats(velocity_block, gravity_gradient_block)
|
||||
: nullptr;
|
||||
|
||||
if (!dv_drho && !dv_dgrad_phi) {
|
||||
return;
|
||||
@@ -265,9 +228,7 @@ namespace mean_field::integrators {
|
||||
|
||||
mfem::Vector velocity_shape(velocity_dofs_count);
|
||||
mfem::Vector density_shape(density_dofs_count);
|
||||
mfem::DenseMatrix gravity_gradient_shape(
|
||||
gravity_gradient_dofs_count, dim
|
||||
);
|
||||
mfem::DenseMatrix gravity_gradient_shape(gravity_gradient_dofs_count, dim);
|
||||
mfem::Vector gravity_gradient_element_value(dim);
|
||||
mfem::Vector gravity_gradient_physical_value(dim);
|
||||
mfem::Vector gravity_basis_element(dim);
|
||||
@@ -276,23 +237,17 @@ namespace mean_field::integrators {
|
||||
const mfem::IntegrationRule &integration_rule = *m_integration_rule;
|
||||
|
||||
for (int q = 0; q < integration_rule.GetNPoints(); ++q) {
|
||||
const mfem::IntegrationPoint &integration_point =
|
||||
integration_rule.IntPoint(q);
|
||||
const mfem::IntegrationPoint &integration_point = integration_rule.IntPoint(q);
|
||||
Tr.SetIntPoint(&integration_point);
|
||||
|
||||
const mapping::VolumeQuadratureContext context =
|
||||
m_map.GetQuadratureContext(Tr, integration_point);
|
||||
const mapping::VolumeQuadratureContext context = m_map.GetQuadratureContext(Tr, integration_point);
|
||||
|
||||
velocity_element->CalcShape(integration_point, velocity_shape);
|
||||
density_element->CalcShape(integration_point, density_shape);
|
||||
gravity_gradient_element->CalcVShape(Tr, gravity_gradient_shape);
|
||||
|
||||
gravity_gradient_shape.MultTranspose(
|
||||
gravity_gradient_dofs, gravity_gradient_element_value
|
||||
);
|
||||
context.J_inv.MultTranspose(
|
||||
gravity_gradient_element_value, gravity_gradient_physical_value
|
||||
);
|
||||
gravity_gradient_shape.MultTranspose(gravity_gradient_dofs, gravity_gradient_element_value);
|
||||
context.J_inv.MultTranspose(gravity_gradient_element_value, gravity_gradient_physical_value);
|
||||
|
||||
double density_value = 0.0;
|
||||
for (int i = 0; i < density_dofs_count; ++i) {
|
||||
@@ -305,10 +260,8 @@ namespace mean_field::integrators {
|
||||
const int row = i + component * velocity_dofs_count;
|
||||
|
||||
for (int j = 0; j < density_dofs_count; ++j) {
|
||||
(*dv_drho)(row, j) +=
|
||||
velocity_shape(i) * density_shape(j) *
|
||||
gravity_gradient_physical_value(component) *
|
||||
context.weight;
|
||||
(*dv_drho)(row, j) += velocity_shape(i) * density_shape(j) *
|
||||
gravity_gradient_physical_value(component) * context.weight;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -317,21 +270,16 @@ namespace mean_field::integrators {
|
||||
if (dv_dgrad_phi) {
|
||||
for (int j = 0; j < gravity_gradient_dofs_count; ++j) {
|
||||
for (int component = 0; component < dim; ++component) {
|
||||
gravity_basis_element(component) =
|
||||
gravity_gradient_shape(j, component);
|
||||
gravity_basis_element(component) = gravity_gradient_shape(j, component);
|
||||
}
|
||||
|
||||
context.J_inv.MultTranspose(
|
||||
gravity_basis_element, gravity_basis_physical
|
||||
);
|
||||
context.J_inv.MultTranspose(gravity_basis_element, gravity_basis_physical);
|
||||
|
||||
for (int i = 0; i < velocity_dofs_count; ++i) {
|
||||
for (int component = 0; component < dim; ++component) {
|
||||
const int row = i + component * velocity_dofs_count;
|
||||
(*dv_dgrad_phi)(row, j) +=
|
||||
velocity_shape(i) * density_value *
|
||||
gravity_basis_physical(component) *
|
||||
context.weight;
|
||||
velocity_shape(i) * density_value * gravity_basis_physical(component) * context.weight;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,10 +4,7 @@ module;
|
||||
module mean_field;
|
||||
|
||||
namespace mean_field::integrators {
|
||||
ContinuityVolumeIntegrator::ContinuityVolumeIntegrator(
|
||||
const mapping::DomainMapper &map
|
||||
)
|
||||
: m_map(map) { };
|
||||
ContinuityVolumeIntegrator::ContinuityVolumeIntegrator(const mapping::DomainMapper &map) : m_map(map) { };
|
||||
|
||||
void ContinuityVolumeIntegrator::AssembleElementVector(
|
||||
const mfem::Array<const mfem::FiniteElement *> &el,
|
||||
@@ -29,9 +26,8 @@ namespace mean_field::integrators {
|
||||
const mfem::Vector v_dofs = *elfun[0];
|
||||
const mfem::Vector rho_dofs = *elfun[1];
|
||||
|
||||
void *data_rho_before =
|
||||
elvec[1] ? (void *)elvec[1]->GetData() : nullptr;
|
||||
int size_rho_before = elvec[1] ? elvec[1]->Size() : -1;
|
||||
void *data_rho_before = elvec[1] ? (void *)elvec[1]->GetData() : nullptr;
|
||||
int size_rho_before = elvec[1] ? elvec[1]->Size() : -1;
|
||||
|
||||
if (elvec[0]) {
|
||||
elvec[0]->SetSize(dof_v * dim);
|
||||
@@ -42,11 +38,9 @@ namespace mean_field::integrators {
|
||||
r_rho = 0.0;
|
||||
|
||||
mfem::Vector shape_v(dof_v), shape_rho(dof_rho);
|
||||
mfem::DenseMatrix dshape_rho_ref(dof_rho, dim),
|
||||
dshape_rho_phys(dof_rho, dim);
|
||||
mfem::DenseMatrix dshape_rho_ref(dof_rho, dim), dshape_rho_phys(dof_rho, dim);
|
||||
|
||||
const mfem::IntegrationRule *ir =
|
||||
&mfem::IntRules.Get(fe_v->GetGeomType(), 2 * fe_v->GetOrder());
|
||||
const mfem::IntegrationRule *ir = &mfem::IntRules.Get(fe_v->GetGeomType(), 2 * fe_v->GetOrder());
|
||||
|
||||
for (int q = 0; q < ir->GetNPoints(); ++q) {
|
||||
const mfem::IntegrationPoint &ip = ir->IntPoint(q);
|
||||
@@ -113,11 +107,9 @@ namespace mean_field::integrators {
|
||||
*drho_drho = 0.0;
|
||||
|
||||
mfem::Vector shape_v(dof_v), shape_rho(dof_rho);
|
||||
mfem::DenseMatrix dshape_rho_ref(dof_rho, dim),
|
||||
dshape_rho_phys(dof_rho, dim);
|
||||
mfem::DenseMatrix dshape_rho_ref(dof_rho, dim), dshape_rho_phys(dof_rho, dim);
|
||||
|
||||
const mfem::IntegrationRule *ir =
|
||||
&mfem::IntRules.Get(fe_v->GetGeomType(), 2 * fe_v->GetOrder());
|
||||
const mfem::IntegrationRule *ir = &mfem::IntRules.Get(fe_v->GetGeomType(), 2 * fe_v->GetOrder());
|
||||
|
||||
for (int q = 0; q < ir->GetNPoints(); ++q) {
|
||||
const mfem::IntegrationPoint &ip = ir->IntPoint(q);
|
||||
@@ -149,8 +141,7 @@ namespace mean_field::integrators {
|
||||
for (int j = 0; j < dof_v; ++j) {
|
||||
for (int d = 0; d < dim; ++d) {
|
||||
const int col = j + d * dof_v;
|
||||
(*drho_dv)(i, col) -= dshape_rho_phys(i, d) *
|
||||
rho_val * shape_v(j) * weight;
|
||||
(*drho_dv)(i, col) -= dshape_rho_phys(i, d) * rho_val * shape_v(j) * weight;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -163,18 +154,14 @@ namespace mean_field::integrators {
|
||||
grad_psi_dot_v += dshape_rho_phys(i, c) * v_val(c);
|
||||
}
|
||||
for (int j = 0; j < dof_rho; ++j) {
|
||||
(*drho_drho)(i, j) -=
|
||||
grad_psi_dot_v * shape_rho(j) * weight;
|
||||
(*drho_drho)(i, j) -= grad_psi_dot_v * shape_rho(j) * weight;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ContinuityFaceIntegrator::ContinuityFaceIntegrator(
|
||||
const mapping::DomainMapper &map
|
||||
)
|
||||
: m_map(map) {
|
||||
ContinuityFaceIntegrator::ContinuityFaceIntegrator(const mapping::DomainMapper &map) : m_map(map) {
|
||||
}
|
||||
|
||||
void ContinuityFaceIntegrator::AssembleFaceVector(
|
||||
@@ -204,10 +191,10 @@ namespace mean_field::integrators {
|
||||
}
|
||||
mfem::Vector &r_rho = *elvect[1];
|
||||
r_rho.SetSize(dof_rho_minus + dof_rho_plus);
|
||||
r_rho = 0.0;
|
||||
r_rho = 0.0;
|
||||
|
||||
const int attr_minus = Tr.Elem1->Attribute;
|
||||
const int attr_plus = (Tr.Elem2 != nullptr) ? Tr.Elem2->Attribute : -1;
|
||||
const int attr_minus = Tr.Elem1->Attribute;
|
||||
const int attr_plus = (Tr.Elem2 != nullptr) ? Tr.Elem2->Attribute : -1;
|
||||
constexpr int VACUUM_ATTR = 3;
|
||||
|
||||
if (attr_minus == VACUUM_ATTR || attr_plus == VACUUM_ATTR) {
|
||||
@@ -218,29 +205,21 @@ namespace mean_field::integrators {
|
||||
return; // Boundary face,
|
||||
}
|
||||
|
||||
const mfem::Vector &v_dofs =
|
||||
*elfun[0]; // Size: dim * dof_v_minus + dim*dof_v_plus
|
||||
const mfem::Vector &rho_dofs =
|
||||
*elfun[1]; // Size: dof_rho_minus + dof_rho_plus
|
||||
const mfem::Vector &v_dofs = *elfun[0]; // Size: dim * dof_v_minus + dim*dof_v_plus
|
||||
const mfem::Vector &rho_dofs = *elfun[1]; // Size: dof_rho_minus + dof_rho_plus
|
||||
|
||||
// Helpers to auto offset to the correct point in the dof array
|
||||
auto rho_minus_dof = [&](const int i) { return rho_dofs(i); };
|
||||
auto rho_plus_dof = [&](const int i) {
|
||||
return rho_dofs(i + dof_rho_minus);
|
||||
};
|
||||
auto v_minus_dof = [&](const int k, const int c) {
|
||||
return v_dofs(k + c * dof_v_minus);
|
||||
};
|
||||
auto rho_minus_dof = [&](const int i) { return rho_dofs(i); };
|
||||
auto rho_plus_dof = [&](const int i) { return rho_dofs(i + dof_rho_minus); };
|
||||
auto v_minus_dof = [&](const int k, const int c) { return v_dofs(k + c * dof_v_minus); };
|
||||
|
||||
const int p_v = fe_v_minus->GetOrder();
|
||||
const int p_rho = fe_rho_minus->GetOrder();
|
||||
const int int_order = 2 * std::max(p_v, p_rho) + 1;
|
||||
const int p_v = fe_v_minus->GetOrder();
|
||||
const int p_rho = fe_rho_minus->GetOrder();
|
||||
const int int_order = 2 * std::max(p_v, p_rho) + 1;
|
||||
|
||||
const mfem::IntegrationRule *ir =
|
||||
&mfem::IntRules.Get(Tr.GetGeometryType(), int_order);
|
||||
const mfem::IntegrationRule *ir = &mfem::IntRules.Get(Tr.GetGeometryType(), int_order);
|
||||
|
||||
mfem::Vector shape_v_minus(dof_v_minus), shape_rho_minus(dof_rho_minus),
|
||||
shape_rho_plus(dof_rho_plus);
|
||||
mfem::Vector shape_v_minus(dof_v_minus), shape_rho_minus(dof_rho_minus), shape_rho_plus(dof_rho_plus);
|
||||
|
||||
for (int q = 0; q < ir->GetNPoints(); ++q) {
|
||||
const mfem::IntegrationPoint &face_ip = ir->IntPoint(q);
|
||||
@@ -249,8 +228,7 @@ namespace mean_field::integrators {
|
||||
const mfem::IntegrationPoint &ip_minus = Tr.GetElement1IntPoint();
|
||||
const mfem::IntegrationPoint &ip_plus = Tr.GetElement2IntPoint();
|
||||
|
||||
auto [n_unit, ds, v_dot_n_scale] =
|
||||
m_map.GetFaceQuadratureContext(Tr, face_ip);
|
||||
auto [n_unit, ds, v_dot_n_scale] = m_map.GetFaceQuadratureContext(Tr, face_ip);
|
||||
|
||||
fe_v_minus->CalcShape(ip_minus, shape_v_minus);
|
||||
fe_rho_minus->CalcShape(ip_minus, shape_rho_minus);
|
||||
@@ -280,7 +258,7 @@ namespace mean_field::integrators {
|
||||
// Upwind density
|
||||
// I use the convention that the flow is positive when moving from
|
||||
// minus to plus
|
||||
const double rho_up = (u_n >= 0) ? rho_minus_val : rho_plus_val;
|
||||
const double rho_up = (u_n >= 0) ? rho_minus_val : rho_plus_val;
|
||||
|
||||
const double flux_weighted = u_n * rho_up * ds;
|
||||
|
||||
@@ -314,11 +292,10 @@ namespace mean_field::integrators {
|
||||
const int dof_rho_plus = fe_rho_plus->GetDof();
|
||||
const int dim = Tr.GetSpaceDim();
|
||||
|
||||
const int N_v_total = dim * (dof_v_minus + dof_v_plus);
|
||||
const int N_rho_total = dof_rho_minus + dof_rho_plus;
|
||||
const int N_v_total = dim * (dof_v_minus + dof_v_plus);
|
||||
const int N_rho_total = dof_rho_minus + dof_rho_plus;
|
||||
|
||||
auto size_and_zero_mat = [&](mfem::DenseMatrix *mat, const int r_size,
|
||||
const int c_size) {
|
||||
auto size_and_zero_mat = [&](mfem::DenseMatrix *mat, const int r_size, const int c_size) {
|
||||
if (mat) {
|
||||
mat->SetSize(r_size, c_size);
|
||||
*mat = 0.0;
|
||||
@@ -339,16 +316,13 @@ namespace mean_field::integrators {
|
||||
if (!drho_dv && !drho_drho)
|
||||
return;
|
||||
|
||||
const mfem::Vector &v_dofs = *elfun[0];
|
||||
const mfem::Vector &rho_dofs = *elfun[1];
|
||||
const mfem::Vector &v_dofs = *elfun[0];
|
||||
const mfem::Vector &rho_dofs = *elfun[1];
|
||||
|
||||
const int int_order =
|
||||
2 * std::max(fe_v_minus->GetOrder(), fe_rho_minus->GetOrder()) + 1;
|
||||
const mfem::IntegrationRule *ir =
|
||||
&mfem::IntRules.Get(Tr.GetGeometryType(), int_order);
|
||||
const int int_order = 2 * std::max(fe_v_minus->GetOrder(), fe_rho_minus->GetOrder()) + 1;
|
||||
const mfem::IntegrationRule *ir = &mfem::IntRules.Get(Tr.GetGeometryType(), int_order);
|
||||
|
||||
mfem::Vector shape_v_minus(dof_v_minus), shape_rho_minus(dof_rho_minus),
|
||||
shape_rho_plus(dof_rho_plus);
|
||||
mfem::Vector shape_v_minus(dof_v_minus), shape_rho_minus(dof_rho_minus), shape_rho_plus(dof_rho_plus);
|
||||
|
||||
for (int q = 0; q < ir->GetNPoints(); ++q) {
|
||||
const mfem::IntegrationPoint &face_ip = ir->IntPoint(q);
|
||||
@@ -356,15 +330,13 @@ namespace mean_field::integrators {
|
||||
const mfem::IntegrationPoint &ip_minus = Tr.GetElement1IntPoint();
|
||||
const mfem::IntegrationPoint &ip_plus = Tr.GetElement2IntPoint();
|
||||
|
||||
auto [n_unit, ds, v_dot_n_scale] =
|
||||
m_map.GetFaceQuadratureContext(Tr, face_ip);
|
||||
auto [n_unit, ds, v_dot_n_scale] = m_map.GetFaceQuadratureContext(Tr, face_ip);
|
||||
|
||||
fe_v_minus->CalcShape(ip_minus, shape_v_minus);
|
||||
fe_rho_minus->CalcShape(ip_minus, shape_rho_minus);
|
||||
fe_rho_plus->CalcShape(ip_plus, shape_rho_plus);
|
||||
|
||||
const double u_n =
|
||||
compute_u_n(v_dofs, shape_v_minus, n_unit, dof_v_minus, dim);
|
||||
const double u_n = compute_u_n(v_dofs, shape_v_minus, n_unit, dof_v_minus, dim);
|
||||
|
||||
double rho_minus_val = 0.0;
|
||||
for (int i = 0; i < dof_rho_minus; ++i) {
|
||||
@@ -377,7 +349,7 @@ namespace mean_field::integrators {
|
||||
}
|
||||
|
||||
const bool upwind_minus = (u_n >= 0.0);
|
||||
const double rho_up = upwind_minus ? rho_minus_val : rho_plus_val;
|
||||
const double rho_up = upwind_minus ? rho_minus_val : rho_plus_val;
|
||||
|
||||
// (1, 1)
|
||||
if (drho_drho) {
|
||||
@@ -390,8 +362,7 @@ namespace mean_field::integrators {
|
||||
(*drho_drho)(i, ip) += shape_rho_minus(i) * col_w;
|
||||
}
|
||||
for (int j = 0; j < dof_rho_plus; ++j) {
|
||||
(*drho_drho)(dof_rho_minus + j, ip) -=
|
||||
shape_rho_plus(j) * col_w;
|
||||
(*drho_drho)(dof_rho_minus + j, ip) -= shape_rho_plus(j) * col_w;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -399,12 +370,10 @@ namespace mean_field::integrators {
|
||||
const double col_w = u_w * shape_rho_plus(jp);
|
||||
const int col_idx = dof_rho_minus + jp;
|
||||
for (int i = 0; i < dof_rho_minus; ++i) {
|
||||
(*drho_drho)(i, col_idx) +=
|
||||
shape_rho_minus(i) * col_w;
|
||||
(*drho_drho)(i, col_idx) += shape_rho_minus(i) * col_w;
|
||||
}
|
||||
for (int j = 0; j < dof_rho_plus; ++j) {
|
||||
(*drho_drho)(dof_rho_minus + j, col_idx) -=
|
||||
shape_rho_plus(j) * col_w;
|
||||
(*drho_drho)(dof_rho_minus + j, col_idx) -= shape_rho_plus(j) * col_w;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -418,12 +387,10 @@ namespace mean_field::integrators {
|
||||
const int col_idx = k + c * dof_v_minus;
|
||||
const double col_w = n_c_rho_w * shape_v_minus(k);
|
||||
for (int i = 0; i < dof_rho_minus; ++i) {
|
||||
(*drho_dv)(i, col_idx) +=
|
||||
shape_rho_minus(i) * col_w;
|
||||
(*drho_dv)(i, col_idx) += shape_rho_minus(i) * col_w;
|
||||
}
|
||||
for (int j = 0; j < dof_rho_plus; ++j) {
|
||||
(*drho_dv)(dof_rho_minus + j, col_idx) -=
|
||||
shape_rho_plus(j) * col_w;
|
||||
(*drho_dv)(dof_rho_minus + j, col_idx) -= shape_rho_plus(j) * col_w;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -431,12 +398,10 @@ namespace mean_field::integrators {
|
||||
}
|
||||
}
|
||||
|
||||
bool ContinuityFaceIntegrator::skip_face(
|
||||
const mfem::FaceElementTransformations &Tr
|
||||
) {
|
||||
bool ContinuityFaceIntegrator::skip_face(const mfem::FaceElementTransformations &Tr) {
|
||||
constexpr int VACUUM_ATTR = 3;
|
||||
const int attr_minus = Tr.Elem1->Attribute;
|
||||
const int attr_plus = (Tr.Elem2 != nullptr) ? Tr.Elem2->Attribute : -1;
|
||||
const int attr_plus = (Tr.Elem2 != nullptr) ? Tr.Elem2->Attribute : -1;
|
||||
if (attr_minus == VACUUM_ATTR || attr_plus == VACUUM_ATTR) {
|
||||
return true; // No flux contribution for vacuum faces
|
||||
}
|
||||
|
||||
@@ -49,9 +49,7 @@ namespace mean_field::integrators {
|
||||
|
||||
mfem::DenseMatrix dshape_v_ref(dof_v, dim), dshape_v_phys(dof_v, dim);
|
||||
|
||||
const mfem::IntegrationRule *ir = &mfem::IntRules.Get(
|
||||
fe_v->GetGeomType(), 2 * fe_v->GetOrder() + m_quad_boost
|
||||
);
|
||||
const mfem::IntegrationRule *ir = &mfem::IntRules.Get(fe_v->GetGeomType(), 2 * fe_v->GetOrder() + m_quad_boost);
|
||||
|
||||
for (int q = 0; q < ir->GetNPoints(); ++q) {
|
||||
|
||||
@@ -127,8 +125,7 @@ namespace mean_field::integrators {
|
||||
|
||||
mfem::DenseMatrix dshape_v_ref(dof_v, dim), dshape_v_phys(dof_v, dim);
|
||||
|
||||
const mfem::IntegrationRule *ir =
|
||||
&mfem::IntRules.Get(fe_v->GetGeomType(), 2 * fe_v->GetOrder());
|
||||
const mfem::IntegrationRule *ir = &mfem::IntRules.Get(fe_v->GetGeomType(), 2 * fe_v->GetOrder());
|
||||
for (int q = 0; q < ir->GetNPoints(); ++q) {
|
||||
const mfem::IntegrationPoint &ip = ir->IntPoint(q);
|
||||
Tr.SetIntPoint(&ip);
|
||||
@@ -158,8 +155,7 @@ namespace mean_field::integrators {
|
||||
|
||||
val += dshape_v_phys(i, d) * dshape_v_phys(n, c);
|
||||
|
||||
val -= (2.0 / 3.0) * dshape_v_phys(i, c) *
|
||||
dshape_v_phys(n, d);
|
||||
val -= (2.0 / 3.0) * dshape_v_phys(i, c) * dshape_v_phys(n, d);
|
||||
(*dv_dv)(row, col) += mu_w * val;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -168,10 +168,7 @@ namespace mean_field::mapping {
|
||||
|
||||
const double map_determinant = map_jacobian.Det();
|
||||
|
||||
MFEM_VERIFY(
|
||||
map_determinant > 0.0,
|
||||
"Domain mapping has a non-positive Jacobian determinant."
|
||||
);
|
||||
MFEM_VERIFY(map_determinant > 0.0, "Domain mapping has a non-positive Jacobian determinant.");
|
||||
|
||||
mfem::MultAtB(map_jacobian, map_jacobian, matrix);
|
||||
matrix *= 1.0 / std::abs(map_determinant);
|
||||
|
||||
@@ -27,26 +27,17 @@ namespace {
|
||||
} // namespace
|
||||
|
||||
namespace mean_field::mapping::compactification {
|
||||
KelvinCompactification::KelvinCompactification(
|
||||
options::KelvinCompactificationOptions options
|
||||
)
|
||||
KelvinCompactification::KelvinCompactification(options::KelvinCompactificationOptions options)
|
||||
: m_options(options) {
|
||||
if (!std::isfinite(m_options.r_star_ref) ||
|
||||
!std::isfinite(m_options.r_inf_ref)) {
|
||||
throw std::invalid_argument(
|
||||
"Kelvin compactification radii must be finite."
|
||||
);
|
||||
if (!std::isfinite(m_options.r_star_ref) || !std::isfinite(m_options.r_inf_ref)) {
|
||||
throw std::invalid_argument("Kelvin compactification radii must be finite.");
|
||||
}
|
||||
|
||||
if (m_options.r_star_ref <= 0.0 ||
|
||||
m_options.r_inf_ref <= m_options.r_star_ref) {
|
||||
throw std::invalid_argument(
|
||||
"Kelvin compactification requires 0 < r_star_ref < r_inf_ref."
|
||||
);
|
||||
if (m_options.r_star_ref <= 0.0 || m_options.r_inf_ref <= m_options.r_star_ref) {
|
||||
throw std::invalid_argument("Kelvin compactification requires 0 < r_star_ref < r_inf_ref.");
|
||||
}
|
||||
|
||||
if (!std::isfinite(m_options.coordinate_tolerance) ||
|
||||
m_options.coordinate_tolerance < 0.0 ||
|
||||
if (!std::isfinite(m_options.coordinate_tolerance) || m_options.coordinate_tolerance < 0.0 ||
|
||||
m_options.coordinate_tolerance >= 1.0) {
|
||||
throw std::invalid_argument(
|
||||
"Kelvin compactification coordinate tolerance must be finite "
|
||||
@@ -65,8 +56,7 @@ namespace mean_field::mapping::compactification {
|
||||
|
||||
const double tolerance = m_options.coordinate_tolerance;
|
||||
|
||||
if (compactification_coordinate < -tolerance ||
|
||||
compactification_coordinate > 1.0 + tolerance) {
|
||||
if (compactification_coordinate < -tolerance || compactification_coordinate > 1.0 + tolerance) {
|
||||
return MappingStatus::outside_reference_domain;
|
||||
}
|
||||
|
||||
@@ -78,26 +68,22 @@ namespace mean_field::mapping::compactification {
|
||||
return MappingStatus::at_compactified_infinity;
|
||||
}
|
||||
|
||||
const double radial_extent = m_options.r_inf_ref - m_options.r_star_ref;
|
||||
const double computational_radius =
|
||||
m_options.r_star_ref + coordinate * radial_extent;
|
||||
const double radial_extent = m_options.r_inf_ref - m_options.r_star_ref;
|
||||
const double computational_radius = m_options.r_star_ref + coordinate * radial_extent;
|
||||
|
||||
if (!std::isfinite(computational_radius) ||
|
||||
computational_radius <= 0.0) {
|
||||
if (!std::isfinite(computational_radius) || computational_radius <= 0.0) {
|
||||
return MappingStatus::invalid_reference_radius;
|
||||
}
|
||||
|
||||
const double one_minus_coordinate = 1.0 - coordinate;
|
||||
const double denominator = computational_radius * one_minus_coordinate;
|
||||
const double denominator = computational_radius * one_minus_coordinate;
|
||||
|
||||
if (!std::isfinite(denominator) || denominator <= 0.0) {
|
||||
return MappingStatus::non_finite_result;
|
||||
}
|
||||
|
||||
const double scale = m_options.r_star_ref / denominator;
|
||||
const double scale_derivative =
|
||||
scale *
|
||||
(1.0 / one_minus_coordinate - radial_extent / computational_radius);
|
||||
const double scale = m_options.r_star_ref / denominator;
|
||||
const double scale_derivative = scale * (1.0 / one_minus_coordinate - radial_extent / computational_radius);
|
||||
|
||||
if (!std::isfinite(scale) || !std::isfinite(scale_derivative)) {
|
||||
return MappingStatus::non_finite_result;
|
||||
@@ -122,21 +108,18 @@ namespace mean_field::mapping::compactification {
|
||||
return MappingStatus::invalid_dimension;
|
||||
}
|
||||
|
||||
if (input.displacement_jacobian.Height() != dimension ||
|
||||
input.displacement_jacobian.Width() != dimension) {
|
||||
if (input.displacement_jacobian.Height() != dimension || input.displacement_jacobian.Width() != dimension) {
|
||||
return MappingStatus::invalid_dimension;
|
||||
}
|
||||
|
||||
if (!vector_is_finite(input.reference_position) ||
|
||||
!vector_is_finite(input.displaced_position) ||
|
||||
if (!vector_is_finite(input.reference_position) || !vector_is_finite(input.displaced_position) ||
|
||||
!vector_is_finite(input.compactification_coordinate_gradient) ||
|
||||
!matrix_is_finite(input.displacement_jacobian)) {
|
||||
return MappingStatus::non_finite_input;
|
||||
}
|
||||
|
||||
RadialFactors factors;
|
||||
const MappingStatus factor_status =
|
||||
ComputeRadialFactors(input.compactification_coordinate, factors);
|
||||
const MappingStatus factor_status = ComputeRadialFactors(input.compactification_coordinate, factors);
|
||||
if (factor_status != MappingStatus::valid)
|
||||
return factor_status;
|
||||
|
||||
@@ -144,21 +127,16 @@ namespace mean_field::mapping::compactification {
|
||||
result.mapping_jacobian.SetSize(dimension, dimension);
|
||||
|
||||
for (int i = 0; i < dimension; ++i) {
|
||||
result.physical_position(i) =
|
||||
factors.scale * input.displaced_position(i);
|
||||
result.physical_position(i) = factors.scale * input.displaced_position(i);
|
||||
|
||||
for (int j = 0; j < dimension; ++j) {
|
||||
const double scale_gradient =
|
||||
factors.scale_derivative *
|
||||
input.compactification_coordinate_gradient(j);
|
||||
const double scale_gradient = factors.scale_derivative * input.compactification_coordinate_gradient(j);
|
||||
result.mapping_jacobian(i, j) =
|
||||
factors.scale * input.displacement_jacobian(i, j) +
|
||||
input.displaced_position(i) * scale_gradient;
|
||||
factors.scale * input.displacement_jacobian(i, j) + input.displaced_position(i) * scale_gradient;
|
||||
}
|
||||
}
|
||||
|
||||
if (!vector_is_finite(result.physical_position) ||
|
||||
!matrix_is_finite(result.mapping_jacobian)) {
|
||||
if (!vector_is_finite(result.physical_position) || !matrix_is_finite(result.mapping_jacobian)) {
|
||||
return MappingStatus::non_finite_result;
|
||||
}
|
||||
|
||||
@@ -185,13 +163,11 @@ namespace mean_field::mapping::compactification {
|
||||
return MappingStatus::invalid_dimension;
|
||||
}
|
||||
|
||||
if (input.displacement_jacobian.Height() != dimension ||
|
||||
input.displacement_jacobian.Width() != dimension) {
|
||||
if (input.displacement_jacobian.Height() != dimension || input.displacement_jacobian.Width() != dimension) {
|
||||
return MappingStatus::invalid_dimension;
|
||||
}
|
||||
|
||||
if (result.physical_position.Size() != dimension ||
|
||||
result.mapping_jacobian.Height() != dimension ||
|
||||
if (result.physical_position.Size() != dimension || result.mapping_jacobian.Height() != dimension ||
|
||||
result.mapping_jacobian.Width() != dimension) {
|
||||
return MappingStatus::invalid_dimension;
|
||||
}
|
||||
@@ -202,23 +178,20 @@ namespace mean_field::mapping::compactification {
|
||||
return MappingStatus::invalid_dimension;
|
||||
}
|
||||
|
||||
if (!vector_is_finite(input.reference_position) ||
|
||||
!vector_is_finite(input.displaced_position) ||
|
||||
if (!vector_is_finite(input.reference_position) || !vector_is_finite(input.displaced_position) ||
|
||||
!vector_is_finite(input.compactification_coordinate_gradient) ||
|
||||
!matrix_is_finite(input.displacement_jacobian)) {
|
||||
return MappingStatus::non_finite_input;
|
||||
}
|
||||
|
||||
if (!vector_is_finite(result.physical_position) ||
|
||||
!matrix_is_finite(result.mapping_jacobian) ||
|
||||
if (!vector_is_finite(result.physical_position) || !matrix_is_finite(result.mapping_jacobian) ||
|
||||
!vector_is_finite(direction.displaced_position_variation) ||
|
||||
!matrix_is_finite(direction.displacement_jacobian_variation)) {
|
||||
return MappingStatus::non_finite_input;
|
||||
}
|
||||
|
||||
RadialFactors factors;
|
||||
const MappingStatus factor_status =
|
||||
ComputeRadialFactors(input.compactification_coordinate, factors);
|
||||
const MappingStatus factor_status = ComputeRadialFactors(input.compactification_coordinate, factors);
|
||||
if (factor_status != MappingStatus::valid)
|
||||
return factor_status;
|
||||
|
||||
@@ -226,16 +199,12 @@ namespace mean_field::mapping::compactification {
|
||||
variation.mapping_jacobian_variation.SetSize(dimension, dimension);
|
||||
|
||||
for (int i = 0; i < dimension; ++i) {
|
||||
variation.physical_position_variation(i) =
|
||||
factors.scale * direction.displaced_position_variation(i);
|
||||
variation.physical_position_variation(i) = factors.scale * direction.displaced_position_variation(i);
|
||||
|
||||
for (int j = 0; j < dimension; ++j) {
|
||||
const double scale_gradient =
|
||||
factors.scale_derivative *
|
||||
input.compactification_coordinate_gradient(j);
|
||||
const double scale_gradient = factors.scale_derivative * input.compactification_coordinate_gradient(j);
|
||||
variation.mapping_jacobian_variation(i, j) =
|
||||
factors.scale *
|
||||
direction.displacement_jacobian_variation(i, j) +
|
||||
factors.scale * direction.displacement_jacobian_variation(i, j) +
|
||||
direction.displaced_position_variation(i) * scale_gradient;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,10 +15,7 @@ namespace {
|
||||
domain_mapper.ComputeJacobian(transformation, map_jacobian);
|
||||
|
||||
const double map_determinant = map_jacobian.Det();
|
||||
MFEM_VERIFY(
|
||||
map_determinant > 0.0,
|
||||
"Domain mapping has a non-positive Jacobian determinant."
|
||||
);
|
||||
MFEM_VERIFY(map_determinant > 0.0, "Domain mapping has a non-positive Jacobian determinant.");
|
||||
return map_determinant;
|
||||
}
|
||||
} // namespace
|
||||
@@ -32,8 +29,7 @@ namespace mean_field::mapping {
|
||||
m_r_star_ref(r_star_ref),
|
||||
m_r_inf_ref(r_inf_ref) {
|
||||
InitAllScratchSpaces();
|
||||
CalcIsIdentity() ? m_displacement_is_identity = true
|
||||
: m_displacement_is_identity = false;
|
||||
CalcIsIdentity() ? m_displacement_is_identity = true : m_displacement_is_identity = false;
|
||||
}
|
||||
|
||||
DomainMapper::DomainMapper(
|
||||
@@ -46,8 +42,7 @@ namespace mean_field::mapping {
|
||||
m_r_star_ref(r_star_ref),
|
||||
m_r_inf_ref(r_inf_ref) {
|
||||
InitAllScratchSpaces();
|
||||
CalcIsIdentity() ? m_displacement_is_identity = true
|
||||
: m_displacement_is_identity = false;
|
||||
CalcIsIdentity() ? m_displacement_is_identity = true : m_displacement_is_identity = false;
|
||||
}
|
||||
|
||||
bool DomainMapper::is_vacuum(const mfem::ElementTransformation &T) const {
|
||||
@@ -76,14 +71,12 @@ namespace mean_field::mapping {
|
||||
m_d = &d;
|
||||
InvalidateCache();
|
||||
|
||||
CalcIsIdentity() ? m_displacement_is_identity = true
|
||||
: m_displacement_is_identity = false;
|
||||
CalcIsIdentity() ? m_displacement_is_identity = true : m_displacement_is_identity = false;
|
||||
}
|
||||
|
||||
bool DomainMapper::HasCompactification() const noexcept {
|
||||
return std::isfinite(m_r_star_ref) && std::isfinite(m_r_inf_ref) &&
|
||||
m_r_star_ref > 0.0 && m_r_inf_ref > m_r_star_ref &&
|
||||
m_xi_clamp > 0.0 && m_xi_clamp < 1.0;
|
||||
return std::isfinite(m_r_star_ref) && std::isfinite(m_r_inf_ref) && m_r_star_ref > 0.0 &&
|
||||
m_r_inf_ref > m_r_star_ref && m_xi_clamp > 0.0 && m_xi_clamp < 1.0;
|
||||
}
|
||||
|
||||
bool DomainMapper::HasDisplacementField() const noexcept {
|
||||
@@ -95,10 +88,9 @@ namespace mean_field::mapping {
|
||||
return true;
|
||||
}
|
||||
|
||||
const int local_identity = m_d->Normlinf() == 0.0 ? 1 : 0;
|
||||
const int local_identity = m_d->Normlinf() == 0.0 ? 1 : 0;
|
||||
|
||||
const auto *parallel_displacement =
|
||||
dynamic_cast<const mfem::ParGridFunction *>(m_d);
|
||||
const auto *parallel_displacement = dynamic_cast<const mfem::ParGridFunction *>(m_d);
|
||||
|
||||
if (parallel_displacement == nullptr) {
|
||||
return local_identity == 1;
|
||||
@@ -106,8 +98,7 @@ namespace mean_field::mapping {
|
||||
|
||||
int global_identity = 0;
|
||||
MPI_Allreduce(
|
||||
&local_identity, &global_identity, 1, MPI_INT, MPI_MIN,
|
||||
parallel_displacement->ParFESpace()->GetComm()
|
||||
&local_identity, &global_identity, 1, MPI_INT, MPI_MIN, parallel_displacement->ParFESpace()->GetComm()
|
||||
);
|
||||
|
||||
return global_identity == 1;
|
||||
@@ -116,8 +107,7 @@ namespace mean_field::mapping {
|
||||
void DomainMapper::ResetDisplacement() {
|
||||
m_d = nullptr;
|
||||
InvalidateCache();
|
||||
CalcIsIdentity() ? m_displacement_is_identity = true
|
||||
: m_displacement_is_identity = false;
|
||||
CalcIsIdentity() ? m_displacement_is_identity = true : m_displacement_is_identity = false;
|
||||
}
|
||||
|
||||
void DomainMapper::ComputeJacobian(
|
||||
@@ -223,11 +213,7 @@ namespace mean_field::mapping {
|
||||
mfem::Vector n_unit(dim);
|
||||
n_unit = n_raw;
|
||||
n_unit /= n_raw_mag;
|
||||
return FaceQuadratureContext{
|
||||
.normal = n_unit,
|
||||
.ds = ip.weight * n_raw_mag,
|
||||
.v_dot_n_scale = 1.0
|
||||
};
|
||||
return FaceQuadratureContext{.normal = n_unit, .ds = ip.weight * n_raw_mag, .v_dot_n_scale = 1.0};
|
||||
}
|
||||
|
||||
// Nanson's Formula
|
||||
@@ -253,9 +239,7 @@ namespace mean_field::mapping {
|
||||
const double n_raw_mag = n_raw.Norml2();
|
||||
|
||||
return FaceQuadratureContext{
|
||||
.normal = n_unit,
|
||||
.ds = ip.weight * n_raw_mag,
|
||||
.v_dot_n_scale = n_phys_mag / n_raw_mag
|
||||
.normal = n_unit, .ds = ip.weight * n_raw_mag, .v_dot_n_scale = n_phys_mag / n_raw_mag
|
||||
};
|
||||
}
|
||||
|
||||
@@ -297,15 +281,11 @@ namespace mean_field::mapping {
|
||||
const mfem::Vector &reference_flux,
|
||||
mfem::Vector &physical_flux
|
||||
) const {
|
||||
MFEM_VERIFY(
|
||||
reference_flux.Size() == m_dim,
|
||||
"The reference H(div) flux has the wrong dimension."
|
||||
);
|
||||
MFEM_VERIFY(reference_flux.Size() == m_dim, "The reference H(div) flux has the wrong dimension.");
|
||||
|
||||
mfem::DenseMatrix map_jacobian(m_dim, m_dim);
|
||||
const double map_determinant = get_positive_map_jacobian(
|
||||
*this, transformation, integration_point, map_jacobian
|
||||
);
|
||||
const double map_determinant =
|
||||
get_positive_map_jacobian(*this, transformation, integration_point, map_jacobian);
|
||||
|
||||
mfem::Vector mapped_flux(m_dim);
|
||||
map_jacobian.Mult(reference_flux, mapped_flux);
|
||||
@@ -320,15 +300,11 @@ namespace mean_field::mapping {
|
||||
const mfem::Vector &physical_flux,
|
||||
mfem::Vector &reference_flux
|
||||
) const {
|
||||
MFEM_VERIFY(
|
||||
physical_flux.Size() == m_dim,
|
||||
"The physical flux has the wrong dimension."
|
||||
);
|
||||
MFEM_VERIFY(physical_flux.Size() == m_dim, "The physical flux has the wrong dimension.");
|
||||
|
||||
mfem::DenseMatrix map_jacobian(m_dim, m_dim);
|
||||
const double map_determinant = get_positive_map_jacobian(
|
||||
*this, transformation, integration_point, map_jacobian
|
||||
);
|
||||
const double map_determinant =
|
||||
get_positive_map_jacobian(*this, transformation, integration_point, map_jacobian);
|
||||
|
||||
mfem::DenseMatrix inverse_map_jacobian(m_dim, m_dim);
|
||||
mfem::CalcInverse(map_jacobian, inverse_map_jacobian);
|
||||
@@ -346,15 +322,10 @@ namespace mean_field::mapping {
|
||||
const mfem::Vector &reference_gradient,
|
||||
mfem::Vector &physical_gradient
|
||||
) const {
|
||||
MFEM_VERIFY(
|
||||
reference_gradient.Size() == m_dim,
|
||||
"The reference gradient has the wrong dimension."
|
||||
);
|
||||
MFEM_VERIFY(reference_gradient.Size() == m_dim, "The reference gradient has the wrong dimension.");
|
||||
|
||||
mfem::DenseMatrix map_jacobian(m_dim, m_dim);
|
||||
get_positive_map_jacobian(
|
||||
*this, transformation, integration_point, map_jacobian
|
||||
);
|
||||
get_positive_map_jacobian(*this, transformation, integration_point, map_jacobian);
|
||||
|
||||
mfem::DenseMatrix inverse_map_jacobian(m_dim, m_dim);
|
||||
mfem::CalcInverse(map_jacobian, inverse_map_jacobian);
|
||||
@@ -382,8 +353,7 @@ namespace mean_field::mapping {
|
||||
}
|
||||
|
||||
double DomainMapper::GetCacheHitRate() const {
|
||||
return (static_cast<double>(m_cache_hits)) /
|
||||
static_cast<double>(m_cache_misses + m_cache_hits);
|
||||
return (static_cast<double>(m_cache_hits)) / static_cast<double>(m_cache_misses + m_cache_hits);
|
||||
}
|
||||
|
||||
void DomainMapper::ResetCacheStats() const {
|
||||
@@ -404,9 +374,9 @@ namespace mean_field::mapping {
|
||||
const mfem::Vector &x_ref,
|
||||
mfem::Vector &x_phys
|
||||
) const {
|
||||
const double r_ref = x_ref.Norml2();
|
||||
double xi = (r_ref - m_r_star_ref) / (m_r_inf_ref - m_r_star_ref);
|
||||
xi = std::clamp(xi, 0.0, m_xi_clamp);
|
||||
const double r_ref = x_ref.Norml2();
|
||||
double xi = (r_ref - m_r_star_ref) / (m_r_inf_ref - m_r_star_ref);
|
||||
xi = std::clamp(xi, 0.0, m_xi_clamp);
|
||||
const double factor = m_r_star_ref / (r_ref * (1 - xi));
|
||||
x_phys *= factor;
|
||||
}
|
||||
@@ -428,8 +398,7 @@ namespace mean_field::mapping {
|
||||
const double k = m_r_star_ref / (r_ref * denom);
|
||||
|
||||
const double dk_dr =
|
||||
m_r_star_ref * ((1.0 / (delta_R * r_ref * denom * denom)) -
|
||||
(1.0 / (r_ref * r_ref * denom)));
|
||||
m_r_star_ref * ((1.0 / (delta_R * r_ref * denom * denom)) - (1.0 / (r_ref * r_ref * denom)));
|
||||
|
||||
J.SetSize(m_dim, m_dim);
|
||||
const double outer_factor = dk_dr / r_ref;
|
||||
@@ -445,14 +414,11 @@ namespace mean_field::mapping {
|
||||
m_cached_elem_id = -1;
|
||||
}
|
||||
|
||||
void DomainMapper::UpdateElementCache(
|
||||
const mfem::ElementTransformation &T
|
||||
) const {
|
||||
void DomainMapper::UpdateElementCache(const mfem::ElementTransformation &T) const {
|
||||
if (!HasDisplacementField())
|
||||
return;
|
||||
|
||||
if (T.ElementNo != m_cached_elem_id ||
|
||||
T.ElementType != m_cached_elem_type) {
|
||||
if (T.ElementNo != m_cached_elem_id || T.ElementType != m_cached_elem_type) {
|
||||
m_cache_misses++;
|
||||
m_cached_elem_id = T.ElementNo;
|
||||
m_cached_elem_type = T.ElementType;
|
||||
|
||||
@@ -39,9 +39,7 @@ namespace mean_field::mapping {
|
||||
: m_element(&element),
|
||||
m_dofs(dofs) {
|
||||
if (element.GetRangeType() != mfem::FiniteElement::SCALAR) {
|
||||
throw std::invalid_argument(
|
||||
"Compactification coordinate requires a scalar finite element."
|
||||
);
|
||||
throw std::invalid_argument("Compactification coordinate requires a scalar finite element.");
|
||||
}
|
||||
|
||||
if (element.GetMapType() != mfem::FiniteElement::VALUE) {
|
||||
@@ -75,8 +73,7 @@ namespace mean_field::mapping {
|
||||
}
|
||||
}
|
||||
|
||||
const mfem::FiniteElement &
|
||||
ElementCompactificationData::GetElement() const noexcept {
|
||||
const mfem::FiniteElement &ElementCompactificationData::GetElement() const noexcept {
|
||||
return *m_element;
|
||||
}
|
||||
|
||||
@@ -102,8 +99,7 @@ namespace mean_field::mapping {
|
||||
"The displacement element must have at least one degree of "
|
||||
"freedom."
|
||||
);
|
||||
if (displacement_dofs.Size() <= 0 ||
|
||||
displacement_dofs.Size() % dof_count != 0) {
|
||||
if (displacement_dofs.Size() <= 0 || displacement_dofs.Size() % dof_count != 0) {
|
||||
throw std::invalid_argument(
|
||||
"The displacement vector size must be a positive multiple of "
|
||||
"the "
|
||||
@@ -117,31 +113,25 @@ namespace mean_field::mapping {
|
||||
if (ordering == mfem::Ordering::byNODES) {
|
||||
for (int component = 0; component < m_dimension; ++component) {
|
||||
for (int i = 0; i < dof_count; ++i) {
|
||||
m_dof_matrix(i, component) =
|
||||
displacement_dofs(i + component * dof_count);
|
||||
m_dof_matrix(i, component) = displacement_dofs(i + component * dof_count);
|
||||
}
|
||||
}
|
||||
} else if (ordering == mfem::Ordering::byVDIM) {
|
||||
for (int i = 0; i < dof_count; ++i) {
|
||||
for (int component = 0; component < m_dimension; ++component) {
|
||||
m_dof_matrix(i, component) =
|
||||
displacement_dofs(component + i * m_dimension);
|
||||
m_dof_matrix(i, component) = displacement_dofs(component + i * m_dimension);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw std::invalid_argument(
|
||||
"Unsupported MFEM displacement ordering."
|
||||
);
|
||||
throw std::invalid_argument("Unsupported MFEM displacement ordering.");
|
||||
}
|
||||
}
|
||||
|
||||
const mfem::FiniteElement &
|
||||
ElementDisplacementData::GetElement() const noexcept {
|
||||
const mfem::FiniteElement &ElementDisplacementData::GetElement() const noexcept {
|
||||
return *m_element;
|
||||
}
|
||||
|
||||
const mfem::DenseMatrix &
|
||||
ElementDisplacementData::GetDofMatrix() const noexcept {
|
||||
const mfem::DenseMatrix &ElementDisplacementData::GetDofMatrix() const noexcept {
|
||||
return m_dof_matrix;
|
||||
}
|
||||
|
||||
@@ -161,9 +151,7 @@ namespace mean_field::mapping {
|
||||
const mfem::FiniteElement &element,
|
||||
const mfem::Vector &displacement_dofs
|
||||
) {
|
||||
return ElementDisplacementData(
|
||||
element, displacement_dofs, mfem::Ordering::byNODES
|
||||
);
|
||||
return ElementDisplacementData(element, displacement_dofs, mfem::Ordering::byNODES);
|
||||
}
|
||||
|
||||
DomainMapperStateless::Workspace::Workspace(const int dimension) {
|
||||
@@ -172,9 +160,7 @@ namespace mean_field::mapping {
|
||||
|
||||
void DomainMapperStateless::Workspace::SetDimension(const int dimension) {
|
||||
if (dimension <= 0) {
|
||||
throw std::invalid_argument(
|
||||
"Domain mapping workspace dimension must be positive."
|
||||
);
|
||||
throw std::invalid_argument("Domain mapping workspace dimension must be positive.");
|
||||
}
|
||||
|
||||
m_dimension = dimension;
|
||||
@@ -197,9 +183,7 @@ namespace mean_field::mapping {
|
||||
m_exterior_result.mapping_jacobian.SetSize(dimension, dimension);
|
||||
|
||||
m_exterior_variation.physical_position_variation.SetSize(dimension);
|
||||
m_exterior_variation.mapping_jacobian_variation.SetSize(
|
||||
dimension, dimension
|
||||
);
|
||||
m_exterior_variation.mapping_jacobian_variation.SetSize(dimension, dimension);
|
||||
}
|
||||
|
||||
int DomainMapperStateless::Workspace::GetDimension() const noexcept {
|
||||
@@ -213,22 +197,15 @@ namespace mean_field::mapping {
|
||||
: m_options(options),
|
||||
m_exterior_map(std::move(exterior_map)) {
|
||||
if (m_options.dimension <= 0)
|
||||
throw std::invalid_argument(
|
||||
"The domain-mapping dimension must be positive."
|
||||
);
|
||||
throw std::invalid_argument("The domain-mapping dimension must be positive.");
|
||||
if (m_options.vacuum_element_attribute <= 0)
|
||||
throw std::invalid_argument(
|
||||
"The vacuum element attribute must be positive."
|
||||
);
|
||||
throw std::invalid_argument("The vacuum element attribute must be positive.");
|
||||
if (!m_exterior_map)
|
||||
throw std::invalid_argument(
|
||||
"DomainMapperStateless requires an exterior-domain mapping."
|
||||
);
|
||||
throw std::invalid_argument("DomainMapperStateless requires an exterior-domain mapping.");
|
||||
}
|
||||
|
||||
bool DomainMapperStateless::IsCompactifiedElement(
|
||||
const mfem::ElementTransformation &transformation
|
||||
) const noexcept {
|
||||
bool
|
||||
DomainMapperStateless::IsCompactifiedElement(const mfem::ElementTransformation &transformation) const noexcept {
|
||||
return transformation.Attribute == m_options.vacuum_element_attribute;
|
||||
}
|
||||
|
||||
@@ -240,17 +217,13 @@ namespace mean_field::mapping {
|
||||
return m_options.vacuum_element_attribute;
|
||||
}
|
||||
|
||||
const compactification::ExteriorDomainMap &
|
||||
DomainMapperStateless::GetExteriorMap() const noexcept {
|
||||
const compactification::ExteriorDomainMap &DomainMapperStateless::GetExteriorMap() const noexcept {
|
||||
return *m_exterior_map;
|
||||
}
|
||||
|
||||
void DomainMapperStateless::ValidateElementData(
|
||||
const ElementMappingData &element_data
|
||||
) const {
|
||||
const ElementDisplacementData &displacement = element_data.displacement;
|
||||
const ElementCompactificationData &compactification =
|
||||
element_data.compactification;
|
||||
void DomainMapperStateless::ValidateElementData(const ElementMappingData &element_data) const {
|
||||
const ElementDisplacementData &displacement = element_data.displacement;
|
||||
const ElementCompactificationData &compactification = element_data.compactification;
|
||||
|
||||
if (displacement.GetDimension() != m_options.dimension) {
|
||||
throw std::invalid_argument(
|
||||
@@ -275,8 +248,7 @@ namespace mean_field::mapping {
|
||||
);
|
||||
}
|
||||
|
||||
if (displacement.GetElement().GetGeomType() !=
|
||||
compactification.GetElement().GetGeomType()) {
|
||||
if (displacement.GetElement().GetGeomType() != compactification.GetElement().GetGeomType()) {
|
||||
throw std::invalid_argument(
|
||||
"Displacement and compactification finite elements have "
|
||||
"different "
|
||||
@@ -284,31 +256,25 @@ namespace mean_field::mapping {
|
||||
);
|
||||
}
|
||||
|
||||
if (compactification.GetElement().GetRangeType() !=
|
||||
mfem::FiniteElement::SCALAR) {
|
||||
throw std::invalid_argument(
|
||||
"Compactification coordinate requires a scalar finite element."
|
||||
);
|
||||
if (compactification.GetElement().GetRangeType() != mfem::FiniteElement::SCALAR) {
|
||||
throw std::invalid_argument("Compactification coordinate requires a scalar finite element.");
|
||||
}
|
||||
|
||||
if (compactification.GetElement().GetMapType() !=
|
||||
mfem::FiniteElement::VALUE) {
|
||||
if (compactification.GetElement().GetMapType() != mfem::FiniteElement::VALUE) {
|
||||
throw std::invalid_argument(
|
||||
"Compactification coordinate requires a value-mapped finite "
|
||||
"element."
|
||||
);
|
||||
}
|
||||
|
||||
if (compactification.GetElement().GetDerivType() !=
|
||||
mfem::FiniteElement::GRAD) {
|
||||
if (compactification.GetElement().GetDerivType() != mfem::FiniteElement::GRAD) {
|
||||
throw std::invalid_argument(
|
||||
"Compactification coordinate finite element does not provide a "
|
||||
"gradient."
|
||||
);
|
||||
}
|
||||
|
||||
if (compactification.GetDofCount() !=
|
||||
compactification.GetElement().GetDof()) {
|
||||
if (compactification.GetDofCount() != compactification.GetElement().GetDof()) {
|
||||
throw std::invalid_argument(
|
||||
"Compactification coordinate DOF count does not match its "
|
||||
"finite "
|
||||
@@ -328,8 +294,7 @@ namespace mean_field::mapping {
|
||||
const mfem::Vector &dofs = compactification.GetDofs();
|
||||
const int dof_count = element.GetDof();
|
||||
|
||||
if (workspace.GetDimension() != m_options.dimension ||
|
||||
transformation.GetSpaceDim() != m_options.dimension ||
|
||||
if (workspace.GetDimension() != m_options.dimension || transformation.GetSpaceDim() != m_options.dimension ||
|
||||
element.GetDim() != m_options.dimension) {
|
||||
return MappingStatus::invalid_dimension;
|
||||
}
|
||||
@@ -346,22 +311,14 @@ namespace mean_field::mapping {
|
||||
transformation.SetIntPoint(&integration_point);
|
||||
|
||||
workspace.m_compactification_shape.SetSize(dof_count);
|
||||
workspace.m_compactification_dshape.SetSize(
|
||||
dof_count, m_options.dimension
|
||||
);
|
||||
workspace.m_compactification_dshape.SetSize(dof_count, m_options.dimension);
|
||||
|
||||
element.CalcShape(
|
||||
integration_point, workspace.m_compactification_shape
|
||||
);
|
||||
element.CalcPhysDShape(
|
||||
transformation, workspace.m_compactification_dshape
|
||||
);
|
||||
element.CalcShape(integration_point, workspace.m_compactification_shape);
|
||||
element.CalcPhysDShape(transformation, workspace.m_compactification_dshape);
|
||||
|
||||
point_data.coordinate = dofs * workspace.m_compactification_shape;
|
||||
point_data.coordinate_gradient.SetSize(m_options.dimension);
|
||||
workspace.m_compactification_dshape.MultTranspose(
|
||||
dofs, point_data.coordinate_gradient
|
||||
);
|
||||
workspace.m_compactification_dshape.MultTranspose(dofs, point_data.coordinate_gradient);
|
||||
|
||||
if (!std::isfinite(point_data.coordinate)) {
|
||||
return MappingStatus::non_finite_result;
|
||||
@@ -411,15 +368,10 @@ namespace mean_field::mapping {
|
||||
ValidateElementData(element_data);
|
||||
|
||||
if (workspace.GetDimension() != m_options.dimension)
|
||||
throw std::invalid_argument(
|
||||
"The mapping workspace has the wrong dimension."
|
||||
);
|
||||
throw std::invalid_argument("The mapping workspace has the wrong dimension.");
|
||||
if (transformation.GetSpaceDim() != m_options.dimension)
|
||||
throw std::invalid_argument(
|
||||
"The element transformation has the wrong spatial dimension."
|
||||
);
|
||||
if (transformation.GetGeometryType() !=
|
||||
element_data.displacement.GetElement().GetGeomType())
|
||||
throw std::invalid_argument("The element transformation has the wrong spatial dimension.");
|
||||
if (transformation.GetGeometryType() != element_data.displacement.GetElement().GetGeomType())
|
||||
throw std::invalid_argument(
|
||||
"The element transformation geometry does not match the "
|
||||
"supplied "
|
||||
@@ -432,12 +384,11 @@ namespace mean_field::mapping {
|
||||
transformation.Transform(integration_point, context.reference_position);
|
||||
|
||||
EvaluateField(
|
||||
element_data.displacement, transformation, integration_point,
|
||||
workspace, workspace.m_field_value, workspace.m_field_jacobian
|
||||
element_data.displacement, transformation, integration_point, workspace, workspace.m_field_value,
|
||||
workspace.m_field_jacobian
|
||||
);
|
||||
|
||||
if (!vector_is_finite(context.reference_position) ||
|
||||
!vector_is_finite(workspace.m_field_value) ||
|
||||
if (!vector_is_finite(context.reference_position) || !vector_is_finite(workspace.m_field_value) ||
|
||||
!matrix_is_finite(workspace.m_field_jacobian)) {
|
||||
return MappingStatus::non_finite_input;
|
||||
}
|
||||
@@ -446,9 +397,7 @@ namespace mean_field::mapping {
|
||||
context.displaced_position = context.reference_position;
|
||||
context.displaced_position += workspace.m_field_value;
|
||||
|
||||
context.displacement_jacobian.SetSize(
|
||||
m_options.dimension, m_options.dimension
|
||||
);
|
||||
context.displacement_jacobian.SetSize(m_options.dimension, m_options.dimension);
|
||||
context.displacement_jacobian = workspace.m_field_jacobian;
|
||||
for (int i = 0; i < m_options.dimension; ++i)
|
||||
context.displacement_jacobian(i, i) += 1.0;
|
||||
@@ -456,43 +405,34 @@ namespace mean_field::mapping {
|
||||
context.compactified = IsCompactifiedElement(transformation);
|
||||
|
||||
if (context.compactified) {
|
||||
const MappingStatus coordinate_status =
|
||||
EvaluateCompactificationCoordinate(
|
||||
element_data.compactification, transformation,
|
||||
integration_point, workspace,
|
||||
workspace.m_compactification_point
|
||||
);
|
||||
const MappingStatus coordinate_status = EvaluateCompactificationCoordinate(
|
||||
element_data.compactification, transformation, integration_point, workspace,
|
||||
workspace.m_compactification_point
|
||||
);
|
||||
|
||||
if (coordinate_status != MappingStatus::valid)
|
||||
return coordinate_status;
|
||||
|
||||
const compactification::ExteriorMapInput exterior_input{
|
||||
.reference_position = context.reference_position,
|
||||
.displaced_position = context.displaced_position,
|
||||
.displacement_jacobian = context.displacement_jacobian,
|
||||
.compactification_coordinate =
|
||||
workspace.m_compactification_point.coordinate,
|
||||
.compactification_coordinate_gradient =
|
||||
workspace.m_compactification_point.coordinate_gradient
|
||||
.reference_position = context.reference_position,
|
||||
.displaced_position = context.displaced_position,
|
||||
.displacement_jacobian = context.displacement_jacobian,
|
||||
.compactification_coordinate = workspace.m_compactification_point.coordinate,
|
||||
.compactification_coordinate_gradient = workspace.m_compactification_point.coordinate_gradient
|
||||
};
|
||||
|
||||
const MappingStatus exterior_status = m_exterior_map->Evaluate(
|
||||
exterior_input, workspace.m_exterior_result
|
||||
);
|
||||
const MappingStatus exterior_status = m_exterior_map->Evaluate(exterior_input, workspace.m_exterior_result);
|
||||
if (exterior_status != MappingStatus::valid)
|
||||
return exterior_status;
|
||||
|
||||
context.physical_position =
|
||||
workspace.m_exterior_result.physical_position;
|
||||
context.mapping_jacobian =
|
||||
workspace.m_exterior_result.mapping_jacobian;
|
||||
context.physical_position = workspace.m_exterior_result.physical_position;
|
||||
context.mapping_jacobian = workspace.m_exterior_result.mapping_jacobian;
|
||||
} else {
|
||||
context.physical_position = context.displaced_position;
|
||||
context.mapping_jacobian = context.displacement_jacobian;
|
||||
}
|
||||
|
||||
if (!vector_is_finite(context.physical_position) ||
|
||||
!matrix_is_finite(context.mapping_jacobian))
|
||||
if (!vector_is_finite(context.physical_position) || !matrix_is_finite(context.mapping_jacobian))
|
||||
return MappingStatus::non_finite_result;
|
||||
|
||||
context.mapping_determinant = context.mapping_jacobian.Det();
|
||||
@@ -501,12 +441,8 @@ namespace mean_field::mapping {
|
||||
if (context.mapping_determinant <= 0.0)
|
||||
return MappingStatus::non_positive_determinant;
|
||||
|
||||
context.inverse_mapping_jacobian.SetSize(
|
||||
m_options.dimension, m_options.dimension
|
||||
);
|
||||
mfem::CalcInverse(
|
||||
context.mapping_jacobian, context.inverse_mapping_jacobian
|
||||
);
|
||||
context.inverse_mapping_jacobian.SetSize(m_options.dimension, m_options.dimension);
|
||||
mfem::CalcInverse(context.mapping_jacobian, context.inverse_mapping_jacobian);
|
||||
|
||||
if (!matrix_is_finite(context.inverse_mapping_jacobian))
|
||||
return MappingStatus::non_finite_result;
|
||||
@@ -521,33 +457,22 @@ namespace mean_field::mapping {
|
||||
Workspace &workspace,
|
||||
VolumeMappingContext &context
|
||||
) const {
|
||||
const MappingStatus point_status = EvaluatePoint(
|
||||
element_data, transformation, integration_point, workspace,
|
||||
context.mapping
|
||||
);
|
||||
const MappingStatus point_status =
|
||||
EvaluatePoint(element_data, transformation, integration_point, workspace, context.mapping);
|
||||
if (point_status != MappingStatus::valid)
|
||||
return point_status;
|
||||
|
||||
transformation.SetIntPoint(&integration_point);
|
||||
mfem::Mult(
|
||||
context.mapping.mapping_jacobian, transformation.Jacobian(),
|
||||
workspace.m_full_element_jacobian
|
||||
);
|
||||
mfem::Mult(context.mapping.mapping_jacobian, transformation.Jacobian(), workspace.m_full_element_jacobian);
|
||||
|
||||
context.quadrature.J_inv.SetSize(
|
||||
m_options.dimension, m_options.dimension
|
||||
);
|
||||
mfem::CalcInverse(
|
||||
workspace.m_full_element_jacobian, context.quadrature.J_inv
|
||||
);
|
||||
context.quadrature.J_inv.SetSize(m_options.dimension, m_options.dimension);
|
||||
mfem::CalcInverse(workspace.m_full_element_jacobian, context.quadrature.J_inv);
|
||||
|
||||
context.quadrature.detJ = context.mapping.mapping_determinant;
|
||||
context.quadrature.weight = integration_point.weight *
|
||||
transformation.Weight() *
|
||||
context.mapping.mapping_determinant;
|
||||
context.quadrature.detJ = context.mapping.mapping_determinant;
|
||||
context.quadrature.weight =
|
||||
integration_point.weight * transformation.Weight() * context.mapping.mapping_determinant;
|
||||
|
||||
if (!matrix_is_finite(context.quadrature.J_inv) ||
|
||||
!std::isfinite(context.quadrature.weight))
|
||||
if (!matrix_is_finite(context.quadrature.J_inv) || !std::isfinite(context.quadrature.weight))
|
||||
return MappingStatus::non_finite_result;
|
||||
if (context.quadrature.weight <= 0.0)
|
||||
return MappingStatus::non_positive_determinant;
|
||||
@@ -555,33 +480,24 @@ namespace mean_field::mapping {
|
||||
return MappingStatus::valid;
|
||||
}
|
||||
|
||||
mfem::ElementTransformation &
|
||||
DomainMapperStateless::SelectFaceElementTransformation(
|
||||
mfem::ElementTransformation &DomainMapperStateless::SelectFaceElementTransformation(
|
||||
mfem::FaceElementTransformations &transformation,
|
||||
const FaceElementSide side
|
||||
) {
|
||||
if (side == FaceElementSide::element_1) {
|
||||
MFEM_VERIFY(
|
||||
transformation.Elem1 != nullptr,
|
||||
"The face does not have an element-1 transformation."
|
||||
);
|
||||
MFEM_VERIFY(transformation.Elem1 != nullptr, "The face does not have an element-1 transformation.");
|
||||
return *transformation.Elem1;
|
||||
}
|
||||
|
||||
MFEM_VERIFY(
|
||||
transformation.Elem2 != nullptr,
|
||||
"The face does not have an element-2 transformation."
|
||||
);
|
||||
MFEM_VERIFY(transformation.Elem2 != nullptr, "The face does not have an element-2 transformation.");
|
||||
return *transformation.Elem2;
|
||||
}
|
||||
|
||||
const mfem::IntegrationPoint &
|
||||
DomainMapperStateless::SelectFaceElementIntegrationPoint(
|
||||
const mfem::IntegrationPoint &DomainMapperStateless::SelectFaceElementIntegrationPoint(
|
||||
mfem::FaceElementTransformations &transformation,
|
||||
const FaceElementSide side
|
||||
) {
|
||||
mfem::ElementTransformation &element_transformation =
|
||||
SelectFaceElementTransformation(transformation, side);
|
||||
mfem::ElementTransformation &element_transformation = SelectFaceElementTransformation(transformation, side);
|
||||
return element_transformation.GetIntPoint();
|
||||
}
|
||||
|
||||
@@ -594,63 +510,47 @@ namespace mean_field::mapping {
|
||||
FaceMappingContext &context
|
||||
) const {
|
||||
transformation.SetAllIntPoints(&integration_point);
|
||||
mfem::ElementTransformation &element_transformation =
|
||||
SelectFaceElementTransformation(transformation, side);
|
||||
mfem::ElementTransformation &element_transformation = SelectFaceElementTransformation(transformation, side);
|
||||
const mfem::IntegrationPoint &element_integration_point =
|
||||
SelectFaceElementIntegrationPoint(transformation, side);
|
||||
|
||||
const MappingStatus point_status = EvaluatePoint(
|
||||
element_data, element_transformation, element_integration_point,
|
||||
workspace, context.mapping
|
||||
);
|
||||
const MappingStatus point_status =
|
||||
EvaluatePoint(element_data, element_transformation, element_integration_point, workspace, context.mapping);
|
||||
if (point_status != MappingStatus::valid)
|
||||
return point_status;
|
||||
|
||||
workspace.m_reference_normal.SetSize(m_options.dimension);
|
||||
mfem::CalcOrtho(
|
||||
transformation.Jacobian(), workspace.m_reference_normal
|
||||
);
|
||||
mfem::CalcOrtho(transformation.Jacobian(), workspace.m_reference_normal);
|
||||
if (side == FaceElementSide::element_2)
|
||||
workspace.m_reference_normal *= -1.0;
|
||||
|
||||
const double reference_normal_magnitude =
|
||||
workspace.m_reference_normal.Norml2();
|
||||
if (!std::isfinite(reference_normal_magnitude) ||
|
||||
reference_normal_magnitude <= 0.0)
|
||||
const double reference_normal_magnitude = workspace.m_reference_normal.Norml2();
|
||||
if (!std::isfinite(reference_normal_magnitude) || reference_normal_magnitude <= 0.0)
|
||||
return MappingStatus::non_finite_result;
|
||||
|
||||
context.reference_normal.SetSize(m_options.dimension);
|
||||
context.reference_normal = workspace.m_reference_normal;
|
||||
context.reference_normal /= reference_normal_magnitude;
|
||||
|
||||
context.mapping.inverse_mapping_jacobian.MultTranspose(
|
||||
workspace.m_reference_normal, workspace.m_mapped_normal
|
||||
);
|
||||
context.mapping.inverse_mapping_jacobian.MultTranspose(workspace.m_reference_normal, workspace.m_mapped_normal);
|
||||
workspace.m_mapped_normal *= context.mapping.mapping_determinant;
|
||||
|
||||
const double mapped_normal_magnitude =
|
||||
workspace.m_mapped_normal.Norml2();
|
||||
if (!std::isfinite(mapped_normal_magnitude) ||
|
||||
mapped_normal_magnitude <= 0.0)
|
||||
const double mapped_normal_magnitude = workspace.m_mapped_normal.Norml2();
|
||||
if (!std::isfinite(mapped_normal_magnitude) || mapped_normal_magnitude <= 0.0)
|
||||
return MappingStatus::non_finite_result;
|
||||
|
||||
context.quadrature.normal.SetSize(m_options.dimension);
|
||||
context.quadrature.normal = workspace.m_mapped_normal;
|
||||
context.quadrature.normal /= mapped_normal_magnitude;
|
||||
|
||||
context.reference_surface_weight =
|
||||
integration_point.weight * reference_normal_magnitude;
|
||||
context.physical_surface_weight =
|
||||
integration_point.weight * mapped_normal_magnitude;
|
||||
context.reference_surface_weight = integration_point.weight * reference_normal_magnitude;
|
||||
context.physical_surface_weight = integration_point.weight * mapped_normal_magnitude;
|
||||
|
||||
context.quadrature.ds = context.reference_surface_weight;
|
||||
context.quadrature.v_dot_n_scale =
|
||||
mapped_normal_magnitude / reference_normal_magnitude;
|
||||
context.quadrature.ds = context.reference_surface_weight;
|
||||
context.quadrature.v_dot_n_scale = mapped_normal_magnitude / reference_normal_magnitude;
|
||||
|
||||
if (!vector_is_finite(context.quadrature.normal) ||
|
||||
!std::isfinite(context.reference_surface_weight) ||
|
||||
!std::isfinite(context.physical_surface_weight) ||
|
||||
!std::isfinite(context.quadrature.v_dot_n_scale)) {
|
||||
if (!vector_is_finite(context.quadrature.normal) || !std::isfinite(context.reference_surface_weight) ||
|
||||
!std::isfinite(context.physical_surface_weight) || !std::isfinite(context.quadrature.v_dot_n_scale)) {
|
||||
return MappingStatus::non_finite_result;
|
||||
}
|
||||
|
||||
@@ -668,8 +568,7 @@ namespace mean_field::mapping {
|
||||
) const {
|
||||
ValidateElementData(element_data);
|
||||
const ElementMappingData direction_data{
|
||||
.displacement = direction,
|
||||
.compactification = element_data.compactification
|
||||
.displacement = direction, .compactification = element_data.compactification
|
||||
};
|
||||
ValidateElementData(direction_data);
|
||||
|
||||
@@ -679,9 +578,7 @@ namespace mean_field::mapping {
|
||||
"degree-of-freedom counts."
|
||||
);
|
||||
if (workspace.GetDimension() != m_options.dimension)
|
||||
throw std::invalid_argument(
|
||||
"The mapping workspace has the wrong dimension."
|
||||
);
|
||||
throw std::invalid_argument("The mapping workspace has the wrong dimension.");
|
||||
if (base_context.compactified != IsCompactifiedElement(transformation))
|
||||
throw std::invalid_argument(
|
||||
"The base mapping context does not match the current element "
|
||||
@@ -689,86 +586,66 @@ namespace mean_field::mapping {
|
||||
);
|
||||
|
||||
EvaluateField(
|
||||
direction, transformation, integration_point, workspace,
|
||||
workspace.m_field_value, workspace.m_field_jacobian
|
||||
direction, transformation, integration_point, workspace, workspace.m_field_value, workspace.m_field_jacobian
|
||||
);
|
||||
|
||||
if (!vector_is_finite(workspace.m_field_value) ||
|
||||
!matrix_is_finite(workspace.m_field_jacobian))
|
||||
if (!vector_is_finite(workspace.m_field_value) || !matrix_is_finite(workspace.m_field_jacobian))
|
||||
return MappingStatus::non_finite_input;
|
||||
|
||||
variation.displacement_variation = workspace.m_field_value;
|
||||
variation.displacement_jacobian_variation = workspace.m_field_jacobian;
|
||||
|
||||
if (base_context.compactified) {
|
||||
const MappingStatus coordinate_status =
|
||||
EvaluateCompactificationCoordinate(
|
||||
element_data.compactification, transformation,
|
||||
integration_point, workspace,
|
||||
workspace.m_compactification_point
|
||||
);
|
||||
const MappingStatus coordinate_status = EvaluateCompactificationCoordinate(
|
||||
element_data.compactification, transformation, integration_point, workspace,
|
||||
workspace.m_compactification_point
|
||||
);
|
||||
|
||||
if (coordinate_status != MappingStatus::valid)
|
||||
return coordinate_status;
|
||||
|
||||
const compactification::ExteriorMapInput exterior_input{
|
||||
.reference_position = base_context.reference_position,
|
||||
.displaced_position = base_context.displaced_position,
|
||||
.displacement_jacobian = base_context.displacement_jacobian,
|
||||
.compactification_coordinate =
|
||||
workspace.m_compactification_point.coordinate,
|
||||
.compactification_coordinate_gradient =
|
||||
workspace.m_compactification_point.coordinate_gradient
|
||||
.reference_position = base_context.reference_position,
|
||||
.displaced_position = base_context.displaced_position,
|
||||
.displacement_jacobian = base_context.displacement_jacobian,
|
||||
.compactification_coordinate = workspace.m_compactification_point.coordinate,
|
||||
.compactification_coordinate_gradient = workspace.m_compactification_point.coordinate_gradient
|
||||
};
|
||||
|
||||
workspace.m_exterior_result.physical_position =
|
||||
base_context.physical_position;
|
||||
workspace.m_exterior_result.mapping_jacobian =
|
||||
base_context.mapping_jacobian;
|
||||
workspace.m_exterior_result.physical_position = base_context.physical_position;
|
||||
workspace.m_exterior_result.mapping_jacobian = base_context.mapping_jacobian;
|
||||
|
||||
const compactification::ExteriorMapDirection exterior_direction{
|
||||
.displaced_position_variation =
|
||||
variation.displacement_variation,
|
||||
.displacement_jacobian_variation =
|
||||
variation.displacement_jacobian_variation
|
||||
.displaced_position_variation = variation.displacement_variation,
|
||||
.displacement_jacobian_variation = variation.displacement_jacobian_variation
|
||||
};
|
||||
|
||||
// ReSharper disable once CppTooWideScopeInitStatement
|
||||
const MappingStatus exterior_status =
|
||||
m_exterior_map->EvaluateVariation(
|
||||
exterior_input, workspace.m_exterior_result,
|
||||
exterior_direction, workspace.m_exterior_variation
|
||||
);
|
||||
const MappingStatus exterior_status = m_exterior_map->EvaluateVariation(
|
||||
exterior_input, workspace.m_exterior_result, exterior_direction, workspace.m_exterior_variation
|
||||
);
|
||||
|
||||
if (exterior_status != MappingStatus::valid) {
|
||||
return exterior_status;
|
||||
}
|
||||
|
||||
variation.physical_position_variation =
|
||||
workspace.m_exterior_variation.physical_position_variation;
|
||||
variation.mapping_jacobian_variation =
|
||||
workspace.m_exterior_variation.mapping_jacobian_variation;
|
||||
variation.physical_position_variation = workspace.m_exterior_variation.physical_position_variation;
|
||||
variation.mapping_jacobian_variation = workspace.m_exterior_variation.mapping_jacobian_variation;
|
||||
} else {
|
||||
variation.physical_position_variation =
|
||||
variation.displacement_variation;
|
||||
variation.mapping_jacobian_variation =
|
||||
variation.displacement_jacobian_variation;
|
||||
variation.physical_position_variation = variation.displacement_variation;
|
||||
variation.mapping_jacobian_variation = variation.displacement_jacobian_variation;
|
||||
}
|
||||
|
||||
mfem::Mult(
|
||||
base_context.inverse_mapping_jacobian,
|
||||
variation.mapping_jacobian_variation, workspace.m_matrix_temp_1
|
||||
base_context.inverse_mapping_jacobian, variation.mapping_jacobian_variation, workspace.m_matrix_temp_1
|
||||
);
|
||||
|
||||
double trace = 0.0;
|
||||
for (int i = 0; i < m_options.dimension; ++i)
|
||||
trace += workspace.m_matrix_temp_1(i, i);
|
||||
variation.mapping_determinant_variation =
|
||||
base_context.mapping_determinant * trace;
|
||||
variation.mapping_determinant_variation = base_context.mapping_determinant * trace;
|
||||
|
||||
variation.inverse_mapping_jacobian_variation.SetSize(
|
||||
m_options.dimension, m_options.dimension
|
||||
);
|
||||
variation.inverse_mapping_jacobian_variation.SetSize(m_options.dimension, m_options.dimension);
|
||||
mfem::Mult(
|
||||
workspace.m_matrix_temp_1, base_context.inverse_mapping_jacobian,
|
||||
variation.inverse_mapping_jacobian_variation
|
||||
@@ -795,34 +672,26 @@ namespace mean_field::mapping {
|
||||
VolumeMappingVariation &variation
|
||||
) const {
|
||||
const MappingStatus point_status = EvaluatePointVariation(
|
||||
element_data, direction, transformation, integration_point,
|
||||
base_context.mapping, workspace, variation.mapping
|
||||
element_data, direction, transformation, integration_point, base_context.mapping, workspace,
|
||||
variation.mapping
|
||||
);
|
||||
if (point_status != MappingStatus::valid)
|
||||
return point_status;
|
||||
|
||||
transformation.SetIntPoint(&integration_point);
|
||||
mfem::Mult(
|
||||
variation.mapping.mapping_jacobian_variation,
|
||||
transformation.Jacobian(), workspace.m_full_element_jacobian
|
||||
);
|
||||
mfem::Mult(
|
||||
base_context.quadrature.J_inv, workspace.m_full_element_jacobian,
|
||||
workspace.m_matrix_temp_1
|
||||
variation.mapping.mapping_jacobian_variation, transformation.Jacobian(), workspace.m_full_element_jacobian
|
||||
);
|
||||
mfem::Mult(base_context.quadrature.J_inv, workspace.m_full_element_jacobian, workspace.m_matrix_temp_1);
|
||||
|
||||
variation.inverse_element_jacobian_variation.SetSize(
|
||||
m_options.dimension, m_options.dimension
|
||||
);
|
||||
variation.inverse_element_jacobian_variation.SetSize(m_options.dimension, m_options.dimension);
|
||||
mfem::Mult(
|
||||
workspace.m_matrix_temp_1, base_context.quadrature.J_inv,
|
||||
variation.inverse_element_jacobian_variation
|
||||
workspace.m_matrix_temp_1, base_context.quadrature.J_inv, variation.inverse_element_jacobian_variation
|
||||
);
|
||||
variation.inverse_element_jacobian_variation *= -1.0;
|
||||
|
||||
variation.weight_variation =
|
||||
integration_point.weight * transformation.Weight() *
|
||||
variation.mapping.mapping_determinant_variation;
|
||||
integration_point.weight * transformation.Weight() * variation.mapping.mapping_determinant_variation;
|
||||
|
||||
if (!matrix_is_finite(variation.inverse_element_jacobian_variation) ||
|
||||
!std::isfinite(variation.weight_variation))
|
||||
@@ -842,30 +711,24 @@ namespace mean_field::mapping {
|
||||
FaceMappingVariation &variation
|
||||
) const {
|
||||
transformation.SetAllIntPoints(&integration_point);
|
||||
mfem::ElementTransformation &element_transformation =
|
||||
SelectFaceElementTransformation(transformation, side);
|
||||
mfem::ElementTransformation &element_transformation = SelectFaceElementTransformation(transformation, side);
|
||||
const mfem::IntegrationPoint &element_integration_point =
|
||||
SelectFaceElementIntegrationPoint(transformation, side);
|
||||
|
||||
const MappingStatus point_status = EvaluatePointVariation(
|
||||
element_data, direction, element_transformation,
|
||||
element_integration_point, base_context.mapping, workspace,
|
||||
element_data, direction, element_transformation, element_integration_point, base_context.mapping, workspace,
|
||||
variation.mapping
|
||||
);
|
||||
if (point_status != MappingStatus::valid)
|
||||
return point_status;
|
||||
|
||||
workspace.m_reference_normal.SetSize(m_options.dimension);
|
||||
mfem::CalcOrtho(
|
||||
transformation.Jacobian(), workspace.m_reference_normal
|
||||
);
|
||||
mfem::CalcOrtho(transformation.Jacobian(), workspace.m_reference_normal);
|
||||
if (side == FaceElementSide::element_2)
|
||||
workspace.m_reference_normal *= -1.0;
|
||||
|
||||
const double reference_normal_magnitude =
|
||||
workspace.m_reference_normal.Norml2();
|
||||
if (!std::isfinite(reference_normal_magnitude) ||
|
||||
reference_normal_magnitude <= 0.0)
|
||||
const double reference_normal_magnitude = workspace.m_reference_normal.Norml2();
|
||||
if (!std::isfinite(reference_normal_magnitude) || reference_normal_magnitude <= 0.0)
|
||||
return MappingStatus::non_finite_result;
|
||||
|
||||
base_context.mapping.inverse_mapping_jacobian.MultTranspose(
|
||||
@@ -878,32 +741,23 @@ namespace mean_field::mapping {
|
||||
variation.mapping.inverse_mapping_jacobian_variation.MultTranspose(
|
||||
workspace.m_reference_normal, variation.physical_normal_variation
|
||||
);
|
||||
variation.physical_normal_variation *=
|
||||
base_context.mapping.mapping_determinant;
|
||||
variation.physical_normal_variation *= base_context.mapping.mapping_determinant;
|
||||
variation.physical_normal_variation.Add(
|
||||
variation.mapping.mapping_determinant_variation,
|
||||
workspace.m_vector_temp
|
||||
variation.mapping.mapping_determinant_variation, workspace.m_vector_temp
|
||||
);
|
||||
|
||||
const double mapped_normal_magnitude =
|
||||
workspace.m_mapped_normal.Norml2();
|
||||
if (!std::isfinite(mapped_normal_magnitude) ||
|
||||
mapped_normal_magnitude <= 0.0)
|
||||
const double mapped_normal_magnitude = workspace.m_mapped_normal.Norml2();
|
||||
if (!std::isfinite(mapped_normal_magnitude) || mapped_normal_magnitude <= 0.0)
|
||||
return MappingStatus::non_finite_result;
|
||||
|
||||
const double mapped_normal_magnitude_variation =
|
||||
base_context.quadrature.normal *
|
||||
variation.physical_normal_variation;
|
||||
base_context.quadrature.normal * variation.physical_normal_variation;
|
||||
|
||||
variation.physical_normal_variation.Add(
|
||||
-mapped_normal_magnitude_variation, base_context.quadrature.normal
|
||||
);
|
||||
variation.physical_normal_variation.Add(-mapped_normal_magnitude_variation, base_context.quadrature.normal);
|
||||
variation.physical_normal_variation /= mapped_normal_magnitude;
|
||||
|
||||
variation.physical_surface_weight_variation =
|
||||
integration_point.weight * mapped_normal_magnitude_variation;
|
||||
variation.normal_flux_scale_variation =
|
||||
mapped_normal_magnitude_variation / reference_normal_magnitude;
|
||||
variation.physical_surface_weight_variation = integration_point.weight * mapped_normal_magnitude_variation;
|
||||
variation.normal_flux_scale_variation = mapped_normal_magnitude_variation / reference_normal_magnitude;
|
||||
|
||||
if (!vector_is_finite(variation.physical_normal_variation) ||
|
||||
!std::isfinite(variation.physical_surface_weight_variation) ||
|
||||
|
||||
@@ -41,15 +41,12 @@ namespace mean_field::mapping {
|
||||
mfem::Vector &physical_gradient
|
||||
) {
|
||||
MFEM_VERIFY(
|
||||
reference_gradient.Size() ==
|
||||
context.inverse_mapping_jacobian.Height(),
|
||||
reference_gradient.Size() == context.inverse_mapping_jacobian.Height(),
|
||||
"The reference scalar gradient has the wrong dimension."
|
||||
);
|
||||
|
||||
physical_gradient.SetSize(reference_gradient.Size());
|
||||
context.inverse_mapping_jacobian.MultTranspose(
|
||||
reference_gradient, physical_gradient
|
||||
);
|
||||
context.inverse_mapping_jacobian.MultTranspose(reference_gradient, physical_gradient);
|
||||
}
|
||||
|
||||
void MapPhysicalGradientToReference(
|
||||
@@ -63,9 +60,7 @@ namespace mean_field::mapping {
|
||||
);
|
||||
|
||||
reference_gradient.SetSize(physical_gradient.Size());
|
||||
context.mapping_jacobian.MultTranspose(
|
||||
physical_gradient, reference_gradient
|
||||
);
|
||||
context.mapping_jacobian.MultTranspose(physical_gradient, reference_gradient);
|
||||
}
|
||||
|
||||
void MapReferenceVectorGradientToPhysical(
|
||||
@@ -74,19 +69,12 @@ namespace mean_field::mapping {
|
||||
mfem::DenseMatrix &physical_gradient
|
||||
) {
|
||||
MFEM_VERIFY(
|
||||
reference_gradient.Width() ==
|
||||
context.inverse_mapping_jacobian.Height(),
|
||||
reference_gradient.Width() == context.inverse_mapping_jacobian.Height(),
|
||||
"The reference vector gradient has the wrong dimension."
|
||||
);
|
||||
|
||||
physical_gradient.SetSize(
|
||||
reference_gradient.Height(),
|
||||
context.inverse_mapping_jacobian.Width()
|
||||
);
|
||||
mfem::Mult(
|
||||
reference_gradient, context.inverse_mapping_jacobian,
|
||||
physical_gradient
|
||||
);
|
||||
physical_gradient.SetSize(reference_gradient.Height(), context.inverse_mapping_jacobian.Width());
|
||||
mfem::Mult(reference_gradient, context.inverse_mapping_jacobian, physical_gradient);
|
||||
}
|
||||
|
||||
void MapPhysicalVectorGradientToReference(
|
||||
@@ -99,12 +87,8 @@ namespace mean_field::mapping {
|
||||
"The physical vector gradient has the wrong dimension."
|
||||
);
|
||||
|
||||
reference_gradient.SetSize(
|
||||
physical_gradient.Height(), context.mapping_jacobian.Width()
|
||||
);
|
||||
mfem::Mult(
|
||||
physical_gradient, context.mapping_jacobian, reference_gradient
|
||||
);
|
||||
reference_gradient.SetSize(physical_gradient.Height(), context.mapping_jacobian.Width());
|
||||
mfem::Mult(physical_gradient, context.mapping_jacobian, reference_gradient);
|
||||
}
|
||||
|
||||
double MapHDivDivergenceToPhysical(
|
||||
@@ -120,19 +104,11 @@ namespace mean_field::mapping {
|
||||
) {
|
||||
const int dimension = context.mapping_jacobian.Height();
|
||||
|
||||
MFEM_VERIFY(
|
||||
context.mapping_jacobian.Width() == dimension,
|
||||
"The mapping Jacobian must be square."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
context.mapping_determinant > 0.0,
|
||||
"The mapping determinant must be positive."
|
||||
);
|
||||
MFEM_VERIFY(context.mapping_jacobian.Width() == dimension, "The mapping Jacobian must be square.");
|
||||
MFEM_VERIFY(context.mapping_determinant > 0.0, "The mapping determinant must be positive.");
|
||||
|
||||
mass_tensor.SetSize(dimension, dimension);
|
||||
mfem::MultAtB(
|
||||
context.mapping_jacobian, context.mapping_jacobian, mass_tensor
|
||||
);
|
||||
mfem::MultAtB(context.mapping_jacobian, context.mapping_jacobian, mass_tensor);
|
||||
mass_tensor *= 1 / context.mapping_determinant;
|
||||
}
|
||||
|
||||
@@ -143,19 +119,12 @@ namespace mean_field::mapping {
|
||||
const int dimension = context.inverse_mapping_jacobian.Height();
|
||||
|
||||
MFEM_VERIFY(
|
||||
context.inverse_mapping_jacobian.Width() == dimension,
|
||||
"The inverse mapping Jacobian must be square."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
context.mapping_determinant > 0.0,
|
||||
"The mapping determinant must be positive."
|
||||
context.inverse_mapping_jacobian.Width() == dimension, "The inverse mapping Jacobian must be square."
|
||||
);
|
||||
MFEM_VERIFY(context.mapping_determinant > 0.0, "The mapping determinant must be positive.");
|
||||
|
||||
diffusion_tensor.SetSize(dimension, dimension);
|
||||
mfem::MultABt(
|
||||
context.inverse_mapping_jacobian, context.inverse_mapping_jacobian,
|
||||
diffusion_tensor
|
||||
);
|
||||
mfem::MultABt(context.inverse_mapping_jacobian, context.inverse_mapping_jacobian, diffusion_tensor);
|
||||
diffusion_tensor *= context.mapping_determinant;
|
||||
}
|
||||
|
||||
@@ -170,9 +139,7 @@ namespace mean_field::mapping {
|
||||
);
|
||||
|
||||
physical_field.SetSize(reference_field.Size());
|
||||
context.inverse_mapping_jacobian.MultTranspose(
|
||||
reference_field, physical_field
|
||||
);
|
||||
context.inverse_mapping_jacobian.MultTranspose(reference_field, physical_field);
|
||||
}
|
||||
|
||||
void MapPhysicalFieldToHCurlReference(
|
||||
@@ -239,35 +206,24 @@ namespace mean_field::mapping {
|
||||
const MappingPointVariation &variation,
|
||||
mfem::DenseMatrix &mass_tensor_variation
|
||||
) {
|
||||
const double determinant = context.mapping_determinant;
|
||||
const double determinant_variation =
|
||||
variation.mapping_determinant_variation;
|
||||
const int dimension = context.inverse_mapping_jacobian.Width();
|
||||
const double determinant = context.mapping_determinant;
|
||||
const double determinant_variation = variation.mapping_determinant_variation;
|
||||
const int dimension = context.inverse_mapping_jacobian.Width();
|
||||
|
||||
mass_tensor_variation.SetSize(dimension, dimension);
|
||||
|
||||
MFEM_VERIFY(
|
||||
std::isfinite(determinant) && determinant > 0.0,
|
||||
"The mapping determinant must be positive and finite."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
std::isfinite(determinant_variation),
|
||||
"The mapping determinant variation must be finite."
|
||||
std::isfinite(determinant) && determinant > 0.0, "The mapping determinant must be positive and finite."
|
||||
);
|
||||
MFEM_VERIFY(std::isfinite(determinant_variation), "The mapping determinant variation must be finite.");
|
||||
|
||||
mfem::DenseMatrix determinant_correction(dimension, dimension);
|
||||
ComputeHDivMassTensor(context, determinant_correction);
|
||||
determinant_correction *= determinant_variation / determinant;
|
||||
|
||||
mfem::DenseMatrix right_jacobian_variation(dimension, dimension);
|
||||
mfem::MultAtB(
|
||||
context.mapping_jacobian, variation.mapping_jacobian_variation,
|
||||
right_jacobian_variation
|
||||
);
|
||||
mfem::MultAtB(
|
||||
variation.mapping_jacobian_variation, context.mapping_jacobian,
|
||||
mass_tensor_variation
|
||||
);
|
||||
mfem::MultAtB(context.mapping_jacobian, variation.mapping_jacobian_variation, right_jacobian_variation);
|
||||
mfem::MultAtB(variation.mapping_jacobian_variation, context.mapping_jacobian, mass_tensor_variation);
|
||||
|
||||
mass_tensor_variation += right_jacobian_variation;
|
||||
mass_tensor_variation *= 1 / determinant;
|
||||
|
||||
260
libmeanfield/impl/models/polytropic.cpp
Normal file
260
libmeanfield/impl/models/polytropic.cpp
Normal file
@@ -0,0 +1,260 @@
|
||||
module;
|
||||
#include <cmath>
|
||||
#include <format>
|
||||
#include <numbers>
|
||||
module mean_field;
|
||||
import :model.structure.polytropic;
|
||||
|
||||
namespace mean_field::models::structure {
|
||||
PolytropicStructure::PolytropicStructure(
|
||||
eos::Polytrope equationOfState,
|
||||
const double targetMass
|
||||
)
|
||||
: m_equationOfState(std::move(equationOfState)),
|
||||
m_targetMass(targetMass) {
|
||||
validate();
|
||||
}
|
||||
|
||||
const eos::EquationOfState &PolytropicStructure::equationOfState() const noexcept {
|
||||
return m_equationOfState;
|
||||
}
|
||||
|
||||
double PolytropicStructure::targetMass() const noexcept {
|
||||
return m_targetMass;
|
||||
}
|
||||
|
||||
StructureSeed PolytropicStructure::makeInitialSeed(const StructureSeedRequest &request) const {
|
||||
validateSeedRequest(request);
|
||||
|
||||
const double polytropicIndex = m_equationOfState.polytropic_index();
|
||||
const std::vector<LaneEmdenPoint> laneEmdenSolution = solveLaneEmden(polytropicIndex);
|
||||
const double surfaceCoordinate = laneEmdenSolution.back().coordinate;
|
||||
const double centralEnthalpy = m_equationOfState.enthalpy_from_density(request.centralDensity);
|
||||
const double radialScaleSquared =
|
||||
centralEnthalpy / (4.0 * std::numbers::pi_v<double> * mean_field::utils::G * request.centralDensity);
|
||||
|
||||
if (!std::isfinite(radialScaleSquared) || radialScaleSquared <= 0.0) {
|
||||
throw std::runtime_error(
|
||||
"The polytropic Lane-Emden radial scale is not "
|
||||
"finite and positive."
|
||||
);
|
||||
}
|
||||
|
||||
const double radialScale = std::sqrt(radialScaleSquared);
|
||||
|
||||
StructureSeed seed;
|
||||
|
||||
seed.radius.SetSize(request.radialSampleCount);
|
||||
seed.density.SetSize(request.radialSampleCount);
|
||||
seed.enthalpy.SetSize(request.radialSampleCount);
|
||||
|
||||
seed.stellarRadius = radialScale * surfaceCoordinate;
|
||||
seed.centralDensity = request.centralDensity;
|
||||
seed.centralEnthalpy = centralEnthalpy;
|
||||
|
||||
std::size_t interpolationIndex = 0;
|
||||
|
||||
for (int sampleIndex = 0; sampleIndex < request.radialSampleCount; ++sampleIndex) {
|
||||
const double sampleFraction =
|
||||
static_cast<double>(sampleIndex) / static_cast<double>(request.radialSampleCount - 1);
|
||||
|
||||
const double dimensionlessRadius = sampleFraction * surfaceCoordinate;
|
||||
const double laneEmdenValue =
|
||||
interpolateLaneEmdenValue(laneEmdenSolution, dimensionlessRadius, interpolationIndex);
|
||||
const double density = request.centralDensity * std::pow(laneEmdenValue, polytropicIndex);
|
||||
|
||||
seed.radius(sampleIndex) = radialScale * dimensionlessRadius;
|
||||
seed.density(sampleIndex) = density;
|
||||
seed.enthalpy(sampleIndex) = m_equationOfState.enthalpy_from_density(density);
|
||||
}
|
||||
|
||||
seed.radius(0) = 0.0;
|
||||
seed.density(0) = request.centralDensity;
|
||||
seed.enthalpy(0) = centralEnthalpy;
|
||||
|
||||
const int surfaceIndex = request.radialSampleCount - 1;
|
||||
|
||||
seed.radius(surfaceIndex) = seed.stellarRadius;
|
||||
seed.density(surfaceIndex) = 0.0;
|
||||
seed.enthalpy(surfaceIndex) = 0.0;
|
||||
|
||||
return seed;
|
||||
}
|
||||
|
||||
void PolytropicStructure::validate() const {
|
||||
const double polytropicIndex = m_equationOfState.polytropic_index();
|
||||
|
||||
if (!std::isfinite(polytropicIndex) || polytropicIndex < 1.0 || polytropicIndex >= 5.0) {
|
||||
throw std::invalid_argument(
|
||||
std::format(
|
||||
"PolytropicStructure requires a finite-radius "
|
||||
"polytrope with 1 <= n < 5. Instead n = {} was "
|
||||
"provided.",
|
||||
polytropicIndex
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if (!std::isfinite(m_targetMass) || m_targetMass <= 0.0) {
|
||||
throw std::invalid_argument(
|
||||
std::format(
|
||||
"The target stellar mass must be finite and "
|
||||
"positive. Instead a value of {} was provided.",
|
||||
m_targetMass
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void PolytropicStructure::validateSeedRequest(const StructureSeedRequest &request) {
|
||||
if (!std::isfinite(request.centralDensity) || request.centralDensity <= 0.0) {
|
||||
throw std::invalid_argument(
|
||||
std::format(
|
||||
"The seed central density must be finite and "
|
||||
"positive. Instead a value of {} was provided.",
|
||||
request.centralDensity
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if (request.radialSampleCount < 2) {
|
||||
throw std::invalid_argument(
|
||||
std::format(
|
||||
"A polytropic seed requires at least two radial "
|
||||
"samples. Instead {} samples were requested.",
|
||||
request.radialSampleCount
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
PolytropicStructure::LaneEmdenDerivative PolytropicStructure::evaluateLaneEmdenRhs(
|
||||
const double coordinate,
|
||||
const double value,
|
||||
const double derivative,
|
||||
const double polytropicIndex
|
||||
) {
|
||||
const double nonnegativeValue = std::max(value, 0.0);
|
||||
|
||||
return {
|
||||
.value = derivative,
|
||||
.derivative = -2.0 * derivative / coordinate - std::pow(nonnegativeValue, polytropicIndex)
|
||||
};
|
||||
}
|
||||
|
||||
PolytropicStructure::LaneEmdenPoint PolytropicStructure::takeLaneEmdenStep(
|
||||
const LaneEmdenPoint &point,
|
||||
const double step,
|
||||
const double polytropicIndex
|
||||
) {
|
||||
const LaneEmdenDerivative first =
|
||||
evaluateLaneEmdenRhs(point.coordinate, point.value, point.derivative, polytropicIndex);
|
||||
|
||||
const LaneEmdenDerivative second = evaluateLaneEmdenRhs(
|
||||
point.coordinate + 0.5 * step, point.value + 0.5 * step * first.value,
|
||||
point.derivative + 0.5 * step * first.derivative, polytropicIndex
|
||||
);
|
||||
|
||||
const LaneEmdenDerivative third = evaluateLaneEmdenRhs(
|
||||
point.coordinate + 0.5 * step, point.value + 0.5 * step * second.value,
|
||||
point.derivative + 0.5 * step * second.derivative, polytropicIndex
|
||||
);
|
||||
|
||||
const LaneEmdenDerivative fourth = evaluateLaneEmdenRhs(
|
||||
point.coordinate + step, point.value + step * third.value, point.derivative + step * third.derivative,
|
||||
polytropicIndex
|
||||
);
|
||||
|
||||
return {
|
||||
.coordinate = point.coordinate + step,
|
||||
.value = point.value + step / 6.0 * (first.value + 2.0 * second.value + 2.0 * third.value + fourth.value),
|
||||
.derivative =
|
||||
point.derivative +
|
||||
step / 6.0 * (first.derivative + 2.0 * second.derivative + 2.0 * third.derivative + fourth.derivative)
|
||||
};
|
||||
}
|
||||
|
||||
std::vector<PolytropicStructure::LaneEmdenPoint> PolytropicStructure::solveLaneEmden(const double polytropicIndex) {
|
||||
constexpr double initialCoordinate = 1.0e-6;
|
||||
constexpr double integrationStep = 1.0e-3;
|
||||
constexpr int maximumStepCount = 2'000'000;
|
||||
|
||||
const double coordinateSquared = initialCoordinate * initialCoordinate;
|
||||
|
||||
const double coordinateCubed = coordinateSquared * initialCoordinate;
|
||||
|
||||
const double coordinateFourth = coordinateSquared * coordinateSquared;
|
||||
|
||||
LaneEmdenPoint point{
|
||||
.coordinate = initialCoordinate,
|
||||
.value = 1.0 - coordinateSquared / 6.0 + polytropicIndex * coordinateFourth / 120.0,
|
||||
.derivative = -initialCoordinate / 3.0 + polytropicIndex * coordinateCubed / 30.0
|
||||
};
|
||||
|
||||
std::vector<LaneEmdenPoint> solution;
|
||||
solution.reserve(8192);
|
||||
solution.push_back({.coordinate = 0.0, .value = 1.0, .derivative = 0.0});
|
||||
solution.push_back(point);
|
||||
|
||||
for (int stepIndex = 0; stepIndex < maximumStepCount; ++stepIndex) {
|
||||
LaneEmdenPoint nextPoint = takeLaneEmdenStep(point, integrationStep, polytropicIndex);
|
||||
|
||||
if (!std::isfinite(nextPoint.value)) {
|
||||
throw std::runtime_error(
|
||||
"The Lane-Emden integration produced a non-finite "
|
||||
"solution before reaching the stellar surface."
|
||||
);
|
||||
}
|
||||
|
||||
if (nextPoint.value <= 0.0) {
|
||||
const double rootFraction = point.value / (point.value - nextPoint.value);
|
||||
|
||||
solution.push_back(
|
||||
{.coordinate = point.coordinate + rootFraction * (nextPoint.coordinate - point.coordinate),
|
||||
.value = 0.0,
|
||||
.derivative = point.derivative + rootFraction * (nextPoint.derivative - point.derivative)}
|
||||
);
|
||||
|
||||
return solution;
|
||||
}
|
||||
|
||||
solution.push_back(nextPoint);
|
||||
point = nextPoint;
|
||||
}
|
||||
|
||||
throw std::runtime_error(
|
||||
"The Lane-Emden integration did not reach its first zero "
|
||||
"within the configured step limit."
|
||||
);
|
||||
}
|
||||
|
||||
double PolytropicStructure::interpolateLaneEmdenValue(
|
||||
const std::vector<LaneEmdenPoint> &solution,
|
||||
const double coordinate,
|
||||
std::size_t &lowerIndex
|
||||
) {
|
||||
while (lowerIndex + 1 < solution.size() && solution[lowerIndex + 1].coordinate < coordinate) {
|
||||
++lowerIndex;
|
||||
}
|
||||
|
||||
if (lowerIndex + 1 >= solution.size()) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
const LaneEmdenPoint &lower = solution[lowerIndex];
|
||||
const LaneEmdenPoint &upper = solution[lowerIndex + 1];
|
||||
|
||||
const double interval = upper.coordinate - lower.coordinate;
|
||||
|
||||
if (interval <= 0.0) {
|
||||
throw std::runtime_error(
|
||||
"The Lane-Emden interpolation grid is not strictly "
|
||||
"increasing."
|
||||
);
|
||||
}
|
||||
|
||||
const double fraction = (coordinate - lower.coordinate) / interval;
|
||||
|
||||
return std::clamp(lower.value + fraction * (upper.value - lower.value), 0.0, 1.0);
|
||||
}
|
||||
}; // namespace mean_field::models::structure
|
||||
@@ -1,135 +1,190 @@
|
||||
module;
|
||||
|
||||
#include <cstdint>
|
||||
#include <cmath>
|
||||
|
||||
#include <mfem.hpp>
|
||||
|
||||
module mean_field;
|
||||
|
||||
import :operators.context.barotropic_closure_linearization;
|
||||
|
||||
namespace {
|
||||
void validate_finite_vector(
|
||||
const mfem::Vector &vector,
|
||||
const char *message
|
||||
) {
|
||||
for (int i = 0; i < vector.Size(); ++i) {
|
||||
MFEM_VERIFY(std::isfinite(vector(i)), message);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Stamp>
|
||||
void validate_dependency_transition(
|
||||
const Stamp &prepared,
|
||||
const Stamp &requested,
|
||||
const char *message
|
||||
) {
|
||||
MFEM_VERIFY(requested.CanFollow(prepared), message);
|
||||
MFEM_VERIFY(
|
||||
prepared.identity == requested.identity || prepared.revision != requested.revision,
|
||||
"A new barotropic-closure dependency identity must also carry a visibly different revision."
|
||||
);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace mean_field::operators::context::barotropic {
|
||||
BarotropicClosureLinearizationContext::
|
||||
BarotropicClosureLinearizationContext(
|
||||
const fem::FEM &f,
|
||||
const mapping::DomainMapperStateless &domainMapper,
|
||||
const physics::PolytropicBarotrope &barotrope
|
||||
)
|
||||
BarotropicClosureLinearizationContext::BarotropicClosureLinearizationContext(
|
||||
const fem::FEM &f,
|
||||
const mapping::DomainMapperStateless &domainMapper,
|
||||
const field::FieldDofMap &densityMap,
|
||||
const field::FieldDofMap &enthalpyMap,
|
||||
const field::FieldDofMap &displacementMap
|
||||
)
|
||||
: m_f(f),
|
||||
m_operator(
|
||||
f,
|
||||
domainMapper,
|
||||
barotrope
|
||||
) {
|
||||
m_domainMapper(domainMapper),
|
||||
m_densitySize(densityMap.reduced_size()),
|
||||
m_enthalpySize(enthalpyMap.reduced_size()),
|
||||
m_displacementSize(displacementMap.reduced_size()) {
|
||||
MFEM_VERIFY(m_f.mesh != nullptr, "BarotropicClosureLinearizationContext requires a mesh.");
|
||||
MFEM_VERIFY(m_f.densityFes != nullptr, "BarotropicClosureLinearizationContext requires the density FE space.");
|
||||
MFEM_VERIFY(
|
||||
m_f.densityFes != nullptr,
|
||||
"The closure linearization context requires the "
|
||||
"density finite-element space."
|
||||
m_f.enthalpyFes != nullptr, "BarotropicClosureLinearizationContext requires the enthalpy FE space."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
m_f.enthalpyFes != nullptr,
|
||||
"The closure linearization context requires the "
|
||||
"enthalpy finite-element space."
|
||||
m_f.displacementFes != nullptr, "BarotropicClosureLinearizationContext requires the displacement FE space."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
m_f.displacementFes != nullptr,
|
||||
"The closure linearization context requires the "
|
||||
"displacement finite-element space."
|
||||
m_domainMapper.GetDimension() == m_f.mesh->Dimension(),
|
||||
"The barotropic-closure context domain-mapper dimension does not match the mesh dimension."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
densityMap.full_size() == m_f.densityFes->GetTrueVSize(),
|
||||
"The density FieldDofMap does not match the density FE space."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
enthalpyMap.full_size() == m_f.enthalpyFes->GetTrueVSize(),
|
||||
"The enthalpy FieldDofMap does not match the enthalpy FE space."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
displacementMap.full_size() == m_f.displacementFes->GetTrueVSize(),
|
||||
"The displacement FieldDofMap does not match the displacement FE space."
|
||||
);
|
||||
}
|
||||
|
||||
void BarotropicClosureLinearizationContext::Prepare(
|
||||
const mfem::Vector &baseDensityTrue,
|
||||
const mfem::Vector &baseEnthalpyTrue,
|
||||
const mfem::Vector &displacementTrue,
|
||||
const BarotropicClosureRevisions &revisions
|
||||
BarotropicClosurePreparationReport BarotropicClosureLinearizationContext::Prepare(
|
||||
const BarotropicClosureStateView &state,
|
||||
const BarotropicClosureDependencies &dependencies
|
||||
) {
|
||||
MFEM_VERIFY(state.density.Size() == m_densitySize, "The supported closure density vector has the wrong size.");
|
||||
MFEM_VERIFY(
|
||||
baseDensityTrue.Size() == m_f.densityFes->GetTrueVSize(),
|
||||
"The closure base-density vector has the wrong size."
|
||||
state.enthalpy.Size() == m_enthalpySize, "The supported closure enthalpy vector has the wrong size."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
state.displacement.Size() == m_displacementSize,
|
||||
"The supported closure displacement vector has the wrong size."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
baseEnthalpyTrue.Size() == m_f.enthalpyFes->GetTrueVSize(),
|
||||
"The closure base-enthalpy vector has the wrong size."
|
||||
);
|
||||
validate_finite_vector(state.density, "The closure density state contains a non-finite value.");
|
||||
validate_finite_vector(state.enthalpy, "The closure enthalpy state contains a non-finite value.");
|
||||
validate_finite_vector(state.displacement, "The closure displacement state contains a non-finite value.");
|
||||
|
||||
MFEM_VERIFY(
|
||||
displacementTrue.Size() == m_f.displacementFes->GetTrueVSize(),
|
||||
"The closure displacement vector has the wrong size."
|
||||
);
|
||||
|
||||
if (m_isPrepared && revisions == m_revisions) {
|
||||
return;
|
||||
if (m_isPrepared) {
|
||||
validate_dependency_transition(
|
||||
m_dependencies.discretization, dependencies.discretization,
|
||||
"BarotropicClosureLinearizationContext received an older discretization revision for the same identity."
|
||||
);
|
||||
validate_dependency_transition(
|
||||
m_dependencies.density, dependencies.density,
|
||||
"BarotropicClosureLinearizationContext received an older density revision for the same identity."
|
||||
);
|
||||
validate_dependency_transition(
|
||||
m_dependencies.enthalpy, dependencies.enthalpy,
|
||||
"BarotropicClosureLinearizationContext received an older enthalpy revision for the same identity."
|
||||
);
|
||||
validate_dependency_transition(
|
||||
m_dependencies.displacement, dependencies.displacement,
|
||||
"BarotropicClosureLinearizationContext received an older displacement revision for the same identity."
|
||||
);
|
||||
}
|
||||
|
||||
m_operator.Prepare(baseDensityTrue, baseEnthalpyTrue, displacementTrue);
|
||||
const bool staticChanged = !m_isPrepared || dependencies.discretization != m_dependencies.discretization;
|
||||
const bool densityChanged = !m_isPrepared || dependencies.density != m_dependencies.density;
|
||||
const bool enthalpyChanged = !m_isPrepared || dependencies.enthalpy != m_dependencies.enthalpy;
|
||||
const bool displacementChanged = !m_isPrepared || dependencies.displacement != m_dependencies.displacement;
|
||||
|
||||
m_baseDensityTrue = baseDensityTrue;
|
||||
m_baseEnthalpyTrue = baseEnthalpyTrue;
|
||||
m_displacementTrue = displacementTrue;
|
||||
const bool geometryPreparationRequired = staticChanged || displacementChanged;
|
||||
const bool baseStatePreparationRequired =
|
||||
staticChanged || geometryPreparationRequired || densityChanged || enthalpyChanged;
|
||||
|
||||
m_revisions = revisions;
|
||||
m_isPrepared = true;
|
||||
++m_preparationCount;
|
||||
BarotropicClosurePreparationReport report;
|
||||
report.preparedStaticDependencies = staticChanged;
|
||||
report.preparedGeometryState = geometryPreparationRequired;
|
||||
report.preparedBaseState = baseStatePreparationRequired;
|
||||
|
||||
if (staticChanged || densityChanged) {
|
||||
m_baseDensity = state.density;
|
||||
report.updatedDensity = true;
|
||||
}
|
||||
if (staticChanged || enthalpyChanged) {
|
||||
m_baseEnthalpy = state.enthalpy;
|
||||
report.updatedEnthalpy = true;
|
||||
}
|
||||
if (geometryPreparationRequired) {
|
||||
m_displacement = state.displacement;
|
||||
report.updatedDisplacement = true;
|
||||
}
|
||||
|
||||
if (report.preparedStaticDependencies) {
|
||||
++m_statistics.staticPreparations;
|
||||
}
|
||||
if (report.preparedGeometryState) {
|
||||
++m_statistics.geometryPreparations;
|
||||
}
|
||||
if (report.preparedBaseState) {
|
||||
++m_statistics.baseStatePreparations;
|
||||
}
|
||||
|
||||
m_dependencies = dependencies;
|
||||
m_isPrepared = true;
|
||||
return report;
|
||||
}
|
||||
|
||||
bool BarotropicClosureLinearizationContext::IsPrepared() const noexcept {
|
||||
return m_isPrepared;
|
||||
}
|
||||
|
||||
bool BarotropicClosureLinearizationContext::MatchesRevisions(
|
||||
const BarotropicClosureRevisions &revisions
|
||||
bool BarotropicClosureLinearizationContext::MatchesDependencies(
|
||||
const BarotropicClosureDependencies &dependencies
|
||||
) const noexcept {
|
||||
return m_isPrepared && revisions == m_revisions;
|
||||
return m_isPrepared && dependencies == m_dependencies;
|
||||
}
|
||||
|
||||
std::uint64_t BarotropicClosureLinearizationContext::
|
||||
GetPreparationCount() const noexcept {
|
||||
return m_preparationCount;
|
||||
}
|
||||
|
||||
const BarotropicClosureRevisions &
|
||||
BarotropicClosureLinearizationContext::GetRevisions() const {
|
||||
const BarotropicClosureDependencies &BarotropicClosureLinearizationContext::GetDependencies() const {
|
||||
VerifyPrepared();
|
||||
return m_revisions;
|
||||
return m_dependencies;
|
||||
}
|
||||
|
||||
const mfem::Vector &
|
||||
BarotropicClosureLinearizationContext::GetBaseDensityTrue() const {
|
||||
const BarotropicClosurePreparationStatistics &
|
||||
BarotropicClosureLinearizationContext::GetPreparationStatistics() const noexcept {
|
||||
return m_statistics;
|
||||
}
|
||||
|
||||
const mfem::Vector &BarotropicClosureLinearizationContext::GetBaseDensity() const {
|
||||
VerifyPrepared();
|
||||
return m_baseDensityTrue;
|
||||
return m_baseDensity;
|
||||
}
|
||||
|
||||
const mfem::Vector &
|
||||
BarotropicClosureLinearizationContext::GetBaseEnthalpyTrue() const {
|
||||
const mfem::Vector &BarotropicClosureLinearizationContext::GetBaseEnthalpy() const {
|
||||
VerifyPrepared();
|
||||
return m_baseEnthalpyTrue;
|
||||
return m_baseEnthalpy;
|
||||
}
|
||||
|
||||
const mfem::Vector &
|
||||
BarotropicClosureLinearizationContext::GetDisplacementTrue() const {
|
||||
const mfem::Vector &BarotropicClosureLinearizationContext::GetDisplacement() const {
|
||||
VerifyPrepared();
|
||||
return m_displacementTrue;
|
||||
}
|
||||
|
||||
const PreparedBarotropicClosureOperator &
|
||||
BarotropicClosureLinearizationContext::GetOperator() const noexcept {
|
||||
return m_operator;
|
||||
}
|
||||
|
||||
void BarotropicClosureLinearizationContext::BuildResidual(
|
||||
mfem::Vector &residual
|
||||
) const {
|
||||
VerifyPrepared();
|
||||
m_operator.BuildResidual(residual);
|
||||
return m_displacement;
|
||||
}
|
||||
|
||||
void BarotropicClosureLinearizationContext::VerifyPrepared() const {
|
||||
MFEM_VERIFY(
|
||||
m_isPrepared, "The barotropic-closure linearization context "
|
||||
"has not been prepared."
|
||||
);
|
||||
MFEM_VERIFY(m_isPrepared, "BarotropicClosureLinearizationContext has not been prepared.");
|
||||
}
|
||||
} // namespace mean_field::operators::context::barotropic
|
||||
@@ -12,9 +12,8 @@ namespace {
|
||||
const mfem::Vector &displacement_true
|
||||
) {
|
||||
MFEM_VERIFY(
|
||||
f.displacementFes != nullptr,
|
||||
"GravityFieldGeometryContext requires the "
|
||||
"displacement finite-element space."
|
||||
f.displacementFes != nullptr, "GravityFieldGeometryContext requires the "
|
||||
"displacement finite-element space."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
displacement_true.Size() == f.displacementFes->GetTrueVSize(),
|
||||
@@ -25,18 +24,16 @@ namespace {
|
||||
|
||||
for (int i = 0; i < displacement_true.Size(); ++i) {
|
||||
MFEM_VERIFY(
|
||||
std::isfinite(displacement_true(i)),
|
||||
"GravityFieldGeometryContext received a non-finite "
|
||||
"displacement "
|
||||
"value."
|
||||
std::isfinite(displacement_true(i)), "GravityFieldGeometryContext received a non-finite "
|
||||
"displacement "
|
||||
"value."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void validate_linearization_state(
|
||||
const mean_field::fem::FEM &f,
|
||||
const mean_field::operators::context::gravity_field::
|
||||
GravityFieldStateView &state
|
||||
const mean_field::operators::context::gravity_field::GravityFieldStateView &state
|
||||
) {
|
||||
MFEM_VERIFY(
|
||||
f.densityFes != nullptr, "GravityFieldLinearizationContext "
|
||||
@@ -44,19 +41,16 @@ namespace {
|
||||
"space."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.gravityPotentialFes != nullptr,
|
||||
"GravityFieldLinearizationContext requires the gravity-potential "
|
||||
"finite-element space."
|
||||
f.gravityPotentialFes != nullptr, "GravityFieldLinearizationContext requires the gravity-potential "
|
||||
"finite-element space."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.gravityFluxFes != nullptr,
|
||||
"GravityFieldLinearizationContext requires the "
|
||||
"gravity-gradient finite-element space."
|
||||
f.gravityFluxFes != nullptr, "GravityFieldLinearizationContext requires the "
|
||||
"gravity-gradient finite-element space."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.displacementFes != nullptr,
|
||||
"GravityFieldLinearizationContext requires "
|
||||
"the displacement finite-element space."
|
||||
f.displacementFes != nullptr, "GravityFieldLinearizationContext requires "
|
||||
"the displacement finite-element space."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
@@ -78,8 +72,7 @@ namespace {
|
||||
"with the wrong size."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
state.gravity_potential.Size() ==
|
||||
f.gravityPotentialFes->GetTrueVSize(),
|
||||
state.gravity_potential.Size() == f.gravityPotentialFes->GetTrueVSize(),
|
||||
"GravityFieldLinearizationContext received a gravity-potential "
|
||||
"vector "
|
||||
"with the wrong size."
|
||||
@@ -87,35 +80,31 @@ namespace {
|
||||
|
||||
for (int i = 0; i < state.density.Size(); ++i) {
|
||||
MFEM_VERIFY(
|
||||
std::isfinite(state.density(i)),
|
||||
"GravityFieldLinearizationContext received a non-finite "
|
||||
"density "
|
||||
"value."
|
||||
std::isfinite(state.density(i)), "GravityFieldLinearizationContext received a non-finite "
|
||||
"density "
|
||||
"value."
|
||||
);
|
||||
}
|
||||
|
||||
for (int i = 0; i < state.displacement.Size(); ++i) {
|
||||
MFEM_VERIFY(
|
||||
std::isfinite(state.displacement(i)),
|
||||
"GravityFieldLinearizationContext received a non-finite "
|
||||
"displacement "
|
||||
"value."
|
||||
std::isfinite(state.displacement(i)), "GravityFieldLinearizationContext received a non-finite "
|
||||
"displacement "
|
||||
"value."
|
||||
);
|
||||
}
|
||||
|
||||
for (int i = 0; i < state.gravity_gradient.Size(); ++i) {
|
||||
MFEM_VERIFY(
|
||||
std::isfinite(state.gravity_gradient(i)),
|
||||
"GravityFieldLinearizationContext received a non-finite "
|
||||
"gravity-gradient value."
|
||||
std::isfinite(state.gravity_gradient(i)), "GravityFieldLinearizationContext received a non-finite "
|
||||
"gravity-gradient value."
|
||||
);
|
||||
}
|
||||
|
||||
for (int i = 0; i < state.gravity_potential.Size(); ++i) {
|
||||
MFEM_VERIFY(
|
||||
std::isfinite(state.gravity_potential(i)),
|
||||
"GravityFieldLinearizationContext received a non-finite "
|
||||
"gravity-potential value."
|
||||
std::isfinite(state.gravity_potential(i)), "GravityFieldLinearizationContext received a non-finite "
|
||||
"gravity-potential value."
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -128,42 +117,33 @@ namespace mean_field::operators::context::gravity_field {
|
||||
)
|
||||
: m_fem(f),
|
||||
m_domain_mapper(domain_mapper) {
|
||||
MFEM_VERIFY(f.mesh != nullptr, "GravityFieldGeometryContext requires a mesh.");
|
||||
MFEM_VERIFY(
|
||||
f.mesh != nullptr, "GravityFieldGeometryContext requires a mesh."
|
||||
f.gravityFluxFes != nullptr, "GravityFieldGeometryContext requires the "
|
||||
"gravity-gradient finite-element space."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.gravityFluxFes != nullptr,
|
||||
"GravityFieldGeometryContext requires the "
|
||||
"gravity-gradient finite-element space."
|
||||
f.densityFes != nullptr, "GravityFieldGeometryContext requires the density finite-element "
|
||||
"space."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.densityFes != nullptr,
|
||||
"GravityFieldGeometryContext requires the density finite-element "
|
||||
"space."
|
||||
f.gravityPotentialFes != nullptr, "GravityFieldGeometryContext requires the gravity-potential "
|
||||
"finite-element space."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.gravityPotentialFes != nullptr,
|
||||
"GravityFieldGeometryContext requires the gravity-potential "
|
||||
"finite-element space."
|
||||
f.displacementFes != nullptr, "GravityFieldGeometryContext requires the "
|
||||
"displacement finite-element space."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.displacementFes != nullptr,
|
||||
"GravityFieldGeometryContext requires the "
|
||||
"displacement finite-element space."
|
||||
f.compactificationFes != nullptr, "GravityFieldGeometryContext requires the compactification "
|
||||
"finite-element space."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.compactificationFes != nullptr,
|
||||
"GravityFieldGeometryContext requires the compactification "
|
||||
"finite-element space."
|
||||
f.compactificationCoordinate != nullptr, "GravityFieldGeometryContext requires the compactification "
|
||||
"coordinate."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.compactificationCoordinate != nullptr,
|
||||
"GravityFieldGeometryContext requires the compactification "
|
||||
"coordinate."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.quadratureFactory != nullptr,
|
||||
"GravityFieldGeometryContext requires the quadrature-rule factory."
|
||||
f.quadratureFactory != nullptr, "GravityFieldGeometryContext requires the quadrature-rule factory."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
domain_mapper.GetDimension() == f.mesh->Dimension(),
|
||||
@@ -192,11 +172,8 @@ namespace mean_field::operators::context::gravity_field {
|
||||
);
|
||||
}
|
||||
|
||||
const bool discretization_changed =
|
||||
!m_is_prepared ||
|
||||
discretization_revision != m_discretization_revision;
|
||||
const bool displacement_changed =
|
||||
!m_is_prepared || displacement_revision != m_displacement_revision;
|
||||
const bool discretization_changed = !m_is_prepared || discretization_revision != m_discretization_revision;
|
||||
const bool displacement_changed = !m_is_prepared || displacement_revision != m_displacement_revision;
|
||||
|
||||
GravityFieldGeometryPreparation preparation;
|
||||
|
||||
@@ -205,14 +182,8 @@ namespace mean_field::operators::context::gravity_field {
|
||||
}
|
||||
|
||||
if (discretization_changed) {
|
||||
auto mass_operator =
|
||||
std::make_unique<PreparedMappedHDivMassOperator>(
|
||||
m_fem, m_domain_mapper
|
||||
);
|
||||
auto source_operator =
|
||||
std::make_unique<PreparedMappedGravitySourceOperator>(
|
||||
m_fem, m_domain_mapper
|
||||
);
|
||||
auto mass_operator = std::make_unique<PreparedMappedHDivMassOperator>(m_fem, m_domain_mapper);
|
||||
auto source_operator = std::make_unique<PreparedMappedGravitySourceOperator>(m_fem, m_domain_mapper);
|
||||
|
||||
mass_operator->Prepare(displacement_true);
|
||||
source_operator->Prepare(displacement_true);
|
||||
@@ -229,9 +200,8 @@ namespace mean_field::operators::context::gravity_field {
|
||||
"no prepared H(div) mass operator."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
m_source_operator != nullptr,
|
||||
"GravityFieldGeometryContext has no prepared gravity source "
|
||||
"operator."
|
||||
m_source_operator != nullptr, "GravityFieldGeometryContext has no prepared gravity source "
|
||||
"operator."
|
||||
);
|
||||
|
||||
m_mass_operator->Prepare(displacement_true);
|
||||
@@ -251,26 +221,19 @@ namespace mean_field::operators::context::gravity_field {
|
||||
return preparation;
|
||||
}
|
||||
|
||||
const PreparedMappedHDivMassOperator &
|
||||
GravityFieldGeometryContext::GetMassOperator() const {
|
||||
const PreparedMappedHDivMassOperator &GravityFieldGeometryContext::GetMassOperator() const {
|
||||
MFEM_VERIFY(
|
||||
m_is_prepared,
|
||||
"GravityFieldGeometryContext must be prepared before "
|
||||
"accessing its mass operator."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
m_mass_operator != nullptr,
|
||||
"GravityFieldGeometryContext has no prepared H(div) mass operator."
|
||||
m_is_prepared, "GravityFieldGeometryContext must be prepared before "
|
||||
"accessing its mass operator."
|
||||
);
|
||||
MFEM_VERIFY(m_mass_operator != nullptr, "GravityFieldGeometryContext has no prepared H(div) mass operator.");
|
||||
return *m_mass_operator;
|
||||
}
|
||||
|
||||
const PreparedMappedGravitySourceOperator &
|
||||
GravityFieldGeometryContext::GetSourceOperator() const {
|
||||
const PreparedMappedGravitySourceOperator &GravityFieldGeometryContext::GetSourceOperator() const {
|
||||
MFEM_VERIFY(
|
||||
m_is_prepared,
|
||||
"GravityFieldGeometryContext must be prepared before "
|
||||
"accessing its source operator."
|
||||
m_is_prepared, "GravityFieldGeometryContext must be prepared before "
|
||||
"accessing its source operator."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
m_source_operator != nullptr, "GravityFieldGeometryContext has no "
|
||||
@@ -281,20 +244,17 @@ namespace mean_field::operators::context::gravity_field {
|
||||
|
||||
const mfem::Vector &GravityFieldGeometryContext::GetDisplacement() const {
|
||||
MFEM_VERIFY(
|
||||
m_is_prepared,
|
||||
"GravityFieldGeometryContext must be prepared before "
|
||||
"accessing its displacement."
|
||||
m_is_prepared, "GravityFieldGeometryContext must be prepared before "
|
||||
"accessing its displacement."
|
||||
);
|
||||
return m_displacement_true;
|
||||
}
|
||||
|
||||
DiscretizationRevision
|
||||
GravityFieldGeometryContext::GetDiscretizationRevision() const noexcept {
|
||||
DiscretizationRevision GravityFieldGeometryContext::GetDiscretizationRevision() const noexcept {
|
||||
return m_discretization_revision;
|
||||
}
|
||||
|
||||
DisplacementRevision
|
||||
GravityFieldGeometryContext::GetDisplacementRevision() const noexcept {
|
||||
DisplacementRevision GravityFieldGeometryContext::GetDisplacementRevision() const noexcept {
|
||||
return m_displacement_revision;
|
||||
}
|
||||
|
||||
@@ -317,19 +277,16 @@ namespace mean_field::operators::context::gravity_field {
|
||||
"space."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.gravityPotentialFes != nullptr,
|
||||
"GravityFieldLinearizationContext requires the gravity-potential "
|
||||
"finite-element space."
|
||||
f.gravityPotentialFes != nullptr, "GravityFieldLinearizationContext requires the gravity-potential "
|
||||
"finite-element space."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.gravityFluxFes != nullptr,
|
||||
"GravityFieldLinearizationContext requires the "
|
||||
"gravity-gradient finite-element space."
|
||||
f.gravityFluxFes != nullptr, "GravityFieldLinearizationContext requires the "
|
||||
"gravity-gradient finite-element space."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.displacementFes != nullptr,
|
||||
"GravityFieldLinearizationContext requires "
|
||||
"the displacement finite-element space."
|
||||
f.displacementFes != nullptr, "GravityFieldLinearizationContext requires "
|
||||
"the displacement finite-element space."
|
||||
);
|
||||
}
|
||||
|
||||
@@ -353,9 +310,8 @@ namespace mean_field::operators::context::gravity_field {
|
||||
"revision."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
revisions.density >= m_revisions.density,
|
||||
"GravityFieldLinearizationContext received an older density "
|
||||
"revision."
|
||||
revisions.density >= m_revisions.density, "GravityFieldLinearizationContext received an older density "
|
||||
"revision."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
revisions.gravity_gradient >= m_revisions.gravity_gradient,
|
||||
@@ -370,20 +326,16 @@ namespace mean_field::operators::context::gravity_field {
|
||||
);
|
||||
}
|
||||
|
||||
const bool discretization_changed =
|
||||
!m_is_prepared ||
|
||||
revisions.discretization != m_revisions.discretization;
|
||||
const bool density_changed = !m_is_prepared || discretization_changed ||
|
||||
revisions.density != m_revisions.density;
|
||||
const bool discretization_changed = !m_is_prepared || revisions.discretization != m_revisions.discretization;
|
||||
const bool density_changed =
|
||||
!m_is_prepared || discretization_changed || revisions.density != m_revisions.density;
|
||||
const bool gravity_gradient_changed =
|
||||
!m_is_prepared || discretization_changed ||
|
||||
revisions.gravity_gradient != m_revisions.gravity_gradient;
|
||||
!m_is_prepared || discretization_changed || revisions.gravity_gradient != m_revisions.gravity_gradient;
|
||||
|
||||
GravityFieldPreparationReport report;
|
||||
|
||||
report.geometry = m_geometry_context.Prepare(
|
||||
state.displacement, revisions.discretization, revisions.displacement
|
||||
);
|
||||
report.geometry =
|
||||
m_geometry_context.Prepare(state.displacement, revisions.discretization, revisions.displacement);
|
||||
|
||||
if (density_changed) {
|
||||
m_density_true = state.density;
|
||||
@@ -401,8 +353,7 @@ namespace mean_field::operators::context::gravity_field {
|
||||
return report;
|
||||
}
|
||||
|
||||
const GravityFieldGeometryContext &
|
||||
GravityFieldLinearizationContext::GetGeometryContext() const {
|
||||
const GravityFieldGeometryContext &GravityFieldLinearizationContext::GetGeometryContext() const {
|
||||
MFEM_VERIFY(
|
||||
m_is_prepared, "GravityFieldLinearizationContext must be prepared "
|
||||
"before accessing its geometry context."
|
||||
@@ -418,8 +369,7 @@ namespace mean_field::operators::context::gravity_field {
|
||||
return m_density_true;
|
||||
}
|
||||
|
||||
const mfem::Vector &
|
||||
GravityFieldLinearizationContext::GetGravityGradient() const {
|
||||
const mfem::Vector &GravityFieldLinearizationContext::GetGravityGradient() const {
|
||||
MFEM_VERIFY(
|
||||
m_is_prepared, "GravityFieldLinearizationContext must be prepared "
|
||||
"before accessing its gravity gradient."
|
||||
@@ -427,8 +377,7 @@ namespace mean_field::operators::context::gravity_field {
|
||||
return m_gravity_gradient_true;
|
||||
}
|
||||
|
||||
const GravityFieldRevisions &
|
||||
GravityFieldLinearizationContext::GetRevisions() const {
|
||||
const GravityFieldRevisions &GravityFieldLinearizationContext::GetRevisions() const {
|
||||
MFEM_VERIFY(
|
||||
m_is_prepared, "GravityFieldLinearizationContext must be prepared "
|
||||
"before accessing its revisions."
|
||||
|
||||
@@ -20,44 +20,37 @@ namespace {
|
||||
|
||||
void validate_state(
|
||||
const mean_field::fem::FEM &f,
|
||||
const mean_field::operators::context::hydrostatic::
|
||||
HydrostaticEquilibriumStateView &state
|
||||
const mean_field::operators::context::hydrostatic::HydrostaticEquilibriumStateView &state
|
||||
) {
|
||||
MFEM_VERIFY(
|
||||
f.enthalpyFes != nullptr,
|
||||
"HydrostaticEquilibriumContext requires the "
|
||||
"enthalpy finite-element space."
|
||||
f.enthalpyFes != nullptr, "HydrostaticEquilibriumContext requires the "
|
||||
"enthalpy finite-element space."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
f.gravityPotentialFes != nullptr,
|
||||
"HydrostaticEquilibriumContext requires the "
|
||||
"gravity-potential finite-element space."
|
||||
f.gravityPotentialFes != nullptr, "HydrostaticEquilibriumContext requires the "
|
||||
"gravity-potential finite-element space."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
f.displacementFes != nullptr,
|
||||
"HydrostaticEquilibriumContext requires the "
|
||||
"displacement finite-element space."
|
||||
f.displacementFes != nullptr, "HydrostaticEquilibriumContext requires the "
|
||||
"displacement finite-element space."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
state.enthalpy.Size() == f.enthalpyFes->GetTrueVSize(),
|
||||
"HydrostaticEquilibriumContext received an "
|
||||
"enthalpy vector with the wrong size."
|
||||
state.enthalpy.Size() == f.enthalpyFes->GetTrueVSize(), "HydrostaticEquilibriumContext received an "
|
||||
"enthalpy vector with the wrong size."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
state.gravityPotential.Size() ==
|
||||
f.gravityPotentialFes->GetTrueVSize(),
|
||||
state.gravityPotential.Size() == f.gravityPotentialFes->GetTrueVSize(),
|
||||
"HydrostaticEquilibriumContext received a "
|
||||
"gravity-potential vector with the wrong size."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
state.displacement.Size() == f.displacementFes->GetTrueVSize(),
|
||||
"HydrostaticEquilibriumContext received a "
|
||||
"displacement vector with the wrong size."
|
||||
state.displacement.Size() == f.displacementFes->GetTrueVSize(), "HydrostaticEquilibriumContext received a "
|
||||
"displacement vector with the wrong size."
|
||||
);
|
||||
|
||||
validate_finite_vector(
|
||||
@@ -76,9 +69,8 @@ namespace {
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
std::isfinite(state.bernoulliConstant),
|
||||
"HydrostaticEquilibriumContext received a "
|
||||
"non-finite Bernoulli constant."
|
||||
std::isfinite(state.bernoulliConstant), "HydrostaticEquilibriumContext received a "
|
||||
"non-finite Bernoulli constant."
|
||||
);
|
||||
}
|
||||
|
||||
@@ -99,34 +91,27 @@ namespace mean_field::operators::context::hydrostatic {
|
||||
)
|
||||
: m_f(f),
|
||||
m_domainMapper(domainMapper) {
|
||||
MFEM_VERIFY(m_f.mesh != nullptr, "HydrostaticEquilibriumContext requires a mesh.");
|
||||
|
||||
MFEM_VERIFY(
|
||||
m_f.mesh != nullptr,
|
||||
"HydrostaticEquilibriumContext requires a mesh."
|
||||
m_f.enthalpyFes != nullptr, "HydrostaticEquilibriumContext requires the "
|
||||
"enthalpy finite-element space."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
m_f.enthalpyFes != nullptr,
|
||||
"HydrostaticEquilibriumContext requires the "
|
||||
"enthalpy finite-element space."
|
||||
m_f.gravityPotentialFes != nullptr, "HydrostaticEquilibriumContext requires the "
|
||||
"gravity-potential finite-element space."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
m_f.gravityPotentialFes != nullptr,
|
||||
"HydrostaticEquilibriumContext requires the "
|
||||
"gravity-potential finite-element space."
|
||||
m_f.displacementFes != nullptr, "HydrostaticEquilibriumContext requires the "
|
||||
"displacement finite-element space."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
m_f.displacementFes != nullptr,
|
||||
"HydrostaticEquilibriumContext requires the "
|
||||
"displacement finite-element space."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
m_domainMapper.GetDimension() == m_f.mesh->Dimension(),
|
||||
"The hydrostatic context's stateless "
|
||||
"domain-mapper dimension does not match the mesh "
|
||||
"dimension."
|
||||
m_domainMapper.GetDimension() == m_f.mesh->Dimension(), "The hydrostatic context's stateless "
|
||||
"domain-mapper dimension does not match the mesh "
|
||||
"dimension."
|
||||
);
|
||||
}
|
||||
|
||||
@@ -168,44 +153,32 @@ namespace mean_field::operators::context::hydrostatic {
|
||||
);
|
||||
|
||||
validate_dependency_transition(
|
||||
m_dependencies.bernoulliConstant,
|
||||
dependencies.bernoulliConstant,
|
||||
m_dependencies.bernoulliConstant, dependencies.bernoulliConstant,
|
||||
"HydrostaticEquilibriumContext received an older "
|
||||
"Bernoulli-constant revision for the same identity."
|
||||
);
|
||||
}
|
||||
|
||||
const bool staticChanged =
|
||||
!m_isPrepared ||
|
||||
dependencies.discretization != m_dependencies.discretization;
|
||||
const bool staticChanged = !m_isPrepared || dependencies.discretization != m_dependencies.discretization;
|
||||
|
||||
const bool enthalpyChanged =
|
||||
!m_isPrepared || dependencies.enthalpy != m_dependencies.enthalpy;
|
||||
const bool enthalpyChanged = !m_isPrepared || dependencies.enthalpy != m_dependencies.enthalpy;
|
||||
|
||||
const bool gravityPotentialChanged =
|
||||
!m_isPrepared ||
|
||||
dependencies.gravityPotential != m_dependencies.gravityPotential;
|
||||
!m_isPrepared || dependencies.gravityPotential != m_dependencies.gravityPotential;
|
||||
|
||||
const bool displacementChanged =
|
||||
!m_isPrepared ||
|
||||
dependencies.displacement != m_dependencies.displacement;
|
||||
const bool displacementChanged = !m_isPrepared || dependencies.displacement != m_dependencies.displacement;
|
||||
|
||||
const bool rotationChanged =
|
||||
!m_isPrepared || dependencies.rotation != m_dependencies.rotation;
|
||||
const bool rotationChanged = !m_isPrepared || dependencies.rotation != m_dependencies.rotation;
|
||||
|
||||
const bool bernoulliConstantChanged =
|
||||
!m_isPrepared ||
|
||||
dependencies.bernoulliConstant != m_dependencies.bernoulliConstant;
|
||||
!m_isPrepared || dependencies.bernoulliConstant != m_dependencies.bernoulliConstant;
|
||||
|
||||
const bool geometryPreparationRequired =
|
||||
staticChanged || displacementChanged;
|
||||
const bool geometryPreparationRequired = staticChanged || displacementChanged;
|
||||
|
||||
const bool rotationPreparationRequired =
|
||||
geometryPreparationRequired || rotationChanged;
|
||||
const bool rotationPreparationRequired = geometryPreparationRequired || rotationChanged;
|
||||
|
||||
const bool baseStatePreparationRequired =
|
||||
rotationPreparationRequired || enthalpyChanged ||
|
||||
gravityPotentialChanged || bernoulliConstantChanged;
|
||||
rotationPreparationRequired || enthalpyChanged || gravityPotentialChanged || bernoulliConstantChanged;
|
||||
|
||||
HydrostaticPreparationReport report;
|
||||
|
||||
@@ -267,31 +240,26 @@ namespace mean_field::operators::context::hydrostatic {
|
||||
return m_isPrepared && dependencies == m_dependencies;
|
||||
}
|
||||
|
||||
const HydrostaticEquilibriumDependencies &
|
||||
HydrostaticEquilibriumContext::GetDependencies() const {
|
||||
const HydrostaticEquilibriumDependencies &HydrostaticEquilibriumContext::GetDependencies() const {
|
||||
VerifyPrepared();
|
||||
return m_dependencies;
|
||||
}
|
||||
|
||||
const HydrostaticPreparationStatistics &
|
||||
HydrostaticEquilibriumContext::GetPreparationStatistics() const noexcept {
|
||||
const HydrostaticPreparationStatistics &HydrostaticEquilibriumContext::GetPreparationStatistics() const noexcept {
|
||||
return m_statistics;
|
||||
}
|
||||
|
||||
const mfem::Vector &
|
||||
HydrostaticEquilibriumContext::GetBaseEnthalpyTrue() const {
|
||||
const mfem::Vector &HydrostaticEquilibriumContext::GetBaseEnthalpyTrue() const {
|
||||
VerifyPrepared();
|
||||
return m_baseEnthalpyTrue;
|
||||
}
|
||||
|
||||
const mfem::Vector &
|
||||
HydrostaticEquilibriumContext::GetBaseGravityPotentialTrue() const {
|
||||
const mfem::Vector &HydrostaticEquilibriumContext::GetBaseGravityPotentialTrue() const {
|
||||
VerifyPrepared();
|
||||
return m_baseGravityPotentialTrue;
|
||||
}
|
||||
|
||||
const mfem::Vector &
|
||||
HydrostaticEquilibriumContext::GetDisplacementTrue() const {
|
||||
const mfem::Vector &HydrostaticEquilibriumContext::GetDisplacementTrue() const {
|
||||
VerifyPrepared();
|
||||
return m_displacementTrue;
|
||||
}
|
||||
@@ -302,8 +270,6 @@ namespace mean_field::operators::context::hydrostatic {
|
||||
}
|
||||
|
||||
void HydrostaticEquilibriumContext::VerifyPrepared() const {
|
||||
MFEM_VERIFY(
|
||||
m_isPrepared, "HydrostaticEquilibriumContext has not been prepared."
|
||||
);
|
||||
MFEM_VERIFY(m_isPrepared, "HydrostaticEquilibriumContext has not been prepared.");
|
||||
}
|
||||
} // namespace mean_field::operators::context::hydrostatic
|
||||
|
||||
219
libmeanfield/impl/operators/contexts/pressure_force_context.cpp
Normal file
219
libmeanfield/impl/operators/contexts/pressure_force_context.cpp
Normal file
@@ -0,0 +1,219 @@
|
||||
module;
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include <mfem.hpp>
|
||||
|
||||
module mean_field;
|
||||
|
||||
import :operators.context.pressure_force;
|
||||
|
||||
namespace {
|
||||
void validate_finite_vector(
|
||||
const mfem::Vector &vector,
|
||||
const char *message
|
||||
) {
|
||||
for (int index = 0; index < vector.Size(); ++index) {
|
||||
MFEM_VERIFY(std::isfinite(vector(index)), message);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Dependency>
|
||||
void validate_dependency_transition(
|
||||
const Dependency &prepared,
|
||||
const Dependency &requested,
|
||||
const char *message
|
||||
) {
|
||||
MFEM_VERIFY(requested.CanFollow(prepared), message);
|
||||
|
||||
MFEM_VERIFY(
|
||||
prepared.identity == requested.identity || prepared.revision != requested.revision,
|
||||
"A new pressure-force dependency identity must also carry "
|
||||
"a visibly different revision."
|
||||
);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace mean_field::operators::context::pressure_force {
|
||||
PressureForceLinearizationContext::PressureForceLinearizationContext(
|
||||
const fem::FEM &f,
|
||||
const mapping::DomainMapperStateless &domainMapper,
|
||||
const field::FieldDofMap &enthalpyMap,
|
||||
const field::FieldDofMap &displacementMap
|
||||
)
|
||||
: m_enthalpySize(enthalpyMap.reduced_size()),
|
||||
m_displacementSize(displacementMap.reduced_size()) {
|
||||
MFEM_VERIFY(f.mesh != nullptr, "PressureForceLinearizationContext requires a mesh.");
|
||||
|
||||
MFEM_VERIFY(
|
||||
f.enthalpyFes != nullptr, "PressureForceLinearizationContext requires the enthalpy "
|
||||
"finite-element space."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
f.displacementFes != nullptr, "PressureForceLinearizationContext requires the displacement "
|
||||
"finite-element space."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
domainMapper.GetDimension() == f.mesh->Dimension(), "The pressure-force context's stateless domain-mapper "
|
||||
"dimension does not match the mesh dimension."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
enthalpyMap.full_size() == f.enthalpyFes->GetTrueVSize(),
|
||||
"The pressure-force enthalpy FieldDofMap does not match the "
|
||||
"enthalpy finite-element space."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
displacementMap.full_size() == f.displacementFes->GetTrueVSize(),
|
||||
"The pressure-force displacement FieldDofMap does not match "
|
||||
"the displacement finite-element space."
|
||||
);
|
||||
}
|
||||
|
||||
PressureForcePreparationReport PressureForceLinearizationContext::Prepare(
|
||||
const PressureForceStateView &state,
|
||||
const PressureForceDependencies &dependencies
|
||||
) {
|
||||
MFEM_VERIFY(
|
||||
state.enthalpy.Size() == m_enthalpySize, "PressureForceLinearizationContext received a supported "
|
||||
"enthalpy vector with the wrong size."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
state.displacement.Size() == m_displacementSize, "PressureForceLinearizationContext received a supported "
|
||||
"displacement vector with the wrong size."
|
||||
);
|
||||
|
||||
validate_finite_vector(
|
||||
state.enthalpy, "PressureForceLinearizationContext received a non-finite "
|
||||
"enthalpy value."
|
||||
);
|
||||
|
||||
validate_finite_vector(
|
||||
state.displacement, "PressureForceLinearizationContext received a non-finite "
|
||||
"displacement value."
|
||||
);
|
||||
|
||||
if (m_isPrepared) {
|
||||
validate_dependency_transition(
|
||||
m_dependencies.discretization, dependencies.discretization,
|
||||
"PressureForceLinearizationContext received an older "
|
||||
"discretization revision for the same identity."
|
||||
);
|
||||
|
||||
validate_dependency_transition(
|
||||
m_dependencies.enthalpy, dependencies.enthalpy,
|
||||
"PressureForceLinearizationContext received an older "
|
||||
"enthalpy revision for the same identity."
|
||||
);
|
||||
|
||||
validate_dependency_transition(
|
||||
m_dependencies.displacement, dependencies.displacement,
|
||||
"PressureForceLinearizationContext received an older "
|
||||
"displacement revision for the same identity."
|
||||
);
|
||||
}
|
||||
|
||||
const bool discretizationChanged =
|
||||
!m_isPrepared || dependencies.discretization != m_dependencies.discretization;
|
||||
|
||||
const bool enthalpyChanged = !m_isPrepared || dependencies.enthalpy != m_dependencies.enthalpy;
|
||||
|
||||
const bool displacementChanged = !m_isPrepared || dependencies.displacement != m_dependencies.displacement;
|
||||
|
||||
/*
|
||||
* Static data depend only on discretization.
|
||||
*
|
||||
* Geometry data depend on discretization and displacement.
|
||||
*
|
||||
* Material data depend on both geometry and enthalpy because
|
||||
* pressure and its enthalpy derivative are evaluated on the frozen
|
||||
* mapped state.
|
||||
*/
|
||||
const bool geometryPreparationRequired = discretizationChanged || displacementChanged;
|
||||
|
||||
const bool materialPreparationRequired = geometryPreparationRequired || enthalpyChanged;
|
||||
|
||||
PressureForcePreparationReport report;
|
||||
|
||||
report.preparedStaticDependencies = discretizationChanged;
|
||||
|
||||
report.preparedGeometryState = geometryPreparationRequired;
|
||||
|
||||
report.preparedMaterialState = materialPreparationRequired;
|
||||
|
||||
/*
|
||||
* A discretization change invalidates every frozen field because
|
||||
* their coordinate interpretation may have changed.
|
||||
*/
|
||||
if (discretizationChanged || enthalpyChanged) {
|
||||
m_baseEnthalpy = state.enthalpy;
|
||||
|
||||
report.updatedEnthalpy = true;
|
||||
}
|
||||
|
||||
if (geometryPreparationRequired) {
|
||||
m_displacement = state.displacement;
|
||||
|
||||
report.updatedDisplacement = true;
|
||||
}
|
||||
|
||||
if (report.preparedStaticDependencies) {
|
||||
++m_statistics.staticPreparations;
|
||||
}
|
||||
|
||||
if (report.preparedGeometryState) {
|
||||
++m_statistics.geometryPreparations;
|
||||
}
|
||||
|
||||
if (report.preparedMaterialState) {
|
||||
++m_statistics.materialPreparations;
|
||||
}
|
||||
|
||||
m_dependencies = dependencies;
|
||||
|
||||
m_isPrepared = true;
|
||||
|
||||
return report;
|
||||
}
|
||||
|
||||
const PressureForcePreparationStatistics &
|
||||
PressureForceLinearizationContext::GetPreparationStatistics() const noexcept {
|
||||
return m_statistics;
|
||||
}
|
||||
|
||||
bool PressureForceLinearizationContext::IsPrepared() const noexcept {
|
||||
return m_isPrepared;
|
||||
}
|
||||
|
||||
bool PressureForceLinearizationContext::MatchesDependencies(
|
||||
const PressureForceDependencies &dependencies
|
||||
) const noexcept {
|
||||
return m_isPrepared && dependencies == m_dependencies;
|
||||
}
|
||||
|
||||
const PressureForceDependencies &PressureForceLinearizationContext::GetDependencies() const {
|
||||
VerifyPrepared();
|
||||
|
||||
return m_dependencies;
|
||||
}
|
||||
|
||||
const mfem::Vector &PressureForceLinearizationContext::GetBaseEnthalpy() const {
|
||||
VerifyPrepared();
|
||||
|
||||
return m_baseEnthalpy;
|
||||
}
|
||||
|
||||
const mfem::Vector &PressureForceLinearizationContext::GetDisplacement() const {
|
||||
VerifyPrepared();
|
||||
|
||||
return m_displacement;
|
||||
}
|
||||
|
||||
void PressureForceLinearizationContext::VerifyPrepared() const {
|
||||
MFEM_VERIFY(m_isPrepared, "PressureForceLinearizationContext has not been prepared.");
|
||||
}
|
||||
} // namespace mean_field::operators::context::pressure_force
|
||||
@@ -0,0 +1,203 @@
|
||||
module;
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include <mfem.hpp>
|
||||
|
||||
module mean_field;
|
||||
|
||||
import :operators.context.rotational_displacement_force;
|
||||
|
||||
namespace {
|
||||
void validate_finite_vector(
|
||||
const mfem::Vector &vector,
|
||||
const char *message
|
||||
) {
|
||||
for (int index = 0; index < vector.Size(); ++index) {
|
||||
MFEM_VERIFY(std::isfinite(vector(index)), message);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Dependency>
|
||||
void validate_dependency_transition(
|
||||
const Dependency &prepared,
|
||||
const Dependency &requested,
|
||||
const char *message
|
||||
) {
|
||||
MFEM_VERIFY(requested.CanFollow(prepared), message);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace mean_field::operators::context::rotational_displacement_force {
|
||||
RotationalDisplacementForceLinearizationContext::RotationalDisplacementForceLinearizationContext(
|
||||
const fem::FEM &f,
|
||||
const mapping::DomainMapperStateless &domainMapper
|
||||
)
|
||||
: m_f(f) {
|
||||
MFEM_VERIFY(
|
||||
m_f.mesh != nullptr, "RotationalDisplacementForceLinearizationContext requires a "
|
||||
"mesh."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
m_f.densityFes != nullptr, "RotationalDisplacementForceLinearizationContext requires the "
|
||||
"density finite-element space."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
m_f.displacementFes != nullptr, "RotationalDisplacementForceLinearizationContext requires the "
|
||||
"displacement finite-element space."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
domainMapper.GetDimension() == m_f.mesh->Dimension(),
|
||||
"The rotational-displacement-force context's stateless "
|
||||
"domain-mapper dimension does not match the mesh dimension."
|
||||
);
|
||||
}
|
||||
|
||||
RotationalDisplacementForcePreparationReport RotationalDisplacementForceLinearizationContext::Prepare(
|
||||
const RotationalDisplacementForceStateView &state,
|
||||
const RotationalDisplacementForceDependencies &dependencies
|
||||
) {
|
||||
MFEM_VERIFY(
|
||||
state.density.Size() == m_f.densityFes->GetTrueVSize(),
|
||||
"RotationalDisplacementForceLinearizationContext received a "
|
||||
"density vector with the wrong size."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
state.displacement.Size() == m_f.displacementFes->GetTrueVSize(),
|
||||
"RotationalDisplacementForceLinearizationContext received a "
|
||||
"displacement vector with the wrong size."
|
||||
);
|
||||
|
||||
validate_finite_vector(
|
||||
state.density, "RotationalDisplacementForceLinearizationContext received a "
|
||||
"non-finite density value."
|
||||
);
|
||||
|
||||
validate_finite_vector(
|
||||
state.displacement, "RotationalDisplacementForceLinearizationContext received a "
|
||||
"non-finite displacement value."
|
||||
);
|
||||
|
||||
if (m_isPrepared) {
|
||||
validate_dependency_transition(
|
||||
m_dependencies.discretization, dependencies.discretization,
|
||||
"RotationalDisplacementForceLinearizationContext received "
|
||||
"an older discretization revision for the same identity."
|
||||
);
|
||||
|
||||
validate_dependency_transition(
|
||||
m_dependencies.density, dependencies.density,
|
||||
"RotationalDisplacementForceLinearizationContext received "
|
||||
"an older density revision for the same identity."
|
||||
);
|
||||
|
||||
validate_dependency_transition(
|
||||
m_dependencies.displacement, dependencies.displacement,
|
||||
"RotationalDisplacementForceLinearizationContext received "
|
||||
"an older displacement revision for the same identity."
|
||||
);
|
||||
|
||||
validate_dependency_transition(
|
||||
m_dependencies.rotation, dependencies.rotation,
|
||||
"RotationalDisplacementForceLinearizationContext received "
|
||||
"an older rotation revision for the same identity."
|
||||
);
|
||||
}
|
||||
|
||||
const bool discretizationChanged =
|
||||
!m_isPrepared || dependencies.discretization != m_dependencies.discretization;
|
||||
|
||||
const bool densityChanged = !m_isPrepared || dependencies.density != m_dependencies.density;
|
||||
|
||||
const bool displacementChanged = !m_isPrepared || dependencies.displacement != m_dependencies.displacement;
|
||||
|
||||
const bool rotationChanged = !m_isPrepared || dependencies.rotation != m_dependencies.rotation;
|
||||
|
||||
const bool geometryPreparationRequired = discretizationChanged || displacementChanged;
|
||||
|
||||
const bool rotationPreparationRequired = discretizationChanged || rotationChanged;
|
||||
|
||||
const bool baseStatePreparationRequired =
|
||||
geometryPreparationRequired || rotationPreparationRequired || densityChanged;
|
||||
|
||||
RotationalDisplacementForcePreparationReport report;
|
||||
|
||||
report.preparedStaticDependencies = discretizationChanged;
|
||||
report.preparedGeometryState = geometryPreparationRequired;
|
||||
report.preparedRotationDependencies = rotationPreparationRequired;
|
||||
report.preparedBaseState = baseStatePreparationRequired;
|
||||
|
||||
if (discretizationChanged || densityChanged) {
|
||||
m_baseDensityTrue = state.density;
|
||||
report.updatedDensity = true;
|
||||
}
|
||||
|
||||
if (geometryPreparationRequired) {
|
||||
m_displacementTrue = state.displacement;
|
||||
report.updatedDisplacement = true;
|
||||
}
|
||||
|
||||
if (report.preparedStaticDependencies) {
|
||||
++m_statistics.staticPreparations;
|
||||
}
|
||||
|
||||
if (report.preparedGeometryState) {
|
||||
++m_statistics.geometryPreparations;
|
||||
}
|
||||
|
||||
if (report.preparedRotationDependencies) {
|
||||
++m_statistics.rotationPreparations;
|
||||
}
|
||||
|
||||
if (report.preparedBaseState) {
|
||||
++m_statistics.baseStatePreparations;
|
||||
}
|
||||
|
||||
m_dependencies = dependencies;
|
||||
m_isPrepared = true;
|
||||
|
||||
return report;
|
||||
}
|
||||
|
||||
bool RotationalDisplacementForceLinearizationContext::IsPrepared() const noexcept {
|
||||
return m_isPrepared;
|
||||
}
|
||||
|
||||
bool RotationalDisplacementForceLinearizationContext::MatchesDependencies(
|
||||
const RotationalDisplacementForceDependencies &dependencies
|
||||
) const noexcept {
|
||||
return m_isPrepared && dependencies == m_dependencies;
|
||||
}
|
||||
|
||||
const RotationalDisplacementForceDependencies &
|
||||
RotationalDisplacementForceLinearizationContext::GetDependencies() const {
|
||||
VerifyPrepared();
|
||||
return m_dependencies;
|
||||
}
|
||||
|
||||
const RotationalDisplacementForcePreparationStatistics &
|
||||
RotationalDisplacementForceLinearizationContext::GetPreparationStatistics() const noexcept {
|
||||
return m_statistics;
|
||||
}
|
||||
|
||||
const mfem::Vector &RotationalDisplacementForceLinearizationContext::GetBaseDensityTrue() const {
|
||||
VerifyPrepared();
|
||||
return m_baseDensityTrue;
|
||||
}
|
||||
|
||||
const mfem::Vector &RotationalDisplacementForceLinearizationContext::GetDisplacementTrue() const {
|
||||
VerifyPrepared();
|
||||
return m_displacementTrue;
|
||||
}
|
||||
|
||||
void RotationalDisplacementForceLinearizationContext::VerifyPrepared() const {
|
||||
MFEM_VERIFY(
|
||||
m_isPrepared, "RotationalDisplacementForceLinearizationContext has not been "
|
||||
"prepared."
|
||||
);
|
||||
}
|
||||
} // namespace mean_field::operators::context::rotational_displacement_force
|
||||
@@ -13,43 +13,31 @@ import :operators.kernels.gravity_field;
|
||||
namespace {
|
||||
using namespace mean_field;
|
||||
int get_state_width(const mfem::Array<int> &state_true_offsets) {
|
||||
MFEM_VERIFY(
|
||||
state_true_offsets.Size() >= 2,
|
||||
"The coupled state requires at least one block."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
state_true_offsets[0] == 0,
|
||||
"The coupled state offsets must begin at zero."
|
||||
);
|
||||
MFEM_VERIFY(state_true_offsets.Size() >= 2, "The coupled state requires at least one block.");
|
||||
MFEM_VERIFY(state_true_offsets[0] == 0, "The coupled state offsets must begin at zero.");
|
||||
|
||||
for (int i = 0; i < state_true_offsets.Size() - 1; ++i) {
|
||||
MFEM_VERIFY(
|
||||
state_true_offsets[i + 1] >= state_true_offsets[i],
|
||||
"The coupled state offsets must be nondecreasing."
|
||||
state_true_offsets[i + 1] >= state_true_offsets[i], "The coupled state offsets must be nondecreasing."
|
||||
);
|
||||
}
|
||||
|
||||
MFEM_VERIFY(
|
||||
state_true_offsets.Last() > 0, "The coupled state cannot be empty."
|
||||
);
|
||||
MFEM_VERIFY(state_true_offsets.Last() > 0, "The coupled state cannot be empty.");
|
||||
return state_true_offsets.Last();
|
||||
}
|
||||
|
||||
int get_gravity_residual_height(const fem::FEM &f) {
|
||||
MFEM_VERIFY(
|
||||
f.gravityFluxFes != nullptr,
|
||||
"GravityFieldOperator requires the gravity-gradient finite-element "
|
||||
"space (RT: Raviart-Thomas)."
|
||||
f.gravityFluxFes != nullptr, "GravityFieldOperator requires the gravity-gradient finite-element "
|
||||
"space (RT: Raviart-Thomas)."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.gravityPotentialFes != nullptr,
|
||||
"GravityFieldOperator requires the gravity-potential "
|
||||
"finite-element "
|
||||
"space (L2: Lebesgue "
|
||||
"space of square-integrable functions)."
|
||||
f.gravityPotentialFes != nullptr, "GravityFieldOperator requires the gravity-potential "
|
||||
"finite-element "
|
||||
"space (L2: Lebesgue "
|
||||
"space of square-integrable functions)."
|
||||
);
|
||||
return f.gravityFluxFes->GetTrueVSize() +
|
||||
f.gravityPotentialFes->GetTrueVSize();
|
||||
return f.gravityFluxFes->GetTrueVSize() + f.gravityPotentialFes->GetTrueVSize();
|
||||
}
|
||||
|
||||
mfem::Array<int> make_gravity_residual_offsets(const fem::FEM &f) {
|
||||
@@ -65,10 +53,7 @@ namespace {
|
||||
const mfem::Array<int> &state_true_offsets,
|
||||
const utils::blocks::value_block<index>
|
||||
) {
|
||||
MFEM_VERIFY(
|
||||
index + 1 < state_true_offsets.Size(),
|
||||
"Value block is not present in the state offsets."
|
||||
);
|
||||
MFEM_VERIFY(index + 1 < state_true_offsets.Size(), "Value block is not present in the state offsets.");
|
||||
return state_true_offsets[index + 1] - state_true_offsets[index];
|
||||
}
|
||||
|
||||
@@ -76,10 +61,7 @@ namespace {
|
||||
const fem::FEM &f,
|
||||
const mfem::Array<int> &state_true_offsets
|
||||
) {
|
||||
MFEM_VERIFY(
|
||||
f.densityFes != nullptr,
|
||||
"GravityFieldOperator requires the density finite-element space."
|
||||
);
|
||||
MFEM_VERIFY(f.densityFes != nullptr, "GravityFieldOperator requires the density finite-element space.");
|
||||
MFEM_VERIFY(
|
||||
f.displacementFes != nullptr, "GravityFieldOperator requires the "
|
||||
"displacement finite-element space."
|
||||
@@ -87,65 +69,44 @@ namespace {
|
||||
|
||||
using form = utils::blocks::gravity_field_form;
|
||||
|
||||
constexpr auto density_block = utils::blocks::get_value_block<form>(
|
||||
utils::blocks::density_field.mass_term
|
||||
);
|
||||
constexpr auto density_block = utils::blocks::get_value_block<form>(utils::blocks::density_field.mass_term);
|
||||
constexpr auto displacement_block =
|
||||
utils::blocks::get_value_block<form>(
|
||||
utils::blocks::displacement_field.geometry_term
|
||||
);
|
||||
utils::blocks::get_value_block<form>(utils::blocks::displacement_field.geometry_term);
|
||||
constexpr auto gravity_gradient_block =
|
||||
utils::blocks::get_value_block<form>(
|
||||
utils::blocks::gravity_field.gradient_term
|
||||
);
|
||||
utils::blocks::get_value_block<form>(utils::blocks::gravity_field.gradient_term);
|
||||
constexpr auto gravity_potential_block =
|
||||
utils::blocks::get_value_block<form>(
|
||||
utils::blocks::gravity_field.poisson_term
|
||||
);
|
||||
utils::blocks::get_value_block<form>(utils::blocks::gravity_field.poisson_term);
|
||||
|
||||
MFEM_VERIFY(
|
||||
state_true_offsets.Size() == form::value_block_count + 1,
|
||||
"The gravity state offsets do not match gravity_field_form."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
get_state_block_size(state_true_offsets, density_block) ==
|
||||
f.densityFes->GetTrueVSize(),
|
||||
get_state_block_size(state_true_offsets, density_block) == f.densityFes->GetTrueVSize(),
|
||||
"The density block does not match the density finite-element space."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
get_state_block_size(state_true_offsets, displacement_block) ==
|
||||
f.displacementFes->GetTrueVSize(),
|
||||
get_state_block_size(state_true_offsets, displacement_block) == f.displacementFes->GetTrueVSize(),
|
||||
"The displacement block does not match the displacement "
|
||||
"finite-element "
|
||||
"space."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
get_state_block_size(state_true_offsets, gravity_gradient_block) ==
|
||||
f.gravityFluxFes->GetTrueVSize(),
|
||||
get_state_block_size(state_true_offsets, gravity_gradient_block) == f.gravityFluxFes->GetTrueVSize(),
|
||||
"The gravity-gradient block does not match the RT finite-element "
|
||||
"space."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
get_state_block_size(state_true_offsets, gravity_potential_block) ==
|
||||
f.gravityPotentialFes->GetTrueVSize(),
|
||||
get_state_block_size(state_true_offsets, gravity_potential_block) == f.gravityPotentialFes->GetTrueVSize(),
|
||||
"The gravity-potential block does not match the potential "
|
||||
"finite-element space."
|
||||
);
|
||||
}
|
||||
|
||||
void validate_gravity_context(const fem::FEM &f) {
|
||||
MFEM_VERIFY(
|
||||
f.gravityContext.b_form != nullptr,
|
||||
"GravityFieldOperator requires the divergence operator."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.gravityContext.BT != nullptr,
|
||||
"GravityFieldOperator requires the transpose divergence operator."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.quadratureFactory != nullptr,
|
||||
"GravityFieldOperator requires the quadrature-rule factory."
|
||||
);
|
||||
MFEM_VERIFY(f.gravityContext.b_form != nullptr, "GravityFieldOperator requires the divergence operator.");
|
||||
MFEM_VERIFY(f.gravityContext.BT != nullptr, "GravityFieldOperator requires the transpose divergence operator.");
|
||||
MFEM_VERIFY(f.quadratureFactory != nullptr, "GravityFieldOperator requires the quadrature-rule factory.");
|
||||
}
|
||||
|
||||
template <int index>
|
||||
@@ -154,21 +115,13 @@ namespace {
|
||||
const mfem::Array<int> &offsets,
|
||||
const utils::blocks::value_block<index>
|
||||
) {
|
||||
MFEM_VERIFY(
|
||||
index + 1 < offsets.Size(),
|
||||
"Value block is not present in the supplied offset array."
|
||||
);
|
||||
MFEM_VERIFY(index + 1 < offsets.Size(), "Value block is not present in the supplied offset array.");
|
||||
|
||||
const int begin = offsets[index];
|
||||
const int size = offsets[index + 1] - begin;
|
||||
|
||||
MFEM_VERIFY(
|
||||
vector.Size() == offsets.Last(),
|
||||
"Vector size does not match the value-block offsets."
|
||||
);
|
||||
return mfem::Vector(
|
||||
const_cast<mfem::real_t *>(vector.GetData()) + begin, size
|
||||
);
|
||||
MFEM_VERIFY(vector.Size() == offsets.Last(), "Vector size does not match the value-block offsets.");
|
||||
return mfem::Vector(const_cast<mfem::real_t *>(vector.GetData()) + begin, size);
|
||||
}
|
||||
|
||||
template <int index>
|
||||
@@ -181,10 +134,7 @@ namespace {
|
||||
const int begin = offsets[block_id];
|
||||
const int size = offsets[block_id + 1] - begin;
|
||||
|
||||
MFEM_VERIFY(
|
||||
vector.Size() == offsets.Last(),
|
||||
"The vector does not match the residual-block layout."
|
||||
);
|
||||
MFEM_VERIFY(vector.Size() == offsets.Last(), "The vector does not match the residual-block layout.");
|
||||
|
||||
mfem::Vector view;
|
||||
view.MakeRef(const_cast<mfem::Vector &>(vector), begin, size);
|
||||
@@ -197,18 +147,12 @@ namespace {
|
||||
const mfem::Array<int> &offsets,
|
||||
const utils::blocks::residual_block<index>
|
||||
) {
|
||||
MFEM_VERIFY(
|
||||
index + 1 < offsets.Size(),
|
||||
"Residual block is not present in the supplied offset array."
|
||||
);
|
||||
MFEM_VERIFY(index + 1 < offsets.Size(), "Residual block is not present in the supplied offset array.");
|
||||
|
||||
const int begin = offsets[index];
|
||||
const int size = offsets[index + 1] - begin;
|
||||
|
||||
MFEM_VERIFY(
|
||||
vector.Size() == offsets.Last(),
|
||||
"Vector size does not match the residual-block offsets."
|
||||
);
|
||||
MFEM_VERIFY(vector.Size() == offsets.Last(), "Vector size does not match the residual-block offsets.");
|
||||
return mfem::Vector(vector.GetData() + begin, size);
|
||||
}
|
||||
} // namespace
|
||||
@@ -217,8 +161,7 @@ namespace mean_field::operators {
|
||||
GravityFieldOperator::GravityFieldOperator(
|
||||
fem::FEM &f,
|
||||
const mapping::DomainMapperStateless &domain_mapper,
|
||||
context::gravity_field::GravityFieldLinearizationContext
|
||||
&linearization_context,
|
||||
context::gravity_field::GravityFieldLinearizationContext &linearization_context,
|
||||
const mfem::Array<int> &state_true_offsets,
|
||||
GravityFieldJacobianOperator &jacobian
|
||||
)
|
||||
@@ -238,14 +181,12 @@ namespace mean_field::operators {
|
||||
"displacement finite-element space."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.smesh.exterior_coordinate != nullptr,
|
||||
"GravityFieldOperator requires the STROID exterior coordinate."
|
||||
f.smesh.exterior_coordinate != nullptr, "GravityFieldOperator requires the STROID exterior coordinate."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.smesh.exterior_coordinate->space != nullptr,
|
||||
"GravityFieldOperator requires the exterior-coordinate "
|
||||
"finite-element "
|
||||
"space."
|
||||
f.smesh.exterior_coordinate->space != nullptr, "GravityFieldOperator requires the exterior-coordinate "
|
||||
"finite-element "
|
||||
"space."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.smesh.exterior_coordinate->values != nullptr,
|
||||
@@ -263,67 +204,46 @@ namespace mean_field::operators {
|
||||
bool has_vacuum_domain = false;
|
||||
|
||||
for (int i = 0; i < f.mesh->attributes.Size(); ++i) {
|
||||
if (f.mesh->attributes[i] ==
|
||||
domain_mapper.GetVacuumElementAttribute()) {
|
||||
if (f.mesh->attributes[i] == domain_mapper.GetVacuumElementAttribute()) {
|
||||
has_vacuum_domain = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
MFEM_VERIFY(has_vacuum_domain, "GravityFieldOperator requires a compactified vacuum domain.");
|
||||
MFEM_VERIFY(
|
||||
has_vacuum_domain,
|
||||
"GravityFieldOperator requires a compactified vacuum domain."
|
||||
m_residual_true_offsets.Last() == Height(), "The gravity residual offsets do not match the operator height."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
m_residual_true_offsets.Last() == Height(),
|
||||
"The gravity residual offsets do not match the operator height."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
m_state_true_offsets.Last() == Width(),
|
||||
"The coupled state offsets do not match the operator width."
|
||||
m_state_true_offsets.Last() == Width(), "The coupled state offsets do not match the operator width."
|
||||
);
|
||||
}
|
||||
|
||||
context::gravity_field::GravityFieldPreparationReport
|
||||
GravityFieldOperator::Prepare(
|
||||
context::gravity_field::GravityFieldPreparationReport GravityFieldOperator::Prepare(
|
||||
const mfem::Vector &state,
|
||||
const context::gravity_field::GravityFieldRevisions &revisions
|
||||
) {
|
||||
using form = utils::blocks::gravity_field_form;
|
||||
|
||||
constexpr auto density_block = utils::blocks::get_value_block<form>(
|
||||
utils::blocks::density_field.mass_term
|
||||
);
|
||||
constexpr auto density_block = utils::blocks::get_value_block<form>(utils::blocks::density_field.mass_term);
|
||||
constexpr auto displacement_block =
|
||||
utils::blocks::get_value_block<form>(
|
||||
utils::blocks::displacement_field.geometry_term
|
||||
);
|
||||
utils::blocks::get_value_block<form>(utils::blocks::displacement_field.geometry_term);
|
||||
constexpr auto gravity_gradient_block =
|
||||
utils::blocks::get_value_block<form>(
|
||||
utils::blocks::gravity_field.gradient_term
|
||||
);
|
||||
utils::blocks::get_value_block<form>(utils::blocks::gravity_field.gradient_term);
|
||||
constexpr auto gravity_potential_block =
|
||||
utils::blocks::get_value_block<form>(
|
||||
utils::blocks::gravity_field.poisson_term
|
||||
);
|
||||
utils::blocks::get_value_block<form>(utils::blocks::gravity_field.poisson_term);
|
||||
|
||||
MFEM_VERIFY(
|
||||
state.Size() == Width(), "GravityFieldOperator received a "
|
||||
"preparation state with the wrong size."
|
||||
);
|
||||
|
||||
const mfem::Vector density = make_read_only_value_view(
|
||||
state, m_state_true_offsets, density_block
|
||||
);
|
||||
const mfem::Vector displacement = make_read_only_value_view(
|
||||
state, m_state_true_offsets, displacement_block
|
||||
);
|
||||
const mfem::Vector gravity_gradient = make_read_only_value_view(
|
||||
state, m_state_true_offsets, gravity_gradient_block
|
||||
);
|
||||
const mfem::Vector gravity_potential = make_read_only_value_view(
|
||||
state, m_state_true_offsets, gravity_potential_block
|
||||
);
|
||||
const mfem::Vector density = make_read_only_value_view(state, m_state_true_offsets, density_block);
|
||||
const mfem::Vector displacement = make_read_only_value_view(state, m_state_true_offsets, displacement_block);
|
||||
const mfem::Vector gravity_gradient =
|
||||
make_read_only_value_view(state, m_state_true_offsets, gravity_gradient_block);
|
||||
const mfem::Vector gravity_potential =
|
||||
make_read_only_value_view(state, m_state_true_offsets, gravity_potential_block);
|
||||
|
||||
return m_linearization_context.Prepare(
|
||||
{.density = density,
|
||||
@@ -334,92 +254,65 @@ namespace mean_field::operators {
|
||||
);
|
||||
}
|
||||
|
||||
const mfem::Array<int> &
|
||||
GravityFieldOperator::GetStateTrueOffsets() const noexcept {
|
||||
const mfem::Array<int> &GravityFieldOperator::GetStateTrueOffsets() const noexcept {
|
||||
return m_state_true_offsets;
|
||||
}
|
||||
|
||||
const mfem::Array<int> &
|
||||
GravityFieldOperator::GetResidualTrueOffsets() const noexcept {
|
||||
const mfem::Array<int> &GravityFieldOperator::GetResidualTrueOffsets() const noexcept {
|
||||
return m_residual_true_offsets;
|
||||
}
|
||||
|
||||
void GravityFieldOperator::ApplyGravityUnknowns(
|
||||
const mfem::Vector &gravity_gradient,
|
||||
const mfem::Vector &gravity_potential,
|
||||
const context::gravity_field::GravityFieldGeometryContext
|
||||
&geometry_context,
|
||||
const context::gravity_field::GravityFieldGeometryContext &geometry_context,
|
||||
mfem::Vector &action
|
||||
) const {
|
||||
using form = utils::blocks::gravity_field_form;
|
||||
|
||||
constexpr auto gravity_gradient_residual_block =
|
||||
utils::blocks::get_residual_block<form>(
|
||||
utils::blocks::gravity_field.gradient_term
|
||||
);
|
||||
utils::blocks::get_residual_block<form>(utils::blocks::gravity_field.gradient_term);
|
||||
constexpr auto gravity_poisson_residual_block =
|
||||
utils::blocks::get_residual_block<form>(
|
||||
utils::blocks::gravity_field.poisson_term
|
||||
);
|
||||
utils::blocks::get_residual_block<form>(utils::blocks::gravity_field.poisson_term);
|
||||
|
||||
MFEM_VERIFY(
|
||||
geometry_context.IsPrepared(),
|
||||
"GravityFieldOperator received an unprepared geometry context."
|
||||
);
|
||||
MFEM_VERIFY(geometry_context.IsPrepared(), "GravityFieldOperator received an unprepared geometry context.");
|
||||
MFEM_VERIFY(
|
||||
gravity_gradient.Size() == m_fem.gravityFluxFes->GetTrueVSize(),
|
||||
"GravityFieldOperator received a gravity-gradient vector with the "
|
||||
"wrong size."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
gravity_potential.Size() ==
|
||||
m_fem.gravityPotentialFes->GetTrueVSize(),
|
||||
gravity_potential.Size() == m_fem.gravityPotentialFes->GetTrueVSize(),
|
||||
"GravityFieldOperator received a gravity-potential vector with the "
|
||||
"wrong size."
|
||||
);
|
||||
|
||||
action.SetSize(Height());
|
||||
action = 0.0;
|
||||
action = 0.0;
|
||||
|
||||
mfem::Vector gravity_gradient_action = make_residual_view(
|
||||
action, m_residual_true_offsets, gravity_gradient_residual_block
|
||||
);
|
||||
mfem::Vector gravity_poisson_action = make_residual_view(
|
||||
action, m_residual_true_offsets, gravity_poisson_residual_block
|
||||
);
|
||||
mfem::Vector transpose_divergence_action(
|
||||
gravity_gradient_action.Size()
|
||||
);
|
||||
mfem::Vector gravity_gradient_action =
|
||||
make_residual_view(action, m_residual_true_offsets, gravity_gradient_residual_block);
|
||||
mfem::Vector gravity_poisson_action =
|
||||
make_residual_view(action, m_residual_true_offsets, gravity_poisson_residual_block);
|
||||
mfem::Vector transpose_divergence_action(gravity_gradient_action.Size());
|
||||
|
||||
geometry_context.GetMassOperator().Mult(
|
||||
gravity_gradient, gravity_gradient_action
|
||||
);
|
||||
m_fem.gravityContext.BT->Mult(
|
||||
gravity_potential, transpose_divergence_action
|
||||
);
|
||||
geometry_context.GetMassOperator().Mult(gravity_gradient, gravity_gradient_action);
|
||||
m_fem.gravityContext.BT->Mult(gravity_potential, transpose_divergence_action);
|
||||
gravity_gradient_action += transpose_divergence_action;
|
||||
m_fem.gravityContext.b_form->Mult(
|
||||
gravity_gradient, gravity_poisson_action
|
||||
);
|
||||
m_fem.gravityContext.b_form->Mult(gravity_gradient, gravity_poisson_action);
|
||||
}
|
||||
|
||||
void GravityFieldOperator::ApplyDensitySource(
|
||||
const mfem::Vector &density,
|
||||
const context::gravity_field::GravityFieldGeometryContext
|
||||
&geometry_context,
|
||||
const context::gravity_field::GravityFieldGeometryContext &geometry_context,
|
||||
mfem::Vector &action
|
||||
) const {
|
||||
using form = utils::blocks::gravity_field_form;
|
||||
|
||||
constexpr auto gravity_poisson_residual_block =
|
||||
utils::blocks::get_residual_block<form>(
|
||||
utils::blocks::gravity_field.poisson_term
|
||||
);
|
||||
utils::blocks::get_residual_block<form>(utils::blocks::gravity_field.poisson_term);
|
||||
|
||||
MFEM_VERIFY(
|
||||
geometry_context.IsPrepared(),
|
||||
"GravityFieldOperator received an unprepared geometry context."
|
||||
);
|
||||
MFEM_VERIFY(geometry_context.IsPrepared(), "GravityFieldOperator received an unprepared geometry context.");
|
||||
MFEM_VERIFY(
|
||||
density.Size() == m_fem.densityFes->GetTrueVSize(),
|
||||
"GravityFieldOperator received a density vector with the wrong "
|
||||
@@ -427,14 +320,11 @@ namespace mean_field::operators {
|
||||
);
|
||||
|
||||
action.SetSize(Height());
|
||||
action = 0.0;
|
||||
action = 0.0;
|
||||
|
||||
mfem::Vector gravity_poisson_action = make_residual_view(
|
||||
action, m_residual_true_offsets, gravity_poisson_residual_block
|
||||
);
|
||||
geometry_context.GetSourceOperator().Mult(
|
||||
density, gravity_poisson_action
|
||||
);
|
||||
mfem::Vector gravity_poisson_action =
|
||||
make_residual_view(action, m_residual_true_offsets, gravity_poisson_residual_block);
|
||||
geometry_context.GetSourceOperator().Mult(density, gravity_poisson_action);
|
||||
}
|
||||
|
||||
void GravityFieldOperator::Mult(
|
||||
@@ -445,51 +335,34 @@ namespace mean_field::operators {
|
||||
|
||||
using form = utils::blocks::gravity_field_form;
|
||||
|
||||
constexpr auto density_block = utils::blocks::get_value_block<form>(
|
||||
utils::blocks::density_field.mass_term
|
||||
);
|
||||
constexpr auto density_block = utils::blocks::get_value_block<form>(utils::blocks::density_field.mass_term);
|
||||
constexpr auto gravity_gradient_block =
|
||||
utils::blocks::get_value_block<form>(
|
||||
utils::blocks::gravity_field.gradient_term
|
||||
);
|
||||
utils::blocks::get_value_block<form>(utils::blocks::gravity_field.gradient_term);
|
||||
constexpr auto gravity_potential_block =
|
||||
utils::blocks::get_value_block<form>(
|
||||
utils::blocks::gravity_field.poisson_term
|
||||
);
|
||||
utils::blocks::get_value_block<form>(utils::blocks::gravity_field.poisson_term);
|
||||
|
||||
MFEM_VERIFY(state.Size() == Width(), "GravityFieldOperator received a state with the wrong size.");
|
||||
MFEM_VERIFY(
|
||||
state.Size() == Width(),
|
||||
"GravityFieldOperator received a state with the wrong size."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
m_linearization_context.IsPrepared(),
|
||||
"GravityFieldOperator must be prepared before Mult is called."
|
||||
m_linearization_context.IsPrepared(), "GravityFieldOperator must be prepared before Mult is called."
|
||||
);
|
||||
|
||||
const mfem::Vector density = make_read_only_value_view(
|
||||
state, m_state_true_offsets, density_block
|
||||
);
|
||||
const mfem::Vector gravity_gradient = make_read_only_value_view(
|
||||
state, m_state_true_offsets, gravity_gradient_block
|
||||
);
|
||||
const mfem::Vector gravity_potential = make_read_only_value_view(
|
||||
state, m_state_true_offsets, gravity_potential_block
|
||||
);
|
||||
const context::gravity_field::GravityFieldGeometryContext
|
||||
&geometry_context = m_linearization_context.GetGeometryContext();
|
||||
const mfem::Vector density = make_read_only_value_view(state, m_state_true_offsets, density_block);
|
||||
const mfem::Vector gravity_gradient =
|
||||
make_read_only_value_view(state, m_state_true_offsets, gravity_gradient_block);
|
||||
const mfem::Vector gravity_potential =
|
||||
make_read_only_value_view(state, m_state_true_offsets, gravity_potential_block);
|
||||
const context::gravity_field::GravityFieldGeometryContext &geometry_context =
|
||||
m_linearization_context.GetGeometryContext();
|
||||
|
||||
mfem::Vector source;
|
||||
|
||||
ApplyGravityUnknowns(
|
||||
gravity_gradient, gravity_potential, geometry_context, residual
|
||||
);
|
||||
ApplyGravityUnknowns(gravity_gradient, gravity_potential, geometry_context, residual);
|
||||
ApplyDensitySource(density, geometry_context, source);
|
||||
|
||||
residual -= source;
|
||||
}
|
||||
|
||||
context::gravity_field::GravityFieldLinearizationContext &
|
||||
GravityFieldOperator::GetLinearizationContext() noexcept {
|
||||
context::gravity_field::GravityFieldLinearizationContext &GravityFieldOperator::GetLinearizationContext() noexcept {
|
||||
return m_linearization_context;
|
||||
}
|
||||
|
||||
@@ -498,24 +371,21 @@ namespace mean_field::operators {
|
||||
return m_linearization_context;
|
||||
}
|
||||
|
||||
mfem::Operator &
|
||||
GravityFieldOperator::GetGradient(const mfem::Vector &state) const {
|
||||
mfem::Operator &GravityFieldOperator::GetGradient(const mfem::Vector &state) const {
|
||||
MFEM_VERIFY(
|
||||
state.Size() == Width(), "GravityFieldOperator received a "
|
||||
"linearization state with the wrong size."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
m_linearization_context.IsPrepared(),
|
||||
"GravityFieldOperator must be prepared before GetGradient is "
|
||||
"called."
|
||||
m_linearization_context.IsPrepared(), "GravityFieldOperator must be prepared before GetGradient is "
|
||||
"called."
|
||||
);
|
||||
|
||||
return m_jacobian;
|
||||
}
|
||||
ReducedGravityFieldOperator::ReducedGravityFieldOperator(
|
||||
GravityFieldOperator &gravity_field_operator,
|
||||
context::gravity_field::GravityFieldGeometryContext
|
||||
&gravity_field_geometry_context,
|
||||
context::gravity_field::GravityFieldGeometryContext &gravity_field_geometry_context,
|
||||
const mfem::Vector &displacement
|
||||
)
|
||||
: Operator(
|
||||
@@ -523,31 +393,20 @@ namespace mean_field::operators {
|
||||
gravity_field_operator.Height()
|
||||
),
|
||||
m_gravity_field_operator(gravity_field_operator),
|
||||
m_gravity_true_offsets(
|
||||
gravity_field_operator.GetResidualTrueOffsets()
|
||||
),
|
||||
m_gravity_true_offsets(gravity_field_operator.GetResidualTrueOffsets()),
|
||||
m_gravity_field_geometry_context(gravity_field_geometry_context) {
|
||||
using form = utils::blocks::gravity_field_form;
|
||||
|
||||
constexpr auto gravity_gradient_block =
|
||||
utils::blocks::get_value_block<form>(
|
||||
utils::blocks::gravity_field.gradient_term
|
||||
);
|
||||
utils::blocks::get_value_block<form>(utils::blocks::gravity_field.gradient_term);
|
||||
constexpr auto gravity_potential_block =
|
||||
utils::blocks::get_value_block<form>(
|
||||
utils::blocks::gravity_field.poisson_term
|
||||
);
|
||||
utils::blocks::get_value_block<form>(utils::blocks::gravity_field.poisson_term);
|
||||
constexpr auto gravity_gradient_residual_block =
|
||||
utils::blocks::get_residual_block<form>(
|
||||
utils::blocks::gravity_field.gradient_term
|
||||
);
|
||||
utils::blocks::get_residual_block<form>(utils::blocks::gravity_field.gradient_term);
|
||||
constexpr auto gravity_poisson_residual_block =
|
||||
utils::blocks::get_residual_block<form>(
|
||||
utils::blocks::gravity_field.poisson_term
|
||||
);
|
||||
utils::blocks::get_residual_block<form>(utils::blocks::gravity_field.poisson_term);
|
||||
|
||||
const mfem::Array<int> &state_offsets =
|
||||
m_gravity_field_operator.GetStateTrueOffsets();
|
||||
const mfem::Array<int> &state_offsets = m_gravity_field_operator.GetStateTrueOffsets();
|
||||
|
||||
MFEM_VERIFY(
|
||||
state_offsets.Size() == form::value_block_count + 1,
|
||||
@@ -558,13 +417,8 @@ namespace mean_field::operators {
|
||||
"ReducedGravityFieldOperator received an invalid gravity-residual "
|
||||
"layout."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
state_offsets[0] == 0, "The full-state offsets must begin at zero."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
m_gravity_true_offsets[0] == 0,
|
||||
"The reduced gravity offsets must begin at zero."
|
||||
);
|
||||
MFEM_VERIFY(state_offsets[0] == 0, "The full-state offsets must begin at zero.");
|
||||
MFEM_VERIFY(m_gravity_true_offsets[0] == 0, "The reduced gravity offsets must begin at zero.");
|
||||
MFEM_VERIFY(
|
||||
state_offsets.Last() == m_gravity_field_operator.Width(),
|
||||
"The full-state offsets do not match the gravity-field operator "
|
||||
@@ -576,23 +430,17 @@ namespace mean_field::operators {
|
||||
"operator "
|
||||
"height."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
Width() == Height(), "ReducedGravityFieldOperator must be square."
|
||||
);
|
||||
MFEM_VERIFY(Width() == Height(), "ReducedGravityFieldOperator must be square.");
|
||||
|
||||
const int full_gradient_size =
|
||||
state_offsets[static_cast<int>(gravity_gradient_block) + 1] -
|
||||
state_offsets[gravity_gradient_block];
|
||||
state_offsets[static_cast<int>(gravity_gradient_block) + 1] - state_offsets[gravity_gradient_block];
|
||||
const int full_potential_size =
|
||||
state_offsets[static_cast<int>(gravity_potential_block) + 1] -
|
||||
state_offsets[gravity_potential_block];
|
||||
state_offsets[static_cast<int>(gravity_potential_block) + 1] - state_offsets[gravity_potential_block];
|
||||
const int reduced_gradient_size =
|
||||
m_gravity_true_offsets
|
||||
[static_cast<int>(gravity_gradient_residual_block) + 1] -
|
||||
m_gravity_true_offsets[static_cast<int>(gravity_gradient_residual_block) + 1] -
|
||||
m_gravity_true_offsets[gravity_gradient_residual_block];
|
||||
const int reduced_potential_size =
|
||||
m_gravity_true_offsets
|
||||
[static_cast<int>(gravity_poisson_residual_block) + 1] -
|
||||
m_gravity_true_offsets[static_cast<int>(gravity_poisson_residual_block) + 1] -
|
||||
m_gravity_true_offsets[gravity_poisson_residual_block];
|
||||
|
||||
MFEM_VERIFY(
|
||||
@@ -609,31 +457,24 @@ namespace mean_field::operators {
|
||||
SetDisplacement(displacement);
|
||||
}
|
||||
|
||||
void ReducedGravityFieldOperator::SetDisplacement(
|
||||
const mfem::Vector &displacement
|
||||
) {
|
||||
void ReducedGravityFieldOperator::SetDisplacement(const mfem::Vector &displacement) {
|
||||
ValidateDisplacement(displacement);
|
||||
|
||||
context::gravity_field::DiscretizationRevision discretization_revision;
|
||||
context::gravity_field::DisplacementRevision displacement_revision;
|
||||
|
||||
if (m_gravity_field_geometry_context.IsPrepared()) {
|
||||
discretization_revision =
|
||||
m_gravity_field_geometry_context.GetDiscretizationRevision();
|
||||
displacement_revision =
|
||||
m_gravity_field_geometry_context.GetDisplacementRevision();
|
||||
discretization_revision = m_gravity_field_geometry_context.GetDiscretizationRevision();
|
||||
displacement_revision = m_gravity_field_geometry_context.GetDisplacementRevision();
|
||||
|
||||
MFEM_VERIFY(
|
||||
displacement_revision.value <
|
||||
std::numeric_limits<std::uint64_t>::max(),
|
||||
displacement_revision.value < std::numeric_limits<std::uint64_t>::max(),
|
||||
"The reduced gravity displacement revision has overflowed."
|
||||
);
|
||||
++displacement_revision.value;
|
||||
}
|
||||
|
||||
m_gravity_field_geometry_context.Prepare(
|
||||
displacement, discretization_revision, displacement_revision
|
||||
);
|
||||
m_gravity_field_geometry_context.Prepare(displacement, discretization_revision, displacement_revision);
|
||||
}
|
||||
|
||||
const mfem::Vector &ReducedGravityFieldOperator::GetDisplacement() const {
|
||||
@@ -646,15 +487,12 @@ namespace mean_field::operators {
|
||||
) const {
|
||||
ValidateDensity(density);
|
||||
|
||||
m_gravity_field_operator.ApplyDensitySource(
|
||||
density, m_gravity_field_geometry_context, right_hand_side
|
||||
);
|
||||
m_gravity_field_operator.ApplyDensitySource(density, m_gravity_field_geometry_context, right_hand_side);
|
||||
|
||||
MFEM_VERIFY(
|
||||
right_hand_side.Size() == Height(),
|
||||
"ReducedGravityFieldOperator produced a right-hand side with the "
|
||||
"wrong "
|
||||
"size."
|
||||
right_hand_side.Size() == Height(), "ReducedGravityFieldOperator produced a right-hand side with the "
|
||||
"wrong "
|
||||
"size."
|
||||
);
|
||||
}
|
||||
|
||||
@@ -667,29 +505,19 @@ namespace mean_field::operators {
|
||||
using form = utils::blocks::gravity_field_form;
|
||||
|
||||
constexpr auto gravity_gradient_residual_block =
|
||||
utils::blocks::get_residual_block<form>(
|
||||
utils::blocks::gravity_field.gradient_term
|
||||
);
|
||||
utils::blocks::get_residual_block<form>(utils::blocks::gravity_field.gradient_term);
|
||||
constexpr auto gravity_poisson_residual_block =
|
||||
utils::blocks::get_residual_block<form>(
|
||||
utils::blocks::gravity_field.poisson_term
|
||||
);
|
||||
utils::blocks::get_residual_block<form>(utils::blocks::gravity_field.poisson_term);
|
||||
|
||||
ValidateGravityState(gravity_state);
|
||||
|
||||
const mfem::Vector gravity_gradient_true = make_read_only_residual_view(
|
||||
gravity_state, m_gravity_true_offsets,
|
||||
gravity_gradient_residual_block
|
||||
);
|
||||
const mfem::Vector gravity_gradient_true =
|
||||
make_read_only_residual_view(gravity_state, m_gravity_true_offsets, gravity_gradient_residual_block);
|
||||
const mfem::Vector gravity_potential_true =
|
||||
make_read_only_residual_view(
|
||||
gravity_state, m_gravity_true_offsets,
|
||||
gravity_poisson_residual_block
|
||||
);
|
||||
make_read_only_residual_view(gravity_state, m_gravity_true_offsets, gravity_poisson_residual_block);
|
||||
|
||||
m_gravity_field_operator.ApplyGravityUnknowns(
|
||||
gravity_gradient_true, gravity_potential_true,
|
||||
m_gravity_field_geometry_context, action
|
||||
gravity_gradient_true, gravity_potential_true, m_gravity_field_geometry_context, action
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
@@ -698,18 +526,15 @@ namespace mean_field::operators {
|
||||
);
|
||||
}
|
||||
|
||||
GravityFieldOperator &
|
||||
ReducedGravityFieldOperator::GetGravityFieldOperator() noexcept {
|
||||
GravityFieldOperator &ReducedGravityFieldOperator::GetGravityFieldOperator() noexcept {
|
||||
return m_gravity_field_operator;
|
||||
}
|
||||
|
||||
const GravityFieldOperator &
|
||||
ReducedGravityFieldOperator::GetGravityFieldOperator() const noexcept {
|
||||
const GravityFieldOperator &ReducedGravityFieldOperator::GetGravityFieldOperator() const noexcept {
|
||||
return m_gravity_field_operator;
|
||||
}
|
||||
|
||||
context::gravity_field::GravityFieldGeometryContext &
|
||||
ReducedGravityFieldOperator::GetGeometryContext() noexcept {
|
||||
context::gravity_field::GravityFieldGeometryContext &ReducedGravityFieldOperator::GetGeometryContext() noexcept {
|
||||
return m_gravity_field_geometry_context;
|
||||
}
|
||||
|
||||
@@ -718,73 +543,53 @@ namespace mean_field::operators {
|
||||
return m_gravity_field_geometry_context;
|
||||
}
|
||||
|
||||
const mfem::Array<int> &
|
||||
ReducedGravityFieldOperator::GetGravityTrueOffsets() const noexcept {
|
||||
const mfem::Array<int> &ReducedGravityFieldOperator::GetGravityTrueOffsets() const noexcept {
|
||||
return m_gravity_true_offsets;
|
||||
}
|
||||
|
||||
void ReducedGravityFieldOperator::ValidateDisplacement(
|
||||
const mfem::Vector &displacement
|
||||
) const {
|
||||
void ReducedGravityFieldOperator::ValidateDisplacement(const mfem::Vector &displacement) const {
|
||||
using form = utils::blocks::gravity_field_form;
|
||||
|
||||
constexpr auto displacement_block =
|
||||
utils::blocks::get_value_block<form>(
|
||||
utils::blocks::displacement_field.geometry_term
|
||||
);
|
||||
utils::blocks::get_value_block<form>(utils::blocks::displacement_field.geometry_term);
|
||||
|
||||
const mfem::Array<int> &state_offsets =
|
||||
m_gravity_field_operator.GetStateTrueOffsets();
|
||||
const mfem::Array<int> &state_offsets = m_gravity_field_operator.GetStateTrueOffsets();
|
||||
const int expected_size =
|
||||
state_offsets[static_cast<int>(displacement_block) + 1] -
|
||||
state_offsets[displacement_block];
|
||||
state_offsets[static_cast<int>(displacement_block) + 1] - state_offsets[displacement_block];
|
||||
|
||||
MFEM_VERIFY(
|
||||
displacement.Size() == expected_size,
|
||||
"ReducedGravityFieldOperator received a displacement with the "
|
||||
"wrong "
|
||||
"size."
|
||||
displacement.Size() == expected_size, "ReducedGravityFieldOperator received a displacement with the "
|
||||
"wrong "
|
||||
"size."
|
||||
);
|
||||
|
||||
for (int i = 0; i < displacement.Size(); ++i) {
|
||||
MFEM_VERIFY(
|
||||
std::isfinite(displacement(i)),
|
||||
"ReducedGravityFieldOperator received a non-finite "
|
||||
"displacement "
|
||||
"value."
|
||||
std::isfinite(displacement(i)), "ReducedGravityFieldOperator received a non-finite "
|
||||
"displacement "
|
||||
"value."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void ReducedGravityFieldOperator::ValidateDensity(
|
||||
const mfem::Vector &density
|
||||
) const {
|
||||
void ReducedGravityFieldOperator::ValidateDensity(const mfem::Vector &density) const {
|
||||
using form = utils::blocks::gravity_field_form;
|
||||
|
||||
constexpr auto density_block = utils::blocks::get_value_block<form>(
|
||||
utils::blocks::density_field.mass_term
|
||||
);
|
||||
constexpr auto density_block = utils::blocks::get_value_block<form>(utils::blocks::density_field.mass_term);
|
||||
|
||||
const mfem::Array<int> &state_offsets =
|
||||
m_gravity_field_operator.GetStateTrueOffsets();
|
||||
const int expected_size =
|
||||
state_offsets[static_cast<int>(density_block) + 1] -
|
||||
state_offsets[density_block];
|
||||
const mfem::Array<int> &state_offsets = m_gravity_field_operator.GetStateTrueOffsets();
|
||||
const int expected_size = state_offsets[static_cast<int>(density_block) + 1] - state_offsets[density_block];
|
||||
|
||||
MFEM_VERIFY(
|
||||
density.Size() == expected_size,
|
||||
"ReducedGravityFieldOperator received a density with the wrong "
|
||||
"size."
|
||||
density.Size() == expected_size, "ReducedGravityFieldOperator received a density with the wrong "
|
||||
"size."
|
||||
);
|
||||
}
|
||||
|
||||
void ReducedGravityFieldOperator::ValidateGravityState(
|
||||
const mfem::Vector &gravity_state
|
||||
) const {
|
||||
void ReducedGravityFieldOperator::ValidateGravityState(const mfem::Vector &gravity_state) const {
|
||||
MFEM_VERIFY(
|
||||
gravity_state.Size() == Width(),
|
||||
"ReducedGravityFieldOperator received "
|
||||
"a gravity state with the wrong size."
|
||||
gravity_state.Size() == Width(), "ReducedGravityFieldOperator received "
|
||||
"a gravity state with the wrong size."
|
||||
);
|
||||
}
|
||||
} // namespace mean_field::operators
|
||||
|
||||
@@ -15,9 +15,7 @@ namespace {
|
||||
) {
|
||||
const int offset = offsets[index];
|
||||
const int size = offsets[index + 1] - offset;
|
||||
return mfem::Vector(
|
||||
const_cast<mfem::real_t *>(vector.GetData()) + offset, size
|
||||
);
|
||||
return mfem::Vector(const_cast<mfem::real_t *>(vector.GetData()) + offset, size);
|
||||
}
|
||||
|
||||
template <int index>
|
||||
@@ -56,10 +54,7 @@ namespace {
|
||||
MFEM_VERIFY(offsets[0] == 0, "Block offsets must begin at zero.");
|
||||
|
||||
for (int i = 0; i < block_count; ++i)
|
||||
MFEM_VERIFY(
|
||||
offsets[i + 1] >= offsets[i],
|
||||
"Block offsets must be nondecreasing."
|
||||
);
|
||||
MFEM_VERIFY(offsets[i + 1] >= offsets[i], "Block offsets must be nondecreasing.");
|
||||
}
|
||||
|
||||
void validate_layout(
|
||||
@@ -70,29 +65,18 @@ namespace {
|
||||
using form = mean_field::utils::blocks::gravity_field_form;
|
||||
|
||||
constexpr auto density_block =
|
||||
mean_field::utils::blocks::get_value_block<form>(
|
||||
mean_field::utils::blocks::density_field.mass_term
|
||||
);
|
||||
constexpr auto displacement_block =
|
||||
mean_field::utils::blocks::get_value_block<form>(
|
||||
mean_field::utils::blocks::displacement_field.geometry_term
|
||||
);
|
||||
mean_field::utils::blocks::get_value_block<form>(mean_field::utils::blocks::density_field.mass_term);
|
||||
constexpr auto displacement_block = mean_field::utils::blocks::get_value_block<form>(
|
||||
mean_field::utils::blocks::displacement_field.geometry_term
|
||||
);
|
||||
constexpr auto gravity_gradient_block =
|
||||
mean_field::utils::blocks::get_value_block<form>(
|
||||
mean_field::utils::blocks::gravity_field.gradient_term
|
||||
);
|
||||
mean_field::utils::blocks::get_value_block<form>(mean_field::utils::blocks::gravity_field.gradient_term);
|
||||
constexpr auto gravity_potential_block =
|
||||
mean_field::utils::blocks::get_value_block<form>(
|
||||
mean_field::utils::blocks::gravity_field.poisson_term
|
||||
);
|
||||
mean_field::utils::blocks::get_value_block<form>(mean_field::utils::blocks::gravity_field.poisson_term);
|
||||
constexpr auto gravity_gradient_residual_block =
|
||||
mean_field::utils::blocks::get_residual_block<form>(
|
||||
mean_field::utils::blocks::gravity_field.gradient_term
|
||||
);
|
||||
mean_field::utils::blocks::get_residual_block<form>(mean_field::utils::blocks::gravity_field.gradient_term);
|
||||
constexpr auto gravity_poisson_residual_block =
|
||||
mean_field::utils::blocks::get_residual_block<form>(
|
||||
mean_field::utils::blocks::gravity_field.poisson_term
|
||||
);
|
||||
mean_field::utils::blocks::get_residual_block<form>(mean_field::utils::blocks::gravity_field.poisson_term);
|
||||
|
||||
validate_offsets(
|
||||
state_offsets, form::value_block_count,
|
||||
@@ -106,33 +90,27 @@ namespace {
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
get_block_size(state_offsets, density_block) ==
|
||||
f.densityFes->GetTrueVSize(),
|
||||
get_block_size(state_offsets, density_block) == f.densityFes->GetTrueVSize(),
|
||||
"The Jacobian density block has the wrong size."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
get_block_size(state_offsets, displacement_block) ==
|
||||
f.displacementFes->GetTrueVSize(),
|
||||
get_block_size(state_offsets, displacement_block) == f.displacementFes->GetTrueVSize(),
|
||||
"The Jacobian displacement block has the wrong size."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
get_block_size(state_offsets, gravity_gradient_block) ==
|
||||
f.gravityFluxFes->GetTrueVSize(),
|
||||
get_block_size(state_offsets, gravity_gradient_block) == f.gravityFluxFes->GetTrueVSize(),
|
||||
"The Jacobian gravity-gradient block has the wrong size."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
get_block_size(state_offsets, gravity_potential_block) ==
|
||||
f.gravityPotentialFes->GetTrueVSize(),
|
||||
get_block_size(state_offsets, gravity_potential_block) == f.gravityPotentialFes->GetTrueVSize(),
|
||||
"The Jacobian gravity-potential block has the wrong size."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
get_block_size(residual_offsets, gravity_gradient_residual_block) ==
|
||||
f.gravityFluxFes->GetTrueVSize(),
|
||||
get_block_size(residual_offsets, gravity_gradient_residual_block) == f.gravityFluxFes->GetTrueVSize(),
|
||||
"The Jacobian gradient-residual block has the wrong size."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
get_block_size(residual_offsets, gravity_poisson_residual_block) ==
|
||||
f.gravityPotentialFes->GetTrueVSize(),
|
||||
get_block_size(residual_offsets, gravity_poisson_residual_block) == f.gravityPotentialFes->GetTrueVSize(),
|
||||
"The Jacobian Poisson-residual block has the wrong size."
|
||||
);
|
||||
}
|
||||
@@ -142,8 +120,7 @@ namespace mean_field::operators {
|
||||
GravityFieldJacobianOperator::GravityFieldJacobianOperator(
|
||||
fem::FEM &f,
|
||||
const mapping::DomainMapperStateless &domain_mapper,
|
||||
const context::gravity_field::GravityFieldLinearizationContext
|
||||
&linearization_context,
|
||||
const context::gravity_field::GravityFieldLinearizationContext &linearization_context,
|
||||
const mfem::Array<int> &state_true_offsets,
|
||||
const mfem::Array<int> &residual_true_offsets
|
||||
)
|
||||
@@ -157,37 +134,30 @@ namespace mean_field::operators {
|
||||
m_state_true_offsets(state_true_offsets),
|
||||
m_residual_true_offsets(residual_true_offsets) {
|
||||
MFEM_VERIFY(
|
||||
f.densityFes != nullptr,
|
||||
"GravityFieldJacobianOperator requires the density finite-element "
|
||||
"space."
|
||||
f.densityFes != nullptr, "GravityFieldJacobianOperator requires the density finite-element "
|
||||
"space."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.gravityPotentialFes != nullptr,
|
||||
"GravityFieldJacobianOperator requires the gravity-potential "
|
||||
"finite-element space."
|
||||
f.gravityPotentialFes != nullptr, "GravityFieldJacobianOperator requires the gravity-potential "
|
||||
"finite-element space."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.gravityFluxFes != nullptr,
|
||||
"GravityFieldJacobianOperator requires the "
|
||||
"gravity-gradient finite-element space."
|
||||
f.gravityFluxFes != nullptr, "GravityFieldJacobianOperator requires the "
|
||||
"gravity-gradient finite-element space."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.displacementFes != nullptr,
|
||||
"GravityFieldJacobianOperator requires the "
|
||||
"displacement finite-element space."
|
||||
f.displacementFes != nullptr, "GravityFieldJacobianOperator requires the "
|
||||
"displacement finite-element space."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.gravityContext.b_form != nullptr,
|
||||
"GravityFieldJacobianOperator requires the divergence operator."
|
||||
f.gravityContext.b_form != nullptr, "GravityFieldJacobianOperator requires the divergence operator."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.gravityContext.BT != nullptr,
|
||||
"GravityFieldJacobianOperator requires the transpose divergence "
|
||||
"operator."
|
||||
f.gravityContext.BT != nullptr, "GravityFieldJacobianOperator requires the transpose divergence "
|
||||
"operator."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.quadratureFactory != nullptr,
|
||||
"GravityFieldJacobianOperator requires the quadrature-rule factory."
|
||||
f.quadratureFactory != nullptr, "GravityFieldJacobianOperator requires the quadrature-rule factory."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
domain_mapper.GetDimension() == f.mesh->Dimension(),
|
||||
@@ -204,107 +174,74 @@ namespace mean_field::operators {
|
||||
mfem::Vector &action
|
||||
) const {
|
||||
MFEM_VERIFY(
|
||||
m_linearization_context.IsPrepared(),
|
||||
"GravityFieldJacobianOperator requires a prepared linearization "
|
||||
"context."
|
||||
m_linearization_context.IsPrepared(), "GravityFieldJacobianOperator requires a prepared linearization "
|
||||
"context."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
direction.Size() == Width(),
|
||||
"GravityFieldJacobianOperator received a direction with the wrong "
|
||||
"size."
|
||||
direction.Size() == Width(), "GravityFieldJacobianOperator received a direction with the wrong "
|
||||
"size."
|
||||
);
|
||||
|
||||
using form = utils::blocks::gravity_field_form;
|
||||
|
||||
constexpr auto density_block = utils::blocks::get_value_block<form>(
|
||||
utils::blocks::density_field.mass_term
|
||||
);
|
||||
constexpr auto density_block = utils::blocks::get_value_block<form>(utils::blocks::density_field.mass_term);
|
||||
constexpr auto displacement_block =
|
||||
utils::blocks::get_value_block<form>(
|
||||
utils::blocks::displacement_field.geometry_term
|
||||
);
|
||||
utils::blocks::get_value_block<form>(utils::blocks::displacement_field.geometry_term);
|
||||
constexpr auto gravity_gradient_block =
|
||||
utils::blocks::get_value_block<form>(
|
||||
utils::blocks::gravity_field.gradient_term
|
||||
);
|
||||
utils::blocks::get_value_block<form>(utils::blocks::gravity_field.gradient_term);
|
||||
constexpr auto gravity_potential_block =
|
||||
utils::blocks::get_value_block<form>(
|
||||
utils::blocks::gravity_field.poisson_term
|
||||
);
|
||||
utils::blocks::get_value_block<form>(utils::blocks::gravity_field.poisson_term);
|
||||
constexpr auto gravity_gradient_residual_block =
|
||||
utils::blocks::get_residual_block<form>(
|
||||
utils::blocks::gravity_field.gradient_term
|
||||
);
|
||||
utils::blocks::get_residual_block<form>(utils::blocks::gravity_field.gradient_term);
|
||||
constexpr auto gravity_poisson_residual_block =
|
||||
utils::blocks::get_residual_block<form>(
|
||||
utils::blocks::gravity_field.poisson_term
|
||||
);
|
||||
utils::blocks::get_residual_block<form>(utils::blocks::gravity_field.poisson_term);
|
||||
|
||||
const context::gravity_field::GravityFieldGeometryContext
|
||||
&geometry_context = m_linearization_context.GetGeometryContext();
|
||||
const mfem::Vector &density = m_linearization_context.GetDensity();
|
||||
const mfem::Vector &displacement = geometry_context.GetDisplacement();
|
||||
const mfem::Vector &gravity_gradient =
|
||||
m_linearization_context.GetGravityGradient();
|
||||
const context::gravity_field::GravityFieldGeometryContext &geometry_context =
|
||||
m_linearization_context.GetGeometryContext();
|
||||
const mfem::Vector &density = m_linearization_context.GetDensity();
|
||||
const mfem::Vector &displacement = geometry_context.GetDisplacement();
|
||||
const mfem::Vector &gravity_gradient = m_linearization_context.GetGravityGradient();
|
||||
|
||||
const mfem::Vector density_direction = make_read_only_value_view(
|
||||
direction, m_state_true_offsets, density_block
|
||||
);
|
||||
const mfem::Vector displacement_direction = make_read_only_value_view(
|
||||
direction, m_state_true_offsets, displacement_block
|
||||
);
|
||||
const mfem::Vector density_direction =
|
||||
make_read_only_value_view(direction, m_state_true_offsets, density_block);
|
||||
const mfem::Vector displacement_direction =
|
||||
make_read_only_value_view(direction, m_state_true_offsets, displacement_block);
|
||||
const mfem::Vector gravity_gradient_direction =
|
||||
make_read_only_value_view(
|
||||
direction, m_state_true_offsets, gravity_gradient_block
|
||||
);
|
||||
make_read_only_value_view(direction, m_state_true_offsets, gravity_gradient_block);
|
||||
const mfem::Vector gravity_potential_direction =
|
||||
make_read_only_value_view(
|
||||
direction, m_state_true_offsets, gravity_potential_block
|
||||
);
|
||||
make_read_only_value_view(direction, m_state_true_offsets, gravity_potential_block);
|
||||
|
||||
action.SetSize(Height());
|
||||
action = 0.0;
|
||||
action = 0.0;
|
||||
|
||||
mfem::Vector gravity_gradient_action = make_residual_view(
|
||||
action, m_residual_true_offsets, gravity_gradient_residual_block
|
||||
);
|
||||
mfem::Vector gravity_poisson_action = make_residual_view(
|
||||
action, m_residual_true_offsets, gravity_poisson_residual_block
|
||||
);
|
||||
mfem::Vector gravity_gradient_action =
|
||||
make_residual_view(action, m_residual_true_offsets, gravity_gradient_residual_block);
|
||||
mfem::Vector gravity_poisson_action =
|
||||
make_residual_view(action, m_residual_true_offsets, gravity_poisson_residual_block);
|
||||
|
||||
mfem::Vector transpose_divergence_action;
|
||||
mfem::Vector source_action;
|
||||
mfem::Vector mass_variation_action;
|
||||
mfem::Vector source_variation_action;
|
||||
|
||||
geometry_context.GetMassOperator().Mult(
|
||||
gravity_gradient_direction, gravity_gradient_action
|
||||
);
|
||||
geometry_context.GetSourceOperator().Mult(
|
||||
density_direction, source_action
|
||||
);
|
||||
geometry_context.GetMassOperator().Mult(gravity_gradient_direction, gravity_gradient_action);
|
||||
geometry_context.GetSourceOperator().Mult(density_direction, source_action);
|
||||
|
||||
kernels::apply_mapped_hdiv_mass_variation(
|
||||
m_fem, m_domain_mapper, gravity_gradient, displacement,
|
||||
displacement_direction, mass_variation_action
|
||||
m_fem, m_domain_mapper, gravity_gradient, displacement, displacement_direction, mass_variation_action
|
||||
);
|
||||
|
||||
kernels::apply_mapped_source_variation(
|
||||
m_fem, m_domain_mapper, density, displacement,
|
||||
displacement_direction, source_variation_action
|
||||
m_fem, m_domain_mapper, density, displacement, displacement_direction, source_variation_action
|
||||
);
|
||||
|
||||
transpose_divergence_action.SetSize(gravity_gradient_action.Size());
|
||||
m_fem.gravityContext.BT->Mult(
|
||||
gravity_potential_direction, transpose_divergence_action
|
||||
);
|
||||
m_fem.gravityContext.BT->Mult(gravity_potential_direction, transpose_divergence_action);
|
||||
|
||||
gravity_gradient_action += transpose_divergence_action;
|
||||
gravity_gradient_action += mass_variation_action;
|
||||
|
||||
m_fem.gravityContext.b_form->Mult(
|
||||
gravity_gradient_direction, gravity_poisson_action
|
||||
);
|
||||
m_fem.gravityContext.b_form->Mult(gravity_gradient_direction, gravity_poisson_action);
|
||||
|
||||
gravity_poisson_action -= source_action;
|
||||
gravity_poisson_action -= source_variation_action;
|
||||
|
||||
@@ -8,24 +8,29 @@ module;
|
||||
module mean_field;
|
||||
|
||||
import :operators.kernels.barotropic_closure;
|
||||
import :field.registry;
|
||||
import :utils.domain;
|
||||
|
||||
namespace {
|
||||
using DomainSchema = mean_field::utils::domain::CoreEnvelopeVacuumDomainSchema;
|
||||
using ClosureDomain = mean_field::field::FieldDomainT<mean_field::field::Density>;
|
||||
|
||||
enum class ClosureAction { residual, density, enthalpy };
|
||||
|
||||
[[nodiscard]] bool element_is_in_closure_support(const int attribute) {
|
||||
return DomainSchema::template attribute_belongs_to<ClosureDomain>(attribute);
|
||||
}
|
||||
|
||||
void true_to_local(
|
||||
const mfem::ParFiniteElementSpace &finiteElementSpace,
|
||||
const mfem::Vector &trueVector,
|
||||
mfem::Vector &localVector
|
||||
) {
|
||||
MFEM_VERIFY(
|
||||
trueVector.Size() == finiteElementSpace.GetTrueVSize(),
|
||||
"True vector has the wrong size."
|
||||
);
|
||||
MFEM_VERIFY(trueVector.Size() == finiteElementSpace.GetTrueVSize(), "True vector has the wrong size.");
|
||||
|
||||
localVector.SetSize(finiteElementSpace.GetVSize());
|
||||
|
||||
const mfem::Operator *prolongation =
|
||||
finiteElementSpace.GetProlongationMatrix();
|
||||
const mfem::Operator *prolongation = finiteElementSpace.GetProlongationMatrix();
|
||||
|
||||
if (prolongation != nullptr) {
|
||||
prolongation->Mult(trueVector, localVector);
|
||||
@@ -39,16 +44,12 @@ namespace {
|
||||
const mfem::Vector &localVector,
|
||||
mfem::Vector &trueVector
|
||||
) {
|
||||
MFEM_VERIFY(
|
||||
localVector.Size() == finiteElementSpace.GetVSize(),
|
||||
"Local vector has the wrong size."
|
||||
);
|
||||
MFEM_VERIFY(localVector.Size() == finiteElementSpace.GetVSize(), "Local vector has the wrong size.");
|
||||
|
||||
trueVector.SetSize(finiteElementSpace.GetTrueVSize());
|
||||
trueVector = 0.0;
|
||||
trueVector = 0.0;
|
||||
|
||||
const mfem::Operator *prolongation =
|
||||
finiteElementSpace.GetProlongationMatrix();
|
||||
const mfem::Operator *prolongation = finiteElementSpace.GetProlongationMatrix();
|
||||
|
||||
if (prolongation != nullptr) {
|
||||
prolongation->MultTranspose(localVector, trueVector);
|
||||
@@ -57,19 +58,13 @@ namespace {
|
||||
}
|
||||
}
|
||||
|
||||
int get_eos_extra_order(
|
||||
const mean_field::physics::PolytropicBarotrope &barotrope
|
||||
) {
|
||||
const double extraOrder =
|
||||
(barotrope.polytropic_index() - 1.0) *
|
||||
static_cast<double>(
|
||||
mean_field::field::Enthalpy::Scalar::familyOrder
|
||||
);
|
||||
int get_eos_extra_order(const mean_field::eos::Polytrope &barotrope) {
|
||||
const double extraOrder = (barotrope.polytropic_index() - 1.0) *
|
||||
static_cast<double>(mean_field::field::Enthalpy::Scalar::familyOrder);
|
||||
|
||||
MFEM_VERIFY(
|
||||
std::isfinite(extraOrder) && extraOrder >= 0.0 &&
|
||||
extraOrder <=
|
||||
static_cast<double>(std::numeric_limits<int>::max()),
|
||||
extraOrder <= static_cast<double>(std::numeric_limits<int>::max()),
|
||||
"The EOS effective polynomial order is invalid."
|
||||
);
|
||||
|
||||
@@ -78,44 +73,37 @@ namespace {
|
||||
|
||||
const mfem::IntegrationRule &get_eos_rule(
|
||||
const mean_field::fem::FEM &f,
|
||||
const mean_field::physics::PolytropicBarotrope &barotrope,
|
||||
const mean_field::eos::Polytrope &barotrope,
|
||||
const mfem::FiniteElement &densityElement,
|
||||
const mfem::FiniteElement &enthalpyElement,
|
||||
const mfem::ElementTransformation &transformation
|
||||
) {
|
||||
using EnthalpyField =
|
||||
mean_field::field::Field<mean_field::field::Enthalpy>;
|
||||
using EnthalpyField = mean_field::field::Field<mean_field::field::Enthalpy>;
|
||||
|
||||
MFEM_VERIFY(
|
||||
densityElement.GetOrder() ==
|
||||
mean_field::field::Density::Scalar::familyOrder,
|
||||
densityElement.GetOrder() == mean_field::field::Density::Scalar::familyOrder,
|
||||
"The EOS test element does not match the "
|
||||
"registered density field."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
enthalpyElement.GetOrder() ==
|
||||
mean_field::field::Enthalpy::Scalar::familyOrder,
|
||||
enthalpyElement.GetOrder() == mean_field::field::Enthalpy::Scalar::familyOrder,
|
||||
"The EOS trial element does not match the "
|
||||
"registered enthalpy field."
|
||||
);
|
||||
|
||||
const mean_field::quadrature::Query query = EnthalpyField::make_query<
|
||||
mean_field::field::Enthalpy::Form::EosClosureSource>(
|
||||
mean_field::quadrature::QuadratureRole::discretization,
|
||||
transformation.OrderW(),
|
||||
std::array<int, 1>{get_eos_extra_order(barotrope)},
|
||||
mean_field::utils::DOMAINS::STELLAR,
|
||||
mean_field::quadrature::MappingKind::general
|
||||
);
|
||||
const mean_field::quadrature::Query query =
|
||||
EnthalpyField::make_query<mean_field::field::Enthalpy::Form::EosClosureSource>(
|
||||
mean_field::quadrature::QuadratureRole::discretization, transformation.OrderW(),
|
||||
std::array<int, 1>{get_eos_extra_order(barotrope)}, mean_field::utils::DOMAINS::STELLAR,
|
||||
mean_field::quadrature::MappingKind::general
|
||||
);
|
||||
|
||||
const auto resolution =
|
||||
f.quadratureFactory->get(query, transformation.GetGeometryType());
|
||||
const auto resolution = f.quadratureFactory->get(query, transformation.GetGeometryType());
|
||||
|
||||
MFEM_VERIFY(
|
||||
resolution.integration_rule != nullptr,
|
||||
"The quadrature policy did not return an "
|
||||
"EOS-closure integration rule."
|
||||
resolution.integration_rule != nullptr, "The quadrature policy did not return an "
|
||||
"EOS-closure integration rule."
|
||||
);
|
||||
|
||||
return *resolution.integration_rule;
|
||||
@@ -126,54 +114,44 @@ namespace {
|
||||
const mean_field::mapping::DomainMapperStateless &domainMapper,
|
||||
const mfem::Vector &displacementTrue
|
||||
) {
|
||||
MFEM_VERIFY(f.mesh != nullptr, "The EOS closure kernel requires a mesh.");
|
||||
MFEM_VERIFY(
|
||||
f.mesh != nullptr, "The EOS closure kernel requires a mesh."
|
||||
f.densityFes != nullptr, "The EOS closure kernel requires the density "
|
||||
"finite-element space."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.densityFes != nullptr,
|
||||
"The EOS closure kernel requires the density "
|
||||
"finite-element space."
|
||||
f.enthalpyFes != nullptr, "The EOS closure kernel requires the enthalpy "
|
||||
"finite-element space."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.enthalpyFes != nullptr,
|
||||
"The EOS closure kernel requires the enthalpy "
|
||||
"finite-element space."
|
||||
f.displacementFes != nullptr, "The EOS closure kernel requires the displacement "
|
||||
"finite-element space."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.displacementFes != nullptr,
|
||||
"The EOS closure kernel requires the displacement "
|
||||
"finite-element space."
|
||||
f.compactificationFes != nullptr, "The EOS closure kernel requires the "
|
||||
"compactification finite-element space."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.compactificationFes != nullptr,
|
||||
"The EOS closure kernel requires the "
|
||||
"compactification finite-element space."
|
||||
f.compactificationCoordinate != nullptr, "The EOS closure kernel requires the "
|
||||
"compactification coordinate."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.compactificationCoordinate != nullptr,
|
||||
"The EOS closure kernel requires the "
|
||||
"compactification coordinate."
|
||||
f.quadratureFactory != nullptr, "The EOS closure kernel requires the quadrature "
|
||||
"rule factory."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.quadratureFactory != nullptr,
|
||||
"The EOS closure kernel requires the quadrature "
|
||||
"rule factory."
|
||||
displacementTrue.Size() == f.displacementFes->GetTrueVSize(), "The displacement vector has the wrong size."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
displacementTrue.Size() == f.displacementFes->GetTrueVSize(),
|
||||
"The displacement vector has the wrong size."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
domainMapper.GetDimension() == f.mesh->Dimension(),
|
||||
"The domain-mapper dimension does not match "
|
||||
"the mesh dimension."
|
||||
domainMapper.GetDimension() == f.mesh->Dimension(), "The domain-mapper dimension does not match "
|
||||
"the mesh dimension."
|
||||
);
|
||||
}
|
||||
|
||||
void apply_closure_action(
|
||||
const mean_field::fem::FEM &f,
|
||||
const mean_field::mapping::DomainMapperStateless &domainMapper,
|
||||
const mean_field::physics::PolytropicBarotrope &barotrope,
|
||||
const mean_field::eos::Polytrope &barotrope,
|
||||
const ClosureAction closureAction,
|
||||
const mfem::Vector *densityInputTrue,
|
||||
const mfem::Vector *baseEnthalpyTrue,
|
||||
@@ -183,29 +161,23 @@ namespace {
|
||||
) {
|
||||
validate_common_inputs(f, domainMapper, displacementTrue);
|
||||
|
||||
if (closureAction == ClosureAction::residual ||
|
||||
closureAction == ClosureAction::density) {
|
||||
if (closureAction == ClosureAction::residual || closureAction == ClosureAction::density) {
|
||||
MFEM_VERIFY(
|
||||
densityInputTrue != nullptr &&
|
||||
densityInputTrue->Size() == f.densityFes->GetTrueVSize(),
|
||||
densityInputTrue != nullptr && densityInputTrue->Size() == f.densityFes->GetTrueVSize(),
|
||||
"The density input has the wrong size."
|
||||
);
|
||||
}
|
||||
|
||||
if (closureAction == ClosureAction::residual ||
|
||||
closureAction == ClosureAction::enthalpy) {
|
||||
if (closureAction == ClosureAction::residual || closureAction == ClosureAction::enthalpy) {
|
||||
MFEM_VERIFY(
|
||||
baseEnthalpyTrue != nullptr &&
|
||||
baseEnthalpyTrue->Size() == f.enthalpyFes->GetTrueVSize(),
|
||||
baseEnthalpyTrue != nullptr && baseEnthalpyTrue->Size() == f.enthalpyFes->GetTrueVSize(),
|
||||
"The base enthalpy has the wrong size."
|
||||
);
|
||||
}
|
||||
|
||||
if (closureAction == ClosureAction::enthalpy) {
|
||||
MFEM_VERIFY(
|
||||
enthalpyVariationTrue != nullptr &&
|
||||
enthalpyVariationTrue->Size() ==
|
||||
f.enthalpyFes->GetTrueVSize(),
|
||||
enthalpyVariationTrue != nullptr && enthalpyVariationTrue->Size() == f.enthalpyFes->GetTrueVSize(),
|
||||
"The enthalpy variation has the wrong size."
|
||||
);
|
||||
}
|
||||
@@ -224,9 +196,7 @@ namespace {
|
||||
}
|
||||
|
||||
if (enthalpyVariationTrue != nullptr) {
|
||||
true_to_local(
|
||||
*f.enthalpyFes, *enthalpyVariationTrue, enthalpyVariationLocal
|
||||
);
|
||||
true_to_local(*f.enthalpyFes, *enthalpyVariationTrue, enthalpyVariationLocal);
|
||||
}
|
||||
|
||||
true_to_local(*f.displacementFes, displacementTrue, displacementLocal);
|
||||
@@ -234,9 +204,7 @@ namespace {
|
||||
mfem::Vector localAction(f.densityFes->GetVSize());
|
||||
localAction = 0.0;
|
||||
|
||||
mean_field::mapping::DomainMapperStateless::Workspace workspace(
|
||||
f.mesh->Dimension()
|
||||
);
|
||||
mean_field::mapping::DomainMapperStateless::Workspace workspace(f.mesh->Dimension());
|
||||
|
||||
mfem::Array<int> densityDofs;
|
||||
mfem::Array<int> enthalpyDofs;
|
||||
@@ -253,150 +221,105 @@ namespace {
|
||||
mfem::Vector densityShape;
|
||||
mfem::Vector enthalpyShape;
|
||||
|
||||
const int vacuumAttribute = domainMapper.GetVacuumElementAttribute();
|
||||
|
||||
for (int elementId = 0; elementId < f.mesh->GetNE(); ++elementId) {
|
||||
mfem::ElementTransformation *transformation =
|
||||
f.mesh->GetElementTransformation(elementId);
|
||||
mfem::ElementTransformation *transformation = f.mesh->GetElementTransformation(elementId);
|
||||
|
||||
MFEM_VERIFY(
|
||||
transformation != nullptr,
|
||||
"The EOS closure kernel received a null "
|
||||
"element transformation."
|
||||
transformation != nullptr, "The EOS closure kernel received a null "
|
||||
"element transformation."
|
||||
);
|
||||
|
||||
if (transformation->Attribute == vacuumAttribute) {
|
||||
if (!element_is_in_closure_support(transformation->Attribute)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const mfem::FiniteElement &densityElement =
|
||||
*f.densityFes->GetFE(elementId);
|
||||
const mfem::FiniteElement &enthalpyElement =
|
||||
*f.enthalpyFes->GetFE(elementId);
|
||||
const mfem::FiniteElement &displacementElement =
|
||||
*f.displacementFes->GetFE(elementId);
|
||||
const mfem::FiniteElement &compactificationElement =
|
||||
*f.compactificationFes->GetFE(elementId);
|
||||
const mfem::FiniteElement &densityElement = *f.densityFes->GetFE(elementId);
|
||||
const mfem::FiniteElement &enthalpyElement = *f.enthalpyFes->GetFE(elementId);
|
||||
const mfem::FiniteElement &displacementElement = *f.displacementFes->GetFE(elementId);
|
||||
const mfem::FiniteElement &compactificationElement = *f.compactificationFes->GetFE(elementId);
|
||||
|
||||
mfem::DofTransformation *densityDofTransformation =
|
||||
f.densityFes->GetElementDofs(elementId, densityDofs);
|
||||
mfem::DofTransformation *densityDofTransformation = f.densityFes->GetElementDofs(elementId, densityDofs);
|
||||
|
||||
mfem::DofTransformation *enthalpyDofTransformation =
|
||||
f.enthalpyFes->GetElementDofs(elementId, enthalpyDofs);
|
||||
mfem::DofTransformation *enthalpyDofTransformation = f.enthalpyFes->GetElementDofs(elementId, enthalpyDofs);
|
||||
|
||||
mfem::DofTransformation *displacementDofTransformation =
|
||||
f.displacementFes->GetElementVDofs(elementId, displacementDofs);
|
||||
|
||||
mfem::DofTransformation *compactificationDofTransformation =
|
||||
f.compactificationFes->GetElementDofs(
|
||||
elementId, compactificationDofs
|
||||
);
|
||||
f.compactificationFes->GetElementDofs(elementId, compactificationDofs);
|
||||
|
||||
if (densityInputTrue != nullptr) {
|
||||
densityInputLocal.GetSubVector(
|
||||
densityDofs, elementDensityInput
|
||||
);
|
||||
densityInputLocal.GetSubVector(densityDofs, elementDensityInput);
|
||||
|
||||
if (densityDofTransformation != nullptr) {
|
||||
densityDofTransformation->InvTransformPrimal(
|
||||
elementDensityInput
|
||||
);
|
||||
densityDofTransformation->InvTransformPrimal(elementDensityInput);
|
||||
}
|
||||
}
|
||||
|
||||
if (baseEnthalpyTrue != nullptr) {
|
||||
baseEnthalpyLocal.GetSubVector(
|
||||
enthalpyDofs, elementBaseEnthalpy
|
||||
);
|
||||
baseEnthalpyLocal.GetSubVector(enthalpyDofs, elementBaseEnthalpy);
|
||||
|
||||
if (enthalpyDofTransformation != nullptr) {
|
||||
enthalpyDofTransformation->InvTransformPrimal(
|
||||
elementBaseEnthalpy
|
||||
);
|
||||
enthalpyDofTransformation->InvTransformPrimal(elementBaseEnthalpy);
|
||||
}
|
||||
}
|
||||
|
||||
if (enthalpyVariationTrue != nullptr) {
|
||||
enthalpyVariationLocal.GetSubVector(
|
||||
enthalpyDofs, elementEnthalpyVariation
|
||||
);
|
||||
enthalpyVariationLocal.GetSubVector(enthalpyDofs, elementEnthalpyVariation);
|
||||
|
||||
if (enthalpyDofTransformation != nullptr) {
|
||||
enthalpyDofTransformation->InvTransformPrimal(
|
||||
elementEnthalpyVariation
|
||||
);
|
||||
enthalpyDofTransformation->InvTransformPrimal(elementEnthalpyVariation);
|
||||
}
|
||||
}
|
||||
|
||||
displacementLocal.GetSubVector(
|
||||
displacementDofs, elementDisplacement
|
||||
);
|
||||
displacementLocal.GetSubVector(displacementDofs, elementDisplacement);
|
||||
|
||||
f.compactificationCoordinate->GetSubVector(
|
||||
compactificationDofs, elementCompactification
|
||||
);
|
||||
f.compactificationCoordinate->GetSubVector(compactificationDofs, elementCompactification);
|
||||
|
||||
if (displacementDofTransformation != nullptr) {
|
||||
displacementDofTransformation->InvTransformPrimal(
|
||||
elementDisplacement
|
||||
);
|
||||
displacementDofTransformation->InvTransformPrimal(elementDisplacement);
|
||||
}
|
||||
|
||||
if (compactificationDofTransformation != nullptr) {
|
||||
compactificationDofTransformation->InvTransformPrimal(
|
||||
elementCompactification
|
||||
);
|
||||
compactificationDofTransformation->InvTransformPrimal(elementCompactification);
|
||||
}
|
||||
|
||||
const mean_field::mapping::ElementDisplacementData
|
||||
displacementData = mean_field::mapping::
|
||||
ElementDisplacementDataFromElementVDofs(
|
||||
displacementElement, elementDisplacement
|
||||
);
|
||||
const mean_field::mapping::ElementDisplacementData displacementData =
|
||||
mean_field::mapping::ElementDisplacementDataFromElementVDofs(displacementElement, elementDisplacement);
|
||||
|
||||
const mean_field::mapping::ElementCompactificationData
|
||||
compactificationData(
|
||||
compactificationElement, elementCompactification
|
||||
);
|
||||
const mean_field::mapping::ElementCompactificationData compactificationData(
|
||||
compactificationElement, elementCompactification
|
||||
);
|
||||
|
||||
const mean_field::mapping::ElementMappingData mappingData{
|
||||
.displacement = displacementData,
|
||||
.compactification = compactificationData
|
||||
.displacement = displacementData, .compactification = compactificationData
|
||||
};
|
||||
|
||||
densityShape.SetSize(densityElement.GetDof());
|
||||
enthalpyShape.SetSize(enthalpyElement.GetDof());
|
||||
elementAction.SetSize(densityElement.GetDof());
|
||||
elementAction = 0.0;
|
||||
elementAction = 0.0;
|
||||
|
||||
const mfem::IntegrationRule &integrationRule = get_eos_rule(
|
||||
f, barotrope, densityElement, enthalpyElement, *transformation
|
||||
);
|
||||
const mfem::IntegrationRule &integrationRule =
|
||||
get_eos_rule(f, barotrope, densityElement, enthalpyElement, *transformation);
|
||||
|
||||
for (int quadratureIndex = 0;
|
||||
quadratureIndex < integrationRule.GetNPoints();
|
||||
++quadratureIndex) {
|
||||
const mfem::IntegrationPoint &integrationPoint =
|
||||
integrationRule.IntPoint(quadratureIndex);
|
||||
for (int quadratureIndex = 0; quadratureIndex < integrationRule.GetNPoints(); ++quadratureIndex) {
|
||||
const mfem::IntegrationPoint &integrationPoint = integrationRule.IntPoint(quadratureIndex);
|
||||
|
||||
transformation->SetIntPoint(&integrationPoint);
|
||||
|
||||
mean_field::mapping::VolumeMappingContext mappingContext;
|
||||
|
||||
const mean_field::mapping::MappingStatus mappingStatus =
|
||||
domainMapper.EvaluateVolume(
|
||||
mappingData, *transformation, integrationPoint,
|
||||
workspace, mappingContext
|
||||
);
|
||||
const mean_field::mapping::MappingStatus mappingStatus = domainMapper.EvaluateVolume(
|
||||
mappingData, *transformation, integrationPoint, workspace, mappingContext
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
mappingStatus == mean_field::mapping::MappingStatus::valid,
|
||||
"Stateless mapping failed in the EOS "
|
||||
"closure kernel. Element: "
|
||||
<< elementId
|
||||
<< ", attribute: " << transformation->Attribute
|
||||
<< ", quadrature point: " << quadratureIndex
|
||||
<< ", status: " << static_cast<int>(mappingStatus)
|
||||
<< elementId << ", attribute: " << transformation->Attribute
|
||||
<< ", quadrature point: " << quadratureIndex << ", status: " << static_cast<int>(mappingStatus)
|
||||
);
|
||||
|
||||
densityElement.CalcShape(integrationPoint, densityShape);
|
||||
@@ -408,34 +331,23 @@ namespace {
|
||||
} else {
|
||||
enthalpyElement.CalcShape(integrationPoint, enthalpyShape);
|
||||
|
||||
const double baseEnthalpy =
|
||||
elementBaseEnthalpy * enthalpyShape;
|
||||
const double baseEnthalpy = elementBaseEnthalpy * enthalpyShape;
|
||||
|
||||
if (closureAction == ClosureAction::residual) {
|
||||
const double density =
|
||||
elementDensityInput * densityShape;
|
||||
const double density = elementDensityInput * densityShape;
|
||||
|
||||
integrand =
|
||||
density -
|
||||
barotrope.density_from_enthalpy(baseEnthalpy);
|
||||
integrand = density - barotrope.density_from_enthalpy(baseEnthalpy);
|
||||
} else {
|
||||
const double enthalpyVariation =
|
||||
elementEnthalpyVariation * enthalpyShape;
|
||||
const double enthalpyVariation = elementEnthalpyVariation * enthalpyShape;
|
||||
|
||||
integrand = -barotrope.density_derivative_from_enthalpy(
|
||||
baseEnthalpy
|
||||
) *
|
||||
enthalpyVariation;
|
||||
integrand = -barotrope.density_derivative_from_enthalpy(baseEnthalpy) * enthalpyVariation;
|
||||
}
|
||||
}
|
||||
|
||||
const double weightedIntegrand =
|
||||
mappingContext.quadrature.weight * integrand;
|
||||
const double weightedIntegrand = mappingContext.quadrature.weight * integrand;
|
||||
|
||||
for (int densityDof = 0; densityDof < densityElement.GetDof();
|
||||
++densityDof) {
|
||||
elementAction(densityDof) +=
|
||||
weightedIntegrand * densityShape(densityDof);
|
||||
for (int densityDof = 0; densityDof < densityElement.GetDof(); ++densityDof) {
|
||||
elementAction(densityDof) += weightedIntegrand * densityShape(densityDof);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -454,51 +366,51 @@ namespace mean_field::operators::kernels {
|
||||
void apply_barotropic_closure(
|
||||
const fem::FEM &f,
|
||||
const mapping::DomainMapperStateless &domainMapper,
|
||||
const physics::PolytropicBarotrope &barotrope,
|
||||
const eos::Polytrope &barotrope,
|
||||
const mfem::Vector &densityTrue,
|
||||
const mfem::Vector &enthalpyTrue,
|
||||
const mfem::Vector &displacementTrue,
|
||||
mfem::Vector &residual
|
||||
) {
|
||||
apply_closure_action(
|
||||
f, domainMapper, barotrope, ClosureAction::residual, &densityTrue,
|
||||
&enthalpyTrue, nullptr, displacementTrue, residual
|
||||
f, domainMapper, barotrope, ClosureAction::residual, &densityTrue, &enthalpyTrue, nullptr, displacementTrue,
|
||||
residual
|
||||
);
|
||||
}
|
||||
|
||||
void apply_barotropic_closure_density_action(
|
||||
const fem::FEM &f,
|
||||
const mapping::DomainMapperStateless &domainMapper,
|
||||
const physics::PolytropicBarotrope &barotrope,
|
||||
const eos::Polytrope &barotrope,
|
||||
const mfem::Vector &densityVariationTrue,
|
||||
const mfem::Vector &displacementTrue,
|
||||
mfem::Vector &action
|
||||
) {
|
||||
apply_closure_action(
|
||||
f, domainMapper, barotrope, ClosureAction::density,
|
||||
&densityVariationTrue, nullptr, nullptr, displacementTrue, action
|
||||
f, domainMapper, barotrope, ClosureAction::density, &densityVariationTrue, nullptr, nullptr,
|
||||
displacementTrue, action
|
||||
);
|
||||
}
|
||||
|
||||
void apply_barotropic_closure_enthalpy_action(
|
||||
const fem::FEM &f,
|
||||
const mapping::DomainMapperStateless &domainMapper,
|
||||
const physics::PolytropicBarotrope &barotrope,
|
||||
const eos::Polytrope &barotrope,
|
||||
const mfem::Vector &baseEnthalpyTrue,
|
||||
const mfem::Vector &enthalpyVariationTrue,
|
||||
const mfem::Vector &displacementTrue,
|
||||
mfem::Vector &action
|
||||
) {
|
||||
apply_closure_action(
|
||||
f, domainMapper, barotrope, ClosureAction::enthalpy, nullptr,
|
||||
&baseEnthalpyTrue, &enthalpyVariationTrue, displacementTrue, action
|
||||
f, domainMapper, barotrope, ClosureAction::enthalpy, nullptr, &baseEnthalpyTrue, &enthalpyVariationTrue,
|
||||
displacementTrue, action
|
||||
);
|
||||
}
|
||||
|
||||
void apply_barotropic_closure_displacement_action(
|
||||
const fem::FEM &f,
|
||||
const mapping::DomainMapperStateless &domainMapper,
|
||||
const physics::PolytropicBarotrope &barotrope,
|
||||
const eos::Polytrope &barotrope,
|
||||
const mfem::Vector &baseDensityTrue,
|
||||
const mfem::Vector &baseEnthalpyTrue,
|
||||
const mfem::Vector &displacementTrue,
|
||||
@@ -511,66 +423,55 @@ namespace mean_field::operators::kernels {
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
f.densityFes != nullptr,
|
||||
"The barotropic-closure displacement action "
|
||||
"requires the density finite-element space."
|
||||
f.densityFes != nullptr, "The barotropic-closure displacement action "
|
||||
"requires the density finite-element space."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
f.enthalpyFes != nullptr,
|
||||
"The barotropic-closure displacement action "
|
||||
"requires the enthalpy finite-element space."
|
||||
f.enthalpyFes != nullptr, "The barotropic-closure displacement action "
|
||||
"requires the enthalpy finite-element space."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
f.displacementFes != nullptr,
|
||||
"The barotropic-closure displacement action "
|
||||
"requires the displacement finite-element space."
|
||||
f.displacementFes != nullptr, "The barotropic-closure displacement action "
|
||||
"requires the displacement finite-element space."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
f.compactificationFes != nullptr,
|
||||
"The barotropic-closure displacement action "
|
||||
"requires the compactification finite-element space."
|
||||
f.compactificationFes != nullptr, "The barotropic-closure displacement action "
|
||||
"requires the compactification finite-element space."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
f.compactificationCoordinate != nullptr,
|
||||
"The barotropic-closure displacement action "
|
||||
"requires the compactification coordinate."
|
||||
f.compactificationCoordinate != nullptr, "The barotropic-closure displacement action "
|
||||
"requires the compactification coordinate."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
f.quadratureFactory != nullptr,
|
||||
"The barotropic-closure displacement action "
|
||||
"requires the quadrature-rule factory."
|
||||
f.quadratureFactory != nullptr, "The barotropic-closure displacement action "
|
||||
"requires the quadrature-rule factory."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
baseDensityTrue.Size() == f.densityFes->GetTrueVSize(),
|
||||
"The base-density vector has the wrong size."
|
||||
baseDensityTrue.Size() == f.densityFes->GetTrueVSize(), "The base-density vector has the wrong size."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
baseEnthalpyTrue.Size() == f.enthalpyFes->GetTrueVSize(),
|
||||
"The base-enthalpy vector has the wrong size."
|
||||
baseEnthalpyTrue.Size() == f.enthalpyFes->GetTrueVSize(), "The base-enthalpy vector has the wrong size."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
displacementTrue.Size() == f.displacementFes->GetTrueVSize(),
|
||||
"The displacement vector has the wrong size."
|
||||
displacementTrue.Size() == f.displacementFes->GetTrueVSize(), "The displacement vector has the wrong size."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
displacementVariationTrue.Size() ==
|
||||
f.displacementFes->GetTrueVSize(),
|
||||
displacementVariationTrue.Size() == f.displacementFes->GetTrueVSize(),
|
||||
"The displacement-variation vector has the wrong size."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
domainMapper.GetDimension() == f.mesh->Dimension(),
|
||||
"The domain-mapper dimension does not match the "
|
||||
"mesh dimension."
|
||||
domainMapper.GetDimension() == f.mesh->Dimension(), "The domain-mapper dimension does not match the "
|
||||
"mesh dimension."
|
||||
);
|
||||
|
||||
mfem::Vector baseDensityLocal;
|
||||
@@ -584,17 +485,12 @@ namespace mean_field::operators::kernels {
|
||||
|
||||
true_to_local(*f.displacementFes, displacementTrue, displacementLocal);
|
||||
|
||||
true_to_local(
|
||||
*f.displacementFes, displacementVariationTrue,
|
||||
displacementVariationLocal
|
||||
);
|
||||
true_to_local(*f.displacementFes, displacementVariationTrue, displacementVariationLocal);
|
||||
|
||||
mfem::Vector localAction(f.densityFes->GetVSize());
|
||||
localAction = 0.0;
|
||||
|
||||
mapping::DomainMapperStateless::Workspace workspace(
|
||||
f.mesh->Dimension()
|
||||
);
|
||||
mapping::DomainMapperStateless::Workspace workspace(f.mesh->Dimension());
|
||||
|
||||
mfem::Array<int> densityDofs;
|
||||
mfem::Array<int> enthalpyDofs;
|
||||
@@ -614,109 +510,76 @@ namespace mean_field::operators::kernels {
|
||||
mapping::VolumeMappingContext mappingContext;
|
||||
mapping::VolumeMappingVariation mappingVariation;
|
||||
|
||||
const int vacuumAttribute = domainMapper.GetVacuumElementAttribute();
|
||||
|
||||
for (int elementId = 0; elementId < f.mesh->GetNE(); ++elementId) {
|
||||
mfem::ElementTransformation *transformation =
|
||||
f.mesh->GetElementTransformation(elementId);
|
||||
mfem::ElementTransformation *transformation = f.mesh->GetElementTransformation(elementId);
|
||||
|
||||
MFEM_VERIFY(
|
||||
transformation != nullptr,
|
||||
"The barotropic-closure displacement action "
|
||||
"received a null element transformation."
|
||||
transformation != nullptr, "The barotropic-closure displacement action "
|
||||
"received a null element transformation."
|
||||
);
|
||||
|
||||
if (transformation->Attribute == vacuumAttribute) {
|
||||
if (!element_is_in_closure_support(transformation->Attribute)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const mfem::FiniteElement &densityElement =
|
||||
*f.densityFes->GetFE(elementId);
|
||||
const mfem::FiniteElement &densityElement = *f.densityFes->GetFE(elementId);
|
||||
|
||||
const mfem::FiniteElement &enthalpyElement =
|
||||
*f.enthalpyFes->GetFE(elementId);
|
||||
const mfem::FiniteElement &enthalpyElement = *f.enthalpyFes->GetFE(elementId);
|
||||
|
||||
const mfem::FiniteElement &displacementElement =
|
||||
*f.displacementFes->GetFE(elementId);
|
||||
const mfem::FiniteElement &displacementElement = *f.displacementFes->GetFE(elementId);
|
||||
|
||||
const mfem::FiniteElement &compactificationElement =
|
||||
*f.compactificationFes->GetFE(elementId);
|
||||
const mfem::FiniteElement &compactificationElement = *f.compactificationFes->GetFE(elementId);
|
||||
|
||||
mfem::DofTransformation *densityDofTransformation =
|
||||
f.densityFes->GetElementDofs(elementId, densityDofs);
|
||||
mfem::DofTransformation *densityDofTransformation = f.densityFes->GetElementDofs(elementId, densityDofs);
|
||||
|
||||
mfem::DofTransformation *enthalpyDofTransformation =
|
||||
f.enthalpyFes->GetElementDofs(elementId, enthalpyDofs);
|
||||
mfem::DofTransformation *enthalpyDofTransformation = f.enthalpyFes->GetElementDofs(elementId, enthalpyDofs);
|
||||
|
||||
mfem::DofTransformation *displacementDofTransformation =
|
||||
f.displacementFes->GetElementVDofs(elementId, displacementDofs);
|
||||
|
||||
mfem::DofTransformation *compactificationDofTransformation =
|
||||
f.compactificationFes->GetElementDofs(
|
||||
elementId, compactificationDofs
|
||||
);
|
||||
f.compactificationFes->GetElementDofs(elementId, compactificationDofs);
|
||||
|
||||
baseDensityLocal.GetSubVector(densityDofs, elementBaseDensity);
|
||||
|
||||
baseEnthalpyLocal.GetSubVector(enthalpyDofs, elementBaseEnthalpy);
|
||||
|
||||
displacementLocal.GetSubVector(
|
||||
displacementDofs, elementDisplacement
|
||||
);
|
||||
displacementLocal.GetSubVector(displacementDofs, elementDisplacement);
|
||||
|
||||
displacementVariationLocal.GetSubVector(
|
||||
displacementDofs, elementDisplacementVariation
|
||||
);
|
||||
displacementVariationLocal.GetSubVector(displacementDofs, elementDisplacementVariation);
|
||||
|
||||
f.compactificationCoordinate->GetSubVector(
|
||||
compactificationDofs, elementCompactification
|
||||
);
|
||||
f.compactificationCoordinate->GetSubVector(compactificationDofs, elementCompactification);
|
||||
|
||||
if (densityDofTransformation != nullptr) {
|
||||
densityDofTransformation->InvTransformPrimal(
|
||||
elementBaseDensity
|
||||
);
|
||||
densityDofTransformation->InvTransformPrimal(elementBaseDensity);
|
||||
}
|
||||
|
||||
if (enthalpyDofTransformation != nullptr) {
|
||||
enthalpyDofTransformation->InvTransformPrimal(
|
||||
elementBaseEnthalpy
|
||||
);
|
||||
enthalpyDofTransformation->InvTransformPrimal(elementBaseEnthalpy);
|
||||
}
|
||||
|
||||
if (displacementDofTransformation != nullptr) {
|
||||
displacementDofTransformation->InvTransformPrimal(
|
||||
elementDisplacement
|
||||
);
|
||||
displacementDofTransformation->InvTransformPrimal(elementDisplacement);
|
||||
|
||||
displacementDofTransformation->InvTransformPrimal(
|
||||
elementDisplacementVariation
|
||||
);
|
||||
displacementDofTransformation->InvTransformPrimal(elementDisplacementVariation);
|
||||
}
|
||||
|
||||
if (compactificationDofTransformation != nullptr) {
|
||||
compactificationDofTransformation->InvTransformPrimal(
|
||||
elementCompactification
|
||||
);
|
||||
compactificationDofTransformation->InvTransformPrimal(elementCompactification);
|
||||
}
|
||||
|
||||
const mapping::ElementDisplacementData displacementData =
|
||||
mapping::ElementDisplacementDataFromElementVDofs(
|
||||
displacementElement, elementDisplacement
|
||||
);
|
||||
mapping::ElementDisplacementDataFromElementVDofs(displacementElement, elementDisplacement);
|
||||
|
||||
const mapping::ElementDisplacementData displacementVariationData =
|
||||
mapping::ElementDisplacementDataFromElementVDofs(
|
||||
displacementElement, elementDisplacementVariation
|
||||
);
|
||||
mapping::ElementDisplacementDataFromElementVDofs(displacementElement, elementDisplacementVariation);
|
||||
|
||||
const mapping::ElementCompactificationData compactificationData(
|
||||
compactificationElement, elementCompactification
|
||||
);
|
||||
|
||||
const mapping::ElementMappingData mappingData{
|
||||
.displacement = displacementData,
|
||||
.compactification = compactificationData
|
||||
.displacement = displacementData, .compactification = compactificationData
|
||||
};
|
||||
|
||||
densityShape.SetSize(densityElement.GetDof());
|
||||
@@ -724,74 +587,57 @@ namespace mean_field::operators::kernels {
|
||||
enthalpyShape.SetSize(enthalpyElement.GetDof());
|
||||
|
||||
elementAction.SetSize(densityElement.GetDof());
|
||||
elementAction = 0.0;
|
||||
elementAction = 0.0;
|
||||
|
||||
const mfem::IntegrationRule &integrationRule = get_eos_rule(
|
||||
f, barotrope, densityElement, enthalpyElement, *transformation
|
||||
);
|
||||
const mfem::IntegrationRule &integrationRule =
|
||||
get_eos_rule(f, barotrope, densityElement, enthalpyElement, *transformation);
|
||||
|
||||
for (int quadraturePoint = 0;
|
||||
quadraturePoint < integrationRule.GetNPoints();
|
||||
++quadraturePoint) {
|
||||
const mfem::IntegrationPoint &integrationPoint =
|
||||
integrationRule.IntPoint(quadraturePoint);
|
||||
for (int quadraturePoint = 0; quadraturePoint < integrationRule.GetNPoints(); ++quadraturePoint) {
|
||||
const mfem::IntegrationPoint &integrationPoint = integrationRule.IntPoint(quadraturePoint);
|
||||
|
||||
transformation->SetIntPoint(&integrationPoint);
|
||||
|
||||
const mapping::MappingStatus mappingStatus =
|
||||
domainMapper.EvaluateVolume(
|
||||
mappingData, *transformation, integrationPoint,
|
||||
workspace, mappingContext
|
||||
);
|
||||
const mapping::MappingStatus mappingStatus = domainMapper.EvaluateVolume(
|
||||
mappingData, *transformation, integrationPoint, workspace, mappingContext
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
mappingStatus == mapping::MappingStatus::valid,
|
||||
"The base mapping is invalid while applying "
|
||||
"the barotropic-closure displacement action. "
|
||||
"Element: "
|
||||
<< elementId
|
||||
<< ", attribute: " << transformation->Attribute
|
||||
<< ", quadrature point: " << quadraturePoint
|
||||
<< ", status: " << static_cast<int>(mappingStatus)
|
||||
<< elementId << ", attribute: " << transformation->Attribute
|
||||
<< ", quadrature point: " << quadraturePoint << ", status: " << static_cast<int>(mappingStatus)
|
||||
);
|
||||
|
||||
const mapping::MappingStatus variationStatus =
|
||||
domainMapper.EvaluateVolumeVariation(
|
||||
mappingData, displacementVariationData, *transformation,
|
||||
integrationPoint, mappingContext, workspace,
|
||||
mappingVariation
|
||||
);
|
||||
const mapping::MappingStatus variationStatus = domainMapper.EvaluateVolumeVariation(
|
||||
mappingData, displacementVariationData, *transformation, integrationPoint, mappingContext,
|
||||
workspace, mappingVariation
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
variationStatus == mapping::MappingStatus::valid,
|
||||
"The mapping variation is invalid while "
|
||||
"applying the barotropic-closure "
|
||||
"displacement action. Element: "
|
||||
<< elementId
|
||||
<< ", attribute: " << transformation->Attribute
|
||||
<< ", quadrature point: " << quadraturePoint
|
||||
<< ", status: " << static_cast<int>(variationStatus)
|
||||
<< elementId << ", attribute: " << transformation->Attribute << ", quadrature point: "
|
||||
<< quadraturePoint << ", status: " << static_cast<int>(variationStatus)
|
||||
);
|
||||
|
||||
densityElement.CalcShape(integrationPoint, densityShape);
|
||||
|
||||
enthalpyElement.CalcShape(integrationPoint, enthalpyShape);
|
||||
|
||||
const double densityValue = elementBaseDensity * densityShape;
|
||||
const double densityValue = elementBaseDensity * densityShape;
|
||||
|
||||
const double enthalpyValue =
|
||||
elementBaseEnthalpy * enthalpyShape;
|
||||
const double enthalpyValue = elementBaseEnthalpy * enthalpyShape;
|
||||
|
||||
const double closureValue =
|
||||
densityValue -
|
||||
barotrope.density_from_enthalpy(enthalpyValue);
|
||||
const double closureValue = densityValue - barotrope.density_from_enthalpy(enthalpyValue);
|
||||
|
||||
const double geometryActionValue =
|
||||
closureValue * mappingVariation.weight_variation;
|
||||
const double geometryActionValue = closureValue * mappingVariation.weight_variation;
|
||||
|
||||
MFEM_VERIFY(
|
||||
std::isfinite(closureValue) &&
|
||||
std::isfinite(geometryActionValue),
|
||||
std::isfinite(closureValue) && std::isfinite(geometryActionValue),
|
||||
"The barotropic-closure displacement action "
|
||||
"encountered a non-finite quadrature value."
|
||||
);
|
||||
|
||||
@@ -0,0 +1,718 @@
|
||||
module;
|
||||
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <optional>
|
||||
|
||||
#include <mfem.hpp>
|
||||
|
||||
module mean_field;
|
||||
|
||||
import :operators.kernels.gravity_displacement_force;
|
||||
|
||||
namespace {
|
||||
enum class GravityDisplacementForceAction { residual, density, gravityGradient, displacement, complete };
|
||||
|
||||
void true_to_local(
|
||||
const mfem::ParFiniteElementSpace &finiteElementSpace,
|
||||
const mfem::Vector &trueVector,
|
||||
mfem::Vector &localVector
|
||||
) {
|
||||
MFEM_VERIFY(
|
||||
trueVector.Size() == finiteElementSpace.GetTrueVSize(),
|
||||
"The gravity-displacement-force true vector has the wrong size."
|
||||
);
|
||||
|
||||
localVector.SetSize(finiteElementSpace.GetVSize());
|
||||
|
||||
const mfem::Operator *prolongation = finiteElementSpace.GetProlongationMatrix();
|
||||
|
||||
if (prolongation != nullptr) {
|
||||
prolongation->Mult(trueVector, localVector);
|
||||
} else {
|
||||
localVector = trueVector;
|
||||
}
|
||||
}
|
||||
|
||||
void local_to_true(
|
||||
const mfem::ParFiniteElementSpace &finiteElementSpace,
|
||||
const mfem::Vector &localVector,
|
||||
mfem::Vector &trueVector
|
||||
) {
|
||||
MFEM_VERIFY(
|
||||
localVector.Size() == finiteElementSpace.GetVSize(),
|
||||
"The gravity-displacement-force local vector has the wrong size."
|
||||
);
|
||||
|
||||
trueVector.SetSize(finiteElementSpace.GetTrueVSize());
|
||||
trueVector = 0.0;
|
||||
|
||||
const mfem::Operator *prolongation = finiteElementSpace.GetProlongationMatrix();
|
||||
|
||||
if (prolongation != nullptr) {
|
||||
prolongation->MultTranspose(localVector, trueVector);
|
||||
} else {
|
||||
trueVector = localVector;
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] int vector_dof_index(
|
||||
const mfem::Ordering::Type ordering,
|
||||
const int scalarDof,
|
||||
const int component,
|
||||
const int scalarDofCount,
|
||||
const int dimension
|
||||
) {
|
||||
if (ordering == mfem::Ordering::byNODES) {
|
||||
return scalarDof + component * scalarDofCount;
|
||||
}
|
||||
|
||||
if (ordering == mfem::Ordering::byVDIM) {
|
||||
return scalarDof * dimension + component;
|
||||
}
|
||||
|
||||
MFEM_ABORT(
|
||||
"The gravity-displacement-force test space uses an unsupported "
|
||||
"ordering."
|
||||
);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
[[nodiscard]] const mfem::IntegrationRule &get_gravity_force_rule(
|
||||
const mean_field::fem::FEM &f,
|
||||
const mfem::FiniteElement &densityElement,
|
||||
const mfem::FiniteElement &gravityGradientElement,
|
||||
const mfem::FiniteElement &displacementElement,
|
||||
const mfem::ElementTransformation &transformation
|
||||
) {
|
||||
using DisplacementField = mean_field::field::Field<mean_field::field::Displacement>;
|
||||
|
||||
MFEM_VERIFY(
|
||||
densityElement.GetOrder() == mean_field::field::Density::Scalar::familyOrder,
|
||||
"The gravity-displacement-force density element does not match "
|
||||
"the registered density field."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
gravityGradientElement.GetOrder() == mean_field::field::Gravity::Flux::familyOrder + 1,
|
||||
"The gravity-displacement-force RT element does not match the "
|
||||
"registered gravity-gradient field."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
displacementElement.GetOrder() == mean_field::field::Displacement::Vector::familyOrder,
|
||||
"The gravity-displacement-force test element does not match the "
|
||||
"registered displacement field."
|
||||
);
|
||||
|
||||
const mean_field::quadrature::Query query =
|
||||
DisplacementField::make_query<mean_field::field::Displacement::Form::GravityForce>(
|
||||
mean_field::quadrature::QuadratureRole::discretization, transformation.OrderW(), {},
|
||||
mean_field::utils::DOMAINS::STELLAR, mean_field::quadrature::MappingKind::general
|
||||
);
|
||||
|
||||
const mean_field::quadrature::MfemRule rule = f.quadratureFactory->get(query, transformation.GetGeometryType());
|
||||
|
||||
MFEM_VERIFY(
|
||||
rule.integration_rule != nullptr, "The quadrature policy did not return a gravity-displacement-"
|
||||
"force integration rule."
|
||||
);
|
||||
|
||||
return *rule.integration_rule;
|
||||
}
|
||||
|
||||
void validate_finite_vector(
|
||||
const mfem::Vector &vector,
|
||||
const char *message
|
||||
) {
|
||||
for (int index = 0; index < vector.Size(); ++index) {
|
||||
MFEM_VERIFY(std::isfinite(vector(index)), message);
|
||||
}
|
||||
}
|
||||
|
||||
void validate_common_inputs(
|
||||
const mean_field::fem::FEM &f,
|
||||
const mean_field::mapping::DomainMapperStateless &domainMapper,
|
||||
const mfem::Vector &displacementTrue
|
||||
) {
|
||||
MFEM_VERIFY(f.mesh != nullptr, "The gravity-displacement-force kernel requires a mesh.");
|
||||
|
||||
MFEM_VERIFY(
|
||||
f.densityFes != nullptr, "The gravity-displacement-force kernel requires the density "
|
||||
"finite-element space."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
f.gravityFluxFes != nullptr, "The gravity-displacement-force kernel requires the gravity-"
|
||||
"gradient finite-element space."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
f.displacementFes != nullptr, "The gravity-displacement-force kernel requires the displacement "
|
||||
"finite-element space."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
f.compactificationFes != nullptr && f.compactificationCoordinate != nullptr,
|
||||
"The gravity-displacement-force kernel requires the "
|
||||
"compactification coordinate."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
f.quadratureFactory != nullptr, "The gravity-displacement-force kernel requires the quadrature "
|
||||
"rule factory."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
displacementTrue.Size() == f.displacementFes->GetTrueVSize(),
|
||||
"The gravity-displacement-force displacement vector has the "
|
||||
"wrong size."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
domainMapper.GetDimension() == f.mesh->Dimension(),
|
||||
"The gravity-displacement-force mapper dimension does not match "
|
||||
"the mesh dimension."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
f.displacementFes->GetVDim() == f.mesh->Dimension(),
|
||||
"The gravity-displacement-force displacement dimension does not "
|
||||
"match the mesh dimension."
|
||||
);
|
||||
|
||||
validate_finite_vector(
|
||||
displacementTrue, "The gravity-displacement-force displacement contains a "
|
||||
"non-finite value."
|
||||
);
|
||||
}
|
||||
|
||||
void validate_density(
|
||||
const mean_field::fem::FEM &f,
|
||||
const mfem::Vector &density,
|
||||
const char *message
|
||||
) {
|
||||
MFEM_VERIFY(density.Size() == f.densityFes->GetTrueVSize(), message);
|
||||
validate_finite_vector(density, message);
|
||||
}
|
||||
|
||||
void validate_gravity_gradient(
|
||||
const mean_field::fem::FEM &f,
|
||||
const mfem::Vector &gravityGradient,
|
||||
const char *message
|
||||
) {
|
||||
MFEM_VERIFY(gravityGradient.Size() == f.gravityFluxFes->GetTrueVSize(), message);
|
||||
|
||||
validate_finite_vector(gravityGradient, message);
|
||||
}
|
||||
|
||||
void apply_gravity_displacement_force_action(
|
||||
const mean_field::fem::FEM &f,
|
||||
const mean_field::mapping::DomainMapperStateless &domainMapper,
|
||||
const GravityDisplacementForceAction requestedAction,
|
||||
const mfem::Vector *baseDensityTrue,
|
||||
const mfem::Vector *densityVariationTrue,
|
||||
const mfem::Vector *baseGravityGradientTrue,
|
||||
const mfem::Vector *gravityGradientVariationTrue,
|
||||
const mfem::Vector *displacementVariationTrue,
|
||||
const mfem::Vector &displacementTrue,
|
||||
mfem::Vector &actionTrue
|
||||
) {
|
||||
validate_common_inputs(f, domainMapper, displacementTrue);
|
||||
|
||||
const bool needsBaseDensity = requestedAction == GravityDisplacementForceAction::residual ||
|
||||
requestedAction == GravityDisplacementForceAction::gravityGradient ||
|
||||
requestedAction == GravityDisplacementForceAction::displacement ||
|
||||
requestedAction == GravityDisplacementForceAction::complete;
|
||||
|
||||
const bool needsDensityVariation = requestedAction == GravityDisplacementForceAction::density ||
|
||||
requestedAction == GravityDisplacementForceAction::complete;
|
||||
|
||||
const bool needsBaseGravityGradient = requestedAction == GravityDisplacementForceAction::residual ||
|
||||
requestedAction == GravityDisplacementForceAction::density ||
|
||||
requestedAction == GravityDisplacementForceAction::displacement ||
|
||||
requestedAction == GravityDisplacementForceAction::complete;
|
||||
|
||||
const bool needsGravityGradientVariation = requestedAction == GravityDisplacementForceAction::gravityGradient ||
|
||||
requestedAction == GravityDisplacementForceAction::complete;
|
||||
|
||||
const bool needsDisplacementVariation = requestedAction == GravityDisplacementForceAction::displacement ||
|
||||
requestedAction == GravityDisplacementForceAction::complete;
|
||||
|
||||
if (needsBaseDensity) {
|
||||
MFEM_VERIFY(
|
||||
baseDensityTrue != nullptr, "The gravity-displacement-force action requires a base "
|
||||
"density."
|
||||
);
|
||||
|
||||
validate_density(f, *baseDensityTrue, "The gravity-displacement-force base density is invalid.");
|
||||
}
|
||||
|
||||
if (needsDensityVariation) {
|
||||
MFEM_VERIFY(
|
||||
densityVariationTrue != nullptr, "The gravity-displacement-force action requires a density "
|
||||
"variation."
|
||||
);
|
||||
|
||||
validate_density(
|
||||
f, *densityVariationTrue,
|
||||
"The gravity-displacement-force density variation is "
|
||||
"invalid."
|
||||
);
|
||||
}
|
||||
|
||||
if (needsBaseGravityGradient) {
|
||||
MFEM_VERIFY(
|
||||
baseGravityGradientTrue != nullptr, "The gravity-displacement-force action requires a base "
|
||||
"gravity gradient."
|
||||
);
|
||||
|
||||
validate_gravity_gradient(
|
||||
f, *baseGravityGradientTrue,
|
||||
"The gravity-displacement-force base gravity gradient is "
|
||||
"invalid."
|
||||
);
|
||||
}
|
||||
|
||||
if (needsGravityGradientVariation) {
|
||||
MFEM_VERIFY(
|
||||
gravityGradientVariationTrue != nullptr, "The gravity-displacement-force action requires a gravity-"
|
||||
"gradient variation."
|
||||
);
|
||||
|
||||
validate_gravity_gradient(
|
||||
f, *gravityGradientVariationTrue,
|
||||
"The gravity-displacement-force gravity-gradient variation "
|
||||
"is invalid."
|
||||
);
|
||||
}
|
||||
|
||||
if (needsDisplacementVariation) {
|
||||
MFEM_VERIFY(
|
||||
displacementVariationTrue != nullptr &&
|
||||
displacementVariationTrue->Size() == f.displacementFes->GetTrueVSize(),
|
||||
"The gravity-displacement-force displacement variation is "
|
||||
"invalid."
|
||||
);
|
||||
|
||||
validate_finite_vector(
|
||||
*displacementVariationTrue, "The gravity-displacement-force displacement variation "
|
||||
"contains a non-finite value."
|
||||
);
|
||||
}
|
||||
|
||||
mfem::Vector baseDensityLocal;
|
||||
mfem::Vector densityVariationLocal;
|
||||
mfem::Vector baseGravityGradientLocal;
|
||||
mfem::Vector gravityGradientVariationLocal;
|
||||
mfem::Vector displacementLocal;
|
||||
mfem::Vector displacementVariationLocal;
|
||||
|
||||
if (needsBaseDensity) {
|
||||
true_to_local(*f.densityFes, *baseDensityTrue, baseDensityLocal);
|
||||
}
|
||||
|
||||
if (needsDensityVariation) {
|
||||
true_to_local(*f.densityFes, *densityVariationTrue, densityVariationLocal);
|
||||
}
|
||||
|
||||
if (needsBaseGravityGradient) {
|
||||
true_to_local(*f.gravityFluxFes, *baseGravityGradientTrue, baseGravityGradientLocal);
|
||||
}
|
||||
|
||||
if (needsGravityGradientVariation) {
|
||||
true_to_local(*f.gravityFluxFes, *gravityGradientVariationTrue, gravityGradientVariationLocal);
|
||||
}
|
||||
|
||||
true_to_local(*f.displacementFes, displacementTrue, displacementLocal);
|
||||
|
||||
if (needsDisplacementVariation) {
|
||||
true_to_local(*f.displacementFes, *displacementVariationTrue, displacementVariationLocal);
|
||||
}
|
||||
|
||||
mfem::Vector localAction(f.displacementFes->GetVSize());
|
||||
localAction = 0.0;
|
||||
|
||||
mean_field::mapping::DomainMapperStateless::Workspace workspace(f.mesh->Dimension());
|
||||
|
||||
mfem::Array<int> densityDofs;
|
||||
mfem::Array<int> gravityGradientDofs;
|
||||
mfem::Array<int> displacementDofs;
|
||||
mfem::Array<int> compactificationDofs;
|
||||
|
||||
mfem::Vector elementBaseDensity;
|
||||
mfem::Vector elementDensityVariation;
|
||||
mfem::Vector elementBaseGravityGradient;
|
||||
mfem::Vector elementGravityGradientVariation;
|
||||
mfem::Vector elementDisplacement;
|
||||
mfem::Vector elementDisplacementVariation;
|
||||
mfem::Vector elementCompactification;
|
||||
mfem::Vector elementAction;
|
||||
|
||||
mfem::Vector densityShape;
|
||||
mfem::Vector displacementShape;
|
||||
mfem::DenseMatrix gravityGradientShape;
|
||||
|
||||
mfem::Vector baseGravityReferenceValue;
|
||||
mfem::Vector gravityVariationReferenceValue;
|
||||
mfem::Vector mappedBaseGravity;
|
||||
mfem::Vector mappedGravityVariation;
|
||||
mfem::Vector mappedGeometryVariation;
|
||||
mfem::Vector forceValue;
|
||||
|
||||
mean_field::mapping::VolumeMappingContext mappingContext;
|
||||
mean_field::mapping::VolumeMappingVariation mappingVariation;
|
||||
|
||||
const int dimension = f.mesh->Dimension();
|
||||
const int vacuumAttribute = domainMapper.GetVacuumElementAttribute();
|
||||
|
||||
const mfem::Ordering::Type displacementOrdering = f.displacementFes->GetOrdering();
|
||||
|
||||
for (int elementId = 0; elementId < f.mesh->GetNE(); ++elementId) {
|
||||
mfem::ElementTransformation *transformation = f.mesh->GetElementTransformation(elementId);
|
||||
|
||||
MFEM_VERIFY(
|
||||
transformation != nullptr, "The gravity-displacement-force kernel received a null "
|
||||
"element transformation."
|
||||
);
|
||||
|
||||
if (transformation->Attribute == vacuumAttribute) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const mfem::FiniteElement &densityElement = *f.densityFes->GetFE(elementId);
|
||||
|
||||
const mfem::FiniteElement &gravityGradientElement = *f.gravityFluxFes->GetFE(elementId);
|
||||
|
||||
const mfem::FiniteElement &displacementElement = *f.displacementFes->GetFE(elementId);
|
||||
|
||||
const mfem::FiniteElement &compactificationElement = *f.compactificationFes->GetFE(elementId);
|
||||
|
||||
mfem::DofTransformation *densityDofTransformation = f.densityFes->GetElementDofs(elementId, densityDofs);
|
||||
|
||||
mfem::DofTransformation *gravityGradientDofTransformation =
|
||||
f.gravityFluxFes->GetElementVDofs(elementId, gravityGradientDofs);
|
||||
|
||||
mfem::DofTransformation *displacementDofTransformation =
|
||||
f.displacementFes->GetElementVDofs(elementId, displacementDofs);
|
||||
|
||||
mfem::DofTransformation *compactificationDofTransformation =
|
||||
f.compactificationFes->GetElementDofs(elementId, compactificationDofs);
|
||||
|
||||
if (needsBaseDensity) {
|
||||
baseDensityLocal.GetSubVector(densityDofs, elementBaseDensity);
|
||||
}
|
||||
|
||||
if (needsDensityVariation) {
|
||||
densityVariationLocal.GetSubVector(densityDofs, elementDensityVariation);
|
||||
}
|
||||
|
||||
if (needsBaseGravityGradient) {
|
||||
baseGravityGradientLocal.GetSubVector(gravityGradientDofs, elementBaseGravityGradient);
|
||||
}
|
||||
|
||||
if (needsGravityGradientVariation) {
|
||||
gravityGradientVariationLocal.GetSubVector(gravityGradientDofs, elementGravityGradientVariation);
|
||||
}
|
||||
|
||||
displacementLocal.GetSubVector(displacementDofs, elementDisplacement);
|
||||
|
||||
if (needsDisplacementVariation) {
|
||||
displacementVariationLocal.GetSubVector(displacementDofs, elementDisplacementVariation);
|
||||
}
|
||||
|
||||
f.compactificationCoordinate->GetSubVector(compactificationDofs, elementCompactification);
|
||||
|
||||
if (densityDofTransformation != nullptr) {
|
||||
if (needsBaseDensity) {
|
||||
densityDofTransformation->InvTransformPrimal(elementBaseDensity);
|
||||
}
|
||||
|
||||
if (needsDensityVariation) {
|
||||
densityDofTransformation->InvTransformPrimal(elementDensityVariation);
|
||||
}
|
||||
}
|
||||
|
||||
if (gravityGradientDofTransformation != nullptr) {
|
||||
if (needsBaseGravityGradient) {
|
||||
gravityGradientDofTransformation->InvTransformPrimal(elementBaseGravityGradient);
|
||||
}
|
||||
|
||||
if (needsGravityGradientVariation) {
|
||||
gravityGradientDofTransformation->InvTransformPrimal(elementGravityGradientVariation);
|
||||
}
|
||||
}
|
||||
|
||||
if (displacementDofTransformation != nullptr) {
|
||||
displacementDofTransformation->InvTransformPrimal(elementDisplacement);
|
||||
|
||||
if (needsDisplacementVariation) {
|
||||
displacementDofTransformation->InvTransformPrimal(elementDisplacementVariation);
|
||||
}
|
||||
}
|
||||
|
||||
if (compactificationDofTransformation != nullptr) {
|
||||
compactificationDofTransformation->InvTransformPrimal(elementCompactification);
|
||||
}
|
||||
|
||||
const mean_field::mapping::ElementDisplacementData displacementData =
|
||||
mean_field::mapping::ElementDisplacementDataFromElementVDofs(displacementElement, elementDisplacement);
|
||||
|
||||
const mean_field::mapping::ElementCompactificationData compactificationData(
|
||||
compactificationElement, elementCompactification
|
||||
);
|
||||
|
||||
const mean_field::mapping::ElementMappingData mappingData{
|
||||
.displacement = displacementData, .compactification = compactificationData
|
||||
};
|
||||
|
||||
std::optional<mean_field::mapping::ElementDisplacementData> displacementVariationData;
|
||||
|
||||
if (needsDisplacementVariation) {
|
||||
displacementVariationData.emplace(
|
||||
mean_field::mapping::ElementDisplacementDataFromElementVDofs(
|
||||
displacementElement, elementDisplacementVariation
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
const int scalarDisplacementDofCount = displacementElement.GetDof();
|
||||
|
||||
MFEM_VERIFY(
|
||||
displacementDofs.Size() == scalarDisplacementDofCount * dimension,
|
||||
"The gravity-displacement-force element displacement vector "
|
||||
"has the wrong size."
|
||||
);
|
||||
|
||||
densityShape.SetSize(densityElement.GetDof());
|
||||
displacementShape.SetSize(scalarDisplacementDofCount);
|
||||
gravityGradientShape.SetSize(gravityGradientElement.GetDof(), dimension);
|
||||
|
||||
baseGravityReferenceValue.SetSize(dimension);
|
||||
gravityVariationReferenceValue.SetSize(dimension);
|
||||
mappedBaseGravity.SetSize(dimension);
|
||||
mappedGravityVariation.SetSize(dimension);
|
||||
mappedGeometryVariation.SetSize(dimension);
|
||||
forceValue.SetSize(dimension);
|
||||
|
||||
elementAction.SetSize(displacementDofs.Size());
|
||||
elementAction = 0.0;
|
||||
|
||||
const mfem::IntegrationRule &integrationRule =
|
||||
get_gravity_force_rule(f, densityElement, gravityGradientElement, displacementElement, *transformation);
|
||||
|
||||
for (int quadratureIndex = 0; quadratureIndex < integrationRule.GetNPoints(); ++quadratureIndex) {
|
||||
const mfem::IntegrationPoint &integrationPoint = integrationRule.IntPoint(quadratureIndex);
|
||||
|
||||
transformation->SetIntPoint(&integrationPoint);
|
||||
|
||||
const mean_field::mapping::MappingStatus mappingStatus = domainMapper.EvaluateVolume(
|
||||
mappingData, *transformation, integrationPoint, workspace, mappingContext
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
mappingStatus == mean_field::mapping::MappingStatus::valid,
|
||||
"Stateless mapping failed in the gravity-displacement-"
|
||||
"force kernel. Element: "
|
||||
<< elementId << ", attribute: " << transformation->Attribute
|
||||
<< ", quadrature point: " << quadratureIndex << ", status: " << static_cast<int>(mappingStatus)
|
||||
);
|
||||
|
||||
if (needsDisplacementVariation) {
|
||||
const mean_field::mapping::MappingStatus variationStatus = domainMapper.EvaluateVolumeVariation(
|
||||
mappingData, *displacementVariationData, *transformation, integrationPoint, mappingContext,
|
||||
workspace, mappingVariation
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
variationStatus == mean_field::mapping::MappingStatus::valid,
|
||||
"Stateless mapping variation failed in the gravity-"
|
||||
"displacement-force kernel. Element: "
|
||||
<< elementId << ", attribute: " << transformation->Attribute << ", quadrature point: "
|
||||
<< quadratureIndex << ", status: " << static_cast<int>(variationStatus)
|
||||
);
|
||||
}
|
||||
|
||||
densityElement.CalcShape(integrationPoint, densityShape);
|
||||
|
||||
displacementElement.CalcShape(integrationPoint, displacementShape);
|
||||
|
||||
gravityGradientElement.CalcVShape(*transformation, gravityGradientShape);
|
||||
|
||||
double baseDensityValue = 0.0;
|
||||
double densityVariationValue = 0.0;
|
||||
|
||||
if (needsBaseDensity) {
|
||||
baseDensityValue = elementBaseDensity * densityShape;
|
||||
}
|
||||
|
||||
if (needsDensityVariation) {
|
||||
densityVariationValue = elementDensityVariation * densityShape;
|
||||
}
|
||||
|
||||
if (needsBaseGravityGradient) {
|
||||
gravityGradientShape.MultTranspose(elementBaseGravityGradient, baseGravityReferenceValue);
|
||||
|
||||
mappingContext.mapping.mapping_jacobian.Mult(baseGravityReferenceValue, mappedBaseGravity);
|
||||
} else {
|
||||
mappedBaseGravity = 0.0;
|
||||
}
|
||||
|
||||
if (needsGravityGradientVariation) {
|
||||
gravityGradientShape.MultTranspose(elementGravityGradientVariation, gravityVariationReferenceValue);
|
||||
|
||||
mappingContext.mapping.mapping_jacobian.Mult(
|
||||
gravityVariationReferenceValue, mappedGravityVariation
|
||||
);
|
||||
} else {
|
||||
mappedGravityVariation = 0.0;
|
||||
}
|
||||
|
||||
if (needsDisplacementVariation) {
|
||||
mappingVariation.mapping.mapping_jacobian_variation.Mult(
|
||||
baseGravityReferenceValue, mappedGeometryVariation
|
||||
);
|
||||
} else {
|
||||
mappedGeometryVariation = 0.0;
|
||||
}
|
||||
|
||||
forceValue = 0.0;
|
||||
|
||||
if (requestedAction == GravityDisplacementForceAction::residual) {
|
||||
forceValue.Add(baseDensityValue, mappedBaseGravity);
|
||||
} else {
|
||||
if (needsDensityVariation) {
|
||||
forceValue.Add(densityVariationValue, mappedBaseGravity);
|
||||
}
|
||||
|
||||
if (needsGravityGradientVariation) {
|
||||
forceValue.Add(baseDensityValue, mappedGravityVariation);
|
||||
}
|
||||
|
||||
if (needsDisplacementVariation) {
|
||||
forceValue.Add(baseDensityValue, mappedGeometryVariation);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If g_ref is the RT pullback, then
|
||||
*
|
||||
* g_phys = J_map g_ref / det(J_map),
|
||||
* dV_phys = det(J_map) dV_ref.
|
||||
*
|
||||
* The determinant cancels exactly. Consequently the base
|
||||
* integrand uses J_map g_ref and its geometry derivative uses
|
||||
* delta(J_map) g_ref. This is algebraically identical to
|
||||
* differentiating the Piola map and physical volume weight,
|
||||
* but avoids a numerically pointless cancellation.
|
||||
*/
|
||||
const double referenceWeight = integrationPoint.weight * transformation->Weight();
|
||||
|
||||
forceValue *= referenceWeight;
|
||||
|
||||
for (int scalarDof = 0; scalarDof < scalarDisplacementDofCount; ++scalarDof) {
|
||||
for (int component = 0; component < dimension; ++component) {
|
||||
const int vectorDof = vector_dof_index(
|
||||
displacementOrdering, scalarDof, component, scalarDisplacementDofCount, dimension
|
||||
);
|
||||
|
||||
const double contribution = displacementShape(scalarDof) * forceValue(component);
|
||||
|
||||
MFEM_VERIFY(
|
||||
std::isfinite(contribution), "The gravity-displacement-force kernel "
|
||||
"encountered a non-finite contribution."
|
||||
);
|
||||
|
||||
elementAction(vectorDof) += contribution;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (displacementDofTransformation != nullptr) {
|
||||
displacementDofTransformation->TransformDual(elementAction);
|
||||
}
|
||||
|
||||
localAction.AddElementVector(displacementDofs, elementAction);
|
||||
}
|
||||
|
||||
local_to_true(*f.displacementFes, localAction, actionTrue);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace mean_field::operators::kernels {
|
||||
void apply_gravity_displacement_force_residual(
|
||||
const fem::FEM &f,
|
||||
const mapping::DomainMapperStateless &domainMapper,
|
||||
const mfem::Vector &densityTrue,
|
||||
const mfem::Vector &gravityGradientTrue,
|
||||
const mfem::Vector &displacementTrue,
|
||||
mfem::Vector &residualTrue
|
||||
) {
|
||||
apply_gravity_displacement_force_action(
|
||||
f, domainMapper, GravityDisplacementForceAction::residual, &densityTrue, nullptr, &gravityGradientTrue,
|
||||
nullptr, nullptr, displacementTrue, residualTrue
|
||||
);
|
||||
}
|
||||
|
||||
void apply_gravity_displacement_force_density_action(
|
||||
const fem::FEM &f,
|
||||
const mapping::DomainMapperStateless &domainMapper,
|
||||
const mfem::Vector &densityVariationTrue,
|
||||
const mfem::Vector &baseGravityGradientTrue,
|
||||
const mfem::Vector &displacementTrue,
|
||||
mfem::Vector &actionTrue
|
||||
) {
|
||||
apply_gravity_displacement_force_action(
|
||||
f, domainMapper, GravityDisplacementForceAction::density, nullptr, &densityVariationTrue,
|
||||
&baseGravityGradientTrue, nullptr, nullptr, displacementTrue, actionTrue
|
||||
);
|
||||
}
|
||||
|
||||
void apply_gravity_displacement_force_gradient_action(
|
||||
const fem::FEM &f,
|
||||
const mapping::DomainMapperStateless &domainMapper,
|
||||
const mfem::Vector &baseDensityTrue,
|
||||
const mfem::Vector &gravityGradientVariationTrue,
|
||||
const mfem::Vector &displacementTrue,
|
||||
mfem::Vector &actionTrue
|
||||
) {
|
||||
apply_gravity_displacement_force_action(
|
||||
f, domainMapper, GravityDisplacementForceAction::gravityGradient, &baseDensityTrue, nullptr, nullptr,
|
||||
&gravityGradientVariationTrue, nullptr, displacementTrue, actionTrue
|
||||
);
|
||||
}
|
||||
|
||||
void apply_gravity_displacement_force_displacement_action(
|
||||
const fem::FEM &f,
|
||||
const mapping::DomainMapperStateless &domainMapper,
|
||||
const mfem::Vector &baseDensityTrue,
|
||||
const mfem::Vector &baseGravityGradientTrue,
|
||||
const mfem::Vector &displacementVariationTrue,
|
||||
const mfem::Vector &displacementTrue,
|
||||
mfem::Vector &actionTrue
|
||||
) {
|
||||
apply_gravity_displacement_force_action(
|
||||
f, domainMapper, GravityDisplacementForceAction::displacement, &baseDensityTrue, nullptr,
|
||||
&baseGravityGradientTrue, nullptr, &displacementVariationTrue, displacementTrue, actionTrue
|
||||
);
|
||||
}
|
||||
|
||||
void apply_gravity_displacement_force_complete_action(
|
||||
const fem::FEM &f,
|
||||
const mapping::DomainMapperStateless &domainMapper,
|
||||
const mfem::Vector &baseDensityTrue,
|
||||
const mfem::Vector &densityVariationTrue,
|
||||
const mfem::Vector &baseGravityGradientTrue,
|
||||
const mfem::Vector &gravityGradientVariationTrue,
|
||||
const mfem::Vector &displacementVariationTrue,
|
||||
const mfem::Vector &displacementTrue,
|
||||
mfem::Vector &actionTrue
|
||||
) {
|
||||
apply_gravity_displacement_force_action(
|
||||
f, domainMapper, GravityDisplacementForceAction::complete, &baseDensityTrue, &densityVariationTrue,
|
||||
&baseGravityGradientTrue, &gravityGradientVariationTrue, &displacementVariationTrue, displacementTrue,
|
||||
actionTrue
|
||||
);
|
||||
}
|
||||
} // namespace mean_field::operators::kernels
|
||||
File diff suppressed because it is too large
Load Diff
@@ -16,15 +16,11 @@ namespace {
|
||||
const mfem::Vector &trueVector,
|
||||
mfem::Vector &localVector
|
||||
) {
|
||||
MFEM_VERIFY(
|
||||
trueVector.Size() == finiteElementSpace.GetTrueVSize(),
|
||||
"True vector has the wrong size."
|
||||
);
|
||||
MFEM_VERIFY(trueVector.Size() == finiteElementSpace.GetTrueVSize(), "True vector has the wrong size.");
|
||||
|
||||
localVector.SetSize(finiteElementSpace.GetVSize());
|
||||
|
||||
const mfem::Operator *prolongation =
|
||||
finiteElementSpace.GetProlongationMatrix();
|
||||
const mfem::Operator *prolongation = finiteElementSpace.GetProlongationMatrix();
|
||||
|
||||
if (prolongation != nullptr) {
|
||||
prolongation->Mult(trueVector, localVector);
|
||||
@@ -38,17 +34,13 @@ namespace {
|
||||
const mfem::Vector &localVector,
|
||||
mfem::Vector &trueVector
|
||||
) {
|
||||
MFEM_VERIFY(
|
||||
localVector.Size() == finiteElementSpace.GetVSize(),
|
||||
"Local vector has the wrong size."
|
||||
);
|
||||
MFEM_VERIFY(localVector.Size() == finiteElementSpace.GetVSize(), "Local vector has the wrong size.");
|
||||
|
||||
trueVector.SetSize(finiteElementSpace.GetTrueVSize());
|
||||
|
||||
trueVector = 0.0;
|
||||
trueVector = 0.0;
|
||||
|
||||
const mfem::Operator *prolongation =
|
||||
finiteElementSpace.GetProlongationMatrix();
|
||||
const mfem::Operator *prolongation = finiteElementSpace.GetProlongationMatrix();
|
||||
|
||||
if (prolongation != nullptr) {
|
||||
prolongation->MultTranspose(localVector, trueVector);
|
||||
@@ -61,9 +53,7 @@ namespace {
|
||||
const mean_field::fem::FEM &f,
|
||||
const mean_field::mapping::DomainMapperStateless &domainMapper
|
||||
) {
|
||||
MFEM_VERIFY(
|
||||
f.mesh != nullptr, "The hydrostatic kernel requires a mesh."
|
||||
);
|
||||
MFEM_VERIFY(f.mesh != nullptr, "The hydrostatic kernel requires a mesh.");
|
||||
|
||||
MFEM_VERIFY(
|
||||
f.enthalpyFes != nullptr, "The hydrostatic kernel requires the "
|
||||
@@ -71,9 +61,8 @@ namespace {
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
f.gravityPotentialFes != nullptr,
|
||||
"The hydrostatic kernel requires the "
|
||||
"gravity-potential finite-element space."
|
||||
f.gravityPotentialFes != nullptr, "The hydrostatic kernel requires the "
|
||||
"gravity-potential finite-element space."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
@@ -82,33 +71,28 @@ namespace {
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
f.compactificationFes != nullptr,
|
||||
"The hydrostatic kernel requires the "
|
||||
"compactification finite-element space."
|
||||
f.compactificationFes != nullptr, "The hydrostatic kernel requires the "
|
||||
"compactification finite-element space."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
f.compactificationCoordinate != nullptr,
|
||||
"The hydrostatic kernel requires the "
|
||||
"compactification coordinate."
|
||||
f.compactificationCoordinate != nullptr, "The hydrostatic kernel requires the "
|
||||
"compactification coordinate."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
f.quadratureFactory != nullptr,
|
||||
"The hydrostatic kernel requires the "
|
||||
"quadrature-rule factory."
|
||||
f.quadratureFactory != nullptr, "The hydrostatic kernel requires the "
|
||||
"quadrature-rule factory."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
f.mesh->Dimension() == 3,
|
||||
"The rigid-rotation hydrostatic kernel "
|
||||
"currently requires a three-dimensional mesh."
|
||||
f.mesh->Dimension() == 3, "The rigid-rotation hydrostatic kernel "
|
||||
"currently requires a three-dimensional mesh."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
domainMapper.GetDimension() == f.mesh->Dimension(),
|
||||
"The domain-mapper dimension does not match "
|
||||
"the mesh dimension."
|
||||
domainMapper.GetDimension() == f.mesh->Dimension(), "The domain-mapper dimension does not match "
|
||||
"the mesh dimension."
|
||||
);
|
||||
}
|
||||
|
||||
@@ -118,69 +102,51 @@ namespace {
|
||||
const mfem::FiniteElement &potentialElement,
|
||||
const mfem::ElementTransformation &transformation
|
||||
) {
|
||||
using EnthalpyField =
|
||||
mean_field::field::Field<mean_field::field::Enthalpy>;
|
||||
using EnthalpyField = mean_field::field::Field<mean_field::field::Enthalpy>;
|
||||
|
||||
MFEM_VERIFY(
|
||||
enthalpyElement.GetOrder() ==
|
||||
mean_field::field::Enthalpy::Scalar::familyOrder,
|
||||
enthalpyElement.GetOrder() == mean_field::field::Enthalpy::Scalar::familyOrder,
|
||||
"The hydrostatic test element does not match "
|
||||
"the registered enthalpy field."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
potentialElement.GetOrder() ==
|
||||
mean_field::field::Gravity::Potential::familyOrder,
|
||||
potentialElement.GetOrder() == mean_field::field::Gravity::Potential::familyOrder,
|
||||
"The hydrostatic potential element does not "
|
||||
"match the registered gravity-potential field."
|
||||
);
|
||||
|
||||
const auto enthalpyQuery = EnthalpyField::make_query<
|
||||
mean_field::field::Enthalpy::Form::EquilibriumEnthalpy>(
|
||||
mean_field::quadrature::QuadratureRole::discretization,
|
||||
transformation.OrderW(), {}, mean_field::utils::DOMAINS::STELLAR,
|
||||
mean_field::quadrature::MappingKind::general
|
||||
const auto enthalpyQuery = EnthalpyField::make_query<mean_field::field::Enthalpy::Form::EquilibriumEnthalpy>(
|
||||
mean_field::quadrature::QuadratureRole::discretization, transformation.OrderW(), {},
|
||||
mean_field::utils::DOMAINS::STELLAR, mean_field::quadrature::MappingKind::general
|
||||
);
|
||||
|
||||
const auto gravityQuery = EnthalpyField::make_query<
|
||||
mean_field::field::Enthalpy::Form::EquilibriumGravity>(
|
||||
mean_field::quadrature::QuadratureRole::discretization,
|
||||
transformation.OrderW(), {}, mean_field::utils::DOMAINS::STELLAR,
|
||||
mean_field::quadrature::MappingKind::general
|
||||
const auto gravityQuery = EnthalpyField::make_query<mean_field::field::Enthalpy::Form::EquilibriumGravity>(
|
||||
mean_field::quadrature::QuadratureRole::discretization, transformation.OrderW(), {},
|
||||
mean_field::utils::DOMAINS::STELLAR, mean_field::quadrature::MappingKind::general
|
||||
);
|
||||
|
||||
const auto rotationQuery = EnthalpyField::make_query<
|
||||
mean_field::field::Enthalpy::Form::EquilibriumRotation>(
|
||||
mean_field::quadrature::QuadratureRole::discretization,
|
||||
transformation.OrderW(), std::array<int, 1>{2},
|
||||
mean_field::utils::DOMAINS::STELLAR,
|
||||
mean_field::quadrature::MappingKind::general
|
||||
const auto rotationQuery = EnthalpyField::make_query<mean_field::field::Enthalpy::Form::EquilibriumRotation>(
|
||||
mean_field::quadrature::QuadratureRole::discretization, transformation.OrderW(), std::array<int, 1>{2},
|
||||
mean_field::utils::DOMAINS::STELLAR, mean_field::quadrature::MappingKind::general
|
||||
);
|
||||
|
||||
const auto constantQuery = EnthalpyField::make_query<
|
||||
mean_field::field::Enthalpy::Form::EquilibriumConstant>(
|
||||
mean_field::quadrature::QuadratureRole::discretization,
|
||||
transformation.OrderW(), {}, mean_field::utils::DOMAINS::STELLAR,
|
||||
mean_field::quadrature::MappingKind::general
|
||||
const auto constantQuery = EnthalpyField::make_query<mean_field::field::Enthalpy::Form::EquilibriumConstant>(
|
||||
mean_field::quadrature::QuadratureRole::discretization, transformation.OrderW(), {},
|
||||
mean_field::utils::DOMAINS::STELLAR, mean_field::quadrature::MappingKind::general
|
||||
);
|
||||
|
||||
int integrationOrder = 0;
|
||||
|
||||
const auto update_order = [&f, &transformation, &integrationOrder](
|
||||
const mean_field::quadrature::Query &query
|
||||
) {
|
||||
const auto rule = f.quadratureFactory->get(
|
||||
query, transformation.GetGeometryType()
|
||||
);
|
||||
const auto update_order = [&f, &transformation, &integrationOrder](const mean_field::quadrature::Query &query) {
|
||||
const auto rule = f.quadratureFactory->get(query, transformation.GetGeometryType());
|
||||
|
||||
MFEM_VERIFY(
|
||||
rule.integration_rule != nullptr,
|
||||
"The quadrature policy did not return "
|
||||
"a hydrostatic-equilibrium rule."
|
||||
rule.integration_rule != nullptr, "The quadrature policy did not return "
|
||||
"a hydrostatic-equilibrium rule."
|
||||
);
|
||||
|
||||
integrationOrder =
|
||||
std::max(integrationOrder, rule.resolution.order);
|
||||
integrationOrder = std::max(integrationOrder, rule.resolution.order);
|
||||
};
|
||||
|
||||
update_order(enthalpyQuery);
|
||||
@@ -188,9 +154,7 @@ namespace {
|
||||
update_order(rotationQuery);
|
||||
update_order(constantQuery);
|
||||
|
||||
return mfem::IntRules.Get(
|
||||
transformation.GetGeometryType(), integrationOrder
|
||||
);
|
||||
return mfem::IntRules.Get(transformation.GetGeometryType(), integrationOrder);
|
||||
}
|
||||
|
||||
struct HydrostaticAssemblyRequest {
|
||||
@@ -219,81 +183,64 @@ namespace {
|
||||
validate_fem(f, domainMapper);
|
||||
|
||||
MFEM_VERIFY(
|
||||
displacementTrue.Size() == f.displacementFes->GetTrueVSize(),
|
||||
"The hydrostatic displacement vector has "
|
||||
"the wrong size."
|
||||
displacementTrue.Size() == f.displacementFes->GetTrueVSize(), "The hydrostatic displacement vector has "
|
||||
"the wrong size."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
std::isfinite(request.bernoulliConstant),
|
||||
"The Bernoulli constant is non-finite."
|
||||
);
|
||||
MFEM_VERIFY(std::isfinite(request.bernoulliConstant), "The Bernoulli constant is non-finite.");
|
||||
|
||||
MFEM_VERIFY(
|
||||
std::isfinite(request.constantVariation),
|
||||
"The Bernoulli-constant variation is non-finite."
|
||||
);
|
||||
MFEM_VERIFY(std::isfinite(request.constantVariation), "The Bernoulli-constant variation is non-finite.");
|
||||
|
||||
const bool requiresBaseState =
|
||||
request.buildResidual ||
|
||||
request.displacementVariationTrue != nullptr;
|
||||
const bool requiresBaseState = request.buildResidual || request.displacementVariationTrue != nullptr;
|
||||
|
||||
if (requiresBaseState) {
|
||||
MFEM_VERIFY(
|
||||
request.rotation != nullptr,
|
||||
"The hydrostatic residual or geometry "
|
||||
"action requires the rotation model."
|
||||
request.rotation != nullptr, "The hydrostatic residual or geometry "
|
||||
"action requires the rotation model."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
request.baseEnthalpyTrue != nullptr,
|
||||
"The hydrostatic residual or geometry "
|
||||
"action requires the base enthalpy."
|
||||
request.baseEnthalpyTrue != nullptr, "The hydrostatic residual or geometry "
|
||||
"action requires the base enthalpy."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
request.basePotentialTrue != nullptr,
|
||||
"The hydrostatic residual or geometry "
|
||||
"action requires the base potential."
|
||||
request.basePotentialTrue != nullptr, "The hydrostatic residual or geometry "
|
||||
"action requires the base potential."
|
||||
);
|
||||
}
|
||||
|
||||
if (request.baseEnthalpyTrue != nullptr) {
|
||||
MFEM_VERIFY(
|
||||
request.baseEnthalpyTrue->Size() ==
|
||||
f.enthalpyFes->GetTrueVSize(),
|
||||
request.baseEnthalpyTrue->Size() == f.enthalpyFes->GetTrueVSize(),
|
||||
"The base enthalpy vector has the wrong size."
|
||||
);
|
||||
}
|
||||
|
||||
if (request.basePotentialTrue != nullptr) {
|
||||
MFEM_VERIFY(
|
||||
request.basePotentialTrue->Size() ==
|
||||
f.gravityPotentialFes->GetTrueVSize(),
|
||||
request.basePotentialTrue->Size() == f.gravityPotentialFes->GetTrueVSize(),
|
||||
"The base potential vector has the wrong size."
|
||||
);
|
||||
}
|
||||
|
||||
if (request.enthalpyVariationTrue != nullptr) {
|
||||
MFEM_VERIFY(
|
||||
request.enthalpyVariationTrue->Size() ==
|
||||
f.enthalpyFes->GetTrueVSize(),
|
||||
request.enthalpyVariationTrue->Size() == f.enthalpyFes->GetTrueVSize(),
|
||||
"The enthalpy variation has the wrong size."
|
||||
);
|
||||
}
|
||||
|
||||
if (request.potentialVariationTrue != nullptr) {
|
||||
MFEM_VERIFY(
|
||||
request.potentialVariationTrue->Size() ==
|
||||
f.gravityPotentialFes->GetTrueVSize(),
|
||||
request.potentialVariationTrue->Size() == f.gravityPotentialFes->GetTrueVSize(),
|
||||
"The potential variation has the wrong size."
|
||||
);
|
||||
}
|
||||
|
||||
if (request.displacementVariationTrue != nullptr) {
|
||||
MFEM_VERIFY(
|
||||
request.displacementVariationTrue->Size() ==
|
||||
f.displacementFes->GetTrueVSize(),
|
||||
request.displacementVariationTrue->Size() == f.displacementFes->GetTrueVSize(),
|
||||
"The displacement variation has the wrong size."
|
||||
);
|
||||
}
|
||||
@@ -308,46 +255,30 @@ namespace {
|
||||
mfem::Vector displacementVariationLocal;
|
||||
|
||||
if (request.baseEnthalpyTrue != nullptr) {
|
||||
true_to_local(
|
||||
*f.enthalpyFes, *request.baseEnthalpyTrue, baseEnthalpyLocal
|
||||
);
|
||||
true_to_local(*f.enthalpyFes, *request.baseEnthalpyTrue, baseEnthalpyLocal);
|
||||
}
|
||||
|
||||
if (request.basePotentialTrue != nullptr) {
|
||||
true_to_local(
|
||||
*f.gravityPotentialFes, *request.basePotentialTrue,
|
||||
basePotentialLocal
|
||||
);
|
||||
true_to_local(*f.gravityPotentialFes, *request.basePotentialTrue, basePotentialLocal);
|
||||
}
|
||||
|
||||
if (request.enthalpyVariationTrue != nullptr) {
|
||||
true_to_local(
|
||||
*f.enthalpyFes, *request.enthalpyVariationTrue,
|
||||
enthalpyVariationLocal
|
||||
);
|
||||
true_to_local(*f.enthalpyFes, *request.enthalpyVariationTrue, enthalpyVariationLocal);
|
||||
}
|
||||
|
||||
if (request.potentialVariationTrue != nullptr) {
|
||||
true_to_local(
|
||||
*f.gravityPotentialFes, *request.potentialVariationTrue,
|
||||
potentialVariationLocal
|
||||
);
|
||||
true_to_local(*f.gravityPotentialFes, *request.potentialVariationTrue, potentialVariationLocal);
|
||||
}
|
||||
|
||||
if (request.displacementVariationTrue != nullptr) {
|
||||
true_to_local(
|
||||
*f.displacementFes, *request.displacementVariationTrue,
|
||||
displacementVariationLocal
|
||||
);
|
||||
true_to_local(*f.displacementFes, *request.displacementVariationTrue, displacementVariationLocal);
|
||||
}
|
||||
|
||||
mfem::Vector localResult(f.enthalpyFes->GetVSize());
|
||||
|
||||
localResult = 0.0;
|
||||
|
||||
mean_field::mapping::DomainMapperStateless::Workspace workspace(
|
||||
f.mesh->Dimension()
|
||||
);
|
||||
mean_field::mapping::DomainMapperStateless::Workspace workspace(f.mesh->Dimension());
|
||||
|
||||
mfem::Array<int> enthalpyDofs;
|
||||
mfem::Array<int> potentialDofs;
|
||||
@@ -369,33 +300,26 @@ namespace {
|
||||
const int vacuumAttribute = domainMapper.GetVacuumElementAttribute();
|
||||
|
||||
for (int elementId = 0; elementId < f.mesh->GetNE(); ++elementId) {
|
||||
mfem::ElementTransformation *transformation =
|
||||
f.mesh->GetElementTransformation(elementId);
|
||||
mfem::ElementTransformation *transformation = f.mesh->GetElementTransformation(elementId);
|
||||
|
||||
MFEM_VERIFY(
|
||||
transformation != nullptr,
|
||||
"The hydrostatic kernel received a null "
|
||||
"element transformation."
|
||||
transformation != nullptr, "The hydrostatic kernel received a null "
|
||||
"element transformation."
|
||||
);
|
||||
|
||||
if (transformation->Attribute == vacuumAttribute) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const mfem::FiniteElement &enthalpyElement =
|
||||
*f.enthalpyFes->GetFE(elementId);
|
||||
const mfem::FiniteElement &enthalpyElement = *f.enthalpyFes->GetFE(elementId);
|
||||
|
||||
const mfem::FiniteElement &potentialElement =
|
||||
*f.gravityPotentialFes->GetFE(elementId);
|
||||
const mfem::FiniteElement &potentialElement = *f.gravityPotentialFes->GetFE(elementId);
|
||||
|
||||
const mfem::FiniteElement &displacementElement =
|
||||
*f.displacementFes->GetFE(elementId);
|
||||
const mfem::FiniteElement &displacementElement = *f.displacementFes->GetFE(elementId);
|
||||
|
||||
const mfem::FiniteElement &compactificationElement =
|
||||
*f.compactificationFes->GetFE(elementId);
|
||||
const mfem::FiniteElement &compactificationElement = *f.compactificationFes->GetFE(elementId);
|
||||
|
||||
mfem::DofTransformation *enthalpyDofTransformation =
|
||||
f.enthalpyFes->GetElementDofs(elementId, enthalpyDofs);
|
||||
mfem::DofTransformation *enthalpyDofTransformation = f.enthalpyFes->GetElementDofs(elementId, enthalpyDofs);
|
||||
|
||||
mfem::DofTransformation *potentialDofTransformation =
|
||||
f.gravityPotentialFes->GetElementDofs(elementId, potentialDofs);
|
||||
@@ -404,119 +328,82 @@ namespace {
|
||||
f.displacementFes->GetElementVDofs(elementId, displacementDofs);
|
||||
|
||||
mfem::DofTransformation *compactificationDofTransformation =
|
||||
f.compactificationFes->GetElementDofs(
|
||||
elementId, compactificationDofs
|
||||
);
|
||||
f.compactificationFes->GetElementDofs(elementId, compactificationDofs);
|
||||
|
||||
displacementLocal.GetSubVector(
|
||||
displacementDofs, elementDisplacement
|
||||
);
|
||||
displacementLocal.GetSubVector(displacementDofs, elementDisplacement);
|
||||
|
||||
f.compactificationCoordinate->GetSubVector(
|
||||
compactificationDofs, elementCompactification
|
||||
);
|
||||
f.compactificationCoordinate->GetSubVector(compactificationDofs, elementCompactification);
|
||||
|
||||
if (request.baseEnthalpyTrue != nullptr) {
|
||||
baseEnthalpyLocal.GetSubVector(
|
||||
enthalpyDofs, elementBaseEnthalpy
|
||||
);
|
||||
baseEnthalpyLocal.GetSubVector(enthalpyDofs, elementBaseEnthalpy);
|
||||
}
|
||||
|
||||
if (request.basePotentialTrue != nullptr) {
|
||||
basePotentialLocal.GetSubVector(
|
||||
potentialDofs, elementBasePotential
|
||||
);
|
||||
basePotentialLocal.GetSubVector(potentialDofs, elementBasePotential);
|
||||
}
|
||||
|
||||
if (request.enthalpyVariationTrue != nullptr) {
|
||||
enthalpyVariationLocal.GetSubVector(
|
||||
enthalpyDofs, elementEnthalpyVariation
|
||||
);
|
||||
enthalpyVariationLocal.GetSubVector(enthalpyDofs, elementEnthalpyVariation);
|
||||
}
|
||||
|
||||
if (request.potentialVariationTrue != nullptr) {
|
||||
potentialVariationLocal.GetSubVector(
|
||||
potentialDofs, elementPotentialVariation
|
||||
);
|
||||
potentialVariationLocal.GetSubVector(potentialDofs, elementPotentialVariation);
|
||||
}
|
||||
|
||||
if (request.displacementVariationTrue != nullptr) {
|
||||
displacementVariationLocal.GetSubVector(
|
||||
displacementDofs, elementDisplacementVariation
|
||||
);
|
||||
displacementVariationLocal.GetSubVector(displacementDofs, elementDisplacementVariation);
|
||||
}
|
||||
|
||||
if (enthalpyDofTransformation != nullptr) {
|
||||
if (request.baseEnthalpyTrue != nullptr) {
|
||||
enthalpyDofTransformation->InvTransformPrimal(
|
||||
elementBaseEnthalpy
|
||||
);
|
||||
enthalpyDofTransformation->InvTransformPrimal(elementBaseEnthalpy);
|
||||
}
|
||||
|
||||
if (request.enthalpyVariationTrue != nullptr) {
|
||||
enthalpyDofTransformation->InvTransformPrimal(
|
||||
elementEnthalpyVariation
|
||||
);
|
||||
enthalpyDofTransformation->InvTransformPrimal(elementEnthalpyVariation);
|
||||
}
|
||||
}
|
||||
|
||||
if (potentialDofTransformation != nullptr) {
|
||||
if (request.basePotentialTrue != nullptr) {
|
||||
potentialDofTransformation->InvTransformPrimal(
|
||||
elementBasePotential
|
||||
);
|
||||
potentialDofTransformation->InvTransformPrimal(elementBasePotential);
|
||||
}
|
||||
|
||||
if (request.potentialVariationTrue != nullptr) {
|
||||
potentialDofTransformation->InvTransformPrimal(
|
||||
elementPotentialVariation
|
||||
);
|
||||
potentialDofTransformation->InvTransformPrimal(elementPotentialVariation);
|
||||
}
|
||||
}
|
||||
|
||||
if (displacementDofTransformation != nullptr) {
|
||||
displacementDofTransformation->InvTransformPrimal(
|
||||
elementDisplacement
|
||||
);
|
||||
displacementDofTransformation->InvTransformPrimal(elementDisplacement);
|
||||
|
||||
if (request.displacementVariationTrue != nullptr) {
|
||||
displacementDofTransformation->InvTransformPrimal(
|
||||
elementDisplacementVariation
|
||||
);
|
||||
displacementDofTransformation->InvTransformPrimal(elementDisplacementVariation);
|
||||
}
|
||||
}
|
||||
|
||||
if (compactificationDofTransformation != nullptr) {
|
||||
compactificationDofTransformation->InvTransformPrimal(
|
||||
elementCompactification
|
||||
);
|
||||
compactificationDofTransformation->InvTransformPrimal(elementCompactification);
|
||||
}
|
||||
|
||||
const mean_field::mapping::ElementDisplacementData
|
||||
displacementData = mean_field::mapping::
|
||||
ElementDisplacementDataFromElementVDofs(
|
||||
displacementElement, elementDisplacement
|
||||
);
|
||||
const mean_field::mapping::ElementDisplacementData displacementData =
|
||||
mean_field::mapping::ElementDisplacementDataFromElementVDofs(displacementElement, elementDisplacement);
|
||||
|
||||
const mean_field::mapping::ElementCompactificationData
|
||||
compactificationData(
|
||||
compactificationElement, elementCompactification
|
||||
);
|
||||
const mean_field::mapping::ElementCompactificationData compactificationData(
|
||||
compactificationElement, elementCompactification
|
||||
);
|
||||
|
||||
const mean_field::mapping::ElementMappingData mappingData{
|
||||
.displacement = displacementData,
|
||||
.compactification = compactificationData
|
||||
.displacement = displacementData, .compactification = compactificationData
|
||||
};
|
||||
|
||||
std::optional<mean_field::mapping::ElementDisplacementData>
|
||||
displacementVariationData;
|
||||
std::optional<mean_field::mapping::ElementDisplacementData> displacementVariationData;
|
||||
|
||||
if (request.displacementVariationTrue != nullptr) {
|
||||
displacementVariationData.emplace(
|
||||
mean_field::mapping::
|
||||
ElementDisplacementDataFromElementVDofs(
|
||||
displacementElement, elementDisplacementVariation
|
||||
)
|
||||
mean_field::mapping::ElementDisplacementDataFromElementVDofs(
|
||||
displacementElement, elementDisplacementVariation
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -528,32 +415,25 @@ namespace {
|
||||
|
||||
potentialShape.SetSize(potentialElement.GetDof());
|
||||
|
||||
const mfem::IntegrationRule &integrationRule = get_hydrostatic_rule(
|
||||
f, enthalpyElement, potentialElement, *transformation
|
||||
);
|
||||
const mfem::IntegrationRule &integrationRule =
|
||||
get_hydrostatic_rule(f, enthalpyElement, potentialElement, *transformation);
|
||||
|
||||
for (int quadraturePoint = 0;
|
||||
quadraturePoint < integrationRule.GetNPoints();
|
||||
++quadraturePoint) {
|
||||
const mfem::IntegrationPoint &integrationPoint =
|
||||
integrationRule.IntPoint(quadraturePoint);
|
||||
for (int quadraturePoint = 0; quadraturePoint < integrationRule.GetNPoints(); ++quadraturePoint) {
|
||||
const mfem::IntegrationPoint &integrationPoint = integrationRule.IntPoint(quadraturePoint);
|
||||
|
||||
transformation->SetIntPoint(&integrationPoint);
|
||||
|
||||
mean_field::mapping::VolumeMappingContext mappingContext;
|
||||
|
||||
const mean_field::mapping::MappingStatus mappingStatus =
|
||||
domainMapper.EvaluateVolume(
|
||||
mappingData, *transformation, integrationPoint,
|
||||
workspace, mappingContext
|
||||
);
|
||||
const mean_field::mapping::MappingStatus mappingStatus = domainMapper.EvaluateVolume(
|
||||
mappingData, *transformation, integrationPoint, workspace, mappingContext
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
mappingStatus == mean_field::mapping::MappingStatus::valid,
|
||||
"The base mapping is invalid in the "
|
||||
"hydrostatic kernel. Element: "
|
||||
<< elementId
|
||||
<< ", quadrature point: " << quadraturePoint
|
||||
<< elementId << ", quadrature point: " << quadraturePoint
|
||||
<< ", status: " << static_cast<int>(mappingStatus)
|
||||
);
|
||||
|
||||
@@ -564,27 +444,18 @@ namespace {
|
||||
double baseIntegrand = 0.0;
|
||||
|
||||
if (requiresBaseState) {
|
||||
const double enthalpyValue =
|
||||
elementBaseEnthalpy * enthalpyShape;
|
||||
const double enthalpyValue = elementBaseEnthalpy * enthalpyShape;
|
||||
|
||||
const double potentialValue =
|
||||
elementBasePotential * potentialShape;
|
||||
const double potentialValue = elementBasePotential * potentialShape;
|
||||
|
||||
const double rotationPotential =
|
||||
request.rotation->potential(
|
||||
mappingContext.mapping.physical_position
|
||||
);
|
||||
request.rotation->potential(mappingContext.mapping.physical_position);
|
||||
|
||||
baseIntegrand = enthalpyValue + potentialValue -
|
||||
rotationPotential -
|
||||
request.bernoulliConstant;
|
||||
baseIntegrand = enthalpyValue + potentialValue - rotationPotential - request.bernoulliConstant;
|
||||
}
|
||||
|
||||
if (request.buildResidual) {
|
||||
elementResult.Add(
|
||||
mappingContext.quadrature.weight * baseIntegrand,
|
||||
enthalpyShape
|
||||
);
|
||||
elementResult.Add(mappingContext.quadrature.weight * baseIntegrand, enthalpyShape);
|
||||
|
||||
continue;
|
||||
}
|
||||
@@ -592,45 +463,35 @@ namespace {
|
||||
double materialVariation = -request.constantVariation;
|
||||
|
||||
if (request.enthalpyVariationTrue != nullptr) {
|
||||
materialVariation +=
|
||||
elementEnthalpyVariation * enthalpyShape;
|
||||
materialVariation += elementEnthalpyVariation * enthalpyShape;
|
||||
}
|
||||
|
||||
if (request.potentialVariationTrue != nullptr) {
|
||||
materialVariation +=
|
||||
elementPotentialVariation * potentialShape;
|
||||
materialVariation += elementPotentialVariation * potentialShape;
|
||||
}
|
||||
|
||||
double weightedVariation =
|
||||
mappingContext.quadrature.weight * materialVariation;
|
||||
double weightedVariation = mappingContext.quadrature.weight * materialVariation;
|
||||
|
||||
if (request.displacementVariationTrue != nullptr) {
|
||||
mean_field::mapping::VolumeMappingVariation
|
||||
mappingVariation;
|
||||
mean_field::mapping::VolumeMappingVariation mappingVariation;
|
||||
|
||||
const mean_field::mapping::MappingStatus variationStatus =
|
||||
domainMapper.EvaluateVolumeVariation(
|
||||
mappingData, *displacementVariationData,
|
||||
*transformation, integrationPoint, mappingContext,
|
||||
workspace, mappingVariation
|
||||
);
|
||||
const mean_field::mapping::MappingStatus variationStatus = domainMapper.EvaluateVolumeVariation(
|
||||
mappingData, *displacementVariationData, *transformation, integrationPoint, mappingContext,
|
||||
workspace, mappingVariation
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
variationStatus ==
|
||||
mean_field::mapping::MappingStatus::valid,
|
||||
variationStatus == mean_field::mapping::MappingStatus::valid,
|
||||
"The mapping variation is invalid "
|
||||
"in the hydrostatic kernel."
|
||||
);
|
||||
|
||||
const double rotationVariation =
|
||||
request.rotation->potential_directional_derivative(
|
||||
mappingContext.mapping.physical_position,
|
||||
mappingVariation.mapping.physical_position_variation
|
||||
);
|
||||
const double rotationVariation = request.rotation->potential_directional_derivative(
|
||||
mappingContext.mapping.physical_position, mappingVariation.mapping.physical_position_variation
|
||||
);
|
||||
|
||||
weightedVariation +=
|
||||
baseIntegrand * mappingVariation.weight_variation -
|
||||
rotationVariation * mappingContext.quadrature.weight;
|
||||
weightedVariation += baseIntegrand * mappingVariation.weight_variation -
|
||||
rotationVariation * mappingContext.quadrature.weight;
|
||||
}
|
||||
|
||||
elementResult.Add(weightedVariation, enthalpyShape);
|
||||
@@ -666,9 +527,7 @@ namespace mean_field::operators::kernels {
|
||||
request.bernoulliConstant = bernoulliConstant;
|
||||
request.buildResidual = true;
|
||||
|
||||
assemble_hydrostatic_form(
|
||||
f, domainMapper, displacementTrue, request, residual
|
||||
);
|
||||
assemble_hydrostatic_form(f, domainMapper, displacementTrue, request, residual);
|
||||
}
|
||||
|
||||
void apply_hydrostatic_equilibrium_enthalpy_action(
|
||||
@@ -682,9 +541,7 @@ namespace mean_field::operators::kernels {
|
||||
|
||||
request.enthalpyVariationTrue = &enthalpyVariationTrue;
|
||||
|
||||
assemble_hydrostatic_form(
|
||||
f, domainMapper, displacementTrue, request, action
|
||||
);
|
||||
assemble_hydrostatic_form(f, domainMapper, displacementTrue, request, action);
|
||||
}
|
||||
|
||||
void apply_hydrostatic_equilibrium_potential_action(
|
||||
@@ -698,9 +555,7 @@ namespace mean_field::operators::kernels {
|
||||
|
||||
request.potentialVariationTrue = &potentialVariationTrue;
|
||||
|
||||
assemble_hydrostatic_form(
|
||||
f, domainMapper, displacementTrue, request, action
|
||||
);
|
||||
assemble_hydrostatic_form(f, domainMapper, displacementTrue, request, action);
|
||||
}
|
||||
|
||||
void apply_hydrostatic_equilibrium_constant_action(
|
||||
@@ -714,9 +569,7 @@ namespace mean_field::operators::kernels {
|
||||
|
||||
request.constantVariation = constantVariation;
|
||||
|
||||
assemble_hydrostatic_form(
|
||||
f, domainMapper, displacementTrue, request, action
|
||||
);
|
||||
assemble_hydrostatic_form(f, domainMapper, displacementTrue, request, action);
|
||||
}
|
||||
|
||||
void apply_hydrostatic_equilibrium_displacement_action(
|
||||
@@ -738,9 +591,7 @@ namespace mean_field::operators::kernels {
|
||||
request.displacementVariationTrue = &displacementVariationTrue;
|
||||
request.bernoulliConstant = baseBernoulliConstant;
|
||||
|
||||
assemble_hydrostatic_form(
|
||||
f, domainMapper, baseDisplacementTrue, request, action
|
||||
);
|
||||
assemble_hydrostatic_form(f, domainMapper, baseDisplacementTrue, request, action);
|
||||
}
|
||||
|
||||
void apply_hydrostatic_equilibrium_action(
|
||||
@@ -768,8 +619,6 @@ namespace mean_field::operators::kernels {
|
||||
request.bernoulliConstant = baseBernoulliConstant;
|
||||
request.constantVariation = constantVariation;
|
||||
|
||||
assemble_hydrostatic_form(
|
||||
f, domainMapper, baseDisplacementTrue, request, action
|
||||
);
|
||||
assemble_hydrostatic_form(f, domainMapper, baseDisplacementTrue, request, action);
|
||||
}
|
||||
} // namespace mean_field::operators::kernels
|
||||
@@ -3,6 +3,7 @@ module;
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
#include <optional>
|
||||
|
||||
#include <mfem.hpp>
|
||||
|
||||
@@ -11,20 +12,20 @@ module mean_field;
|
||||
import :operators.kernels.pressure_force;
|
||||
|
||||
namespace {
|
||||
enum class PressureForceAction { residual, enthalpy, displacement };
|
||||
|
||||
void true_to_local(
|
||||
const mfem::ParFiniteElementSpace &finiteElementSpace,
|
||||
const mfem::Vector &trueVector,
|
||||
mfem::Vector &localVector
|
||||
) {
|
||||
MFEM_VERIFY(
|
||||
trueVector.Size() == finiteElementSpace.GetTrueVSize(),
|
||||
"The pressure-force true vector has the wrong size."
|
||||
trueVector.Size() == finiteElementSpace.GetTrueVSize(), "The pressure-force true vector has the wrong size."
|
||||
);
|
||||
|
||||
localVector.SetSize(finiteElementSpace.GetVSize());
|
||||
|
||||
const mfem::Operator *prolongation =
|
||||
finiteElementSpace.GetProlongationMatrix();
|
||||
const mfem::Operator *prolongation = finiteElementSpace.GetProlongationMatrix();
|
||||
|
||||
if (prolongation != nullptr) {
|
||||
prolongation->Mult(trueVector, localVector);
|
||||
@@ -39,15 +40,13 @@ namespace {
|
||||
mfem::Vector &trueVector
|
||||
) {
|
||||
MFEM_VERIFY(
|
||||
localVector.Size() == finiteElementSpace.GetVSize(),
|
||||
"The pressure-force local vector has the wrong size."
|
||||
localVector.Size() == finiteElementSpace.GetVSize(), "The pressure-force local vector has the wrong size."
|
||||
);
|
||||
|
||||
trueVector.SetSize(finiteElementSpace.GetTrueVSize());
|
||||
trueVector = 0.0;
|
||||
trueVector = 0.0;
|
||||
|
||||
const mfem::Operator *prolongation =
|
||||
finiteElementSpace.GetProlongationMatrix();
|
||||
const mfem::Operator *prolongation = finiteElementSpace.GetProlongationMatrix();
|
||||
|
||||
if (prolongation != nullptr) {
|
||||
prolongation->MultTranspose(localVector, trueVector);
|
||||
@@ -68,15 +67,14 @@ namespace {
|
||||
}
|
||||
|
||||
if (ordering == mfem::Ordering::byVDIM) {
|
||||
return component + scalarDof * dimension;
|
||||
return scalarDof * dimension + component;
|
||||
}
|
||||
|
||||
MFEM_ABORT("The displacement space uses an unsupported ordering.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
[[nodiscard]] int get_pressure_extra_order(
|
||||
const mean_field::physics::PolytropicBarotrope &barotrope
|
||||
) {
|
||||
[[nodiscard]] int get_pressure_extra_order(const mean_field::eos::Polytrope &barotrope) {
|
||||
/*
|
||||
* Pressure has the enthalpy dependence
|
||||
*
|
||||
@@ -87,15 +85,11 @@ namespace {
|
||||
* contribution is therefore n times that order.
|
||||
*/
|
||||
const double extraOrder =
|
||||
barotrope.polytropic_index() *
|
||||
static_cast<double>(
|
||||
mean_field::field::Enthalpy::Scalar::familyOrder
|
||||
);
|
||||
barotrope.polytropic_index() * static_cast<double>(mean_field::field::Enthalpy::Scalar::familyOrder);
|
||||
|
||||
MFEM_VERIFY(
|
||||
std::isfinite(extraOrder) && extraOrder >= 0.0 &&
|
||||
extraOrder <=
|
||||
static_cast<double>(std::numeric_limits<int>::max()),
|
||||
extraOrder <= static_cast<double>(std::numeric_limits<int>::max()),
|
||||
"The pressure EOS effective polynomial order is invalid."
|
||||
);
|
||||
|
||||
@@ -104,44 +98,37 @@ namespace {
|
||||
|
||||
[[nodiscard]] const mfem::IntegrationRule &get_pressure_force_rule(
|
||||
const mean_field::fem::FEM &f,
|
||||
const mean_field::physics::PolytropicBarotrope &barotrope,
|
||||
const mean_field::eos::Polytrope &barotrope,
|
||||
const mfem::FiniteElement &enthalpyElement,
|
||||
const mfem::FiniteElement &displacementElement,
|
||||
const mfem::ElementTransformation &transformation
|
||||
) {
|
||||
using EnthalpyField =
|
||||
mean_field::field::Field<mean_field::field::Enthalpy>;
|
||||
using EnthalpyField = mean_field::field::Field<mean_field::field::Enthalpy>;
|
||||
|
||||
MFEM_VERIFY(
|
||||
enthalpyElement.GetOrder() ==
|
||||
mean_field::field::Enthalpy::Scalar::familyOrder,
|
||||
enthalpyElement.GetOrder() == mean_field::field::Enthalpy::Scalar::familyOrder,
|
||||
"The pressure-force enthalpy element does not match the "
|
||||
"registered enthalpy field."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
displacementElement.GetOrder() ==
|
||||
mean_field::field::Displacement::Vector::familyOrder,
|
||||
displacementElement.GetOrder() == mean_field::field::Displacement::Vector::familyOrder,
|
||||
"The pressure-force test element does not match the "
|
||||
"registered displacement field."
|
||||
);
|
||||
|
||||
const mean_field::quadrature::Query query = EnthalpyField::make_query<
|
||||
mean_field::field::Enthalpy::Form::PressureForce>(
|
||||
mean_field::quadrature::QuadratureRole::discretization,
|
||||
transformation.OrderW(),
|
||||
std::array<int, 1>{get_pressure_extra_order(barotrope)},
|
||||
mean_field::utils::DOMAINS::STELLAR,
|
||||
mean_field::quadrature::MappingKind::general
|
||||
);
|
||||
const mean_field::quadrature::Query query =
|
||||
EnthalpyField::make_query<mean_field::field::Enthalpy::Form::PressureForce>(
|
||||
mean_field::quadrature::QuadratureRole::discretization, transformation.OrderW(),
|
||||
std::array<int, 1>{get_pressure_extra_order(barotrope)}, mean_field::utils::DOMAINS::STELLAR,
|
||||
mean_field::quadrature::MappingKind::general
|
||||
);
|
||||
|
||||
const mean_field::quadrature::MfemRule rule =
|
||||
f.quadratureFactory->get(query, transformation.GetGeometryType());
|
||||
const mean_field::quadrature::MfemRule rule = f.quadratureFactory->get(query, transformation.GetGeometryType());
|
||||
|
||||
MFEM_VERIFY(
|
||||
rule.integration_rule != nullptr,
|
||||
"The quadrature policy did not return a pressure-force "
|
||||
"integration rule."
|
||||
rule.integration_rule != nullptr, "The quadrature policy did not return a pressure-force "
|
||||
"integration rule."
|
||||
);
|
||||
|
||||
return *rule.integration_rule;
|
||||
@@ -153,38 +140,31 @@ namespace {
|
||||
const mfem::Vector &enthalpyTrue,
|
||||
const mfem::Vector &displacementTrue
|
||||
) {
|
||||
MFEM_VERIFY(f.mesh != nullptr, "The pressure-force kernel requires a mesh.");
|
||||
|
||||
MFEM_VERIFY(
|
||||
f.mesh != nullptr, "The pressure-force kernel requires a mesh."
|
||||
f.enthalpyFes != nullptr, "The pressure-force kernel requires the enthalpy "
|
||||
"finite-element space."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
f.enthalpyFes != nullptr,
|
||||
"The pressure-force kernel requires the enthalpy "
|
||||
"finite-element space."
|
||||
f.displacementFes != nullptr, "The pressure-force kernel requires the displacement "
|
||||
"finite-element space."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
f.displacementFes != nullptr,
|
||||
"The pressure-force kernel requires the displacement "
|
||||
"finite-element space."
|
||||
f.compactificationFes != nullptr, "The pressure-force kernel requires the compactification "
|
||||
"finite-element space."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
f.compactificationFes != nullptr,
|
||||
"The pressure-force kernel requires the compactification "
|
||||
"finite-element space."
|
||||
f.compactificationCoordinate != nullptr, "The pressure-force kernel requires the compactification "
|
||||
"coordinate."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
f.compactificationCoordinate != nullptr,
|
||||
"The pressure-force kernel requires the compactification "
|
||||
"coordinate."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
f.quadratureFactory != nullptr,
|
||||
"The pressure-force kernel requires the quadrature "
|
||||
"rule factory."
|
||||
f.quadratureFactory != nullptr, "The pressure-force kernel requires the quadrature "
|
||||
"rule factory."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
@@ -204,73 +184,105 @@ namespace {
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
f.displacementFes->GetVDim() == f.mesh->Dimension(),
|
||||
"The displacement vector dimension does not match the "
|
||||
"mesh dimension."
|
||||
f.displacementFes->GetVDim() == f.mesh->Dimension(), "The displacement vector dimension does not match the "
|
||||
"mesh dimension."
|
||||
);
|
||||
|
||||
/*
|
||||
* ElementDisplacementDataFromElementVDofs currently consumes the
|
||||
* registered byNODES layout. Keep this explicit so a future
|
||||
* registry change fails immediately rather than silently
|
||||
* corrupting the geometry.
|
||||
*/
|
||||
MFEM_VERIFY(
|
||||
f.displacementFes->GetOrdering() == mfem::Ordering::byNODES,
|
||||
"The pressure-force kernel requires the registered byNODES "
|
||||
"displacement ordering."
|
||||
);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace mean_field::operators::kernels {
|
||||
void apply_pressure_force_residual(
|
||||
const fem::FEM &f,
|
||||
const mapping::DomainMapperStateless &domainMapper,
|
||||
const physics::PolytropicBarotrope &barotrope,
|
||||
const mfem::Vector &enthalpyTrue,
|
||||
void apply_pressure_force_action(
|
||||
const mean_field::fem::FEM &f,
|
||||
const mean_field::mapping::DomainMapperStateless &domainMapper,
|
||||
const mean_field::eos::Polytrope &barotrope,
|
||||
const PressureForceAction pressureForceAction,
|
||||
const mfem::Vector &baseEnthalpyTrue,
|
||||
const mfem::Vector *enthalpyVariationTrue,
|
||||
const mfem::Vector *displacementVariationTrue,
|
||||
const mfem::Vector &displacementTrue,
|
||||
mfem::Vector &residualTrue
|
||||
mfem::Vector &actionTrue
|
||||
) {
|
||||
validate_inputs(f, domainMapper, enthalpyTrue, displacementTrue);
|
||||
validate_inputs(f, domainMapper, baseEnthalpyTrue, displacementTrue);
|
||||
|
||||
mfem::Vector enthalpyLocal;
|
||||
if (pressureForceAction == PressureForceAction::enthalpy) {
|
||||
MFEM_VERIFY(
|
||||
enthalpyVariationTrue != nullptr && enthalpyVariationTrue->Size() == f.enthalpyFes->GetTrueVSize(),
|
||||
"The pressure-force enthalpy variation has the wrong size."
|
||||
);
|
||||
}
|
||||
|
||||
if (pressureForceAction == PressureForceAction::displacement) {
|
||||
MFEM_VERIFY(
|
||||
displacementVariationTrue != nullptr &&
|
||||
displacementVariationTrue->Size() == f.displacementFes->GetTrueVSize(),
|
||||
"The pressure-force displacement variation has the wrong "
|
||||
"size."
|
||||
);
|
||||
}
|
||||
|
||||
mfem::Vector baseEnthalpyLocal;
|
||||
mfem::Vector enthalpyVariationLocal;
|
||||
mfem::Vector displacementLocal;
|
||||
mfem::Vector displacementVariationLocal;
|
||||
|
||||
true_to_local(*f.enthalpyFes, enthalpyTrue, enthalpyLocal);
|
||||
true_to_local(*f.enthalpyFes, baseEnthalpyTrue, baseEnthalpyLocal);
|
||||
|
||||
if (enthalpyVariationTrue != nullptr) {
|
||||
true_to_local(*f.enthalpyFes, *enthalpyVariationTrue, enthalpyVariationLocal);
|
||||
}
|
||||
|
||||
true_to_local(*f.displacementFes, displacementTrue, displacementLocal);
|
||||
|
||||
mfem::Vector localResidual(f.displacementFes->GetVSize());
|
||||
localResidual = 0.0;
|
||||
if (displacementVariationTrue != nullptr) {
|
||||
true_to_local(*f.displacementFes, *displacementVariationTrue, displacementVariationLocal);
|
||||
}
|
||||
|
||||
mapping::DomainMapperStateless::Workspace workspace(
|
||||
f.mesh->Dimension()
|
||||
);
|
||||
mfem::Vector localAction(f.displacementFes->GetVSize());
|
||||
localAction = 0.0;
|
||||
|
||||
mfem::Array<int> enthalpyDofs;
|
||||
mean_field::mapping::DomainMapperStateless::Workspace workspace(f.mesh->Dimension());
|
||||
|
||||
mfem::Array<int> enthalpyDofsofs;
|
||||
mfem::Array<int> displacementDofs;
|
||||
mfem::Array<int> compactificationDofs;
|
||||
|
||||
mfem::Vector elementEnthalpy;
|
||||
mfem::Vector elementBaseEnthalpy;
|
||||
mfem::Vector elementEnthalpyVariation;
|
||||
mfem::Vector elementDisplacement;
|
||||
mfem::Vector elementDisplacementVariation;
|
||||
mfem::Vector elementCompactification;
|
||||
mfem::Vector elementResidual;
|
||||
mfem::Vector elementAction;
|
||||
mfem::Vector enthalpyShape;
|
||||
|
||||
mfem::Array<int> enthalpyDofs;
|
||||
|
||||
mfem::DenseMatrix displacementDShapeReference;
|
||||
mfem::DenseMatrix displacementDShapePhysical;
|
||||
mfem::DenseMatrix displacementDShapePhysicalVariation;
|
||||
|
||||
mapping::VolumeMappingContext mappingContext;
|
||||
mean_field::mapping::VolumeMappingContext mappingContext;
|
||||
|
||||
const int dimension = f.mesh->Dimension();
|
||||
const int vacuumAttribute = domainMapper.GetVacuumElementAttribute();
|
||||
const int dimension = f.mesh->Dimension();
|
||||
const int vacuumAttribute = domainMapper.GetVacuumElementAttribute();
|
||||
|
||||
const mfem::Ordering::Type displacementOrdering =
|
||||
f.displacementFes->GetOrdering();
|
||||
const mfem::Ordering::Type displacementOrdering = f.displacementFes->GetOrdering();
|
||||
|
||||
for (int elementId = 0; elementId < f.mesh->GetNE(); ++elementId) {
|
||||
mfem::ElementTransformation *transformation =
|
||||
f.mesh->GetElementTransformation(elementId);
|
||||
mfem::ElementTransformation *transformation = f.mesh->GetElementTransformation(elementId);
|
||||
|
||||
MFEM_VERIFY(
|
||||
transformation != nullptr,
|
||||
"The pressure-force kernel received a null element "
|
||||
"transformation."
|
||||
transformation != nullptr, "The pressure-force kernel received a null element "
|
||||
"transformation."
|
||||
);
|
||||
|
||||
/*
|
||||
@@ -281,128 +293,131 @@ namespace mean_field::operators::kernels {
|
||||
continue;
|
||||
}
|
||||
|
||||
const mfem::FiniteElement &enthalpyElement =
|
||||
*f.enthalpyFes->GetFE(elementId);
|
||||
const mfem::FiniteElement &enthalpyElement = *f.enthalpyFes->GetFE(elementId);
|
||||
|
||||
const mfem::FiniteElement &displacementElement =
|
||||
*f.displacementFes->GetFE(elementId);
|
||||
const mfem::FiniteElement &displacementElement = *f.displacementFes->GetFE(elementId);
|
||||
|
||||
const mfem::FiniteElement &compactificationElement =
|
||||
*f.compactificationFes->GetFE(elementId);
|
||||
const mfem::FiniteElement &compactificationElement = *f.compactificationFes->GetFE(elementId);
|
||||
|
||||
mfem::DofTransformation *enthalpyDofTransformation =
|
||||
f.enthalpyFes->GetElementDofs(elementId, enthalpyDofs);
|
||||
mfem::DofTransformation *enthalpyDofTransformation = f.enthalpyFes->GetElementDofs(elementId, enthalpyDofs);
|
||||
|
||||
mfem::DofTransformation *displacementDofTransformation =
|
||||
f.displacementFes->GetElementVDofs(elementId, displacementDofs);
|
||||
|
||||
mfem::DofTransformation *compactificationDofTransformation =
|
||||
f.compactificationFes->GetElementDofs(
|
||||
elementId, compactificationDofs
|
||||
);
|
||||
f.compactificationFes->GetElementDofs(elementId, compactificationDofs);
|
||||
|
||||
enthalpyLocal.GetSubVector(enthalpyDofs, elementEnthalpy);
|
||||
baseEnthalpyLocal.GetSubVector(enthalpyDofs, elementBaseEnthalpy);
|
||||
|
||||
displacementLocal.GetSubVector(
|
||||
displacementDofs, elementDisplacement
|
||||
);
|
||||
if (enthalpyVariationTrue != nullptr) {
|
||||
enthalpyVariationLocal.GetSubVector(enthalpyDofs, elementEnthalpyVariation);
|
||||
}
|
||||
|
||||
f.compactificationCoordinate->GetSubVector(
|
||||
compactificationDofs, elementCompactification
|
||||
);
|
||||
displacementLocal.GetSubVector(displacementDofs, elementDisplacement);
|
||||
|
||||
if (displacementVariationTrue != nullptr) {
|
||||
displacementVariationLocal.GetSubVector(displacementDofs, elementDisplacementVariation);
|
||||
}
|
||||
|
||||
f.compactificationCoordinate->GetSubVector(compactificationDofs, elementCompactification);
|
||||
|
||||
if (enthalpyDofTransformation != nullptr) {
|
||||
enthalpyDofTransformation->InvTransformPrimal(elementEnthalpy);
|
||||
enthalpyDofTransformation->InvTransformPrimal(elementBaseEnthalpy);
|
||||
|
||||
if (enthalpyVariationTrue != nullptr) {
|
||||
enthalpyDofTransformation->InvTransformPrimal(elementEnthalpyVariation);
|
||||
}
|
||||
}
|
||||
|
||||
if (displacementDofTransformation != nullptr) {
|
||||
displacementDofTransformation->InvTransformPrimal(
|
||||
elementDisplacement
|
||||
);
|
||||
displacementDofTransformation->InvTransformPrimal(elementDisplacement);
|
||||
|
||||
if (displacementVariationTrue != nullptr) {
|
||||
displacementDofTransformation->InvTransformPrimal(elementDisplacementVariation);
|
||||
}
|
||||
}
|
||||
|
||||
if (compactificationDofTransformation != nullptr) {
|
||||
compactificationDofTransformation->InvTransformPrimal(
|
||||
elementCompactification
|
||||
);
|
||||
compactificationDofTransformation->InvTransformPrimal(elementCompactification);
|
||||
}
|
||||
|
||||
const mapping::ElementDisplacementData displacementData =
|
||||
mapping::ElementDisplacementDataFromElementVDofs(
|
||||
displacementElement, elementDisplacement
|
||||
);
|
||||
const mean_field::mapping::ElementDisplacementData displacementData =
|
||||
mean_field::mapping::ElementDisplacementDataFromElementVDofs(displacementElement, elementDisplacement);
|
||||
|
||||
const mapping::ElementCompactificationData compactificationData(
|
||||
const mean_field::mapping::ElementCompactificationData compactificationData(
|
||||
compactificationElement, elementCompactification
|
||||
);
|
||||
|
||||
const mapping::ElementMappingData mappingData{
|
||||
.displacement = displacementData,
|
||||
.compactification = compactificationData
|
||||
const mean_field::mapping::ElementMappingData mappingData{
|
||||
.displacement = displacementData, .compactification = compactificationData
|
||||
};
|
||||
|
||||
std::optional<mean_field::mapping::ElementDisplacementData> displacementVariationData;
|
||||
|
||||
if (displacementVariationTrue != nullptr) {
|
||||
displacementVariationData.emplace(
|
||||
mean_field::mapping::ElementDisplacementDataFromElementVDofs(
|
||||
displacementElement, elementDisplacementVariation
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
const int scalarDisplacementDofCount = displacementElement.GetDof();
|
||||
|
||||
MFEM_VERIFY(
|
||||
displacementDofs.Size() ==
|
||||
scalarDisplacementDofCount * dimension,
|
||||
displacementDofs.Size() == scalarDisplacementDofCount * dimension,
|
||||
"The pressure-force element displacement vector has "
|
||||
"the wrong size."
|
||||
);
|
||||
|
||||
enthalpyShape.SetSize(enthalpyElement.GetDof());
|
||||
|
||||
displacementDShapeReference.SetSize(
|
||||
scalarDisplacementDofCount, dimension
|
||||
);
|
||||
displacementDShapeReference.SetSize(scalarDisplacementDofCount, dimension);
|
||||
|
||||
displacementDShapePhysical.SetSize(
|
||||
scalarDisplacementDofCount, dimension
|
||||
);
|
||||
displacementDShapePhysical.SetSize(scalarDisplacementDofCount, dimension);
|
||||
|
||||
elementResidual.SetSize(displacementDofs.Size());
|
||||
elementResidual = 0.0;
|
||||
displacementDShapePhysicalVariation.SetSize(scalarDisplacementDofCount, dimension);
|
||||
|
||||
elementAction.SetSize(displacementDofs.Size());
|
||||
elementAction = 0.0;
|
||||
|
||||
const mfem::IntegrationRule &integrationRule =
|
||||
get_pressure_force_rule(
|
||||
f, barotrope, enthalpyElement, displacementElement,
|
||||
*transformation
|
||||
);
|
||||
get_pressure_force_rule(f, barotrope, enthalpyElement, displacementElement, *transformation);
|
||||
|
||||
for (int quadratureIndex = 0;
|
||||
quadratureIndex < integrationRule.GetNPoints();
|
||||
++quadratureIndex) {
|
||||
const mfem::IntegrationPoint &integrationPoint =
|
||||
integrationRule.IntPoint(quadratureIndex);
|
||||
for (int quadratureIndex = 0; quadratureIndex < integrationRule.GetNPoints(); ++quadratureIndex) {
|
||||
const mfem::IntegrationPoint &integrationPoint = integrationRule.IntPoint(quadratureIndex);
|
||||
|
||||
transformation->SetIntPoint(&integrationPoint);
|
||||
|
||||
const mapping::MappingStatus mappingStatus =
|
||||
domainMapper.EvaluateVolume(
|
||||
mappingData, *transformation, integrationPoint,
|
||||
workspace, mappingContext
|
||||
);
|
||||
const mean_field::mapping::MappingStatus mappingStatus = domainMapper.EvaluateVolume(
|
||||
mappingData, *transformation, integrationPoint, workspace, mappingContext
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
mappingStatus == mapping::MappingStatus::valid,
|
||||
mappingStatus == mean_field::mapping::MappingStatus::valid,
|
||||
"Stateless mapping failed in the pressure-force "
|
||||
"kernel. Element: "
|
||||
<< elementId
|
||||
<< ", attribute: " << transformation->Attribute
|
||||
<< ", quadrature point: " << quadratureIndex
|
||||
<< ", status: " << static_cast<int>(mappingStatus)
|
||||
<< elementId << ", attribute: " << transformation->Attribute
|
||||
<< ", quadrature point: " << quadratureIndex << ", status: " << static_cast<int>(mappingStatus)
|
||||
);
|
||||
|
||||
enthalpyElement.CalcShape(integrationPoint, enthalpyShape);
|
||||
|
||||
const double enthalpyValue = elementEnthalpy * enthalpyShape;
|
||||
const double enthalpyValue = elementBaseEnthalpy * enthalpyShape;
|
||||
|
||||
const double pressureValue =
|
||||
barotrope.pressure_from_enthalpy(enthalpyValue);
|
||||
double pressureFactor = 0.0;
|
||||
|
||||
displacementElement.CalcDShape(
|
||||
integrationPoint, displacementDShapeReference
|
||||
);
|
||||
if (pressureForceAction == PressureForceAction::residual ||
|
||||
pressureForceAction == PressureForceAction::displacement) {
|
||||
pressureFactor = barotrope.pressure_from_enthalpy(enthalpyValue);
|
||||
} else {
|
||||
const double enthalpyVariationValue = elementEnthalpyVariation * enthalpyShape;
|
||||
|
||||
pressureFactor =
|
||||
barotrope.pressure_derivative_from_enthalpy(enthalpyValue) * enthalpyVariationValue;
|
||||
}
|
||||
|
||||
displacementElement.CalcDShape(integrationPoint, displacementDShapeReference);
|
||||
|
||||
/*
|
||||
* Row i of DShape is grad_reference(N_i). Multiplication
|
||||
@@ -411,17 +426,45 @@ namespace mean_field::operators::kernels {
|
||||
* grad_physical(N_i)
|
||||
* = grad_reference(N_i) J^{-1}.
|
||||
*/
|
||||
mfem::Mult(
|
||||
displacementDShapeReference,
|
||||
mappingContext.quadrature.J_inv, displacementDShapePhysical
|
||||
);
|
||||
mfem::Mult(displacementDShapeReference, mappingContext.quadrature.J_inv, displacementDShapePhysical);
|
||||
|
||||
const double weightedPressure =
|
||||
pressureValue * mappingContext.quadrature.weight;
|
||||
std::optional<mean_field::mapping::VolumeMappingVariation> mappingVariation;
|
||||
|
||||
if (pressureForceAction == PressureForceAction::displacement) {
|
||||
mappingVariation.emplace();
|
||||
|
||||
const mean_field::mapping::MappingStatus variationStatus = domainMapper.EvaluateVolumeVariation(
|
||||
mappingData, *displacementVariationData, *transformation, integrationPoint, mappingContext,
|
||||
workspace, *mappingVariation
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
variationStatus == mean_field::mapping::MappingStatus::valid,
|
||||
"Stateless mapping variation failed in the "
|
||||
"pressure-force kernel. Element: "
|
||||
<< elementId << ", attribute: " << transformation->Attribute << ", quadrature point: "
|
||||
<< quadratureIndex << ", status: " << static_cast<int>(variationStatus)
|
||||
);
|
||||
|
||||
/*
|
||||
* Differentiating
|
||||
*
|
||||
* grad_x(N_i) = grad_reference(N_i) J^{-1}
|
||||
*
|
||||
* at the frozen base geometry gives the physical
|
||||
* test-gradient variation used by the geometric
|
||||
* pressure block.
|
||||
*/
|
||||
mfem::Mult(
|
||||
displacementDShapeReference, mappingVariation->inverse_element_jacobian_variation,
|
||||
displacementDShapePhysicalVariation
|
||||
);
|
||||
}
|
||||
|
||||
const double weightedPressureFactor = pressureFactor * mappingContext.quadrature.weight;
|
||||
|
||||
MFEM_VERIFY(
|
||||
std::isfinite(pressureValue) &&
|
||||
std::isfinite(weightedPressure),
|
||||
std::isfinite(pressureFactor) && std::isfinite(weightedPressureFactor),
|
||||
"The pressure-force kernel encountered a non-finite "
|
||||
"quadrature value."
|
||||
);
|
||||
@@ -436,29 +479,96 @@ namespace mean_field::operators::kernels {
|
||||
* R_(i,c)
|
||||
* = -integral P partial_c N_i dV.
|
||||
*/
|
||||
for (int scalarDof = 0; scalarDof < scalarDisplacementDofCount;
|
||||
++scalarDof) {
|
||||
for (int component = 0; component < dimension;
|
||||
++component) {
|
||||
for (int scalarDof = 0; scalarDof < scalarDisplacementDofCount; ++scalarDof) {
|
||||
for (int component = 0; component < dimension; ++component) {
|
||||
const int vectorDof = vector_dof_index(
|
||||
displacementOrdering, scalarDof, component,
|
||||
scalarDisplacementDofCount, dimension
|
||||
displacementOrdering, scalarDof, component, scalarDisplacementDofCount, dimension
|
||||
);
|
||||
|
||||
elementResidual(vectorDof) -=
|
||||
weightedPressure *
|
||||
displacementDShapePhysical(scalarDof, component);
|
||||
if (pressureForceAction == PressureForceAction::displacement) {
|
||||
/*
|
||||
* Differentiate the complete discrete factor
|
||||
*
|
||||
* grad_x(N_i) dV_x.
|
||||
*
|
||||
* The enthalpy DOFs, and therefore P(h), are
|
||||
* frozen in this Jacobian column.
|
||||
*/
|
||||
const double gradientWeightVariation =
|
||||
mappingContext.quadrature.weight *
|
||||
displacementDShapePhysicalVariation(scalarDof, component) +
|
||||
mappingVariation->weight_variation * displacementDShapePhysical(scalarDof, component);
|
||||
|
||||
const double contribution = pressureFactor * gradientWeightVariation;
|
||||
|
||||
MFEM_VERIFY(
|
||||
std::isfinite(gradientWeightVariation) && std::isfinite(contribution),
|
||||
"The pressure-force geometry action "
|
||||
"encountered a non-finite contribution."
|
||||
);
|
||||
|
||||
elementAction(vectorDof) -= contribution;
|
||||
} else {
|
||||
elementAction(vectorDof) -=
|
||||
weightedPressureFactor * displacementDShapePhysical(scalarDof, component);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (displacementDofTransformation != nullptr) {
|
||||
displacementDofTransformation->TransformDual(elementResidual);
|
||||
displacementDofTransformation->TransformDual(elementAction);
|
||||
}
|
||||
|
||||
localResidual.AddElementVector(displacementDofs, elementResidual);
|
||||
localAction.AddElementVector(displacementDofs, elementAction);
|
||||
}
|
||||
|
||||
local_to_true(*f.displacementFes, localResidual, residualTrue);
|
||||
local_to_true(*f.displacementFes, localAction, actionTrue);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace mean_field::operators::kernels {
|
||||
void apply_pressure_force_residual(
|
||||
const fem::FEM &f,
|
||||
const mapping::DomainMapperStateless &domainMapper,
|
||||
const eos::Polytrope &barotrope,
|
||||
const mfem::Vector &enthalpyTrue,
|
||||
const mfem::Vector &displacementTrue,
|
||||
mfem::Vector &residualTrue
|
||||
) {
|
||||
apply_pressure_force_action(
|
||||
f, domainMapper, barotrope, PressureForceAction::residual, enthalpyTrue, nullptr, nullptr, displacementTrue,
|
||||
residualTrue
|
||||
);
|
||||
}
|
||||
|
||||
void apply_pressure_force_enthalpy_action(
|
||||
const fem::FEM &f,
|
||||
const mapping::DomainMapperStateless &domainMapper,
|
||||
const eos::Polytrope &barotrope,
|
||||
const mfem::Vector &baseEnthalpyTrue,
|
||||
const mfem::Vector &enthalpyVariationTrue,
|
||||
const mfem::Vector &displacementTrue,
|
||||
mfem::Vector &actionTrue
|
||||
) {
|
||||
apply_pressure_force_action(
|
||||
f, domainMapper, barotrope, PressureForceAction::enthalpy, baseEnthalpyTrue, &enthalpyVariationTrue,
|
||||
nullptr, displacementTrue, actionTrue
|
||||
);
|
||||
}
|
||||
|
||||
void apply_pressure_force_displacement_action(
|
||||
const fem::FEM &f,
|
||||
const mapping::DomainMapperStateless &domainMapper,
|
||||
const eos::Polytrope &barotrope,
|
||||
const mfem::Vector &baseEnthalpyTrue,
|
||||
const mfem::Vector &displacementVariationTrue,
|
||||
const mfem::Vector &displacementTrue,
|
||||
mfem::Vector &actionTrue
|
||||
) {
|
||||
apply_pressure_force_action(
|
||||
f, domainMapper, barotrope, PressureForceAction::displacement, baseEnthalpyTrue, nullptr,
|
||||
&displacementVariationTrue, displacementTrue, actionTrue
|
||||
);
|
||||
}
|
||||
} // namespace mean_field::operators::kernels
|
||||
@@ -0,0 +1,590 @@
|
||||
module;
|
||||
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <optional>
|
||||
|
||||
#include <mfem.hpp>
|
||||
|
||||
module mean_field;
|
||||
|
||||
import :operators.kernels.rotational_displacement_force;
|
||||
|
||||
namespace {
|
||||
enum class RotationalDisplacementForceAction { residual, density, displacement, complete };
|
||||
|
||||
void true_to_local(
|
||||
const mfem::ParFiniteElementSpace &finiteElementSpace,
|
||||
const mfem::Vector &trueVector,
|
||||
mfem::Vector &localVector
|
||||
) {
|
||||
MFEM_VERIFY(
|
||||
trueVector.Size() == finiteElementSpace.GetTrueVSize(),
|
||||
"The rotational-displacement-force true vector has the wrong "
|
||||
"size."
|
||||
);
|
||||
|
||||
localVector.SetSize(finiteElementSpace.GetVSize());
|
||||
|
||||
const mfem::Operator *prolongation = finiteElementSpace.GetProlongationMatrix();
|
||||
|
||||
if (prolongation != nullptr) {
|
||||
prolongation->Mult(trueVector, localVector);
|
||||
} else {
|
||||
localVector = trueVector;
|
||||
}
|
||||
}
|
||||
|
||||
void local_to_true(
|
||||
const mfem::ParFiniteElementSpace &finiteElementSpace,
|
||||
const mfem::Vector &localVector,
|
||||
mfem::Vector &trueVector
|
||||
) {
|
||||
MFEM_VERIFY(
|
||||
localVector.Size() == finiteElementSpace.GetVSize(),
|
||||
"The rotational-displacement-force local vector has the wrong "
|
||||
"size."
|
||||
);
|
||||
|
||||
trueVector.SetSize(finiteElementSpace.GetTrueVSize());
|
||||
trueVector = 0.0;
|
||||
|
||||
const mfem::Operator *prolongation = finiteElementSpace.GetProlongationMatrix();
|
||||
|
||||
if (prolongation != nullptr) {
|
||||
prolongation->MultTranspose(localVector, trueVector);
|
||||
} else {
|
||||
trueVector = localVector;
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] int vector_dof_index(
|
||||
const mfem::Ordering::Type ordering,
|
||||
const int scalarDof,
|
||||
const int component,
|
||||
const int scalarDofCount,
|
||||
const int dimension
|
||||
) {
|
||||
if (ordering == mfem::Ordering::byNODES) {
|
||||
return scalarDof + component * scalarDofCount;
|
||||
}
|
||||
|
||||
if (ordering == mfem::Ordering::byVDIM) {
|
||||
return scalarDof * dimension + component;
|
||||
}
|
||||
|
||||
MFEM_ABORT(
|
||||
"The rotational-displacement-force test space uses an "
|
||||
"unsupported ordering."
|
||||
);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
[[nodiscard]] const mfem::IntegrationRule &get_rotation_force_rule(
|
||||
const mean_field::fem::FEM &f,
|
||||
const mfem::FiniteElement &densityElement,
|
||||
const mfem::FiniteElement &displacementElement,
|
||||
const mfem::ElementTransformation &transformation
|
||||
) {
|
||||
using DisplacementField = mean_field::field::Field<mean_field::field::Displacement>;
|
||||
|
||||
MFEM_VERIFY(
|
||||
densityElement.GetOrder() == mean_field::field::Density::Scalar::familyOrder,
|
||||
"The rotational-displacement-force density element does not "
|
||||
"match the registered density field."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
displacementElement.GetOrder() == mean_field::field::Displacement::Vector::familyOrder,
|
||||
"The rotational-displacement-force test element does not match "
|
||||
"the registered displacement field."
|
||||
);
|
||||
|
||||
/*
|
||||
* grad(Psi_rotation) is linear in physical position, so it adds one
|
||||
* dynamic polynomial-order contribution.
|
||||
*/
|
||||
const mean_field::quadrature::Query query =
|
||||
DisplacementField::make_query<mean_field::field::Displacement::Form::CentrifugalForce>(
|
||||
mean_field::quadrature::QuadratureRole::discretization, transformation.OrderW(), std::array<int, 1>{1},
|
||||
mean_field::utils::DOMAINS::STELLAR, mean_field::quadrature::MappingKind::general
|
||||
);
|
||||
|
||||
const mean_field::quadrature::MfemRule rule = f.quadratureFactory->get(query, transformation.GetGeometryType());
|
||||
|
||||
MFEM_VERIFY(
|
||||
rule.integration_rule != nullptr, "The quadrature policy did not return a rotational-"
|
||||
"displacement-force integration rule."
|
||||
);
|
||||
|
||||
return *rule.integration_rule;
|
||||
}
|
||||
|
||||
void validate_finite_vector(
|
||||
const mfem::Vector &vector,
|
||||
const char *message
|
||||
) {
|
||||
for (int index = 0; index < vector.Size(); ++index) {
|
||||
MFEM_VERIFY(std::isfinite(vector(index)), message);
|
||||
}
|
||||
}
|
||||
|
||||
void validate_common_inputs(
|
||||
const mean_field::fem::FEM &f,
|
||||
const mean_field::mapping::DomainMapperStateless &domainMapper,
|
||||
const mfem::Vector &displacementTrue
|
||||
) {
|
||||
MFEM_VERIFY(f.mesh != nullptr, "The rotational-displacement-force kernel requires a mesh.");
|
||||
|
||||
MFEM_VERIFY(
|
||||
f.mesh->Dimension() == 3, "The rotational-displacement-force kernel requires a "
|
||||
"three-dimensional mesh."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
f.densityFes != nullptr, "The rotational-displacement-force kernel requires the density "
|
||||
"finite-element space."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
f.displacementFes != nullptr, "The rotational-displacement-force kernel requires the "
|
||||
"displacement finite-element space."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
f.compactificationFes != nullptr && f.compactificationCoordinate != nullptr,
|
||||
"The rotational-displacement-force kernel requires the "
|
||||
"compactification coordinate."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
f.quadratureFactory != nullptr, "The rotational-displacement-force kernel requires the "
|
||||
"quadrature-rule factory."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
displacementTrue.Size() == f.displacementFes->GetTrueVSize(),
|
||||
"The rotational-displacement-force displacement vector has the "
|
||||
"wrong size."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
domainMapper.GetDimension() == f.mesh->Dimension(),
|
||||
"The rotational-displacement-force mapper dimension does not "
|
||||
"match the mesh dimension."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
f.displacementFes->GetVDim() == f.mesh->Dimension(),
|
||||
"The rotational-displacement-force displacement dimension does "
|
||||
"not match the mesh dimension."
|
||||
);
|
||||
|
||||
validate_finite_vector(
|
||||
displacementTrue, "The rotational-displacement-force displacement contains a "
|
||||
"non-finite value."
|
||||
);
|
||||
}
|
||||
|
||||
void validate_density(
|
||||
const mean_field::fem::FEM &f,
|
||||
const mfem::Vector &density,
|
||||
const char *message
|
||||
) {
|
||||
MFEM_VERIFY(density.Size() == f.densityFes->GetTrueVSize(), message);
|
||||
validate_finite_vector(density, message);
|
||||
}
|
||||
|
||||
void apply_rotational_displacement_force_action(
|
||||
const mean_field::fem::FEM &f,
|
||||
const mean_field::mapping::DomainMapperStateless &domainMapper,
|
||||
const mean_field::physics::RigidRotation &rotation,
|
||||
const RotationalDisplacementForceAction requestedAction,
|
||||
const mfem::Vector *baseDensityTrue,
|
||||
const mfem::Vector *densityVariationTrue,
|
||||
const mfem::Vector *displacementVariationTrue,
|
||||
const mfem::Vector &displacementTrue,
|
||||
mfem::Vector &actionTrue
|
||||
) {
|
||||
validate_common_inputs(f, domainMapper, displacementTrue);
|
||||
|
||||
const bool needsBaseDensity = requestedAction == RotationalDisplacementForceAction::residual ||
|
||||
requestedAction == RotationalDisplacementForceAction::displacement ||
|
||||
requestedAction == RotationalDisplacementForceAction::complete;
|
||||
|
||||
const bool needsDensityVariation = requestedAction == RotationalDisplacementForceAction::density ||
|
||||
requestedAction == RotationalDisplacementForceAction::complete;
|
||||
|
||||
const bool needsDisplacementVariation = requestedAction == RotationalDisplacementForceAction::displacement ||
|
||||
requestedAction == RotationalDisplacementForceAction::complete;
|
||||
|
||||
if (needsBaseDensity) {
|
||||
MFEM_VERIFY(
|
||||
baseDensityTrue != nullptr, "The rotational-displacement-force action requires a base "
|
||||
"density."
|
||||
);
|
||||
|
||||
validate_density(f, *baseDensityTrue, "The rotational-displacement-force base density is invalid.");
|
||||
}
|
||||
|
||||
if (needsDensityVariation) {
|
||||
MFEM_VERIFY(
|
||||
densityVariationTrue != nullptr, "The rotational-displacement-force action requires a "
|
||||
"density variation."
|
||||
);
|
||||
|
||||
validate_density(
|
||||
f, *densityVariationTrue,
|
||||
"The rotational-displacement-force density variation is "
|
||||
"invalid."
|
||||
);
|
||||
}
|
||||
|
||||
if (needsDisplacementVariation) {
|
||||
MFEM_VERIFY(
|
||||
displacementVariationTrue != nullptr &&
|
||||
displacementVariationTrue->Size() == f.displacementFes->GetTrueVSize(),
|
||||
"The rotational-displacement-force displacement variation "
|
||||
"is invalid."
|
||||
);
|
||||
|
||||
validate_finite_vector(
|
||||
*displacementVariationTrue, "The rotational-displacement-force displacement variation "
|
||||
"contains a non-finite value."
|
||||
);
|
||||
}
|
||||
|
||||
mfem::Vector baseDensityLocal;
|
||||
mfem::Vector densityVariationLocal;
|
||||
mfem::Vector displacementLocal;
|
||||
mfem::Vector displacementVariationLocal;
|
||||
|
||||
if (needsBaseDensity) {
|
||||
true_to_local(*f.densityFes, *baseDensityTrue, baseDensityLocal);
|
||||
}
|
||||
|
||||
if (needsDensityVariation) {
|
||||
true_to_local(*f.densityFes, *densityVariationTrue, densityVariationLocal);
|
||||
}
|
||||
|
||||
true_to_local(*f.displacementFes, displacementTrue, displacementLocal);
|
||||
|
||||
if (needsDisplacementVariation) {
|
||||
true_to_local(*f.displacementFes, *displacementVariationTrue, displacementVariationLocal);
|
||||
}
|
||||
|
||||
mfem::Vector localAction(f.displacementFes->GetVSize());
|
||||
localAction = 0.0;
|
||||
|
||||
mean_field::mapping::DomainMapperStateless::Workspace workspace(f.mesh->Dimension());
|
||||
|
||||
mfem::Array<int> densityDofs;
|
||||
mfem::Array<int> displacementDofs;
|
||||
mfem::Array<int> compactificationDofs;
|
||||
|
||||
mfem::Vector elementBaseDensity;
|
||||
mfem::Vector elementDensityVariation;
|
||||
mfem::Vector elementDisplacement;
|
||||
mfem::Vector elementDisplacementVariation;
|
||||
mfem::Vector elementCompactification;
|
||||
mfem::Vector elementAction;
|
||||
|
||||
mfem::Vector densityShape;
|
||||
mfem::Vector displacementShape;
|
||||
mfem::Vector potentialGradient;
|
||||
mfem::Vector potentialGradientVariation;
|
||||
mfem::Vector centrifugalAcceleration;
|
||||
mfem::Vector centrifugalAccelerationVariation;
|
||||
mfem::Vector weightedForce;
|
||||
|
||||
mean_field::mapping::VolumeMappingContext mappingContext;
|
||||
mean_field::mapping::VolumeMappingVariation mappingVariation;
|
||||
|
||||
const int dimension = f.mesh->Dimension();
|
||||
const int vacuumAttribute = domainMapper.GetVacuumElementAttribute();
|
||||
|
||||
const mfem::Ordering::Type displacementOrdering = f.displacementFes->GetOrdering();
|
||||
|
||||
for (int elementId = 0; elementId < f.mesh->GetNE(); ++elementId) {
|
||||
mfem::ElementTransformation *transformation = f.mesh->GetElementTransformation(elementId);
|
||||
|
||||
MFEM_VERIFY(
|
||||
transformation != nullptr, "The rotational-displacement-force kernel received a null "
|
||||
"element transformation."
|
||||
);
|
||||
|
||||
if (transformation->Attribute == vacuumAttribute) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const mfem::FiniteElement &densityElement = *f.densityFes->GetFE(elementId);
|
||||
|
||||
const mfem::FiniteElement &displacementElement = *f.displacementFes->GetFE(elementId);
|
||||
|
||||
const mfem::FiniteElement &compactificationElement = *f.compactificationFes->GetFE(elementId);
|
||||
|
||||
mfem::DofTransformation *densityDofTransformation = f.densityFes->GetElementDofs(elementId, densityDofs);
|
||||
|
||||
mfem::DofTransformation *displacementDofTransformation =
|
||||
f.displacementFes->GetElementVDofs(elementId, displacementDofs);
|
||||
|
||||
mfem::DofTransformation *compactificationDofTransformation =
|
||||
f.compactificationFes->GetElementDofs(elementId, compactificationDofs);
|
||||
|
||||
if (needsBaseDensity) {
|
||||
baseDensityLocal.GetSubVector(densityDofs, elementBaseDensity);
|
||||
}
|
||||
|
||||
if (needsDensityVariation) {
|
||||
densityVariationLocal.GetSubVector(densityDofs, elementDensityVariation);
|
||||
}
|
||||
|
||||
displacementLocal.GetSubVector(displacementDofs, elementDisplacement);
|
||||
|
||||
if (needsDisplacementVariation) {
|
||||
displacementVariationLocal.GetSubVector(displacementDofs, elementDisplacementVariation);
|
||||
}
|
||||
|
||||
f.compactificationCoordinate->GetSubVector(compactificationDofs, elementCompactification);
|
||||
|
||||
if (densityDofTransformation != nullptr) {
|
||||
if (needsBaseDensity) {
|
||||
densityDofTransformation->InvTransformPrimal(elementBaseDensity);
|
||||
}
|
||||
|
||||
if (needsDensityVariation) {
|
||||
densityDofTransformation->InvTransformPrimal(elementDensityVariation);
|
||||
}
|
||||
}
|
||||
|
||||
if (displacementDofTransformation != nullptr) {
|
||||
displacementDofTransformation->InvTransformPrimal(elementDisplacement);
|
||||
|
||||
if (needsDisplacementVariation) {
|
||||
displacementDofTransformation->InvTransformPrimal(elementDisplacementVariation);
|
||||
}
|
||||
}
|
||||
|
||||
if (compactificationDofTransformation != nullptr) {
|
||||
compactificationDofTransformation->InvTransformPrimal(elementCompactification);
|
||||
}
|
||||
|
||||
const mean_field::mapping::ElementDisplacementData displacementData =
|
||||
mean_field::mapping::ElementDisplacementDataFromElementVDofs(displacementElement, elementDisplacement);
|
||||
|
||||
const mean_field::mapping::ElementCompactificationData compactificationData(
|
||||
compactificationElement, elementCompactification
|
||||
);
|
||||
|
||||
const mean_field::mapping::ElementMappingData mappingData{
|
||||
.displacement = displacementData, .compactification = compactificationData
|
||||
};
|
||||
|
||||
std::optional<mean_field::mapping::ElementDisplacementData> displacementVariationData;
|
||||
|
||||
if (needsDisplacementVariation) {
|
||||
displacementVariationData.emplace(
|
||||
mean_field::mapping::ElementDisplacementDataFromElementVDofs(
|
||||
displacementElement, elementDisplacementVariation
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
const int scalarDisplacementDofCount = displacementElement.GetDof();
|
||||
|
||||
MFEM_VERIFY(
|
||||
displacementDofs.Size() == scalarDisplacementDofCount * dimension,
|
||||
"The rotational-displacement-force element displacement "
|
||||
"vector has the wrong size."
|
||||
);
|
||||
|
||||
densityShape.SetSize(densityElement.GetDof());
|
||||
displacementShape.SetSize(scalarDisplacementDofCount);
|
||||
potentialGradient.SetSize(dimension);
|
||||
potentialGradientVariation.SetSize(dimension);
|
||||
centrifugalAcceleration.SetSize(dimension);
|
||||
centrifugalAccelerationVariation.SetSize(dimension);
|
||||
weightedForce.SetSize(dimension);
|
||||
|
||||
elementAction.SetSize(displacementDofs.Size());
|
||||
elementAction = 0.0;
|
||||
|
||||
const mfem::IntegrationRule &integrationRule =
|
||||
get_rotation_force_rule(f, densityElement, displacementElement, *transformation);
|
||||
|
||||
for (int quadratureIndex = 0; quadratureIndex < integrationRule.GetNPoints(); ++quadratureIndex) {
|
||||
const mfem::IntegrationPoint &integrationPoint = integrationRule.IntPoint(quadratureIndex);
|
||||
|
||||
transformation->SetIntPoint(&integrationPoint);
|
||||
|
||||
const mean_field::mapping::MappingStatus mappingStatus = domainMapper.EvaluateVolume(
|
||||
mappingData, *transformation, integrationPoint, workspace, mappingContext
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
mappingStatus == mean_field::mapping::MappingStatus::valid,
|
||||
"Stateless mapping failed in the rotational-"
|
||||
"displacement-force kernel. Element: "
|
||||
<< elementId << ", attribute: " << transformation->Attribute
|
||||
<< ", quadrature point: " << quadratureIndex << ", status: " << static_cast<int>(mappingStatus)
|
||||
);
|
||||
|
||||
if (needsDisplacementVariation) {
|
||||
const mean_field::mapping::MappingStatus variationStatus = domainMapper.EvaluateVolumeVariation(
|
||||
mappingData, *displacementVariationData, *transformation, integrationPoint, mappingContext,
|
||||
workspace, mappingVariation
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
variationStatus == mean_field::mapping::MappingStatus::valid,
|
||||
"Stateless mapping variation failed in the "
|
||||
"rotational-displacement-force kernel. Element: "
|
||||
<< elementId << ", attribute: " << transformation->Attribute << ", quadrature point: "
|
||||
<< quadratureIndex << ", status: " << static_cast<int>(variationStatus)
|
||||
);
|
||||
}
|
||||
|
||||
densityElement.CalcShape(integrationPoint, densityShape);
|
||||
|
||||
displacementElement.CalcShape(integrationPoint, displacementShape);
|
||||
|
||||
double baseDensityValue = 0.0;
|
||||
double densityVariationValue = 0.0;
|
||||
|
||||
if (needsBaseDensity) {
|
||||
baseDensityValue = elementBaseDensity * densityShape;
|
||||
}
|
||||
|
||||
if (needsDensityVariation) {
|
||||
densityVariationValue = elementDensityVariation * densityShape;
|
||||
}
|
||||
|
||||
rotation.potential_gradient(mappingContext.mapping.physical_position, potentialGradient);
|
||||
|
||||
centrifugalAcceleration = potentialGradient;
|
||||
centrifugalAcceleration *= -1.0;
|
||||
|
||||
if (needsDisplacementVariation) {
|
||||
rotation.potential_gradient_directional_derivative(
|
||||
mappingVariation.mapping.physical_position_variation, potentialGradientVariation
|
||||
);
|
||||
|
||||
centrifugalAccelerationVariation = potentialGradientVariation;
|
||||
|
||||
centrifugalAccelerationVariation *= -1.0;
|
||||
} else {
|
||||
centrifugalAccelerationVariation = 0.0;
|
||||
}
|
||||
|
||||
weightedForce = 0.0;
|
||||
|
||||
if (requestedAction == RotationalDisplacementForceAction::residual) {
|
||||
weightedForce.Add(baseDensityValue * mappingContext.quadrature.weight, centrifugalAcceleration);
|
||||
} else {
|
||||
if (needsDensityVariation) {
|
||||
weightedForce.Add(
|
||||
densityVariationValue * mappingContext.quadrature.weight, centrifugalAcceleration
|
||||
);
|
||||
}
|
||||
|
||||
if (needsDisplacementVariation) {
|
||||
weightedForce.Add(
|
||||
baseDensityValue * mappingContext.quadrature.weight, centrifugalAccelerationVariation
|
||||
);
|
||||
|
||||
weightedForce.Add(
|
||||
baseDensityValue * mappingVariation.weight_variation, centrifugalAcceleration
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
for (int scalarDof = 0; scalarDof < scalarDisplacementDofCount; ++scalarDof) {
|
||||
for (int component = 0; component < dimension; ++component) {
|
||||
const int vectorDof = vector_dof_index(
|
||||
displacementOrdering, scalarDof, component, scalarDisplacementDofCount, dimension
|
||||
);
|
||||
|
||||
const double contribution = displacementShape(scalarDof) * weightedForce(component);
|
||||
|
||||
MFEM_VERIFY(
|
||||
std::isfinite(contribution), "The rotational-displacement-force kernel "
|
||||
"encountered a non-finite contribution."
|
||||
);
|
||||
|
||||
elementAction(vectorDof) += contribution;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (displacementDofTransformation != nullptr) {
|
||||
displacementDofTransformation->TransformDual(elementAction);
|
||||
}
|
||||
|
||||
localAction.AddElementVector(displacementDofs, elementAction);
|
||||
}
|
||||
|
||||
local_to_true(*f.displacementFes, localAction, actionTrue);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace mean_field::operators::kernels {
|
||||
void apply_rotational_displacement_force_residual(
|
||||
const fem::FEM &f,
|
||||
const mapping::DomainMapperStateless &domainMapper,
|
||||
const physics::RigidRotation &rotation,
|
||||
const mfem::Vector &densityTrue,
|
||||
const mfem::Vector &displacementTrue,
|
||||
mfem::Vector &residualTrue
|
||||
) {
|
||||
apply_rotational_displacement_force_action(
|
||||
f, domainMapper, rotation, RotationalDisplacementForceAction::residual, &densityTrue, nullptr, nullptr,
|
||||
displacementTrue, residualTrue
|
||||
);
|
||||
}
|
||||
|
||||
void apply_rotational_displacement_force_density_action(
|
||||
const fem::FEM &f,
|
||||
const mapping::DomainMapperStateless &domainMapper,
|
||||
const physics::RigidRotation &rotation,
|
||||
const mfem::Vector &densityVariationTrue,
|
||||
const mfem::Vector &displacementTrue,
|
||||
mfem::Vector &actionTrue
|
||||
) {
|
||||
apply_rotational_displacement_force_action(
|
||||
f, domainMapper, rotation, RotationalDisplacementForceAction::density, nullptr, &densityVariationTrue,
|
||||
nullptr, displacementTrue, actionTrue
|
||||
);
|
||||
}
|
||||
|
||||
void apply_rotational_displacement_force_displacement_action(
|
||||
const fem::FEM &f,
|
||||
const mapping::DomainMapperStateless &domainMapper,
|
||||
const physics::RigidRotation &rotation,
|
||||
const mfem::Vector &baseDensityTrue,
|
||||
const mfem::Vector &displacementVariationTrue,
|
||||
const mfem::Vector &displacementTrue,
|
||||
mfem::Vector &actionTrue
|
||||
) {
|
||||
apply_rotational_displacement_force_action(
|
||||
f, domainMapper, rotation, RotationalDisplacementForceAction::displacement, &baseDensityTrue, nullptr,
|
||||
&displacementVariationTrue, displacementTrue, actionTrue
|
||||
);
|
||||
}
|
||||
|
||||
void apply_rotational_displacement_force_complete_action(
|
||||
const fem::FEM &f,
|
||||
const mapping::DomainMapperStateless &domainMapper,
|
||||
const physics::RigidRotation &rotation,
|
||||
const mfem::Vector &baseDensityTrue,
|
||||
const mfem::Vector &densityVariationTrue,
|
||||
const mfem::Vector &displacementVariationTrue,
|
||||
const mfem::Vector &displacementTrue,
|
||||
mfem::Vector &actionTrue
|
||||
) {
|
||||
apply_rotational_displacement_force_action(
|
||||
f, domainMapper, rotation, RotationalDisplacementForceAction::complete, &baseDensityTrue,
|
||||
&densityVariationTrue, &displacementVariationTrue, displacementTrue, actionTrue
|
||||
);
|
||||
}
|
||||
} // namespace mean_field::operators::kernels
|
||||
@@ -5,38 +5,54 @@ module;
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <mfem.hpp>
|
||||
#include <utility>
|
||||
|
||||
module mean_field;
|
||||
|
||||
import :operators.prepared_barotropic_closure;
|
||||
import :operators.kernels.barotropic_closure;
|
||||
import :field.registry;
|
||||
import :utils.domain;
|
||||
|
||||
namespace {
|
||||
int get_density_size(const mean_field::fem::FEM &f) {
|
||||
MFEM_VERIFY(
|
||||
f.densityFes != nullptr,
|
||||
"PreparedBarotropicClosureOperator requires the "
|
||||
"density finite-element space."
|
||||
);
|
||||
using DomainSchema = mean_field::utils::domain::CoreEnvelopeVacuumDomainSchema;
|
||||
using ClosureDomain = mean_field::field::FieldDomainT<mean_field::field::Density>;
|
||||
|
||||
return f.densityFes->GetTrueVSize();
|
||||
void verify_required_spaces(const mean_field::fem::FEM &f) {
|
||||
MFEM_VERIFY(f.mesh != nullptr, "PreparedBarotropicClosureOperator requires a mesh.");
|
||||
MFEM_VERIFY(
|
||||
f.densityFes != nullptr, "PreparedBarotropicClosureOperator requires the density finite-element space."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.enthalpyFes != nullptr, "PreparedBarotropicClosureOperator requires the enthalpy finite-element space."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.displacementFes != nullptr,
|
||||
"PreparedBarotropicClosureOperator requires the displacement finite-element space."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.compactificationFes != nullptr,
|
||||
"PreparedBarotropicClosureOperator requires the compactification finite-element space."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.compactificationCoordinate != nullptr,
|
||||
"PreparedBarotropicClosureOperator requires the compactification coordinate."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.quadratureFactory != nullptr, "PreparedBarotropicClosureOperator requires the quadrature factory."
|
||||
);
|
||||
}
|
||||
|
||||
int get_enthalpy_size(const mean_field::fem::FEM &f) {
|
||||
MFEM_VERIFY(
|
||||
f.enthalpyFes != nullptr,
|
||||
"PreparedBarotropicClosureOperator requires the "
|
||||
"enthalpy finite-element space."
|
||||
);
|
||||
|
||||
return f.enthalpyFes->GetTrueVSize();
|
||||
[[nodiscard]] bool element_is_in_closure_support(const int attribute) {
|
||||
return DomainSchema::template attribute_belongs_to<ClosureDomain>(attribute);
|
||||
}
|
||||
|
||||
void validate_finite_vector(
|
||||
const mfem::Vector &vector,
|
||||
const char *message
|
||||
) {
|
||||
for (int i = 0; i < vector.Size(); ++i) {
|
||||
MFEM_VERIFY(std::isfinite(vector(i)), message);
|
||||
for (int index = 0; index < vector.Size(); ++index) {
|
||||
MFEM_VERIFY(std::isfinite(vector(index)), message);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,15 +61,11 @@ namespace {
|
||||
const mfem::Vector &trueVector,
|
||||
mfem::Vector &localVector
|
||||
) {
|
||||
MFEM_VERIFY(
|
||||
trueVector.Size() == finiteElementSpace.GetTrueVSize(),
|
||||
"True vector has the wrong size."
|
||||
);
|
||||
MFEM_VERIFY(trueVector.Size() == finiteElementSpace.GetTrueVSize(), "True vector has the wrong size.");
|
||||
|
||||
localVector.SetSize(finiteElementSpace.GetVSize());
|
||||
|
||||
const mfem::Operator *prolongation =
|
||||
finiteElementSpace.GetProlongationMatrix();
|
||||
const mfem::Operator *prolongation = finiteElementSpace.GetProlongationMatrix();
|
||||
|
||||
if (prolongation != nullptr) {
|
||||
prolongation->Mult(trueVector, localVector);
|
||||
@@ -67,16 +79,12 @@ namespace {
|
||||
const mfem::Vector &localVector,
|
||||
mfem::Vector &trueVector
|
||||
) {
|
||||
MFEM_VERIFY(
|
||||
localVector.Size() == finiteElementSpace.GetVSize(),
|
||||
"Local vector has the wrong size."
|
||||
);
|
||||
MFEM_VERIFY(localVector.Size() == finiteElementSpace.GetVSize(), "Local vector has the wrong size.");
|
||||
|
||||
trueVector.SetSize(finiteElementSpace.GetTrueVSize());
|
||||
trueVector = 0.0;
|
||||
trueVector = 0.0;
|
||||
|
||||
const mfem::Operator *prolongation =
|
||||
finiteElementSpace.GetProlongationMatrix();
|
||||
const mfem::Operator *prolongation = finiteElementSpace.GetProlongationMatrix();
|
||||
|
||||
if (prolongation != nullptr) {
|
||||
prolongation->MultTranspose(localVector, trueVector);
|
||||
@@ -85,65 +93,57 @@ namespace {
|
||||
}
|
||||
}
|
||||
|
||||
int get_eos_extra_order(
|
||||
const mean_field::physics::PolytropicBarotrope &barotrope
|
||||
) {
|
||||
const double extraOrder =
|
||||
(barotrope.polytropic_index() - 1.0) *
|
||||
static_cast<double>(
|
||||
mean_field::field::Enthalpy::Scalar::familyOrder
|
||||
);
|
||||
[[nodiscard]] int get_eos_extra_order(const mean_field::eos::Polytrope &equationOfState) {
|
||||
const double extraOrder = (equationOfState.polytropic_index() - 1.0) *
|
||||
static_cast<double>(mean_field::field::Enthalpy::Scalar::familyOrder);
|
||||
|
||||
MFEM_VERIFY(
|
||||
std::isfinite(extraOrder) && extraOrder >= 0.0 &&
|
||||
extraOrder <=
|
||||
static_cast<double>(std::numeric_limits<int>::max()),
|
||||
extraOrder <= static_cast<double>(std::numeric_limits<int>::max()),
|
||||
"The EOS effective polynomial order is invalid."
|
||||
);
|
||||
|
||||
return static_cast<int>(std::ceil(extraOrder));
|
||||
}
|
||||
|
||||
const mfem::IntegrationRule &get_eos_rule(
|
||||
[[nodiscard]] const mfem::IntegrationRule &get_eos_rule(
|
||||
const mean_field::fem::FEM &f,
|
||||
const mean_field::physics::PolytropicBarotrope &barotrope,
|
||||
const mean_field::eos::Polytrope &equationOfState,
|
||||
const mfem::FiniteElement &densityElement,
|
||||
const mfem::FiniteElement &enthalpyElement,
|
||||
const mfem::ElementTransformation &transformation
|
||||
) {
|
||||
using EnthalpyField =
|
||||
mean_field::field::Field<mean_field::field::Enthalpy>;
|
||||
using EnthalpyField = mean_field::field::Field<mean_field::field::Enthalpy>;
|
||||
|
||||
MFEM_VERIFY(
|
||||
densityElement.GetOrder() ==
|
||||
mean_field::field::Density::Scalar::familyOrder,
|
||||
"The prepared EOS test element does not match "
|
||||
"the registered density field."
|
||||
densityElement.GetOrder() == mean_field::field::Density::Scalar::familyOrder,
|
||||
"The prepared EOS test element does not match the registered density field."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
enthalpyElement.GetOrder() ==
|
||||
mean_field::field::Enthalpy::Scalar::familyOrder,
|
||||
"The prepared EOS trial element does not match "
|
||||
"the registered enthalpy field."
|
||||
enthalpyElement.GetOrder() == mean_field::field::Enthalpy::Scalar::familyOrder,
|
||||
"The prepared EOS trial element does not match the registered enthalpy field."
|
||||
);
|
||||
|
||||
const mean_field::quadrature::Query query = EnthalpyField::make_query<
|
||||
mean_field::field::Enthalpy::Form::EosClosureSource>(
|
||||
mean_field::quadrature::QuadratureRole::discretization,
|
||||
transformation.OrderW(),
|
||||
std::array<int, 1>{get_eos_extra_order(barotrope)},
|
||||
mean_field::utils::DOMAINS::STELLAR,
|
||||
mean_field::quadrature::MappingKind::general
|
||||
);
|
||||
/*
|
||||
* The quadrature Query still carries the legacy DOMAINS metadata.
|
||||
* Element support itself is no longer selected through that enum;
|
||||
* support is determined above through Density::Support + DomainSchema.
|
||||
* The Query metadata can be migrated independently with the quadrature
|
||||
* subsystem without changing this operator's algebra.
|
||||
*/
|
||||
const mean_field::quadrature::Query query =
|
||||
EnthalpyField::make_query<mean_field::field::Enthalpy::Form::EosClosureSource>(
|
||||
mean_field::quadrature::QuadratureRole::discretization, transformation.OrderW(),
|
||||
std::array<int, 1>{get_eos_extra_order(equationOfState)}, mean_field::utils::DOMAINS::STELLAR,
|
||||
mean_field::quadrature::MappingKind::general
|
||||
);
|
||||
|
||||
const auto resolution =
|
||||
f.quadratureFactory->get(query, transformation.GetGeometryType());
|
||||
const auto resolution = f.quadratureFactory->get(query, transformation.GetGeometryType());
|
||||
|
||||
MFEM_VERIFY(
|
||||
resolution.integration_rule != nullptr,
|
||||
"The quadrature policy did not return a prepared "
|
||||
"EOS-closure integration rule."
|
||||
"The quadrature policy did not return a prepared EOS-closure integration rule."
|
||||
);
|
||||
|
||||
return *resolution.integration_rule;
|
||||
@@ -151,91 +151,127 @@ namespace {
|
||||
} // namespace
|
||||
|
||||
namespace mean_field::operators {
|
||||
struct PreparedBarotropicClosureOperator::ConstructionData final {
|
||||
field::FieldDofMap densityMap;
|
||||
field::FieldDofMap enthalpyMap;
|
||||
field::FieldDofMap displacementMap;
|
||||
|
||||
explicit ConstructionData(const fem::FEM &f)
|
||||
: densityMap(
|
||||
field::make_field_dof_map<
|
||||
field::Density,
|
||||
DomainSchema>(*f.densityFes)
|
||||
),
|
||||
enthalpyMap(
|
||||
field::make_field_dof_map<
|
||||
field::Enthalpy,
|
||||
DomainSchema>(*f.enthalpyFes)
|
||||
),
|
||||
displacementMap(
|
||||
field::make_field_dof_map<
|
||||
field::Displacement,
|
||||
DomainSchema>(*f.displacementFes)
|
||||
) {
|
||||
}
|
||||
};
|
||||
|
||||
PreparedBarotropicClosureOperator::ConstructionData
|
||||
PreparedBarotropicClosureOperator::MakeConstructionData(const fem::FEM &f) {
|
||||
verify_required_spaces(f);
|
||||
return ConstructionData(f);
|
||||
}
|
||||
|
||||
PreparedBarotropicClosureOperator::PreparedBarotropicClosureOperator(
|
||||
const fem::FEM &f,
|
||||
const mapping::DomainMapperStateless &domainMapper,
|
||||
const physics::PolytropicBarotrope &barotrope
|
||||
const eos::Polytrope &equationOfState
|
||||
)
|
||||
: PreparedBarotropicClosureOperator(
|
||||
f,
|
||||
domainMapper,
|
||||
equationOfState,
|
||||
MakeConstructionData(f)
|
||||
) {
|
||||
}
|
||||
|
||||
PreparedBarotropicClosureOperator::PreparedBarotropicClosureOperator(
|
||||
const fem::FEM &f,
|
||||
const mapping::DomainMapperStateless &domainMapper,
|
||||
const eos::Polytrope &equationOfState,
|
||||
ConstructionData constructionData
|
||||
)
|
||||
: mfem::Operator(
|
||||
f.densityFes->GetTrueVSize(),
|
||||
f.densityFes->GetTrueVSize() + f.enthalpyFes->GetTrueVSize() +
|
||||
f.displacementFes->GetTrueVSize()
|
||||
constructionData.densityMap.reduced_size(),
|
||||
constructionData.densityMap.reduced_size() + constructionData.enthalpyMap.reduced_size() +
|
||||
constructionData.displacementMap.reduced_size()
|
||||
),
|
||||
m_fem(f),
|
||||
m_domainMapper(domainMapper),
|
||||
m_barotrope(barotrope),
|
||||
m_densitySize(f.densityFes->GetTrueVSize()),
|
||||
m_enthalpySize(f.enthalpyFes->GetTrueVSize()) {
|
||||
m_equationOfState(equationOfState),
|
||||
m_densityMap(std::move(constructionData.densityMap)),
|
||||
m_enthalpyMap(std::move(constructionData.enthalpyMap)),
|
||||
m_displacementMap(std::move(constructionData.displacementMap)),
|
||||
m_context(
|
||||
f,
|
||||
domainMapper,
|
||||
m_densityMap,
|
||||
m_enthalpyMap,
|
||||
m_displacementMap
|
||||
) {
|
||||
MFEM_VERIFY(
|
||||
m_fem.densityFes != nullptr,
|
||||
"PreparedBarotropicClosureOperator requires "
|
||||
"a density finite-element space."
|
||||
m_densityMap.full_size() == m_fem.densityFes->GetTrueVSize(),
|
||||
"The density FieldDofMap does not match the density finite-element space."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
m_enthalpyMap.full_size() == m_fem.enthalpyFes->GetTrueVSize(),
|
||||
"The enthalpy FieldDofMap does not match the enthalpy finite-element space."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
m_displacementMap.full_size() == m_fem.displacementFes->GetTrueVSize(),
|
||||
"The displacement FieldDofMap does not match the displacement finite-element space."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
m_fem.enthalpyFes != nullptr,
|
||||
"PreparedBarotropicClosureOperator requires "
|
||||
"an enthalpy finite-element space."
|
||||
);
|
||||
m_baseDensityTrue.SetSize(m_densityMap.full_size());
|
||||
m_baseEnthalpyTrue.SetSize(m_enthalpyMap.full_size());
|
||||
m_baseDisplacementTrue.SetSize(m_displacementMap.full_size());
|
||||
|
||||
MFEM_VERIFY(
|
||||
m_fem.displacementFes != nullptr,
|
||||
"PreparedBarotropicClosureOperator requires "
|
||||
"a displacement finite-element space."
|
||||
);
|
||||
m_densityVariationTrue.SetSize(m_densityMap.full_size());
|
||||
m_enthalpyVariationTrue.SetSize(m_enthalpyMap.full_size());
|
||||
m_displacementVariationTrue.SetSize(m_displacementMap.full_size());
|
||||
m_fullThermodynamicAction.SetSize(m_densityMap.full_size());
|
||||
m_fullDisplacementAction.SetSize(m_densityMap.full_size());
|
||||
m_fullResidual.SetSize(m_densityMap.full_size());
|
||||
|
||||
m_baseDensityTrue = 0.0;
|
||||
m_baseEnthalpyTrue = 0.0;
|
||||
m_baseDisplacementTrue = 0.0;
|
||||
m_densityVariationTrue = 0.0;
|
||||
m_enthalpyVariationTrue = 0.0;
|
||||
m_displacementVariationTrue = 0.0;
|
||||
m_fullThermodynamicAction = 0.0;
|
||||
m_fullDisplacementAction = 0.0;
|
||||
m_fullResidual = 0.0;
|
||||
}
|
||||
|
||||
void PreparedBarotropicClosureOperator::Prepare(
|
||||
const mfem::Vector &baseDensityTrue,
|
||||
const mfem::Vector &baseEnthalpyTrue,
|
||||
const mfem::Vector &displacementTrue
|
||||
PreparedBarotropicClosureReport PreparedBarotropicClosureOperator::Prepare(
|
||||
const context::barotropic::BarotropicClosureStateView &state,
|
||||
const context::barotropic::BarotropicClosureDependencies &dependencies
|
||||
) {
|
||||
MFEM_VERIFY(
|
||||
baseDensityTrue.Size() == m_densitySize,
|
||||
"PreparedBarotropicClosureOperator received a "
|
||||
"base-density vector with the wrong size."
|
||||
);
|
||||
PreparedBarotropicClosureReport report;
|
||||
report.contextReport = m_context.Prepare(state, dependencies);
|
||||
|
||||
MFEM_VERIFY(
|
||||
baseEnthalpyTrue.Size() == m_enthalpySize,
|
||||
"PreparedBarotropicClosureOperator received a "
|
||||
"base-enthalpy vector with the wrong size."
|
||||
);
|
||||
if (!report.contextReport.DidAnyWork() && m_isPrepared) {
|
||||
return report;
|
||||
}
|
||||
|
||||
MFEM_VERIFY(
|
||||
displacementTrue.Size() == m_fem.displacementFes->GetTrueVSize(),
|
||||
"PreparedBarotropicClosureOperator received a "
|
||||
"displacement vector with the wrong size."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
baseDensityTrue.Size() == m_fem.densityFes->GetTrueVSize(),
|
||||
"The base density true vector has the wrong size."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
baseEnthalpyTrue.Size() == m_fem.enthalpyFes->GetTrueVSize(),
|
||||
"The base enthalpy true vector has the wrong size."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
displacementTrue.Size() == m_fem.displacementFes->GetTrueVSize(),
|
||||
"The base displacement true vector has the wrong size."
|
||||
);
|
||||
|
||||
validate_finite_vector(
|
||||
baseDensityTrue, "PreparedBarotropicClosureOperator received a "
|
||||
"non-finite base-density value."
|
||||
);
|
||||
|
||||
validate_finite_vector(
|
||||
baseEnthalpyTrue, "PreparedBarotropicClosureOperator received a "
|
||||
"non-finite base-enthalpy value."
|
||||
);
|
||||
|
||||
validate_finite_vector(
|
||||
displacementTrue, "PreparedBarotropicClosureOperator received a "
|
||||
"non-finite displacement value."
|
||||
);
|
||||
/*
|
||||
* Canonical solver -> MFEM expansion. Unsupported density and
|
||||
* enthalpy DOFs are zero. Displacement is currently an identity map,
|
||||
* but it is deliberately routed through the same abstraction.
|
||||
*/
|
||||
m_densityMap.scatter(m_context.GetBaseDensity(), m_baseDensityTrue);
|
||||
m_enthalpyMap.scatter(m_context.GetBaseEnthalpy(), m_baseEnthalpyTrue);
|
||||
m_displacementMap.scatter(m_context.GetDisplacement(), m_baseDisplacementTrue);
|
||||
|
||||
m_isPrepared = false;
|
||||
m_elements.clear();
|
||||
@@ -245,17 +281,11 @@ namespace mean_field::operators {
|
||||
mfem::Vector baseEnthalpyLocal;
|
||||
mfem::Vector displacementLocal;
|
||||
|
||||
true_to_local(*m_fem.densityFes, baseDensityTrue, baseDensityLocal);
|
||||
true_to_local(*m_fem.densityFes, m_baseDensityTrue, baseDensityLocal);
|
||||
true_to_local(*m_fem.enthalpyFes, m_baseEnthalpyTrue, baseEnthalpyLocal);
|
||||
true_to_local(*m_fem.displacementFes, m_baseDisplacementTrue, displacementLocal);
|
||||
|
||||
true_to_local(*m_fem.enthalpyFes, baseEnthalpyTrue, baseEnthalpyLocal);
|
||||
|
||||
true_to_local(
|
||||
*m_fem.displacementFes, displacementTrue, displacementLocal
|
||||
);
|
||||
|
||||
mapping::DomainMapperStateless::Workspace workspace(
|
||||
m_fem.mesh->Dimension()
|
||||
);
|
||||
mapping::DomainMapperStateless::Workspace workspace(m_fem.mesh->Dimension());
|
||||
|
||||
mfem::Array<int> displacementDofs;
|
||||
mfem::Array<int> compactificationDofs;
|
||||
@@ -268,222 +298,134 @@ namespace mean_field::operators {
|
||||
mfem::Vector densityShape;
|
||||
mfem::Vector enthalpyShape;
|
||||
|
||||
const int vacuumAttribute = m_domainMapper.GetVacuumElementAttribute();
|
||||
|
||||
for (int elementId = 0; elementId < m_fem.mesh->GetNE(); ++elementId) {
|
||||
mfem::ElementTransformation *transformation =
|
||||
m_fem.mesh->GetElementTransformation(elementId);
|
||||
mfem::ElementTransformation *transformation = m_fem.mesh->GetElementTransformation(elementId);
|
||||
|
||||
MFEM_VERIFY(
|
||||
transformation != nullptr,
|
||||
"PreparedBarotropicClosureOperator received "
|
||||
"a null element transformation."
|
||||
transformation != nullptr, "PreparedBarotropicClosureOperator received a null element transformation."
|
||||
);
|
||||
|
||||
if (transformation->Attribute == vacuumAttribute) {
|
||||
if (!element_is_in_closure_support(transformation->Attribute)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
m_elements.emplace_back();
|
||||
ElementPAData &data = m_elements.back();
|
||||
ElementPAData &data = m_elements.back();
|
||||
|
||||
data.densityDofTransformation =
|
||||
m_fem.densityFes->GetElementDofs(elementId, data.densityDofs);
|
||||
|
||||
data.enthalpyDofTransformation =
|
||||
m_fem.enthalpyFes->GetElementDofs(elementId, data.enthalpyDofs);
|
||||
data.densityDofTransformation = m_fem.densityFes->GetElementDofs(elementId, data.densityDofs);
|
||||
data.enthalpyDofTransformation = m_fem.enthalpyFes->GetElementDofs(elementId, data.enthalpyDofs);
|
||||
|
||||
mfem::DofTransformation *displacementDofTransformation =
|
||||
m_fem.displacementFes->GetElementVDofs(
|
||||
elementId, displacementDofs
|
||||
);
|
||||
|
||||
m_fem.displacementFes->GetElementVDofs(elementId, displacementDofs);
|
||||
mfem::DofTransformation *compactificationDofTransformation =
|
||||
m_fem.compactificationFes->GetElementDofs(
|
||||
elementId, compactificationDofs
|
||||
);
|
||||
m_fem.compactificationFes->GetElementDofs(elementId, compactificationDofs);
|
||||
|
||||
baseDensityLocal.GetSubVector(data.densityDofs, elementBaseDensity);
|
||||
|
||||
baseEnthalpyLocal.GetSubVector(
|
||||
data.enthalpyDofs, elementBaseEnthalpy
|
||||
);
|
||||
|
||||
displacementLocal.GetSubVector(
|
||||
displacementDofs, elementDisplacement
|
||||
);
|
||||
|
||||
m_fem.compactificationCoordinate->GetSubVector(
|
||||
compactificationDofs, elementCompactification
|
||||
);
|
||||
baseEnthalpyLocal.GetSubVector(data.enthalpyDofs, elementBaseEnthalpy);
|
||||
displacementLocal.GetSubVector(displacementDofs, elementDisplacement);
|
||||
m_fem.compactificationCoordinate->GetSubVector(compactificationDofs, elementCompactification);
|
||||
|
||||
if (data.densityDofTransformation != nullptr) {
|
||||
data.densityDofTransformation->InvTransformPrimal(
|
||||
elementBaseDensity
|
||||
);
|
||||
data.densityDofTransformation->InvTransformPrimal(elementBaseDensity);
|
||||
}
|
||||
|
||||
if (data.enthalpyDofTransformation != nullptr) {
|
||||
data.enthalpyDofTransformation->InvTransformPrimal(
|
||||
elementBaseEnthalpy
|
||||
);
|
||||
data.enthalpyDofTransformation->InvTransformPrimal(elementBaseEnthalpy);
|
||||
}
|
||||
|
||||
if (displacementDofTransformation != nullptr) {
|
||||
displacementDofTransformation->InvTransformPrimal(
|
||||
elementDisplacement
|
||||
);
|
||||
displacementDofTransformation->InvTransformPrimal(elementDisplacement);
|
||||
}
|
||||
|
||||
if (compactificationDofTransformation != nullptr) {
|
||||
compactificationDofTransformation->InvTransformPrimal(
|
||||
elementCompactification
|
||||
);
|
||||
compactificationDofTransformation->InvTransformPrimal(elementCompactification);
|
||||
}
|
||||
|
||||
const mfem::FiniteElement &densityElement =
|
||||
*m_fem.densityFes->GetFE(elementId);
|
||||
|
||||
const mfem::FiniteElement &enthalpyElement =
|
||||
*m_fem.enthalpyFes->GetFE(elementId);
|
||||
|
||||
const mfem::FiniteElement &displacementElement =
|
||||
*m_fem.displacementFes->GetFE(elementId);
|
||||
|
||||
const mfem::FiniteElement &compactificationElement =
|
||||
*m_fem.compactificationFes->GetFE(elementId);
|
||||
const mfem::FiniteElement &densityElement = *m_fem.densityFes->GetFE(elementId);
|
||||
const mfem::FiniteElement &enthalpyElement = *m_fem.enthalpyFes->GetFE(elementId);
|
||||
const mfem::FiniteElement &displacementElement = *m_fem.displacementFes->GetFE(elementId);
|
||||
const mfem::FiniteElement &compactificationElement = *m_fem.compactificationFes->GetFE(elementId);
|
||||
|
||||
const mapping::ElementDisplacementData displacementData =
|
||||
mapping::ElementDisplacementDataFromElementVDofs(
|
||||
displacementElement, elementDisplacement
|
||||
);
|
||||
mapping::ElementDisplacementDataFromElementVDofs(displacementElement, elementDisplacement);
|
||||
|
||||
const mapping::ElementCompactificationData compactificationData(
|
||||
compactificationElement, elementCompactification
|
||||
);
|
||||
|
||||
const mapping::ElementMappingData mappingData{
|
||||
.displacement = displacementData,
|
||||
.compactification = compactificationData
|
||||
.displacement = displacementData, .compactification = compactificationData
|
||||
};
|
||||
|
||||
const mfem::IntegrationRule &integrationRule = get_eos_rule(
|
||||
m_fem, m_barotrope, densityElement, enthalpyElement,
|
||||
*transformation
|
||||
);
|
||||
const mfem::IntegrationRule &integrationRule =
|
||||
get_eos_rule(m_fem, m_equationOfState, densityElement, enthalpyElement, *transformation);
|
||||
|
||||
const int quadraturePointCount = integrationRule.GetNPoints();
|
||||
|
||||
const int densityDofCount = densityElement.GetDof();
|
||||
|
||||
const int enthalpyDofCount = enthalpyElement.GetDof();
|
||||
|
||||
data.densityBasis.SetSize(quadraturePointCount, densityDofCount);
|
||||
|
||||
data.enthalpyBasis.SetSize(quadraturePointCount, enthalpyDofCount);
|
||||
|
||||
data.weightedResidual.SetSize(quadraturePointCount);
|
||||
|
||||
data.quadratureWeights.SetSize(quadraturePointCount);
|
||||
|
||||
data.weightedEnthalpyDerivative.SetSize(quadraturePointCount);
|
||||
|
||||
densityShape.SetSize(densityDofCount);
|
||||
|
||||
enthalpyShape.SetSize(enthalpyDofCount);
|
||||
|
||||
for (int quadraturePoint = 0;
|
||||
quadraturePoint < quadraturePointCount; ++quadraturePoint) {
|
||||
const mfem::IntegrationPoint &integrationPoint =
|
||||
integrationRule.IntPoint(quadraturePoint);
|
||||
for (int quadraturePoint = 0; quadraturePoint < quadraturePointCount; ++quadraturePoint) {
|
||||
const mfem::IntegrationPoint &integrationPoint = integrationRule.IntPoint(quadraturePoint);
|
||||
|
||||
transformation->SetIntPoint(&integrationPoint);
|
||||
|
||||
mapping::VolumeMappingContext mappingContext;
|
||||
|
||||
const mapping::MappingStatus mappingStatus =
|
||||
m_domainMapper.EvaluateVolume(
|
||||
mappingData, *transformation, integrationPoint,
|
||||
workspace, mappingContext
|
||||
);
|
||||
const mapping::MappingStatus mappingStatus = m_domainMapper.EvaluateVolume(
|
||||
mappingData, *transformation, integrationPoint, workspace, mappingContext
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
mappingStatus == mapping::MappingStatus::valid,
|
||||
"Stateless mapping failed while preparing "
|
||||
"the barotropic closure operator. Element: "
|
||||
<< elementId
|
||||
<< ", attribute: " << transformation->Attribute
|
||||
<< ", quadrature point: " << quadraturePoint
|
||||
<< ", status: " << static_cast<int>(mappingStatus)
|
||||
"Stateless mapping failed while preparing the barotropic closure operator. Element: "
|
||||
<< elementId << ", attribute: " << transformation->Attribute
|
||||
<< ", quadrature point: " << quadraturePoint << ", status: " << static_cast<int>(mappingStatus)
|
||||
);
|
||||
|
||||
densityElement.CalcShape(integrationPoint, densityShape);
|
||||
|
||||
enthalpyElement.CalcShape(integrationPoint, enthalpyShape);
|
||||
|
||||
for (int densityDof = 0; densityDof < densityDofCount;
|
||||
++densityDof) {
|
||||
data.densityBasis(quadraturePoint, densityDof) =
|
||||
densityShape(densityDof);
|
||||
for (int densityDof = 0; densityDof < densityDofCount; ++densityDof) {
|
||||
data.densityBasis(quadraturePoint, densityDof) = densityShape(densityDof);
|
||||
}
|
||||
for (int enthalpyDof = 0; enthalpyDof < enthalpyDofCount; ++enthalpyDof) {
|
||||
data.enthalpyBasis(quadraturePoint, enthalpyDof) = enthalpyShape(enthalpyDof);
|
||||
}
|
||||
|
||||
for (int enthalpyDof = 0; enthalpyDof < enthalpyDofCount;
|
||||
++enthalpyDof) {
|
||||
data.enthalpyBasis(quadraturePoint, enthalpyDof) =
|
||||
enthalpyShape(enthalpyDof);
|
||||
}
|
||||
|
||||
const double density = elementBaseDensity * densityShape;
|
||||
|
||||
const double enthalpy = elementBaseEnthalpy * enthalpyShape;
|
||||
|
||||
const double quadratureWeight =
|
||||
mappingContext.quadrature.weight;
|
||||
|
||||
const double eosDensity =
|
||||
m_barotrope.density_from_enthalpy(enthalpy);
|
||||
|
||||
const double enthalpyDerivative =
|
||||
m_barotrope.density_derivative_from_enthalpy(enthalpy);
|
||||
const double density = elementBaseDensity * densityShape;
|
||||
const double enthalpy = elementBaseEnthalpy * enthalpyShape;
|
||||
const double quadratureWeight = mappingContext.quadrature.weight;
|
||||
const double eosDensity = m_equationOfState.density_from_enthalpy(enthalpy);
|
||||
const double enthalpyDerivative = m_equationOfState.density_derivative_from_enthalpy(enthalpy);
|
||||
|
||||
MFEM_VERIFY(
|
||||
std::isfinite(quadratureWeight) && quadratureWeight > 0.0 &&
|
||||
std::isfinite(eosDensity) &&
|
||||
std::isfinite(quadratureWeight) && quadratureWeight > 0.0 && std::isfinite(eosDensity) &&
|
||||
std::isfinite(enthalpyDerivative),
|
||||
"PreparedBarotropicClosureOperator "
|
||||
"encountered invalid quadrature data."
|
||||
"PreparedBarotropicClosureOperator encountered invalid quadrature data."
|
||||
);
|
||||
|
||||
data.quadratureWeights(quadraturePoint) = quadratureWeight;
|
||||
|
||||
data.weightedResidual(quadraturePoint) =
|
||||
quadratureWeight * (density - eosDensity);
|
||||
|
||||
data.weightedEnthalpyDerivative(quadraturePoint) =
|
||||
quadratureWeight * enthalpyDerivative;
|
||||
data.quadratureWeights(quadraturePoint) = quadratureWeight;
|
||||
data.weightedResidual(quadraturePoint) = quadratureWeight * (density - eosDensity);
|
||||
data.weightedEnthalpyDerivative(quadraturePoint) = quadratureWeight * enthalpyDerivative;
|
||||
}
|
||||
}
|
||||
|
||||
MFEM_VERIFY(
|
||||
!m_elements.empty(), "PreparedBarotropicClosureOperator found no "
|
||||
"stellar elements."
|
||||
);
|
||||
MFEM_VERIFY(!m_elements.empty(), "PreparedBarotropicClosureOperator found no elements in Density::Support.");
|
||||
|
||||
m_baseDensityTrue = baseDensityTrue;
|
||||
m_baseEnthalpyTrue = baseEnthalpyTrue;
|
||||
m_baseDisplacementTrue = displacementTrue;
|
||||
|
||||
m_isPrepared = true;
|
||||
m_isPrepared = true;
|
||||
++m_preparationCount;
|
||||
report.preparedElementData = true;
|
||||
return report;
|
||||
}
|
||||
|
||||
void PreparedBarotropicClosureOperator::BuildResidual(
|
||||
mfem::Vector &residual
|
||||
) const {
|
||||
MFEM_VERIFY(
|
||||
m_isPrepared, "PreparedBarotropicClosureOperator must be "
|
||||
"prepared before BuildResidual is called."
|
||||
);
|
||||
void PreparedBarotropicClosureOperator::BuildResidual(mfem::Vector &residual) const {
|
||||
VerifyPrepared();
|
||||
|
||||
mfem::Vector localResidual(m_fem.densityFes->GetVSize());
|
||||
localResidual = 0.0;
|
||||
@@ -492,10 +434,7 @@ namespace mean_field::operators {
|
||||
|
||||
for (const ElementPAData &data : m_elements) {
|
||||
elementResidual.SetSize(data.densityDofs.Size());
|
||||
|
||||
data.densityBasis.MultTranspose(
|
||||
data.weightedResidual, elementResidual
|
||||
);
|
||||
data.densityBasis.MultTranspose(data.weightedResidual, elementResidual);
|
||||
|
||||
if (data.densityDofTransformation != nullptr) {
|
||||
data.densityDofTransformation->TransformDual(elementResidual);
|
||||
@@ -504,53 +443,56 @@ namespace mean_field::operators {
|
||||
localResidual.AddElementVector(data.densityDofs, elementResidual);
|
||||
}
|
||||
|
||||
local_to_true(*m_fem.densityFes, localResidual, residual);
|
||||
local_to_true(*m_fem.densityFes, localResidual, m_fullResidual);
|
||||
residual.SetSize(m_densityMap.reduced_size());
|
||||
m_densityMap.gather(m_fullResidual, residual);
|
||||
}
|
||||
|
||||
void PreparedBarotropicClosureOperator::Mult(
|
||||
const mfem::Vector &densityVariationTrue,
|
||||
const mfem::Vector &enthalpyVariationTrue,
|
||||
const mfem::Vector &displacementVariationTrue,
|
||||
const mfem::Vector &densityVariation,
|
||||
const mfem::Vector &enthalpyVariation,
|
||||
const mfem::Vector &displacementVariation,
|
||||
mfem::Vector &action
|
||||
) const {
|
||||
VerifyPrepared();
|
||||
|
||||
MFEM_VERIFY(
|
||||
densityVariationTrue.Size() == m_densitySize,
|
||||
"The density-variation true vector has "
|
||||
"the wrong size."
|
||||
densityVariation.Size() == m_densityMap.reduced_size(),
|
||||
"The supported density-variation vector has the wrong size."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
enthalpyVariationTrue.Size() == m_enthalpySize,
|
||||
"The enthalpy-variation true vector has "
|
||||
"the wrong size."
|
||||
enthalpyVariation.Size() == m_enthalpyMap.reduced_size(),
|
||||
"The supported enthalpy-variation vector has the wrong size."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
displacementVariationTrue.Size() ==
|
||||
m_fem.displacementFes->GetTrueVSize(),
|
||||
"The displacement-variation true vector has "
|
||||
"the wrong size."
|
||||
displacementVariation.Size() == m_displacementMap.reduced_size(),
|
||||
"The supported displacement-variation vector has the wrong size."
|
||||
);
|
||||
|
||||
Mult(densityVariationTrue, enthalpyVariationTrue, action);
|
||||
validate_finite_vector(densityVariation, "The density variation contains a non-finite value.");
|
||||
validate_finite_vector(enthalpyVariation, "The enthalpy variation contains a non-finite value.");
|
||||
validate_finite_vector(displacementVariation, "The displacement variation contains a non-finite value.");
|
||||
|
||||
mfem::Vector displacementAction;
|
||||
m_densityMap.scatter(densityVariation, m_densityVariationTrue);
|
||||
m_enthalpyMap.scatter(enthalpyVariation, m_enthalpyVariationTrue);
|
||||
m_displacementMap.scatter(displacementVariation, m_displacementVariationTrue);
|
||||
|
||||
ApplyThermodynamicActionFull(m_densityVariationTrue, m_enthalpyVariationTrue, m_fullThermodynamicAction);
|
||||
|
||||
kernels::apply_barotropic_closure_displacement_action(
|
||||
m_fem, m_domainMapper, m_barotrope, m_baseDensityTrue,
|
||||
m_baseEnthalpyTrue, m_baseDisplacementTrue,
|
||||
displacementVariationTrue, displacementAction
|
||||
m_fem, m_domainMapper, m_equationOfState, m_baseDensityTrue, m_baseEnthalpyTrue, m_baseDisplacementTrue,
|
||||
m_displacementVariationTrue, m_fullDisplacementAction
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
displacementAction.Size() == m_densitySize,
|
||||
"The barotropic-closure displacement action "
|
||||
"returned a vector with the wrong size."
|
||||
m_fullThermodynamicAction.Size() == m_densityMap.full_size() &&
|
||||
m_fullDisplacementAction.Size() == m_densityMap.full_size(),
|
||||
"A full barotropic-closure Jacobian action has an incompatible density-space size."
|
||||
);
|
||||
|
||||
action += displacementAction;
|
||||
m_fullThermodynamicAction += m_fullDisplacementAction;
|
||||
action.SetSize(m_densityMap.reduced_size());
|
||||
m_densityMap.gather(m_fullThermodynamicAction, action);
|
||||
}
|
||||
|
||||
void PreparedBarotropicClosureOperator::Mult(
|
||||
@@ -559,70 +501,40 @@ namespace mean_field::operators {
|
||||
) const {
|
||||
VerifyPrepared();
|
||||
|
||||
const int displacementSize = m_fem.displacementFes->GetTrueVSize();
|
||||
|
||||
const int combinedSize =
|
||||
m_densitySize + m_enthalpySize + displacementSize;
|
||||
|
||||
MFEM_VERIFY(
|
||||
combinedVariation.Size() == combinedSize,
|
||||
"The combined barotropic-closure variation "
|
||||
"vector has the wrong size. Expected "
|
||||
<< combinedSize << " entries but received "
|
||||
<< combinedVariation.Size() << "."
|
||||
combinedVariation.Size() == Width(), "The packed supported barotropic-closure variation has the wrong size."
|
||||
);
|
||||
|
||||
mfem::real_t *combinedData =
|
||||
const_cast<mfem::real_t *>(combinedVariation.HostRead());
|
||||
mfem::real_t *combinedData = const_cast<mfem::real_t *>(combinedVariation.HostRead());
|
||||
|
||||
const mfem::Vector densityVariationTrue(combinedData, m_densitySize);
|
||||
const int densitySize = m_densityMap.reduced_size();
|
||||
const int enthalpySize = m_enthalpyMap.reduced_size();
|
||||
const int displacementSize = m_displacementMap.reduced_size();
|
||||
|
||||
const mfem::Vector enthalpyVariationTrue(
|
||||
combinedData + m_densitySize, m_enthalpySize
|
||||
);
|
||||
const mfem::Vector densityVariation(combinedData, densitySize);
|
||||
const mfem::Vector enthalpyVariation(combinedData + densitySize, enthalpySize);
|
||||
const mfem::Vector displacementVariation(combinedData + densitySize + enthalpySize, displacementSize);
|
||||
|
||||
const mfem::Vector displacementVariationTrue(
|
||||
combinedData + m_densitySize + m_enthalpySize, displacementSize
|
||||
);
|
||||
|
||||
Mult(
|
||||
densityVariationTrue, enthalpyVariationTrue,
|
||||
displacementVariationTrue, action
|
||||
);
|
||||
Mult(densityVariation, enthalpyVariation, displacementVariation, action);
|
||||
}
|
||||
|
||||
void PreparedBarotropicClosureOperator::Mult(
|
||||
void PreparedBarotropicClosureOperator::ApplyThermodynamicActionFull(
|
||||
const mfem::Vector &densityVariationTrue,
|
||||
const mfem::Vector &enthalpyVariationTrue,
|
||||
mfem::Vector &action
|
||||
mfem::Vector &actionTrue
|
||||
) const {
|
||||
MFEM_VERIFY(
|
||||
m_isPrepared, "PreparedBarotropicClosureOperator must be "
|
||||
"prepared before Mult is called."
|
||||
densityVariationTrue.Size() == m_densityMap.full_size(), "The full density variation has the wrong size."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
densityVariationTrue.Size() == m_densitySize,
|
||||
"PreparedBarotropicClosureOperator received a "
|
||||
"density variation with the wrong size."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
enthalpyVariationTrue.Size() == m_enthalpySize,
|
||||
"PreparedBarotropicClosureOperator received an "
|
||||
"enthalpy variation with the wrong size."
|
||||
enthalpyVariationTrue.Size() == m_enthalpyMap.full_size(), "The full enthalpy variation has the wrong size."
|
||||
);
|
||||
|
||||
mfem::Vector densityVariationLocal;
|
||||
mfem::Vector enthalpyVariationLocal;
|
||||
|
||||
true_to_local(
|
||||
*m_fem.densityFes, densityVariationTrue, densityVariationLocal
|
||||
);
|
||||
|
||||
true_to_local(
|
||||
*m_fem.enthalpyFes, enthalpyVariationTrue, enthalpyVariationLocal
|
||||
);
|
||||
true_to_local(*m_fem.densityFes, densityVariationTrue, densityVariationLocal);
|
||||
true_to_local(*m_fem.enthalpyFes, enthalpyVariationTrue, enthalpyVariationLocal);
|
||||
|
||||
mfem::Vector localAction(m_fem.densityFes->GetVSize());
|
||||
localAction = 0.0;
|
||||
@@ -635,51 +547,30 @@ namespace mean_field::operators {
|
||||
mfem::Vector elementAction;
|
||||
|
||||
for (const ElementPAData &data : m_elements) {
|
||||
densityVariationLocal.GetSubVector(
|
||||
data.densityDofs, elementDensityVariation
|
||||
);
|
||||
|
||||
enthalpyVariationLocal.GetSubVector(
|
||||
data.enthalpyDofs, elementEnthalpyVariation
|
||||
);
|
||||
densityVariationLocal.GetSubVector(data.densityDofs, elementDensityVariation);
|
||||
enthalpyVariationLocal.GetSubVector(data.enthalpyDofs, elementEnthalpyVariation);
|
||||
|
||||
if (data.densityDofTransformation != nullptr) {
|
||||
data.densityDofTransformation->InvTransformPrimal(
|
||||
elementDensityVariation
|
||||
);
|
||||
data.densityDofTransformation->InvTransformPrimal(elementDensityVariation);
|
||||
}
|
||||
|
||||
if (data.enthalpyDofTransformation != nullptr) {
|
||||
data.enthalpyDofTransformation->InvTransformPrimal(
|
||||
elementEnthalpyVariation
|
||||
);
|
||||
data.enthalpyDofTransformation->InvTransformPrimal(elementEnthalpyVariation);
|
||||
}
|
||||
|
||||
quadratureDensityVariation.SetSize(data.quadratureWeights.Size());
|
||||
|
||||
quadratureEnthalpyVariation.SetSize(data.quadratureWeights.Size());
|
||||
|
||||
quadratureAction.SetSize(data.quadratureWeights.Size());
|
||||
|
||||
data.densityBasis.Mult(
|
||||
elementDensityVariation, quadratureDensityVariation
|
||||
);
|
||||
data.densityBasis.Mult(elementDensityVariation, quadratureDensityVariation);
|
||||
data.enthalpyBasis.Mult(elementEnthalpyVariation, quadratureEnthalpyVariation);
|
||||
|
||||
data.enthalpyBasis.Mult(
|
||||
elementEnthalpyVariation, quadratureEnthalpyVariation
|
||||
);
|
||||
|
||||
for (int quadraturePoint = 0;
|
||||
quadraturePoint < quadratureAction.Size(); ++quadraturePoint) {
|
||||
for (int quadraturePoint = 0; quadraturePoint < quadratureAction.Size(); ++quadraturePoint) {
|
||||
quadratureAction(quadraturePoint) =
|
||||
data.quadratureWeights(quadraturePoint) *
|
||||
quadratureDensityVariation(quadraturePoint) -
|
||||
data.weightedEnthalpyDerivative(quadraturePoint) *
|
||||
quadratureEnthalpyVariation(quadraturePoint);
|
||||
data.quadratureWeights(quadraturePoint) * quadratureDensityVariation(quadraturePoint) -
|
||||
data.weightedEnthalpyDerivative(quadraturePoint) * quadratureEnthalpyVariation(quadraturePoint);
|
||||
}
|
||||
|
||||
elementAction.SetSize(data.densityDofs.Size());
|
||||
|
||||
data.densityBasis.MultTranspose(quadratureAction, elementAction);
|
||||
|
||||
if (data.densityDofTransformation != nullptr) {
|
||||
@@ -689,30 +580,42 @@ namespace mean_field::operators {
|
||||
localAction.AddElementVector(data.densityDofs, elementAction);
|
||||
}
|
||||
|
||||
local_to_true(*m_fem.densityFes, localAction, action);
|
||||
local_to_true(*m_fem.densityFes, localAction, actionTrue);
|
||||
}
|
||||
|
||||
bool PreparedBarotropicClosureOperator::IsPrepared() const noexcept {
|
||||
return m_isPrepared;
|
||||
return m_isPrepared && m_context.IsPrepared();
|
||||
}
|
||||
|
||||
std::uint64_t
|
||||
PreparedBarotropicClosureOperator::GetPreparationCount() const noexcept {
|
||||
std::uint64_t PreparedBarotropicClosureOperator::GetPreparationCount() const noexcept {
|
||||
return m_preparationCount;
|
||||
}
|
||||
|
||||
int PreparedBarotropicClosureOperator::GetDensitySize() const noexcept {
|
||||
return m_densitySize;
|
||||
return m_densityMap.reduced_size();
|
||||
}
|
||||
|
||||
int PreparedBarotropicClosureOperator::GetEnthalpySize() const noexcept {
|
||||
return m_enthalpySize;
|
||||
return m_enthalpyMap.reduced_size();
|
||||
}
|
||||
|
||||
int PreparedBarotropicClosureOperator::GetDisplacementSize() const noexcept {
|
||||
return m_displacementMap.reduced_size();
|
||||
}
|
||||
|
||||
const context::barotropic::BarotropicClosureLinearizationContext &
|
||||
PreparedBarotropicClosureOperator::GetContext() const noexcept {
|
||||
return m_context;
|
||||
}
|
||||
|
||||
const context::barotropic::BarotropicClosurePreparationStatistics &
|
||||
PreparedBarotropicClosureOperator::GetContextPreparationStatistics() const noexcept {
|
||||
return m_context.GetPreparationStatistics();
|
||||
}
|
||||
|
||||
void PreparedBarotropicClosureOperator::VerifyPrepared() const {
|
||||
MFEM_VERIFY(
|
||||
m_isPrepared, "PreparedBarotropicClosureOperator must be "
|
||||
"prepared before this operation is called."
|
||||
m_isPrepared, "PreparedBarotropicClosureOperator must be prepared before this operation is called."
|
||||
);
|
||||
}
|
||||
} // namespace mean_field::operators
|
||||
554
libmeanfield/impl/operators/prepared_displacement_operator.cpp
Normal file
554
libmeanfield/impl/operators/prepared_displacement_operator.cpp
Normal file
@@ -0,0 +1,554 @@
|
||||
module;
|
||||
|
||||
#include <mfem.hpp>
|
||||
|
||||
module mean_field;
|
||||
|
||||
import :operators.prepared_displacement_residual;
|
||||
|
||||
namespace {
|
||||
using Dependencies = mean_field::operators::DisplacementResidualDependencies;
|
||||
|
||||
[[nodiscard]] mean_field::operators::context::pressure_force::PressureForceDependencies
|
||||
make_pressure_dependencies(const Dependencies &dependencies) {
|
||||
return {
|
||||
.discretization =
|
||||
{.identity = dependencies.discretization.identity, .revision = dependencies.discretization.revision},
|
||||
.enthalpy = {.identity = dependencies.enthalpy.identity, .revision = dependencies.enthalpy.revision},
|
||||
.displacement = {
|
||||
.identity = dependencies.displacement.identity, .revision = dependencies.displacement.revision
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
[[nodiscard]] mean_field::operators::context::rotational_displacement_force::RotationalDisplacementForceDependencies
|
||||
make_rotational_dependencies(const Dependencies &dependencies) {
|
||||
return {
|
||||
.discretization =
|
||||
{.identity = dependencies.discretization.identity, .revision = dependencies.discretization.revision},
|
||||
.density = {.identity = dependencies.density.identity, .revision = dependencies.density.revision},
|
||||
.displacement =
|
||||
{.identity = dependencies.displacement.identity, .revision = dependencies.displacement.revision},
|
||||
.rotation = {.identity = dependencies.rotation.identity, .revision = dependencies.rotation.revision}
|
||||
};
|
||||
}
|
||||
|
||||
void validate_shared_gravity_revisions(
|
||||
const mean_field::operators::context::gravity_field::GravityFieldLinearizationContext &gravityContext,
|
||||
const Dependencies &dependencies
|
||||
) {
|
||||
MFEM_VERIFY(
|
||||
gravityContext.IsPrepared(), "PreparedDisplacementResidualOperator requires the shared "
|
||||
"gravity linearization context to be prepared first."
|
||||
);
|
||||
|
||||
const mean_field::operators::context::gravity_field::GravityFieldRevisions &gravityRevisions =
|
||||
gravityContext.GetRevisions();
|
||||
|
||||
MFEM_VERIFY(
|
||||
gravityRevisions.discretization.value == dependencies.discretization.revision &&
|
||||
gravityRevisions.density.value == dependencies.density.revision &&
|
||||
gravityRevisions.displacement.value == dependencies.displacement.revision &&
|
||||
gravityRevisions.gravity_gradient.value == dependencies.gravityGradient.revision,
|
||||
"PreparedDisplacementResidualOperator received dependency "
|
||||
"revisions that do not match the shared gravity context."
|
||||
);
|
||||
}
|
||||
|
||||
void validate_shared_identity_transition(
|
||||
const mean_field::operators::DisplacementResidualDependencyStamp &prepared,
|
||||
const mean_field::operators::DisplacementResidualDependencyStamp &requested,
|
||||
const char *message
|
||||
) {
|
||||
MFEM_VERIFY(prepared.identity == requested.identity || prepared.revision != requested.revision, message);
|
||||
}
|
||||
|
||||
void add_compatible(
|
||||
mfem::Vector &destination,
|
||||
const mfem::Vector &source,
|
||||
const char *message
|
||||
) {
|
||||
MFEM_VERIFY(destination.Size() == source.Size(), message);
|
||||
destination += source;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace mean_field::operators {
|
||||
PreparedDisplacementResidualOperator::PreparedDisplacementResidualOperator(
|
||||
const fem::FEM &f,
|
||||
const mapping::DomainMapperStateless &domainMapper,
|
||||
const eos::Polytrope &barotrope,
|
||||
const context::gravity_field::GravityFieldLinearizationContext &gravityContext
|
||||
)
|
||||
: m_fem(f),
|
||||
m_domainMapper(domainMapper),
|
||||
m_gravityContext(gravityContext),
|
||||
m_pressureOperator(
|
||||
f,
|
||||
domainMapper,
|
||||
barotrope
|
||||
),
|
||||
m_gravityOperator(
|
||||
f,
|
||||
domainMapper,
|
||||
gravityContext
|
||||
),
|
||||
m_rotationalOperator(
|
||||
f,
|
||||
domainMapper
|
||||
) {
|
||||
MFEM_VERIFY(m_fem.mesh != nullptr, "PreparedDisplacementResidualOperator requires a mesh.");
|
||||
|
||||
MFEM_VERIFY(
|
||||
m_fem.densityFes != nullptr && m_fem.displacementFes != nullptr && m_fem.gravityFluxFes != nullptr &&
|
||||
m_fem.enthalpyFes != nullptr,
|
||||
"PreparedDisplacementResidualOperator requires density, "
|
||||
"displacement, gravity-gradient, and enthalpy finite-element "
|
||||
"spaces."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
m_domainMapper.GetDimension() == m_fem.mesh->Dimension(),
|
||||
"PreparedDisplacementResidualOperator received a mapper with "
|
||||
"the wrong dimension."
|
||||
);
|
||||
}
|
||||
|
||||
PreparedDisplacementResidualReport PreparedDisplacementResidualOperator::Prepare(
|
||||
const DisplacementResidualStateView &state,
|
||||
const DisplacementResidualDependencies &dependencies,
|
||||
const physics::RigidRotation &rotation
|
||||
) {
|
||||
validate_shared_gravity_revisions(m_gravityContext, dependencies);
|
||||
|
||||
if (m_isPrepared) {
|
||||
/*
|
||||
* GravityFieldLinearizationContext currently tracks revisions
|
||||
* but not semantic identities. Require an identity replacement
|
||||
* to be accompanied by a visible revision change so it cannot
|
||||
* silently reuse the old shared density, geometry, or flux.
|
||||
*/
|
||||
validate_shared_identity_transition(
|
||||
m_preparedDependencies.discretization, dependencies.discretization,
|
||||
"A new displacement-residual discretization identity must "
|
||||
"also change the shared gravity revision."
|
||||
);
|
||||
|
||||
validate_shared_identity_transition(
|
||||
m_preparedDependencies.density, dependencies.density,
|
||||
"A new displacement-residual density identity must also "
|
||||
"change the shared gravity revision."
|
||||
);
|
||||
|
||||
validate_shared_identity_transition(
|
||||
m_preparedDependencies.displacement, dependencies.displacement,
|
||||
"A new displacement-residual displacement identity must "
|
||||
"also change the shared gravity revision."
|
||||
);
|
||||
|
||||
validate_shared_identity_transition(
|
||||
m_preparedDependencies.gravityGradient, dependencies.gravityGradient,
|
||||
"A new displacement-residual gravity-gradient identity "
|
||||
"must also change the shared gravity revision."
|
||||
);
|
||||
}
|
||||
|
||||
const mfem::Vector &density = m_gravityContext.GetDensity();
|
||||
const mfem::Vector &displacement = m_gravityContext.GetGeometryContext().GetDisplacement();
|
||||
|
||||
m_isPrepared = false;
|
||||
|
||||
PreparedDisplacementResidualReport report;
|
||||
|
||||
report.pressure = m_pressureOperator.Prepare(
|
||||
{.enthalpy = state.enthalpy, .displacement = displacement}, make_pressure_dependencies(dependencies)
|
||||
);
|
||||
|
||||
report.gravity = m_gravityOperator.Prepare();
|
||||
|
||||
report.rotation = m_rotationalOperator.Prepare(
|
||||
{.density = density, .displacement = displacement}, make_rotational_dependencies(dependencies), rotation
|
||||
);
|
||||
|
||||
if (report.DidAnyChildWork() || m_cachedResidual.Size() != m_fem.displacementFes->GetTrueVSize()) {
|
||||
AssembleResidual();
|
||||
report.assembledResidual = true;
|
||||
}
|
||||
|
||||
MFEM_VERIFY(
|
||||
m_cachedResidual.Size() == m_fem.displacementFes->GetTrueVSize(),
|
||||
"PreparedDisplacementResidualOperator produced a cached "
|
||||
"residual with the wrong size."
|
||||
);
|
||||
|
||||
m_preparedDependencies = dependencies;
|
||||
m_isPrepared = true;
|
||||
return report;
|
||||
}
|
||||
|
||||
void PreparedDisplacementResidualOperator::AssembleResidual() {
|
||||
mfem::Vector pressureResidual;
|
||||
mfem::Vector gravityResidual;
|
||||
mfem::Vector rotationalResidual;
|
||||
|
||||
m_pressureOperator.BuildResidual(pressureResidual);
|
||||
m_gravityOperator.BuildResidual(gravityResidual);
|
||||
m_rotationalOperator.BuildResidual(rotationalResidual);
|
||||
|
||||
m_cachedResidual = pressureResidual;
|
||||
|
||||
add_compatible(
|
||||
m_cachedResidual, gravityResidual,
|
||||
"Cannot combine pressure and gravity displacement residuals "
|
||||
"with different sizes."
|
||||
);
|
||||
|
||||
add_compatible(
|
||||
m_cachedResidual, rotationalResidual,
|
||||
"Cannot combine mechanical displacement residuals with "
|
||||
"different sizes."
|
||||
);
|
||||
|
||||
++m_residualPreparationCount;
|
||||
}
|
||||
|
||||
void PreparedDisplacementResidualOperator::BuildResidual(mfem::Vector &residual) const {
|
||||
VerifyPrepared();
|
||||
residual = m_cachedResidual;
|
||||
++m_residualApplicationCount;
|
||||
}
|
||||
|
||||
void PreparedDisplacementResidualOperator::ApplyDensityJacobianAction(
|
||||
const mfem::Vector &densityVariation,
|
||||
mfem::Vector &action
|
||||
) const {
|
||||
VerifyPrepared();
|
||||
|
||||
mfem::Vector rotationalAction;
|
||||
|
||||
m_gravityOperator.ApplyDensityJacobianAction(densityVariation, action);
|
||||
|
||||
m_rotationalOperator.ApplyDensityJacobianAction(densityVariation, rotationalAction);
|
||||
|
||||
add_compatible(
|
||||
action, rotationalAction,
|
||||
"Cannot combine gravity and rotation density-column actions "
|
||||
"with different sizes."
|
||||
);
|
||||
|
||||
++m_actionStatistics.densityApplications;
|
||||
}
|
||||
|
||||
void PreparedDisplacementResidualOperator::ApplyDisplacementJacobianAction(
|
||||
const mfem::Vector &displacementVariation,
|
||||
mfem::Vector &action
|
||||
) const {
|
||||
VerifyPrepared();
|
||||
|
||||
mfem::Vector gravityAction;
|
||||
mfem::Vector rotationalAction;
|
||||
|
||||
m_pressureOperator.ApplyDisplacementJacobianAction(displacementVariation, action);
|
||||
|
||||
m_gravityOperator.ApplyDisplacementJacobianAction(displacementVariation, gravityAction);
|
||||
|
||||
m_rotationalOperator.ApplyDisplacementJacobianAction(displacementVariation, rotationalAction);
|
||||
|
||||
add_compatible(
|
||||
action, gravityAction,
|
||||
"Cannot combine pressure and gravity displacement-column "
|
||||
"actions with different sizes."
|
||||
);
|
||||
|
||||
add_compatible(
|
||||
action, rotationalAction,
|
||||
"Cannot combine mechanical displacement-column actions with "
|
||||
"different sizes."
|
||||
);
|
||||
|
||||
++m_actionStatistics.displacementApplications;
|
||||
}
|
||||
|
||||
void PreparedDisplacementResidualOperator::ApplyGravityGradientJacobianAction(
|
||||
const mfem::Vector &gravityGradientVariation,
|
||||
mfem::Vector &action
|
||||
) const {
|
||||
VerifyPrepared();
|
||||
|
||||
m_gravityOperator.ApplyGravityGradientJacobianAction(gravityGradientVariation, action);
|
||||
|
||||
++m_actionStatistics.gravityGradientApplications;
|
||||
}
|
||||
|
||||
void PreparedDisplacementResidualOperator::ApplyEnthalpyJacobianAction(
|
||||
const mfem::Vector &enthalpyVariation,
|
||||
mfem::Vector &action
|
||||
) const {
|
||||
VerifyPrepared();
|
||||
|
||||
m_pressureOperator.ApplyEnthalpyJacobianAction(enthalpyVariation, action);
|
||||
|
||||
++m_actionStatistics.enthalpyApplications;
|
||||
}
|
||||
|
||||
void PreparedDisplacementResidualOperator::ApplyCompleteJacobianAction(
|
||||
const mfem::Vector &densityVariation,
|
||||
const mfem::Vector &displacementVariation,
|
||||
const mfem::Vector &gravityGradientVariation,
|
||||
const mfem::Vector &enthalpyVariation,
|
||||
mfem::Vector &action
|
||||
) const {
|
||||
VerifyPrepared();
|
||||
|
||||
mfem::Vector gravityAction;
|
||||
mfem::Vector rotationalAction;
|
||||
|
||||
m_pressureOperator.ApplyCompleteJacobianAction(enthalpyVariation, displacementVariation, action);
|
||||
|
||||
m_gravityOperator.ApplyCompleteJacobianAction(
|
||||
densityVariation, displacementVariation, gravityGradientVariation, gravityAction
|
||||
);
|
||||
|
||||
m_rotationalOperator.ApplyCompleteJacobianAction(densityVariation, displacementVariation, rotationalAction);
|
||||
|
||||
add_compatible(
|
||||
action, gravityAction,
|
||||
"Cannot combine pressure and gravity complete Jacobian "
|
||||
"actions with different sizes."
|
||||
);
|
||||
|
||||
add_compatible(
|
||||
action, rotationalAction,
|
||||
"Cannot combine mechanical complete Jacobian actions with "
|
||||
"different sizes."
|
||||
);
|
||||
|
||||
++m_actionStatistics.densityApplications;
|
||||
++m_actionStatistics.displacementApplications;
|
||||
++m_actionStatistics.gravityGradientApplications;
|
||||
++m_actionStatistics.enthalpyApplications;
|
||||
++m_actionStatistics.completeApplications;
|
||||
}
|
||||
|
||||
bool PreparedDisplacementResidualOperator::IsPrepared() const noexcept {
|
||||
if (!m_isPrepared || !m_pressureOperator.IsPrepared() || !m_gravityOperator.IsPrepared() ||
|
||||
!m_rotationalOperator.IsPrepared() || !m_gravityContext.IsPrepared()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const context::gravity_field::GravityFieldRevisions &gravityRevisions = m_gravityContext.GetRevisions();
|
||||
|
||||
return gravityRevisions.discretization.value == m_preparedDependencies.discretization.revision &&
|
||||
gravityRevisions.density.value == m_preparedDependencies.density.revision &&
|
||||
gravityRevisions.displacement.value == m_preparedDependencies.displacement.revision &&
|
||||
gravityRevisions.gravity_gradient.value == m_preparedDependencies.gravityGradient.revision;
|
||||
}
|
||||
|
||||
std::uint64_t PreparedDisplacementResidualOperator::GetResidualPreparationCount() const noexcept {
|
||||
return m_residualPreparationCount;
|
||||
}
|
||||
|
||||
std::uint64_t PreparedDisplacementResidualOperator::GetResidualApplicationCount() const noexcept {
|
||||
return m_residualApplicationCount;
|
||||
}
|
||||
|
||||
const PreparedDisplacementResidualActionStatistics &
|
||||
PreparedDisplacementResidualOperator::GetActionStatistics() const noexcept {
|
||||
return m_actionStatistics;
|
||||
}
|
||||
|
||||
const PreparedPressureForceOperator &PreparedDisplacementResidualOperator::GetPressureOperator() const noexcept {
|
||||
return m_pressureOperator;
|
||||
}
|
||||
|
||||
const PreparedGravityDisplacementForceOperator &
|
||||
PreparedDisplacementResidualOperator::GetGravityOperator() const noexcept {
|
||||
return m_gravityOperator;
|
||||
}
|
||||
|
||||
const PreparedRotationalDisplacementForceOperator &
|
||||
PreparedDisplacementResidualOperator::GetRotationalOperator() const noexcept {
|
||||
return m_rotationalOperator;
|
||||
}
|
||||
|
||||
const fem::FEM &PreparedDisplacementResidualOperator::GetFEM() const noexcept {
|
||||
return m_fem;
|
||||
}
|
||||
|
||||
const context::gravity_field::GravityFieldLinearizationContext &
|
||||
PreparedDisplacementResidualOperator::GetGravityContext() const noexcept {
|
||||
return m_gravityContext;
|
||||
}
|
||||
|
||||
void PreparedDisplacementResidualOperator::VerifyPrepared() const {
|
||||
MFEM_VERIFY(
|
||||
IsPrepared(), "PreparedDisplacementResidualOperator must be prepared for "
|
||||
"the current shared gravity-context revisions before residual "
|
||||
"or Jacobian application."
|
||||
);
|
||||
}
|
||||
|
||||
PreparedDisplacementResidualJacobianOperator::PreparedDisplacementResidualJacobianOperator(
|
||||
const DisplacementResidualLayout &layout,
|
||||
const PreparedDisplacementResidualOperator &preparedOperator
|
||||
)
|
||||
: mfem::Operator(
|
||||
layout.residual_offsets().Last(),
|
||||
layout.value_offsets().Last()
|
||||
),
|
||||
m_layout(layout),
|
||||
m_preparedOperator(preparedOperator) {
|
||||
const fem::FEM &f = m_preparedOperator.GetFEM();
|
||||
|
||||
MFEM_VERIFY(
|
||||
f.densityFes != nullptr && f.displacementFes != nullptr && f.gravityFluxFes != nullptr &&
|
||||
f.gravityPotentialFes != nullptr && f.enthalpyFes != nullptr,
|
||||
"Prepared displacement-residual MFEM adapter requires every "
|
||||
"finite-element space in the barotropic equilibrium layout."
|
||||
);
|
||||
|
||||
using Form = utils::blocks::barotropic_equilibrium_form;
|
||||
|
||||
constexpr auto densityValue = utils::blocks::get_value_block<Form>(utils::blocks::density_field.mass_term);
|
||||
|
||||
constexpr auto displacementValue =
|
||||
utils::blocks::get_value_block<Form>(utils::blocks::displacement_field.geometry_term);
|
||||
|
||||
constexpr auto gravityGradientValue =
|
||||
utils::blocks::get_value_block<Form>(utils::blocks::gravity_field.gradient_term);
|
||||
|
||||
constexpr auto gravityPotentialValue =
|
||||
utils::blocks::get_value_block<Form>(utils::blocks::gravity_field.poisson_term);
|
||||
|
||||
constexpr auto enthalpyValue =
|
||||
utils::blocks::get_value_block<Form>(utils::blocks::enthalpy_field.specific_term);
|
||||
|
||||
constexpr auto barotropicConstantValue =
|
||||
utils::blocks::get_value_block<Form>(utils::blocks::barotropic_constant_field.mass_normalization_term);
|
||||
|
||||
constexpr auto gravityGradientResidual =
|
||||
utils::blocks::get_residual_block<Form>(utils::blocks::gravity_field.gradient_term);
|
||||
|
||||
constexpr auto gravityPotentialResidual =
|
||||
utils::blocks::get_residual_block<Form>(utils::blocks::gravity_field.poisson_term);
|
||||
|
||||
constexpr auto densityResidual =
|
||||
utils::blocks::get_residual_block<Form>(utils::blocks::density_field.mass_term);
|
||||
|
||||
constexpr auto displacementResidual =
|
||||
utils::blocks::get_residual_block<Form>(utils::blocks::displacement_field.geometry_term);
|
||||
|
||||
constexpr auto enthalpyResidual =
|
||||
utils::blocks::get_residual_block<Form>(utils::blocks::enthalpy_field.specific_term);
|
||||
|
||||
constexpr auto massResidual =
|
||||
utils::blocks::get_residual_block<Form>(utils::blocks::barotropic_constant_field.mass_normalization_term);
|
||||
|
||||
MFEM_VERIFY(
|
||||
m_layout.size(densityValue) == f.densityFes->GetTrueVSize() &&
|
||||
m_layout.size(displacementValue) == f.displacementFes->GetTrueVSize() &&
|
||||
m_layout.size(gravityGradientValue) == f.gravityFluxFes->GetTrueVSize() &&
|
||||
m_layout.size(gravityPotentialValue) == f.gravityPotentialFes->GetTrueVSize() &&
|
||||
m_layout.size(barotropicConstantValue) == 1,
|
||||
"Prepared displacement-residual MFEM adapter received "
|
||||
"incompatible barotropic value-block sizes."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
m_layout.size(enthalpyValue) == m_preparedOperator.GetPressureOperator().GetEnthalpySize(),
|
||||
"Prepared displacement-residual MFEM adapter received an "
|
||||
"incompatible enthalpy value block."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
m_layout.size(gravityGradientResidual) == f.gravityFluxFes->GetTrueVSize() &&
|
||||
m_layout.size(gravityPotentialResidual) == f.gravityPotentialFes->GetTrueVSize() &&
|
||||
m_layout.size(densityResidual) == f.densityFes->GetTrueVSize() &&
|
||||
m_layout.size(displacementResidual) == f.displacementFes->GetTrueVSize() &&
|
||||
m_layout.size(enthalpyResidual) == f.enthalpyFes->GetTrueVSize() && m_layout.size(massResidual) == 1,
|
||||
"Prepared displacement-residual MFEM adapter received "
|
||||
"incompatible barotropic residual-block sizes."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
Height() == m_layout.residual_offsets().Last() && Width() == m_layout.value_offsets().Last(),
|
||||
"Prepared displacement-residual MFEM adapter has inconsistent "
|
||||
"operator dimensions."
|
||||
);
|
||||
}
|
||||
|
||||
void PreparedDisplacementResidualJacobianOperator::Mult(
|
||||
const mfem::Vector &direction,
|
||||
mfem::Vector &action
|
||||
) const {
|
||||
MFEM_VERIFY(
|
||||
m_preparedOperator.IsPrepared(), "Prepared displacement-residual MFEM adapter requires a "
|
||||
"prepared row operator."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
direction.Size() == Width(), "Prepared displacement-residual MFEM adapter received a "
|
||||
"direction with the wrong size."
|
||||
);
|
||||
|
||||
using Form = utils::blocks::barotropic_equilibrium_form;
|
||||
|
||||
constexpr auto densityValue = utils::blocks::get_value_block<Form>(utils::blocks::density_field.mass_term);
|
||||
|
||||
constexpr auto displacementValue =
|
||||
utils::blocks::get_value_block<Form>(utils::blocks::displacement_field.geometry_term);
|
||||
|
||||
constexpr auto gravityGradientValue =
|
||||
utils::blocks::get_value_block<Form>(utils::blocks::gravity_field.gradient_term);
|
||||
|
||||
constexpr auto enthalpyValue =
|
||||
utils::blocks::get_value_block<Form>(utils::blocks::enthalpy_field.specific_term);
|
||||
|
||||
constexpr auto displacementResidual =
|
||||
utils::blocks::get_residual_block<Form>(utils::blocks::displacement_field.geometry_term);
|
||||
|
||||
const mfem::Vector densityVariation(
|
||||
const_cast<mfem::real_t *>(direction.GetData()) + m_layout.offset(densityValue), m_layout.size(densityValue)
|
||||
);
|
||||
|
||||
const mfem::Vector displacementVariation(
|
||||
const_cast<mfem::real_t *>(direction.GetData()) + m_layout.offset(displacementValue),
|
||||
m_layout.size(displacementValue)
|
||||
);
|
||||
|
||||
const mfem::Vector gravityGradientVariation(
|
||||
const_cast<mfem::real_t *>(direction.GetData()) + m_layout.offset(gravityGradientValue),
|
||||
m_layout.size(gravityGradientValue)
|
||||
);
|
||||
|
||||
const mfem::Vector enthalpyVariation(
|
||||
const_cast<mfem::real_t *>(direction.GetData()) + m_layout.offset(enthalpyValue),
|
||||
m_layout.size(enthalpyValue)
|
||||
);
|
||||
|
||||
mfem::Vector displacementAction;
|
||||
|
||||
m_preparedOperator.ApplyCompleteJacobianAction(
|
||||
densityVariation, displacementVariation, gravityGradientVariation, enthalpyVariation, displacementAction
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
displacementAction.Size() == m_layout.size(displacementResidual),
|
||||
"Prepared displacement-residual MFEM adapter produced an "
|
||||
"action with the wrong size."
|
||||
);
|
||||
|
||||
action.SetSize(Height());
|
||||
action = 0.0;
|
||||
|
||||
const int residualOffset = m_layout.offset(displacementResidual);
|
||||
|
||||
for (int entry = 0; entry < displacementAction.Size(); ++entry) {
|
||||
action(residualOffset + entry) = displacementAction(entry);
|
||||
}
|
||||
}
|
||||
|
||||
const DisplacementResidualLayout &PreparedDisplacementResidualJacobianOperator::GetLayout() const noexcept {
|
||||
return m_layout;
|
||||
}
|
||||
} // namespace mean_field::operators
|
||||
@@ -0,0 +1,290 @@
|
||||
module;
|
||||
|
||||
#include <mfem.hpp>
|
||||
|
||||
module mean_field;
|
||||
|
||||
import :operators.kernels.gravity_displacement_force;
|
||||
import :operators.prepared_gravity_displacement_force;
|
||||
|
||||
namespace {
|
||||
[[nodiscard]] bool relevant_revisions_match(
|
||||
const mean_field::operators::context::gravity_field::GravityFieldRevisions &left,
|
||||
const mean_field::operators::context::gravity_field::GravityFieldRevisions &right
|
||||
) noexcept {
|
||||
return left.discretization == right.discretization && left.displacement == right.displacement &&
|
||||
left.density == right.density && left.gravity_gradient == right.gravity_gradient;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace mean_field::operators {
|
||||
PreparedGravityDisplacementForceOperator::PreparedGravityDisplacementForceOperator(
|
||||
const fem::FEM &f,
|
||||
const mapping::DomainMapperStateless &domainMapper,
|
||||
const context::gravity_field::GravityFieldLinearizationContext &gravityContext
|
||||
)
|
||||
: m_fem(f),
|
||||
m_domainMapper(domainMapper),
|
||||
m_gravityContext(gravityContext) {
|
||||
MFEM_VERIFY(m_fem.mesh != nullptr, "PreparedGravityDisplacementForceOperator requires a mesh.");
|
||||
|
||||
MFEM_VERIFY(
|
||||
m_fem.densityFes != nullptr && m_fem.gravityFluxFes != nullptr && m_fem.displacementFes != nullptr,
|
||||
"PreparedGravityDisplacementForceOperator requires density, "
|
||||
"gravity-gradient, and displacement finite-element spaces."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
m_domainMapper.GetDimension() == m_fem.mesh->Dimension(),
|
||||
"PreparedGravityDisplacementForceOperator received a mapper "
|
||||
"with the wrong dimension."
|
||||
);
|
||||
}
|
||||
|
||||
PreparedGravityDisplacementForceReport PreparedGravityDisplacementForceOperator::Prepare() {
|
||||
MFEM_VERIFY(
|
||||
m_gravityContext.IsPrepared(), "PreparedGravityDisplacementForceOperator requires the shared "
|
||||
"gravity linearization context to be prepared first."
|
||||
);
|
||||
|
||||
const context::gravity_field::GravityFieldRevisions &requestedRevisions = m_gravityContext.GetRevisions();
|
||||
|
||||
if (m_isPrepared && relevant_revisions_match(requestedRevisions, m_preparedRevisions)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
kernels::apply_gravity_displacement_force_residual(
|
||||
m_fem, m_domainMapper, m_gravityContext.GetDensity(), m_gravityContext.GetGravityGradient(),
|
||||
m_gravityContext.GetGeometryContext().GetDisplacement(), m_cachedResidual
|
||||
);
|
||||
|
||||
m_preparedRevisions = requestedRevisions;
|
||||
++m_residualPreparationCount;
|
||||
m_isPrepared = true;
|
||||
|
||||
return {.preparedResidual = true};
|
||||
}
|
||||
|
||||
void PreparedGravityDisplacementForceOperator::BuildResidual(mfem::Vector &residual) const {
|
||||
VerifyPrepared();
|
||||
residual = m_cachedResidual;
|
||||
++m_residualApplicationCount;
|
||||
}
|
||||
|
||||
void PreparedGravityDisplacementForceOperator::ApplyDensityJacobianAction(
|
||||
const mfem::Vector &densityVariation,
|
||||
mfem::Vector &action
|
||||
) const {
|
||||
VerifyPrepared();
|
||||
|
||||
kernels::apply_gravity_displacement_force_density_action(
|
||||
m_fem, m_domainMapper, densityVariation, m_gravityContext.GetGravityGradient(),
|
||||
m_gravityContext.GetGeometryContext().GetDisplacement(), action
|
||||
);
|
||||
|
||||
++m_densityJacobianStatistics.applications;
|
||||
}
|
||||
|
||||
void PreparedGravityDisplacementForceOperator::ApplyGravityGradientJacobianAction(
|
||||
const mfem::Vector &gravityGradientVariation,
|
||||
mfem::Vector &action
|
||||
) const {
|
||||
VerifyPrepared();
|
||||
|
||||
kernels::apply_gravity_displacement_force_gradient_action(
|
||||
m_fem, m_domainMapper, m_gravityContext.GetDensity(), gravityGradientVariation,
|
||||
m_gravityContext.GetGeometryContext().GetDisplacement(), action
|
||||
);
|
||||
|
||||
++m_gravityGradientJacobianStatistics.applications;
|
||||
}
|
||||
|
||||
void PreparedGravityDisplacementForceOperator::ApplyDisplacementJacobianAction(
|
||||
const mfem::Vector &displacementVariation,
|
||||
mfem::Vector &action
|
||||
) const {
|
||||
VerifyPrepared();
|
||||
|
||||
kernels::apply_gravity_displacement_force_displacement_action(
|
||||
m_fem, m_domainMapper, m_gravityContext.GetDensity(), m_gravityContext.GetGravityGradient(),
|
||||
displacementVariation, m_gravityContext.GetGeometryContext().GetDisplacement(), action
|
||||
);
|
||||
|
||||
++m_displacementJacobianStatistics.applications;
|
||||
}
|
||||
|
||||
void PreparedGravityDisplacementForceOperator::ApplyCompleteJacobianAction(
|
||||
const mfem::Vector &densityVariation,
|
||||
const mfem::Vector &displacementVariation,
|
||||
const mfem::Vector &gravityGradientVariation,
|
||||
mfem::Vector &action
|
||||
) const {
|
||||
VerifyPrepared();
|
||||
|
||||
kernels::apply_gravity_displacement_force_complete_action(
|
||||
m_fem, m_domainMapper, m_gravityContext.GetDensity(), densityVariation,
|
||||
m_gravityContext.GetGravityGradient(), gravityGradientVariation, displacementVariation,
|
||||
m_gravityContext.GetGeometryContext().GetDisplacement(), action
|
||||
);
|
||||
|
||||
++m_densityJacobianStatistics.applications;
|
||||
++m_gravityGradientJacobianStatistics.applications;
|
||||
++m_displacementJacobianStatistics.applications;
|
||||
++m_completeJacobianStatistics.applications;
|
||||
}
|
||||
|
||||
bool PreparedGravityDisplacementForceOperator::IsPrepared() const noexcept {
|
||||
if (!m_isPrepared || !m_gravityContext.IsPrepared()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return relevant_revisions_match(m_gravityContext.GetRevisions(), m_preparedRevisions);
|
||||
}
|
||||
|
||||
std::uint64_t PreparedGravityDisplacementForceOperator::GetResidualPreparationCount() const noexcept {
|
||||
return m_residualPreparationCount;
|
||||
}
|
||||
|
||||
std::uint64_t PreparedGravityDisplacementForceOperator::GetResidualApplicationCount() const noexcept {
|
||||
return m_residualApplicationCount;
|
||||
}
|
||||
|
||||
const PreparedGravityDisplacementForceColumnStatistics &
|
||||
PreparedGravityDisplacementForceOperator::GetDensityJacobianStatistics() const noexcept {
|
||||
return m_densityJacobianStatistics;
|
||||
}
|
||||
|
||||
const PreparedGravityDisplacementForceColumnStatistics &
|
||||
PreparedGravityDisplacementForceOperator::GetGravityGradientJacobianStatistics() const noexcept {
|
||||
return m_gravityGradientJacobianStatistics;
|
||||
}
|
||||
|
||||
const PreparedGravityDisplacementForceColumnStatistics &
|
||||
PreparedGravityDisplacementForceOperator::GetDisplacementJacobianStatistics() const noexcept {
|
||||
return m_displacementJacobianStatistics;
|
||||
}
|
||||
|
||||
const PreparedGravityDisplacementForceCompleteStatistics &
|
||||
PreparedGravityDisplacementForceOperator::GetCompleteJacobianStatistics() const noexcept {
|
||||
return m_completeJacobianStatistics;
|
||||
}
|
||||
|
||||
const fem::FEM &PreparedGravityDisplacementForceOperator::GetFEM() const noexcept {
|
||||
return m_fem;
|
||||
}
|
||||
|
||||
const context::gravity_field::GravityFieldLinearizationContext &
|
||||
PreparedGravityDisplacementForceOperator::GetGravityContext() const noexcept {
|
||||
return m_gravityContext;
|
||||
}
|
||||
|
||||
void PreparedGravityDisplacementForceOperator::VerifyPrepared() const {
|
||||
MFEM_VERIFY(
|
||||
IsPrepared(), "PreparedGravityDisplacementForceOperator must be prepared for "
|
||||
"the current shared gravity-context revisions before residual or "
|
||||
"Jacobian application."
|
||||
);
|
||||
}
|
||||
|
||||
PreparedGravityDisplacementForceJacobianOperator::PreparedGravityDisplacementForceJacobianOperator(
|
||||
const GravityDisplacementForceLayout &layout,
|
||||
const PreparedGravityDisplacementForceOperator &preparedOperator
|
||||
)
|
||||
: mfem::Operator(
|
||||
layout.residual_offsets().Last(),
|
||||
layout.value_offsets().Last()
|
||||
),
|
||||
m_layout(layout),
|
||||
m_preparedOperator(preparedOperator) {
|
||||
const fem::FEM &f = m_preparedOperator.GetFEM();
|
||||
|
||||
using Form = utils::blocks::barotropic_equilibrium_form;
|
||||
|
||||
constexpr auto densityValue = utils::blocks::get_value_block<Form>(utils::blocks::density_field.mass_term);
|
||||
|
||||
constexpr auto displacementValue =
|
||||
utils::blocks::get_value_block<Form>(utils::blocks::displacement_field.geometry_term);
|
||||
|
||||
constexpr auto gravityGradientValue =
|
||||
utils::blocks::get_value_block<Form>(utils::blocks::gravity_field.gradient_term);
|
||||
|
||||
constexpr auto displacementResidual =
|
||||
utils::blocks::get_residual_block<Form>(utils::blocks::displacement_field.geometry_term);
|
||||
|
||||
MFEM_VERIFY(
|
||||
m_layout.size(densityValue) == f.densityFes->GetTrueVSize() &&
|
||||
m_layout.size(displacementValue) == f.displacementFes->GetTrueVSize() &&
|
||||
m_layout.size(gravityGradientValue) == f.gravityFluxFes->GetTrueVSize() &&
|
||||
m_layout.size(displacementResidual) == f.displacementFes->GetTrueVSize(),
|
||||
"Prepared gravity-displacement-force MFEM adapter received "
|
||||
"incompatible coupled block sizes."
|
||||
);
|
||||
}
|
||||
|
||||
void PreparedGravityDisplacementForceJacobianOperator::Mult(
|
||||
const mfem::Vector &direction,
|
||||
mfem::Vector &action
|
||||
) const {
|
||||
MFEM_VERIFY(
|
||||
m_preparedOperator.IsPrepared(), "Prepared gravity-displacement-force MFEM adapter requires a "
|
||||
"prepared operator."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
direction.Size() == Width(), "Prepared gravity-displacement-force MFEM adapter received a "
|
||||
"direction with the wrong size."
|
||||
);
|
||||
|
||||
using Form = utils::blocks::barotropic_equilibrium_form;
|
||||
|
||||
constexpr auto densityValue = utils::blocks::get_value_block<Form>(utils::blocks::density_field.mass_term);
|
||||
|
||||
constexpr auto displacementValue =
|
||||
utils::blocks::get_value_block<Form>(utils::blocks::displacement_field.geometry_term);
|
||||
|
||||
constexpr auto gravityGradientValue =
|
||||
utils::blocks::get_value_block<Form>(utils::blocks::gravity_field.gradient_term);
|
||||
|
||||
constexpr auto displacementResidual =
|
||||
utils::blocks::get_residual_block<Form>(utils::blocks::displacement_field.geometry_term);
|
||||
|
||||
const mfem::Vector densityVariation(
|
||||
const_cast<mfem::real_t *>(direction.GetData()) + m_layout.offset(densityValue), m_layout.size(densityValue)
|
||||
);
|
||||
|
||||
const mfem::Vector displacementVariation(
|
||||
const_cast<mfem::real_t *>(direction.GetData()) + m_layout.offset(displacementValue),
|
||||
m_layout.size(displacementValue)
|
||||
);
|
||||
|
||||
const mfem::Vector gravityGradientVariation(
|
||||
const_cast<mfem::real_t *>(direction.GetData()) + m_layout.offset(gravityGradientValue),
|
||||
m_layout.size(gravityGradientValue)
|
||||
);
|
||||
|
||||
mfem::Vector displacementAction;
|
||||
|
||||
m_preparedOperator.ApplyCompleteJacobianAction(
|
||||
densityVariation, displacementVariation, gravityGradientVariation, displacementAction
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
displacementAction.Size() == m_layout.size(displacementResidual),
|
||||
"Prepared gravity-displacement-force MFEM adapter produced a "
|
||||
"displacement action with the wrong size."
|
||||
);
|
||||
|
||||
action.SetSize(Height());
|
||||
action = 0.0;
|
||||
|
||||
const int residualOffset = m_layout.offset(displacementResidual);
|
||||
|
||||
for (int entry = 0; entry < displacementAction.Size(); ++entry) {
|
||||
action(residualOffset + entry) = displacementAction(entry);
|
||||
}
|
||||
}
|
||||
|
||||
const GravityDisplacementForceLayout &PreparedGravityDisplacementForceJacobianOperator::GetLayout() const noexcept {
|
||||
return m_layout;
|
||||
}
|
||||
} // namespace mean_field::operators
|
||||
@@ -11,19 +11,17 @@ import :operators.prepared_gravity_source;
|
||||
namespace {
|
||||
int get_operator_height(const mean_field::fem::FEM &f) {
|
||||
MFEM_VERIFY(
|
||||
f.gravityPotentialFes != nullptr,
|
||||
"PreparedMappedGravitySourceOperator requires the "
|
||||
"gravity-potential "
|
||||
"finite-element space."
|
||||
f.gravityPotentialFes != nullptr, "PreparedMappedGravitySourceOperator requires the "
|
||||
"gravity-potential "
|
||||
"finite-element space."
|
||||
);
|
||||
return f.gravityPotentialFes->GetTrueVSize();
|
||||
}
|
||||
|
||||
int get_operator_width(const mean_field::fem::FEM &f) {
|
||||
MFEM_VERIFY(
|
||||
f.densityFes != nullptr,
|
||||
"PreparedMappedGravitySourceOperator requires the density "
|
||||
"finite-element space."
|
||||
f.densityFes != nullptr, "PreparedMappedGravitySourceOperator requires the density "
|
||||
"finite-element space."
|
||||
);
|
||||
return f.densityFes->GetTrueVSize();
|
||||
}
|
||||
@@ -35,8 +33,7 @@ namespace {
|
||||
) {
|
||||
local_vector.SetSize(finite_element_space.GetVSize());
|
||||
|
||||
const mfem::Operator *prolongation =
|
||||
finite_element_space.GetProlongationMatrix();
|
||||
const mfem::Operator *prolongation = finite_element_space.GetProlongationMatrix();
|
||||
|
||||
if (prolongation != nullptr) {
|
||||
prolongation->Mult(true_vector, local_vector);
|
||||
@@ -50,16 +47,12 @@ namespace {
|
||||
const mfem::Vector &local_vector,
|
||||
mfem::Vector &true_vector
|
||||
) {
|
||||
MFEM_VERIFY(
|
||||
local_vector.Size() == finite_element_space.GetVSize(),
|
||||
"Local vector has the wrong size."
|
||||
);
|
||||
MFEM_VERIFY(local_vector.Size() == finite_element_space.GetVSize(), "Local vector has the wrong size.");
|
||||
|
||||
true_vector.SetSize(finite_element_space.GetTrueVSize());
|
||||
true_vector = 0.0;
|
||||
true_vector = 0.0;
|
||||
|
||||
const mfem::Operator *prolongation =
|
||||
finite_element_space.GetProlongationMatrix();
|
||||
const mfem::Operator *prolongation = finite_element_space.GetProlongationMatrix();
|
||||
|
||||
if (prolongation != nullptr) {
|
||||
prolongation->MultTranspose(local_vector, true_vector);
|
||||
@@ -74,34 +67,27 @@ namespace {
|
||||
const mfem::FiniteElement &potential_element,
|
||||
const mfem::ElementTransformation &transformation
|
||||
) {
|
||||
using GravityField =
|
||||
mean_field::field::Field<mean_field::field::Gravity>;
|
||||
using GravityField = mean_field::field::Field<mean_field::field::Gravity>;
|
||||
MFEM_VERIFY(
|
||||
density_element.GetOrder() ==
|
||||
mean_field::field::Density::Scalar::familyOrder,
|
||||
density_element.GetOrder() == mean_field::field::Density::Scalar::familyOrder,
|
||||
"The prepared source trial element does not match the registered "
|
||||
"density field."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
potential_element.GetOrder() ==
|
||||
mean_field::field::Gravity::Potential::familyOrder,
|
||||
potential_element.GetOrder() == mean_field::field::Gravity::Potential::familyOrder,
|
||||
"The prepared source test element does not match the registered "
|
||||
"gravity potential."
|
||||
);
|
||||
const mean_field::quadrature::Query query = GravityField::make_query<
|
||||
mean_field::field::Gravity::Form::SourceProjection>(
|
||||
mean_field::quadrature::QuadratureRole::discretization,
|
||||
transformation.OrderW(), {}, mean_field::utils::DOMAINS::STELLAR,
|
||||
mean_field::quadrature::MappingKind::general
|
||||
);
|
||||
const mean_field::quadrature::Query query =
|
||||
GravityField::make_query<mean_field::field::Gravity::Form::SourceProjection>(
|
||||
mean_field::quadrature::QuadratureRole::discretization, transformation.OrderW(), {},
|
||||
mean_field::utils::DOMAINS::STELLAR, mean_field::quadrature::MappingKind::general
|
||||
);
|
||||
|
||||
return *f.quadratureFactory
|
||||
->get(query, transformation.GetGeometryType())
|
||||
.integration_rule;
|
||||
return *f.quadratureFactory->get(query, transformation.GetGeometryType()).integration_rule;
|
||||
}
|
||||
|
||||
class FrozenMappedGravitySourceCoefficient final
|
||||
: public mfem::Coefficient {
|
||||
class FrozenMappedGravitySourceCoefficient final : public mfem::Coefficient {
|
||||
public:
|
||||
FrozenMappedGravitySourceCoefficient(
|
||||
const mean_field::fem::FEM &f,
|
||||
@@ -111,9 +97,7 @@ namespace {
|
||||
: m_fem(f),
|
||||
m_domain_mapper(domain_mapper),
|
||||
m_workspace(domain_mapper.GetDimension()) {
|
||||
true_to_local(
|
||||
*m_fem.displacementFes, displacement_true, m_displacement_local
|
||||
);
|
||||
true_to_local(*m_fem.displacementFes, displacement_true, m_displacement_local);
|
||||
}
|
||||
|
||||
double Eval(
|
||||
@@ -128,79 +112,55 @@ namespace {
|
||||
"Mapped gravity source coefficient received an invalid element "
|
||||
"ID."
|
||||
);
|
||||
if (transformation.Attribute ==
|
||||
m_domain_mapper.GetVacuumElementAttribute()) {
|
||||
if (transformation.Attribute == m_domain_mapper.GetVacuumElementAttribute()) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
LoadElement(element_id);
|
||||
const mean_field::mapping::ElementMappingData mapping_data{
|
||||
.displacement = *m_displacement_data,
|
||||
.compactification = *m_compactification_data
|
||||
.displacement = *m_displacement_data, .compactification = *m_compactification_data
|
||||
};
|
||||
|
||||
mean_field::mapping::VolumeMappingContext mapping_context;
|
||||
|
||||
const mean_field::mapping::MappingStatus status =
|
||||
m_domain_mapper.EvaluateVolume(
|
||||
mapping_data, transformation, integration_point,
|
||||
m_workspace, mapping_context
|
||||
);
|
||||
const mean_field::mapping::MappingStatus status = m_domain_mapper.EvaluateVolume(
|
||||
mapping_data, transformation, integration_point, m_workspace, mapping_context
|
||||
);
|
||||
|
||||
if (status != mean_field::mapping::MappingStatus::valid) {
|
||||
const mfem::FiniteElement &displacement_element =
|
||||
*m_fem.displacementFes->GetFE(element_id);
|
||||
const mfem::FiniteElement &compactification_element =
|
||||
*m_fem.compactificationFes->GetFE(element_id);
|
||||
const mfem::FiniteElement &displacement_element = *m_fem.displacementFes->GetFE(element_id);
|
||||
const mfem::FiniteElement &compactification_element = *m_fem.compactificationFes->GetFE(element_id);
|
||||
|
||||
mfem::Vector displacement_shape(displacement_element.GetDof());
|
||||
mfem::Vector compactification_shape(
|
||||
compactification_element.GetDof()
|
||||
);
|
||||
mfem::Vector compactification_shape(compactification_element.GetDof());
|
||||
mfem::Vector reference_position(m_domain_mapper.GetDimension());
|
||||
mfem::Vector displacement_value(m_domain_mapper.GetDimension());
|
||||
|
||||
displacement_element.CalcShape(
|
||||
integration_point, displacement_shape
|
||||
);
|
||||
compactification_element.CalcShape(
|
||||
integration_point, compactification_shape
|
||||
);
|
||||
displacement_element.CalcShape(integration_point, displacement_shape);
|
||||
compactification_element.CalcShape(integration_point, compactification_shape);
|
||||
transformation.Transform(integration_point, reference_position);
|
||||
m_displacement_data->GetDofMatrix().MultTranspose(
|
||||
displacement_shape, displacement_value
|
||||
);
|
||||
m_displacement_data->GetDofMatrix().MultTranspose(displacement_shape, displacement_value);
|
||||
|
||||
const double compactification_coordinate =
|
||||
m_compactification_data->GetDofs() * compactification_shape;
|
||||
const double compactification_coordinate = m_compactification_data->GetDofs() * compactification_shape;
|
||||
|
||||
MFEM_ABORT(
|
||||
"Stateless domain mapping failed while preparing the "
|
||||
"gravity "
|
||||
"source operator."
|
||||
<< "\nMapping status = " << static_cast<int>(status)
|
||||
<< "\nElement ID = " << element_id
|
||||
<< "\nMapping status = " << static_cast<int>(status) << "\nElement ID = " << element_id
|
||||
<< "\nElement attribute = " << transformation.Attribute
|
||||
<< "\nIntegration-point index = " << integration_point.index
|
||||
<< "\nIntegration point = <" << integration_point.x << ", "
|
||||
<< integration_point.y << ", " << integration_point.z << ">"
|
||||
<< "\nReference position = <" << reference_position(0)
|
||||
<< ", " << reference_position(1) << ", "
|
||||
<< "\nIntegration-point index = " << integration_point.index << "\nIntegration point = <"
|
||||
<< integration_point.x << ", " << integration_point.y << ", " << integration_point.z << ">"
|
||||
<< "\nReference position = <" << reference_position(0) << ", " << reference_position(1) << ", "
|
||||
<< reference_position(2) << ">"
|
||||
<< "\nReference radius = " << reference_position.Norml2()
|
||||
<< "\nDisplacement value = <" << displacement_value(0)
|
||||
<< ", " << displacement_value(1) << ", "
|
||||
<< displacement_value(2) << ">"
|
||||
<< "\nDisplacement magnitude = "
|
||||
<< displacement_value.Norml2()
|
||||
<< "\nCompactification coordinate = "
|
||||
<< compactification_coordinate
|
||||
<< "\nDisplacement ordering = "
|
||||
<< static_cast<int>(m_fem.displacementFes->GetOrdering())
|
||||
<< "\nReference radius = " << reference_position.Norml2() << "\nDisplacement value = <"
|
||||
<< displacement_value(0) << ", " << displacement_value(1) << ", " << displacement_value(2) << ">"
|
||||
<< "\nDisplacement magnitude = " << displacement_value.Norml2()
|
||||
<< "\nCompactification coordinate = " << compactification_coordinate
|
||||
<< "\nDisplacement ordering = " << static_cast<int>(m_fem.displacementFes->GetOrdering())
|
||||
);
|
||||
}
|
||||
const double mapping_determinant =
|
||||
mapping_context.mapping.mapping_determinant;
|
||||
const double mapping_determinant = mapping_context.mapping.mapping_determinant;
|
||||
MFEM_VERIFY(
|
||||
std::isfinite(mapping_determinant) && mapping_determinant > 0.0,
|
||||
"Prepared gravity source operator encountered a non-positive "
|
||||
@@ -208,8 +168,7 @@ namespace {
|
||||
"non-finite mapping determinant."
|
||||
);
|
||||
|
||||
return 4.0 * std::numbers::pi * mean_field::utils::G *
|
||||
mapping_determinant;
|
||||
return 4.0 * std::numbers::pi * mean_field::utils::G * mapping_determinant;
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -218,48 +177,32 @@ namespace {
|
||||
return;
|
||||
}
|
||||
|
||||
const mfem::FiniteElement &displacement_element =
|
||||
*m_fem.displacementFes->GetFE(element_id);
|
||||
const mfem::FiniteElement &compactification_element =
|
||||
*m_fem.compactificationFes->GetFE(element_id);
|
||||
const mfem::FiniteElement &displacement_element = *m_fem.displacementFes->GetFE(element_id);
|
||||
const mfem::FiniteElement &compactification_element = *m_fem.compactificationFes->GetFE(element_id);
|
||||
|
||||
mfem::DofTransformation *displacement_dof_transformation =
|
||||
m_fem.displacementFes->GetElementVDofs(
|
||||
element_id, m_displacement_dofs
|
||||
);
|
||||
m_fem.displacementFes->GetElementVDofs(element_id, m_displacement_dofs);
|
||||
mfem::DofTransformation *compactification_dof_transformation =
|
||||
m_fem.compactificationFes->GetElementDofs(
|
||||
element_id, m_compactification_dofs
|
||||
);
|
||||
m_fem.compactificationFes->GetElementDofs(element_id, m_compactification_dofs);
|
||||
|
||||
m_displacement_local.GetSubVector(
|
||||
m_displacement_dofs, m_element_displacement
|
||||
);
|
||||
m_fem.compactificationCoordinate->GetSubVector(
|
||||
m_compactification_dofs, m_element_compactification
|
||||
);
|
||||
m_displacement_local.GetSubVector(m_displacement_dofs, m_element_displacement);
|
||||
m_fem.compactificationCoordinate->GetSubVector(m_compactification_dofs, m_element_compactification);
|
||||
|
||||
if (displacement_dof_transformation != nullptr) {
|
||||
displacement_dof_transformation->InvTransformPrimal(
|
||||
m_element_displacement
|
||||
);
|
||||
displacement_dof_transformation->InvTransformPrimal(m_element_displacement);
|
||||
}
|
||||
|
||||
if (compactification_dof_transformation != nullptr) {
|
||||
compactification_dof_transformation->InvTransformPrimal(
|
||||
m_element_compactification
|
||||
);
|
||||
compactification_dof_transformation->InvTransformPrimal(m_element_compactification);
|
||||
}
|
||||
|
||||
m_displacement_data = std::make_unique<
|
||||
mean_field::mapping::ElementDisplacementData>(
|
||||
m_displacement_data = std::make_unique<mean_field::mapping::ElementDisplacementData>(
|
||||
mean_field::mapping::ElementDisplacementDataFromElementVDofs(
|
||||
displacement_element, m_element_displacement
|
||||
)
|
||||
);
|
||||
|
||||
m_compactification_data = std::make_unique<
|
||||
mean_field::mapping::ElementCompactificationData>(
|
||||
m_compactification_data = std::make_unique<mean_field::mapping::ElementCompactificationData>(
|
||||
compactification_element, m_element_compactification
|
||||
);
|
||||
|
||||
@@ -277,10 +220,8 @@ namespace {
|
||||
mfem::Vector m_element_displacement;
|
||||
mfem::Vector m_element_compactification;
|
||||
|
||||
std::unique_ptr<mean_field::mapping::ElementDisplacementData>
|
||||
m_displacement_data;
|
||||
std::unique_ptr<mean_field::mapping::ElementCompactificationData>
|
||||
m_compactification_data;
|
||||
std::unique_ptr<mean_field::mapping::ElementDisplacementData> m_displacement_data;
|
||||
std::unique_ptr<mean_field::mapping::ElementCompactificationData> m_compactification_data;
|
||||
|
||||
mean_field::mapping::DomainMapperStateless::Workspace m_workspace;
|
||||
int m_cached_element_id{-1};
|
||||
@@ -298,30 +239,23 @@ namespace mean_field::operators {
|
||||
),
|
||||
m_fem(f),
|
||||
m_domain_mapper(domain_mapper) {
|
||||
MFEM_VERIFY(f.mesh != nullptr, "PreparedMappedGravitySourceOperator requires a mesh.");
|
||||
MFEM_VERIFY(
|
||||
f.mesh != nullptr,
|
||||
"PreparedMappedGravitySourceOperator requires a mesh."
|
||||
f.densityFes != nullptr, "PreparedMappedGravitySourceOperator requires the density "
|
||||
"finite-element space."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.densityFes != nullptr,
|
||||
"PreparedMappedGravitySourceOperator requires the density "
|
||||
"finite-element space."
|
||||
f.gravityPotentialFes != nullptr, "PreparedMappedGravitySourceOperator requires the "
|
||||
"gravity-potential "
|
||||
"finite-element space."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.gravityPotentialFes != nullptr,
|
||||
"PreparedMappedGravitySourceOperator requires the "
|
||||
"gravity-potential "
|
||||
"finite-element space."
|
||||
f.displacementFes != nullptr, "PreparedMappedGravitySourceOperator requires "
|
||||
"the displacement finite-element space."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.displacementFes != nullptr,
|
||||
"PreparedMappedGravitySourceOperator requires "
|
||||
"the displacement finite-element space."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.compactificationFes != nullptr,
|
||||
"PreparedMappedGravitySourceOperator requires the compactification "
|
||||
"finite-element space."
|
||||
f.compactificationFes != nullptr, "PreparedMappedGravitySourceOperator requires the compactification "
|
||||
"finite-element space."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.compactificationCoordinate != nullptr,
|
||||
@@ -329,9 +263,8 @@ namespace mean_field::operators {
|
||||
"coordinate."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.quadratureFactory != nullptr,
|
||||
"PreparedMappedGravitySourceOperator "
|
||||
"requires the quadrature-rule factory."
|
||||
f.quadratureFactory != nullptr, "PreparedMappedGravitySourceOperator "
|
||||
"requires the quadrature-rule factory."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
domain_mapper.GetDimension() == f.mesh->Dimension(),
|
||||
@@ -339,14 +272,10 @@ namespace mean_field::operators {
|
||||
"dimension."
|
||||
);
|
||||
|
||||
utils::populate_element_mask(
|
||||
f.mesh.get(), utils::DOMAINS::STELLAR, m_stellar_marker
|
||||
);
|
||||
utils::populate_element_mask(f.mesh.get(), utils::DOMAINS::STELLAR, m_stellar_marker);
|
||||
}
|
||||
|
||||
void PreparedMappedGravitySourceOperator::Prepare(
|
||||
const mfem::Vector &displacement_true
|
||||
) {
|
||||
void PreparedMappedGravitySourceOperator::Prepare(const mfem::Vector &displacement_true) {
|
||||
MFEM_VERIFY(
|
||||
displacement_true.Size() == m_fem.displacementFes->GetTrueVSize(),
|
||||
"PreparedMappedGravitySourceOperator received a displacement "
|
||||
@@ -356,9 +285,8 @@ namespace mean_field::operators {
|
||||
|
||||
for (int i = 0; i < displacement_true.Size(); ++i) {
|
||||
MFEM_VERIFY(
|
||||
std::isfinite(displacement_true(i)),
|
||||
"PreparedMappedGravitySourceOperator received a non-finite "
|
||||
"displacement value."
|
||||
std::isfinite(displacement_true(i)), "PreparedMappedGravitySourceOperator received a non-finite "
|
||||
"displacement value."
|
||||
);
|
||||
}
|
||||
|
||||
@@ -366,44 +294,33 @@ namespace mean_field::operators {
|
||||
m_elements.clear();
|
||||
m_elements.reserve(m_fem.mesh->GetNE());
|
||||
|
||||
FrozenMappedGravitySourceCoefficient source_coefficient(
|
||||
m_fem, m_domain_mapper, displacement_true
|
||||
);
|
||||
FrozenMappedGravitySourceCoefficient source_coefficient(m_fem, m_domain_mapper, displacement_true);
|
||||
|
||||
for (int element_id = 0; element_id < m_fem.mesh->GetNE();
|
||||
++element_id) {
|
||||
for (int element_id = 0; element_id < m_fem.mesh->GetNE(); ++element_id) {
|
||||
const int attribute = m_fem.mesh->GetAttribute(element_id);
|
||||
|
||||
if (attribute <= 0 || attribute > m_stellar_marker.Size() ||
|
||||
m_stellar_marker[attribute - 1] == 0) {
|
||||
if (attribute <= 0 || attribute > m_stellar_marker.Size() || m_stellar_marker[attribute - 1] == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
m_elements.emplace_back();
|
||||
ElementPAData &data = m_elements.back();
|
||||
ElementPAData &data = m_elements.back();
|
||||
|
||||
data.element_id = element_id;
|
||||
data.element_id = element_id;
|
||||
|
||||
data.density_dof_transformation =
|
||||
m_fem.densityFes->GetElementDofs(element_id, data.density_dofs);
|
||||
data.density_dof_transformation = m_fem.densityFes->GetElementDofs(element_id, data.density_dofs);
|
||||
|
||||
data.potential_dof_transformation =
|
||||
m_fem.gravityPotentialFes->GetElementDofs(
|
||||
element_id, data.potential_dofs
|
||||
);
|
||||
m_fem.gravityPotentialFes->GetElementDofs(element_id, data.potential_dofs);
|
||||
|
||||
const mfem::FiniteElement &density_element =
|
||||
*m_fem.densityFes->GetFE(element_id);
|
||||
const mfem::FiniteElement &density_element = *m_fem.densityFes->GetFE(element_id);
|
||||
|
||||
const mfem::FiniteElement &potential_element =
|
||||
*m_fem.gravityPotentialFes->GetFE(element_id);
|
||||
const mfem::FiniteElement &potential_element = *m_fem.gravityPotentialFes->GetFE(element_id);
|
||||
|
||||
mfem::ElementTransformation &transformation =
|
||||
*m_fem.mesh->GetElementTransformation(element_id);
|
||||
mfem::ElementTransformation &transformation = *m_fem.mesh->GetElementTransformation(element_id);
|
||||
|
||||
const mfem::IntegrationRule &integration_rule = get_source_rule(
|
||||
m_fem, density_element, potential_element, transformation
|
||||
);
|
||||
const mfem::IntegrationRule &integration_rule =
|
||||
get_source_rule(m_fem, density_element, potential_element, transformation);
|
||||
|
||||
const int quadrature_point_count = integration_rule.GetNPoints();
|
||||
|
||||
@@ -411,24 +328,17 @@ namespace mean_field::operators {
|
||||
|
||||
const int potential_dof_count = potential_element.GetDof();
|
||||
|
||||
data.density_basis.SetSize(
|
||||
quadrature_point_count, density_dof_count
|
||||
);
|
||||
data.density_basis.SetSize(quadrature_point_count, density_dof_count);
|
||||
|
||||
data.potential_basis.SetSize(
|
||||
quadrature_point_count, potential_dof_count
|
||||
);
|
||||
data.potential_basis.SetSize(quadrature_point_count, potential_dof_count);
|
||||
|
||||
data.quadrature_data.SetSize(quadrature_point_count);
|
||||
|
||||
mfem::Vector density_shape(density_dof_count);
|
||||
mfem::Vector potential_shape(potential_dof_count);
|
||||
|
||||
for (int quadrature_point = 0;
|
||||
quadrature_point < quadrature_point_count;
|
||||
++quadrature_point) {
|
||||
const mfem::IntegrationPoint &integration_point =
|
||||
integration_rule.IntPoint(quadrature_point);
|
||||
for (int quadrature_point = 0; quadrature_point < quadrature_point_count; ++quadrature_point) {
|
||||
const mfem::IntegrationPoint &integration_point = integration_rule.IntPoint(quadrature_point);
|
||||
|
||||
transformation.SetIntPoint(&integration_point);
|
||||
|
||||
@@ -436,44 +346,34 @@ namespace mean_field::operators {
|
||||
// including the finite-element map type.
|
||||
density_element.CalcPhysShape(transformation, density_shape);
|
||||
|
||||
potential_element.CalcPhysShape(
|
||||
transformation, potential_shape
|
||||
);
|
||||
potential_element.CalcPhysShape(transformation, potential_shape);
|
||||
|
||||
for (int i = 0; i < density_dof_count; ++i) {
|
||||
data.density_basis(quadrature_point, i) = density_shape(i);
|
||||
}
|
||||
|
||||
for (int i = 0; i < potential_dof_count; ++i) {
|
||||
data.potential_basis(quadrature_point, i) =
|
||||
potential_shape(i);
|
||||
data.potential_basis(quadrature_point, i) = potential_shape(i);
|
||||
}
|
||||
|
||||
const double coefficient_value =
|
||||
source_coefficient.Eval(transformation, integration_point);
|
||||
const double coefficient_value = source_coefficient.Eval(transformation, integration_point);
|
||||
|
||||
transformation.SetIntPoint(&integration_point);
|
||||
|
||||
const double quadrature_value = integration_point.weight *
|
||||
transformation.Weight() *
|
||||
coefficient_value;
|
||||
const double quadrature_value = integration_point.weight * transformation.Weight() * coefficient_value;
|
||||
|
||||
MFEM_VERIFY(
|
||||
std::isfinite(quadrature_value) && quadrature_value > 0.0,
|
||||
"Prepared gravity source operator encountered invalid "
|
||||
"quadrature data on element "
|
||||
<< element_id << ", quadrature point "
|
||||
<< quadrature_point << "."
|
||||
<< element_id << ", quadrature point " << quadrature_point << "."
|
||||
);
|
||||
|
||||
data.quadrature_data(quadrature_point) = quadrature_value;
|
||||
}
|
||||
}
|
||||
|
||||
MFEM_VERIFY(
|
||||
!m_elements.empty(),
|
||||
"PreparedMappedGravitySourceOperator found no stellar elements."
|
||||
);
|
||||
MFEM_VERIFY(!m_elements.empty(), "PreparedMappedGravitySourceOperator found no stellar elements.");
|
||||
|
||||
m_is_prepared = true;
|
||||
++m_preparation_count;
|
||||
@@ -483,15 +383,13 @@ namespace mean_field::operators {
|
||||
mfem::Vector &action
|
||||
) const {
|
||||
MFEM_VERIFY(
|
||||
m_is_prepared,
|
||||
"PreparedMappedGravitySourceOperator must be prepared before "
|
||||
"Mult is called."
|
||||
m_is_prepared, "PreparedMappedGravitySourceOperator must be prepared before "
|
||||
"Mult is called."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
density_true.Size() == Width(),
|
||||
"PreparedMappedGravitySourceOperator received a density vector "
|
||||
"with the wrong size."
|
||||
density_true.Size() == Width(), "PreparedMappedGravitySourceOperator received a density vector "
|
||||
"with the wrong size."
|
||||
);
|
||||
|
||||
mfem::Vector density_local;
|
||||
@@ -509,9 +407,7 @@ namespace mean_field::operators {
|
||||
density_local.GetSubVector(data.density_dofs, element_density);
|
||||
|
||||
if (data.density_dof_transformation != nullptr) {
|
||||
data.density_dof_transformation->InvTransformPrimal(
|
||||
element_density
|
||||
);
|
||||
data.density_dof_transformation->InvTransformPrimal(element_density);
|
||||
}
|
||||
|
||||
quadrature_density.SetSize(data.quadrature_data.Size());
|
||||
@@ -527,14 +423,10 @@ namespace mean_field::operators {
|
||||
element_action.SetSize(data.potential_dofs.Size());
|
||||
|
||||
// B_potential^T * D * B_density * x_e
|
||||
data.potential_basis.MultTranspose(
|
||||
quadrature_density, element_action
|
||||
);
|
||||
data.potential_basis.MultTranspose(quadrature_density, element_action);
|
||||
|
||||
if (data.potential_dof_transformation != nullptr) {
|
||||
data.potential_dof_transformation->TransformDual(
|
||||
element_action
|
||||
);
|
||||
data.potential_dof_transformation->TransformDual(element_action);
|
||||
}
|
||||
|
||||
local_action.AddElementVector(data.potential_dofs, element_action);
|
||||
@@ -548,22 +440,18 @@ namespace mean_field::operators {
|
||||
mfem::Vector &action
|
||||
) const {
|
||||
MFEM_VERIFY(
|
||||
m_is_prepared,
|
||||
"PreparedMappedGravitySourceOperator must be prepared before "
|
||||
"MultTranspose is called."
|
||||
m_is_prepared, "PreparedMappedGravitySourceOperator must be prepared before "
|
||||
"MultTranspose is called."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
potential_true.Size() == Height(),
|
||||
"PreparedMappedGravitySourceOperator received a potential vector "
|
||||
"with the wrong size."
|
||||
potential_true.Size() == Height(), "PreparedMappedGravitySourceOperator received a potential vector "
|
||||
"with the wrong size."
|
||||
);
|
||||
|
||||
mfem::Vector potential_local;
|
||||
|
||||
true_to_local(
|
||||
*m_fem.gravityPotentialFes, potential_true, potential_local
|
||||
);
|
||||
true_to_local(*m_fem.gravityPotentialFes, potential_true, potential_local);
|
||||
|
||||
mfem::Vector local_action(m_fem.densityFes->GetVSize());
|
||||
local_action = 0.0;
|
||||
@@ -573,14 +461,10 @@ namespace mean_field::operators {
|
||||
mfem::Vector element_action;
|
||||
|
||||
for (const ElementPAData &data : m_elements) {
|
||||
potential_local.GetSubVector(
|
||||
data.potential_dofs, element_potential
|
||||
);
|
||||
potential_local.GetSubVector(data.potential_dofs, element_potential);
|
||||
|
||||
if (data.potential_dof_transformation != nullptr) {
|
||||
data.potential_dof_transformation->InvTransformPrimal(
|
||||
element_potential
|
||||
);
|
||||
data.potential_dof_transformation->InvTransformPrimal(element_potential);
|
||||
}
|
||||
|
||||
quadrature_potential.SetSize(data.quadrature_data.Size());
|
||||
@@ -593,9 +477,7 @@ namespace mean_field::operators {
|
||||
|
||||
element_action.SetSize(data.density_dofs.Size());
|
||||
|
||||
data.density_basis.MultTranspose(
|
||||
quadrature_potential, element_action
|
||||
);
|
||||
data.density_basis.MultTranspose(quadrature_potential, element_action);
|
||||
|
||||
if (data.density_dof_transformation != nullptr) {
|
||||
data.density_dof_transformation->TransformDual(element_action);
|
||||
@@ -610,8 +492,7 @@ namespace mean_field::operators {
|
||||
return m_is_prepared;
|
||||
}
|
||||
|
||||
std::uint64_t
|
||||
PreparedMappedGravitySourceOperator::GetPreparationCount() const noexcept {
|
||||
std::uint64_t PreparedMappedGravitySourceOperator::GetPreparationCount() const noexcept {
|
||||
return m_preparation_count;
|
||||
}
|
||||
} // namespace mean_field::operators
|
||||
|
||||
@@ -10,9 +10,8 @@ import :operators.prepared_hdiv_mass;
|
||||
namespace {
|
||||
int get_operator_size(const mean_field::fem::FEM &f) {
|
||||
MFEM_VERIFY(
|
||||
f.gravityFluxFes != nullptr,
|
||||
"PreparedMappedHDivMassOperator requires the "
|
||||
"gravity-gradient finite-element space."
|
||||
f.gravityFluxFes != nullptr, "PreparedMappedHDivMassOperator requires the "
|
||||
"gravity-gradient finite-element space."
|
||||
);
|
||||
return f.gravityFluxFes->GetTrueVSize();
|
||||
}
|
||||
@@ -24,8 +23,7 @@ namespace {
|
||||
) {
|
||||
local_vector.SetSize(finite_element_space.GetVSize());
|
||||
|
||||
const mfem::Operator *prolongation =
|
||||
finite_element_space.GetProlongationMatrix();
|
||||
const mfem::Operator *prolongation = finite_element_space.GetProlongationMatrix();
|
||||
|
||||
if (prolongation != nullptr) {
|
||||
prolongation->Mult(true_vector, local_vector);
|
||||
@@ -41,8 +39,7 @@ namespace {
|
||||
for (int element_id = 0; element_id < f.mesh->GetNE(); ++element_id) {
|
||||
const int attribute = f.mesh->GetAttribute(element_id);
|
||||
|
||||
if (attribute > 0 && attribute <= marker.Size() &&
|
||||
marker[attribute - 1] != 0) {
|
||||
if (attribute > 0 && attribute <= marker.Size() && marker[attribute - 1] != 0) {
|
||||
return element_id;
|
||||
}
|
||||
}
|
||||
@@ -55,23 +52,19 @@ namespace {
|
||||
const mfem::Array<int> &marker,
|
||||
const int representative_element_id
|
||||
) {
|
||||
const mfem::FiniteElement &representative_element =
|
||||
*f.gravityFluxFes->GetFE(representative_element_id);
|
||||
const mfem::FiniteElement &representative_element = *f.gravityFluxFes->GetFE(representative_element_id);
|
||||
const mfem::ElementTransformation &representative_transformation =
|
||||
*f.mesh->GetElementTransformation(representative_element_id);
|
||||
|
||||
for (int element_id = 0; element_id < f.mesh->GetNE(); ++element_id) {
|
||||
const int attribute = f.mesh->GetAttribute(element_id);
|
||||
|
||||
if (attribute <= 0 || attribute > marker.Size() ||
|
||||
marker[attribute - 1] == 0) {
|
||||
if (attribute <= 0 || attribute > marker.Size() || marker[attribute - 1] == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const mfem::FiniteElement &element =
|
||||
*f.gravityFluxFes->GetFE(element_id);
|
||||
const mfem::ElementTransformation &transformation =
|
||||
*f.mesh->GetElementTransformation(element_id);
|
||||
const mfem::FiniteElement &element = *f.gravityFluxFes->GetFE(element_id);
|
||||
const mfem::ElementTransformation &transformation = *f.mesh->GetElementTransformation(element_id);
|
||||
|
||||
MFEM_VERIFY(
|
||||
element.GetGeomType() == representative_element.GetGeomType(),
|
||||
@@ -85,16 +78,14 @@ namespace {
|
||||
"finite-element order."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
transformation.OrderW() ==
|
||||
representative_transformation.OrderW(),
|
||||
transformation.OrderW() == representative_transformation.OrderW(),
|
||||
"Prepared H(div) mass domains currently require a uniform "
|
||||
"geometry-weight order."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class FrozenMappedHDivMassCoefficient final
|
||||
: public mfem::MatrixCoefficient {
|
||||
class FrozenMappedHDivMassCoefficient final : public mfem::MatrixCoefficient {
|
||||
public:
|
||||
FrozenMappedHDivMassCoefficient(
|
||||
const mean_field::fem::FEM &f,
|
||||
@@ -107,9 +98,7 @@ namespace {
|
||||
m_domain_mapper(domain_mapper),
|
||||
m_workspace(domain_mapper.GetDimension()),
|
||||
m_elevates_vacuum(elevates_vacuum) {
|
||||
true_to_local(
|
||||
*m_fem.displacementFes, displacement_true, m_displacement_local
|
||||
);
|
||||
true_to_local(*m_fem.displacementFes, displacement_true, m_displacement_local);
|
||||
}
|
||||
|
||||
void Eval(
|
||||
@@ -125,9 +114,7 @@ namespace {
|
||||
"Mapped H(div) mass coefficient received an invalid element ID."
|
||||
);
|
||||
|
||||
const bool element_is_vacuum =
|
||||
transformation.Attribute ==
|
||||
m_domain_mapper.GetVacuumElementAttribute();
|
||||
const bool element_is_vacuum = transformation.Attribute == m_domain_mapper.GetVacuumElementAttribute();
|
||||
|
||||
if (element_is_vacuum != m_elevates_vacuum) {
|
||||
mass_tensor.SetSize(m_domain_mapper.GetDimension());
|
||||
@@ -138,34 +125,27 @@ namespace {
|
||||
LoadElement(element_id);
|
||||
|
||||
const mean_field::mapping::ElementMappingData mapping_data{
|
||||
.displacement = *m_displacement_data,
|
||||
.compactification = *m_compactification_data
|
||||
.displacement = *m_displacement_data, .compactification = *m_compactification_data
|
||||
};
|
||||
|
||||
mean_field::mapping::VolumeMappingContext mapping_context;
|
||||
|
||||
const mean_field::mapping::MappingStatus status =
|
||||
m_domain_mapper.EvaluateVolume(
|
||||
mapping_data, transformation, integration_point,
|
||||
m_workspace, mapping_context
|
||||
);
|
||||
const mean_field::mapping::MappingStatus status = m_domain_mapper.EvaluateVolume(
|
||||
mapping_data, transformation, integration_point, m_workspace, mapping_context
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
status == mean_field::mapping::MappingStatus::valid,
|
||||
"Stateless domain mapping failed while preparing the H(div) "
|
||||
"mass "
|
||||
"operator. Mapping status = "
|
||||
<< static_cast<int>(status)
|
||||
<< ", element ID = " << element_id
|
||||
<< static_cast<int>(status) << ", element ID = " << element_id
|
||||
<< ", element attribute = " << transformation.Attribute
|
||||
<< ", coefficient domain = "
|
||||
<< (m_elevates_vacuum ? "vacuum" : "stellar")
|
||||
<< ", coefficient domain = " << (m_elevates_vacuum ? "vacuum" : "stellar")
|
||||
);
|
||||
|
||||
const mfem::DenseMatrix &mapping_jacobian =
|
||||
mapping_context.mapping.mapping_jacobian;
|
||||
const double mapping_determinant =
|
||||
mapping_context.mapping.mapping_determinant;
|
||||
const mfem::DenseMatrix &mapping_jacobian = mapping_context.mapping.mapping_jacobian;
|
||||
const double mapping_determinant = mapping_context.mapping.mapping_determinant;
|
||||
|
||||
MFEM_VERIFY(
|
||||
std::isfinite(mapping_determinant) && mapping_determinant > 0.0,
|
||||
@@ -183,48 +163,32 @@ namespace {
|
||||
return;
|
||||
}
|
||||
|
||||
const mfem::FiniteElement &displacement_element =
|
||||
*m_fem.displacementFes->GetFE(element_id);
|
||||
const mfem::FiniteElement &compactification_element =
|
||||
*m_fem.compactificationFes->GetFE(element_id);
|
||||
const mfem::FiniteElement &displacement_element = *m_fem.displacementFes->GetFE(element_id);
|
||||
const mfem::FiniteElement &compactification_element = *m_fem.compactificationFes->GetFE(element_id);
|
||||
|
||||
mfem::DofTransformation *displacement_dof_transformation =
|
||||
m_fem.displacementFes->GetElementVDofs(
|
||||
element_id, m_displacement_dofs
|
||||
);
|
||||
m_fem.displacementFes->GetElementVDofs(element_id, m_displacement_dofs);
|
||||
mfem::DofTransformation *compactification_dof_transformation =
|
||||
m_fem.compactificationFes->GetElementDofs(
|
||||
element_id, m_compactification_dofs
|
||||
);
|
||||
m_fem.compactificationFes->GetElementDofs(element_id, m_compactification_dofs);
|
||||
|
||||
m_displacement_local.GetSubVector(
|
||||
m_displacement_dofs, m_element_displacement
|
||||
);
|
||||
m_fem.compactificationCoordinate->GetSubVector(
|
||||
m_compactification_dofs, m_element_compactification
|
||||
);
|
||||
m_displacement_local.GetSubVector(m_displacement_dofs, m_element_displacement);
|
||||
m_fem.compactificationCoordinate->GetSubVector(m_compactification_dofs, m_element_compactification);
|
||||
|
||||
if (displacement_dof_transformation != nullptr) {
|
||||
displacement_dof_transformation->InvTransformPrimal(
|
||||
m_element_displacement
|
||||
);
|
||||
displacement_dof_transformation->InvTransformPrimal(m_element_displacement);
|
||||
}
|
||||
|
||||
if (compactification_dof_transformation != nullptr) {
|
||||
compactification_dof_transformation->InvTransformPrimal(
|
||||
m_element_compactification
|
||||
);
|
||||
compactification_dof_transformation->InvTransformPrimal(m_element_compactification);
|
||||
}
|
||||
|
||||
m_displacement_data = std::make_unique<
|
||||
mean_field::mapping::ElementDisplacementData>(
|
||||
m_displacement_data = std::make_unique<mean_field::mapping::ElementDisplacementData>(
|
||||
mean_field::mapping::ElementDisplacementDataFromElementVDofs(
|
||||
displacement_element, m_element_displacement
|
||||
)
|
||||
);
|
||||
|
||||
m_compactification_data = std::make_unique<
|
||||
mean_field::mapping::ElementCompactificationData>(
|
||||
m_compactification_data = std::make_unique<mean_field::mapping::ElementCompactificationData>(
|
||||
compactification_element, m_element_compactification
|
||||
);
|
||||
|
||||
@@ -242,10 +206,8 @@ namespace {
|
||||
mfem::Vector m_element_displacement;
|
||||
mfem::Vector m_element_compactification;
|
||||
|
||||
std::unique_ptr<mean_field::mapping::ElementDisplacementData>
|
||||
m_displacement_data;
|
||||
std::unique_ptr<mean_field::mapping::ElementCompactificationData>
|
||||
m_compactification_data;
|
||||
std::unique_ptr<mean_field::mapping::ElementDisplacementData> m_displacement_data;
|
||||
std::unique_ptr<mean_field::mapping::ElementCompactificationData> m_compactification_data;
|
||||
|
||||
mean_field::mapping::DomainMapperStateless::Workspace m_workspace;
|
||||
int m_cached_element_id{-1};
|
||||
@@ -261,33 +223,26 @@ namespace mean_field::operators {
|
||||
: Operator(get_operator_size(f)),
|
||||
m_fem(f),
|
||||
m_domain_mapper(domain_mapper) {
|
||||
MFEM_VERIFY(f.mesh != nullptr, "PreparedMappedHDivMassOperator requires a mesh.");
|
||||
MFEM_VERIFY(
|
||||
f.mesh != nullptr, "PreparedMappedHDivMassOperator requires a mesh."
|
||||
f.gravityFluxFes != nullptr, "PreparedMappedHDivMassOperator requires the "
|
||||
"gravity-gradient finite-element space."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.gravityFluxFes != nullptr,
|
||||
"PreparedMappedHDivMassOperator requires the "
|
||||
"gravity-gradient finite-element space."
|
||||
f.displacementFes != nullptr, "PreparedMappedHDivMassOperator requires the "
|
||||
"displacement finite-element space."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.displacementFes != nullptr,
|
||||
"PreparedMappedHDivMassOperator requires the "
|
||||
"displacement finite-element space."
|
||||
f.compactificationFes != nullptr, "PreparedMappedHDivMassOperator requires the compactification "
|
||||
"finite-element space."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.compactificationFes != nullptr,
|
||||
"PreparedMappedHDivMassOperator requires the compactification "
|
||||
"finite-element space."
|
||||
f.compactificationCoordinate != nullptr, "PreparedMappedHDivMassOperator requires the compactification "
|
||||
"coordinate."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.compactificationCoordinate != nullptr,
|
||||
"PreparedMappedHDivMassOperator requires the compactification "
|
||||
"coordinate."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.quadratureFactory != nullptr,
|
||||
"PreparedMappedHDivMassOperator requires the quadrature-rule "
|
||||
"factory."
|
||||
f.quadratureFactory != nullptr, "PreparedMappedHDivMassOperator requires the quadrature-rule "
|
||||
"factory."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
domain_mapper.GetDimension() == f.mesh->Dimension(),
|
||||
@@ -295,39 +250,26 @@ namespace mean_field::operators {
|
||||
"dimension."
|
||||
);
|
||||
|
||||
utils::populate_element_mask(
|
||||
f.mesh.get(), utils::DOMAINS::STELLAR, m_stellar_marker
|
||||
);
|
||||
utils::populate_element_mask(
|
||||
f.mesh.get(), utils::DOMAINS::VACUUM, m_vacuum_marker
|
||||
);
|
||||
utils::populate_element_mask(f.mesh.get(), utils::DOMAINS::STELLAR, m_stellar_marker);
|
||||
utils::populate_element_mask(f.mesh.get(), utils::DOMAINS::VACUUM, m_vacuum_marker);
|
||||
|
||||
const int stellar_element_id =
|
||||
find_representative_element(f, m_stellar_marker);
|
||||
const int vacuum_element_id =
|
||||
find_representative_element(f, m_vacuum_marker);
|
||||
const int stellar_element_id = find_representative_element(f, m_stellar_marker);
|
||||
const int vacuum_element_id = find_representative_element(f, m_vacuum_marker);
|
||||
|
||||
MFEM_VERIFY(
|
||||
stellar_element_id >= 0, "PreparedMappedHDivMassOperator requires "
|
||||
"at least one stellar element."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
vacuum_element_id >= 0,
|
||||
"PreparedMappedHDivMassOperator requires at "
|
||||
"least one compactified vacuum element."
|
||||
vacuum_element_id >= 0, "PreparedMappedHDivMassOperator requires at "
|
||||
"least one compactified vacuum element."
|
||||
);
|
||||
|
||||
validate_uniform_domain_discretization(
|
||||
f, m_stellar_marker, stellar_element_id
|
||||
);
|
||||
validate_uniform_domain_discretization(
|
||||
f, m_vacuum_marker, vacuum_element_id
|
||||
);
|
||||
validate_uniform_domain_discretization(f, m_stellar_marker, stellar_element_id);
|
||||
validate_uniform_domain_discretization(f, m_vacuum_marker, vacuum_element_id);
|
||||
}
|
||||
|
||||
void PreparedMappedHDivMassOperator::Prepare(
|
||||
const mfem::Vector &displacement_true
|
||||
) {
|
||||
void PreparedMappedHDivMassOperator::Prepare(const mfem::Vector &displacement_true) {
|
||||
MFEM_VERIFY(
|
||||
displacement_true.Size() == m_fem.displacementFes->GetTrueVSize(),
|
||||
"PreparedMappedHDivMassOperator received a displacement vector "
|
||||
@@ -337,71 +279,48 @@ namespace mean_field::operators {
|
||||
|
||||
for (int i = 0; i < displacement_true.Size(); ++i) {
|
||||
MFEM_VERIFY(
|
||||
std::isfinite(displacement_true(i)),
|
||||
"PreparedMappedHDivMassOperator received a non-finite "
|
||||
"displacement "
|
||||
"value."
|
||||
std::isfinite(displacement_true(i)), "PreparedMappedHDivMassOperator received a non-finite "
|
||||
"displacement "
|
||||
"value."
|
||||
);
|
||||
}
|
||||
|
||||
const int stellar_element_id =
|
||||
find_representative_element(m_fem, m_stellar_marker);
|
||||
const int vacuum_element_id =
|
||||
find_representative_element(m_fem, m_vacuum_marker);
|
||||
const int stellar_element_id = find_representative_element(m_fem, m_stellar_marker);
|
||||
const int vacuum_element_id = find_representative_element(m_fem, m_vacuum_marker);
|
||||
|
||||
const mfem::FiniteElement &stellar_element =
|
||||
*m_fem.gravityFluxFes->GetFE(stellar_element_id);
|
||||
const mfem::FiniteElement &vacuum_element =
|
||||
*m_fem.gravityFluxFes->GetFE(vacuum_element_id);
|
||||
const mfem::FiniteElement &stellar_element = *m_fem.gravityFluxFes->GetFE(stellar_element_id);
|
||||
const mfem::FiniteElement &vacuum_element = *m_fem.gravityFluxFes->GetFE(vacuum_element_id);
|
||||
|
||||
mfem::ElementTransformation &stellar_transformation =
|
||||
*m_fem.mesh->GetElementTransformation(stellar_element_id);
|
||||
mfem::ElementTransformation &vacuum_transformation =
|
||||
*m_fem.mesh->GetElementTransformation(vacuum_element_id);
|
||||
mfem::ElementTransformation &stellar_transformation = *m_fem.mesh->GetElementTransformation(stellar_element_id);
|
||||
mfem::ElementTransformation &vacuum_transformation = *m_fem.mesh->GetElementTransformation(vacuum_element_id);
|
||||
|
||||
m_mass_form.reset();
|
||||
m_stellar_mass_coefficient.reset();
|
||||
m_vacuum_mass_coefficient.reset();
|
||||
|
||||
m_stellar_mass_coefficient =
|
||||
std::make_unique<FrozenMappedHDivMassCoefficient>(
|
||||
m_fem, m_domain_mapper, displacement_true, false
|
||||
);
|
||||
std::make_unique<FrozenMappedHDivMassCoefficient>(m_fem, m_domain_mapper, displacement_true, false);
|
||||
m_vacuum_mass_coefficient =
|
||||
std::make_unique<FrozenMappedHDivMassCoefficient>(
|
||||
m_fem, m_domain_mapper, displacement_true, true
|
||||
);
|
||||
std::make_unique<FrozenMappedHDivMassCoefficient>(m_fem, m_domain_mapper, displacement_true, true);
|
||||
|
||||
m_mass_form =
|
||||
std::make_unique<mfem::ParBilinearForm>(m_fem.gravityFluxFes.get());
|
||||
m_mass_form = std::make_unique<mfem::ParBilinearForm>(m_fem.gravityFluxFes.get());
|
||||
m_mass_form->SetAssemblyLevel(mfem::AssemblyLevel::PARTIAL);
|
||||
|
||||
auto stellar_integrator =
|
||||
std::make_unique<mfem::VectorFEMassIntegrator>(
|
||||
*m_stellar_mass_coefficient
|
||||
);
|
||||
auto vacuum_integrator = std::make_unique<mfem::VectorFEMassIntegrator>(
|
||||
*m_vacuum_mass_coefficient
|
||||
auto stellar_integrator = std::make_unique<mfem::VectorFEMassIntegrator>(*m_stellar_mass_coefficient);
|
||||
auto vacuum_integrator = std::make_unique<mfem::VectorFEMassIntegrator>(*m_vacuum_mass_coefficient);
|
||||
|
||||
m_fem.quadratureFactory->configure_gravity_hdiv_mass(
|
||||
*stellar_integrator, quadrature::QuadratureRole::discretization, stellar_element, stellar_transformation,
|
||||
utils::DOMAINS::STELLAR, quadrature::MappingKind::general
|
||||
);
|
||||
|
||||
m_fem.quadratureFactory->configure_gravity_hdiv_mass(
|
||||
*stellar_integrator, quadrature::QuadratureRole::discretization,
|
||||
stellar_element, stellar_transformation, utils::DOMAINS::STELLAR,
|
||||
quadrature::MappingKind::general
|
||||
*vacuum_integrator, quadrature::QuadratureRole::discretization, vacuum_element, vacuum_transformation,
|
||||
utils::DOMAINS::VACUUM, quadrature::MappingKind::kelvin
|
||||
);
|
||||
|
||||
m_fem.quadratureFactory->configure_gravity_hdiv_mass(
|
||||
*vacuum_integrator, quadrature::QuadratureRole::discretization,
|
||||
vacuum_element, vacuum_transformation, utils::DOMAINS::VACUUM,
|
||||
quadrature::MappingKind::kelvin
|
||||
);
|
||||
|
||||
m_mass_form->AddDomainIntegrator(
|
||||
stellar_integrator.release(), m_stellar_marker
|
||||
);
|
||||
m_mass_form->AddDomainIntegrator(
|
||||
vacuum_integrator.release(), m_vacuum_marker
|
||||
);
|
||||
m_mass_form->AddDomainIntegrator(stellar_integrator.release(), m_stellar_marker);
|
||||
m_mass_form->AddDomainIntegrator(vacuum_integrator.release(), m_vacuum_marker);
|
||||
m_mass_form->Assemble();
|
||||
|
||||
m_is_prepared = true;
|
||||
@@ -434,8 +353,7 @@ namespace mean_field::operators {
|
||||
return m_is_prepared;
|
||||
}
|
||||
|
||||
std::uint64_t
|
||||
PreparedMappedHDivMassOperator::GetPreparationCount() const noexcept {
|
||||
std::uint64_t PreparedMappedHDivMassOperator::GetPreparationCount() const noexcept {
|
||||
return m_preparation_count;
|
||||
}
|
||||
} // namespace mean_field::operators
|
||||
File diff suppressed because it is too large
Load Diff
668
libmeanfield/impl/operators/prepared_mass_normalization.cpp
Normal file
668
libmeanfield/impl/operators/prepared_mass_normalization.cpp
Normal file
@@ -0,0 +1,668 @@
|
||||
module;
|
||||
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <mfem.hpp>
|
||||
|
||||
module mean_field;
|
||||
|
||||
import :operators.prepared_mass_normalization;
|
||||
|
||||
namespace {
|
||||
void validate_finite_vector(
|
||||
const mfem::Vector &vector,
|
||||
const char *message
|
||||
) {
|
||||
for (int index = 0; index < vector.Size(); ++index) {
|
||||
MFEM_VERIFY(std::isfinite(vector(index)), message);
|
||||
}
|
||||
}
|
||||
|
||||
void true_to_local(
|
||||
const mfem::ParFiniteElementSpace &finiteElementSpace,
|
||||
const mfem::Vector &trueVector,
|
||||
mfem::Vector &localVector
|
||||
) {
|
||||
MFEM_VERIFY(trueVector.Size() == finiteElementSpace.GetTrueVSize(), "True vector has the wrong size.");
|
||||
|
||||
localVector.SetSize(finiteElementSpace.GetVSize());
|
||||
|
||||
const mfem::Operator *prolongation = finiteElementSpace.GetProlongationMatrix();
|
||||
|
||||
if (prolongation != nullptr) {
|
||||
prolongation->Mult(trueVector, localVector);
|
||||
} else {
|
||||
localVector = trueVector;
|
||||
}
|
||||
}
|
||||
|
||||
const mfem::IntegrationRule &get_mass_normalization_rule(
|
||||
const mean_field::fem::FEM &f,
|
||||
const mfem::FiniteElement &densityElement,
|
||||
const mfem::ElementTransformation &transformation
|
||||
) {
|
||||
using DensityField = mean_field::field::Field<mean_field::field::Density>;
|
||||
|
||||
MFEM_VERIFY(
|
||||
densityElement.GetOrder() == mean_field::field::Density::Scalar::familyOrder,
|
||||
"The mass-normalization element does not match the registered "
|
||||
"density field."
|
||||
);
|
||||
|
||||
const mean_field::quadrature::Query query =
|
||||
DensityField::make_query<mean_field::field::Density::Form::MassNormalization>(
|
||||
mean_field::quadrature::QuadratureRole::discretization, transformation.OrderW(), std::array<int, 0>{},
|
||||
mean_field::utils::DOMAINS::STELLAR, mean_field::quadrature::MappingKind::general
|
||||
);
|
||||
|
||||
const auto resolution = f.quadratureFactory->get(query, transformation.GetGeometryType());
|
||||
|
||||
MFEM_VERIFY(
|
||||
resolution.integration_rule != nullptr, "The quadrature policy did not return a mass-normalization rule."
|
||||
);
|
||||
|
||||
return *resolution.integration_rule;
|
||||
}
|
||||
|
||||
void validate_shared_gravity_revisions(
|
||||
const mean_field::operators::context::gravity_field::GravityFieldLinearizationContext &gravityContext,
|
||||
const mean_field::operators::MassNormalizationDependencies &dependencies
|
||||
) {
|
||||
MFEM_VERIFY(
|
||||
gravityContext.IsPrepared(), "PreparedMassNormalizationOperator requires the shared gravity "
|
||||
"linearization context to be prepared first."
|
||||
);
|
||||
|
||||
const auto &revisions = gravityContext.GetRevisions();
|
||||
|
||||
MFEM_VERIFY(
|
||||
revisions.discretization.value == dependencies.discretization.revision &&
|
||||
revisions.density.value == dependencies.density.revision &&
|
||||
revisions.displacement.value == dependencies.displacement.revision,
|
||||
"PreparedMassNormalizationOperator received dependency revisions "
|
||||
"that do not match the shared gravity context."
|
||||
);
|
||||
}
|
||||
|
||||
void validate_shared_identity_transition(
|
||||
const mean_field::operators::MassNormalizationDependencyStamp &prepared,
|
||||
const mean_field::operators::MassNormalizationDependencyStamp &requested,
|
||||
const char *message
|
||||
) {
|
||||
MFEM_VERIFY(prepared.identity == requested.identity || prepared.revision != requested.revision, message);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace mean_field::operators {
|
||||
PreparedMassNormalizationOperator::PreparedMassNormalizationOperator(
|
||||
const fem::FEM &f,
|
||||
const mapping::DomainMapperStateless &domainMapper,
|
||||
const context::gravity_field::GravityFieldLinearizationContext &gravityContext
|
||||
)
|
||||
: m_fem(f),
|
||||
m_domainMapper(domainMapper),
|
||||
m_gravityContext(gravityContext) {
|
||||
MFEM_VERIFY(m_fem.mesh != nullptr, "PreparedMassNormalizationOperator requires a mesh.");
|
||||
MFEM_VERIFY(
|
||||
m_fem.densityFes != nullptr && m_fem.displacementFes != nullptr && m_fem.compactificationFes != nullptr &&
|
||||
m_fem.compactificationCoordinate != nullptr && m_fem.quadratureFactory != nullptr,
|
||||
"PreparedMassNormalizationOperator requires density, "
|
||||
"displacement, compactification, and quadrature data."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
m_domainMapper.GetDimension() == m_fem.mesh->Dimension(),
|
||||
"PreparedMassNormalizationOperator received a mapper with the "
|
||||
"wrong dimension."
|
||||
);
|
||||
}
|
||||
|
||||
PreparedMassNormalizationReport PreparedMassNormalizationOperator::Prepare(
|
||||
const MassNormalizationStateView &state,
|
||||
const MassNormalizationDependencies &dependencies
|
||||
) {
|
||||
MFEM_VERIFY(
|
||||
std::isfinite(state.targetMass) && state.targetMass > 0.0,
|
||||
"PreparedMassNormalizationOperator requires a finite, positive "
|
||||
"target mass."
|
||||
);
|
||||
|
||||
validate_shared_gravity_revisions(m_gravityContext, dependencies);
|
||||
|
||||
if (m_isPrepared) {
|
||||
validate_shared_identity_transition(
|
||||
m_preparedDependencies.discretization, dependencies.discretization,
|
||||
"A new mass-normalization discretization identity must also "
|
||||
"change the shared gravity revision."
|
||||
);
|
||||
validate_shared_identity_transition(
|
||||
m_preparedDependencies.density, dependencies.density,
|
||||
"A new mass-normalization density identity must also change "
|
||||
"the shared gravity revision."
|
||||
);
|
||||
validate_shared_identity_transition(
|
||||
m_preparedDependencies.displacement, dependencies.displacement,
|
||||
"A new mass-normalization displacement identity must also "
|
||||
"change the shared gravity revision."
|
||||
);
|
||||
}
|
||||
|
||||
const bool rebuildStaticPlan =
|
||||
!m_isPrepared || dependencies.discretization != m_preparedDependencies.discretization;
|
||||
|
||||
const bool refreshGeometry =
|
||||
rebuildStaticPlan || dependencies.displacement != m_preparedDependencies.displacement;
|
||||
|
||||
const bool refreshDensity = rebuildStaticPlan || dependencies.density != m_preparedDependencies.density;
|
||||
|
||||
const bool updateTargetMass = !m_isPrepared || dependencies.targetMass != m_preparedDependencies.targetMass ||
|
||||
state.targetMass != m_targetMass;
|
||||
|
||||
m_isPrepared = false;
|
||||
|
||||
PreparedMassNormalizationReport report;
|
||||
|
||||
if (rebuildStaticPlan) {
|
||||
BuildStaticPlan();
|
||||
report.rebuiltStaticPlan = true;
|
||||
}
|
||||
|
||||
if (refreshGeometry) {
|
||||
RefreshGeometry(m_gravityContext.GetGeometryContext().GetDisplacement());
|
||||
report.refreshedGeometry = true;
|
||||
}
|
||||
|
||||
if (refreshDensity) {
|
||||
RefreshDensity(m_gravityContext.GetDensity());
|
||||
report.refreshedDensity = true;
|
||||
}
|
||||
|
||||
if (updateTargetMass) {
|
||||
m_targetMass = state.targetMass;
|
||||
report.updatedTargetMass = true;
|
||||
}
|
||||
|
||||
if (refreshGeometry || refreshDensity) {
|
||||
AssembleResidual();
|
||||
report.assembledResidual = true;
|
||||
} else if (updateTargetMass) {
|
||||
m_cachedResidual.SetSize(1);
|
||||
m_cachedResidual(0) = m_currentMass - m_targetMass;
|
||||
++m_preparationCount;
|
||||
report.assembledResidual = true;
|
||||
}
|
||||
|
||||
m_preparedDependencies = dependencies;
|
||||
m_isPrepared = true;
|
||||
return report;
|
||||
}
|
||||
|
||||
void PreparedMassNormalizationOperator::BuildStaticPlan() {
|
||||
m_elements.clear();
|
||||
m_elements.reserve(m_fem.mesh->GetNE());
|
||||
|
||||
const int vacuumAttribute = m_domainMapper.GetVacuumElementAttribute();
|
||||
|
||||
int localStellarElementCount = 0;
|
||||
|
||||
for (int elementId = 0; elementId < m_fem.mesh->GetNE(); ++elementId) {
|
||||
mfem::ElementTransformation *transformation = m_fem.mesh->GetElementTransformation(elementId);
|
||||
|
||||
MFEM_VERIFY(
|
||||
transformation != nullptr, "PreparedMassNormalizationOperator received a null element "
|
||||
"transformation."
|
||||
);
|
||||
|
||||
if (transformation->Attribute == vacuumAttribute) {
|
||||
continue;
|
||||
}
|
||||
|
||||
++localStellarElementCount;
|
||||
m_elements.emplace_back();
|
||||
ElementPAData &data = m_elements.back();
|
||||
data.elementId = elementId;
|
||||
|
||||
data.densityDofTransformation = m_fem.densityFes->GetElementDofs(elementId, data.densityDofs);
|
||||
|
||||
data.displacementDofTransformation =
|
||||
m_fem.displacementFes->GetElementVDofs(elementId, data.displacementDofs);
|
||||
|
||||
data.compactificationDofTransformation =
|
||||
m_fem.compactificationFes->GetElementDofs(elementId, data.compactificationDofs);
|
||||
|
||||
const mfem::FiniteElement &densityElement = *m_fem.densityFes->GetFE(elementId);
|
||||
|
||||
const mfem::IntegrationRule &integrationRule =
|
||||
get_mass_normalization_rule(m_fem, densityElement, *transformation);
|
||||
|
||||
data.quadraturePoints.resize(integrationRule.GetNPoints());
|
||||
|
||||
for (int quadraturePoint = 0; quadraturePoint < integrationRule.GetNPoints(); ++quadraturePoint) {
|
||||
QuadraturePointData &point = data.quadraturePoints[quadraturePoint];
|
||||
|
||||
point.integrationPoint = integrationRule.IntPoint(quadraturePoint);
|
||||
|
||||
point.densityShape.SetSize(densityElement.GetDof());
|
||||
densityElement.CalcShape(point.integrationPoint, point.densityShape);
|
||||
}
|
||||
}
|
||||
|
||||
int globalStellarElementCount = 0;
|
||||
MPI_Allreduce(
|
||||
&localStellarElementCount, &globalStellarElementCount, 1, MPI_INT, MPI_SUM, m_fem.mesh->GetComm()
|
||||
);
|
||||
|
||||
MFEM_VERIFY(globalStellarElementCount > 0, "PreparedMassNormalizationOperator found no stellar elements.");
|
||||
}
|
||||
|
||||
void PreparedMassNormalizationOperator::RefreshGeometry(const mfem::Vector &displacement) {
|
||||
MFEM_VERIFY(
|
||||
displacement.Size() == m_fem.displacementFes->GetTrueVSize(),
|
||||
"PreparedMassNormalizationOperator received a displacement "
|
||||
"vector with the wrong size."
|
||||
);
|
||||
validate_finite_vector(
|
||||
displacement, "PreparedMassNormalizationOperator received a non-finite "
|
||||
"displacement value."
|
||||
);
|
||||
|
||||
mfem::Vector displacementLocal;
|
||||
true_to_local(*m_fem.displacementFes, displacement, displacementLocal);
|
||||
|
||||
mapping::DomainMapperStateless::Workspace workspace(m_fem.mesh->Dimension());
|
||||
|
||||
for (ElementPAData &data : m_elements) {
|
||||
displacementLocal.GetSubVector(data.displacementDofs, data.baseDisplacement);
|
||||
|
||||
m_fem.compactificationCoordinate->GetSubVector(data.compactificationDofs, data.compactification);
|
||||
|
||||
if (data.displacementDofTransformation != nullptr) {
|
||||
data.displacementDofTransformation->InvTransformPrimal(data.baseDisplacement);
|
||||
}
|
||||
|
||||
if (data.compactificationDofTransformation != nullptr) {
|
||||
data.compactificationDofTransformation->InvTransformPrimal(data.compactification);
|
||||
}
|
||||
|
||||
const mfem::FiniteElement &displacementElement = *m_fem.displacementFes->GetFE(data.elementId);
|
||||
|
||||
const mfem::FiniteElement &compactificationElement = *m_fem.compactificationFes->GetFE(data.elementId);
|
||||
|
||||
const mapping::ElementDisplacementData displacementData =
|
||||
mapping::ElementDisplacementDataFromElementVDofs(displacementElement, data.baseDisplacement);
|
||||
|
||||
const mapping::ElementCompactificationData compactificationData(
|
||||
compactificationElement, data.compactification
|
||||
);
|
||||
|
||||
const mapping::ElementMappingData mappingData{
|
||||
.displacement = displacementData, .compactification = compactificationData
|
||||
};
|
||||
|
||||
mfem::ElementTransformation *transformation = m_fem.mesh->GetElementTransformation(data.elementId);
|
||||
|
||||
for (QuadraturePointData &point : data.quadraturePoints) {
|
||||
const mapping::MappingStatus status = m_domainMapper.EvaluateVolume(
|
||||
mappingData, *transformation, point.integrationPoint, workspace, point.mappingContext
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
status == mapping::MappingStatus::valid, "Stateless mapping failed while preparing mass "
|
||||
"normalization. Element: "
|
||||
<< data.elementId
|
||||
<< ", attribute: " << transformation->Attribute
|
||||
<< ", status: " << static_cast<int>(status)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PreparedMassNormalizationOperator::RefreshDensity(const mfem::Vector &density) {
|
||||
MFEM_VERIFY(
|
||||
density.Size() == m_fem.densityFes->GetTrueVSize(),
|
||||
"PreparedMassNormalizationOperator received a density vector "
|
||||
"with the wrong size."
|
||||
);
|
||||
validate_finite_vector(
|
||||
density, "PreparedMassNormalizationOperator received a non-finite density "
|
||||
"value."
|
||||
);
|
||||
|
||||
mfem::Vector densityLocal;
|
||||
true_to_local(*m_fem.densityFes, density, densityLocal);
|
||||
|
||||
mfem::Vector elementDensity;
|
||||
|
||||
for (ElementPAData &data : m_elements) {
|
||||
densityLocal.GetSubVector(data.densityDofs, elementDensity);
|
||||
|
||||
if (data.densityDofTransformation != nullptr) {
|
||||
data.densityDofTransformation->InvTransformPrimal(elementDensity);
|
||||
}
|
||||
|
||||
for (QuadraturePointData &point : data.quadraturePoints) {
|
||||
point.density = elementDensity * point.densityShape;
|
||||
MFEM_VERIFY(
|
||||
std::isfinite(point.density), "PreparedMassNormalizationOperator produced a non-finite "
|
||||
"quadrature density."
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PreparedMassNormalizationOperator::AssembleResidual() {
|
||||
double localMass = 0.0;
|
||||
|
||||
for (const ElementPAData &data : m_elements) {
|
||||
for (const QuadraturePointData &point : data.quadraturePoints) {
|
||||
localMass += point.density * point.mappingContext.quadrature.weight;
|
||||
}
|
||||
}
|
||||
|
||||
m_currentMass = GlobalSum(localMass);
|
||||
MFEM_VERIFY(std::isfinite(m_currentMass), "PreparedMassNormalizationOperator assembled a non-finite mass.");
|
||||
|
||||
m_cachedResidual.SetSize(1);
|
||||
m_cachedResidual(0) = m_currentMass - m_targetMass;
|
||||
++m_preparationCount;
|
||||
}
|
||||
|
||||
void PreparedMassNormalizationOperator::BuildResidual(mfem::Vector &residual) const {
|
||||
VerifyPrepared();
|
||||
residual = m_cachedResidual;
|
||||
++m_residualApplicationCount;
|
||||
}
|
||||
|
||||
double PreparedMassNormalizationOperator::EvaluateDensityActionLocal(const mfem::Vector &densityVariation) const {
|
||||
MFEM_VERIFY(
|
||||
densityVariation.Size() == m_fem.densityFes->GetTrueVSize(),
|
||||
"Mass-normalization density action received a vector with the "
|
||||
"wrong size."
|
||||
);
|
||||
validate_finite_vector(densityVariation, "Mass-normalization density action received a non-finite value.");
|
||||
|
||||
mfem::Vector densityVariationLocal;
|
||||
true_to_local(*m_fem.densityFes, densityVariation, densityVariationLocal);
|
||||
|
||||
mfem::Vector elementDensityVariation;
|
||||
double localAction = 0.0;
|
||||
|
||||
for (const ElementPAData &data : m_elements) {
|
||||
densityVariationLocal.GetSubVector(data.densityDofs, elementDensityVariation);
|
||||
|
||||
if (data.densityDofTransformation != nullptr) {
|
||||
data.densityDofTransformation->InvTransformPrimal(elementDensityVariation);
|
||||
}
|
||||
|
||||
for (const QuadraturePointData &point : data.quadraturePoints) {
|
||||
localAction += (elementDensityVariation * point.densityShape) * point.mappingContext.quadrature.weight;
|
||||
}
|
||||
}
|
||||
|
||||
return localAction;
|
||||
}
|
||||
|
||||
double PreparedMassNormalizationOperator::EvaluateDisplacementActionLocal(
|
||||
const mfem::Vector &displacementVariation
|
||||
) const {
|
||||
MFEM_VERIFY(
|
||||
displacementVariation.Size() == m_fem.displacementFes->GetTrueVSize(),
|
||||
"Mass-normalization displacement action received a vector with "
|
||||
"the wrong size."
|
||||
);
|
||||
validate_finite_vector(
|
||||
displacementVariation, "Mass-normalization displacement action received a non-finite "
|
||||
"value."
|
||||
);
|
||||
|
||||
mfem::Vector displacementVariationLocal;
|
||||
true_to_local(*m_fem.displacementFes, displacementVariation, displacementVariationLocal);
|
||||
|
||||
mapping::DomainMapperStateless::Workspace workspace(m_fem.mesh->Dimension());
|
||||
|
||||
mfem::Vector elementDisplacementVariation;
|
||||
double localAction = 0.0;
|
||||
|
||||
for (const ElementPAData &data : m_elements) {
|
||||
displacementVariationLocal.GetSubVector(data.displacementDofs, elementDisplacementVariation);
|
||||
|
||||
if (data.displacementDofTransformation != nullptr) {
|
||||
data.displacementDofTransformation->InvTransformPrimal(elementDisplacementVariation);
|
||||
}
|
||||
|
||||
const mfem::FiniteElement &displacementElement = *m_fem.displacementFes->GetFE(data.elementId);
|
||||
|
||||
const mfem::FiniteElement &compactificationElement = *m_fem.compactificationFes->GetFE(data.elementId);
|
||||
|
||||
const mapping::ElementDisplacementData baseDisplacementData =
|
||||
mapping::ElementDisplacementDataFromElementVDofs(displacementElement, data.baseDisplacement);
|
||||
|
||||
const mapping::ElementDisplacementData directionData =
|
||||
mapping::ElementDisplacementDataFromElementVDofs(displacementElement, elementDisplacementVariation);
|
||||
|
||||
const mapping::ElementCompactificationData compactificationData(
|
||||
compactificationElement, data.compactification
|
||||
);
|
||||
|
||||
const mapping::ElementMappingData mappingData{
|
||||
.displacement = baseDisplacementData, .compactification = compactificationData
|
||||
};
|
||||
|
||||
mfem::ElementTransformation *transformation = m_fem.mesh->GetElementTransformation(data.elementId);
|
||||
|
||||
for (const QuadraturePointData &point : data.quadraturePoints) {
|
||||
mapping::VolumeMappingVariation variation;
|
||||
|
||||
const mapping::MappingStatus status = m_domainMapper.EvaluateVolumeVariation(
|
||||
mappingData, directionData, *transformation, point.integrationPoint, point.mappingContext,
|
||||
workspace, variation
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
status == mapping::MappingStatus::valid, "Stateless mapping variation failed in the "
|
||||
"mass-normalization displacement action. Element: "
|
||||
<< data.elementId
|
||||
<< ", status: " << static_cast<int>(status)
|
||||
);
|
||||
|
||||
localAction += point.density * variation.weight_variation;
|
||||
}
|
||||
}
|
||||
|
||||
return localAction;
|
||||
}
|
||||
|
||||
void PreparedMassNormalizationOperator::ApplyDensityJacobianAction(
|
||||
const mfem::Vector &densityVariation,
|
||||
mfem::Vector &action
|
||||
) const {
|
||||
VerifyPrepared();
|
||||
action.SetSize(1);
|
||||
action(0) = GlobalSum(EvaluateDensityActionLocal(densityVariation));
|
||||
++m_actionStatistics.densityApplications;
|
||||
}
|
||||
|
||||
void PreparedMassNormalizationOperator::ApplyDisplacementJacobianAction(
|
||||
const mfem::Vector &displacementVariation,
|
||||
mfem::Vector &action
|
||||
) const {
|
||||
VerifyPrepared();
|
||||
action.SetSize(1);
|
||||
action(0) = GlobalSum(EvaluateDisplacementActionLocal(displacementVariation));
|
||||
++m_actionStatistics.displacementApplications;
|
||||
}
|
||||
|
||||
void PreparedMassNormalizationOperator::ApplyCompleteJacobianAction(
|
||||
const mfem::Vector &densityVariation,
|
||||
const mfem::Vector &displacementVariation,
|
||||
mfem::Vector &action
|
||||
) const {
|
||||
VerifyPrepared();
|
||||
|
||||
const double localAction =
|
||||
EvaluateDensityActionLocal(densityVariation) + EvaluateDisplacementActionLocal(displacementVariation);
|
||||
|
||||
action.SetSize(1);
|
||||
action(0) = GlobalSum(localAction);
|
||||
++m_actionStatistics.completeApplications;
|
||||
}
|
||||
|
||||
double PreparedMassNormalizationOperator::GlobalSum(const double localValue) const {
|
||||
double globalValue = 0.0;
|
||||
MPI_Allreduce(&localValue, &globalValue, 1, MPI_DOUBLE, MPI_SUM, m_fem.mesh->GetComm());
|
||||
return globalValue;
|
||||
}
|
||||
|
||||
bool PreparedMassNormalizationOperator::IsPrepared() const noexcept {
|
||||
if (!m_isPrepared || !m_gravityContext.IsPrepared()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto &revisions = m_gravityContext.GetRevisions();
|
||||
return revisions.discretization.value == m_preparedDependencies.discretization.revision &&
|
||||
revisions.density.value == m_preparedDependencies.density.revision &&
|
||||
revisions.displacement.value == m_preparedDependencies.displacement.revision;
|
||||
}
|
||||
|
||||
double PreparedMassNormalizationOperator::GetCurrentMass() const {
|
||||
VerifyPrepared();
|
||||
return m_currentMass;
|
||||
}
|
||||
|
||||
double PreparedMassNormalizationOperator::GetTargetMass() const {
|
||||
VerifyPrepared();
|
||||
return m_targetMass;
|
||||
}
|
||||
|
||||
std::uint64_t PreparedMassNormalizationOperator::GetPreparationCount() const noexcept {
|
||||
return m_preparationCount;
|
||||
}
|
||||
|
||||
std::uint64_t PreparedMassNormalizationOperator::GetResidualApplicationCount() const noexcept {
|
||||
return m_residualApplicationCount;
|
||||
}
|
||||
|
||||
const PreparedMassNormalizationActionStatistics &
|
||||
PreparedMassNormalizationOperator::GetActionStatistics() const noexcept {
|
||||
return m_actionStatistics;
|
||||
}
|
||||
|
||||
const fem::FEM &PreparedMassNormalizationOperator::GetFEM() const noexcept {
|
||||
return m_fem;
|
||||
}
|
||||
|
||||
const context::gravity_field::GravityFieldLinearizationContext &
|
||||
PreparedMassNormalizationOperator::GetGravityContext() const noexcept {
|
||||
return m_gravityContext;
|
||||
}
|
||||
|
||||
void PreparedMassNormalizationOperator::VerifyPrepared() const {
|
||||
MFEM_VERIFY(
|
||||
IsPrepared(), "PreparedMassNormalizationOperator must be prepared for the "
|
||||
"current shared gravity-context revisions."
|
||||
);
|
||||
}
|
||||
|
||||
PreparedMassNormalizationJacobianOperator::PreparedMassNormalizationJacobianOperator(
|
||||
const MassNormalizationLayout &layout,
|
||||
const PreparedMassNormalizationOperator &preparedOperator
|
||||
)
|
||||
: mfem::Operator(
|
||||
layout.residual_offsets().Last(),
|
||||
layout.value_offsets().Last()
|
||||
),
|
||||
m_layout(layout),
|
||||
m_preparedOperator(preparedOperator) {
|
||||
const fem::FEM &f = m_preparedOperator.GetFEM();
|
||||
|
||||
MFEM_VERIFY(
|
||||
f.densityFes != nullptr && f.displacementFes != nullptr && f.gravityFluxFes != nullptr &&
|
||||
f.gravityPotentialFes != nullptr && f.enthalpyFes != nullptr,
|
||||
"Prepared mass-normalization MFEM adapter requires every "
|
||||
"finite-element space in the barotropic equilibrium layout."
|
||||
);
|
||||
|
||||
using Form = utils::blocks::barotropic_equilibrium_form;
|
||||
|
||||
constexpr auto densityValue = utils::blocks::get_value_block<Form>(utils::blocks::density_field.mass_term);
|
||||
constexpr auto displacementValue =
|
||||
utils::blocks::get_value_block<Form>(utils::blocks::displacement_field.geometry_term);
|
||||
constexpr auto gravityGradientValue =
|
||||
utils::blocks::get_value_block<Form>(utils::blocks::gravity_field.gradient_term);
|
||||
constexpr auto gravityPotentialValue =
|
||||
utils::blocks::get_value_block<Form>(utils::blocks::gravity_field.poisson_term);
|
||||
constexpr auto enthalpyValue =
|
||||
utils::blocks::get_value_block<Form>(utils::blocks::enthalpy_field.specific_term);
|
||||
constexpr auto barotropicConstantValue =
|
||||
utils::blocks::get_value_block<Form>(utils::blocks::barotropic_constant_field.mass_normalization_term);
|
||||
constexpr auto gravityGradientResidual =
|
||||
utils::blocks::get_residual_block<Form>(utils::blocks::gravity_field.gradient_term);
|
||||
constexpr auto gravityPotentialResidual =
|
||||
utils::blocks::get_residual_block<Form>(utils::blocks::gravity_field.poisson_term);
|
||||
constexpr auto densityResidual =
|
||||
utils::blocks::get_residual_block<Form>(utils::blocks::density_field.mass_term);
|
||||
constexpr auto displacementResidual =
|
||||
utils::blocks::get_residual_block<Form>(utils::blocks::displacement_field.geometry_term);
|
||||
constexpr auto enthalpyResidual =
|
||||
utils::blocks::get_residual_block<Form>(utils::blocks::enthalpy_field.specific_term);
|
||||
constexpr auto massResidual =
|
||||
utils::blocks::get_residual_block<Form>(utils::blocks::barotropic_constant_field.mass_normalization_term);
|
||||
|
||||
MFEM_VERIFY(
|
||||
m_layout.size(densityValue) == f.densityFes->GetTrueVSize() &&
|
||||
m_layout.size(displacementValue) == f.displacementFes->GetTrueVSize() &&
|
||||
m_layout.size(gravityGradientValue) == f.gravityFluxFes->GetTrueVSize() &&
|
||||
m_layout.size(gravityPotentialValue) == f.gravityPotentialFes->GetTrueVSize() &&
|
||||
m_layout.size(enthalpyValue) == f.enthalpyFes->GetTrueVSize() &&
|
||||
m_layout.size(barotropicConstantValue) == 1 &&
|
||||
m_layout.size(gravityGradientResidual) == f.gravityFluxFes->GetTrueVSize() &&
|
||||
m_layout.size(gravityPotentialResidual) == f.gravityPotentialFes->GetTrueVSize() &&
|
||||
m_layout.size(densityResidual) == f.densityFes->GetTrueVSize() &&
|
||||
m_layout.size(displacementResidual) == f.displacementFes->GetTrueVSize() &&
|
||||
m_layout.size(enthalpyResidual) == f.enthalpyFes->GetTrueVSize() && m_layout.size(massResidual) == 1,
|
||||
"Prepared mass-normalization MFEM adapter received incompatible "
|
||||
"barotropic block sizes."
|
||||
);
|
||||
}
|
||||
|
||||
void PreparedMassNormalizationJacobianOperator::Mult(
|
||||
const mfem::Vector &direction,
|
||||
mfem::Vector &action
|
||||
) const {
|
||||
MFEM_VERIFY(
|
||||
m_preparedOperator.IsPrepared(), "Prepared mass-normalization MFEM adapter requires a prepared "
|
||||
"row operator."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
direction.Size() == Width(), "Prepared mass-normalization MFEM adapter received a direction "
|
||||
"with the wrong size."
|
||||
);
|
||||
|
||||
using Form = utils::blocks::barotropic_equilibrium_form;
|
||||
|
||||
constexpr auto densityValue = utils::blocks::get_value_block<Form>(utils::blocks::density_field.mass_term);
|
||||
constexpr auto displacementValue =
|
||||
utils::blocks::get_value_block<Form>(utils::blocks::displacement_field.geometry_term);
|
||||
constexpr auto massResidual =
|
||||
utils::blocks::get_residual_block<Form>(utils::blocks::barotropic_constant_field.mass_normalization_term);
|
||||
|
||||
const mfem::Vector densityVariation(
|
||||
const_cast<mfem::real_t *>(direction.GetData()) + m_layout.offset(densityValue), m_layout.size(densityValue)
|
||||
);
|
||||
|
||||
const mfem::Vector displacementVariation(
|
||||
const_cast<mfem::real_t *>(direction.GetData()) + m_layout.offset(displacementValue),
|
||||
m_layout.size(displacementValue)
|
||||
);
|
||||
|
||||
mfem::Vector massAction;
|
||||
m_preparedOperator.ApplyCompleteJacobianAction(densityVariation, displacementVariation, massAction);
|
||||
|
||||
action.SetSize(Height());
|
||||
action = 0.0;
|
||||
action(m_layout.offset(massResidual)) = massAction(0);
|
||||
}
|
||||
|
||||
const MassNormalizationLayout &PreparedMassNormalizationJacobianOperator::GetLayout() const noexcept {
|
||||
return m_layout;
|
||||
}
|
||||
} // namespace mean_field::operators
|
||||
1137
libmeanfield/impl/operators/prepared_pressure_force.cpp
Normal file
1137
libmeanfield/impl/operators/prepared_pressure_force.cpp
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,288 @@
|
||||
module;
|
||||
|
||||
#include <mfem.hpp>
|
||||
|
||||
module mean_field;
|
||||
|
||||
import :operators.kernels.rotational_displacement_force;
|
||||
import :operators.prepared_rotational_displacement_force;
|
||||
|
||||
namespace mean_field::operators {
|
||||
PreparedRotationalDisplacementForceOperator::PreparedRotationalDisplacementForceOperator(
|
||||
const fem::FEM &f,
|
||||
const mapping::DomainMapperStateless &domainMapper
|
||||
)
|
||||
: m_fem(f),
|
||||
m_domainMapper(domainMapper),
|
||||
m_context(
|
||||
f,
|
||||
domainMapper
|
||||
) {
|
||||
MFEM_VERIFY(m_fem.mesh != nullptr, "PreparedRotationalDisplacementForceOperator requires a mesh.");
|
||||
|
||||
MFEM_VERIFY(
|
||||
m_fem.mesh->Dimension() == 3, "PreparedRotationalDisplacementForceOperator requires a "
|
||||
"three-dimensional mesh."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
m_fem.densityFes != nullptr && m_fem.displacementFes != nullptr,
|
||||
"PreparedRotationalDisplacementForceOperator requires density "
|
||||
"and displacement finite-element spaces."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
m_fem.compactificationFes != nullptr && m_fem.compactificationCoordinate != nullptr,
|
||||
"PreparedRotationalDisplacementForceOperator requires the "
|
||||
"compactification coordinate."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
m_fem.quadratureFactory != nullptr, "PreparedRotationalDisplacementForceOperator requires the "
|
||||
"quadrature-rule factory."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
m_domainMapper.GetDimension() == m_fem.mesh->Dimension(),
|
||||
"PreparedRotationalDisplacementForceOperator received a mapper "
|
||||
"with the wrong dimension."
|
||||
);
|
||||
}
|
||||
|
||||
PreparedRotationalDisplacementForceReport PreparedRotationalDisplacementForceOperator::Prepare(
|
||||
const context::rotational_displacement_force::RotationalDisplacementForceStateView &state,
|
||||
const context::rotational_displacement_force::RotationalDisplacementForceDependencies &dependencies,
|
||||
const physics::RigidRotation &rotation
|
||||
) {
|
||||
const bool rotationChanged =
|
||||
!m_context.IsPrepared() || dependencies.rotation != m_context.GetDependencies().rotation;
|
||||
|
||||
PreparedRotationalDisplacementForceReport report;
|
||||
report.contextReport = m_context.Prepare(state, dependencies);
|
||||
|
||||
if (!report.contextReport.DidAnyWork()) {
|
||||
return report;
|
||||
}
|
||||
|
||||
m_isPrepared = false;
|
||||
|
||||
if (rotationChanged) {
|
||||
m_rotation = rotation;
|
||||
report.updatedRotation = true;
|
||||
}
|
||||
|
||||
MFEM_VERIFY(
|
||||
m_rotation.has_value(), "PreparedRotationalDisplacementForceOperator has no frozen "
|
||||
"rotation state."
|
||||
);
|
||||
|
||||
if (report.contextReport.preparedBaseState) {
|
||||
kernels::apply_rotational_displacement_force_residual(
|
||||
m_fem, m_domainMapper, *m_rotation, m_context.GetBaseDensityTrue(), m_context.GetDisplacementTrue(),
|
||||
m_cachedResidual
|
||||
);
|
||||
|
||||
++m_residualPreparationCount;
|
||||
report.preparedResidual = true;
|
||||
}
|
||||
|
||||
MFEM_VERIFY(
|
||||
m_cachedResidual.Size() == m_fem.displacementFes->GetTrueVSize(),
|
||||
"The prepared rotational-displacement-force residual has the "
|
||||
"wrong size."
|
||||
);
|
||||
|
||||
m_preparedDependencies = dependencies;
|
||||
m_isPrepared = true;
|
||||
|
||||
return report;
|
||||
}
|
||||
|
||||
void PreparedRotationalDisplacementForceOperator::BuildResidual(mfem::Vector &residual) const {
|
||||
VerifyPrepared();
|
||||
residual = m_cachedResidual;
|
||||
++m_residualApplicationCount;
|
||||
}
|
||||
|
||||
void PreparedRotationalDisplacementForceOperator::ApplyDensityJacobianAction(
|
||||
const mfem::Vector &densityVariation,
|
||||
mfem::Vector &action
|
||||
) const {
|
||||
VerifyPrepared();
|
||||
|
||||
kernels::apply_rotational_displacement_force_density_action(
|
||||
m_fem, m_domainMapper, *m_rotation, densityVariation, m_context.GetDisplacementTrue(), action
|
||||
);
|
||||
|
||||
++m_densityJacobianStatistics.applications;
|
||||
}
|
||||
|
||||
void PreparedRotationalDisplacementForceOperator::ApplyDisplacementJacobianAction(
|
||||
const mfem::Vector &displacementVariation,
|
||||
mfem::Vector &action
|
||||
) const {
|
||||
VerifyPrepared();
|
||||
|
||||
kernels::apply_rotational_displacement_force_displacement_action(
|
||||
m_fem, m_domainMapper, *m_rotation, m_context.GetBaseDensityTrue(), displacementVariation,
|
||||
m_context.GetDisplacementTrue(), action
|
||||
);
|
||||
|
||||
++m_displacementJacobianStatistics.applications;
|
||||
}
|
||||
|
||||
void PreparedRotationalDisplacementForceOperator::ApplyCompleteJacobianAction(
|
||||
const mfem::Vector &densityVariation,
|
||||
const mfem::Vector &displacementVariation,
|
||||
mfem::Vector &action
|
||||
) const {
|
||||
VerifyPrepared();
|
||||
|
||||
kernels::apply_rotational_displacement_force_complete_action(
|
||||
m_fem, m_domainMapper, *m_rotation, m_context.GetBaseDensityTrue(), densityVariation, displacementVariation,
|
||||
m_context.GetDisplacementTrue(), action
|
||||
);
|
||||
|
||||
++m_densityJacobianStatistics.applications;
|
||||
++m_displacementJacobianStatistics.applications;
|
||||
++m_completeJacobianStatistics.applications;
|
||||
}
|
||||
|
||||
bool PreparedRotationalDisplacementForceOperator::IsPrepared() const noexcept {
|
||||
return m_isPrepared && m_rotation.has_value() && m_context.MatchesDependencies(m_preparedDependencies);
|
||||
}
|
||||
|
||||
const context::rotational_displacement_force::RotationalDisplacementForcePreparationStatistics &
|
||||
PreparedRotationalDisplacementForceOperator::GetContextPreparationStatistics() const noexcept {
|
||||
return m_context.GetPreparationStatistics();
|
||||
}
|
||||
|
||||
std::uint64_t PreparedRotationalDisplacementForceOperator::GetResidualPreparationCount() const noexcept {
|
||||
return m_residualPreparationCount;
|
||||
}
|
||||
|
||||
std::uint64_t PreparedRotationalDisplacementForceOperator::GetResidualApplicationCount() const noexcept {
|
||||
return m_residualApplicationCount;
|
||||
}
|
||||
|
||||
const PreparedRotationalDisplacementForceColumnStatistics &
|
||||
PreparedRotationalDisplacementForceOperator::GetDensityJacobianStatistics() const noexcept {
|
||||
return m_densityJacobianStatistics;
|
||||
}
|
||||
|
||||
const PreparedRotationalDisplacementForceColumnStatistics &
|
||||
PreparedRotationalDisplacementForceOperator::GetDisplacementJacobianStatistics() const noexcept {
|
||||
return m_displacementJacobianStatistics;
|
||||
}
|
||||
|
||||
const PreparedRotationalDisplacementForceCompleteStatistics &
|
||||
PreparedRotationalDisplacementForceOperator::GetCompleteJacobianStatistics() const noexcept {
|
||||
return m_completeJacobianStatistics;
|
||||
}
|
||||
|
||||
const fem::FEM &PreparedRotationalDisplacementForceOperator::GetFEM() const noexcept {
|
||||
return m_fem;
|
||||
}
|
||||
|
||||
const context::rotational_displacement_force::RotationalDisplacementForceLinearizationContext &
|
||||
PreparedRotationalDisplacementForceOperator::GetContext() const noexcept {
|
||||
return m_context;
|
||||
}
|
||||
|
||||
void PreparedRotationalDisplacementForceOperator::VerifyPrepared() const {
|
||||
MFEM_VERIFY(
|
||||
IsPrepared(), "PreparedRotationalDisplacementForceOperator must be prepared "
|
||||
"for the current revisions before residual or Jacobian "
|
||||
"application."
|
||||
);
|
||||
}
|
||||
|
||||
PreparedRotationalDisplacementForceJacobianOperator::PreparedRotationalDisplacementForceJacobianOperator(
|
||||
const RotationalDisplacementForceLayout &layout,
|
||||
const PreparedRotationalDisplacementForceOperator &preparedOperator
|
||||
)
|
||||
: mfem::Operator(
|
||||
layout.residual_offsets().Last(),
|
||||
layout.value_offsets().Last()
|
||||
),
|
||||
m_layout(layout),
|
||||
m_preparedOperator(preparedOperator) {
|
||||
const fem::FEM &f = m_preparedOperator.GetFEM();
|
||||
|
||||
using Form = utils::blocks::barotropic_equilibrium_form;
|
||||
|
||||
constexpr auto densityValue = utils::blocks::get_value_block<Form>(utils::blocks::density_field.mass_term);
|
||||
|
||||
constexpr auto displacementValue =
|
||||
utils::blocks::get_value_block<Form>(utils::blocks::displacement_field.geometry_term);
|
||||
|
||||
constexpr auto displacementResidual =
|
||||
utils::blocks::get_residual_block<Form>(utils::blocks::displacement_field.geometry_term);
|
||||
|
||||
MFEM_VERIFY(
|
||||
m_layout.size(densityValue) == f.densityFes->GetTrueVSize() &&
|
||||
m_layout.size(displacementValue) == f.displacementFes->GetTrueVSize() &&
|
||||
m_layout.size(displacementResidual) == f.displacementFes->GetTrueVSize(),
|
||||
"Prepared rotational-displacement-force MFEM adapter received "
|
||||
"incompatible coupled block sizes."
|
||||
);
|
||||
}
|
||||
|
||||
void PreparedRotationalDisplacementForceJacobianOperator::Mult(
|
||||
const mfem::Vector &direction,
|
||||
mfem::Vector &action
|
||||
) const {
|
||||
MFEM_VERIFY(
|
||||
m_preparedOperator.IsPrepared(), "Prepared rotational-displacement-force MFEM adapter requires "
|
||||
"a prepared operator."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
direction.Size() == Width(), "Prepared rotational-displacement-force MFEM adapter received "
|
||||
"a direction with the wrong size."
|
||||
);
|
||||
|
||||
using Form = utils::blocks::barotropic_equilibrium_form;
|
||||
|
||||
constexpr auto densityValue = utils::blocks::get_value_block<Form>(utils::blocks::density_field.mass_term);
|
||||
|
||||
constexpr auto displacementValue =
|
||||
utils::blocks::get_value_block<Form>(utils::blocks::displacement_field.geometry_term);
|
||||
|
||||
constexpr auto displacementResidual =
|
||||
utils::blocks::get_residual_block<Form>(utils::blocks::displacement_field.geometry_term);
|
||||
|
||||
const mfem::Vector densityVariation(
|
||||
const_cast<mfem::real_t *>(direction.GetData()) + m_layout.offset(densityValue), m_layout.size(densityValue)
|
||||
);
|
||||
|
||||
const mfem::Vector displacementVariation(
|
||||
const_cast<mfem::real_t *>(direction.GetData()) + m_layout.offset(displacementValue),
|
||||
m_layout.size(displacementValue)
|
||||
);
|
||||
|
||||
mfem::Vector displacementAction;
|
||||
|
||||
m_preparedOperator.ApplyCompleteJacobianAction(densityVariation, displacementVariation, displacementAction);
|
||||
|
||||
MFEM_VERIFY(
|
||||
displacementAction.Size() == m_layout.size(displacementResidual),
|
||||
"Prepared rotational-displacement-force MFEM adapter produced "
|
||||
"a displacement action with the wrong size."
|
||||
);
|
||||
|
||||
action.SetSize(Height());
|
||||
action = 0.0;
|
||||
|
||||
const int residualOffset = m_layout.offset(displacementResidual);
|
||||
|
||||
for (int entry = 0; entry < displacementAction.Size(); ++entry) {
|
||||
action(residualOffset + entry) = displacementAction(entry);
|
||||
}
|
||||
}
|
||||
|
||||
const RotationalDisplacementForceLayout &
|
||||
PreparedRotationalDisplacementForceJacobianOperator::GetLayout() const noexcept {
|
||||
return m_layout;
|
||||
}
|
||||
} // namespace mean_field::operators
|
||||
809
libmeanfield/impl/operators/prepared_stellar_equilibrium.cpp
Normal file
809
libmeanfield/impl/operators/prepared_stellar_equilibrium.cpp
Normal file
@@ -0,0 +1,809 @@
|
||||
module;
|
||||
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <utility>
|
||||
|
||||
#include <mfem.hpp>
|
||||
|
||||
module mean_field;
|
||||
|
||||
import :operators.prepared_stellar_equilibrium;
|
||||
import :physics.gravity;
|
||||
|
||||
namespace {
|
||||
using DomainSchema = mean_field::utils::domain::CoreEnvelopeVacuumDomainSchema;
|
||||
|
||||
[[nodiscard]] mean_field::fem::FEM &ensure_gravity_static_operators(mean_field::fem::FEM &f) {
|
||||
MFEM_VERIFY(
|
||||
f.mesh != nullptr && f.densityFes != nullptr && f.displacementFes != nullptr &&
|
||||
f.gravityFluxFes != nullptr && f.gravityPotentialFes != nullptr && f.enthalpyFes != nullptr,
|
||||
"PreparedStellarEquilibriumOperator requires the complete coupled finite-element discretization."
|
||||
);
|
||||
|
||||
if (f.gravityContext.b_form == nullptr || f.gravityContext.BT == nullptr) {
|
||||
mean_field::physics::update_stiffness_matrix(f);
|
||||
}
|
||||
|
||||
MFEM_VERIFY(
|
||||
f.gravityContext.b_form != nullptr && f.gravityContext.BT != nullptr,
|
||||
"PreparedStellarEquilibriumOperator could not initialize the static gravity divergence operators."
|
||||
);
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
[[nodiscard]] mean_field::operators::StellarEquilibriumLayout make_layout(
|
||||
const mean_field::field::FieldDofMap &densityMap,
|
||||
const mean_field::field::FieldDofMap &displacementMap,
|
||||
const mean_field::field::FieldDofMap &gravityFluxMap,
|
||||
const mean_field::field::FieldDofMap &gravityPotentialMap,
|
||||
const mean_field::field::FieldDofMap &enthalpyMap
|
||||
) {
|
||||
using Form = mean_field::utils::blocks::barotropic_equilibrium_form;
|
||||
|
||||
const std::array<int, Form::value_block_count> valueSizes{
|
||||
densityMap.reduced_size(), displacementMap.reduced_size(), gravityFluxMap.reduced_size(),
|
||||
gravityPotentialMap.reduced_size(), enthalpyMap.reduced_size(), 1
|
||||
};
|
||||
|
||||
const std::array<int, Form::residual_block_count> residualSizes{
|
||||
gravityFluxMap.reduced_size(), gravityPotentialMap.reduced_size(), densityMap.reduced_size(),
|
||||
displacementMap.reduced_size(), enthalpyMap.reduced_size(), 1
|
||||
};
|
||||
|
||||
return {valueSizes, residualSizes};
|
||||
}
|
||||
|
||||
[[nodiscard]] mfem::Array<int> make_gravity_state_offsets(const mean_field::fem::FEM &f) {
|
||||
mfem::Array<int> offsets(5);
|
||||
offsets[0] = 0;
|
||||
offsets[1] = offsets[0] + f.densityFes->GetTrueVSize();
|
||||
offsets[2] = offsets[1] + f.displacementFes->GetTrueVSize();
|
||||
offsets[3] = offsets[2] + f.gravityFluxFes->GetTrueVSize();
|
||||
offsets[4] = offsets[3] + f.gravityPotentialFes->GetTrueVSize();
|
||||
return offsets;
|
||||
}
|
||||
|
||||
[[nodiscard]] mfem::Array<int> make_gravity_residual_offsets(const mean_field::fem::FEM &f) {
|
||||
mfem::Array<int> offsets(3);
|
||||
offsets[0] = 0;
|
||||
offsets[1] = f.gravityFluxFes->GetTrueVSize();
|
||||
offsets[2] = offsets[1] + f.gravityPotentialFes->GetTrueVSize();
|
||||
return offsets;
|
||||
}
|
||||
|
||||
template <int index>
|
||||
[[nodiscard]] mfem::Vector make_value_view(
|
||||
const mfem::Vector &vector,
|
||||
const mean_field::operators::StellarEquilibriumLayout &layout,
|
||||
const mean_field::utils::blocks::value_block<index> block
|
||||
) {
|
||||
MFEM_VERIFY(
|
||||
vector.Size() == layout.value_offsets().Last(),
|
||||
"The coupled vector does not match the stellar-equilibrium value layout."
|
||||
);
|
||||
|
||||
return mfem::Vector(const_cast<mfem::real_t *>(vector.GetData()) + layout.offset(block), layout.size(block));
|
||||
}
|
||||
|
||||
template <int index>
|
||||
[[nodiscard]] mfem::Vector make_residual_view(
|
||||
mfem::Vector &vector,
|
||||
const mean_field::operators::StellarEquilibriumLayout &layout,
|
||||
const mean_field::utils::blocks::residual_block<index> block
|
||||
) {
|
||||
MFEM_VERIFY(
|
||||
vector.Size() == layout.residual_offsets().Last(),
|
||||
"The coupled vector does not match the stellar-equilibrium residual layout."
|
||||
);
|
||||
|
||||
return mfem::Vector(vector.GetData() + layout.offset(block), layout.size(block));
|
||||
}
|
||||
|
||||
template <int index>
|
||||
void assign_residual_block(
|
||||
mfem::Vector &coupledResidual,
|
||||
const mean_field::operators::StellarEquilibriumLayout &layout,
|
||||
const mean_field::utils::blocks::residual_block<index> block,
|
||||
const mfem::Vector &blockResidual,
|
||||
const char *message
|
||||
) {
|
||||
MFEM_VERIFY(layout.size(block) == blockResidual.Size(), message);
|
||||
mfem::Vector destination = make_residual_view(coupledResidual, layout, block);
|
||||
destination = blockResidual;
|
||||
}
|
||||
|
||||
void assign_gravity_block(
|
||||
mfem::Vector &gravityState,
|
||||
const mfem::Array<int> &offsets,
|
||||
const int blockIndex,
|
||||
const mfem::Vector &source,
|
||||
const char *message
|
||||
) {
|
||||
MFEM_VERIFY(offsets.Size() == 5, "Gravity state offsets are invalid.");
|
||||
MFEM_VERIFY(blockIndex >= 0 && blockIndex + 1 < offsets.Size(), "Requested gravity-state block is invalid.");
|
||||
|
||||
const int blockSize = offsets[blockIndex + 1] - offsets[blockIndex];
|
||||
MFEM_VERIFY(blockSize == source.Size(), message);
|
||||
MFEM_VERIFY(gravityState.Size() == offsets.Last(), "Packed gravity state has the wrong size.");
|
||||
|
||||
mfem::Vector destination(gravityState.GetData() + offsets[blockIndex], blockSize);
|
||||
destination = source;
|
||||
}
|
||||
|
||||
void pack_gravity_vector(
|
||||
mfem::Vector &gravityState,
|
||||
const mfem::Array<int> &offsets,
|
||||
const mfem::Vector &density,
|
||||
const mfem::Vector &displacement,
|
||||
const mfem::Vector &gravityGradient,
|
||||
const mfem::Vector &gravityPotential
|
||||
) {
|
||||
MFEM_VERIFY(offsets.Size() == 5, "Packed gravity state requires four blocks.");
|
||||
if (gravityState.Size() != offsets.Last()) {
|
||||
gravityState.SetSize(offsets.Last());
|
||||
}
|
||||
|
||||
assign_gravity_block(
|
||||
gravityState, offsets, 0, density, "The full density vector has the wrong gravity-state size."
|
||||
);
|
||||
assign_gravity_block(
|
||||
gravityState, offsets, 1, displacement, "The displacement vector has the wrong gravity-state size."
|
||||
);
|
||||
assign_gravity_block(
|
||||
gravityState, offsets, 2, gravityGradient, "The gravity-gradient vector has the wrong gravity-state size."
|
||||
);
|
||||
assign_gravity_block(
|
||||
gravityState, offsets, 3, gravityPotential, "The gravity-potential vector has the wrong gravity-state size."
|
||||
);
|
||||
}
|
||||
|
||||
void validate_finite_vector(
|
||||
const mfem::Vector &vector,
|
||||
const char *message
|
||||
) {
|
||||
for (int index = 0; index < vector.Size(); ++index) {
|
||||
MFEM_VERIFY(std::isfinite(vector(index)), message);
|
||||
}
|
||||
}
|
||||
|
||||
void validate_dependency_transition(
|
||||
const mean_field::operators::StellarEquilibriumDependencyStamp &prepared,
|
||||
const mean_field::operators::StellarEquilibriumDependencyStamp &requested,
|
||||
const char *message
|
||||
) {
|
||||
MFEM_VERIFY(prepared.identity != requested.identity || requested.revision >= prepared.revision, message);
|
||||
MFEM_VERIFY(
|
||||
prepared.identity == requested.identity || prepared.revision != requested.revision,
|
||||
"A new stellar-equilibrium dependency identity must also carry a visibly different revision."
|
||||
);
|
||||
}
|
||||
|
||||
[[nodiscard]] mean_field::operators::context::gravity_field::GravityFieldRevisions
|
||||
make_gravity_revisions(const mean_field::operators::StellarEquilibriumDependencies &dependencies) {
|
||||
return {
|
||||
.discretization = {.value = dependencies.discretization.revision},
|
||||
.displacement = {.value = dependencies.displacement.revision},
|
||||
.density = {.value = dependencies.density.revision},
|
||||
.gravity_gradient = {.value = dependencies.gravityGradient.revision},
|
||||
.gravity_potential = {.value = dependencies.gravityPotential.revision}
|
||||
};
|
||||
}
|
||||
|
||||
[[nodiscard]] mean_field::operators::context::barotropic::BarotropicClosureDependencies
|
||||
make_barotropic_closure_dependencies(const mean_field::operators::StellarEquilibriumDependencies &dependencies) {
|
||||
return {
|
||||
.discretization =
|
||||
{.identity = dependencies.discretization.identity, .revision = dependencies.discretization.revision},
|
||||
.density = {.identity = dependencies.density.identity, .revision = dependencies.density.revision},
|
||||
.enthalpy = {.identity = dependencies.enthalpy.identity, .revision = dependencies.enthalpy.revision},
|
||||
.displacement = {
|
||||
.identity = dependencies.displacement.identity, .revision = dependencies.displacement.revision
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
[[nodiscard]] mean_field::operators::DisplacementResidualDependencies
|
||||
make_displacement_dependencies(const mean_field::operators::StellarEquilibriumDependencies &dependencies) {
|
||||
return {
|
||||
.discretization =
|
||||
{.identity = dependencies.discretization.identity, .revision = dependencies.discretization.revision},
|
||||
.density = {.identity = dependencies.density.identity, .revision = dependencies.density.revision},
|
||||
.displacement =
|
||||
{.identity = dependencies.displacement.identity, .revision = dependencies.displacement.revision},
|
||||
.gravityGradient =
|
||||
{.identity = dependencies.gravityGradient.identity, .revision = dependencies.gravityGradient.revision},
|
||||
.enthalpy = {.identity = dependencies.enthalpy.identity, .revision = dependencies.enthalpy.revision},
|
||||
.rotation = {.identity = dependencies.rotation.identity, .revision = dependencies.rotation.revision}
|
||||
};
|
||||
}
|
||||
|
||||
[[nodiscard]] mean_field::operators::context::hydrostatic::HydrostaticEquilibriumDependencies
|
||||
make_hydrostatic_dependencies(const mean_field::operators::StellarEquilibriumDependencies &dependencies) {
|
||||
return {
|
||||
.discretization =
|
||||
{.identity = dependencies.discretization.identity, .revision = dependencies.discretization.revision},
|
||||
.enthalpy = {.identity = dependencies.enthalpy.identity, .revision = dependencies.enthalpy.revision},
|
||||
.gravityPotential =
|
||||
{.identity = dependencies.gravityPotential.identity,
|
||||
.revision = dependencies.gravityPotential.revision},
|
||||
.displacement =
|
||||
{.identity = dependencies.displacement.identity, .revision = dependencies.displacement.revision},
|
||||
.rotation = {.identity = dependencies.rotation.identity, .revision = dependencies.rotation.revision},
|
||||
.bernoulliConstant = {
|
||||
.identity = dependencies.bernoulliConstant.identity, .revision = dependencies.bernoulliConstant.revision
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
[[nodiscard]] mean_field::operators::MassNormalizationDependencies
|
||||
make_mass_dependencies(const mean_field::operators::StellarEquilibriumDependencies &dependencies) {
|
||||
return {
|
||||
.discretization =
|
||||
{.identity = dependencies.discretization.identity, .revision = dependencies.discretization.revision},
|
||||
.density = {.identity = dependencies.density.identity, .revision = dependencies.density.revision},
|
||||
.displacement =
|
||||
{.identity = dependencies.displacement.identity, .revision = dependencies.displacement.revision},
|
||||
.targetMass = {.identity = dependencies.targetMass.identity, .revision = dependencies.targetMass.revision}
|
||||
};
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace mean_field::operators {
|
||||
struct PreparedStellarEquilibriumOperator::ConstructionData {
|
||||
field::FieldDofMap densityMap;
|
||||
field::FieldDofMap displacementMap;
|
||||
field::FieldDofMap gravityFluxMap;
|
||||
field::FieldDofMap gravityPotentialMap;
|
||||
field::FieldDofMap enthalpyMap;
|
||||
|
||||
StellarEquilibriumLayout layout;
|
||||
mfem::Array<int> gravityStateOffsets;
|
||||
mfem::Array<int> gravityResidualOffsets;
|
||||
|
||||
explicit ConstructionData(fem::FEM &f)
|
||||
: densityMap(
|
||||
field::make_field_dof_map<
|
||||
field::Density,
|
||||
DomainSchema>(*f.densityFes)
|
||||
),
|
||||
displacementMap(
|
||||
field::make_field_dof_map<
|
||||
field::Displacement,
|
||||
DomainSchema>(*f.displacementFes)
|
||||
),
|
||||
gravityFluxMap(
|
||||
field::make_field_dof_map<
|
||||
field::Gravity,
|
||||
DomainSchema>(*f.gravityFluxFes)
|
||||
),
|
||||
gravityPotentialMap(
|
||||
field::make_field_dof_map<
|
||||
field::Gravity,
|
||||
DomainSchema>(*f.gravityPotentialFes)
|
||||
),
|
||||
enthalpyMap(
|
||||
field::make_field_dof_map<
|
||||
field::Enthalpy,
|
||||
DomainSchema>(*f.enthalpyFes)
|
||||
),
|
||||
layout(make_layout(
|
||||
densityMap,
|
||||
displacementMap,
|
||||
gravityFluxMap,
|
||||
gravityPotentialMap,
|
||||
enthalpyMap
|
||||
)),
|
||||
gravityStateOffsets(make_gravity_state_offsets(f)),
|
||||
gravityResidualOffsets(make_gravity_residual_offsets(f)) {
|
||||
}
|
||||
};
|
||||
|
||||
PreparedStellarEquilibriumOperator::ConstructionData
|
||||
PreparedStellarEquilibriumOperator::MakeConstructionData(fem::FEM &f) {
|
||||
ensure_gravity_static_operators(f);
|
||||
return ConstructionData(f);
|
||||
}
|
||||
|
||||
PreparedStellarEquilibriumOperator::PreparedStellarEquilibriumOperator(
|
||||
fem::FEM &f,
|
||||
const mapping::DomainMapperStateless &domainMapper,
|
||||
const eos::Polytrope &equationOfState,
|
||||
const models::StellarModel &stellarModel
|
||||
)
|
||||
: PreparedStellarEquilibriumOperator(
|
||||
f,
|
||||
domainMapper,
|
||||
equationOfState,
|
||||
stellarModel.targetMass()
|
||||
) {
|
||||
}
|
||||
|
||||
PreparedStellarEquilibriumOperator::PreparedStellarEquilibriumOperator(
|
||||
fem::FEM &f,
|
||||
const mapping::DomainMapperStateless &domainMapper,
|
||||
const eos::Polytrope &equationOfState,
|
||||
const double targetMass
|
||||
)
|
||||
: PreparedStellarEquilibriumOperator(
|
||||
f,
|
||||
domainMapper,
|
||||
equationOfState,
|
||||
targetMass,
|
||||
MakeConstructionData(f)
|
||||
) {
|
||||
}
|
||||
|
||||
PreparedStellarEquilibriumOperator::PreparedStellarEquilibriumOperator(
|
||||
fem::FEM &f,
|
||||
const mapping::DomainMapperStateless &domainMapper,
|
||||
const eos::Polytrope &equationOfState,
|
||||
const double targetMass,
|
||||
ConstructionData constructionData
|
||||
)
|
||||
: mfem::Operator(
|
||||
constructionData.layout.residual_offsets().Last(),
|
||||
constructionData.layout.value_offsets().Last()
|
||||
),
|
||||
m_layout(constructionData.layout),
|
||||
m_gravityStateOffsets(constructionData.gravityStateOffsets),
|
||||
m_gravityContext(
|
||||
f,
|
||||
domainMapper
|
||||
),
|
||||
m_gravityJacobianOperator(
|
||||
f,
|
||||
domainMapper,
|
||||
m_gravityContext,
|
||||
m_gravityStateOffsets,
|
||||
constructionData.gravityResidualOffsets
|
||||
),
|
||||
m_gravityOperator(
|
||||
f,
|
||||
domainMapper,
|
||||
m_gravityContext,
|
||||
m_gravityStateOffsets,
|
||||
m_gravityJacobianOperator
|
||||
),
|
||||
m_barotropicClosureOperator(
|
||||
f,
|
||||
domainMapper,
|
||||
equationOfState
|
||||
),
|
||||
m_hydrostaticOperator(
|
||||
f,
|
||||
domainMapper
|
||||
),
|
||||
m_displacementOperator(
|
||||
f,
|
||||
domainMapper,
|
||||
equationOfState,
|
||||
m_gravityContext
|
||||
),
|
||||
m_massNormalizationOperator(
|
||||
f,
|
||||
domainMapper,
|
||||
m_gravityContext
|
||||
),
|
||||
m_targetMass(targetMass),
|
||||
m_densityMap(std::move(constructionData.densityMap)),
|
||||
m_displacementMap(std::move(constructionData.displacementMap)),
|
||||
m_gravityFluxMap(std::move(constructionData.gravityFluxMap)),
|
||||
m_gravityPotentialMap(std::move(constructionData.gravityPotentialMap)),
|
||||
m_enthalpyMap(std::move(constructionData.enthalpyMap)) {
|
||||
MFEM_VERIFY(
|
||||
std::isfinite(m_targetMass) && m_targetMass > 0.0,
|
||||
"PreparedStellarEquilibriumOperator requires a finite, positive target mass."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
Width() == m_layout.value_offsets().Last() && Height() == m_layout.residual_offsets().Last(),
|
||||
"PreparedStellarEquilibriumOperator has inconsistent block dimensions."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
m_displacementMap.is_identity(), "PreparedStellarEquilibriumOperator currently requires Displacement "
|
||||
"support to span the full MFEM true-DOF space."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
m_gravityFluxMap.is_identity(), "PreparedStellarEquilibriumOperator currently requires gravity-flux "
|
||||
"support to span the full MFEM true-DOF space."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
m_gravityPotentialMap.is_identity(), "PreparedStellarEquilibriumOperator currently requires "
|
||||
"gravity-potential support to span the full MFEM true-DOF space."
|
||||
);
|
||||
|
||||
m_fullDensity.SetSize(m_densityMap.full_size());
|
||||
m_fullEnthalpy.SetSize(m_enthalpyMap.full_size());
|
||||
m_fullGravityState.SetSize(m_gravityStateOffsets.Last());
|
||||
|
||||
m_fullDensityVariation.SetSize(m_densityMap.full_size());
|
||||
m_fullEnthalpyVariation.SetSize(m_enthalpyMap.full_size());
|
||||
m_fullGravityDirection.SetSize(m_gravityStateOffsets.Last());
|
||||
m_fullEnthalpyAction.SetSize(m_enthalpyMap.full_size());
|
||||
|
||||
m_fullDensity = 0.0;
|
||||
m_fullEnthalpy = 0.0;
|
||||
m_fullGravityState = 0.0;
|
||||
m_fullDensityVariation = 0.0;
|
||||
m_fullEnthalpyVariation = 0.0;
|
||||
m_fullGravityDirection = 0.0;
|
||||
m_fullEnthalpyAction = 0.0;
|
||||
}
|
||||
|
||||
PreparedStellarEquilibriumReport PreparedStellarEquilibriumOperator::Prepare(
|
||||
const mfem::Vector &state,
|
||||
const StellarEquilibriumDependencies &dependencies,
|
||||
const physics::RigidRotation &rotation
|
||||
) {
|
||||
MFEM_VERIFY(
|
||||
state.Size() == Width(), "PreparedStellarEquilibriumOperator received a state with the wrong size."
|
||||
);
|
||||
validate_finite_vector(state, "PreparedStellarEquilibriumOperator received a non-finite state.");
|
||||
|
||||
const bool wasPrepared = m_isPrepared;
|
||||
if (wasPrepared) {
|
||||
validate_dependency_transition(
|
||||
m_preparedDependencies.discretization, dependencies.discretization,
|
||||
"The discretization revision cannot move backwards."
|
||||
);
|
||||
validate_dependency_transition(
|
||||
m_preparedDependencies.density, dependencies.density, "The density revision cannot move backwards."
|
||||
);
|
||||
validate_dependency_transition(
|
||||
m_preparedDependencies.displacement, dependencies.displacement,
|
||||
"The displacement revision cannot move backwards."
|
||||
);
|
||||
validate_dependency_transition(
|
||||
m_preparedDependencies.gravityGradient, dependencies.gravityGradient,
|
||||
"The gravity-gradient revision cannot move backwards."
|
||||
);
|
||||
validate_dependency_transition(
|
||||
m_preparedDependencies.gravityPotential, dependencies.gravityPotential,
|
||||
"The gravity-potential revision cannot move backwards."
|
||||
);
|
||||
validate_dependency_transition(
|
||||
m_preparedDependencies.enthalpy, dependencies.enthalpy, "The enthalpy revision cannot move backwards."
|
||||
);
|
||||
validate_dependency_transition(
|
||||
m_preparedDependencies.bernoulliConstant, dependencies.bernoulliConstant,
|
||||
"The Bernoulli-constant revision cannot move backwards."
|
||||
);
|
||||
validate_dependency_transition(
|
||||
m_preparedDependencies.rotation, dependencies.rotation, "The rotation revision cannot move backwards."
|
||||
);
|
||||
validate_dependency_transition(
|
||||
m_preparedDependencies.targetMass, dependencies.targetMass,
|
||||
"The target-mass revision cannot move backwards."
|
||||
);
|
||||
}
|
||||
|
||||
m_isPrepared = false;
|
||||
|
||||
using Form = utils::blocks::barotropic_equilibrium_form;
|
||||
constexpr auto densityValue = utils::blocks::get_value_block<Form>(utils::blocks::density_field.mass_term);
|
||||
constexpr auto displacementValue =
|
||||
utils::blocks::get_value_block<Form>(utils::blocks::displacement_field.geometry_term);
|
||||
constexpr auto gravityGradientValue =
|
||||
utils::blocks::get_value_block<Form>(utils::blocks::gravity_field.gradient_term);
|
||||
constexpr auto gravityPotentialValue =
|
||||
utils::blocks::get_value_block<Form>(utils::blocks::gravity_field.poisson_term);
|
||||
constexpr auto enthalpyValue =
|
||||
utils::blocks::get_value_block<Form>(utils::blocks::enthalpy_field.specific_term);
|
||||
constexpr auto bernoulliValue =
|
||||
utils::blocks::get_value_block<Form>(utils::blocks::barotropic_constant_field.mass_normalization_term);
|
||||
|
||||
const mfem::Vector reducedDensity = make_value_view(state, m_layout, densityValue);
|
||||
const mfem::Vector displacement = make_value_view(state, m_layout, displacementValue);
|
||||
const mfem::Vector gravityGradient = make_value_view(state, m_layout, gravityGradientValue);
|
||||
const mfem::Vector gravityPotential = make_value_view(state, m_layout, gravityPotentialValue);
|
||||
const mfem::Vector reducedEnthalpy = make_value_view(state, m_layout, enthalpyValue);
|
||||
const mfem::Vector bernoulli = make_value_view(state, m_layout, bernoulliValue);
|
||||
|
||||
m_densityMap.scatter(reducedDensity, m_fullDensity);
|
||||
m_enthalpyMap.scatter(reducedEnthalpy, m_fullEnthalpy);
|
||||
|
||||
pack_gravity_vector(
|
||||
m_fullGravityState, m_gravityStateOffsets, m_fullDensity, displacement, gravityGradient, gravityPotential
|
||||
);
|
||||
|
||||
PreparedStellarEquilibriumReport report;
|
||||
|
||||
report.gravity = m_gravityOperator.Prepare(m_fullGravityState, make_gravity_revisions(dependencies));
|
||||
|
||||
report.barotropicClosure = m_barotropicClosureOperator.Prepare(
|
||||
{.density = reducedDensity, .enthalpy = reducedEnthalpy, .displacement = displacement},
|
||||
make_barotropic_closure_dependencies(dependencies)
|
||||
);
|
||||
|
||||
report.hydrostatic = m_hydrostaticOperator.Prepare(
|
||||
{.enthalpy = m_fullEnthalpy,
|
||||
.gravityPotential = gravityPotential,
|
||||
.displacement = displacement,
|
||||
.bernoulliConstant = bernoulli(0)},
|
||||
make_hydrostatic_dependencies(dependencies), rotation
|
||||
);
|
||||
|
||||
report.displacement = m_displacementOperator.Prepare(
|
||||
{.enthalpy = reducedEnthalpy}, make_displacement_dependencies(dependencies), rotation
|
||||
);
|
||||
|
||||
report.massNormalization =
|
||||
m_massNormalizationOperator.Prepare({.targetMass = m_targetMass}, make_mass_dependencies(dependencies));
|
||||
|
||||
const bool dependenciesChanged = !wasPrepared || dependencies != m_preparedDependencies;
|
||||
if (dependenciesChanged || report.DidAnyChildWork()) {
|
||||
AssembleResidual();
|
||||
report.assembledResidual = true;
|
||||
}
|
||||
|
||||
m_preparedDependencies = dependencies;
|
||||
m_isPrepared = true;
|
||||
return report;
|
||||
}
|
||||
|
||||
void PreparedStellarEquilibriumOperator::AssembleResidual() {
|
||||
using Form = utils::blocks::barotropic_equilibrium_form;
|
||||
|
||||
constexpr auto gravityGradientResidual =
|
||||
utils::blocks::get_residual_block<Form>(utils::blocks::gravity_field.gradient_term);
|
||||
constexpr auto gravityPotentialResidual =
|
||||
utils::blocks::get_residual_block<Form>(utils::blocks::gravity_field.poisson_term);
|
||||
constexpr auto densityResidual =
|
||||
utils::blocks::get_residual_block<Form>(utils::blocks::density_field.mass_term);
|
||||
constexpr auto displacementResidual =
|
||||
utils::blocks::get_residual_block<Form>(utils::blocks::displacement_field.geometry_term);
|
||||
constexpr auto enthalpyResidual =
|
||||
utils::blocks::get_residual_block<Form>(utils::blocks::enthalpy_field.specific_term);
|
||||
constexpr auto massResidual =
|
||||
utils::blocks::get_residual_block<Form>(utils::blocks::barotropic_constant_field.mass_normalization_term);
|
||||
|
||||
mfem::Vector gravity;
|
||||
mfem::Vector closure;
|
||||
mfem::Vector displacement;
|
||||
mfem::Vector mass;
|
||||
|
||||
m_gravityOperator.Mult(m_fullGravityState, gravity);
|
||||
m_barotropicClosureOperator.BuildResidual(closure);
|
||||
m_displacementOperator.BuildResidual(displacement);
|
||||
m_hydrostaticOperator.BuildResidual(m_fullEnthalpyAction);
|
||||
m_massNormalizationOperator.BuildResidual(mass);
|
||||
|
||||
m_cachedResidual.SetSize(Height());
|
||||
m_cachedResidual = 0.0;
|
||||
|
||||
MFEM_VERIFY(
|
||||
gravity.Size() == m_layout.size(gravityGradientResidual) + m_layout.size(gravityPotentialResidual),
|
||||
"The gravity residual has the wrong size."
|
||||
);
|
||||
|
||||
mfem::Vector gravityGradient(gravity.GetData(), m_layout.size(gravityGradientResidual));
|
||||
mfem::Vector gravityPotential(
|
||||
gravity.GetData() + m_layout.size(gravityGradientResidual), m_layout.size(gravityPotentialResidual)
|
||||
);
|
||||
|
||||
assign_residual_block(
|
||||
m_cachedResidual, m_layout, gravityGradientResidual, gravityGradient,
|
||||
"The gravity-gradient residual has the wrong size."
|
||||
);
|
||||
assign_residual_block(
|
||||
m_cachedResidual, m_layout, gravityPotentialResidual, gravityPotential,
|
||||
"The gravity-potential residual has the wrong size."
|
||||
);
|
||||
assign_residual_block(
|
||||
m_cachedResidual, m_layout, densityResidual, closure, "The closure residual has the wrong size."
|
||||
);
|
||||
assign_residual_block(
|
||||
m_cachedResidual, m_layout, displacementResidual, displacement,
|
||||
"The displacement residual has the wrong size."
|
||||
);
|
||||
|
||||
{
|
||||
mfem::Vector reducedEnthalpyResidual = make_residual_view(m_cachedResidual, m_layout, enthalpyResidual);
|
||||
m_enthalpyMap.gather(m_fullEnthalpyAction, reducedEnthalpyResidual);
|
||||
}
|
||||
|
||||
assign_residual_block(
|
||||
m_cachedResidual, m_layout, massResidual, mass, "The mass-normalization residual has the wrong size."
|
||||
);
|
||||
|
||||
++m_statistics.residualAssemblies;
|
||||
}
|
||||
|
||||
void PreparedStellarEquilibriumOperator::BuildResidual(mfem::Vector &residual) const {
|
||||
VerifyPrepared();
|
||||
residual = m_cachedResidual;
|
||||
++m_statistics.residualApplications;
|
||||
}
|
||||
|
||||
void PreparedStellarEquilibriumOperator::Mult(
|
||||
const mfem::Vector &direction,
|
||||
mfem::Vector &action
|
||||
) const {
|
||||
VerifyPrepared();
|
||||
MFEM_VERIFY(
|
||||
direction.Size() == Width(),
|
||||
"PreparedStellarEquilibriumOperator received a Jacobian direction with the wrong size."
|
||||
);
|
||||
validate_finite_vector(
|
||||
direction, "PreparedStellarEquilibriumOperator received a non-finite Jacobian direction."
|
||||
);
|
||||
|
||||
using Form = utils::blocks::barotropic_equilibrium_form;
|
||||
|
||||
constexpr auto densityValue = utils::blocks::get_value_block<Form>(utils::blocks::density_field.mass_term);
|
||||
constexpr auto displacementValue =
|
||||
utils::blocks::get_value_block<Form>(utils::blocks::displacement_field.geometry_term);
|
||||
constexpr auto gravityGradientValue =
|
||||
utils::blocks::get_value_block<Form>(utils::blocks::gravity_field.gradient_term);
|
||||
constexpr auto gravityPotentialValue =
|
||||
utils::blocks::get_value_block<Form>(utils::blocks::gravity_field.poisson_term);
|
||||
constexpr auto enthalpyValue =
|
||||
utils::blocks::get_value_block<Form>(utils::blocks::enthalpy_field.specific_term);
|
||||
constexpr auto bernoulliValue =
|
||||
utils::blocks::get_value_block<Form>(utils::blocks::barotropic_constant_field.mass_normalization_term);
|
||||
|
||||
constexpr auto gravityGradientResidual =
|
||||
utils::blocks::get_residual_block<Form>(utils::blocks::gravity_field.gradient_term);
|
||||
constexpr auto gravityPotentialResidual =
|
||||
utils::blocks::get_residual_block<Form>(utils::blocks::gravity_field.poisson_term);
|
||||
constexpr auto densityResidual =
|
||||
utils::blocks::get_residual_block<Form>(utils::blocks::density_field.mass_term);
|
||||
constexpr auto displacementResidual =
|
||||
utils::blocks::get_residual_block<Form>(utils::blocks::displacement_field.geometry_term);
|
||||
constexpr auto enthalpyResidual =
|
||||
utils::blocks::get_residual_block<Form>(utils::blocks::enthalpy_field.specific_term);
|
||||
constexpr auto massResidual =
|
||||
utils::blocks::get_residual_block<Form>(utils::blocks::barotropic_constant_field.mass_normalization_term);
|
||||
|
||||
const mfem::Vector reducedDensityDirection = make_value_view(direction, m_layout, densityValue);
|
||||
const mfem::Vector displacementDirection = make_value_view(direction, m_layout, displacementValue);
|
||||
const mfem::Vector gravityGradientDirection = make_value_view(direction, m_layout, gravityGradientValue);
|
||||
const mfem::Vector gravityPotentialDirection = make_value_view(direction, m_layout, gravityPotentialValue);
|
||||
const mfem::Vector reducedEnthalpyDirection = make_value_view(direction, m_layout, enthalpyValue);
|
||||
const mfem::Vector bernoulliDirection = make_value_view(direction, m_layout, bernoulliValue);
|
||||
|
||||
m_densityMap.scatter(reducedDensityDirection, m_fullDensityVariation);
|
||||
m_enthalpyMap.scatter(reducedEnthalpyDirection, m_fullEnthalpyVariation);
|
||||
|
||||
pack_gravity_vector(
|
||||
m_fullGravityDirection, m_gravityStateOffsets, m_fullDensityVariation, displacementDirection,
|
||||
gravityGradientDirection, gravityPotentialDirection
|
||||
);
|
||||
|
||||
mfem::Vector gravityAction;
|
||||
mfem::Vector closureAction;
|
||||
mfem::Vector displacementAction;
|
||||
mfem::Vector massAction;
|
||||
|
||||
m_gravityJacobianOperator.Mult(m_fullGravityDirection, gravityAction);
|
||||
|
||||
m_barotropicClosureOperator.Mult(
|
||||
reducedDensityDirection, reducedEnthalpyDirection, displacementDirection, closureAction
|
||||
);
|
||||
|
||||
m_displacementOperator.ApplyCompleteJacobianAction(
|
||||
m_fullDensityVariation, displacementDirection, gravityGradientDirection, reducedEnthalpyDirection,
|
||||
displacementAction
|
||||
);
|
||||
|
||||
m_hydrostaticOperator.ApplyCompleteJacobianAction(
|
||||
m_fullEnthalpyVariation, gravityPotentialDirection, bernoulliDirection(0), displacementDirection,
|
||||
m_fullEnthalpyAction
|
||||
);
|
||||
|
||||
m_massNormalizationOperator.ApplyCompleteJacobianAction(
|
||||
m_fullDensityVariation, displacementDirection, massAction
|
||||
);
|
||||
|
||||
action.SetSize(Height());
|
||||
action = 0.0;
|
||||
|
||||
MFEM_VERIFY(
|
||||
gravityAction.Size() == m_layout.size(gravityGradientResidual) + m_layout.size(gravityPotentialResidual),
|
||||
"The gravity Jacobian action has the wrong size."
|
||||
);
|
||||
|
||||
mfem::Vector gravityGradientAction(gravityAction.GetData(), m_layout.size(gravityGradientResidual));
|
||||
mfem::Vector gravityPotentialAction(
|
||||
gravityAction.GetData() + m_layout.size(gravityGradientResidual), m_layout.size(gravityPotentialResidual)
|
||||
);
|
||||
|
||||
assign_residual_block(
|
||||
action, m_layout, gravityGradientResidual, gravityGradientAction,
|
||||
"The gravity-gradient Jacobian action has the wrong size."
|
||||
);
|
||||
assign_residual_block(
|
||||
action, m_layout, gravityPotentialResidual, gravityPotentialAction,
|
||||
"The gravity-potential Jacobian action has the wrong size."
|
||||
);
|
||||
assign_residual_block(
|
||||
action, m_layout, densityResidual, closureAction, "The closure Jacobian action has the wrong size."
|
||||
);
|
||||
assign_residual_block(
|
||||
action, m_layout, displacementResidual, displacementAction,
|
||||
"The displacement Jacobian action has the wrong size."
|
||||
);
|
||||
|
||||
{
|
||||
mfem::Vector reducedEnthalpyAction = make_residual_view(action, m_layout, enthalpyResidual);
|
||||
m_enthalpyMap.gather(m_fullEnthalpyAction, reducedEnthalpyAction);
|
||||
}
|
||||
|
||||
assign_residual_block(
|
||||
action, m_layout, massResidual, massAction, "The mass-normalization Jacobian action has the wrong size."
|
||||
);
|
||||
|
||||
++m_statistics.jacobianApplications;
|
||||
}
|
||||
|
||||
bool PreparedStellarEquilibriumOperator::IsPrepared() const noexcept {
|
||||
return m_isPrepared && m_gravityContext.IsPrepared() && m_barotropicClosureOperator.IsPrepared() &&
|
||||
m_hydrostaticOperator.IsPrepared() && m_displacementOperator.IsPrepared() &&
|
||||
m_massNormalizationOperator.IsPrepared();
|
||||
}
|
||||
|
||||
double PreparedStellarEquilibriumOperator::GetTargetMass() const noexcept {
|
||||
return m_targetMass;
|
||||
}
|
||||
|
||||
const StellarEquilibriumLayout &PreparedStellarEquilibriumOperator::GetLayout() const noexcept {
|
||||
return m_layout;
|
||||
}
|
||||
|
||||
const StellarEquilibriumDependencies &PreparedStellarEquilibriumOperator::GetDependencies() const {
|
||||
VerifyPrepared();
|
||||
return m_preparedDependencies;
|
||||
}
|
||||
|
||||
const PreparedStellarEquilibriumStatistics &PreparedStellarEquilibriumOperator::GetStatistics() const noexcept {
|
||||
return m_statistics;
|
||||
}
|
||||
|
||||
const context::gravity_field::GravityFieldLinearizationContext &
|
||||
PreparedStellarEquilibriumOperator::GetGravityContext() const noexcept {
|
||||
return m_gravityContext;
|
||||
}
|
||||
|
||||
const GravityFieldOperator &PreparedStellarEquilibriumOperator::GetGravityOperator() const noexcept {
|
||||
return m_gravityOperator;
|
||||
}
|
||||
|
||||
const GravityFieldJacobianOperator &
|
||||
PreparedStellarEquilibriumOperator::GetGravityJacobianOperator() const noexcept {
|
||||
return m_gravityJacobianOperator;
|
||||
}
|
||||
|
||||
const PreparedBarotropicClosureOperator &
|
||||
PreparedStellarEquilibriumOperator::GetBarotropicClosureOperator() const noexcept {
|
||||
return m_barotropicClosureOperator;
|
||||
}
|
||||
|
||||
const context::barotropic::BarotropicClosureLinearizationContext &
|
||||
PreparedStellarEquilibriumOperator::GetBarotropicClosureContext() const noexcept {
|
||||
return m_barotropicClosureOperator.GetContext();
|
||||
}
|
||||
|
||||
const PreparedHydrostaticEquilibriumOperator &
|
||||
PreparedStellarEquilibriumOperator::GetHydrostaticOperator() const noexcept {
|
||||
return m_hydrostaticOperator;
|
||||
}
|
||||
|
||||
const PreparedDisplacementResidualOperator &
|
||||
PreparedStellarEquilibriumOperator::GetDisplacementOperator() const noexcept {
|
||||
return m_displacementOperator;
|
||||
}
|
||||
|
||||
const PreparedMassNormalizationOperator &
|
||||
PreparedStellarEquilibriumOperator::GetMassNormalizationOperator() const noexcept {
|
||||
return m_massNormalizationOperator;
|
||||
}
|
||||
|
||||
void PreparedStellarEquilibriumOperator::VerifyPrepared() const {
|
||||
MFEM_VERIFY(
|
||||
IsPrepared(), "PreparedStellarEquilibriumOperator must be prepared before residual or Jacobian application."
|
||||
);
|
||||
}
|
||||
} // namespace mean_field::operators
|
||||
@@ -33,17 +33,15 @@ namespace {
|
||||
|
||||
true_dofs.SetSize(finite_element_space.GetTrueVSize());
|
||||
|
||||
const mfem::Operator *restriction =
|
||||
finite_element_space.GetRestrictionMatrix();
|
||||
const mfem::Operator *restriction = finite_element_space.GetRestrictionMatrix();
|
||||
|
||||
if (restriction != nullptr) {
|
||||
restriction->Mult(grid_function, true_dofs);
|
||||
} else {
|
||||
MFEM_VERIFY(
|
||||
grid_function.Size() == true_dofs.Size(),
|
||||
"A finite-element space without a restriction operator must "
|
||||
"have "
|
||||
"matching local and true sizes."
|
||||
grid_function.Size() == true_dofs.Size(), "A finite-element space without a restriction operator must "
|
||||
"have "
|
||||
"matching local and true sizes."
|
||||
);
|
||||
|
||||
true_dofs = grid_function;
|
||||
@@ -62,10 +60,7 @@ namespace mean_field::physics {
|
||||
f.densityFes != nullptr && rho.FESpace() == f.densityFes.get(),
|
||||
"Gravity solve requires rho to use the registered density space."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.gravityPotentialFes != nullptr,
|
||||
"Gravity solve requires the registered gravity-potential space."
|
||||
);
|
||||
MFEM_VERIFY(f.gravityPotentialFes != nullptr, "Gravity solve requires the registered gravity-potential space.");
|
||||
|
||||
mfem::Array<int> outer_bdr_marker(f.mesh->bdr_attributes.Max());
|
||||
outer_bdr_marker = 0;
|
||||
@@ -86,23 +81,16 @@ namespace mean_field::physics {
|
||||
return l2_multipole_potential(f, utils::MASS, x_physical);
|
||||
};
|
||||
|
||||
boundary_potential_coeff =
|
||||
std::make_unique<mfem::FunctionCoefficient>(boundary_potential);
|
||||
boundary_potential_coeff = std::make_unique<mfem::FunctionCoefficient>(boundary_potential);
|
||||
auto boundary_integrator =
|
||||
std::make_unique<mfem::VectorFEBoundaryFluxLFIntegrator>(
|
||||
*boundary_potential_coeff
|
||||
);
|
||||
const mfem::FiniteElement &boundary_element =
|
||||
*f.gravityFluxFes->GetTypicalTraceElement();
|
||||
std::make_unique<mfem::VectorFEBoundaryFluxLFIntegrator>(*boundary_potential_coeff);
|
||||
const mfem::FiniteElement &boundary_element = *f.gravityFluxFes->GetTypicalTraceElement();
|
||||
|
||||
f.quadratureFactory->configure_gravity_boundary(
|
||||
*boundary_integrator,
|
||||
quadrature::QuadratureRole::discretization, boundary_element,
|
||||
*boundary_integrator, quadrature::QuadratureRole::discretization, boundary_element,
|
||||
utils::DOMAINS::VACUUM, quadrature::MappingKind::none
|
||||
);
|
||||
g_rhs.AddBoundaryIntegrator(
|
||||
boundary_integrator.release(), outer_bdr_marker
|
||||
);
|
||||
g_rhs.AddBoundaryIntegrator(boundary_integrator.release(), outer_bdr_marker);
|
||||
}
|
||||
|
||||
g_rhs.Assemble();
|
||||
@@ -112,36 +100,25 @@ namespace mean_field::physics {
|
||||
mfem::ParLinearForm f_rhs(f.gravityPotentialFes.get());
|
||||
|
||||
std::unique_ptr<mfem::Coefficient> mapped_source_coeff;
|
||||
mfem::Coefficient *active_source_coeff = &source_coeff;
|
||||
quadrature::MappingKind source_mapping_kind =
|
||||
quadrature::MappingKind::none;
|
||||
mfem::Coefficient *active_source_coeff = &source_coeff;
|
||||
quadrature::MappingKind source_mapping_kind = quadrature::MappingKind::none;
|
||||
|
||||
if (f.has_mapping()) {
|
||||
mapped_source_coeff =
|
||||
std::make_unique<mapping::MappedScalarCoefficient>(
|
||||
*f.mapping, source_coeff
|
||||
);
|
||||
mapped_source_coeff = std::make_unique<mapping::MappedScalarCoefficient>(*f.mapping, source_coeff);
|
||||
active_source_coeff = mapped_source_coeff.get();
|
||||
source_mapping_kind = quadrature::MappingKind::general;
|
||||
}
|
||||
|
||||
auto source_integrator =
|
||||
std::make_unique<mfem::DomainLFIntegrator>(*active_source_coeff);
|
||||
const mfem::FiniteElement &source_test_element =
|
||||
*f.gravityPotentialFes->GetTypicalFE();
|
||||
const mfem::ElementTransformation &source_transformation =
|
||||
*f.mesh->GetElementTransformation(0);
|
||||
const int source_coefficient_order = f.densityFes->GetMaxElementOrder();
|
||||
auto source_integrator = std::make_unique<mfem::DomainLFIntegrator>(*active_source_coeff);
|
||||
const mfem::FiniteElement &source_test_element = *f.gravityPotentialFes->GetTypicalFE();
|
||||
const mfem::ElementTransformation &source_transformation = *f.mesh->GetElementTransformation(0);
|
||||
const int source_coefficient_order = f.densityFes->GetMaxElementOrder();
|
||||
|
||||
f.quadratureFactory->configure_gravity_source(
|
||||
*source_integrator, quadrature::QuadratureRole::discretization,
|
||||
source_test_element, source_transformation,
|
||||
source_coefficient_order, utils::DOMAINS::STELLAR,
|
||||
source_mapping_kind
|
||||
);
|
||||
f_rhs.AddDomainIntegrator(
|
||||
source_integrator.release(), f.gravityContext.stellar_mask
|
||||
*source_integrator, quadrature::QuadratureRole::discretization, source_test_element, source_transformation,
|
||||
source_coefficient_order, utils::DOMAINS::STELLAR, source_mapping_kind
|
||||
);
|
||||
f_rhs.AddDomainIntegrator(source_integrator.release(), f.gravityContext.stellar_mask);
|
||||
f_rhs.Assemble();
|
||||
|
||||
mfem::BlockVector RHS(f.gravityBlockTrueOffsets);
|
||||
@@ -177,13 +154,9 @@ namespace mean_field::physics {
|
||||
|
||||
std::unique_ptr<mfem::Coefficient> centrifugal_coeff;
|
||||
if (fem.has_mapping()) {
|
||||
centrifugal_coeff = std::make_unique<
|
||||
mapping::PhysicalPositionFunctionCoefficient>(
|
||||
*fem.mapping, rot
|
||||
);
|
||||
centrifugal_coeff = std::make_unique<mapping::PhysicalPositionFunctionCoefficient>(*fem.mapping, rot);
|
||||
} else {
|
||||
centrifugal_coeff =
|
||||
std::make_unique<mfem::FunctionCoefficient>(rot);
|
||||
centrifugal_coeff = std::make_unique<mfem::FunctionCoefficient>(rot);
|
||||
}
|
||||
|
||||
mfem::GridFunction centrifugal_gf(fem.gravityPotentialFes.get());
|
||||
@@ -207,19 +180,14 @@ namespace mean_field::physics {
|
||||
if (fem.mesh->GetAttribute(i) == 3)
|
||||
continue;
|
||||
|
||||
mfem::ElementTransformation *trans =
|
||||
fem.mesh->GetElementTransformation(i);
|
||||
using DensityField = field::Field<field::Density>;
|
||||
const quadrature::Query query =
|
||||
DensityField::make_query<field::Density::Form::Quadrupole>(
|
||||
quadrature::QuadratureRole::diagnostic, trans->OrderW(),
|
||||
std::array<int, 1>{2}, utils::DOMAINS::STELLAR,
|
||||
fem.has_mapping() ? quadrature::MappingKind::general
|
||||
: quadrature::MappingKind::none
|
||||
);
|
||||
mfem::ElementTransformation *trans = fem.mesh->GetElementTransformation(i);
|
||||
using DensityField = field::Field<field::Density>;
|
||||
const quadrature::Query query = DensityField::make_query<field::Density::Form::Quadrupole>(
|
||||
quadrature::QuadratureRole::diagnostic, trans->OrderW(), std::array<int, 1>{2}, utils::DOMAINS::STELLAR,
|
||||
fem.has_mapping() ? quadrature::MappingKind::general : quadrature::MappingKind::none
|
||||
);
|
||||
const mfem::IntegrationRule &ir =
|
||||
*fem.quadratureFactory->get(query, trans->GetGeometryType())
|
||||
.integration_rule;
|
||||
*fem.quadratureFactory->get(query, trans->GetGeometryType()).integration_rule;
|
||||
|
||||
for (int j = 0; j < ir.GetNPoints(); ++j) {
|
||||
const mfem::IntegrationPoint &ip = ir.IntPoint(j);
|
||||
@@ -250,9 +218,8 @@ namespace mean_field::physics {
|
||||
|
||||
for (int m = 0; m < dim; ++m) {
|
||||
for (int n = 0; n < dim; ++n) {
|
||||
const double delta = (m == n) ? 1.0 : 0.0;
|
||||
const double contrib =
|
||||
3.0 * x_prime(m) * x_prime(n) - delta * r_sq;
|
||||
const double delta = (m == n) ? 1.0 : 0.0;
|
||||
const double contrib = 3.0 * x_prime(m) * x_prime(n) - delta * r_sq;
|
||||
local_Q(m, n) += rho_val * contrib * weight;
|
||||
}
|
||||
}
|
||||
@@ -260,10 +227,7 @@ namespace mean_field::physics {
|
||||
}
|
||||
|
||||
mfem::DenseMatrix global_Q(dim, dim);
|
||||
MPI_Allreduce(
|
||||
local_Q.GetData(), global_Q.GetData(), dim * dim, MPI_DOUBLE,
|
||||
MPI_SUM, fem.mesh->GetComm()
|
||||
);
|
||||
MPI_Allreduce(local_Q.GetData(), global_Q.GetData(), dim * dim, MPI_DOUBLE, MPI_SUM, fem.mesh->GetComm());
|
||||
|
||||
return global_Q;
|
||||
}
|
||||
@@ -289,8 +253,7 @@ namespace mean_field::physics {
|
||||
}
|
||||
}
|
||||
|
||||
const double l2_contrib =
|
||||
-(utils::G / (2.0 * std::pow(r, 3))) * l2_mult_factor;
|
||||
const double l2_contrib = -(utils::G / (2.0 * std::pow(r, 3))) * l2_mult_factor;
|
||||
|
||||
const double l0_contrib = -utils::G * total_mass / r;
|
||||
|
||||
@@ -304,92 +267,69 @@ namespace mean_field::physics {
|
||||
// ==========================================
|
||||
// 1. Partially Assemble the High-Order Mass Block
|
||||
// ==========================================
|
||||
f.gravityContext.m_form =
|
||||
std::make_unique<mfem::ParBilinearForm>(f.gravityFluxFes.get());
|
||||
f.gravityContext.m_form = std::make_unique<mfem::ParBilinearForm>(f.gravityFluxFes.get());
|
||||
f.gravityContext.m_form->SetAssemblyLevel(mfem::AssemblyLevel::PARTIAL);
|
||||
|
||||
std::unique_ptr<mfem::VectorFEMassIntegrator> hdiv_mass_integrator;
|
||||
|
||||
if (f.has_mapping()) {
|
||||
f.gravityContext.mapped_hdiv_mass_coeff =
|
||||
std::make_unique<mapping::MappedHDivMassCoefficient>(
|
||||
*f.mapping, f.mesh->Dimension()
|
||||
);
|
||||
std::make_unique<mapping::MappedHDivMassCoefficient>(*f.mapping, f.mesh->Dimension());
|
||||
hdiv_mass_integrator =
|
||||
std::make_unique<mfem::VectorFEMassIntegrator>(
|
||||
*f.gravityContext.mapped_hdiv_mass_coeff
|
||||
);
|
||||
std::make_unique<mfem::VectorFEMassIntegrator>(*f.gravityContext.mapped_hdiv_mass_coeff);
|
||||
} else {
|
||||
f.gravityContext.mapped_hdiv_mass_coeff.reset();
|
||||
hdiv_mass_integrator =
|
||||
std::make_unique<mfem::VectorFEMassIntegrator>();
|
||||
hdiv_mass_integrator = std::make_unique<mfem::VectorFEMassIntegrator>();
|
||||
}
|
||||
|
||||
const mfem::FiniteElement &hdiv_element =
|
||||
*f.gravityFluxFes->GetTypicalFE();
|
||||
const mfem::ElementTransformation &hdiv_transformation =
|
||||
*f.mesh->GetElementTransformation(0);
|
||||
const mfem::FiniteElement &hdiv_element = *f.gravityFluxFes->GetTypicalFE();
|
||||
const mfem::ElementTransformation &hdiv_transformation = *f.mesh->GetElementTransformation(0);
|
||||
const quadrature::MappingKind mapping_kind =
|
||||
f.has_mapping() ? quadrature::MappingKind::general
|
||||
: quadrature::MappingKind::none;
|
||||
f.has_mapping() ? quadrature::MappingKind::general : quadrature::MappingKind::none;
|
||||
|
||||
f.quadratureFactory->configure_gravity_hdiv_mass(
|
||||
*hdiv_mass_integrator, quadrature::QuadratureRole::discretization,
|
||||
hdiv_element, hdiv_transformation, utils::DOMAINS::ALL, mapping_kind
|
||||
);
|
||||
f.gravityContext.m_form->AddDomainIntegrator(
|
||||
hdiv_mass_integrator.release()
|
||||
*hdiv_mass_integrator, quadrature::QuadratureRole::discretization, hdiv_element, hdiv_transformation,
|
||||
utils::DOMAINS::ALL, mapping_kind
|
||||
);
|
||||
f.gravityContext.m_form->AddDomainIntegrator(hdiv_mass_integrator.release());
|
||||
|
||||
f.gravityContext.m_form->Assemble();
|
||||
|
||||
// ==========================================
|
||||
// 2. Partially Assemble the High-Order Divergence Block
|
||||
// ==========================================
|
||||
f.gravityContext.b_form = std::make_unique<mfem::ParMixedBilinearForm>(
|
||||
f.gravityFluxFes.get(), f.gravityPotentialFes.get()
|
||||
);
|
||||
f.gravityContext.b_form =
|
||||
std::make_unique<mfem::ParMixedBilinearForm>(f.gravityFluxFes.get(), f.gravityPotentialFes.get());
|
||||
f.gravityContext.b_form->SetAssemblyLevel(mfem::AssemblyLevel::PARTIAL);
|
||||
|
||||
auto divergence_discretization_integrator =
|
||||
std::make_unique<mfem::VectorFEDivergenceIntegrator>();
|
||||
const mfem::FiniteElement &divergence_discretization_test_element =
|
||||
*f.gravityPotentialFes->GetTypicalFE();
|
||||
auto divergence_discretization_integrator = std::make_unique<mfem::VectorFEDivergenceIntegrator>();
|
||||
const mfem::FiniteElement &divergence_discretization_test_element = *f.gravityPotentialFes->GetTypicalFE();
|
||||
|
||||
f.quadratureFactory->configure_gravity_divergence(
|
||||
*divergence_discretization_integrator,
|
||||
quadrature::QuadratureRole::discretization, hdiv_element,
|
||||
divergence_discretization_test_element, hdiv_transformation,
|
||||
utils::DOMAINS::ALL, quadrature::MappingKind::none
|
||||
);
|
||||
f.gravityContext.b_form->AddDomainIntegrator(
|
||||
divergence_discretization_integrator.release()
|
||||
*divergence_discretization_integrator, quadrature::QuadratureRole::discretization, hdiv_element,
|
||||
divergence_discretization_test_element, hdiv_transformation, utils::DOMAINS::ALL,
|
||||
quadrature::MappingKind::none
|
||||
);
|
||||
f.gravityContext.b_form->AddDomainIntegrator(divergence_discretization_integrator.release());
|
||||
|
||||
f.gravityContext.b_form->Assemble();
|
||||
|
||||
MFEM_VERIFY(
|
||||
f.domainMapperStateless != nullptr,
|
||||
"Gravity source partial assembly requires the stateless domain "
|
||||
"mapper."
|
||||
f.domainMapperStateless != nullptr, "Gravity source partial assembly requires the stateless domain "
|
||||
"mapper."
|
||||
);
|
||||
|
||||
mfem::Vector displacement_true(f.displacementFes->GetTrueVSize());
|
||||
displacement_true = 0.0;
|
||||
displacement_true = 0.0;
|
||||
|
||||
const mfem::GridFunction *active_displacement =
|
||||
f.mapping->GetDisplacement();
|
||||
const mfem::GridFunction *active_displacement = f.mapping->GetDisplacement();
|
||||
|
||||
if (active_displacement != nullptr) {
|
||||
grid_function_to_true_dofs(
|
||||
*f.displacementFes, *active_displacement, displacement_true
|
||||
);
|
||||
grid_function_to_true_dofs(*f.displacementFes, *active_displacement, displacement_true);
|
||||
}
|
||||
|
||||
auto source_form =
|
||||
std::make_unique<operators::PreparedMappedGravitySourceOperator>(
|
||||
f, *f.domainMapperStateless
|
||||
);
|
||||
std::make_unique<operators::PreparedMappedGravitySourceOperator>(f, *f.domainMapperStateless);
|
||||
|
||||
source_form->Prepare(displacement_true);
|
||||
|
||||
@@ -397,12 +337,9 @@ namespace mean_field::physics {
|
||||
// ==========================================
|
||||
// 3. Assemble Global Block Operator
|
||||
// ==========================================
|
||||
f.gravityContext.BT = std::make_unique<mfem::TransposeOperator>(
|
||||
f.gravityContext.b_form.get()
|
||||
);
|
||||
f.gravityContext.BT = std::make_unique<mfem::TransposeOperator>(f.gravityContext.b_form.get());
|
||||
|
||||
f.gravityContext.block_A =
|
||||
std::make_unique<mfem::BlockOperator>(f.gravityBlockTrueOffsets);
|
||||
f.gravityContext.block_A = std::make_unique<mfem::BlockOperator>(f.gravityBlockTrueOffsets);
|
||||
f.gravityContext.block_A->SetBlock(0, 0, f.gravityContext.m_form.get());
|
||||
f.gravityContext.block_A->SetBlock(0, 1, f.gravityContext.BT.get());
|
||||
f.gravityContext.block_A->SetBlock(1, 0, f.gravityContext.b_form.get());
|
||||
@@ -417,8 +354,7 @@ namespace mean_field::physics {
|
||||
|
||||
for (int i = 0; i < inverse_mass_diagonal.Size(); ++i) {
|
||||
MFEM_VERIFY(
|
||||
std::isfinite(inverse_mass_diagonal(i)) &&
|
||||
inverse_mass_diagonal(i) > 0.0,
|
||||
std::isfinite(inverse_mass_diagonal(i)) && inverse_mass_diagonal(i) > 0.0,
|
||||
"Mapped RT mass matrix has a non-positive or non-finite "
|
||||
"diagonal "
|
||||
"entry."
|
||||
@@ -426,58 +362,34 @@ namespace mean_field::physics {
|
||||
inverse_mass_diagonal(i) = 1.0 / inverse_mass_diagonal(i);
|
||||
}
|
||||
|
||||
mfem::ParMixedBilinearForm b_preconditioner(
|
||||
f.gravityFluxFes.get(), f.gravityPotentialFes.get()
|
||||
);
|
||||
auto divergence_preconditioner_integrator =
|
||||
std::make_unique<mfem::VectorFEDivergenceIntegrator>();
|
||||
mfem::ParMixedBilinearForm b_preconditioner(f.gravityFluxFes.get(), f.gravityPotentialFes.get());
|
||||
auto divergence_preconditioner_integrator = std::make_unique<mfem::VectorFEDivergenceIntegrator>();
|
||||
|
||||
const mfem::FiniteElement &divergence_trial_element =
|
||||
*f.gravityFluxFes->GetTypicalFE();
|
||||
const mfem::FiniteElement &divergence_test_element =
|
||||
*f.gravityPotentialFes->GetTypicalFE();
|
||||
const mfem::ElementTransformation &divergence_transformation =
|
||||
*f.mesh->GetElementTransformation(0);
|
||||
const mfem::FiniteElement &divergence_trial_element = *f.gravityFluxFes->GetTypicalFE();
|
||||
const mfem::FiniteElement &divergence_test_element = *f.gravityPotentialFes->GetTypicalFE();
|
||||
const mfem::ElementTransformation &divergence_transformation = *f.mesh->GetElementTransformation(0);
|
||||
|
||||
f.quadratureFactory->configure_gravity_divergence(
|
||||
*divergence_preconditioner_integrator,
|
||||
quadrature::QuadratureRole::preconditioner,
|
||||
divergence_trial_element, divergence_test_element,
|
||||
divergence_transformation, utils::DOMAINS::ALL,
|
||||
quadrature::MappingKind::none
|
||||
);
|
||||
b_preconditioner.AddDomainIntegrator(
|
||||
divergence_preconditioner_integrator.release()
|
||||
*divergence_preconditioner_integrator, quadrature::QuadratureRole::preconditioner, divergence_trial_element,
|
||||
divergence_test_element, divergence_transformation, utils::DOMAINS::ALL, quadrature::MappingKind::none
|
||||
);
|
||||
b_preconditioner.AddDomainIntegrator(divergence_preconditioner_integrator.release());
|
||||
b_preconditioner.Assemble();
|
||||
b_preconditioner.Finalize();
|
||||
std::unique_ptr<mfem::HypreParMatrix> b_matrix(
|
||||
b_preconditioner.ParallelAssemble()
|
||||
);
|
||||
std::unique_ptr<mfem::HypreParMatrix> inverse_mass_b_transpose(
|
||||
b_matrix->Transpose()
|
||||
);
|
||||
std::unique_ptr<mfem::HypreParMatrix> b_matrix(b_preconditioner.ParallelAssemble());
|
||||
std::unique_ptr<mfem::HypreParMatrix> inverse_mass_b_transpose(b_matrix->Transpose());
|
||||
|
||||
inverse_mass_b_transpose->ScaleRows(inverse_mass_diagonal);
|
||||
|
||||
f.gravityContext.Schur.reset(
|
||||
mfem::ParMult(b_matrix.get(), inverse_mass_b_transpose.get())
|
||||
);
|
||||
f.gravityContext.Schur.reset(mfem::ParMult(b_matrix.get(), inverse_mass_b_transpose.get()));
|
||||
|
||||
// ==========================================
|
||||
// 5. Wire Up the preconditioners
|
||||
// ==========================================
|
||||
f.gravityContext.prec_M =
|
||||
std::make_unique<mfem::OperatorJacobiSmoother>(
|
||||
mass_diagonal, empty_tdofs
|
||||
);
|
||||
f.gravityContext.prec_M = std::make_unique<mfem::OperatorJacobiSmoother>(mass_diagonal, empty_tdofs);
|
||||
f.gravityContext.prec_Phi->SetOperator(*f.gravityContext.Schur);
|
||||
f.gravityContext.block_prec->SetDiagonalBlock(
|
||||
0, f.gravityContext.prec_M.get()
|
||||
);
|
||||
f.gravityContext.block_prec->SetDiagonalBlock(
|
||||
1, f.gravityContext.prec_Phi.get()
|
||||
);
|
||||
f.gravityContext.block_prec->SetDiagonalBlock(0, f.gravityContext.prec_M.get());
|
||||
f.gravityContext.block_prec->SetDiagonalBlock(1, f.gravityContext.prec_Phi.get());
|
||||
}
|
||||
|
||||
GravitySolution grav_potential_new(
|
||||
@@ -486,49 +398,32 @@ namespace mean_field::physics {
|
||||
const mfem::GridFunction &rho,
|
||||
const mfem::GridFunction &displacement
|
||||
) {
|
||||
MFEM_VERIFY(f.mesh != nullptr, "Gravity initialization requires a parallel mesh.");
|
||||
MFEM_VERIFY(f.densityFes != nullptr, "Gravity initialization requires the density finite-element space.");
|
||||
MFEM_VERIFY(
|
||||
f.mesh != nullptr,
|
||||
"Gravity initialization requires a parallel mesh."
|
||||
f.gravityPotentialFes != nullptr, "Gravity initialization requires the gravity-potential "
|
||||
"finite-element "
|
||||
"space."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.densityFes != nullptr,
|
||||
"Gravity initialization requires the density finite-element space."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.gravityPotentialFes != nullptr,
|
||||
"Gravity initialization requires the gravity-potential "
|
||||
"finite-element "
|
||||
"space."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.gravityFluxFes != nullptr,
|
||||
"Gravity initialization requires the "
|
||||
"gravity-gradient finite-element space."
|
||||
f.gravityFluxFes != nullptr, "Gravity initialization requires the "
|
||||
"gravity-gradient finite-element space."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.displacementFes != nullptr, "Gravity initialization requires the "
|
||||
"displacement finite-element space."
|
||||
);
|
||||
MFEM_VERIFY(f.domainMapperStateless != nullptr, "Gravity initialization requires the stateless domain mapper.");
|
||||
MFEM_VERIFY(f.gravityContext.b_form != nullptr, "Gravity initialization requires the divergence operator.");
|
||||
MFEM_VERIFY(
|
||||
f.domainMapperStateless != nullptr,
|
||||
"Gravity initialization requires the stateless domain mapper."
|
||||
f.gravityContext.BT != nullptr, "Gravity initialization requires the transpose divergence operator."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.gravityContext.b_form != nullptr,
|
||||
"Gravity initialization requires the divergence operator."
|
||||
f.gravityContext.block_prec != nullptr, "Gravity initialization requires the gravity block preconditioner."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.gravityContext.BT != nullptr,
|
||||
"Gravity initialization requires the transpose divergence operator."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.gravityContext.block_prec != nullptr,
|
||||
"Gravity initialization requires the gravity block preconditioner."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
rho.FESpace() == f.densityFes.get(),
|
||||
"Gravity initialization requires density to use the FEM density "
|
||||
"space."
|
||||
rho.FESpace() == f.densityFes.get(), "Gravity initialization requires density to use the FEM density "
|
||||
"space."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
displacement.FESpace() == f.displacementFes.get(),
|
||||
@@ -540,53 +435,43 @@ namespace mean_field::physics {
|
||||
using form = utils::blocks::gravity_field_form;
|
||||
|
||||
constexpr auto gravity_gradient_residual_block =
|
||||
utils::blocks::get_residual_block<form>(
|
||||
utils::blocks::gravity_field.gradient_term
|
||||
);
|
||||
utils::blocks::get_residual_block<form>(utils::blocks::gravity_field.gradient_term);
|
||||
|
||||
constexpr auto gravity_poisson_residual_block =
|
||||
utils::blocks::get_residual_block<form>(
|
||||
utils::blocks::gravity_field.poisson_term
|
||||
);
|
||||
utils::blocks::get_residual_block<form>(utils::blocks::gravity_field.poisson_term);
|
||||
|
||||
const std::array<int, form::value_block_count> value_sizes{
|
||||
f.densityFes->GetTrueVSize(), f.displacementFes->GetTrueVSize(),
|
||||
f.gravityFluxFes->GetTrueVSize(),
|
||||
f.densityFes->GetTrueVSize(), f.displacementFes->GetTrueVSize(), f.gravityFluxFes->GetTrueVSize(),
|
||||
f.gravityPotentialFes->GetTrueVSize()
|
||||
};
|
||||
|
||||
const std::array<int, form::residual_block_count> residual_sizes{
|
||||
f.gravityFluxFes->GetTrueVSize(),
|
||||
f.gravityPotentialFes->GetTrueVSize()
|
||||
f.gravityFluxFes->GetTrueVSize(), f.gravityPotentialFes->GetTrueVSize()
|
||||
};
|
||||
|
||||
const utils::blocks::form_layout<form> layout(
|
||||
value_sizes, residual_sizes
|
||||
);
|
||||
const utils::blocks::form_layout<form> layout(value_sizes, residual_sizes);
|
||||
|
||||
mfem::Vector density_true;
|
||||
mfem::Vector displacement_true;
|
||||
|
||||
grid_function_to_true_dofs(*f.densityFes, rho, density_true);
|
||||
grid_function_to_true_dofs(
|
||||
*f.displacementFes, displacement, displacement_true
|
||||
grid_function_to_true_dofs(*f.displacementFes, displacement, displacement_true);
|
||||
|
||||
operators::context::gravity_field::GravityFieldLinearizationContext linearization_context(
|
||||
f, *f.domainMapperStateless
|
||||
);
|
||||
|
||||
operators::context::gravity_field::GravityFieldLinearizationContext
|
||||
linearization_context(f, *f.domainMapperStateless);
|
||||
|
||||
operators::GravityFieldJacobianOperator gravity_jacobian(
|
||||
f, *f.domainMapperStateless, linearization_context,
|
||||
layout.value_offsets(), layout.residual_offsets()
|
||||
f, *f.domainMapperStateless, linearization_context, layout.value_offsets(), layout.residual_offsets()
|
||||
);
|
||||
|
||||
operators::GravityFieldOperator gravity_operator(
|
||||
f, *f.domainMapperStateless, linearization_context,
|
||||
layout.value_offsets(), gravity_jacobian
|
||||
f, *f.domainMapperStateless, linearization_context, layout.value_offsets(), gravity_jacobian
|
||||
);
|
||||
|
||||
operators::context::gravity_field::GravityFieldGeometryContext
|
||||
reduced_geometry_context(f, *f.domainMapperStateless);
|
||||
operators::context::gravity_field::GravityFieldGeometryContext reduced_geometry_context(
|
||||
f, *f.domainMapperStateless
|
||||
);
|
||||
|
||||
operators::ReducedGravityFieldOperator reduced_operator(
|
||||
gravity_operator, reduced_geometry_context, displacement_true
|
||||
@@ -600,9 +485,7 @@ namespace mean_field::physics {
|
||||
"The reduced gravity right-hand side has the wrong size."
|
||||
);
|
||||
|
||||
mfem::BlockVector gravity_state(
|
||||
reduced_operator.GetGravityTrueOffsets()
|
||||
);
|
||||
mfem::BlockVector gravity_state(reduced_operator.GetGravityTrueOffsets());
|
||||
gravity_state = 0.0;
|
||||
|
||||
mfem::MINRESSolver minres(f.mesh->GetComm());
|
||||
@@ -614,20 +497,13 @@ namespace mean_field::physics {
|
||||
minres.SetPrintLevel(1);
|
||||
minres.Mult(right_hand_side, gravity_state);
|
||||
|
||||
MFEM_VERIFY(
|
||||
minres.GetConverged(),
|
||||
"The reduced gravity solve failed to converge."
|
||||
);
|
||||
MFEM_VERIFY(minres.GetConverged(), "The reduced gravity solve failed to converge.");
|
||||
|
||||
GravitySolution solution(f);
|
||||
|
||||
solution.gradPhi.SetFromTrueDofs(
|
||||
gravity_state.GetBlock(gravity_gradient_residual_block)
|
||||
);
|
||||
solution.gradPhi.SetFromTrueDofs(gravity_state.GetBlock(gravity_gradient_residual_block));
|
||||
|
||||
solution.phi.SetFromTrueDofs(
|
||||
gravity_state.GetBlock(gravity_poisson_residual_block)
|
||||
);
|
||||
solution.phi.SetFromTrueDofs(gravity_state.GetBlock(gravity_poisson_residual_block));
|
||||
|
||||
return solution;
|
||||
}
|
||||
|
||||
@@ -15,18 +15,13 @@ namespace mean_field::physics {
|
||||
if (fem.mesh->GetAttribute(i) == 3)
|
||||
continue;
|
||||
|
||||
mfem::ElementTransformation *T =
|
||||
fem.mesh->GetElementTransformation(i);
|
||||
using DensityField = field::Field<field::Density>;
|
||||
const quadrature::Query query =
|
||||
DensityField::make_query<field::Density::Form::Quadrupole>(
|
||||
quadrature::QuadratureRole::diagnostic, T->OrderW(),
|
||||
std::array<int, 1>{2}, utils::DOMAINS::STELLAR,
|
||||
quadrature::MappingKind::general
|
||||
);
|
||||
const mfem::IntegrationRule &ir =
|
||||
*fem.quadratureFactory->get(query, T->GetGeometryType())
|
||||
.integration_rule;
|
||||
mfem::ElementTransformation *T = fem.mesh->GetElementTransformation(i);
|
||||
using DensityField = field::Field<field::Density>;
|
||||
const quadrature::Query query = DensityField::make_query<field::Density::Form::Quadrupole>(
|
||||
quadrature::QuadratureRole::diagnostic, T->OrderW(), std::array<int, 1>{2}, utils::DOMAINS::STELLAR,
|
||||
quadrature::MappingKind::general
|
||||
);
|
||||
const mfem::IntegrationRule &ir = *fem.quadratureFactory->get(query, T->GetGeometryType()).integration_rule;
|
||||
|
||||
for (int j = 0; j < ir.GetNPoints(); j++) {
|
||||
const mfem::IntegrationPoint &ip = ir.IntPoint(j);
|
||||
@@ -37,19 +32,16 @@ namespace mean_field::physics {
|
||||
mfem::Vector x_phys;
|
||||
fem.mapping->GetPhysicalPoint(*T, ip, x_phys);
|
||||
|
||||
const double r_cyl_sq =
|
||||
x_phys(0) * x_phys(0) + x_phys(1) * x_phys(1);
|
||||
const double detJ = std::fabs(fem.mapping->ComputeDetJ(*T, ip));
|
||||
const double weight = T->Weight() * ip.weight * detJ;
|
||||
const double r_cyl_sq = x_phys(0) * x_phys(0) + x_phys(1) * x_phys(1);
|
||||
const double detJ = std::fabs(fem.mapping->ComputeDetJ(*T, ip));
|
||||
const double weight = T->Weight() * ip.weight * detJ;
|
||||
|
||||
local_I += rho_hat * r_cyl_sq * weight;
|
||||
}
|
||||
}
|
||||
|
||||
double global_I = 0.0;
|
||||
MPI_Allreduce(
|
||||
&local_I, &global_I, 1, MPI_DOUBLE, MPI_SUM, fem.mesh->GetComm()
|
||||
);
|
||||
MPI_Allreduce(&local_I, &global_I, 1, MPI_DOUBLE, MPI_SUM, fem.mesh->GetComm());
|
||||
|
||||
return global_I;
|
||||
}
|
||||
|
||||
@@ -29,10 +29,8 @@ namespace mean_field::utils {
|
||||
mfem::Array<mfem::IntegrationPoint> origin_ip;
|
||||
fem.mesh->FindPoints(P_origin, origin_elem, origin_ip, false);
|
||||
|
||||
if (origin_elem.Size() > 0 && origin_elem[0] >= 0 &&
|
||||
fem.mapping->HasDisplacementField()) {
|
||||
mfem::ElementTransformation *T0 =
|
||||
fem.mesh->GetElementTransformation(origin_elem[0]);
|
||||
if (origin_elem.Size() > 0 && origin_elem[0] >= 0 && fem.mapping->HasDisplacementField()) {
|
||||
mfem::ElementTransformation *T0 = fem.mesh->GetElementTransformation(origin_elem[0]);
|
||||
T0->SetIntPoint(&origin_ip[0]);
|
||||
|
||||
mfem::DenseMatrix J0(dim, dim), J0_inv(dim, dim);
|
||||
@@ -98,8 +96,7 @@ namespace mean_field::utils {
|
||||
int elemID = elem_ids[0];
|
||||
const mfem::IntegrationPoint &ip = ips[0];
|
||||
|
||||
mfem::ElementTransformation *T =
|
||||
fem.mesh->GetElementTransformation(elemID);
|
||||
mfem::ElementTransformation *T = fem.mesh->GetElementTransformation(elemID);
|
||||
T->SetIntPoint(&ip);
|
||||
|
||||
mfem::Vector current_x_phys(dim);
|
||||
@@ -160,8 +157,7 @@ namespace mean_field::utils {
|
||||
const mapping::COORDINATE_SPACE rspace
|
||||
) {
|
||||
mfem::Vector x_search;
|
||||
if (vspace == mapping::COORDINATE_SPACE::PHYSICAL &&
|
||||
fem.has_mapping()) {
|
||||
if (vspace == mapping::COORDINATE_SPACE::PHYSICAL && fem.has_mapping()) {
|
||||
GetReferencePoint(fem, x, x_search);
|
||||
} else {
|
||||
x_search = x;
|
||||
@@ -177,8 +173,7 @@ namespace mean_field::utils {
|
||||
double local_val = 0.0;
|
||||
if (elem_ids.Size() > 0 && elem_ids[0] >= 0) {
|
||||
const double val = u.GetValue(elem_ids[0], ips[0]);
|
||||
if (rspace == mapping::COORDINATE_SPACE::PHYSICAL &&
|
||||
!fem.has_mapping()) {
|
||||
if (rspace == mapping::COORDINATE_SPACE::PHYSICAL && !fem.has_mapping()) {
|
||||
MFEM_ABORT(
|
||||
"Physical evaluation mode requested but no mapping "
|
||||
"provided. Check "
|
||||
@@ -189,9 +184,7 @@ namespace mean_field::utils {
|
||||
}
|
||||
|
||||
double global_val = 0.0;
|
||||
MPI_Allreduce(
|
||||
&local_val, &global_val, 1, MPI_DOUBLE, MPI_MAX, fem.mesh->GetComm()
|
||||
);
|
||||
MPI_Allreduce(&local_val, &global_val, 1, MPI_DOUBLE, MPI_MAX, fem.mesh->GetComm());
|
||||
return global_val;
|
||||
}
|
||||
|
||||
|
||||
@@ -10,18 +10,14 @@ namespace mean_field::utils {
|
||||
DOMAINS lhs,
|
||||
DOMAINS rhs
|
||||
) {
|
||||
return static_cast<DOMAINS>(
|
||||
static_cast<uint8_t>(lhs) | static_cast<uint8_t>(rhs)
|
||||
);
|
||||
return static_cast<DOMAINS>(static_cast<uint8_t>(lhs) | static_cast<uint8_t>(rhs));
|
||||
}
|
||||
|
||||
DOMAINS operator&(
|
||||
DOMAINS lhs,
|
||||
DOMAINS rhs
|
||||
) {
|
||||
return static_cast<DOMAINS>(
|
||||
static_cast<uint8_t>(lhs) & static_cast<uint8_t>(rhs)
|
||||
);
|
||||
return static_cast<DOMAINS>(static_cast<uint8_t>(lhs) & static_cast<uint8_t>(rhs));
|
||||
}
|
||||
|
||||
void populate_element_mask(
|
||||
@@ -37,8 +33,7 @@ namespace mean_field::utils {
|
||||
mask[0] = 1;
|
||||
}
|
||||
|
||||
if ((domain & DOMAINS::ENVELOPE) == DOMAINS::ENVELOPE &&
|
||||
max_attr >= 2) {
|
||||
if ((domain & DOMAINS::ENVELOPE) == DOMAINS::ENVELOPE && max_attr >= 2) {
|
||||
mask[1] = 1;
|
||||
}
|
||||
|
||||
@@ -92,10 +87,7 @@ namespace mean_field::utils {
|
||||
mesh->GetElementVertices(i, vertices);
|
||||
for (const int v : vertices) {
|
||||
const double *coords = mesh->GetVertex(v);
|
||||
double r = std::sqrt(
|
||||
coords[0] * coords[0] + coords[1] * coords[1] +
|
||||
coords[2] * coords[2]
|
||||
);
|
||||
double r = std::sqrt(coords[0] * coords[0] + coords[1] * coords[1] + coords[2] * coords[2]);
|
||||
local_min_r = std::min(local_min_r, r);
|
||||
local_max_r = std::max(local_max_r, r);
|
||||
}
|
||||
@@ -111,15 +103,9 @@ namespace mean_field::utils {
|
||||
comm = pmesh->GetComm();
|
||||
}
|
||||
|
||||
MPI_Allreduce(
|
||||
&local_min_r, &global_min_r, 1, MPI_DOUBLE, MPI_MIN, comm
|
||||
);
|
||||
MPI_Allreduce(
|
||||
&local_max_r, &global_max_r, 1, MPI_DOUBLE, MPI_MAX, comm
|
||||
);
|
||||
MPI_Allreduce(
|
||||
&l_found, &global_found_vacuum, 1, MPI_INT, MPI_MAX, comm
|
||||
);
|
||||
MPI_Allreduce(&local_min_r, &global_min_r, 1, MPI_DOUBLE, MPI_MIN, comm);
|
||||
MPI_Allreduce(&local_max_r, &global_max_r, 1, MPI_DOUBLE, MPI_MAX, comm);
|
||||
MPI_Allreduce(&l_found, &global_found_vacuum, 1, MPI_INT, MPI_MAX, comm);
|
||||
|
||||
if (global_found_vacuum) {
|
||||
return boundary::Bounds(global_min_r, global_max_r);
|
||||
|
||||
@@ -11,9 +11,8 @@ export namespace mean_field::analysis {
|
||||
double domain_integrate_grid_function(
|
||||
const fem::FEM &fem,
|
||||
const mfem::GridFunction &gf,
|
||||
utils::DOMAINS domain = utils::DOMAINS::ALL,
|
||||
mapping::COORDINATE_SPACE coord_space =
|
||||
mapping::COORDINATE_SPACE::PHYSICAL
|
||||
utils::DOMAINS domain = utils::DOMAINS::ALL,
|
||||
mapping::COORDINATE_SPACE coord_space = mapping::COORDINATE_SPACE::PHYSICAL
|
||||
);
|
||||
|
||||
mfem::Vector get_com(
|
||||
@@ -34,8 +33,7 @@ export namespace mean_field::analysis {
|
||||
|
||||
double get_mesh_volume(
|
||||
const fem::FEM &fem,
|
||||
mapping::COORDINATE_SPACE coordinate_space =
|
||||
mapping::COORDINATE_SPACE::PHYSICAL,
|
||||
utils::DOMAINS domain = utils::DOMAINS::STELLAR
|
||||
mapping::COORDINATE_SPACE coordinate_space = mapping::COORDINATE_SPACE::PHYSICAL,
|
||||
utils::DOMAINS domain = utils::DOMAINS::STELLAR
|
||||
);
|
||||
} // namespace mean_field::analysis
|
||||
|
||||
@@ -15,9 +15,7 @@ export namespace mean_field::boundary {
|
||||
Boundaries b,
|
||||
const int a
|
||||
) {
|
||||
return static_cast<int>(
|
||||
static_cast<uint8_t>(b) - static_cast<uint8_t>(a)
|
||||
);
|
||||
return static_cast<int>(static_cast<uint8_t>(b) - static_cast<uint8_t>(a));
|
||||
}
|
||||
|
||||
struct Bounds {
|
||||
|
||||
16
libmeanfield/interface/eos/eos_base.cppm
Normal file
16
libmeanfield/interface/eos/eos_base.cppm
Normal file
@@ -0,0 +1,16 @@
|
||||
export module mean_field:eos.base;
|
||||
|
||||
export namespace mean_field::eos {
|
||||
class EquationOfState {
|
||||
public:
|
||||
virtual ~EquationOfState() = default;
|
||||
[[nodiscard]] virtual double pressure_from_density(double density) const = 0;
|
||||
[[nodiscard]] virtual double pressure_from_enthalpy(double enthalpy) const = 0;
|
||||
[[nodiscard]] virtual double enthalpy_from_density(double density) const = 0;
|
||||
[[nodiscard]] virtual double enthalpy_from_pressure(double pressure) const = 0;
|
||||
[[nodiscard]] virtual double density_from_enthalpy(double enthalpy) const = 0;
|
||||
[[nodiscard]] virtual double density_derivative_from_enthalpy(double enthalpy) const = 0;
|
||||
[[nodiscard]] virtual double pressure_derivative_from_enthalpy(double enthalpy) const = 0;
|
||||
[[nodiscard]] virtual double pressure_derivative_from_density(double density) const = 0;
|
||||
};
|
||||
} // namespace mean_field::eos
|
||||
170
libmeanfield/interface/eos/polytropic.cppm
Normal file
170
libmeanfield/interface/eos/polytropic.cppm
Normal file
@@ -0,0 +1,170 @@
|
||||
module;
|
||||
#include <cmath>
|
||||
#include <format>
|
||||
#include <stdexcept>
|
||||
export module mean_field:eos.polytrope;
|
||||
export import :eos.base;
|
||||
|
||||
export namespace mean_field::eos {
|
||||
class Polytrope final : public EquationOfState {
|
||||
public:
|
||||
Polytrope(
|
||||
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 override {
|
||||
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 override {
|
||||
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 override {
|
||||
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 override {
|
||||
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 override {
|
||||
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 override {
|
||||
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 override {
|
||||
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);
|
||||
}
|
||||
|
||||
[[nodiscard]] double enthalpy_from_pressure(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);
|
||||
}
|
||||
|
||||
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
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
private:
|
||||
double m_polytropic_index;
|
||||
double m_polytropic_constant;
|
||||
double m_enthalpy_scale;
|
||||
};
|
||||
} // namespace mean_field::eos
|
||||
@@ -148,26 +148,21 @@ export namespace mean_field::fem {
|
||||
[[nodiscard]] bool okay() const {
|
||||
return mesh != nullptr &&
|
||||
|
||||
gravityPotentialFec != nullptr &&
|
||||
gravityPotentialFes != nullptr &&
|
||||
gravityFluxFec != nullptr && gravityFluxFes != nullptr &&
|
||||
gravityPotentialFec != nullptr && gravityPotentialFes != nullptr && gravityFluxFec != nullptr &&
|
||||
gravityFluxFes != nullptr &&
|
||||
|
||||
displacementFec != nullptr && displacementFes != nullptr &&
|
||||
displacement != nullptr &&
|
||||
displacementFec != nullptr && displacementFes != nullptr && displacement != nullptr &&
|
||||
|
||||
densityFec != nullptr && densityFes != nullptr &&
|
||||
|
||||
enthalpyFec != nullptr && enthalpyFes != nullptr &&
|
||||
|
||||
compactificationFec != nullptr &&
|
||||
compactificationFes != nullptr &&
|
||||
compactificationFec != nullptr && compactificationFes != nullptr &&
|
||||
compactificationCoordinate != nullptr &&
|
||||
|
||||
mapping != nullptr && domainMapperStateless != nullptr &&
|
||||
quadratureFactory != nullptr &&
|
||||
mapping != nullptr && domainMapperStateless != nullptr && quadratureFactory != nullptr &&
|
||||
|
||||
blockTrueOffsets.Size() == 3 &&
|
||||
gravityBlockTrueOffsets.Size() == 3;
|
||||
blockTrueOffsets.Size() == 3 && gravityBlockTrueOffsets.Size() == 3;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool has_mapping() const {
|
||||
|
||||
@@ -6,6 +6,7 @@ module;
|
||||
#include <type_traits>
|
||||
|
||||
export module mean_field:field.base;
|
||||
export import :utils.domain;
|
||||
|
||||
export namespace mean_field::field {
|
||||
template <typename... Ts> struct TypeList { };
|
||||
@@ -13,11 +14,9 @@ export namespace mean_field::field {
|
||||
template <typename T, typename ListT> struct TypeListContains;
|
||||
|
||||
template <typename T, typename... Ts>
|
||||
struct TypeListContains<T, TypeList<Ts...>>
|
||||
: std::bool_constant<(std::same_as<T, Ts> || ...)> { };
|
||||
struct TypeListContains<T, TypeList<Ts...>> : std::bool_constant<(std::same_as<T, Ts> || ...)> { };
|
||||
|
||||
template <typename T, typename ListT>
|
||||
inline constexpr bool typeListContains = TypeListContains<T, ListT>::value;
|
||||
template <typename T, typename ListT> inline constexpr bool typeListContains = TypeListContains<T, ListT>::value;
|
||||
|
||||
enum class StorageKind { finite_element, global_scalar };
|
||||
|
||||
@@ -44,14 +43,13 @@ export namespace mean_field::field {
|
||||
};
|
||||
|
||||
template <typename SpaceT>
|
||||
concept SpaceTag = std::same_as<SpaceT, L2> || std::same_as<SpaceT, H1> ||
|
||||
std::same_as<SpaceT, RT> || std::same_as<SpaceT, ND>;
|
||||
concept SpaceTag =
|
||||
std::same_as<SpaceT, L2> || std::same_as<SpaceT, H1> || std::same_as<SpaceT, RT> || std::same_as<SpaceT, ND>;
|
||||
|
||||
template <SpaceTag SpaceT, int RankV>
|
||||
inline constexpr bool spaceSupportsRank =
|
||||
(std::same_as<SpaceT, H1> && (RankV == 0 || RankV == 1)) ||
|
||||
(std::same_as<SpaceT, L2> && (RankV == 0 || RankV == 1)) ||
|
||||
(std::same_as<SpaceT, RT> && RankV == 1) ||
|
||||
(std::same_as<SpaceT, L2> && (RankV == 0 || RankV == 1)) || (std::same_as<SpaceT, RT> && RankV == 1) ||
|
||||
(std::same_as<SpaceT, ND> && RankV == 1);
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
@@ -105,51 +103,39 @@ export namespace mean_field::field {
|
||||
|
||||
template <typename T> struct IsCurl : std::false_type { };
|
||||
|
||||
template <typename SourceT>
|
||||
struct IsGradient<FieldRelation::Gradient<SourceT>> : std::true_type { };
|
||||
template <typename SourceT> struct IsGradient<FieldRelation::Gradient<SourceT>> : std::true_type { };
|
||||
|
||||
template <typename SourceT>
|
||||
struct IsDivergence<FieldRelation::Divergence<SourceT>> : std::true_type {
|
||||
};
|
||||
template <typename SourceT> struct IsDivergence<FieldRelation::Divergence<SourceT>> : std::true_type { };
|
||||
|
||||
template <typename SourceT>
|
||||
struct IsCurl<FieldRelation::Curl<SourceT>> : std::true_type { };
|
||||
template <typename SourceT> struct IsCurl<FieldRelation::Curl<SourceT>> : std::true_type { };
|
||||
|
||||
template <typename RelationT>
|
||||
concept ValidRelation =
|
||||
std::same_as<RelationT, FieldRelation::Independent> ||
|
||||
IsGradient<RelationT>::value || IsDivergence<RelationT>::value ||
|
||||
IsCurl<RelationT>::value;
|
||||
concept ValidRelation = std::same_as<RelationT, FieldRelation::Independent> || IsGradient<RelationT>::value ||
|
||||
IsDivergence<RelationT>::value || IsCurl<RelationT>::value;
|
||||
|
||||
template <typename RelationT> struct RelationTarget {
|
||||
using Type = void;
|
||||
};
|
||||
|
||||
template <typename SourceT>
|
||||
struct RelationTarget<FieldRelation::Gradient<SourceT>> {
|
||||
template <typename SourceT> struct RelationTarget<FieldRelation::Gradient<SourceT>> {
|
||||
using Type = SourceT;
|
||||
};
|
||||
|
||||
template <typename SourceT>
|
||||
struct RelationTarget<FieldRelation::Divergence<SourceT>> {
|
||||
template <typename SourceT> struct RelationTarget<FieldRelation::Divergence<SourceT>> {
|
||||
using Type = SourceT;
|
||||
};
|
||||
|
||||
template <typename SourceT>
|
||||
struct RelationTarget<FieldRelation::Curl<SourceT>> {
|
||||
template <typename SourceT> struct RelationTarget<FieldRelation::Curl<SourceT>> {
|
||||
using Type = SourceT;
|
||||
};
|
||||
|
||||
template <typename QuantityT>
|
||||
using RelationTargetT =
|
||||
typename RelationTarget<typename QuantityT::Relation>::Type;
|
||||
template <typename QuantityT> using RelationTargetT = typename RelationTarget<typename QuantityT::Relation>::Type;
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Field quantities
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
template <int RankV, ValidRelation RelationT, DiscretizationTag DiscT>
|
||||
struct Quantity {
|
||||
template <int RankV, ValidRelation RelationT, DiscretizationTag DiscT> struct Quantity {
|
||||
using Relation = RelationT;
|
||||
using Discretization = DiscT;
|
||||
using Space = typename DiscT::Space;
|
||||
@@ -173,11 +159,9 @@ export namespace mean_field::field {
|
||||
);
|
||||
};
|
||||
|
||||
template <ValidRelation RelationT, DiscretizationTag DiscT>
|
||||
using ScalarQ = Quantity<0, RelationT, DiscT>;
|
||||
template <ValidRelation RelationT, DiscretizationTag DiscT> using ScalarQ = Quantity<0, RelationT, DiscT>;
|
||||
|
||||
template <ValidRelation RelationT, DiscretizationTag DiscT>
|
||||
using VectorQ = Quantity<1, RelationT, DiscT>;
|
||||
template <ValidRelation RelationT, DiscretizationTag DiscT> using VectorQ = Quantity<1, RelationT, DiscT>;
|
||||
|
||||
struct GlobalScalarQ {
|
||||
using Relation = FieldRelation::Independent;
|
||||
@@ -188,73 +172,57 @@ export namespace mean_field::field {
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
concept FieldQuantity =
|
||||
requires {
|
||||
typename T::Relation;
|
||||
typename T::Discretization;
|
||||
typename T::Space;
|
||||
concept FieldQuantity = requires {
|
||||
typename T::Relation;
|
||||
typename T::Discretization;
|
||||
typename T::Space;
|
||||
|
||||
{ T::rankValue } -> std::convertible_to<int>;
|
||||
{ T::familyOrder } -> std::convertible_to<int>;
|
||||
{ T::storageKind } -> std::convertible_to<StorageKind>;
|
||||
{ T::staticBlockSize } -> std::convertible_to<int>;
|
||||
} && SpaceTag<typename T::Space> &&
|
||||
T::storageKind == StorageKind::finite_element;
|
||||
{ T::rankValue } -> std::convertible_to<int>;
|
||||
{ T::familyOrder } -> std::convertible_to<int>;
|
||||
{ T::storageKind } -> std::convertible_to<StorageKind>;
|
||||
{ T::staticBlockSize } -> std::convertible_to<int>;
|
||||
} && SpaceTag<typename T::Space> && T::storageKind == StorageKind::finite_element;
|
||||
|
||||
template <typename T>
|
||||
concept GlobalScalarQuantity =
|
||||
requires {
|
||||
typename T::Relation;
|
||||
concept GlobalScalarQuantity = requires {
|
||||
typename T::Relation;
|
||||
|
||||
{ T::rankValue } -> std::convertible_to<int>;
|
||||
{ T::storageKind } -> std::convertible_to<StorageKind>;
|
||||
{ T::staticBlockSize } -> std::convertible_to<int>;
|
||||
} && T::rankValue == 0 &&
|
||||
T::storageKind == StorageKind::global_scalar && T::staticBlockSize == 1;
|
||||
{ T::rankValue } -> std::convertible_to<int>;
|
||||
{ T::storageKind } -> std::convertible_to<StorageKind>;
|
||||
{ T::staticBlockSize } -> std::convertible_to<int>;
|
||||
} && T::rankValue == 0 && T::storageKind == StorageKind::global_scalar && T::staticBlockSize == 1;
|
||||
|
||||
template <typename T>
|
||||
concept RegisteredQuantity = FieldQuantity<T> || GlobalScalarQuantity<T>;
|
||||
|
||||
template <typename QuantityT>
|
||||
concept DerivedQuantity = FieldQuantity<QuantityT> &&
|
||||
(!std::same_as<RelationTargetT<QuantityT>, void>);
|
||||
concept DerivedQuantity = FieldQuantity<QuantityT> && (!std::same_as<RelationTargetT<QuantityT>, void>);
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Compile-time discretization constraints
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
template <FieldQuantity FluxT, FieldQuantity PotentialT>
|
||||
struct RtL2StablePair {
|
||||
template <FieldQuantity FluxT, FieldQuantity PotentialT> struct RtL2StablePair {
|
||||
static consteval void validate() {
|
||||
static_assert(
|
||||
std::same_as<typename FluxT::Space, RT>,
|
||||
"The flux in an RT/L2 pair must use Raviart-Thomas elements."
|
||||
std::same_as<typename FluxT::Space, RT>, "The flux in an RT/L2 pair must use Raviart-Thomas elements."
|
||||
);
|
||||
|
||||
static_assert(
|
||||
std::same_as<typename PotentialT::Space, L2>,
|
||||
"The potential in an RT/L2 pair must use L2 elements."
|
||||
std::same_as<typename PotentialT::Space, L2>, "The potential in an RT/L2 pair must use L2 elements."
|
||||
);
|
||||
|
||||
static_assert(
|
||||
FluxT::rankValue == 1,
|
||||
"The flux in an RT/L2 pair must be vector-valued."
|
||||
);
|
||||
static_assert(FluxT::rankValue == 1, "The flux in an RT/L2 pair must be vector-valued.");
|
||||
|
||||
static_assert(PotentialT::rankValue == 0, "The potential in an RT/L2 pair must be scalar-valued.");
|
||||
|
||||
static_assert(
|
||||
PotentialT::rankValue == 0,
|
||||
"The potential in an RT/L2 pair must be scalar-valued."
|
||||
);
|
||||
|
||||
static_assert(
|
||||
FluxT::familyOrder == PotentialT::familyOrder,
|
||||
"The MFEM RT and L2 family orders must match."
|
||||
FluxT::familyOrder == PotentialT::familyOrder, "The MFEM RT and L2 family orders must match."
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename... ConstraintTs>
|
||||
consteval bool validate_constraints(TypeList<ConstraintTs...>) {
|
||||
template <typename... ConstraintTs> consteval bool validate_constraints(TypeList<ConstraintTs...>) {
|
||||
(ConstraintTs::validate(), ...);
|
||||
return true;
|
||||
}
|
||||
@@ -276,16 +244,11 @@ export namespace mean_field::field {
|
||||
|
||||
template <typename OperationT>
|
||||
concept FieldOperationTag =
|
||||
std::same_as<OperationT, FieldOperation::Value> ||
|
||||
std::same_as<OperationT, FieldOperation::Gradient> ||
|
||||
std::same_as<OperationT, FieldOperation::Divergence> ||
|
||||
std::same_as<OperationT, FieldOperation::Curl> ||
|
||||
std::same_as<OperationT, FieldOperation::Value> || std::same_as<OperationT, FieldOperation::Gradient> ||
|
||||
std::same_as<OperationT, FieldOperation::Divergence> || std::same_as<OperationT, FieldOperation::Curl> ||
|
||||
std::same_as<OperationT, FieldOperation::NormalTrace>;
|
||||
|
||||
template <
|
||||
RegisteredQuantity QuantityT,
|
||||
FieldOperationTag OperationT = FieldOperation::Value>
|
||||
struct Operand {
|
||||
template <RegisteredQuantity QuantityT, FieldOperationTag OperationT = FieldOperation::Value> struct Operand {
|
||||
using Quantity = QuantityT;
|
||||
using Operation = OperationT;
|
||||
|
||||
@@ -298,12 +261,10 @@ export namespace mean_field::field {
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
concept FieldOperand =
|
||||
requires {
|
||||
typename T::Quantity;
|
||||
typename T::Operation;
|
||||
} && RegisteredQuantity<typename T::Quantity> &&
|
||||
FieldOperationTag<typename T::Operation>;
|
||||
concept FieldOperand = requires {
|
||||
typename T::Quantity;
|
||||
typename T::Operation;
|
||||
} && RegisteredQuantity<typename T::Quantity> && FieldOperationTag<typename T::Operation>;
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Weak-form descriptions
|
||||
@@ -315,11 +276,7 @@ export namespace mean_field::field {
|
||||
// coefficient supplied at runtime contributes one dynamic order.
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
template <
|
||||
auto PolicyKeyV,
|
||||
std::size_t DynamicOrderCountV,
|
||||
FieldOperand... OperandTs>
|
||||
struct FormSpec {
|
||||
template <auto PolicyKeyV, std::size_t DynamicOrderCountV, FieldOperand... OperandTs> struct FormSpec {
|
||||
static constexpr auto policyKey = PolicyKeyV;
|
||||
static constexpr std::size_t dynamicOrderCount = DynamicOrderCountV;
|
||||
|
||||
@@ -335,22 +292,46 @@ export namespace mean_field::field {
|
||||
{ T::dynamicOrderCount } -> std::convertible_to<std::size_t>;
|
||||
};
|
||||
|
||||
template <typename ListT>
|
||||
struct IsRegisteredQuantityList : std::false_type { };
|
||||
template <typename ListT> struct IsRegisteredQuantityList : std::false_type { };
|
||||
|
||||
template <RegisteredQuantity... QuantityTs>
|
||||
struct IsRegisteredQuantityList<TypeList<QuantityTs...>> : std::true_type {
|
||||
};
|
||||
struct IsRegisteredQuantityList<TypeList<QuantityTs...>> : std::true_type { };
|
||||
|
||||
template <typename ListT>
|
||||
inline constexpr bool isRegisteredQuantityList =
|
||||
IsRegisteredQuantityList<ListT>::value;
|
||||
template <typename ListT> inline constexpr bool isRegisteredQuantityList = IsRegisteredQuantityList<ListT>::value;
|
||||
|
||||
template <typename ListT> struct IsFieldFormList : std::false_type { };
|
||||
|
||||
template <FieldForm... FormTs>
|
||||
struct IsFieldFormList<TypeList<FormTs...>> : std::true_type { };
|
||||
template <FieldForm... FormTs> struct IsFieldFormList<TypeList<FormTs...>> : std::true_type { };
|
||||
|
||||
template <typename ListT> inline constexpr bool isFieldFormList = IsFieldFormList<ListT>::value;
|
||||
|
||||
struct FieldSupport { };
|
||||
|
||||
template <utils::domain::IsDomainOrSet DomainT> struct DomainSupport final : FieldSupport {
|
||||
using Domain = DomainT;
|
||||
};
|
||||
|
||||
struct NonSpatialSupport final : FieldSupport { };
|
||||
|
||||
template <typename T> constexpr bool isDomainSupportV = false;
|
||||
|
||||
template <utils::domain::IsDomainOrSet DomainT> constexpr bool isDomainSupportV<DomainSupport<DomainT>> = true;
|
||||
|
||||
template <typename T>
|
||||
concept IsDomainSupport = isDomainSupportV<T>;
|
||||
|
||||
template <typename T>
|
||||
concept IsFieldSupport = std::derived_from<T, FieldSupport>;
|
||||
|
||||
template <typename FieldT> using FieldSupportT = typename FieldT::Support;
|
||||
|
||||
template <typename FieldT>
|
||||
concept DomainSupportedField = requires { typename FieldT::Support; } && IsDomainSupport<FieldSupportT<FieldT>>;
|
||||
|
||||
template <typename FieldT>
|
||||
concept NonSpatialField =
|
||||
requires { typename FieldT::Support; } && std::same_as<FieldSupportT<FieldT>, NonSpatialSupport>;
|
||||
|
||||
template <DomainSupportedField FieldT> using FieldDomainT = typename FieldSupportT<FieldT>::Domain;
|
||||
|
||||
template <typename ListT>
|
||||
inline constexpr bool isFieldFormList = IsFieldFormList<ListT>::value;
|
||||
} // namespace mean_field::field
|
||||
@@ -26,9 +26,7 @@ namespace mean_field::field::detail {
|
||||
int familyOrder,
|
||||
int dimension
|
||||
) {
|
||||
return std::make_unique<mfem::L2_FECollection>(
|
||||
familyOrder, dimension
|
||||
);
|
||||
return std::make_unique<mfem::L2_FECollection>(familyOrder, dimension);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -37,9 +35,7 @@ namespace mean_field::field::detail {
|
||||
int familyOrder,
|
||||
int dimension
|
||||
) {
|
||||
return std::make_unique<mfem::H1_FECollection>(
|
||||
familyOrder, dimension
|
||||
);
|
||||
return std::make_unique<mfem::H1_FECollection>(familyOrder, dimension);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -48,9 +44,7 @@ namespace mean_field::field::detail {
|
||||
int familyOrder,
|
||||
int dimension
|
||||
) {
|
||||
return std::make_unique<mfem::RT_FECollection>(
|
||||
familyOrder, dimension
|
||||
);
|
||||
return std::make_unique<mfem::RT_FECollection>(familyOrder, dimension);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -59,9 +53,7 @@ namespace mean_field::field::detail {
|
||||
int familyOrder,
|
||||
int dimension
|
||||
) {
|
||||
return std::make_unique<mfem::ND_FECollection>(
|
||||
familyOrder, dimension
|
||||
);
|
||||
return std::make_unique<mfem::ND_FECollection>(familyOrder, dimension);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -86,8 +78,7 @@ namespace mean_field::field::detail {
|
||||
static constexpr int orderValue = []() consteval {
|
||||
if constexpr (GlobalScalarQuantity<QuantityT>) {
|
||||
static_assert(
|
||||
std::same_as<OperationT, FieldOperation::Value>,
|
||||
"Global scalars support only the value operation."
|
||||
std::same_as<OperationT, FieldOperation::Value>, "Global scalars support only the value operation."
|
||||
);
|
||||
|
||||
return 0;
|
||||
@@ -101,51 +92,36 @@ namespace mean_field::field::detail {
|
||||
} else {
|
||||
return familyOrder;
|
||||
}
|
||||
} else if constexpr (
|
||||
std::same_as<OperationT, FieldOperation::Divergence>
|
||||
) {
|
||||
} else if constexpr (std::same_as<OperationT, FieldOperation::Divergence>) {
|
||||
static_assert(
|
||||
std::same_as<Space, RT>,
|
||||
"Only RT quantities currently support the divergence "
|
||||
"polynomial-order rule."
|
||||
std::same_as<Space, RT>, "Only RT quantities currently support the divergence "
|
||||
"polynomial-order rule."
|
||||
);
|
||||
|
||||
return familyOrder;
|
||||
} else if constexpr (
|
||||
std::same_as<OperationT, FieldOperation::Gradient>
|
||||
) {
|
||||
} else if constexpr (std::same_as<OperationT, FieldOperation::Gradient>) {
|
||||
static_assert(
|
||||
std::same_as<Space, H1>,
|
||||
"Only H1 quantities currently support the gradient "
|
||||
"polynomial-order rule."
|
||||
std::same_as<Space, H1>, "Only H1 quantities currently support the gradient "
|
||||
"polynomial-order rule."
|
||||
);
|
||||
|
||||
return familyOrder > 0 ? familyOrder - 1 : 0;
|
||||
} else if constexpr (
|
||||
std::same_as<OperationT, FieldOperation::Curl>
|
||||
) {
|
||||
} else if constexpr (std::same_as<OperationT, FieldOperation::Curl>) {
|
||||
static_assert(
|
||||
std::same_as<Space, ND>,
|
||||
"Only ND quantities currently support the curl "
|
||||
"polynomial-order rule."
|
||||
std::same_as<Space, ND>, "Only ND quantities currently support the curl "
|
||||
"polynomial-order rule."
|
||||
);
|
||||
|
||||
return familyOrder > 0 ? familyOrder - 1 : 0;
|
||||
} else if constexpr (
|
||||
std::same_as<OperationT, FieldOperation::NormalTrace>
|
||||
) {
|
||||
} else if constexpr (std::same_as<OperationT, FieldOperation::NormalTrace>) {
|
||||
static_assert(
|
||||
std::same_as<Space, RT>,
|
||||
"Only RT quantities currently support the normal-trace "
|
||||
"polynomial-order rule."
|
||||
std::same_as<Space, RT>, "Only RT quantities currently support the normal-trace "
|
||||
"polynomial-order rule."
|
||||
);
|
||||
|
||||
return familyOrder;
|
||||
} else {
|
||||
static_assert(
|
||||
alwaysFalse<OperationT>,
|
||||
"Unsupported MFEM field operation."
|
||||
);
|
||||
static_assert(alwaysFalse<OperationT>, "Unsupported MFEM field operation.");
|
||||
}
|
||||
}
|
||||
}();
|
||||
@@ -157,14 +133,9 @@ namespace mean_field::field::detail {
|
||||
|
||||
template <typename FormT> struct MfemFormOrder;
|
||||
|
||||
template <
|
||||
auto PolicyKeyV,
|
||||
std::size_t DynamicOrderCountV,
|
||||
FieldOperand... OperandTs>
|
||||
struct MfemFormOrder<
|
||||
FormSpec<PolicyKeyV, DynamicOrderCountV, OperandTs...>> {
|
||||
static constexpr int staticOrder =
|
||||
(MfemOperandOrder<OperandTs>::orderValue + ... + 0);
|
||||
template <auto PolicyKeyV, std::size_t DynamicOrderCountV, FieldOperand... OperandTs>
|
||||
struct MfemFormOrder<FormSpec<PolicyKeyV, DynamicOrderCountV, OperandTs...>> {
|
||||
static constexpr int staticOrder = (MfemOperandOrder<OperandTs>::orderValue + ... + 0);
|
||||
};
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
@@ -183,8 +154,7 @@ namespace mean_field::field::detail {
|
||||
if constexpr (QuantityT::rankValue == 0) {
|
||||
return 1;
|
||||
} else if constexpr (
|
||||
std::same_as<typename QuantityT::Space, H1> ||
|
||||
std::same_as<typename QuantityT::Space, L2>
|
||||
std::same_as<typename QuantityT::Space, H1> || std::same_as<typename QuantityT::Space, L2>
|
||||
) {
|
||||
return spaceDimension;
|
||||
} else {
|
||||
@@ -192,12 +162,10 @@ namespace mean_field::field::detail {
|
||||
}
|
||||
}
|
||||
|
||||
template <FieldQuantity QuantityT>
|
||||
constexpr mfem::Ordering::Type get_ordering() {
|
||||
template <FieldQuantity QuantityT> constexpr mfem::Ordering::Type get_ordering() {
|
||||
if constexpr (
|
||||
QuantityT::rankValue == 1 &&
|
||||
(std::same_as<typename QuantityT::Space, H1> ||
|
||||
std::same_as<typename QuantityT::Space, L2>)
|
||||
(std::same_as<typename QuantityT::Space, H1> || std::same_as<typename QuantityT::Space, L2>)
|
||||
) {
|
||||
return mfem::Ordering::byVDIM;
|
||||
} else {
|
||||
@@ -213,40 +181,29 @@ namespace mean_field::field::detail {
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
template <FieldQuantity QuantityT> struct MfemQuantityTraits {
|
||||
static std::unique_ptr<mfem::FiniteElementCollection>
|
||||
make_fec(int dimension) {
|
||||
return FecFor<typename QuantityT::Space>::make(
|
||||
QuantityT::familyOrder, dimension
|
||||
);
|
||||
static std::unique_ptr<mfem::FiniteElementCollection> make_fec(int dimension) {
|
||||
return FecFor<typename QuantityT::Space>::make(QuantityT::familyOrder, dimension);
|
||||
}
|
||||
|
||||
static constexpr mfem::Ordering::Type ordering =
|
||||
get_ordering<QuantityT>();
|
||||
static constexpr mfem::Ordering::Type ordering = get_ordering<QuantityT>();
|
||||
};
|
||||
|
||||
template <> struct MfemQuantityTraits<Gravity::Flux> {
|
||||
static std::unique_ptr<mfem::FiniteElementCollection>
|
||||
make_fec(int dimension) {
|
||||
static std::unique_ptr<mfem::FiniteElementCollection> make_fec(int dimension) {
|
||||
return std::make_unique<mfem::RT_FECollection>(
|
||||
Gravity::Flux::familyOrder, dimension,
|
||||
mfem::BasisType::GaussLobatto, mfem::BasisType::IntegratedGLL
|
||||
Gravity::Flux::familyOrder, dimension, mfem::BasisType::GaussLobatto, mfem::BasisType::IntegratedGLL
|
||||
);
|
||||
}
|
||||
|
||||
static constexpr mfem::Ordering::Type ordering =
|
||||
mfem::Ordering::byNODES;
|
||||
static constexpr mfem::Ordering::Type ordering = mfem::Ordering::byNODES;
|
||||
};
|
||||
|
||||
template <> struct MfemQuantityTraits<Displacement::Vector> {
|
||||
static std::unique_ptr<mfem::FiniteElementCollection>
|
||||
make_fec(int dimension) {
|
||||
return FecFor<H1>::make(
|
||||
Displacement::Vector::familyOrder, dimension
|
||||
);
|
||||
static std::unique_ptr<mfem::FiniteElementCollection> make_fec(int dimension) {
|
||||
return FecFor<H1>::make(Displacement::Vector::familyOrder, dimension);
|
||||
}
|
||||
|
||||
static constexpr mfem::Ordering::Type ordering =
|
||||
mfem::Ordering::byNODES;
|
||||
static constexpr mfem::Ordering::Type ordering = mfem::Ordering::byNODES;
|
||||
};
|
||||
} // namespace mean_field::field::detail
|
||||
|
||||
@@ -275,8 +232,7 @@ export namespace mean_field::field {
|
||||
requires typeListContains<
|
||||
QuantityT,
|
||||
typename TagT::Quantities>
|
||||
static std::unique_ptr<mfem::FiniteElementCollection>
|
||||
make_fec(int dimension) {
|
||||
static std::unique_ptr<mfem::FiniteElementCollection> make_fec(int dimension) {
|
||||
if (dimension <= 0) {
|
||||
throw std::invalid_argument("Mesh dimension must be positive.");
|
||||
}
|
||||
@@ -299,8 +255,7 @@ export namespace mean_field::field {
|
||||
mfem::FiniteElementCollection &finiteElementCollection
|
||||
) {
|
||||
return std::make_unique<mfem::ParFiniteElementSpace>(
|
||||
&mesh, &finiteElementCollection,
|
||||
detail::get_vdim<QuantityT>(mesh.SpaceDimension()),
|
||||
&mesh, &finiteElementCollection, detail::get_vdim<QuantityT>(mesh.SpaceDimension()),
|
||||
detail::MfemQuantityTraits<QuantityT>::ordering
|
||||
);
|
||||
}
|
||||
@@ -338,22 +293,17 @@ export namespace mean_field::field {
|
||||
int,
|
||||
FormT::dynamicOrderCount> dynamicOrders = {},
|
||||
utils::DOMAINS domain = utils::DOMAINS::ALL,
|
||||
quadrature::MappingKind mapping = quadrature::MappingKind::none
|
||||
quadrature::MappingKind mapping = quadrature::MappingKind::none
|
||||
) {
|
||||
if (geometryWeightOrder < 0) {
|
||||
throw std::invalid_argument(
|
||||
"Geometry weight order cannot be negative."
|
||||
);
|
||||
throw std::invalid_argument("Geometry weight order cannot be negative.");
|
||||
}
|
||||
|
||||
int baseOrder =
|
||||
detail::MfemFormOrder<FormT>::staticOrder + geometryWeightOrder;
|
||||
int baseOrder = detail::MfemFormOrder<FormT>::staticOrder + geometryWeightOrder;
|
||||
|
||||
for (const int dynamicOrder : dynamicOrders) {
|
||||
if (dynamicOrder < 0) {
|
||||
throw std::invalid_argument(
|
||||
"Dynamic polynomial orders cannot be negative."
|
||||
);
|
||||
throw std::invalid_argument("Dynamic polynomial orders cannot be negative.");
|
||||
}
|
||||
|
||||
baseOrder += dynamicOrder;
|
||||
@@ -377,4 +327,580 @@ export namespace mean_field::field {
|
||||
static_assert(FieldTag<Displacement>);
|
||||
static_assert(FieldTag<Density>);
|
||||
static_assert(FieldTag<BarotropicConstant>);
|
||||
|
||||
/*
|
||||
* Field-support realization onto MFEM element and DOF indices.
|
||||
*
|
||||
* A field's compile-time Support is declared in field.registry.
|
||||
* These utilities resolve that semantic support through a DomainSchema
|
||||
* onto a concrete MFEM finite-element space.
|
||||
*
|
||||
* Important:
|
||||
*
|
||||
* active DOFs = union of DOFs touched by supported elements
|
||||
*
|
||||
* This is deliberately NOT implemented as "remove every DOF touched by
|
||||
* an unsupported element". For continuous spaces such as H1, a DOF on
|
||||
* the Stellar/Vacuum interface is shared by elements on both sides and
|
||||
* remains an active stellar-field DOF.
|
||||
*/
|
||||
|
||||
struct FieldLocalDofSupport {
|
||||
/*
|
||||
* Marker in local/vector-DOF numbering.
|
||||
*
|
||||
* Size == finiteElementSpace.GetVSize().
|
||||
* Entries are 1 for active DOFs and 0 otherwise.
|
||||
*/
|
||||
mfem::Array<int> activeVDofMarker;
|
||||
|
||||
/*
|
||||
* Sorted MFEM local/vector DOF indices.
|
||||
*/
|
||||
mfem::Array<int> activeVDofs;
|
||||
mfem::Array<int> inactiveVDofs;
|
||||
};
|
||||
|
||||
struct FieldDofSupport {
|
||||
/*
|
||||
* Local/vector-DOF information.
|
||||
*
|
||||
* For a ParFiniteElementSpace the marker is synchronized across
|
||||
* neighboring ranks before these lists are constructed, so a shared
|
||||
* DOF is active on every rank carrying it if any rank has a supported
|
||||
* element touching it.
|
||||
*/
|
||||
mfem::Array<int> activeVDofMarker;
|
||||
mfem::Array<int> activeVDofs;
|
||||
mfem::Array<int> inactiveVDofs;
|
||||
|
||||
/*
|
||||
* True-DOF information owned by this MPI rank.
|
||||
*
|
||||
* Size of activeTrueDofMarker == GetTrueVSize().
|
||||
*/
|
||||
mfem::Array<int> activeTrueDofMarker;
|
||||
mfem::Array<int> activeTrueDofs;
|
||||
mfem::Array<int> inactiveTrueDofs;
|
||||
};
|
||||
|
||||
template <typename FieldT>
|
||||
concept MfemDomainField = FieldTag<FieldT> && DomainSupportedField<FieldT>;
|
||||
|
||||
template <
|
||||
MfemDomainField FieldT,
|
||||
utils::domain::IsSchema SchemaT>
|
||||
[[nodiscard]]
|
||||
bool element_is_in_field_support(
|
||||
const mfem::Mesh &mesh,
|
||||
const int elementId
|
||||
) {
|
||||
using DomainT = FieldDomainT<FieldT>;
|
||||
|
||||
static_assert(
|
||||
SchemaT::template contains_domain<DomainT>(), "The field support is not completely registered in the "
|
||||
"supplied DomainSchema."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
elementId >= 0 && elementId < mesh.GetNE(), "The requested field-support element ID is outside the mesh."
|
||||
);
|
||||
|
||||
return SchemaT::template attribute_belongs_to<DomainT>(mesh.GetAttribute(elementId));
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
inline void build_marker_lists(
|
||||
const mfem::Array<int> &activeMarker,
|
||||
mfem::Array<int> &activeDofs,
|
||||
mfem::Array<int> &inactiveDofs
|
||||
) {
|
||||
mfem::FiniteElementSpace::MarkerToList(activeMarker, activeDofs);
|
||||
|
||||
mfem::Array<int> inactiveMarker(activeMarker.Size());
|
||||
|
||||
for (int dofId = 0; dofId < activeMarker.Size(); ++dofId) {
|
||||
inactiveMarker[dofId] = activeMarker[dofId] == 0 ? 1 : 0;
|
||||
}
|
||||
|
||||
mfem::FiniteElementSpace::MarkerToList(inactiveMarker, inactiveDofs);
|
||||
}
|
||||
|
||||
template <
|
||||
MfemDomainField FieldT,
|
||||
utils::domain::IsSchema SchemaT>
|
||||
[[nodiscard]]
|
||||
mfem::Array<int> build_local_active_vdof_marker(const mfem::FiniteElementSpace &finiteElementSpace) {
|
||||
using DomainT = FieldDomainT<FieldT>;
|
||||
|
||||
static_assert(
|
||||
SchemaT::template contains_domain<DomainT>(), "The field support is not completely registered in the "
|
||||
"supplied DomainSchema."
|
||||
);
|
||||
|
||||
const mfem::Mesh *mesh = finiteElementSpace.GetMesh();
|
||||
|
||||
MFEM_VERIFY(mesh != nullptr, "Field-support DOF resolution requires an MFEM mesh.");
|
||||
|
||||
MFEM_VERIFY(
|
||||
finiteElementSpace.GetNE() == mesh->GetNE(), "The finite-element space and mesh have incompatible "
|
||||
"element counts."
|
||||
);
|
||||
|
||||
mfem::Array<int> activeMarker(finiteElementSpace.GetVSize());
|
||||
|
||||
activeMarker = 0;
|
||||
|
||||
mfem::Array<int> elementVDofs;
|
||||
|
||||
for (int elementId = 0; elementId < mesh->GetNE(); ++elementId) {
|
||||
const int materialId = mesh->GetAttribute(elementId);
|
||||
|
||||
if (!SchemaT::template attribute_belongs_to<DomainT>(materialId)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
finiteElementSpace.GetElementVDofs(elementId, elementVDofs);
|
||||
|
||||
for (int localIndex = 0; localIndex < elementVDofs.Size(); ++localIndex) {
|
||||
/*
|
||||
* MFEM can encode orientation in a DOF index by using a
|
||||
* negative value. DecodeDof removes that orientation sign
|
||||
* and returns the actual local/vector DOF index.
|
||||
*/
|
||||
const int vdof = mfem::FiniteElementSpace::DecodeDof(elementVDofs[localIndex]);
|
||||
|
||||
MFEM_VERIFY(
|
||||
vdof >= 0 && vdof < finiteElementSpace.GetVSize(),
|
||||
"MFEM returned an invalid element vector DOF."
|
||||
);
|
||||
|
||||
activeMarker[vdof] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
return activeMarker;
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
/*
|
||||
* Serial/local support resolution.
|
||||
*
|
||||
* This works with any mfem::FiniteElementSpace and is particularly
|
||||
* useful for topology/unit tests.
|
||||
*
|
||||
* The returned indices use MFEM local/vector-DOF numbering, not
|
||||
* true-DOF numbering.
|
||||
*/
|
||||
template <
|
||||
MfemDomainField FieldT,
|
||||
utils::domain::IsSchema SchemaT>
|
||||
[[nodiscard]]
|
||||
FieldLocalDofSupport resolve_field_local_dof_support(const mfem::FiniteElementSpace &finiteElementSpace) {
|
||||
FieldLocalDofSupport result;
|
||||
|
||||
result.activeVDofMarker = detail::build_local_active_vdof_marker<FieldT, SchemaT>(finiteElementSpace);
|
||||
|
||||
detail::build_marker_lists(result.activeVDofMarker, result.activeVDofs, result.inactiveVDofs);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Parallel production support resolution.
|
||||
*
|
||||
* This additionally converts the field support to the locally-owned
|
||||
* true-DOF numbering used by nonlinear vectors and operators.
|
||||
*
|
||||
* For now this intentionally requires a conforming ParFiniteElementSpace.
|
||||
* MFEM's nonconforming spaces require an additional constraint/conforming-
|
||||
* DOF projection step; silently treating their local DOFs as ordinary
|
||||
* true DOFs would be incorrect.
|
||||
*/
|
||||
template <
|
||||
MfemDomainField FieldT,
|
||||
utils::domain::IsSchema SchemaT>
|
||||
[[nodiscard]]
|
||||
FieldDofSupport resolve_field_dof_support(const mfem::ParFiniteElementSpace &finiteElementSpace) {
|
||||
FieldDofSupport result;
|
||||
|
||||
MFEM_VERIFY(
|
||||
!finiteElementSpace.Nonconforming(), "Field-support true-DOF resolution currently requires a "
|
||||
"conforming mfem::ParFiniteElementSpace."
|
||||
);
|
||||
|
||||
result.activeVDofMarker = detail::build_local_active_vdof_marker<FieldT, SchemaT>(finiteElementSpace);
|
||||
|
||||
/*
|
||||
* Shared H1/RT DOFs can lie on an MPI partition boundary.
|
||||
*
|
||||
* If a supported element exists on one rank and the shared DOF also
|
||||
* exists on a neighboring rank whose local elements are unsupported,
|
||||
* that DOF must nevertheless be active globally.
|
||||
*
|
||||
* MFEM Synchronize performs the required OR-like synchronization of
|
||||
* the marker across shared local DOFs.
|
||||
*/
|
||||
finiteElementSpace.Synchronize(result.activeVDofMarker);
|
||||
|
||||
detail::build_marker_lists(result.activeVDofMarker, result.activeVDofs, result.inactiveVDofs);
|
||||
|
||||
result.activeTrueDofMarker.SetSize(finiteElementSpace.GetTrueVSize());
|
||||
|
||||
result.activeTrueDofMarker = 0;
|
||||
|
||||
for (int vdof = 0; vdof < result.activeVDofMarker.Size(); ++vdof) {
|
||||
if (result.activeVDofMarker[vdof] == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* GetLocalTDofNumber returns the locally-owned true-DOF index
|
||||
* for this local/vector DOF, or -1 when this rank does not own
|
||||
* the shared true DOF.
|
||||
*
|
||||
* Because activeVDofMarker was synchronized first, the owning
|
||||
* rank will also see the active marker.
|
||||
*/
|
||||
const int trueDof = finiteElementSpace.GetLocalTDofNumber(vdof);
|
||||
|
||||
if (trueDof < 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
MFEM_VERIFY(trueDof < result.activeTrueDofMarker.Size(), "MFEM returned an invalid local true DOF.");
|
||||
|
||||
result.activeTrueDofMarker[trueDof] = 1;
|
||||
}
|
||||
|
||||
detail::build_marker_lists(result.activeTrueDofMarker, result.activeTrueDofs, result.inactiveTrueDofs);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Canonical correspondence between a dense reduced field vector and
|
||||
* the selected MFEM true DOFs representing that field.
|
||||
*
|
||||
* The map contains no field, domain, mesh, or solver policy. It is an
|
||||
* immutable indexing object once constructed:
|
||||
*
|
||||
* reduced index i
|
||||
* |
|
||||
* v
|
||||
* reducedToTrue[i]
|
||||
* |
|
||||
* v
|
||||
* MFEM true DOF
|
||||
*
|
||||
* trueToReduced supplies the inverse map. Unsupported true DOFs carry
|
||||
* the sentinel -1.
|
||||
*
|
||||
* The reduced-to-true list is required to be strictly increasing.
|
||||
* This makes reduced ordering deterministic and agrees with the
|
||||
* canonical ordering produced by MFEM MarkerToList().
|
||||
*/
|
||||
class FieldDofMap {
|
||||
public:
|
||||
FieldDofMap() = default;
|
||||
|
||||
FieldDofMap(
|
||||
const int fullTrueDofSize,
|
||||
const mfem::Array<int> &reducedToTrue
|
||||
) {
|
||||
if (fullTrueDofSize < 0) {
|
||||
throw std::invalid_argument("FieldDofMap requires a non-negative full true-DOF size.");
|
||||
}
|
||||
|
||||
m_fullTrueDofSize = fullTrueDofSize;
|
||||
|
||||
m_reducedToTrue.SetSize(reducedToTrue.Size());
|
||||
|
||||
m_trueToReduced.SetSize(m_fullTrueDofSize);
|
||||
|
||||
m_trueToReduced = -1;
|
||||
|
||||
int previousTrueDof = -1;
|
||||
|
||||
for (int reducedDof = 0; reducedDof < reducedToTrue.Size(); ++reducedDof) {
|
||||
const int trueDof = reducedToTrue[reducedDof];
|
||||
|
||||
if (trueDof < 0 || trueDof >= m_fullTrueDofSize) {
|
||||
throw std::invalid_argument(
|
||||
"FieldDofMap contains a true DOF outside the full "
|
||||
"true-DOF space."
|
||||
);
|
||||
}
|
||||
|
||||
if (reducedDof > 0 && trueDof <= previousTrueDof) {
|
||||
throw std::invalid_argument(
|
||||
"FieldDofMap reduced-to-true indices must be "
|
||||
"strictly increasing and unique."
|
||||
);
|
||||
}
|
||||
|
||||
m_reducedToTrue[reducedDof] = trueDof;
|
||||
|
||||
m_trueToReduced[trueDof] = reducedDof;
|
||||
|
||||
previousTrueDof = trueDof;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Construct directly from the support result produced by
|
||||
* resolve_field_dof_support().
|
||||
*
|
||||
* The marker is checked against the active true-DOF list so that
|
||||
* an internally inconsistent FieldDofSupport cannot silently
|
||||
* produce a solver map.
|
||||
*/
|
||||
explicit FieldDofMap(const FieldDofSupport &support)
|
||||
: FieldDofMap(
|
||||
support.activeTrueDofMarker.Size(),
|
||||
support.activeTrueDofs
|
||||
) {
|
||||
for (int trueDof = 0; trueDof < m_fullTrueDofSize; ++trueDof) {
|
||||
const bool markerSaysActive = support.activeTrueDofMarker[trueDof] != 0;
|
||||
|
||||
const bool mapSaysActive = m_trueToReduced[trueDof] >= 0;
|
||||
|
||||
if (markerSaysActive != mapSaysActive) {
|
||||
throw std::invalid_argument(
|
||||
"FieldDofSupport active marker and active true-DOF "
|
||||
"list are inconsistent."
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
int full_size() const noexcept {
|
||||
return m_fullTrueDofSize;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
int reduced_size() const noexcept {
|
||||
return m_reducedToTrue.Size();
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
int inactive_size() const noexcept {
|
||||
return full_size() - reduced_size();
|
||||
}
|
||||
|
||||
/*
|
||||
* Because reducedToTrue is strictly increasing, a map containing
|
||||
* every true DOF necessarily has
|
||||
*
|
||||
* reducedToTrue[i] == i.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
bool is_identity() const noexcept {
|
||||
return reduced_size() == full_size();
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
const mfem::Array<int> &reduced_to_true() const noexcept {
|
||||
return m_reducedToTrue;
|
||||
}
|
||||
|
||||
/*
|
||||
* Values are:
|
||||
*
|
||||
* >= 0 reduced DOF index
|
||||
* -1 unsupported/inactive true DOF
|
||||
*/
|
||||
[[nodiscard]]
|
||||
const mfem::Array<int> &true_to_reduced() const noexcept {
|
||||
return m_trueToReduced;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
bool contains_true_dof(const int trueDof) const {
|
||||
validate_true_dof(trueDof);
|
||||
|
||||
return m_trueToReduced[trueDof] >= 0;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
int true_dof(const int reducedDof) const {
|
||||
if (reducedDof < 0 || reducedDof >= reduced_size()) {
|
||||
throw std::out_of_range("Reduced DOF index is outside FieldDofMap.");
|
||||
}
|
||||
|
||||
return m_reducedToTrue[reducedDof];
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
std::optional<int> reduced_dof(const int trueDof) const {
|
||||
validate_true_dof(trueDof);
|
||||
|
||||
const int reducedDof = m_trueToReduced[trueDof];
|
||||
|
||||
if (reducedDof < 0) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return reducedDof;
|
||||
}
|
||||
|
||||
/*
|
||||
* Gather:
|
||||
*
|
||||
* full MFEM true vector
|
||||
* |
|
||||
* v
|
||||
* dense reduced solver vector
|
||||
*/
|
||||
void gather(
|
||||
const mfem::Vector &full,
|
||||
mfem::Vector &reduced
|
||||
) const {
|
||||
require_full_size(full);
|
||||
|
||||
require_reduced_size(reduced);
|
||||
|
||||
for (int reducedDof = 0; reducedDof < reduced_size(); ++reducedDof) {
|
||||
reduced(reducedDof) = full(m_reducedToTrue[reducedDof]);
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
mfem::Vector gather(const mfem::Vector &full) const {
|
||||
mfem::Vector reduced(reduced_size());
|
||||
|
||||
gather(full, reduced);
|
||||
|
||||
return reduced;
|
||||
}
|
||||
|
||||
/*
|
||||
* Scatter with projection semantics.
|
||||
*
|
||||
* All unsupported true DOFs are explicitly zeroed.
|
||||
*
|
||||
* This is the normal operation for constructing a complete MFEM
|
||||
* representation of a supported field from the reduced nonlinear
|
||||
* state.
|
||||
*
|
||||
* The output vector is NOT resized. This is intentional: callers
|
||||
* may provide an mfem::Vector view into an mfem::BlockVector.
|
||||
*/
|
||||
void scatter(
|
||||
const mfem::Vector &reduced,
|
||||
mfem::Vector &full
|
||||
) const {
|
||||
require_reduced_size(reduced);
|
||||
|
||||
require_full_size(full);
|
||||
|
||||
full = 0.0;
|
||||
|
||||
scatter_into(reduced, full);
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
mfem::Vector scatter(const mfem::Vector &reduced) const {
|
||||
mfem::Vector full(full_size());
|
||||
|
||||
scatter(reduced, full);
|
||||
|
||||
return full;
|
||||
}
|
||||
|
||||
/*
|
||||
* Scatter while preserving unsupported values already present in
|
||||
* the full vector.
|
||||
*
|
||||
* This is distinct from scatter() because future constrained field
|
||||
* representations may need to preserve prescribed values outside
|
||||
* the current reduced/free set.
|
||||
*/
|
||||
void scatter_into(
|
||||
const mfem::Vector &reduced,
|
||||
mfem::Vector &full
|
||||
) const {
|
||||
require_reduced_size(reduced);
|
||||
|
||||
require_full_size(full);
|
||||
|
||||
for (int reducedDof = 0; reducedDof < reduced_size(); ++reducedDof) {
|
||||
full(m_reducedToTrue[reducedDof]) = reduced(reducedDof);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Add a reduced vector into the selected true DOFs.
|
||||
*
|
||||
* Unsupported true DOFs are untouched.
|
||||
*/
|
||||
void scatter_add(
|
||||
const mfem::Vector &reduced,
|
||||
mfem::Vector &full,
|
||||
const double scale = 1.0
|
||||
) const {
|
||||
require_reduced_size(reduced);
|
||||
|
||||
require_full_size(full);
|
||||
|
||||
for (int reducedDof = 0; reducedDof < reduced_size(); ++reducedDof) {
|
||||
full(m_reducedToTrue[reducedDof]) += scale * reduced(reducedDof);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void validate_true_dof(const int trueDof) const {
|
||||
if (trueDof < 0 || trueDof >= full_size()) {
|
||||
throw std::out_of_range("True DOF index is outside FieldDofMap.");
|
||||
}
|
||||
}
|
||||
|
||||
void require_full_size(const mfem::Vector &vector) const {
|
||||
if (vector.Size() != full_size()) {
|
||||
throw std::invalid_argument("FieldDofMap full vector has an incompatible size.");
|
||||
}
|
||||
}
|
||||
|
||||
void require_reduced_size(const mfem::Vector &vector) const {
|
||||
if (vector.Size() != reduced_size()) {
|
||||
throw std::invalid_argument("FieldDofMap reduced vector has an incompatible size.");
|
||||
}
|
||||
}
|
||||
|
||||
int m_fullTrueDofSize{0};
|
||||
|
||||
/*
|
||||
* Canonical forward mapping:
|
||||
*
|
||||
* reduced -> MFEM true
|
||||
*/
|
||||
mfem::Array<int> m_reducedToTrue;
|
||||
|
||||
/*
|
||||
* Inverse mapping:
|
||||
*
|
||||
* MFEM true -> reduced
|
||||
*
|
||||
* Unsupported true DOFs are -1.
|
||||
*/
|
||||
mfem::Array<int> m_trueToReduced;
|
||||
};
|
||||
|
||||
/*
|
||||
* Construct the canonical solver map for a registered spatial field.
|
||||
*
|
||||
* Field and domain semantics are used only while constructing the map.
|
||||
* Consumers receive a plain FieldDofMap and therefore do not need to
|
||||
* understand DomainSchema or field-support types.
|
||||
*/
|
||||
template <
|
||||
MfemDomainField FieldT,
|
||||
utils::domain::IsSchema SchemaT>
|
||||
[[nodiscard]]
|
||||
FieldDofMap make_field_dof_map(const mfem::ParFiniteElementSpace &finiteElementSpace) {
|
||||
const FieldDofSupport support = resolve_field_dof_support<FieldT, SchemaT>(finiteElementSpace);
|
||||
|
||||
return FieldDofMap(support);
|
||||
}
|
||||
} // namespace mean_field::field
|
||||
|
||||
@@ -7,6 +7,7 @@ export module mean_field:field.registry;
|
||||
|
||||
export import :field.base;
|
||||
export import :quadrature.policy;
|
||||
export import :utils.domain;
|
||||
|
||||
export namespace mean_field::field {
|
||||
// =========================================================================
|
||||
@@ -17,70 +18,47 @@ export namespace mean_field::field {
|
||||
static constexpr std::string_view name = "density";
|
||||
static constexpr int scalarOrder = 2;
|
||||
|
||||
struct Scalar final
|
||||
: ScalarQ<FieldRelation::Independent, Disc<L2, scalarOrder>> {
|
||||
using Support = DomainSupport<utils::domain::Stellar>;
|
||||
|
||||
struct Scalar final : ScalarQ<FieldRelation::Independent, Disc<L2, scalarOrder>> {
|
||||
static constexpr std::string_view symbol = "ρ";
|
||||
};
|
||||
|
||||
using Quantities = TypeList<Scalar>;
|
||||
using Constraints = TypeList<>;
|
||||
using Quantities = TypeList<Scalar>;
|
||||
using Constraints = TypeList<>;
|
||||
|
||||
static constexpr bool constraintsAreValid =
|
||||
validate_constraints(Constraints{});
|
||||
static constexpr bool constraintsAreValid = validate_constraints(Constraints{});
|
||||
|
||||
static_assert(constraintsAreValid);
|
||||
|
||||
struct Form {
|
||||
// Density-space mass matrix: (rho, q).
|
||||
using ProjectionMass = FormSpec<
|
||||
quadrature::Term::density_projection,
|
||||
0,
|
||||
Operand<Scalar>,
|
||||
Operand<Scalar>>;
|
||||
using ProjectionMass = FormSpec<quadrature::Term::density_projection, 0, Operand<Scalar>, Operand<Scalar>>;
|
||||
|
||||
// Projection RHS with one runtime coefficient order.
|
||||
using ProjectionSource = FormSpec<
|
||||
quadrature::Term::density_projection,
|
||||
1,
|
||||
Operand<Scalar>>;
|
||||
using ProjectionSource = FormSpec<quadrature::Term::density_projection, 1, Operand<Scalar>>;
|
||||
|
||||
// Density-space contribution to the barotropic EOS closure:
|
||||
// (rho, q_rho).
|
||||
using EosClosureMass = FormSpec<
|
||||
quadrature::Term::eos_closure,
|
||||
0,
|
||||
Operand<Scalar>,
|
||||
Operand<Scalar>>;
|
||||
using EosClosureMass = FormSpec<quadrature::Term::eos_closure, 0, Operand<Scalar>, Operand<Scalar>>;
|
||||
|
||||
// Integral of density over the physical volume.
|
||||
using MassConservation = FormSpec<
|
||||
quadrature::Term::mass_conservation,
|
||||
0,
|
||||
Operand<Scalar>>;
|
||||
using MassConservation = FormSpec<quadrature::Term::mass_conservation, 0, Operand<Scalar>>;
|
||||
|
||||
// The same physical integral used as a nonlinear normalization
|
||||
// constraint. It has a distinct policy key so solver assembly and
|
||||
// diagnostics can be overintegrated independently.
|
||||
using MassNormalization = FormSpec<
|
||||
quadrature::Term::mass_normalization,
|
||||
0,
|
||||
Operand<Scalar>>;
|
||||
using MassNormalization = FormSpec<quadrature::Term::mass_normalization, 0, Operand<Scalar>>;
|
||||
|
||||
// Integral of rho * x. The combined position-coefficient order is
|
||||
// supplied as one dynamic order.
|
||||
using CenterOfMass =
|
||||
FormSpec<quadrature::Term::center_of_mass, 1, Operand<Scalar>>;
|
||||
using CenterOfMass = FormSpec<quadrature::Term::center_of_mass, 1, Operand<Scalar>>;
|
||||
|
||||
// Integral of rho times the quadratic position tensor. The
|
||||
// combined tensor-coefficient order is supplied dynamically.
|
||||
using Quadrupole =
|
||||
FormSpec<quadrature::Term::quadrupole, 1, Operand<Scalar>>;
|
||||
using Quadrupole = FormSpec<quadrature::Term::quadrupole, 1, Operand<Scalar>>;
|
||||
|
||||
using ErrorNorm = FormSpec<
|
||||
quadrature::Term::error_norm,
|
||||
0,
|
||||
Operand<Scalar>,
|
||||
Operand<Scalar>>;
|
||||
using ErrorNorm = FormSpec<quadrature::Term::error_norm, 0, Operand<Scalar>, Operand<Scalar>>;
|
||||
};
|
||||
|
||||
using FormList = TypeList<
|
||||
@@ -104,31 +82,26 @@ export namespace mean_field::field {
|
||||
static constexpr int potentialOrder = 2;
|
||||
static constexpr int fluxOrder = 2;
|
||||
|
||||
struct Potential final
|
||||
: ScalarQ<FieldRelation::Independent, Disc<L2, potentialOrder>> {
|
||||
using Support = DomainSupport<utils::domain::All>;
|
||||
|
||||
struct Potential final : ScalarQ<FieldRelation::Independent, Disc<L2, potentialOrder>> {
|
||||
static constexpr std::string_view symbol = "φ";
|
||||
};
|
||||
|
||||
struct Flux final
|
||||
: VectorQ<FieldRelation::Gradient<Potential>, Disc<RT, fluxOrder>> {
|
||||
struct Flux final : VectorQ<FieldRelation::Gradient<Potential>, Disc<RT, fluxOrder>> {
|
||||
static constexpr std::string_view symbol = "∇φ";
|
||||
};
|
||||
|
||||
using Quantities = TypeList<Potential, Flux>;
|
||||
using Quantities = TypeList<Potential, Flux>;
|
||||
|
||||
using Constraints = TypeList<RtL2StablePair<Flux, Potential>>;
|
||||
using Constraints = TypeList<RtL2StablePair<Flux, Potential>>;
|
||||
|
||||
static constexpr bool constraintsAreValid =
|
||||
validate_constraints(Constraints{});
|
||||
static constexpr bool constraintsAreValid = validate_constraints(Constraints{});
|
||||
|
||||
static_assert(constraintsAreValid);
|
||||
|
||||
struct Form {
|
||||
using HDivMass = FormSpec<
|
||||
quadrature::Term::gravity_hdiv_mass,
|
||||
0,
|
||||
Operand<Flux>,
|
||||
Operand<Flux>>;
|
||||
using HDivMass = FormSpec<quadrature::Term::gravity_hdiv_mass, 0, Operand<Flux>, Operand<Flux>>;
|
||||
|
||||
using DivergenceCoupling = FormSpec<
|
||||
quadrature::Term::gravity_divergence,
|
||||
@@ -144,31 +117,18 @@ export namespace mean_field::field {
|
||||
|
||||
// Density is a registered coefficient field and potential is the
|
||||
// test field, so the full polynomial order is compile-time data.
|
||||
using SourceLinear = FormSpec<
|
||||
quadrature::Term::gravity_source,
|
||||
0,
|
||||
Operand<Density::Scalar>,
|
||||
Operand<Potential>>;
|
||||
using SourceLinear =
|
||||
FormSpec<quadrature::Term::gravity_source, 0, Operand<Density::Scalar>, Operand<Potential>>;
|
||||
|
||||
// Mixed density-to-potential projection. Both trial and test
|
||||
// orders are registered quantities.
|
||||
using SourceProjection = FormSpec<
|
||||
quadrature::Term::gravity_source,
|
||||
0,
|
||||
Operand<Density::Scalar>,
|
||||
Operand<Potential>>;
|
||||
using SourceProjection =
|
||||
FormSpec<quadrature::Term::gravity_source, 0, Operand<Density::Scalar>, Operand<Potential>>;
|
||||
|
||||
using PotentialErrorNorm = FormSpec<
|
||||
quadrature::Term::error_norm,
|
||||
0,
|
||||
Operand<Potential>,
|
||||
Operand<Potential>>;
|
||||
using PotentialErrorNorm =
|
||||
FormSpec<quadrature::Term::error_norm, 0, Operand<Potential>, Operand<Potential>>;
|
||||
|
||||
using FluxErrorNorm = FormSpec<
|
||||
quadrature::Term::error_norm,
|
||||
0,
|
||||
Operand<Flux>,
|
||||
Operand<Flux>>;
|
||||
using FluxErrorNorm = FormSpec<quadrature::Term::error_norm, 0, Operand<Flux>, Operand<Flux>>;
|
||||
};
|
||||
|
||||
using FormList = TypeList<
|
||||
@@ -189,16 +149,16 @@ export namespace mean_field::field {
|
||||
static constexpr std::string_view name = "displacement";
|
||||
static constexpr int vectorOrder = 3;
|
||||
|
||||
struct Vector final
|
||||
: VectorQ<FieldRelation::Independent, Disc<H1, vectorOrder>> {
|
||||
using Support = DomainSupport<utils::domain::All>;
|
||||
|
||||
struct Vector final : VectorQ<FieldRelation::Independent, Disc<H1, vectorOrder>> {
|
||||
static constexpr std::string_view symbol = "d";
|
||||
};
|
||||
|
||||
using Quantities = TypeList<Vector>;
|
||||
using Constraints = TypeList<>;
|
||||
using Quantities = TypeList<Vector>;
|
||||
using Constraints = TypeList<>;
|
||||
|
||||
static constexpr bool constraintsAreValid =
|
||||
validate_constraints(Constraints{});
|
||||
static constexpr bool constraintsAreValid = validate_constraints(Constraints{});
|
||||
|
||||
static_assert(constraintsAreValid);
|
||||
|
||||
@@ -211,29 +171,50 @@ export namespace mean_field::field {
|
||||
Operand<Vector, FieldOperation::Gradient>,
|
||||
Operand<Vector, FieldOperation::Gradient>>;
|
||||
|
||||
using ErrorNorm = FormSpec<
|
||||
quadrature::Term::error_norm,
|
||||
// Positive gravitational contribution to the displacement row:
|
||||
//
|
||||
// int rho grad(phi) . w dV.
|
||||
//
|
||||
// Both the base geometry Jacobian and the displacement test
|
||||
// function contribute to the polynomial order. The RT flux is
|
||||
// mapped to physical space by the contravariant Piola map.
|
||||
using GravityForce = FormSpec<
|
||||
quadrature::Term::gravity_force,
|
||||
0,
|
||||
Operand<Vector>,
|
||||
Operand<Density::Scalar>,
|
||||
Operand<Gravity::Flux>,
|
||||
Operand<Vector, FieldOperation::Gradient>,
|
||||
Operand<Vector>>;
|
||||
|
||||
// Rigid-rotation contribution to the displacement row:
|
||||
//
|
||||
// -int rho grad(Psi_rotation) . w dV.
|
||||
//
|
||||
// grad(Psi_rotation) is linear in physical position, so its
|
||||
// polynomial order is supplied as one runtime contribution.
|
||||
using CentrifugalForce =
|
||||
FormSpec<quadrature::Term::centrifugal, 1, Operand<Density::Scalar>, Operand<Vector>>;
|
||||
|
||||
using ErrorNorm = FormSpec<quadrature::Term::error_norm, 0, Operand<Vector>, Operand<Vector>>;
|
||||
};
|
||||
|
||||
using FormList = TypeList<Form::MeshExtension, Form::ErrorNorm>;
|
||||
using FormList = TypeList<Form::MeshExtension, Form::GravityForce, Form::CentrifugalForce, Form::ErrorNorm>;
|
||||
};
|
||||
|
||||
struct BarotropicConstant {
|
||||
static constexpr std::string_view name = "barotropic_constant";
|
||||
|
||||
using Support = NonSpatialSupport;
|
||||
|
||||
struct Scalar final : GlobalScalarQ {
|
||||
static constexpr std::string_view symbol = "C";
|
||||
};
|
||||
|
||||
using Quantities = TypeList<Scalar>;
|
||||
using Constraints = TypeList<>;
|
||||
using FormList = TypeList<>;
|
||||
using Quantities = TypeList<Scalar>;
|
||||
using Constraints = TypeList<>;
|
||||
using FormList = TypeList<>;
|
||||
|
||||
static constexpr bool constraintsAreValid =
|
||||
validate_constraints(Constraints{});
|
||||
static constexpr bool constraintsAreValid = validate_constraints(Constraints{});
|
||||
|
||||
static_assert(constraintsAreValid);
|
||||
};
|
||||
@@ -250,16 +231,16 @@ export namespace mean_field::field {
|
||||
static constexpr std::string_view name = "specific_enthalpy";
|
||||
static constexpr int scalarOrder = 3;
|
||||
|
||||
struct Scalar final
|
||||
: ScalarQ<FieldRelation::Independent, Disc<H1, scalarOrder>> {
|
||||
using Support = DomainSupport<utils::domain::Stellar>;
|
||||
|
||||
struct Scalar final : ScalarQ<FieldRelation::Independent, Disc<H1, scalarOrder>> {
|
||||
static constexpr std::string_view symbol = "h";
|
||||
};
|
||||
|
||||
using Quantities = TypeList<Scalar>;
|
||||
using Constraints = TypeList<>;
|
||||
using Quantities = TypeList<Scalar>;
|
||||
using Constraints = TypeList<>;
|
||||
|
||||
static constexpr bool constraintsAreValid =
|
||||
validate_constraints(Constraints{});
|
||||
static constexpr bool constraintsAreValid = validate_constraints(Constraints{});
|
||||
|
||||
static_assert(constraintsAreValid);
|
||||
|
||||
@@ -268,33 +249,21 @@ export namespace mean_field::field {
|
||||
// the extra polynomial order introduced by the nonlinear EOS
|
||||
// beyond the registered order of h. For an n=3 polytrope this is
|
||||
// 2 * hOrder, making rho(h) cubic in h.
|
||||
using EosClosureSource = FormSpec<
|
||||
quadrature::Term::eos_closure,
|
||||
1,
|
||||
Operand<Scalar>,
|
||||
Operand<Density::Scalar>>;
|
||||
using EosClosureSource =
|
||||
FormSpec<quadrature::Term::eos_closure, 1, Operand<Scalar>, Operand<Density::Scalar>>;
|
||||
|
||||
// (h, q_h) contribution to
|
||||
// h + phi - Psi_rotation - C = 0.
|
||||
using EquilibriumEnthalpy = FormSpec<
|
||||
quadrature::Term::hydrostatic_equilibrium,
|
||||
0,
|
||||
Operand<Scalar>,
|
||||
Operand<Scalar>>;
|
||||
using EquilibriumEnthalpy =
|
||||
FormSpec<quadrature::Term::hydrostatic_equilibrium, 0, Operand<Scalar>, Operand<Scalar>>;
|
||||
|
||||
// (phi, q_h) contribution to hydrostatic equilibrium.
|
||||
using EquilibriumGravity = FormSpec<
|
||||
quadrature::Term::hydrostatic_equilibrium,
|
||||
0,
|
||||
Operand<Gravity::Potential>,
|
||||
Operand<Scalar>>;
|
||||
using EquilibriumGravity =
|
||||
FormSpec<quadrature::Term::hydrostatic_equilibrium, 0, Operand<Gravity::Potential>, Operand<Scalar>>;
|
||||
|
||||
// (Psi_rotation, q_h). The rotation-potential order is supplied
|
||||
// dynamically because it belongs to runtime rotation data.
|
||||
using EquilibriumRotation = FormSpec<
|
||||
quadrature::Term::hydrostatic_equilibrium,
|
||||
1,
|
||||
Operand<Scalar>>;
|
||||
using EquilibriumRotation = FormSpec<quadrature::Term::hydrostatic_equilibrium, 1, Operand<Scalar>>;
|
||||
|
||||
// (C, q_h), where C is spatially constant.
|
||||
using EquilibriumConstant = FormSpec<
|
||||
@@ -305,18 +274,11 @@ export namespace mean_field::field {
|
||||
|
||||
// Boundary trace form available for weak enforcement, testing, or
|
||||
// a future multiplier formulation of h|Gamma_star = 0.
|
||||
using IsobaricSurface = FormSpec<
|
||||
quadrature::Term::isobaric_surface,
|
||||
0,
|
||||
Operand<Scalar>,
|
||||
Operand<Scalar>>;
|
||||
using IsobaricSurface = FormSpec<quadrature::Term::isobaric_surface, 0, Operand<Scalar>, Operand<Scalar>>;
|
||||
|
||||
// Integral of P(h). The dynamic order is the extra EOS order
|
||||
// beyond the registered order of h.
|
||||
using PressureIntegral = FormSpec<
|
||||
quadrature::Term::pressure_integral,
|
||||
1,
|
||||
Operand<Scalar>>;
|
||||
using PressureIntegral = FormSpec<quadrature::Term::pressure_integral, 1, Operand<Scalar>>;
|
||||
|
||||
// Weak pressure force in the displacement test space:
|
||||
//
|
||||
@@ -325,17 +287,13 @@ export namespace mean_field::field {
|
||||
// which is equivalent to -int P(h) div(w) dV. The dynamic order
|
||||
// is the extra EOS order beyond the registered order of h. For an
|
||||
// n=3 polytrope this is 3 * hOrder, making P(h) quartic in h.
|
||||
using PressureForce = FormSpec<
|
||||
using PressureForce = FormSpec<
|
||||
quadrature::Term::pressure_force,
|
||||
1,
|
||||
Operand<Scalar>,
|
||||
Operand<Displacement::Vector, FieldOperation::Gradient>>;
|
||||
|
||||
using ErrorNorm = FormSpec<
|
||||
quadrature::Term::error_norm,
|
||||
0,
|
||||
Operand<Scalar>,
|
||||
Operand<Scalar>>;
|
||||
using ErrorNorm = FormSpec<quadrature::Term::error_norm, 0, Operand<Scalar>, Operand<Scalar>>;
|
||||
};
|
||||
|
||||
using FormList = TypeList<
|
||||
@@ -360,9 +318,10 @@ export namespace mean_field::field {
|
||||
typename T::Quantities;
|
||||
typename T::Constraints;
|
||||
typename T::FormList;
|
||||
typename T::Support;
|
||||
|
||||
{ T::name } -> std::convertible_to<std::string_view>;
|
||||
} && isRegisteredQuantityList<typename T::Quantities> &&
|
||||
} && IsFieldSupport<typename T::Support> && isRegisteredQuantityList<typename T::Quantities> &&
|
||||
isFieldFormList<typename T::FormList>;
|
||||
|
||||
static_assert(FieldTag<Gravity>);
|
||||
@@ -376,4 +335,22 @@ export namespace mean_field::field {
|
||||
static_assert(std::same_as<
|
||||
RelationTargetT<Gravity::Flux>,
|
||||
Gravity::Potential>);
|
||||
|
||||
static_assert(std::same_as<
|
||||
FieldDomainT<Density>,
|
||||
utils::domain::Stellar>);
|
||||
|
||||
static_assert(std::same_as<
|
||||
FieldDomainT<Enthalpy>,
|
||||
utils::domain::Stellar>);
|
||||
|
||||
static_assert(std::same_as<
|
||||
FieldDomainT<Gravity>,
|
||||
utils::domain::All>);
|
||||
|
||||
static_assert(std::same_as<
|
||||
FieldDomainT<Displacement>,
|
||||
utils::domain::All>);
|
||||
|
||||
static_assert(NonSpatialField<BarotropicConstant>);
|
||||
} // namespace mean_field::field
|
||||
|
||||
@@ -4,8 +4,7 @@ export module mean_field:integrators.centrifugal;
|
||||
export import :mapping.domain_mapper;
|
||||
|
||||
export namespace mean_field::integrators {
|
||||
class CentrifugalForceIntegrator
|
||||
: public mfem::BlockNonlinearFormIntegrator {
|
||||
class CentrifugalForceIntegrator : public mfem::BlockNonlinearFormIntegrator {
|
||||
public:
|
||||
CentrifugalForceIntegrator(
|
||||
const mapping::DomainMapper &map,
|
||||
|
||||
@@ -5,19 +5,13 @@ export module mean_field:integrators.gravity;
|
||||
import :mapping.domain_mapper;
|
||||
|
||||
export namespace mean_field::integrators {
|
||||
enum class GravityForceJacobianMode : std::uint8_t {
|
||||
minimal,
|
||||
field_coupled,
|
||||
exact
|
||||
};
|
||||
enum class GravityForceJacobianMode : std::uint8_t { minimal, field_coupled, exact };
|
||||
|
||||
class GravityMomentumIntegrator
|
||||
: public mfem::BlockNonlinearFormIntegrator {
|
||||
class GravityMomentumIntegrator : public mfem::BlockNonlinearFormIntegrator {
|
||||
public:
|
||||
explicit GravityMomentumIntegrator(
|
||||
const mapping::DomainMapper &map,
|
||||
GravityForceJacobianMode jacobian_mode =
|
||||
GravityForceJacobianMode::field_coupled
|
||||
GravityForceJacobianMode jacobian_mode = GravityForceJacobianMode::field_coupled
|
||||
);
|
||||
|
||||
void SetJacobianMode(GravityForceJacobianMode jacobian_mode);
|
||||
|
||||
@@ -4,8 +4,7 @@ export module mean_field:integrators.mass_continuity;
|
||||
import :mapping.domain_mapper;
|
||||
|
||||
export namespace mean_field::integrators {
|
||||
class ContinuityVolumeIntegrator
|
||||
: public mfem::BlockNonlinearFormIntegrator {
|
||||
class ContinuityVolumeIntegrator : public mfem::BlockNonlinearFormIntegrator {
|
||||
public:
|
||||
explicit ContinuityVolumeIntegrator(const mapping::DomainMapper &map);
|
||||
|
||||
|
||||
@@ -7,9 +7,7 @@ import :mapping.domain_mapper;
|
||||
import :utils.misc;
|
||||
|
||||
export namespace mean_field::integrators {
|
||||
template <utils::is_xad EOS_T>
|
||||
class PressureGradientIntegrator
|
||||
: public mfem::BlockNonlinearFormIntegrator {
|
||||
template <utils::is_xad EOS_T> class PressureGradientIntegrator : public mfem::BlockNonlinearFormIntegrator {
|
||||
public:
|
||||
PressureGradientIntegrator(
|
||||
const mapping::DomainMapper &map,
|
||||
@@ -74,8 +72,7 @@ export namespace mean_field::integrators {
|
||||
mfem::DenseMatrix dshape_v_ref(dof_v, dim), dshape_v_phys(dof_v, dim);
|
||||
mfem::Vector shape_rho(dof_rho);
|
||||
|
||||
const mfem::IntegrationRule *ir =
|
||||
&mfem::IntRules.Get(fe_v->GetGeomType(), 2 * fe_v->GetOrder());
|
||||
const mfem::IntegrationRule *ir = &mfem::IntRules.Get(fe_v->GetGeomType(), 2 * fe_v->GetOrder());
|
||||
|
||||
for (int q = 0; q < ir->GetNPoints(); ++q) {
|
||||
const mfem::IntegrationPoint &ip = ir->IntPoint(q);
|
||||
@@ -136,8 +133,7 @@ export namespace mean_field::integrators {
|
||||
mfem::DenseMatrix dshape_v_ref(dof_v, dim), dshape_v_phys(dof_v, dim);
|
||||
mfem::Vector shape_rho(dof_rho);
|
||||
|
||||
const mfem::IntegrationRule *ir =
|
||||
&mfem::IntRules.Get(fe_v->GetGeomType(), 2 * fe_v->GetOrder());
|
||||
const mfem::IntegrationRule *ir = &mfem::IntRules.Get(fe_v->GetGeomType(), 2 * fe_v->GetOrder());
|
||||
|
||||
for (int q = 0; q < ir->GetNPoints(); ++q) {
|
||||
using Scalar = EOS_T::value_type;
|
||||
@@ -168,8 +164,7 @@ export namespace mean_field::integrators {
|
||||
|
||||
double debug_K = 1.5;
|
||||
double debug_n = 3.0;
|
||||
double analytic_dp = debug_K * (1.0 + 1.0 / debug_n) *
|
||||
std::pow(xad::value(x_rho), 1.0 / debug_n);
|
||||
double analytic_dp = debug_K * (1.0 + 1.0 / debug_n) * std::pow(xad::value(x_rho), 1.0 / debug_n);
|
||||
|
||||
double ad_err = std::abs(dP_drho - analytic_dp);
|
||||
|
||||
@@ -177,9 +172,8 @@ export namespace mean_field::integrators {
|
||||
for (int c = 0; c < dim; ++c) {
|
||||
int row = i + c * dof_v;
|
||||
for (int j = 0; j < dof_rho; ++j) {
|
||||
int col = j;
|
||||
double term =
|
||||
dshape_v_phys(i, c) * dP_drho * shape_rho(j);
|
||||
int col = j;
|
||||
double term = dshape_v_phys(i, c) * dP_drho * shape_rho(j);
|
||||
(*dv_drho)(row, col) -= term * weight;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,9 +10,7 @@ export namespace mean_field::mapping::compactification {
|
||||
|
||||
class KelvinCompactification final : public ExteriorDomainMap {
|
||||
public:
|
||||
explicit KelvinCompactification(
|
||||
options::KelvinCompactificationOptions options
|
||||
);
|
||||
explicit KelvinCompactification(options::KelvinCompactificationOptions options);
|
||||
|
||||
[[nodiscard]] MappingStatus Evaluate(
|
||||
const ExteriorMapInput &input,
|
||||
|
||||
@@ -36,8 +36,7 @@ export namespace mean_field::mapping {
|
||||
mfem::Vector coordinate_gradient;
|
||||
};
|
||||
|
||||
[[nodiscard]] ElementDisplacementData
|
||||
ElementDisplacementDataFromElementVDofs(
|
||||
[[nodiscard]] ElementDisplacementData ElementDisplacementDataFromElementVDofs(
|
||||
const mfem::FiniteElement &element,
|
||||
const mfem::Vector &displacement_dofs
|
||||
);
|
||||
@@ -102,15 +101,13 @@ export namespace mean_field::mapping {
|
||||
public:
|
||||
DomainMapperStateless(
|
||||
utils::DomainMapperStatelessOptions options,
|
||||
std::unique_ptr<const compactification::ExteriorDomainMap>
|
||||
exterior_map
|
||||
std::unique_ptr<const compactification::ExteriorDomainMap> exterior_map
|
||||
);
|
||||
|
||||
DomainMapperStateless(const DomainMapperStateless &) = delete;
|
||||
DomainMapperStateless &
|
||||
operator=(const DomainMapperStateless &) = delete;
|
||||
DomainMapperStateless(DomainMapperStateless &&) = default;
|
||||
DomainMapperStateless &operator=(DomainMapperStateless &&) = default;
|
||||
DomainMapperStateless(const DomainMapperStateless &) = delete;
|
||||
DomainMapperStateless &operator=(const DomainMapperStateless &) = delete;
|
||||
DomainMapperStateless(DomainMapperStateless &&) = default;
|
||||
DomainMapperStateless &operator=(DomainMapperStateless &&) = default;
|
||||
|
||||
[[nodiscard]] MappingStatus EvaluatePoint(
|
||||
const ElementMappingData &element_data,
|
||||
@@ -168,13 +165,10 @@ export namespace mean_field::mapping {
|
||||
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;
|
||||
@@ -196,21 +190,18 @@ export namespace mean_field::mapping {
|
||||
CompactificationPointData &point_data
|
||||
) const;
|
||||
|
||||
[[nodiscard]] static mfem::ElementTransformation &
|
||||
SelectFaceElementTransformation(
|
||||
[[nodiscard]] static mfem::ElementTransformation &SelectFaceElementTransformation(
|
||||
mfem::FaceElementTransformations &transformation,
|
||||
FaceElementSide side
|
||||
);
|
||||
|
||||
[[nodiscard]] static const mfem::IntegrationPoint &
|
||||
SelectFaceElementIntegrationPoint(
|
||||
[[nodiscard]] static const mfem::IntegrationPoint &SelectFaceElementIntegrationPoint(
|
||||
mfem::FaceElementTransformations &transformation,
|
||||
FaceElementSide side
|
||||
);
|
||||
|
||||
utils::DomainMapperStatelessOptions m_options;
|
||||
std::unique_ptr<const compactification::ExteriorDomainMap>
|
||||
m_exterior_map;
|
||||
std::unique_ptr<const compactification::ExteriorDomainMap> m_exterior_map;
|
||||
};
|
||||
class DomainMapper {
|
||||
|
||||
@@ -226,8 +217,7 @@ export namespace mean_field::mapping {
|
||||
const double r_inf_ref
|
||||
);
|
||||
|
||||
[[nodiscard]] bool
|
||||
is_vacuum(const mfem::ElementTransformation &T) const;
|
||||
[[nodiscard]] bool is_vacuum(const mfem::ElementTransformation &T) const;
|
||||
|
||||
void SetDisplacement(const mfem::GridFunction &d);
|
||||
|
||||
|
||||
@@ -45,3 +45,21 @@ export import :operators.kernels.hydrostatic_equilibrium;
|
||||
export import :operators.context.hydrostatic_equilibrium;
|
||||
export import :operators.prepared_hydrostatic_equilibrium;
|
||||
export import :operators.kernels.pressure_force;
|
||||
export import :operators.context.pressure_force;
|
||||
export import :operators.prepared_pressure_force;
|
||||
export import :operators.kernels.gravity_displacement_force;
|
||||
export import :operators.prepared_gravity_displacement_force;
|
||||
export import :operators.context.rotational_displacement_force;
|
||||
export import :operators.kernels.rotational_displacement_force;
|
||||
export import :operators.prepared_rotational_displacement_force;
|
||||
export import :operators.prepared_displacement_residual;
|
||||
export import :model.structure_profile;
|
||||
export import :model.structure.base;
|
||||
export import :model.structure.polytropic;
|
||||
export import :eos.base;
|
||||
export import :eos.polytrope;
|
||||
export import :surface.base;
|
||||
export import :surface.isobaric;
|
||||
export import :model.stellar;
|
||||
export import :operators.prepared_mass_normalization;
|
||||
export import :operators.prepared_stellar_equilibrium;
|
||||
|
||||
114
libmeanfield/interface/models/stellar_model.cppm
Normal file
114
libmeanfield/interface/models/stellar_model.cppm
Normal file
@@ -0,0 +1,114 @@
|
||||
module;
|
||||
|
||||
#include <concepts>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
export module mean_field:model.stellar;
|
||||
|
||||
export import :eos.base;
|
||||
export import :model.structure.base;
|
||||
export import :surface.base;
|
||||
|
||||
export namespace mean_field::models {
|
||||
template <typename Candidate>
|
||||
concept StructurePrescription =
|
||||
std::derived_from<std::remove_cvref_t<Candidate>, mean_field::models::structure::StructureBase>;
|
||||
|
||||
template <typename Candidate>
|
||||
concept SurfacePrescription = std::derived_from<std::remove_cvref_t<Candidate>, mean_field::surface::SurfaceBase>;
|
||||
|
||||
/*
|
||||
* Public ownership facade for a physical structure prescription and its
|
||||
* stellar-surface prescription.
|
||||
*
|
||||
* The concrete prescriptions are allocated once at construction. Their
|
||||
* stable addresses allow future prepared operators and contexts to borrow
|
||||
* references without making ownership part of the user-facing API.
|
||||
*/
|
||||
class StellarModel final {
|
||||
public:
|
||||
template <
|
||||
StructurePrescription StructureType,
|
||||
SurfacePrescription SurfaceType>
|
||||
explicit StellarModel(
|
||||
StructureType &&structurePrescription,
|
||||
SurfaceType &&surfacePrescription
|
||||
)
|
||||
: StellarModel(
|
||||
std::make_unique<std::remove_cvref_t<StructureType>>(
|
||||
std::forward<StructureType>(structurePrescription)
|
||||
),
|
||||
std::make_unique<std::remove_cvref_t<SurfaceType>>(std::forward<SurfaceType>(surfacePrescription))
|
||||
) {
|
||||
}
|
||||
|
||||
~StellarModel() = default;
|
||||
|
||||
StellarModel(const StellarModel &) = delete;
|
||||
|
||||
StellarModel &operator=(const StellarModel &) = delete;
|
||||
|
||||
StellarModel(StellarModel &&) noexcept = default;
|
||||
|
||||
StellarModel &operator=(StellarModel &&) noexcept = default;
|
||||
|
||||
[[nodiscard]] const mean_field::models::structure::StructureBase &structurePrescription() const noexcept {
|
||||
return *m_structurePrescription;
|
||||
}
|
||||
|
||||
[[nodiscard]] const mean_field::surface::SurfaceBase &surfacePrescription() const noexcept {
|
||||
return *m_surfacePrescription;
|
||||
}
|
||||
|
||||
[[nodiscard]] const mean_field::eos::EquationOfState &equationOfState() const noexcept {
|
||||
return m_structurePrescription->equationOfState();
|
||||
}
|
||||
|
||||
[[nodiscard]] double targetMass() const noexcept {
|
||||
return m_structurePrescription->targetMass();
|
||||
}
|
||||
|
||||
[[nodiscard]] mean_field::models::structure::StructureSeed
|
||||
makeInitialSeed(const mean_field::models::structure::StructureSeedRequest &request) const {
|
||||
return m_structurePrescription->makeInitialSeed(request);
|
||||
}
|
||||
|
||||
[[nodiscard]] const mean_field::surface::ResolvedSurfaceCondition &resolvedSurfaceCondition() const noexcept {
|
||||
return m_resolvedSurfaceCondition;
|
||||
}
|
||||
|
||||
private:
|
||||
explicit StellarModel(
|
||||
std::unique_ptr<mean_field::models::structure::StructureBase> structurePrescription,
|
||||
std::unique_ptr<mean_field::surface::SurfaceBase> surfacePrescription
|
||||
)
|
||||
: m_structurePrescription(std::move(structurePrescription)),
|
||||
m_surfacePrescription(std::move(surfacePrescription)),
|
||||
m_resolvedSurfaceCondition(validateAndResolve(
|
||||
*m_structurePrescription,
|
||||
*m_surfacePrescription
|
||||
)) {
|
||||
}
|
||||
|
||||
[[nodiscard]] static mean_field::surface::ResolvedSurfaceCondition validateAndResolve(
|
||||
const mean_field::models::structure::StructureBase &structurePrescription,
|
||||
const mean_field::surface::SurfaceBase &surfacePrescription
|
||||
) {
|
||||
structurePrescription.validate();
|
||||
|
||||
const mean_field::eos::EquationOfState &equationOfState = structurePrescription.equationOfState();
|
||||
|
||||
surfacePrescription.validate(equationOfState);
|
||||
|
||||
return surfacePrescription.resolve(equationOfState);
|
||||
}
|
||||
|
||||
std::unique_ptr<mean_field::models::structure::StructureBase> m_structurePrescription;
|
||||
|
||||
std::unique_ptr<mean_field::surface::SurfaceBase> m_surfacePrescription;
|
||||
|
||||
mean_field::surface::ResolvedSurfaceCondition m_resolvedSurfaceCondition;
|
||||
};
|
||||
} // namespace mean_field::models
|
||||
68
libmeanfield/interface/models/structure/polytropic.cppm
Normal file
68
libmeanfield/interface/models/structure/polytropic.cppm
Normal file
@@ -0,0 +1,68 @@
|
||||
module;
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <mfem.hpp>
|
||||
|
||||
export module mean_field:model.structure.polytropic;
|
||||
|
||||
export import :eos.polytrope;
|
||||
export import :model.structure.base;
|
||||
|
||||
import :utils.misc;
|
||||
|
||||
export namespace mean_field::models::structure {
|
||||
class PolytropicStructure final : public StructureBase {
|
||||
public:
|
||||
explicit PolytropicStructure(
|
||||
eos::Polytrope equationOfState,
|
||||
double targetMass
|
||||
);
|
||||
|
||||
[[nodiscard]] const eos::EquationOfState &equationOfState() const noexcept override;
|
||||
|
||||
[[nodiscard]] double targetMass() const noexcept override;
|
||||
|
||||
[[nodiscard]] StructureSeed makeInitialSeed(const StructureSeedRequest &request) const override;
|
||||
|
||||
void validate() const override;
|
||||
|
||||
private:
|
||||
struct LaneEmdenPoint {
|
||||
double coordinate{0.0};
|
||||
double value{0.0};
|
||||
double derivative{0.0};
|
||||
};
|
||||
|
||||
struct LaneEmdenDerivative {
|
||||
double value{0.0};
|
||||
double derivative{0.0};
|
||||
};
|
||||
|
||||
static void validateSeedRequest(const StructureSeedRequest &request);
|
||||
|
||||
[[nodiscard]] static LaneEmdenDerivative evaluateLaneEmdenRhs(
|
||||
double coordinate,
|
||||
double value,
|
||||
double derivative,
|
||||
double polytropicIndex
|
||||
);
|
||||
|
||||
[[nodiscard]] static LaneEmdenPoint takeLaneEmdenStep(
|
||||
const LaneEmdenPoint &point,
|
||||
double step,
|
||||
double polytropicIndex
|
||||
);
|
||||
|
||||
[[nodiscard]] static std::vector<LaneEmdenPoint> solveLaneEmden(double polytropicIndex);
|
||||
|
||||
[[nodiscard]] static double interpolateLaneEmdenValue(
|
||||
const std::vector<LaneEmdenPoint> &solution,
|
||||
double coordinate,
|
||||
std::size_t &lowerIndex
|
||||
);
|
||||
|
||||
eos::Polytrope m_equationOfState;
|
||||
double m_targetMass;
|
||||
};
|
||||
} // namespace mean_field::models::structure
|
||||
37
libmeanfield/interface/models/structure/structure_base.cppm
Normal file
37
libmeanfield/interface/models/structure/structure_base.cppm
Normal file
@@ -0,0 +1,37 @@
|
||||
module;
|
||||
#include <mfem.hpp>
|
||||
export module mean_field:model.structure.base;
|
||||
export import :eos.base;
|
||||
|
||||
export namespace mean_field::models::structure {
|
||||
struct StructureSeed {
|
||||
mfem::Vector radius;
|
||||
mfem::Vector density;
|
||||
mfem::Vector enthalpy;
|
||||
|
||||
double stellarRadius;
|
||||
double centralDensity;
|
||||
double centralEnthalpy;
|
||||
};
|
||||
|
||||
struct StructureSeedRequest {
|
||||
double centralDensity;
|
||||
int radialSampleCount{512};
|
||||
};
|
||||
|
||||
class StructureBase {
|
||||
public:
|
||||
virtual ~StructureBase() = default;
|
||||
|
||||
[[nodiscard]] virtual const eos::EquationOfState &equationOfState() const noexcept = 0;
|
||||
|
||||
[[nodiscard]] virtual double targetMass() const noexcept = 0;
|
||||
|
||||
[[nodiscard]] virtual StructureSeed makeInitialSeed(const StructureSeedRequest &request) const = 0;
|
||||
|
||||
virtual void validate() const = 0;
|
||||
|
||||
protected:
|
||||
StructureBase() = default;
|
||||
};
|
||||
} // namespace mean_field::models::structure
|
||||
146
libmeanfield/interface/models/structure_profile.cppm
Normal file
146
libmeanfield/interface/models/structure_profile.cppm
Normal file
@@ -0,0 +1,146 @@
|
||||
module;
|
||||
#include <mfem.hpp>
|
||||
|
||||
export module mean_field:model.structure_profile;
|
||||
export import :fem;
|
||||
|
||||
export namespace mean_field::models {
|
||||
constexpr double DEFAULT_COLATITUDE_DEGREES = 90;
|
||||
constexpr double DEFAULT_LONGITUDE_DEGREES = 0;
|
||||
|
||||
struct RadialDirection {
|
||||
double coLatitudeDegrees{DEFAULT_COLATITUDE_DEGREES};
|
||||
double longitudeDegrees{DEFAULT_LONGITUDE_DEGREES};
|
||||
|
||||
[[nodiscard]] mfem::Vector toUnitCartesian() const;
|
||||
};
|
||||
|
||||
struct RadialProfile {
|
||||
double coLatitudeDegrees;
|
||||
double longitudeDegrees;
|
||||
|
||||
mfem::Vector radius;
|
||||
mfem::Vector param;
|
||||
};
|
||||
|
||||
struct SliceProfile {
|
||||
double radius;
|
||||
std::array<double, 2> normal;
|
||||
|
||||
mfem::GridFunction param;
|
||||
};
|
||||
|
||||
class StructureProfile {
|
||||
public:
|
||||
explicit StructureProfile(
|
||||
const fem::FEM &fem,
|
||||
mfem::Vector state
|
||||
);
|
||||
|
||||
// Profiles
|
||||
mfem::GridFunction pressureProfile();
|
||||
mfem::GridFunction densityProfile();
|
||||
mfem::GridFunction gravitationalPotentialProfile();
|
||||
mfem::GridFunction gravitationalFieldProfile();
|
||||
mfem::GridFunction enthalpyProfile();
|
||||
mfem::GridFunction entropyProfile();
|
||||
mfem::GridFunction temperatureProfile();
|
||||
mfem::GridFunction internalEnergyProfile();
|
||||
|
||||
// Local Evaluation
|
||||
double pressureAt(const mfem::Vector &position);
|
||||
double densityAt(const mfem::Vector &position);
|
||||
double gravitationalPotentialAt(const mfem::Vector &position);
|
||||
mfem::Vector gravitationalFieldAt(const mfem::Vector &position);
|
||||
double enthalpyAt(const mfem::Vector &position);
|
||||
double entropyAt(const mfem::Vector &position);
|
||||
double temperatureAt(const mfem::Vector &position);
|
||||
double internalEnergyAt(const mfem::Vector &position);
|
||||
|
||||
// Radial helpers
|
||||
mfem::Vector radius(RadialDirection direction = {});
|
||||
|
||||
RadialProfile radialPressureProfile(RadialDirection direction = {});
|
||||
RadialProfile radialDensityProfile(RadialDirection direction = {});
|
||||
RadialProfile radialGravitationalPotentialProfile(RadialDirection direction = {});
|
||||
RadialProfile radialGravitationalFieldProfile(RadialDirection direction = {});
|
||||
RadialProfile radialEnthalpyProfile(RadialDirection direction = {});
|
||||
RadialProfile radialEntropyProfile(RadialDirection direction = {});
|
||||
RadialProfile radialTemperatureProfile(RadialDirection direction = {});
|
||||
RadialProfile radialInternalEnergyProfile(RadialDirection direction = {});
|
||||
|
||||
// Ellipsoidal Slices
|
||||
SliceProfile slicePressureProfile(
|
||||
double radius,
|
||||
const std::array<
|
||||
double,
|
||||
2> &normal
|
||||
);
|
||||
SliceProfile sliceDensityProfile(
|
||||
double radius,
|
||||
const std::array<
|
||||
double,
|
||||
2> &normal
|
||||
);
|
||||
SliceProfile sliceGravitationalPotentialProfile(
|
||||
double radius,
|
||||
const std::array<
|
||||
double,
|
||||
2> &normal
|
||||
);
|
||||
SliceProfile sliceGravitationalFieldProfile(
|
||||
double radius,
|
||||
const std::array<
|
||||
double,
|
||||
2> &normal
|
||||
);
|
||||
SliceProfile sliceEnthalpyProfile(
|
||||
double radius,
|
||||
const std::array<
|
||||
double,
|
||||
2> &normal
|
||||
);
|
||||
SliceProfile sliceEntropyProfile(
|
||||
double radius,
|
||||
const std::array<
|
||||
double,
|
||||
2> &normal
|
||||
);
|
||||
SliceProfile sliceTemperatureProfile(
|
||||
double radius,
|
||||
const std::array<
|
||||
double,
|
||||
2> &normal
|
||||
);
|
||||
SliceProfile sliceInternalEnergyProfile(
|
||||
double radius,
|
||||
const std::array<
|
||||
double,
|
||||
2> &normal
|
||||
);
|
||||
|
||||
// Integral Constraints
|
||||
double totalMass();
|
||||
double virialRatio();
|
||||
|
||||
// Diagnostics
|
||||
bool isBound();
|
||||
|
||||
// IO
|
||||
void radialToCSV(
|
||||
const std::string &filename,
|
||||
RadialDirection direction = {}
|
||||
);
|
||||
void radialToBIN(
|
||||
const std::string &filename,
|
||||
RadialDirection direction = {}
|
||||
);
|
||||
void toBIN(const std::string &filename);
|
||||
|
||||
private:
|
||||
const fem::FEM &m_fem;
|
||||
const mfem::Vector m_state;
|
||||
};
|
||||
|
||||
StructureProfile StructureProfileFromBIN(const std::string &filename);
|
||||
} // namespace mean_field::models
|
||||
@@ -1,5 +1,6 @@
|
||||
module;
|
||||
|
||||
#include <compare>
|
||||
#include <cstdint>
|
||||
|
||||
#include <mfem.hpp>
|
||||
@@ -7,18 +8,66 @@ module;
|
||||
export module mean_field:operators.context.barotropic_closure_linearization;
|
||||
|
||||
export import :fem;
|
||||
export import :field.mfem;
|
||||
export import :mapping.domain_mapper;
|
||||
export import :operators.prepared_barotropic_closure;
|
||||
export import :physics.barotrope;
|
||||
|
||||
export namespace mean_field::operators::context::barotropic {
|
||||
struct BarotropicClosureRevisions final {
|
||||
std::uint64_t density = 0;
|
||||
std::uint64_t enthalpy = 0;
|
||||
std::uint64_t displacement = 0;
|
||||
template <typename Tag> struct DependencyStamp {
|
||||
std::uint64_t identity{0};
|
||||
std::uint64_t revision{0};
|
||||
|
||||
[[nodiscard]] bool
|
||||
operator==(const BarotropicClosureRevisions &) const noexcept = default;
|
||||
[[nodiscard]] constexpr bool CanFollow(const DependencyStamp &prepared) const noexcept {
|
||||
return identity != prepared.identity || revision >= prepared.revision;
|
||||
}
|
||||
|
||||
constexpr auto operator<=>(const DependencyStamp &) const = default;
|
||||
};
|
||||
|
||||
struct DiscretizationDependencyTag { };
|
||||
struct DensityDependencyTag { };
|
||||
struct EnthalpyDependencyTag { };
|
||||
struct DisplacementDependencyTag { };
|
||||
|
||||
using DiscretizationDependency = DependencyStamp<DiscretizationDependencyTag>;
|
||||
using DensityDependency = DependencyStamp<DensityDependencyTag>;
|
||||
using EnthalpyDependency = DependencyStamp<EnthalpyDependencyTag>;
|
||||
using DisplacementDependency = DependencyStamp<DisplacementDependencyTag>;
|
||||
|
||||
struct BarotropicClosureDependencies final {
|
||||
DiscretizationDependency discretization;
|
||||
DensityDependency density;
|
||||
EnthalpyDependency enthalpy;
|
||||
DisplacementDependency displacement;
|
||||
|
||||
constexpr auto operator<=>(const BarotropicClosureDependencies &) const = default;
|
||||
};
|
||||
|
||||
struct BarotropicClosureStateView final {
|
||||
const mfem::Vector &density;
|
||||
const mfem::Vector &enthalpy;
|
||||
const mfem::Vector &displacement;
|
||||
};
|
||||
|
||||
struct BarotropicClosurePreparationReport final {
|
||||
bool preparedStaticDependencies{false};
|
||||
bool preparedGeometryState{false};
|
||||
bool preparedBaseState{false};
|
||||
|
||||
bool updatedDensity{false};
|
||||
bool updatedEnthalpy{false};
|
||||
bool updatedDisplacement{false};
|
||||
|
||||
[[nodiscard]] bool DidAnyWork() const noexcept {
|
||||
return preparedStaticDependencies || preparedGeometryState || preparedBaseState;
|
||||
}
|
||||
};
|
||||
|
||||
struct BarotropicClosurePreparationStatistics final {
|
||||
std::uint64_t staticPreparations{0};
|
||||
std::uint64_t geometryPreparations{0};
|
||||
std::uint64_t baseStatePreparations{0};
|
||||
|
||||
constexpr auto operator<=>(const BarotropicClosurePreparationStatistics &) const = default;
|
||||
};
|
||||
|
||||
class BarotropicClosureLinearizationContext final {
|
||||
@@ -26,51 +75,46 @@ export namespace mean_field::operators::context::barotropic {
|
||||
BarotropicClosureLinearizationContext(
|
||||
const fem::FEM &f,
|
||||
const mapping::DomainMapperStateless &domainMapper,
|
||||
const physics::PolytropicBarotrope &barotrope
|
||||
const field::FieldDofMap &densityMap,
|
||||
const field::FieldDofMap &enthalpyMap,
|
||||
const field::FieldDofMap &displacementMap
|
||||
);
|
||||
|
||||
void Prepare(
|
||||
const mfem::Vector &baseDensityTrue,
|
||||
const mfem::Vector &baseEnthalpyTrue,
|
||||
const mfem::Vector &displacementTrue,
|
||||
const BarotropicClosureRevisions &revisions
|
||||
BarotropicClosureLinearizationContext(const BarotropicClosureLinearizationContext &) = delete;
|
||||
BarotropicClosureLinearizationContext &operator=(const BarotropicClosureLinearizationContext &) = delete;
|
||||
BarotropicClosureLinearizationContext(BarotropicClosureLinearizationContext &&) = delete;
|
||||
BarotropicClosureLinearizationContext &operator=(BarotropicClosureLinearizationContext &&) = delete;
|
||||
|
||||
BarotropicClosurePreparationReport Prepare(
|
||||
const BarotropicClosureStateView &state,
|
||||
const BarotropicClosureDependencies &dependencies
|
||||
);
|
||||
|
||||
[[nodiscard]] bool IsPrepared() const noexcept;
|
||||
[[nodiscard]] bool MatchesDependencies(const BarotropicClosureDependencies &dependencies) const noexcept;
|
||||
[[nodiscard]] const BarotropicClosureDependencies &GetDependencies() const;
|
||||
[[nodiscard]] const BarotropicClosurePreparationStatistics &GetPreparationStatistics() const noexcept;
|
||||
|
||||
[[nodiscard]] bool MatchesRevisions(
|
||||
const BarotropicClosureRevisions &revisions
|
||||
) const noexcept;
|
||||
|
||||
[[nodiscard]] std::uint64_t GetPreparationCount() const noexcept;
|
||||
|
||||
[[nodiscard]] const BarotropicClosureRevisions &GetRevisions() const;
|
||||
|
||||
[[nodiscard]] const mfem::Vector &GetBaseDensityTrue() const;
|
||||
|
||||
[[nodiscard]] const mfem::Vector &GetBaseEnthalpyTrue() const;
|
||||
|
||||
[[nodiscard]] const mfem::Vector &GetDisplacementTrue() const;
|
||||
|
||||
[[nodiscard]]
|
||||
const PreparedBarotropicClosureOperator &GetOperator() const noexcept;
|
||||
|
||||
void BuildResidual(mfem::Vector &residual) const;
|
||||
[[nodiscard]] const mfem::Vector &GetBaseDensity() const;
|
||||
[[nodiscard]] const mfem::Vector &GetBaseEnthalpy() const;
|
||||
[[nodiscard]] const mfem::Vector &GetDisplacement() const;
|
||||
|
||||
private:
|
||||
void VerifyPrepared() const;
|
||||
|
||||
const fem::FEM &m_f;
|
||||
const mapping::DomainMapperStateless &m_domainMapper;
|
||||
|
||||
PreparedBarotropicClosureOperator m_operator;
|
||||
int m_densitySize{0};
|
||||
int m_enthalpySize{0};
|
||||
int m_displacementSize{0};
|
||||
|
||||
mfem::Vector m_baseDensityTrue;
|
||||
mfem::Vector m_baseEnthalpyTrue;
|
||||
mfem::Vector m_displacementTrue;
|
||||
mfem::Vector m_baseDensity;
|
||||
mfem::Vector m_baseEnthalpy;
|
||||
mfem::Vector m_displacement;
|
||||
|
||||
BarotropicClosureRevisions m_revisions;
|
||||
|
||||
std::uint64_t m_preparationCount = 0;
|
||||
bool m_isPrepared = false;
|
||||
BarotropicClosureDependencies m_dependencies;
|
||||
BarotropicClosurePreparationStatistics m_statistics;
|
||||
bool m_isPrepared{false};
|
||||
};
|
||||
} // namespace mean_field::operators::context::barotropic
|
||||
@@ -51,8 +51,8 @@ export namespace mean_field::operators::context::gravity_field {
|
||||
bool refreshed_variation_state{false};
|
||||
|
||||
[[nodiscard]] bool DidAnyWork() const noexcept {
|
||||
return reconstructed_operators || rebuilt_mass_operator ||
|
||||
rebuilt_source_operator || refreshed_variation_state;
|
||||
return reconstructed_operators || rebuilt_mass_operator || rebuilt_source_operator ||
|
||||
refreshed_variation_state;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -63,13 +63,10 @@ export namespace mean_field::operators::context::gravity_field {
|
||||
const mapping::DomainMapperStateless &domain_mapper
|
||||
);
|
||||
|
||||
GravityFieldGeometryContext(const GravityFieldGeometryContext &) =
|
||||
delete;
|
||||
GravityFieldGeometryContext &
|
||||
operator=(const GravityFieldGeometryContext &) = delete;
|
||||
GravityFieldGeometryContext(GravityFieldGeometryContext &&) = delete;
|
||||
GravityFieldGeometryContext &
|
||||
operator=(GravityFieldGeometryContext &&) = delete;
|
||||
GravityFieldGeometryContext(const GravityFieldGeometryContext &) = delete;
|
||||
GravityFieldGeometryContext &operator=(const GravityFieldGeometryContext &) = delete;
|
||||
GravityFieldGeometryContext(GravityFieldGeometryContext &&) = delete;
|
||||
GravityFieldGeometryContext &operator=(GravityFieldGeometryContext &&) = delete;
|
||||
|
||||
GravityFieldGeometryPreparation Prepare(
|
||||
const mfem::Vector &displacement_true,
|
||||
@@ -77,15 +74,11 @@ export namespace mean_field::operators::context::gravity_field {
|
||||
DisplacementRevision displacement_revision
|
||||
);
|
||||
|
||||
[[nodiscard]] const PreparedMappedHDivMassOperator &
|
||||
GetMassOperator() const;
|
||||
[[nodiscard]] const PreparedMappedGravitySourceOperator &
|
||||
GetSourceOperator() const;
|
||||
[[nodiscard]] const PreparedMappedHDivMassOperator &GetMassOperator() const;
|
||||
[[nodiscard]] const PreparedMappedGravitySourceOperator &GetSourceOperator() const;
|
||||
[[nodiscard]] const mfem::Vector &GetDisplacement() const;
|
||||
[[nodiscard]] DiscretizationRevision
|
||||
GetDiscretizationRevision() const noexcept;
|
||||
[[nodiscard]] DisplacementRevision
|
||||
GetDisplacementRevision() const noexcept;
|
||||
[[nodiscard]] DiscretizationRevision GetDiscretizationRevision() const noexcept;
|
||||
[[nodiscard]] DisplacementRevision GetDisplacementRevision() const noexcept;
|
||||
[[nodiscard]] bool IsPrepared() const noexcept;
|
||||
|
||||
private:
|
||||
@@ -109,8 +102,7 @@ export namespace mean_field::operators::context::gravity_field {
|
||||
bool updated_gravity_gradient{false};
|
||||
|
||||
[[nodiscard]] bool DidAnyWork() const noexcept {
|
||||
return geometry.DidAnyWork() || updated_density ||
|
||||
updated_gravity_gradient;
|
||||
return geometry.DidAnyWork() || updated_density || updated_gravity_gradient;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -121,23 +113,17 @@ export namespace mean_field::operators::context::gravity_field {
|
||||
const mapping::DomainMapperStateless &domain_mapper
|
||||
);
|
||||
|
||||
GravityFieldLinearizationContext(
|
||||
const GravityFieldLinearizationContext &
|
||||
) = delete;
|
||||
GravityFieldLinearizationContext &
|
||||
operator=(const GravityFieldLinearizationContext &) = delete;
|
||||
GravityFieldLinearizationContext(GravityFieldLinearizationContext &&) =
|
||||
delete;
|
||||
GravityFieldLinearizationContext &
|
||||
operator=(GravityFieldLinearizationContext &&) = delete;
|
||||
GravityFieldLinearizationContext(const GravityFieldLinearizationContext &) = delete;
|
||||
GravityFieldLinearizationContext &operator=(const GravityFieldLinearizationContext &) = delete;
|
||||
GravityFieldLinearizationContext(GravityFieldLinearizationContext &&) = delete;
|
||||
GravityFieldLinearizationContext &operator=(GravityFieldLinearizationContext &&) = delete;
|
||||
|
||||
GravityFieldPreparationReport Prepare(
|
||||
const GravityFieldStateView &state,
|
||||
const GravityFieldRevisions &revisions
|
||||
);
|
||||
|
||||
[[nodiscard]] const GravityFieldGeometryContext &
|
||||
GetGeometryContext() const;
|
||||
[[nodiscard]] const GravityFieldGeometryContext &GetGeometryContext() const;
|
||||
[[nodiscard]] const mfem::Vector &GetDensity() const;
|
||||
[[nodiscard]] const mfem::Vector &GetGravityGradient() const;
|
||||
[[nodiscard]] const GravityFieldRevisions &GetRevisions() const;
|
||||
|
||||
@@ -15,10 +15,8 @@ export namespace mean_field::operators::context::hydrostatic {
|
||||
std::uint64_t identity{0};
|
||||
std::uint64_t revision{0};
|
||||
|
||||
[[nodiscard]] constexpr bool
|
||||
CanFollow(const DependencyStamp &prepared) const noexcept {
|
||||
return identity != prepared.identity ||
|
||||
revision >= prepared.revision;
|
||||
[[nodiscard]] constexpr bool CanFollow(const DependencyStamp &prepared) const noexcept {
|
||||
return identity != prepared.identity || revision >= prepared.revision;
|
||||
}
|
||||
|
||||
constexpr auto operator<=>(const DependencyStamp &) const = default;
|
||||
@@ -31,20 +29,17 @@ export namespace mean_field::operators::context::hydrostatic {
|
||||
struct RotationDependencyTag { };
|
||||
struct BernoulliConstantDependencyTag { };
|
||||
|
||||
using DiscretizationDependency =
|
||||
DependencyStamp<DiscretizationDependencyTag>;
|
||||
using DiscretizationDependency = DependencyStamp<DiscretizationDependencyTag>;
|
||||
|
||||
using EnthalpyDependency = DependencyStamp<EnthalpyDependencyTag>;
|
||||
using EnthalpyDependency = DependencyStamp<EnthalpyDependencyTag>;
|
||||
|
||||
using GravityPotentialDependency =
|
||||
DependencyStamp<GravityPotentialDependencyTag>;
|
||||
using GravityPotentialDependency = DependencyStamp<GravityPotentialDependencyTag>;
|
||||
|
||||
using DisplacementDependency = DependencyStamp<DisplacementDependencyTag>;
|
||||
using DisplacementDependency = DependencyStamp<DisplacementDependencyTag>;
|
||||
|
||||
using RotationDependency = DependencyStamp<RotationDependencyTag>;
|
||||
using RotationDependency = DependencyStamp<RotationDependencyTag>;
|
||||
|
||||
using BernoulliConstantDependency =
|
||||
DependencyStamp<BernoulliConstantDependencyTag>;
|
||||
using BernoulliConstantDependency = DependencyStamp<BernoulliConstantDependencyTag>;
|
||||
|
||||
struct HydrostaticEquilibriumDependencies {
|
||||
DiscretizationDependency discretization;
|
||||
@@ -54,8 +49,7 @@ export namespace mean_field::operators::context::hydrostatic {
|
||||
RotationDependency rotation;
|
||||
BernoulliConstantDependency bernoulliConstant;
|
||||
|
||||
constexpr auto
|
||||
operator<=>(const HydrostaticEquilibriumDependencies &) const = default;
|
||||
constexpr auto operator<=>(const HydrostaticEquilibriumDependencies &) const = default;
|
||||
};
|
||||
|
||||
struct HydrostaticEquilibriumStateView {
|
||||
@@ -77,8 +71,8 @@ export namespace mean_field::operators::context::hydrostatic {
|
||||
bool updatedBernoulliConstant{false};
|
||||
|
||||
[[nodiscard]] bool DidAnyWork() const noexcept {
|
||||
return preparedStaticDependencies || preparedGeometryState ||
|
||||
preparedRotationDependencies || preparedBaseState;
|
||||
return preparedStaticDependencies || preparedGeometryState || preparedRotationDependencies ||
|
||||
preparedBaseState;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -88,8 +82,7 @@ export namespace mean_field::operators::context::hydrostatic {
|
||||
std::uint64_t rotationPreparations{0};
|
||||
std::uint64_t baseStatePreparations{0};
|
||||
|
||||
constexpr auto
|
||||
operator<=>(const HydrostaticPreparationStatistics &) const = default;
|
||||
constexpr auto operator<=>(const HydrostaticPreparationStatistics &) const = default;
|
||||
};
|
||||
|
||||
class HydrostaticEquilibriumContext {
|
||||
@@ -99,17 +92,13 @@ export namespace mean_field::operators::context::hydrostatic {
|
||||
const mapping::DomainMapperStateless &domainMapper
|
||||
);
|
||||
|
||||
HydrostaticEquilibriumContext(const HydrostaticEquilibriumContext &) =
|
||||
delete;
|
||||
HydrostaticEquilibriumContext(const HydrostaticEquilibriumContext &) = delete;
|
||||
|
||||
HydrostaticEquilibriumContext &
|
||||
operator=(const HydrostaticEquilibriumContext &) = delete;
|
||||
HydrostaticEquilibriumContext &operator=(const HydrostaticEquilibriumContext &) = delete;
|
||||
|
||||
HydrostaticEquilibriumContext(HydrostaticEquilibriumContext &&) =
|
||||
delete;
|
||||
HydrostaticEquilibriumContext(HydrostaticEquilibriumContext &&) = delete;
|
||||
|
||||
HydrostaticEquilibriumContext &
|
||||
operator=(HydrostaticEquilibriumContext &&) = delete;
|
||||
HydrostaticEquilibriumContext &operator=(HydrostaticEquilibriumContext &&) = delete;
|
||||
|
||||
HydrostaticPreparationReport Prepare(
|
||||
const HydrostaticEquilibriumStateView &state,
|
||||
@@ -118,15 +107,11 @@ export namespace mean_field::operators::context::hydrostatic {
|
||||
|
||||
[[nodiscard]] bool IsPrepared() const noexcept;
|
||||
|
||||
[[nodiscard]] bool MatchesDependencies(
|
||||
const HydrostaticEquilibriumDependencies &dependencies
|
||||
) const noexcept;
|
||||
[[nodiscard]] bool MatchesDependencies(const HydrostaticEquilibriumDependencies &dependencies) const noexcept;
|
||||
|
||||
[[nodiscard]] const HydrostaticEquilibriumDependencies &
|
||||
GetDependencies() const;
|
||||
[[nodiscard]] const HydrostaticEquilibriumDependencies &GetDependencies() const;
|
||||
|
||||
[[nodiscard]] const HydrostaticPreparationStatistics &
|
||||
GetPreparationStatistics() const noexcept;
|
||||
[[nodiscard]] const HydrostaticPreparationStatistics &GetPreparationStatistics() const noexcept;
|
||||
|
||||
[[nodiscard]] const mfem::Vector &GetBaseEnthalpyTrue() const;
|
||||
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
module;
|
||||
|
||||
#include <compare>
|
||||
#include <cstdint>
|
||||
|
||||
#include <mfem.hpp>
|
||||
|
||||
export module mean_field:operators.context.pressure_force;
|
||||
|
||||
export import :fem;
|
||||
export import :field.mfem;
|
||||
export import :mapping.domain_mapper;
|
||||
|
||||
export namespace mean_field::operators::context::pressure_force {
|
||||
template <typename Tag> struct DependencyStamp final {
|
||||
std::uint64_t identity{0};
|
||||
std::uint64_t revision{0};
|
||||
|
||||
[[nodiscard]] constexpr bool CanFollow(const DependencyStamp &prepared) const noexcept {
|
||||
return identity != prepared.identity || revision >= prepared.revision;
|
||||
}
|
||||
|
||||
constexpr auto operator<=>(const DependencyStamp &) const = default;
|
||||
};
|
||||
|
||||
struct DiscretizationDependencyTag final { };
|
||||
struct EnthalpyDependencyTag final { };
|
||||
struct DisplacementDependencyTag final { };
|
||||
|
||||
using DiscretizationDependency = DependencyStamp<DiscretizationDependencyTag>;
|
||||
|
||||
using EnthalpyDependency = DependencyStamp<EnthalpyDependencyTag>;
|
||||
|
||||
using DisplacementDependency = DependencyStamp<DisplacementDependencyTag>;
|
||||
|
||||
struct PressureForceDependencies final {
|
||||
DiscretizationDependency discretization;
|
||||
EnthalpyDependency enthalpy;
|
||||
DisplacementDependency displacement;
|
||||
|
||||
constexpr auto operator<=>(const PressureForceDependencies &) const = default;
|
||||
};
|
||||
|
||||
/*
|
||||
* Frozen solver-facing state.
|
||||
*
|
||||
* Both vectors use their registered FieldDof coordinates.
|
||||
*
|
||||
* Under the current registry:
|
||||
*
|
||||
* enthalpy -> Stellar -> reduced
|
||||
* displacement -> All -> identity/full
|
||||
*/
|
||||
struct PressureForceStateView final {
|
||||
const mfem::Vector &enthalpy;
|
||||
const mfem::Vector &displacement;
|
||||
};
|
||||
|
||||
struct PressureForcePreparationReport final {
|
||||
bool preparedStaticDependencies{false};
|
||||
bool preparedGeometryState{false};
|
||||
bool preparedMaterialState{false};
|
||||
|
||||
bool updatedEnthalpy{false};
|
||||
bool updatedDisplacement{false};
|
||||
|
||||
[[nodiscard]] bool DidAnyWork() const noexcept {
|
||||
return preparedStaticDependencies || preparedGeometryState || preparedMaterialState;
|
||||
}
|
||||
};
|
||||
|
||||
struct PressureForcePreparationStatistics final {
|
||||
std::uint64_t staticPreparations{0};
|
||||
std::uint64_t geometryPreparations{0};
|
||||
std::uint64_t materialPreparations{0};
|
||||
|
||||
constexpr auto operator<=>(const PressureForcePreparationStatistics &) const = default;
|
||||
};
|
||||
|
||||
class PressureForceLinearizationContext final {
|
||||
public:
|
||||
PressureForceLinearizationContext(
|
||||
const fem::FEM &f,
|
||||
const mapping::DomainMapperStateless &domainMapper,
|
||||
const field::FieldDofMap &enthalpyMap,
|
||||
const field::FieldDofMap &displacementMap
|
||||
);
|
||||
|
||||
PressureForceLinearizationContext(const PressureForceLinearizationContext &) = delete;
|
||||
|
||||
PressureForceLinearizationContext &operator=(const PressureForceLinearizationContext &) = delete;
|
||||
|
||||
PressureForceLinearizationContext(PressureForceLinearizationContext &&) = delete;
|
||||
|
||||
PressureForceLinearizationContext &operator=(PressureForceLinearizationContext &&) = delete;
|
||||
|
||||
PressureForcePreparationReport Prepare(
|
||||
const PressureForceStateView &state,
|
||||
const PressureForceDependencies &dependencies
|
||||
);
|
||||
|
||||
[[nodiscard]] bool IsPrepared() const noexcept;
|
||||
|
||||
[[nodiscard]] bool MatchesDependencies(const PressureForceDependencies &dependencies) const noexcept;
|
||||
|
||||
[[nodiscard]] const PressureForceDependencies &GetDependencies() const;
|
||||
|
||||
[[nodiscard]] const PressureForcePreparationStatistics &GetPreparationStatistics() const noexcept;
|
||||
|
||||
[[nodiscard]] const mfem::Vector &GetBaseEnthalpy() const;
|
||||
|
||||
[[nodiscard]] const mfem::Vector &GetDisplacement() const;
|
||||
|
||||
private:
|
||||
void VerifyPrepared() const;
|
||||
|
||||
int m_enthalpySize{0};
|
||||
int m_displacementSize{0};
|
||||
|
||||
mfem::Vector m_baseEnthalpy;
|
||||
mfem::Vector m_displacement;
|
||||
|
||||
PressureForcePreparationStatistics m_statistics;
|
||||
PressureForceDependencies m_dependencies;
|
||||
|
||||
bool m_isPrepared{false};
|
||||
};
|
||||
} // namespace mean_field::operators::context::pressure_force
|
||||
@@ -0,0 +1,124 @@
|
||||
module;
|
||||
|
||||
#include <compare>
|
||||
#include <cstdint>
|
||||
|
||||
#include <mfem.hpp>
|
||||
|
||||
export module mean_field:operators.context.rotational_displacement_force;
|
||||
|
||||
export import :fem;
|
||||
export import :mapping.domain_mapper;
|
||||
|
||||
export namespace mean_field::operators::context::rotational_displacement_force {
|
||||
template <typename Tag> struct DependencyStamp final {
|
||||
std::uint64_t identity{0};
|
||||
std::uint64_t revision{0};
|
||||
|
||||
[[nodiscard]] constexpr bool CanFollow(const DependencyStamp &prepared) const noexcept {
|
||||
return identity != prepared.identity || revision >= prepared.revision;
|
||||
}
|
||||
|
||||
constexpr auto operator<=>(const DependencyStamp &) const = default;
|
||||
};
|
||||
|
||||
struct DiscretizationDependencyTag final { };
|
||||
struct DensityDependencyTag final { };
|
||||
struct DisplacementDependencyTag final { };
|
||||
struct RotationDependencyTag final { };
|
||||
|
||||
using DiscretizationDependency = DependencyStamp<DiscretizationDependencyTag>;
|
||||
|
||||
using DensityDependency = DependencyStamp<DensityDependencyTag>;
|
||||
|
||||
using DisplacementDependency = DependencyStamp<DisplacementDependencyTag>;
|
||||
|
||||
using RotationDependency = DependencyStamp<RotationDependencyTag>;
|
||||
|
||||
struct RotationalDisplacementForceDependencies final {
|
||||
DiscretizationDependency discretization;
|
||||
DensityDependency density;
|
||||
DisplacementDependency displacement;
|
||||
RotationDependency rotation;
|
||||
|
||||
constexpr auto operator<=>(const RotationalDisplacementForceDependencies &) const = default;
|
||||
};
|
||||
|
||||
struct RotationalDisplacementForceStateView final {
|
||||
const mfem::Vector &density;
|
||||
const mfem::Vector &displacement;
|
||||
};
|
||||
|
||||
struct RotationalDisplacementForcePreparationReport final {
|
||||
bool preparedStaticDependencies{false};
|
||||
bool preparedGeometryState{false};
|
||||
bool preparedRotationDependencies{false};
|
||||
bool preparedBaseState{false};
|
||||
|
||||
bool updatedDensity{false};
|
||||
bool updatedDisplacement{false};
|
||||
|
||||
[[nodiscard]] bool DidAnyWork() const noexcept {
|
||||
return preparedStaticDependencies || preparedGeometryState || preparedRotationDependencies ||
|
||||
preparedBaseState;
|
||||
}
|
||||
};
|
||||
|
||||
struct RotationalDisplacementForcePreparationStatistics final {
|
||||
std::uint64_t staticPreparations{0};
|
||||
std::uint64_t geometryPreparations{0};
|
||||
std::uint64_t rotationPreparations{0};
|
||||
std::uint64_t baseStatePreparations{0};
|
||||
|
||||
constexpr auto operator<=>(const RotationalDisplacementForcePreparationStatistics &) const = default;
|
||||
};
|
||||
|
||||
class RotationalDisplacementForceLinearizationContext final {
|
||||
public:
|
||||
RotationalDisplacementForceLinearizationContext(
|
||||
const fem::FEM &f,
|
||||
const mapping::DomainMapperStateless &domainMapper
|
||||
);
|
||||
|
||||
RotationalDisplacementForceLinearizationContext(const RotationalDisplacementForceLinearizationContext &) =
|
||||
delete;
|
||||
|
||||
RotationalDisplacementForceLinearizationContext &
|
||||
operator=(const RotationalDisplacementForceLinearizationContext &) = delete;
|
||||
|
||||
RotationalDisplacementForceLinearizationContext(RotationalDisplacementForceLinearizationContext &&) = delete;
|
||||
|
||||
RotationalDisplacementForceLinearizationContext &
|
||||
operator=(RotationalDisplacementForceLinearizationContext &&) = delete;
|
||||
|
||||
RotationalDisplacementForcePreparationReport Prepare(
|
||||
const RotationalDisplacementForceStateView &state,
|
||||
const RotationalDisplacementForceDependencies &dependencies
|
||||
);
|
||||
|
||||
[[nodiscard]] bool IsPrepared() const noexcept;
|
||||
|
||||
[[nodiscard]] bool
|
||||
MatchesDependencies(const RotationalDisplacementForceDependencies &dependencies) const noexcept;
|
||||
|
||||
[[nodiscard]] const RotationalDisplacementForceDependencies &GetDependencies() const;
|
||||
|
||||
[[nodiscard]] const RotationalDisplacementForcePreparationStatistics &GetPreparationStatistics() const noexcept;
|
||||
|
||||
[[nodiscard]] const mfem::Vector &GetBaseDensityTrue() const;
|
||||
|
||||
[[nodiscard]] const mfem::Vector &GetDisplacementTrue() const;
|
||||
|
||||
private:
|
||||
void VerifyPrepared() const;
|
||||
|
||||
const fem::FEM &m_f;
|
||||
|
||||
mfem::Vector m_baseDensityTrue;
|
||||
mfem::Vector m_displacementTrue;
|
||||
|
||||
RotationalDisplacementForceDependencies m_dependencies;
|
||||
RotationalDisplacementForcePreparationStatistics m_statistics;
|
||||
bool m_isPrepared{false};
|
||||
};
|
||||
} // namespace mean_field::operators::context::rotational_displacement_force
|
||||
@@ -10,27 +10,20 @@ export import :operators.gravity_field_jacobian;
|
||||
export import :operators.context.gravity_field;
|
||||
|
||||
export namespace mean_field::operators {
|
||||
enum class GravityResidualBlock : std::uint8_t {
|
||||
gradient_equation = 0,
|
||||
poisson_equation = 1,
|
||||
count = 2
|
||||
};
|
||||
enum class GravityResidualBlock : std::uint8_t { gradient_equation = 0, poisson_equation = 1, count = 2 };
|
||||
|
||||
constexpr int
|
||||
gravity_residual_block_index(const GravityResidualBlock block) noexcept {
|
||||
constexpr int gravity_residual_block_index(const GravityResidualBlock block) noexcept {
|
||||
return static_cast<int>(block);
|
||||
}
|
||||
|
||||
inline constexpr int gravity_residual_block_count =
|
||||
gravity_residual_block_index(GravityResidualBlock::count);
|
||||
inline constexpr int gravity_residual_block_count = gravity_residual_block_index(GravityResidualBlock::count);
|
||||
|
||||
class GravityFieldOperator final : public mfem::Operator {
|
||||
public:
|
||||
GravityFieldOperator(
|
||||
fem::FEM &f,
|
||||
const mapping::DomainMapperStateless &domain_mapper,
|
||||
context::gravity_field::GravityFieldLinearizationContext
|
||||
&linearization_context,
|
||||
context::gravity_field::GravityFieldLinearizationContext &linearization_context,
|
||||
const mfem::Array<int> &state_true_offsets,
|
||||
GravityFieldJacobianOperator &jacobian
|
||||
);
|
||||
@@ -47,39 +40,32 @@ export namespace mean_field::operators {
|
||||
|
||||
Operator &GetGradient(const mfem::Vector &state) const override;
|
||||
|
||||
[[nodiscard]] const mfem::Array<int> &
|
||||
GetStateTrueOffsets() const noexcept;
|
||||
[[nodiscard]] const mfem::Array<int> &GetStateTrueOffsets() const noexcept;
|
||||
|
||||
[[nodiscard]] const mfem::Array<int> &
|
||||
GetResidualTrueOffsets() const noexcept;
|
||||
[[nodiscard]] const mfem::Array<int> &GetResidualTrueOffsets() const noexcept;
|
||||
|
||||
[[nodiscard]] context::gravity_field::GravityFieldLinearizationContext &
|
||||
GetLinearizationContext() noexcept;
|
||||
[[nodiscard]] context::gravity_field::GravityFieldLinearizationContext &GetLinearizationContext() noexcept;
|
||||
|
||||
[[nodiscard]] const context::gravity_field::
|
||||
GravityFieldLinearizationContext &
|
||||
GetLinearizationContext() const noexcept;
|
||||
[[nodiscard]] const context::gravity_field::GravityFieldLinearizationContext &
|
||||
GetLinearizationContext() const noexcept;
|
||||
|
||||
void ApplyGravityUnknowns(
|
||||
const mfem::Vector &gravity_gradient,
|
||||
const mfem::Vector &gravity_potential,
|
||||
const context::gravity_field::GravityFieldGeometryContext
|
||||
&geometry_context,
|
||||
const context::gravity_field::GravityFieldGeometryContext &geometry_context,
|
||||
mfem::Vector &action
|
||||
) const;
|
||||
|
||||
void ApplyDensitySource(
|
||||
const mfem::Vector &density,
|
||||
const context::gravity_field::GravityFieldGeometryContext
|
||||
&geometry_context,
|
||||
const context::gravity_field::GravityFieldGeometryContext &geometry_context,
|
||||
mfem::Vector &action
|
||||
) const;
|
||||
|
||||
private:
|
||||
fem::FEM &m_fem;
|
||||
const mapping::DomainMapperStateless &m_domain_mapper;
|
||||
context::gravity_field::GravityFieldLinearizationContext
|
||||
&m_linearization_context;
|
||||
context::gravity_field::GravityFieldLinearizationContext &m_linearization_context;
|
||||
mfem::Array<int> m_state_true_offsets;
|
||||
mfem::Array<int> m_residual_true_offsets;
|
||||
GravityFieldJacobianOperator &m_jacobian;
|
||||
@@ -89,18 +75,14 @@ export namespace mean_field::operators {
|
||||
public:
|
||||
ReducedGravityFieldOperator(
|
||||
GravityFieldOperator &gravity_field_operator,
|
||||
context::gravity_field::GravityFieldGeometryContext
|
||||
&gravity_field_geometry_context,
|
||||
context::gravity_field::GravityFieldGeometryContext &gravity_field_geometry_context,
|
||||
const mfem::Vector &displacement
|
||||
);
|
||||
|
||||
ReducedGravityFieldOperator(const ReducedGravityFieldOperator &) =
|
||||
delete;
|
||||
ReducedGravityFieldOperator &
|
||||
operator=(const ReducedGravityFieldOperator &) = delete;
|
||||
ReducedGravityFieldOperator(ReducedGravityFieldOperator &&) = delete;
|
||||
ReducedGravityFieldOperator &
|
||||
operator=(ReducedGravityFieldOperator &&) = delete;
|
||||
ReducedGravityFieldOperator(const ReducedGravityFieldOperator &) = delete;
|
||||
ReducedGravityFieldOperator &operator=(const ReducedGravityFieldOperator &) = delete;
|
||||
ReducedGravityFieldOperator(ReducedGravityFieldOperator &&) = delete;
|
||||
ReducedGravityFieldOperator &operator=(ReducedGravityFieldOperator &&) = delete;
|
||||
|
||||
void SetDisplacement(const mfem::Vector &displacement);
|
||||
|
||||
@@ -118,18 +100,13 @@ export namespace mean_field::operators {
|
||||
|
||||
[[nodiscard]] GravityFieldOperator &GetGravityFieldOperator() noexcept;
|
||||
|
||||
[[nodiscard]] const GravityFieldOperator &
|
||||
GetGravityFieldOperator() const noexcept;
|
||||
[[nodiscard]] const GravityFieldOperator &GetGravityFieldOperator() const noexcept;
|
||||
|
||||
[[nodiscard]] context::gravity_field::GravityFieldGeometryContext &
|
||||
GetGeometryContext() noexcept;
|
||||
[[nodiscard]] context::gravity_field::GravityFieldGeometryContext &GetGeometryContext() noexcept;
|
||||
|
||||
[[nodiscard]] const context::gravity_field::
|
||||
GravityFieldGeometryContext &
|
||||
GetGeometryContext() const noexcept;
|
||||
[[nodiscard]] const context::gravity_field::GravityFieldGeometryContext &GetGeometryContext() const noexcept;
|
||||
|
||||
[[nodiscard]] const mfem::Array<int> &
|
||||
GetGravityTrueOffsets() const noexcept;
|
||||
[[nodiscard]] const mfem::Array<int> &GetGravityTrueOffsets() const noexcept;
|
||||
|
||||
private:
|
||||
void ValidateDisplacement(const mfem::Vector &displacement) const;
|
||||
@@ -141,7 +118,6 @@ export namespace mean_field::operators {
|
||||
private:
|
||||
GravityFieldOperator &m_gravity_field_operator;
|
||||
mfem::Array<int> m_gravity_true_offsets;
|
||||
context::gravity_field::GravityFieldGeometryContext
|
||||
&m_gravity_field_geometry_context;
|
||||
context::gravity_field::GravityFieldGeometryContext &m_gravity_field_geometry_context;
|
||||
};
|
||||
} // namespace mean_field::operators
|
||||
@@ -12,8 +12,7 @@ export namespace mean_field::operators {
|
||||
GravityFieldJacobianOperator(
|
||||
fem::FEM &f,
|
||||
const mapping::DomainMapperStateless &domain_mapper,
|
||||
const context::gravity_field::GravityFieldLinearizationContext
|
||||
&linearization_context,
|
||||
const context::gravity_field::GravityFieldLinearizationContext &linearization_context,
|
||||
const mfem::Array<int> &state_true_offsets,
|
||||
const mfem::Array<int> &residual_true_offsets
|
||||
);
|
||||
@@ -23,15 +22,13 @@ export namespace mean_field::operators {
|
||||
mfem::Vector &action
|
||||
) const override;
|
||||
|
||||
[[nodiscard]] const context::gravity_field::
|
||||
GravityFieldLinearizationContext &
|
||||
GetLinearizationContext() const noexcept;
|
||||
[[nodiscard]] const context::gravity_field::GravityFieldLinearizationContext &
|
||||
GetLinearizationContext() const noexcept;
|
||||
|
||||
private:
|
||||
fem::FEM &m_fem;
|
||||
const mapping::DomainMapperStateless &m_domain_mapper;
|
||||
const context::gravity_field::GravityFieldLinearizationContext
|
||||
&m_linearization_context;
|
||||
const context::gravity_field::GravityFieldLinearizationContext &m_linearization_context;
|
||||
mfem::Array<int> m_state_true_offsets;
|
||||
mfem::Array<int> m_residual_true_offsets;
|
||||
};
|
||||
|
||||
@@ -4,15 +4,26 @@ module;
|
||||
|
||||
export module mean_field:operators.kernels.barotropic_closure;
|
||||
|
||||
export import :eos.polytrope;
|
||||
export import :fem;
|
||||
export import :mapping.domain_mapper;
|
||||
export import :physics.barotrope;
|
||||
|
||||
export namespace mean_field::operators::kernels {
|
||||
/*
|
||||
* Stateless full-MFEM reference kernels for
|
||||
*
|
||||
* R_rho = \int_{Omega_star} (rho - rho_EOS(h)) q_rho dV.
|
||||
*
|
||||
* These functions intentionally remain expressed in complete MFEM true
|
||||
* vectors. Solver/prepared-facing support reduction belongs to
|
||||
* PreparedBarotropicClosureOperator through FieldDofMap. Keeping this
|
||||
* layer full-space preserves an independent reference implementation for
|
||||
* R K P tests of the reduced prepared operator.
|
||||
*/
|
||||
void apply_barotropic_closure(
|
||||
const fem::FEM &f,
|
||||
const mapping::DomainMapperStateless &domainMapper,
|
||||
const physics::PolytropicBarotrope &barotrope,
|
||||
const eos::Polytrope &equationOfState,
|
||||
const mfem::Vector &densityTrue,
|
||||
const mfem::Vector &enthalpyTrue,
|
||||
const mfem::Vector &displacementTrue,
|
||||
@@ -22,7 +33,7 @@ export namespace mean_field::operators::kernels {
|
||||
void apply_barotropic_closure_density_action(
|
||||
const fem::FEM &f,
|
||||
const mapping::DomainMapperStateless &domainMapper,
|
||||
const physics::PolytropicBarotrope &barotrope,
|
||||
const eos::Polytrope &equationOfState,
|
||||
const mfem::Vector &densityVariationTrue,
|
||||
const mfem::Vector &displacementTrue,
|
||||
mfem::Vector &action
|
||||
@@ -31,7 +42,7 @@ export namespace mean_field::operators::kernels {
|
||||
void apply_barotropic_closure_enthalpy_action(
|
||||
const fem::FEM &f,
|
||||
const mapping::DomainMapperStateless &domainMapper,
|
||||
const physics::PolytropicBarotrope &barotrope,
|
||||
const eos::Polytrope &equationOfState,
|
||||
const mfem::Vector &baseEnthalpyTrue,
|
||||
const mfem::Vector &enthalpyVariationTrue,
|
||||
const mfem::Vector &displacementTrue,
|
||||
@@ -41,7 +52,7 @@ export namespace mean_field::operators::kernels {
|
||||
void apply_barotropic_closure_displacement_action(
|
||||
const fem::FEM &f,
|
||||
const mapping::DomainMapperStateless &domainMapper,
|
||||
const physics::PolytropicBarotrope &barotrope,
|
||||
const eos::Polytrope &equationOfState,
|
||||
const mfem::Vector &baseDensityTrue,
|
||||
const mfem::Vector &baseEnthalpyTrue,
|
||||
const mfem::Vector &displacementTrue,
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
module;
|
||||
|
||||
#include <mfem.hpp>
|
||||
|
||||
export module mean_field:operators.kernels.gravity_displacement_force;
|
||||
|
||||
export import :fem;
|
||||
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 mfem::Vector &densityTrue,
|
||||
const mfem::Vector &gravityGradientTrue,
|
||||
const mfem::Vector &displacementTrue,
|
||||
mfem::Vector &residualTrue
|
||||
);
|
||||
|
||||
void apply_gravity_displacement_force_density_action(
|
||||
const fem::FEM &f,
|
||||
const mapping::DomainMapperStateless &domainMapper,
|
||||
const mfem::Vector &densityVariationTrue,
|
||||
const mfem::Vector &baseGravityGradientTrue,
|
||||
const mfem::Vector &displacementTrue,
|
||||
mfem::Vector &actionTrue
|
||||
);
|
||||
|
||||
void apply_gravity_displacement_force_gradient_action(
|
||||
const fem::FEM &f,
|
||||
const mapping::DomainMapperStateless &domainMapper,
|
||||
const mfem::Vector &baseDensityTrue,
|
||||
const mfem::Vector &gravityGradientVariationTrue,
|
||||
const mfem::Vector &displacementTrue,
|
||||
mfem::Vector &actionTrue
|
||||
);
|
||||
|
||||
void apply_gravity_displacement_force_displacement_action(
|
||||
const fem::FEM &f,
|
||||
const mapping::DomainMapperStateless &domainMapper,
|
||||
const mfem::Vector &baseDensityTrue,
|
||||
const mfem::Vector &baseGravityGradientTrue,
|
||||
const mfem::Vector &displacementVariationTrue,
|
||||
const mfem::Vector &displacementTrue,
|
||||
mfem::Vector &actionTrue
|
||||
);
|
||||
|
||||
void apply_gravity_displacement_force_complete_action(
|
||||
const fem::FEM &f,
|
||||
const mapping::DomainMapperStateless &domainMapper,
|
||||
const mfem::Vector &baseDensityTrue,
|
||||
const mfem::Vector &densityVariationTrue,
|
||||
const mfem::Vector &baseGravityGradientTrue,
|
||||
const mfem::Vector &gravityGradientVariationTrue,
|
||||
const mfem::Vector &displacementVariationTrue,
|
||||
const mfem::Vector &displacementTrue,
|
||||
mfem::Vector &actionTrue
|
||||
);
|
||||
} // namespace mean_field::operators::kernels
|
||||
@@ -6,15 +6,35 @@ export module mean_field:operators.kernels.pressure_force;
|
||||
|
||||
export import :fem;
|
||||
export import :mapping.domain_mapper;
|
||||
export import :physics.barotrope;
|
||||
export import :eos.polytrope;
|
||||
|
||||
export namespace mean_field::operators::kernels {
|
||||
void apply_pressure_force_residual(
|
||||
const fem::FEM &f,
|
||||
const mapping::DomainMapperStateless &domainMapper,
|
||||
const physics::PolytropicBarotrope &barotrope,
|
||||
const eos::Polytrope &barotrope,
|
||||
const mfem::Vector &enthalpyTrue,
|
||||
const mfem::Vector &displacementTrue,
|
||||
mfem::Vector &residualTrue
|
||||
);
|
||||
|
||||
void apply_pressure_force_enthalpy_action(
|
||||
const fem::FEM &f,
|
||||
const mapping::DomainMapperStateless &domainMapper,
|
||||
const eos::Polytrope &barotrope,
|
||||
const mfem::Vector &baseEnthalpyTrue,
|
||||
const mfem::Vector &enthalpyVariationTrue,
|
||||
const mfem::Vector &displacementTrue,
|
||||
mfem::Vector &actionTrue
|
||||
);
|
||||
|
||||
void apply_pressure_force_displacement_action(
|
||||
const fem::FEM &f,
|
||||
const mapping::DomainMapperStateless &domainMapper,
|
||||
const eos::Polytrope &barotrope,
|
||||
const mfem::Vector &baseEnthalpyTrue,
|
||||
const mfem::Vector &displacementVariationTrue,
|
||||
const mfem::Vector &displacementTrue,
|
||||
mfem::Vector &actionTrue
|
||||
);
|
||||
} // namespace mean_field::operators::kernels
|
||||
@@ -0,0 +1,64 @@
|
||||
module;
|
||||
|
||||
#include <mfem.hpp>
|
||||
|
||||
export module mean_field:operators.kernels.rotational_displacement_force;
|
||||
|
||||
export import :fem;
|
||||
export import :mapping.domain_mapper;
|
||||
export import :physics.rigid_rotation;
|
||||
|
||||
export namespace mean_field::operators::kernels {
|
||||
/*
|
||||
* Rotational contribution to the displacement row:
|
||||
*
|
||||
* R_d^rotation(w)
|
||||
* = -int_{Omega_star} rho grad(Psi_rotation) . w dV
|
||||
* = int_{Omega_star}
|
||||
* rho [Omega x (Omega x (x - x_0))] . w dV.
|
||||
*
|
||||
* RigidRotation stores the positive potential
|
||||
*
|
||||
* Psi_rotation = 0.5 |Omega x (x - x_0)|^2.
|
||||
*
|
||||
* Vacuum elements are excluded exactly.
|
||||
*/
|
||||
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
|
||||
);
|
||||
|
||||
void apply_rotational_displacement_force_density_action(
|
||||
const fem::FEM &f,
|
||||
const mapping::DomainMapperStateless &domainMapper,
|
||||
const physics::RigidRotation &rotation,
|
||||
const mfem::Vector &densityVariationTrue,
|
||||
const mfem::Vector &displacementTrue,
|
||||
mfem::Vector &actionTrue
|
||||
);
|
||||
|
||||
void apply_rotational_displacement_force_displacement_action(
|
||||
const fem::FEM &f,
|
||||
const mapping::DomainMapperStateless &domainMapper,
|
||||
const physics::RigidRotation &rotation,
|
||||
const mfem::Vector &baseDensityTrue,
|
||||
const mfem::Vector &displacementVariationTrue,
|
||||
const mfem::Vector &displacementTrue,
|
||||
mfem::Vector &actionTrue
|
||||
);
|
||||
|
||||
void apply_rotational_displacement_force_complete_action(
|
||||
const fem::FEM &f,
|
||||
const mapping::DomainMapperStateless &domainMapper,
|
||||
const physics::RigidRotation &rotation,
|
||||
const mfem::Vector &baseDensityTrue,
|
||||
const mfem::Vector &densityVariationTrue,
|
||||
const mfem::Vector &displacementVariationTrue,
|
||||
const mfem::Vector &displacementTrue,
|
||||
mfem::Vector &actionTrue
|
||||
);
|
||||
} // namespace mean_field::operators::kernels
|
||||
@@ -6,29 +6,44 @@ module;
|
||||
|
||||
export module mean_field:operators.prepared_barotropic_closure;
|
||||
|
||||
export import :eos.polytrope;
|
||||
export import :fem;
|
||||
export import :field.mfem;
|
||||
export import :mapping.domain_mapper;
|
||||
export import :physics.barotrope;
|
||||
export import :operators.context.barotropic_closure_linearization;
|
||||
|
||||
export namespace mean_field::operators {
|
||||
struct PreparedBarotropicClosureReport final {
|
||||
context::barotropic::BarotropicClosurePreparationReport contextReport;
|
||||
bool preparedElementData{false};
|
||||
|
||||
[[nodiscard]] bool DidAnyWork() const noexcept {
|
||||
return contextReport.DidAnyWork() || preparedElementData;
|
||||
}
|
||||
};
|
||||
|
||||
class PreparedBarotropicClosureOperator final : public mfem::Operator {
|
||||
public:
|
||||
PreparedBarotropicClosureOperator(
|
||||
const fem::FEM &f,
|
||||
const mapping::DomainMapperStateless &domainMapper,
|
||||
const physics::PolytropicBarotrope &barotrope
|
||||
const eos::Polytrope &equationOfState
|
||||
);
|
||||
|
||||
void Prepare(
|
||||
const mfem::Vector &baseDensityTrue,
|
||||
const mfem::Vector &baseEnthalpyTrue,
|
||||
const mfem::Vector &displacementTrue
|
||||
PreparedBarotropicClosureOperator(const PreparedBarotropicClosureOperator &) = delete;
|
||||
PreparedBarotropicClosureOperator &operator=(const PreparedBarotropicClosureOperator &) = delete;
|
||||
PreparedBarotropicClosureOperator(PreparedBarotropicClosureOperator &&) = delete;
|
||||
PreparedBarotropicClosureOperator &operator=(PreparedBarotropicClosureOperator &&) = delete;
|
||||
|
||||
PreparedBarotropicClosureReport Prepare(
|
||||
const context::barotropic::BarotropicClosureStateView &state,
|
||||
const context::barotropic::BarotropicClosureDependencies &dependencies
|
||||
);
|
||||
|
||||
void Mult(
|
||||
const mfem::Vector &densityVariationTrue,
|
||||
const mfem::Vector &enthalpyVariationTrue,
|
||||
const mfem::Vector &displacementVariationTrue,
|
||||
const mfem::Vector &densityVariation,
|
||||
const mfem::Vector &enthalpyVariation,
|
||||
const mfem::Vector &displacementVariation,
|
||||
mfem::Vector &action
|
||||
) const;
|
||||
|
||||
@@ -40,20 +55,33 @@ export namespace mean_field::operators {
|
||||
void BuildResidual(mfem::Vector &residual) const;
|
||||
|
||||
[[nodiscard]] bool IsPrepared() const noexcept;
|
||||
|
||||
[[nodiscard]] std::uint64_t GetPreparationCount() const noexcept;
|
||||
|
||||
[[nodiscard]] int GetDensitySize() const noexcept;
|
||||
|
||||
[[nodiscard]] int GetEnthalpySize() const noexcept;
|
||||
[[nodiscard]] int GetDisplacementSize() const noexcept;
|
||||
|
||||
[[nodiscard]] const context::barotropic::BarotropicClosureLinearizationContext &GetContext() const noexcept;
|
||||
[[nodiscard]] const context::barotropic::BarotropicClosurePreparationStatistics &
|
||||
GetContextPreparationStatistics() const noexcept;
|
||||
|
||||
private:
|
||||
struct ConstructionData;
|
||||
|
||||
[[nodiscard]] static ConstructionData MakeConstructionData(const fem::FEM &f);
|
||||
|
||||
PreparedBarotropicClosureOperator(
|
||||
const fem::FEM &f,
|
||||
const mapping::DomainMapperStateless &domainMapper,
|
||||
const eos::Polytrope &equationOfState,
|
||||
ConstructionData constructionData
|
||||
);
|
||||
|
||||
void VerifyPrepared() const;
|
||||
|
||||
void Mult(
|
||||
void ApplyThermodynamicActionFull(
|
||||
const mfem::Vector &densityVariationTrue,
|
||||
const mfem::Vector &enthalpyVariationTrue,
|
||||
mfem::Vector &action
|
||||
mfem::Vector &actionTrue
|
||||
) const;
|
||||
|
||||
struct ElementPAData {
|
||||
@@ -73,7 +101,13 @@ export namespace mean_field::operators {
|
||||
|
||||
const fem::FEM &m_fem;
|
||||
const mapping::DomainMapperStateless &m_domainMapper;
|
||||
const physics::PolytropicBarotrope &m_barotrope;
|
||||
const eos::Polytrope &m_equationOfState;
|
||||
|
||||
field::FieldDofMap m_densityMap;
|
||||
field::FieldDofMap m_enthalpyMap;
|
||||
field::FieldDofMap m_displacementMap;
|
||||
|
||||
context::barotropic::BarotropicClosureLinearizationContext m_context;
|
||||
|
||||
std::vector<ElementPAData> m_elements;
|
||||
|
||||
@@ -81,8 +115,12 @@ export namespace mean_field::operators {
|
||||
mfem::Vector m_baseEnthalpyTrue;
|
||||
mfem::Vector m_baseDisplacementTrue;
|
||||
|
||||
int m_densitySize{0};
|
||||
int m_enthalpySize{0};
|
||||
mutable mfem::Vector m_densityVariationTrue;
|
||||
mutable mfem::Vector m_enthalpyVariationTrue;
|
||||
mutable mfem::Vector m_displacementVariationTrue;
|
||||
mutable mfem::Vector m_fullThermodynamicAction;
|
||||
mutable mfem::Vector m_fullDisplacementAction;
|
||||
mutable mfem::Vector m_fullResidual;
|
||||
|
||||
std::uint64_t m_preparationCount{0};
|
||||
bool m_isPrepared{false};
|
||||
|
||||
@@ -0,0 +1,198 @@
|
||||
module;
|
||||
|
||||
#include <compare>
|
||||
#include <cstdint>
|
||||
|
||||
#include <mfem.hpp>
|
||||
|
||||
export module mean_field:operators.prepared_displacement_residual;
|
||||
|
||||
export import :fem;
|
||||
export import :mapping.domain_mapper;
|
||||
export import :operators.context.gravity_field;
|
||||
export import :operators.prepared_gravity_displacement_force;
|
||||
export import :operators.prepared_pressure_force;
|
||||
export import :operators.prepared_rotational_displacement_force;
|
||||
export import :eos.polytrope;
|
||||
export import :physics.rigid_rotation;
|
||||
export import :utils.blocks;
|
||||
|
||||
export namespace mean_field::operators {
|
||||
struct DisplacementResidualDependencyStamp final {
|
||||
std::uint64_t identity{0};
|
||||
std::uint64_t revision{0};
|
||||
|
||||
constexpr auto operator<=>(const DisplacementResidualDependencyStamp &) const = default;
|
||||
};
|
||||
|
||||
struct DisplacementResidualDependencies final {
|
||||
DisplacementResidualDependencyStamp discretization;
|
||||
DisplacementResidualDependencyStamp density;
|
||||
DisplacementResidualDependencyStamp displacement;
|
||||
DisplacementResidualDependencyStamp gravityGradient;
|
||||
DisplacementResidualDependencyStamp enthalpy;
|
||||
DisplacementResidualDependencyStamp rotation;
|
||||
|
||||
constexpr auto operator<=>(const DisplacementResidualDependencies &) const = default;
|
||||
};
|
||||
|
||||
/*
|
||||
* Density, displacement, and gravity gradient deliberately do not appear
|
||||
* here. They are obtained from the shared, already-prepared gravity-field
|
||||
* linearization context so every mechanical-force contribution consumes
|
||||
* the same frozen density and geometry as the gravity equations.
|
||||
*/
|
||||
struct DisplacementResidualStateView final {
|
||||
const mfem::Vector &enthalpy;
|
||||
};
|
||||
|
||||
struct PreparedDisplacementResidualReport final {
|
||||
PreparedPressureForceReport pressure;
|
||||
PreparedGravityDisplacementForceReport gravity;
|
||||
PreparedRotationalDisplacementForceReport rotation;
|
||||
|
||||
bool assembledResidual{false};
|
||||
|
||||
[[nodiscard]] bool DidAnyChildWork() const noexcept {
|
||||
return pressure.DidAnyWork() || gravity.DidAnyWork() || rotation.DidAnyWork();
|
||||
}
|
||||
|
||||
[[nodiscard]] bool DidAnyWork() const noexcept {
|
||||
return DidAnyChildWork() || assembledResidual;
|
||||
}
|
||||
};
|
||||
|
||||
struct PreparedDisplacementResidualActionStatistics final {
|
||||
std::uint64_t densityApplications{0};
|
||||
std::uint64_t displacementApplications{0};
|
||||
std::uint64_t gravityGradientApplications{0};
|
||||
std::uint64_t enthalpyApplications{0};
|
||||
std::uint64_t completeApplications{0};
|
||||
|
||||
constexpr auto operator<=>(const PreparedDisplacementResidualActionStatistics &) const = default;
|
||||
};
|
||||
|
||||
/*
|
||||
* Row-level composer for
|
||||
*
|
||||
* R_d = R_d^pressure + R_d^gravity + R_d^rotation.
|
||||
*
|
||||
* This class owns the three prepared contributors and only orchestrates
|
||||
* their existing residual and Jacobian APIs. It contains no force kernel
|
||||
* and no independent copy of the gravity linearization context.
|
||||
*/
|
||||
class PreparedDisplacementResidualOperator final {
|
||||
public:
|
||||
PreparedDisplacementResidualOperator(
|
||||
const fem::FEM &f,
|
||||
const mapping::DomainMapperStateless &domainMapper,
|
||||
const eos::Polytrope &barotrope,
|
||||
const context::gravity_field::GravityFieldLinearizationContext &gravityContext
|
||||
);
|
||||
|
||||
PreparedDisplacementResidualOperator(const PreparedDisplacementResidualOperator &) = delete;
|
||||
|
||||
PreparedDisplacementResidualOperator &operator=(const PreparedDisplacementResidualOperator &) = delete;
|
||||
|
||||
PreparedDisplacementResidualOperator(PreparedDisplacementResidualOperator &&) = delete;
|
||||
|
||||
PreparedDisplacementResidualOperator &operator=(PreparedDisplacementResidualOperator &&) = delete;
|
||||
|
||||
PreparedDisplacementResidualReport Prepare(
|
||||
const DisplacementResidualStateView &state,
|
||||
const DisplacementResidualDependencies &dependencies,
|
||||
const physics::RigidRotation &rotation
|
||||
);
|
||||
|
||||
void BuildResidual(mfem::Vector &residual) const;
|
||||
|
||||
void ApplyDensityJacobianAction(
|
||||
const mfem::Vector &densityVariation,
|
||||
mfem::Vector &action
|
||||
) const;
|
||||
|
||||
void ApplyDisplacementJacobianAction(
|
||||
const mfem::Vector &displacementVariation,
|
||||
mfem::Vector &action
|
||||
) const;
|
||||
|
||||
void ApplyGravityGradientJacobianAction(
|
||||
const mfem::Vector &gravityGradientVariation,
|
||||
mfem::Vector &action
|
||||
) const;
|
||||
|
||||
void ApplyEnthalpyJacobianAction(
|
||||
const mfem::Vector &enthalpyVariation,
|
||||
mfem::Vector &action
|
||||
) const;
|
||||
|
||||
void ApplyCompleteJacobianAction(
|
||||
const mfem::Vector &densityVariation,
|
||||
const mfem::Vector &displacementVariation,
|
||||
const mfem::Vector &gravityGradientVariation,
|
||||
const mfem::Vector &enthalpyVariation,
|
||||
mfem::Vector &action
|
||||
) const;
|
||||
|
||||
[[nodiscard]] bool IsPrepared() const noexcept;
|
||||
|
||||
[[nodiscard]] std::uint64_t GetResidualPreparationCount() const noexcept;
|
||||
|
||||
[[nodiscard]] std::uint64_t GetResidualApplicationCount() const noexcept;
|
||||
|
||||
[[nodiscard]] const PreparedDisplacementResidualActionStatistics &GetActionStatistics() const noexcept;
|
||||
|
||||
[[nodiscard]] const PreparedPressureForceOperator &GetPressureOperator() const noexcept;
|
||||
|
||||
[[nodiscard]] const PreparedGravityDisplacementForceOperator &GetGravityOperator() const noexcept;
|
||||
|
||||
[[nodiscard]] const PreparedRotationalDisplacementForceOperator &GetRotationalOperator() const noexcept;
|
||||
|
||||
[[nodiscard]] const fem::FEM &GetFEM() const noexcept;
|
||||
|
||||
[[nodiscard]] const context::gravity_field::GravityFieldLinearizationContext &
|
||||
GetGravityContext() const noexcept;
|
||||
|
||||
private:
|
||||
void AssembleResidual();
|
||||
void VerifyPrepared() const;
|
||||
|
||||
const fem::FEM &m_fem;
|
||||
const mapping::DomainMapperStateless &m_domainMapper;
|
||||
const context::gravity_field::GravityFieldLinearizationContext &m_gravityContext;
|
||||
|
||||
PreparedPressureForceOperator m_pressureOperator;
|
||||
PreparedGravityDisplacementForceOperator m_gravityOperator;
|
||||
PreparedRotationalDisplacementForceOperator m_rotationalOperator;
|
||||
|
||||
DisplacementResidualDependencies m_preparedDependencies;
|
||||
mfem::Vector m_cachedResidual;
|
||||
|
||||
std::uint64_t m_residualPreparationCount{0};
|
||||
mutable std::uint64_t m_residualApplicationCount{0};
|
||||
mutable PreparedDisplacementResidualActionStatistics m_actionStatistics;
|
||||
|
||||
bool m_isPrepared{false};
|
||||
};
|
||||
|
||||
using DisplacementResidualLayout = utils::blocks::form_layout<utils::blocks::barotropic_equilibrium_form>;
|
||||
|
||||
class PreparedDisplacementResidualJacobianOperator final : public mfem::Operator {
|
||||
public:
|
||||
PreparedDisplacementResidualJacobianOperator(
|
||||
const DisplacementResidualLayout &layout,
|
||||
const PreparedDisplacementResidualOperator &preparedOperator
|
||||
);
|
||||
|
||||
void Mult(
|
||||
const mfem::Vector &direction,
|
||||
mfem::Vector &action
|
||||
) const override;
|
||||
|
||||
[[nodiscard]] const DisplacementResidualLayout &GetLayout() const noexcept;
|
||||
|
||||
private:
|
||||
DisplacementResidualLayout m_layout;
|
||||
const PreparedDisplacementResidualOperator &m_preparedOperator;
|
||||
};
|
||||
} // namespace mean_field::operators
|
||||
@@ -0,0 +1,150 @@
|
||||
module;
|
||||
|
||||
#include <compare>
|
||||
#include <cstdint>
|
||||
|
||||
#include <mfem.hpp>
|
||||
|
||||
export module mean_field:operators.prepared_gravity_displacement_force;
|
||||
|
||||
export import :fem;
|
||||
export import :mapping.domain_mapper;
|
||||
export import :operators.context.gravity_field;
|
||||
export import :utils.blocks;
|
||||
|
||||
export namespace mean_field::operators {
|
||||
struct PreparedGravityDisplacementForceReport final {
|
||||
bool preparedResidual{false};
|
||||
|
||||
[[nodiscard]] bool DidAnyWork() const noexcept {
|
||||
return preparedResidual;
|
||||
}
|
||||
};
|
||||
|
||||
struct PreparedGravityDisplacementForceColumnStatistics final {
|
||||
std::uint64_t applications{0};
|
||||
|
||||
constexpr auto operator<=>(const PreparedGravityDisplacementForceColumnStatistics &) const = default;
|
||||
};
|
||||
|
||||
struct PreparedGravityDisplacementForceCompleteStatistics final {
|
||||
std::uint64_t applications{0};
|
||||
|
||||
constexpr auto operator<=>(const PreparedGravityDisplacementForceCompleteStatistics &) const = default;
|
||||
};
|
||||
|
||||
class PreparedGravityDisplacementForceOperator final {
|
||||
public:
|
||||
PreparedGravityDisplacementForceOperator(
|
||||
const fem::FEM &f,
|
||||
const mapping::DomainMapperStateless &domainMapper,
|
||||
const context::gravity_field::GravityFieldLinearizationContext &gravityContext
|
||||
);
|
||||
|
||||
PreparedGravityDisplacementForceOperator(const PreparedGravityDisplacementForceOperator &) = delete;
|
||||
|
||||
PreparedGravityDisplacementForceOperator &operator=(const PreparedGravityDisplacementForceOperator &) = delete;
|
||||
|
||||
PreparedGravityDisplacementForceOperator(PreparedGravityDisplacementForceOperator &&) = delete;
|
||||
|
||||
PreparedGravityDisplacementForceOperator &operator=(PreparedGravityDisplacementForceOperator &&) = delete;
|
||||
|
||||
/*
|
||||
* Freeze the already-prepared shared gravity context. The caller must
|
||||
* first prepare GravityFieldLinearizationContext for the desired
|
||||
* state. Repeated calls with unchanged revisions do no work.
|
||||
*/
|
||||
PreparedGravityDisplacementForceReport Prepare();
|
||||
|
||||
void BuildResidual(mfem::Vector &residual) const;
|
||||
|
||||
void ApplyDensityJacobianAction(
|
||||
const mfem::Vector &densityVariation,
|
||||
mfem::Vector &action
|
||||
) const;
|
||||
|
||||
void ApplyGravityGradientJacobianAction(
|
||||
const mfem::Vector &gravityGradientVariation,
|
||||
mfem::Vector &action
|
||||
) const;
|
||||
|
||||
void ApplyDisplacementJacobianAction(
|
||||
const mfem::Vector &displacementVariation,
|
||||
mfem::Vector &action
|
||||
) const;
|
||||
|
||||
void ApplyCompleteJacobianAction(
|
||||
const mfem::Vector &densityVariation,
|
||||
const mfem::Vector &displacementVariation,
|
||||
const mfem::Vector &gravityGradientVariation,
|
||||
mfem::Vector &action
|
||||
) const;
|
||||
|
||||
[[nodiscard]] bool IsPrepared() const noexcept;
|
||||
|
||||
[[nodiscard]] std::uint64_t GetResidualPreparationCount() const noexcept;
|
||||
|
||||
[[nodiscard]] std::uint64_t GetResidualApplicationCount() const noexcept;
|
||||
|
||||
[[nodiscard]] const PreparedGravityDisplacementForceColumnStatistics &
|
||||
GetDensityJacobianStatistics() const noexcept;
|
||||
|
||||
[[nodiscard]] const PreparedGravityDisplacementForceColumnStatistics &
|
||||
GetGravityGradientJacobianStatistics() const noexcept;
|
||||
|
||||
[[nodiscard]] const PreparedGravityDisplacementForceColumnStatistics &
|
||||
GetDisplacementJacobianStatistics() const noexcept;
|
||||
|
||||
[[nodiscard]] const PreparedGravityDisplacementForceCompleteStatistics &
|
||||
GetCompleteJacobianStatistics() const noexcept;
|
||||
|
||||
[[nodiscard]] const fem::FEM &GetFEM() const noexcept;
|
||||
|
||||
[[nodiscard]] const context::gravity_field::GravityFieldLinearizationContext &
|
||||
GetGravityContext() const noexcept;
|
||||
|
||||
private:
|
||||
void VerifyPrepared() const;
|
||||
|
||||
const fem::FEM &m_fem;
|
||||
const mapping::DomainMapperStateless &m_domainMapper;
|
||||
const context::gravity_field::GravityFieldLinearizationContext &m_gravityContext;
|
||||
|
||||
context::gravity_field::GravityFieldRevisions m_preparedRevisions;
|
||||
mfem::Vector m_cachedResidual;
|
||||
|
||||
std::uint64_t m_residualPreparationCount{0};
|
||||
mutable std::uint64_t m_residualApplicationCount{0};
|
||||
|
||||
mutable PreparedGravityDisplacementForceColumnStatistics m_densityJacobianStatistics;
|
||||
|
||||
mutable PreparedGravityDisplacementForceColumnStatistics m_gravityGradientJacobianStatistics;
|
||||
|
||||
mutable PreparedGravityDisplacementForceColumnStatistics m_displacementJacobianStatistics;
|
||||
|
||||
mutable PreparedGravityDisplacementForceCompleteStatistics m_completeJacobianStatistics;
|
||||
|
||||
bool m_isPrepared{false};
|
||||
};
|
||||
|
||||
using GravityDisplacementForceLayout = utils::blocks::form_layout<utils::blocks::barotropic_equilibrium_form>;
|
||||
|
||||
class PreparedGravityDisplacementForceJacobianOperator final : public mfem::Operator {
|
||||
public:
|
||||
PreparedGravityDisplacementForceJacobianOperator(
|
||||
const GravityDisplacementForceLayout &layout,
|
||||
const PreparedGravityDisplacementForceOperator &preparedOperator
|
||||
);
|
||||
|
||||
void Mult(
|
||||
const mfem::Vector &direction,
|
||||
mfem::Vector &action
|
||||
) const override;
|
||||
|
||||
[[nodiscard]] const GravityDisplacementForceLayout &GetLayout() const noexcept;
|
||||
|
||||
private:
|
||||
GravityDisplacementForceLayout m_layout;
|
||||
const PreparedGravityDisplacementForceOperator &m_preparedOperator;
|
||||
};
|
||||
} // namespace mean_field::operators
|
||||
@@ -25,8 +25,7 @@ export namespace mean_field::operators {
|
||||
bool preparedResidual{false};
|
||||
|
||||
[[nodiscard]] bool DidAnyWork() const noexcept {
|
||||
return contextReport.DidAnyWork() || updatedRotation ||
|
||||
preparedAlgebraicJacobianBlocks ||
|
||||
return contextReport.DidAnyWork() || updatedRotation || preparedAlgebraicJacobianBlocks ||
|
||||
preparedDisplacementJacobianData || preparedResidual;
|
||||
}
|
||||
};
|
||||
@@ -38,26 +37,20 @@ export namespace mean_field::operators {
|
||||
std::uint64_t bernoulliConstantApplications{0};
|
||||
std::uint64_t combinedApplications{0};
|
||||
|
||||
constexpr auto operator<=>(
|
||||
const PreparedHydrostaticAlgebraicJacobianStatistics &
|
||||
) const = default;
|
||||
constexpr auto operator<=>(const PreparedHydrostaticAlgebraicJacobianStatistics &) const = default;
|
||||
};
|
||||
|
||||
struct PreparedHydrostaticDisplacementJacobianStatistics {
|
||||
std::uint64_t preparations{0};
|
||||
std::uint64_t applications{0};
|
||||
|
||||
constexpr auto operator<=>(
|
||||
const PreparedHydrostaticDisplacementJacobianStatistics &
|
||||
) const = default;
|
||||
constexpr auto operator<=>(const PreparedHydrostaticDisplacementJacobianStatistics &) const = default;
|
||||
};
|
||||
|
||||
struct PreparedHydrostaticCompleteJacobianStatistics {
|
||||
std::uint64_t applications{0};
|
||||
|
||||
constexpr auto operator<=>(
|
||||
const PreparedHydrostaticCompleteJacobianStatistics &
|
||||
) const = default;
|
||||
constexpr auto operator<=>(const PreparedHydrostaticCompleteJacobianStatistics &) const = default;
|
||||
};
|
||||
|
||||
enum class HydrostaticJacobianInputBlock : int {
|
||||
@@ -94,24 +87,17 @@ export namespace mean_field::operators {
|
||||
const mapping::DomainMapperStateless &domainMapper
|
||||
);
|
||||
|
||||
PreparedHydrostaticEquilibriumOperator(
|
||||
const PreparedHydrostaticEquilibriumOperator &
|
||||
) = delete;
|
||||
PreparedHydrostaticEquilibriumOperator(const PreparedHydrostaticEquilibriumOperator &) = delete;
|
||||
|
||||
PreparedHydrostaticEquilibriumOperator &
|
||||
operator=(const PreparedHydrostaticEquilibriumOperator &) = delete;
|
||||
PreparedHydrostaticEquilibriumOperator &operator=(const PreparedHydrostaticEquilibriumOperator &) = delete;
|
||||
|
||||
PreparedHydrostaticEquilibriumOperator(
|
||||
PreparedHydrostaticEquilibriumOperator &&
|
||||
) = delete;
|
||||
PreparedHydrostaticEquilibriumOperator(PreparedHydrostaticEquilibriumOperator &&) = delete;
|
||||
|
||||
PreparedHydrostaticEquilibriumOperator &
|
||||
operator=(PreparedHydrostaticEquilibriumOperator &&) = delete;
|
||||
PreparedHydrostaticEquilibriumOperator &operator=(PreparedHydrostaticEquilibriumOperator &&) = delete;
|
||||
|
||||
PreparedHydrostaticEquilibriumReport Prepare(
|
||||
const context::hydrostatic::HydrostaticEquilibriumStateView &state,
|
||||
const context::hydrostatic::HydrostaticEquilibriumDependencies
|
||||
&dependencies,
|
||||
const context::hydrostatic::HydrostaticEquilibriumDependencies &dependencies,
|
||||
const physics::RigidRotation &rotation
|
||||
);
|
||||
|
||||
@@ -154,15 +140,12 @@ export namespace mean_field::operators {
|
||||
|
||||
[[nodiscard]] bool IsPrepared() const noexcept;
|
||||
|
||||
[[nodiscard]] const context::hydrostatic::
|
||||
HydrostaticPreparationStatistics &
|
||||
GetContextPreparationStatistics() const noexcept;
|
||||
[[nodiscard]] const context::hydrostatic::HydrostaticPreparationStatistics &
|
||||
GetContextPreparationStatistics() const noexcept;
|
||||
|
||||
[[nodiscard]] std::uint64_t
|
||||
GetResidualPreparationCount() const noexcept;
|
||||
[[nodiscard]] std::uint64_t GetResidualPreparationCount() const noexcept;
|
||||
|
||||
[[nodiscard]] std::uint64_t
|
||||
GetResidualApplicationCount() const noexcept;
|
||||
[[nodiscard]] std::uint64_t GetResidualApplicationCount() const noexcept;
|
||||
|
||||
[[nodiscard]] const PreparedHydrostaticAlgebraicJacobianStatistics &
|
||||
GetAlgebraicJacobianStatistics() const noexcept;
|
||||
@@ -203,11 +186,9 @@ export namespace mean_field::operators {
|
||||
mfem::Vector quadratureWeights;
|
||||
std::vector<mapping::VolumeMappingContext> baseMappingContexts;
|
||||
|
||||
std::optional<mapping::ElementDisplacementData>
|
||||
baseDisplacementData;
|
||||
std::optional<mapping::ElementDisplacementData> baseDisplacementData;
|
||||
|
||||
std::optional<mapping::ElementCompactificationData>
|
||||
compactificationData;
|
||||
std::optional<mapping::ElementCompactificationData> compactificationData;
|
||||
|
||||
mfem::Vector rotationPotential;
|
||||
mfem::DenseMatrix rotationGradient;
|
||||
@@ -242,20 +223,16 @@ export namespace mean_field::operators {
|
||||
|
||||
mutable std::uint64_t m_residualApplicationCount{0};
|
||||
|
||||
mutable PreparedHydrostaticAlgebraicJacobianStatistics
|
||||
m_algebraicJacobianStatistics;
|
||||
mutable PreparedHydrostaticAlgebraicJacobianStatistics m_algebraicJacobianStatistics;
|
||||
|
||||
mutable PreparedHydrostaticDisplacementJacobianStatistics
|
||||
m_displacementJacobianStatistics;
|
||||
mutable PreparedHydrostaticDisplacementJacobianStatistics m_displacementJacobianStatistics;
|
||||
|
||||
mutable PreparedHydrostaticCompleteJacobianStatistics
|
||||
m_completeJacobianStatistics;
|
||||
mutable PreparedHydrostaticCompleteJacobianStatistics m_completeJacobianStatistics;
|
||||
|
||||
bool m_isPrepared{false};
|
||||
};
|
||||
|
||||
class PreparedHydrostaticEquilibriumJacobianOperator final
|
||||
: public mfem::Operator {
|
||||
class PreparedHydrostaticEquilibriumJacobianOperator final : public mfem::Operator {
|
||||
public:
|
||||
PreparedHydrostaticEquilibriumJacobianOperator(
|
||||
const fem::FEM &f,
|
||||
@@ -267,8 +244,7 @@ export namespace mean_field::operators {
|
||||
mfem::Vector &action
|
||||
) const override;
|
||||
|
||||
[[nodiscard]] const HydrostaticJacobianBlockLayout &
|
||||
GetLayout() const noexcept;
|
||||
[[nodiscard]] const HydrostaticJacobianBlockLayout &GetLayout() const noexcept;
|
||||
|
||||
private:
|
||||
HydrostaticJacobianBlockLayout m_layout;
|
||||
|
||||
@@ -0,0 +1,185 @@
|
||||
module;
|
||||
|
||||
#include <compare>
|
||||
#include <cstdint>
|
||||
#include <mfem.hpp>
|
||||
#include <vector>
|
||||
|
||||
export module mean_field:operators.prepared_mass_normalization;
|
||||
|
||||
export import :fem;
|
||||
export import :mapping.domain_mapper;
|
||||
export import :operators.context.gravity_field;
|
||||
export import :utils.blocks;
|
||||
|
||||
export namespace mean_field::operators {
|
||||
struct MassNormalizationDependencyStamp final {
|
||||
std::uint64_t identity{0};
|
||||
std::uint64_t revision{0};
|
||||
|
||||
constexpr auto operator<=>(const MassNormalizationDependencyStamp &) const = default;
|
||||
};
|
||||
|
||||
struct MassNormalizationDependencies final {
|
||||
MassNormalizationDependencyStamp discretization;
|
||||
MassNormalizationDependencyStamp density;
|
||||
MassNormalizationDependencyStamp displacement;
|
||||
MassNormalizationDependencyStamp targetMass;
|
||||
|
||||
constexpr auto operator<=>(const MassNormalizationDependencies &) const = default;
|
||||
};
|
||||
|
||||
struct MassNormalizationStateView final {
|
||||
double targetMass{0.0};
|
||||
};
|
||||
|
||||
struct PreparedMassNormalizationReport final {
|
||||
bool rebuiltStaticPlan{false};
|
||||
bool refreshedGeometry{false};
|
||||
bool refreshedDensity{false};
|
||||
bool updatedTargetMass{false};
|
||||
bool assembledResidual{false};
|
||||
|
||||
[[nodiscard]] bool DidAnyWork() const noexcept {
|
||||
return rebuiltStaticPlan || refreshedGeometry || refreshedDensity || updatedTargetMass || assembledResidual;
|
||||
}
|
||||
};
|
||||
|
||||
struct PreparedMassNormalizationActionStatistics final {
|
||||
std::uint64_t densityApplications{0};
|
||||
std::uint64_t displacementApplications{0};
|
||||
std::uint64_t completeApplications{0};
|
||||
|
||||
constexpr auto operator<=>(const PreparedMassNormalizationActionStatistics &) const = default;
|
||||
};
|
||||
|
||||
/*
|
||||
* Prepared scalar row
|
||||
*
|
||||
* R_M(rho, d) = integral_{Omega_star(d)} rho dV - M_target.
|
||||
*
|
||||
* Density and displacement are borrowed from the shared gravity-field
|
||||
* linearization context. This keeps the mass row on exactly the same
|
||||
* frozen state and geometry as the gravity and mechanical rows.
|
||||
*/
|
||||
class PreparedMassNormalizationOperator final {
|
||||
public:
|
||||
PreparedMassNormalizationOperator(
|
||||
const fem::FEM &f,
|
||||
const mapping::DomainMapperStateless &domainMapper,
|
||||
const context::gravity_field::GravityFieldLinearizationContext &gravityContext
|
||||
);
|
||||
|
||||
PreparedMassNormalizationOperator(const PreparedMassNormalizationOperator &) = delete;
|
||||
PreparedMassNormalizationOperator &operator=(const PreparedMassNormalizationOperator &) = delete;
|
||||
PreparedMassNormalizationOperator(PreparedMassNormalizationOperator &&) = delete;
|
||||
PreparedMassNormalizationOperator &operator=(PreparedMassNormalizationOperator &&) = delete;
|
||||
|
||||
PreparedMassNormalizationReport Prepare(
|
||||
const MassNormalizationStateView &state,
|
||||
const MassNormalizationDependencies &dependencies
|
||||
);
|
||||
|
||||
void BuildResidual(mfem::Vector &residual) const;
|
||||
|
||||
void ApplyDensityJacobianAction(
|
||||
const mfem::Vector &densityVariation,
|
||||
mfem::Vector &action
|
||||
) const;
|
||||
|
||||
void ApplyDisplacementJacobianAction(
|
||||
const mfem::Vector &displacementVariation,
|
||||
mfem::Vector &action
|
||||
) const;
|
||||
|
||||
void ApplyCompleteJacobianAction(
|
||||
const mfem::Vector &densityVariation,
|
||||
const mfem::Vector &displacementVariation,
|
||||
mfem::Vector &action
|
||||
) const;
|
||||
|
||||
[[nodiscard]] bool IsPrepared() const noexcept;
|
||||
[[nodiscard]] double GetCurrentMass() const;
|
||||
[[nodiscard]] double GetTargetMass() const;
|
||||
[[nodiscard]] std::uint64_t GetPreparationCount() const noexcept;
|
||||
[[nodiscard]] std::uint64_t GetResidualApplicationCount() const noexcept;
|
||||
[[nodiscard]] const PreparedMassNormalizationActionStatistics &GetActionStatistics() const noexcept;
|
||||
[[nodiscard]] const fem::FEM &GetFEM() const noexcept;
|
||||
[[nodiscard]] const context::gravity_field::GravityFieldLinearizationContext &
|
||||
GetGravityContext() const noexcept;
|
||||
|
||||
private:
|
||||
struct QuadraturePointData final {
|
||||
mfem::IntegrationPoint integrationPoint;
|
||||
mfem::Vector densityShape;
|
||||
mapping::VolumeMappingContext mappingContext;
|
||||
double density{0.0};
|
||||
};
|
||||
|
||||
struct ElementPAData final {
|
||||
int elementId{-1};
|
||||
|
||||
mfem::Array<int> densityDofs;
|
||||
mfem::Array<int> displacementDofs;
|
||||
mfem::Array<int> compactificationDofs;
|
||||
|
||||
mfem::DofTransformation *densityDofTransformation{nullptr};
|
||||
mfem::DofTransformation *displacementDofTransformation{nullptr};
|
||||
mfem::DofTransformation *compactificationDofTransformation{nullptr};
|
||||
|
||||
mfem::Vector baseDisplacement;
|
||||
mfem::Vector compactification;
|
||||
|
||||
std::vector<QuadraturePointData> quadraturePoints;
|
||||
};
|
||||
|
||||
void BuildStaticPlan();
|
||||
void RefreshGeometry(const mfem::Vector &displacement);
|
||||
void RefreshDensity(const mfem::Vector &density);
|
||||
void AssembleResidual();
|
||||
void VerifyPrepared() const;
|
||||
|
||||
[[nodiscard]] double EvaluateDensityActionLocal(const mfem::Vector &densityVariation) const;
|
||||
|
||||
[[nodiscard]] double EvaluateDisplacementActionLocal(const mfem::Vector &displacementVariation) const;
|
||||
|
||||
[[nodiscard]] double GlobalSum(double localValue) const;
|
||||
|
||||
const fem::FEM &m_fem;
|
||||
const mapping::DomainMapperStateless &m_domainMapper;
|
||||
const context::gravity_field::GravityFieldLinearizationContext &m_gravityContext;
|
||||
|
||||
std::vector<ElementPAData> m_elements;
|
||||
MassNormalizationDependencies m_preparedDependencies;
|
||||
mfem::Vector m_cachedResidual;
|
||||
|
||||
double m_currentMass{0.0};
|
||||
double m_targetMass{0.0};
|
||||
|
||||
std::uint64_t m_preparationCount{0};
|
||||
mutable std::uint64_t m_residualApplicationCount{0};
|
||||
mutable PreparedMassNormalizationActionStatistics m_actionStatistics;
|
||||
bool m_isPrepared{false};
|
||||
};
|
||||
|
||||
using MassNormalizationLayout = utils::blocks::form_layout<utils::blocks::barotropic_equilibrium_form>;
|
||||
|
||||
class PreparedMassNormalizationJacobianOperator final : public mfem::Operator {
|
||||
public:
|
||||
PreparedMassNormalizationJacobianOperator(
|
||||
const MassNormalizationLayout &layout,
|
||||
const PreparedMassNormalizationOperator &preparedOperator
|
||||
);
|
||||
|
||||
void Mult(
|
||||
const mfem::Vector &direction,
|
||||
mfem::Vector &action
|
||||
) const override;
|
||||
|
||||
[[nodiscard]] const MassNormalizationLayout &GetLayout() const noexcept;
|
||||
|
||||
private:
|
||||
MassNormalizationLayout m_layout;
|
||||
const PreparedMassNormalizationOperator &m_preparedOperator;
|
||||
};
|
||||
} // namespace mean_field::operators
|
||||
304
libmeanfield/interface/operators/prepared_pressure_force.cppm
Normal file
304
libmeanfield/interface/operators/prepared_pressure_force.cppm
Normal file
@@ -0,0 +1,304 @@
|
||||
module;
|
||||
|
||||
#include <compare>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
#include <mfem.hpp>
|
||||
|
||||
export module mean_field:operators.prepared_pressure_force;
|
||||
|
||||
export import :eos.polytrope;
|
||||
export import :fem;
|
||||
export import :field.mfem;
|
||||
export import :mapping.domain_mapper;
|
||||
export import :operators.context.pressure_force;
|
||||
export import :utils.blocks;
|
||||
|
||||
export namespace mean_field::operators {
|
||||
struct PreparedPressureForceReport final {
|
||||
context::pressure_force::PressureForcePreparationReport contextReport;
|
||||
|
||||
bool preparedEnthalpyJacobianData{false};
|
||||
bool preparedDisplacementJacobianData{false};
|
||||
bool preparedResidual{false};
|
||||
|
||||
[[nodiscard]]
|
||||
bool DidAnyWork() const noexcept {
|
||||
return contextReport.DidAnyWork() || preparedEnthalpyJacobianData || preparedDisplacementJacobianData ||
|
||||
preparedResidual;
|
||||
}
|
||||
};
|
||||
|
||||
struct PreparedPressureForceEnthalpyJacobianStatistics final {
|
||||
std::uint64_t preparations{0};
|
||||
std::uint64_t applications{0};
|
||||
|
||||
constexpr auto operator<=>(const PreparedPressureForceEnthalpyJacobianStatistics &) const = default;
|
||||
};
|
||||
|
||||
struct PreparedPressureForceDisplacementJacobianStatistics final {
|
||||
std::uint64_t preparations{0};
|
||||
std::uint64_t applications{0};
|
||||
|
||||
constexpr auto operator<=>(const PreparedPressureForceDisplacementJacobianStatistics &) const = default;
|
||||
};
|
||||
|
||||
struct PreparedPressureForceCompleteJacobianStatistics final {
|
||||
std::uint64_t applications{0};
|
||||
|
||||
constexpr auto operator<=>(const PreparedPressureForceCompleteJacobianStatistics &) const = default;
|
||||
};
|
||||
|
||||
/*
|
||||
* Prepared pressure contribution
|
||||
*
|
||||
* R_d^P(w)
|
||||
* =
|
||||
* - integral_{Omega_star(d)}
|
||||
* P(h) div(w) dV.
|
||||
*
|
||||
* Public state and Jacobian directions are expressed in FieldDof
|
||||
* coordinates.
|
||||
*
|
||||
* Current registry:
|
||||
*
|
||||
* h -> Stellar -> reduced
|
||||
* d -> All -> identity/full
|
||||
* R_d -> All -> identity/full
|
||||
*
|
||||
* Full MFEM true/local vectors are private implementation details.
|
||||
*/
|
||||
class PreparedPressureForceOperator final {
|
||||
public:
|
||||
PreparedPressureForceOperator(
|
||||
const fem::FEM &f,
|
||||
const mapping::DomainMapperStateless &domainMapper,
|
||||
const eos::Polytrope &equationOfState
|
||||
);
|
||||
|
||||
PreparedPressureForceOperator(const PreparedPressureForceOperator &) = delete;
|
||||
|
||||
PreparedPressureForceOperator &operator=(const PreparedPressureForceOperator &) = delete;
|
||||
|
||||
PreparedPressureForceOperator(PreparedPressureForceOperator &&) = delete;
|
||||
|
||||
PreparedPressureForceOperator &operator=(PreparedPressureForceOperator &&) = delete;
|
||||
|
||||
PreparedPressureForceReport Prepare(
|
||||
const context::pressure_force::PressureForceStateView &state,
|
||||
const context::pressure_force::PressureForceDependencies &dependencies
|
||||
);
|
||||
|
||||
void BuildResidual(mfem::Vector &residual) const;
|
||||
|
||||
void ApplyEnthalpyJacobianAction(
|
||||
const mfem::Vector &enthalpyVariation,
|
||||
mfem::Vector &action
|
||||
) const;
|
||||
|
||||
void ApplyDisplacementJacobianAction(
|
||||
const mfem::Vector &displacementVariation,
|
||||
mfem::Vector &action
|
||||
) const;
|
||||
|
||||
void ApplyCompleteJacobianAction(
|
||||
const mfem::Vector &enthalpyVariation,
|
||||
const mfem::Vector &displacementVariation,
|
||||
mfem::Vector &action
|
||||
) const;
|
||||
|
||||
[[nodiscard]]
|
||||
bool IsPrepared() const noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
int GetEnthalpySize() const noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
int GetDisplacementSize() const noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
const context::pressure_force::PressureForceLinearizationContext &GetContext() const noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
const context::pressure_force::PressureForcePreparationStatistics &
|
||||
GetContextPreparationStatistics() const noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
std::uint64_t GetResidualPreparationCount() const noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
std::uint64_t GetResidualApplicationCount() const noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
const PreparedPressureForceEnthalpyJacobianStatistics &GetEnthalpyJacobianStatistics() const noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
const PreparedPressureForceDisplacementJacobianStatistics &GetDisplacementJacobianStatistics() const noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
const PreparedPressureForceCompleteJacobianStatistics &GetCompleteJacobianStatistics() const noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
std::size_t GetStellarElementCount() const noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
const fem::FEM &GetFEM() const noexcept;
|
||||
|
||||
private:
|
||||
struct ConstructionData;
|
||||
|
||||
[[nodiscard]]
|
||||
static ConstructionData MakeConstructionData(const fem::FEM &f);
|
||||
|
||||
PreparedPressureForceOperator(
|
||||
const fem::FEM &f,
|
||||
const mapping::DomainMapperStateless &domainMapper,
|
||||
const eos::Polytrope &equationOfState,
|
||||
ConstructionData constructionData
|
||||
);
|
||||
|
||||
struct ElementPAData final {
|
||||
int elementId{-1};
|
||||
|
||||
mfem::Array<int> enthalpyDofs;
|
||||
mfem::Array<int> displacementDofs;
|
||||
mfem::Array<int> compactificationDofs;
|
||||
|
||||
mfem::DofTransformation *enthalpyDofTransformation{nullptr};
|
||||
|
||||
mfem::DofTransformation *displacementDofTransformation{nullptr};
|
||||
|
||||
mfem::DofTransformation *compactificationDofTransformation{nullptr};
|
||||
|
||||
const mfem::IntegrationRule *integrationRule{nullptr};
|
||||
|
||||
/*
|
||||
* Rows are quadrature points and columns are enthalpy DOFs.
|
||||
*/
|
||||
mfem::DenseMatrix enthalpyBasis;
|
||||
|
||||
/*
|
||||
* Each entry is:
|
||||
*
|
||||
* scalar displacement DOF
|
||||
* x
|
||||
* physical dimension.
|
||||
*/
|
||||
std::vector<mfem::DenseMatrix> referenceTestGradients;
|
||||
|
||||
std::vector<mfem::DenseMatrix> physicalTestGradients;
|
||||
|
||||
std::vector<mapping::VolumeMappingContext> baseMappingContexts;
|
||||
|
||||
std::optional<mapping::ElementDisplacementData> baseDisplacementData;
|
||||
|
||||
std::optional<mapping::ElementCompactificationData> compactificationData;
|
||||
|
||||
mfem::Vector quadratureWeights;
|
||||
mfem::Vector pressure;
|
||||
mfem::Vector pressureDerivative;
|
||||
|
||||
mfem::Vector elementResidual;
|
||||
mfem::DenseMatrix enthalpyJacobian;
|
||||
};
|
||||
|
||||
void PrepareStaticPlan();
|
||||
void PrepareGeometry();
|
||||
void PrepareMaterialState();
|
||||
|
||||
void FinalizeDisplacementJacobianPreparation();
|
||||
|
||||
void AssembleCachedResidual();
|
||||
|
||||
void VerifyPrepared() const;
|
||||
|
||||
const fem::FEM &m_fem;
|
||||
|
||||
const mapping::DomainMapperStateless &m_domainMapper;
|
||||
|
||||
const eos::Polytrope &m_equationOfState;
|
||||
|
||||
field::FieldDofMap m_enthalpyMap;
|
||||
|
||||
field::FieldDofMap m_displacementMap;
|
||||
|
||||
context::pressure_force::PressureForceLinearizationContext m_context;
|
||||
|
||||
std::vector<ElementPAData> m_elements;
|
||||
|
||||
/*
|
||||
* Canonical full-MFEM expansion of the frozen FieldDof state.
|
||||
*/
|
||||
mfem::Vector m_baseEnthalpyTrue;
|
||||
mfem::Vector m_baseDisplacementTrue;
|
||||
|
||||
/*
|
||||
* Reusable Krylov work storage.
|
||||
*/
|
||||
mutable mfem::Vector m_enthalpyVariationTrue;
|
||||
|
||||
mutable mfem::Vector m_displacementVariationTrue;
|
||||
|
||||
mutable mfem::Vector m_fullDisplacementAction;
|
||||
|
||||
/*
|
||||
* Solver-facing cached residual in Displacement FieldDof
|
||||
* coordinates.
|
||||
*/
|
||||
mfem::Vector m_cachedResidual;
|
||||
|
||||
std::uint64_t m_residualPreparationCount{0};
|
||||
|
||||
mutable std::uint64_t m_residualApplicationCount{0};
|
||||
|
||||
mutable PreparedPressureForceEnthalpyJacobianStatistics m_enthalpyJacobianStatistics;
|
||||
|
||||
mutable PreparedPressureForceDisplacementJacobianStatistics m_displacementJacobianStatistics;
|
||||
|
||||
mutable PreparedPressureForceCompleteJacobianStatistics m_completeJacobianStatistics;
|
||||
|
||||
bool m_isPrepared{false};
|
||||
};
|
||||
|
||||
using BarotropicEquilibriumLayout = utils::blocks::form_layout<utils::blocks::barotropic_equilibrium_form>;
|
||||
|
||||
/*
|
||||
* Coupled-layout adapter around the modern prepared pressure-force
|
||||
* Jacobian.
|
||||
*
|
||||
* Reads:
|
||||
*
|
||||
* delta d
|
||||
* delta h
|
||||
*
|
||||
* Writes:
|
||||
*
|
||||
* R_d
|
||||
*
|
||||
* It deliberately imposes no raw-FES-size assumptions on unrelated
|
||||
* coupled blocks.
|
||||
*/
|
||||
class PreparedPressureForceJacobianOperator final : public mfem::Operator {
|
||||
public:
|
||||
PreparedPressureForceJacobianOperator(
|
||||
const BarotropicEquilibriumLayout &layout,
|
||||
const PreparedPressureForceOperator &preparedOperator
|
||||
);
|
||||
|
||||
void Mult(
|
||||
const mfem::Vector &direction,
|
||||
mfem::Vector &action
|
||||
) const override;
|
||||
|
||||
[[nodiscard]]
|
||||
const BarotropicEquilibriumLayout &GetLayout() const noexcept;
|
||||
|
||||
private:
|
||||
BarotropicEquilibriumLayout m_layout;
|
||||
|
||||
const PreparedPressureForceOperator &m_preparedOperator;
|
||||
};
|
||||
} // namespace mean_field::operators
|
||||
@@ -0,0 +1,149 @@
|
||||
module;
|
||||
|
||||
#include <compare>
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
|
||||
#include <mfem.hpp>
|
||||
|
||||
export module mean_field:operators.prepared_rotational_displacement_force;
|
||||
|
||||
export import :fem;
|
||||
export import :mapping.domain_mapper;
|
||||
export import :operators.context.rotational_displacement_force;
|
||||
export import :physics.rigid_rotation;
|
||||
export import :utils.blocks;
|
||||
|
||||
export namespace mean_field::operators {
|
||||
struct PreparedRotationalDisplacementForceReport final {
|
||||
context::rotational_displacement_force::RotationalDisplacementForcePreparationReport contextReport;
|
||||
|
||||
bool updatedRotation{false};
|
||||
bool preparedResidual{false};
|
||||
|
||||
[[nodiscard]] bool DidAnyWork() const noexcept {
|
||||
return contextReport.DidAnyWork() || updatedRotation || preparedResidual;
|
||||
}
|
||||
};
|
||||
|
||||
struct PreparedRotationalDisplacementForceColumnStatistics final {
|
||||
std::uint64_t applications{0};
|
||||
|
||||
constexpr auto operator<=>(const PreparedRotationalDisplacementForceColumnStatistics &) const = default;
|
||||
};
|
||||
|
||||
struct PreparedRotationalDisplacementForceCompleteStatistics final {
|
||||
std::uint64_t applications{0};
|
||||
|
||||
constexpr auto operator<=>(const PreparedRotationalDisplacementForceCompleteStatistics &) const = default;
|
||||
};
|
||||
|
||||
class PreparedRotationalDisplacementForceOperator final {
|
||||
public:
|
||||
PreparedRotationalDisplacementForceOperator(
|
||||
const fem::FEM &f,
|
||||
const mapping::DomainMapperStateless &domainMapper
|
||||
);
|
||||
|
||||
PreparedRotationalDisplacementForceOperator(const PreparedRotationalDisplacementForceOperator &) = delete;
|
||||
|
||||
PreparedRotationalDisplacementForceOperator &
|
||||
operator=(const PreparedRotationalDisplacementForceOperator &) = delete;
|
||||
|
||||
PreparedRotationalDisplacementForceOperator(PreparedRotationalDisplacementForceOperator &&) = delete;
|
||||
|
||||
PreparedRotationalDisplacementForceOperator &operator=(PreparedRotationalDisplacementForceOperator &&) = delete;
|
||||
|
||||
PreparedRotationalDisplacementForceReport Prepare(
|
||||
const context::rotational_displacement_force::RotationalDisplacementForceStateView &state,
|
||||
const context::rotational_displacement_force::RotationalDisplacementForceDependencies &dependencies,
|
||||
const physics::RigidRotation &rotation
|
||||
);
|
||||
|
||||
void BuildResidual(mfem::Vector &residual) const;
|
||||
|
||||
void ApplyDensityJacobianAction(
|
||||
const mfem::Vector &densityVariation,
|
||||
mfem::Vector &action
|
||||
) const;
|
||||
|
||||
void ApplyDisplacementJacobianAction(
|
||||
const mfem::Vector &displacementVariation,
|
||||
mfem::Vector &action
|
||||
) const;
|
||||
|
||||
void ApplyCompleteJacobianAction(
|
||||
const mfem::Vector &densityVariation,
|
||||
const mfem::Vector &displacementVariation,
|
||||
mfem::Vector &action
|
||||
) const;
|
||||
|
||||
[[nodiscard]] bool IsPrepared() const noexcept;
|
||||
|
||||
[[nodiscard]] const context::rotational_displacement_force::RotationalDisplacementForcePreparationStatistics &
|
||||
GetContextPreparationStatistics() const noexcept;
|
||||
|
||||
[[nodiscard]] std::uint64_t GetResidualPreparationCount() const noexcept;
|
||||
|
||||
[[nodiscard]] std::uint64_t GetResidualApplicationCount() const noexcept;
|
||||
|
||||
[[nodiscard]] const PreparedRotationalDisplacementForceColumnStatistics &
|
||||
GetDensityJacobianStatistics() const noexcept;
|
||||
|
||||
[[nodiscard]] const PreparedRotationalDisplacementForceColumnStatistics &
|
||||
GetDisplacementJacobianStatistics() const noexcept;
|
||||
|
||||
[[nodiscard]] const PreparedRotationalDisplacementForceCompleteStatistics &
|
||||
GetCompleteJacobianStatistics() const noexcept;
|
||||
|
||||
[[nodiscard]] const fem::FEM &GetFEM() const noexcept;
|
||||
|
||||
[[nodiscard]] const context::rotational_displacement_force::RotationalDisplacementForceLinearizationContext &
|
||||
GetContext() const noexcept;
|
||||
|
||||
private:
|
||||
void VerifyPrepared() const;
|
||||
|
||||
const fem::FEM &m_fem;
|
||||
const mapping::DomainMapperStateless &m_domainMapper;
|
||||
|
||||
context::rotational_displacement_force::RotationalDisplacementForceLinearizationContext m_context;
|
||||
|
||||
std::optional<physics::RigidRotation> m_rotation;
|
||||
mfem::Vector m_cachedResidual;
|
||||
|
||||
context::rotational_displacement_force::RotationalDisplacementForceDependencies m_preparedDependencies;
|
||||
|
||||
std::uint64_t m_residualPreparationCount{0};
|
||||
mutable std::uint64_t m_residualApplicationCount{0};
|
||||
|
||||
mutable PreparedRotationalDisplacementForceColumnStatistics m_densityJacobianStatistics;
|
||||
|
||||
mutable PreparedRotationalDisplacementForceColumnStatistics m_displacementJacobianStatistics;
|
||||
|
||||
mutable PreparedRotationalDisplacementForceCompleteStatistics m_completeJacobianStatistics;
|
||||
|
||||
bool m_isPrepared{false};
|
||||
};
|
||||
|
||||
using RotationalDisplacementForceLayout = utils::blocks::form_layout<utils::blocks::barotropic_equilibrium_form>;
|
||||
|
||||
class PreparedRotationalDisplacementForceJacobianOperator final : public mfem::Operator {
|
||||
public:
|
||||
PreparedRotationalDisplacementForceJacobianOperator(
|
||||
const RotationalDisplacementForceLayout &layout,
|
||||
const PreparedRotationalDisplacementForceOperator &preparedOperator
|
||||
);
|
||||
|
||||
void Mult(
|
||||
const mfem::Vector &direction,
|
||||
mfem::Vector &action
|
||||
) const override;
|
||||
|
||||
[[nodiscard]] const RotationalDisplacementForceLayout &GetLayout() const noexcept;
|
||||
|
||||
private:
|
||||
RotationalDisplacementForceLayout m_layout;
|
||||
const PreparedRotationalDisplacementForceOperator &m_preparedOperator;
|
||||
};
|
||||
} // namespace mean_field::operators
|
||||
@@ -0,0 +1,177 @@
|
||||
module;
|
||||
|
||||
#include <compare>
|
||||
#include <cstdint>
|
||||
|
||||
#include <mfem.hpp>
|
||||
|
||||
export module mean_field:operators.prepared_stellar_equilibrium;
|
||||
|
||||
export import :eos.polytrope;
|
||||
export import :fem;
|
||||
export import :field.mfem;
|
||||
export import :mapping.domain_mapper;
|
||||
export import :model.stellar;
|
||||
export import :operators.context.gravity_field;
|
||||
export import :operators.gravity_field;
|
||||
export import :operators.gravity_field_jacobian;
|
||||
export import :operators.prepared_barotropic_closure;
|
||||
export import :operators.prepared_displacement_residual;
|
||||
export import :operators.prepared_hydrostatic_equilibrium;
|
||||
export import :operators.prepared_mass_normalization;
|
||||
export import :physics.rigid_rotation;
|
||||
export import :utils.blocks;
|
||||
|
||||
export namespace mean_field::operators {
|
||||
struct StellarEquilibriumDependencyStamp final {
|
||||
std::uint64_t identity{0};
|
||||
std::uint64_t revision{0};
|
||||
|
||||
constexpr auto operator<=>(const StellarEquilibriumDependencyStamp &) const = default;
|
||||
};
|
||||
|
||||
struct StellarEquilibriumDependencies final {
|
||||
StellarEquilibriumDependencyStamp discretization;
|
||||
StellarEquilibriumDependencyStamp density;
|
||||
StellarEquilibriumDependencyStamp displacement;
|
||||
StellarEquilibriumDependencyStamp gravityGradient;
|
||||
StellarEquilibriumDependencyStamp gravityPotential;
|
||||
StellarEquilibriumDependencyStamp enthalpy;
|
||||
StellarEquilibriumDependencyStamp bernoulliConstant;
|
||||
StellarEquilibriumDependencyStamp rotation;
|
||||
StellarEquilibriumDependencyStamp targetMass;
|
||||
|
||||
constexpr auto operator<=>(const StellarEquilibriumDependencies &) const = default;
|
||||
};
|
||||
|
||||
struct PreparedStellarEquilibriumReport final {
|
||||
context::gravity_field::GravityFieldPreparationReport gravity;
|
||||
PreparedBarotropicClosureReport barotropicClosure;
|
||||
PreparedHydrostaticEquilibriumReport hydrostatic;
|
||||
PreparedDisplacementResidualReport displacement;
|
||||
PreparedMassNormalizationReport massNormalization;
|
||||
bool assembledResidual{false};
|
||||
|
||||
[[nodiscard]] bool DidAnyChildWork() const noexcept {
|
||||
return gravity.DidAnyWork() || barotropicClosure.DidAnyWork() || hydrostatic.DidAnyWork() ||
|
||||
displacement.DidAnyWork() || massNormalization.DidAnyWork();
|
||||
}
|
||||
|
||||
[[nodiscard]] bool DidAnyWork() const noexcept {
|
||||
return DidAnyChildWork() || assembledResidual;
|
||||
}
|
||||
};
|
||||
|
||||
struct PreparedStellarEquilibriumStatistics final {
|
||||
std::uint64_t residualAssemblies{0};
|
||||
std::uint64_t residualApplications{0};
|
||||
std::uint64_t jacobianApplications{0};
|
||||
|
||||
constexpr auto operator<=>(const PreparedStellarEquilibriumStatistics &) const = default;
|
||||
};
|
||||
|
||||
using StellarEquilibriumLayout = utils::blocks::form_layout<utils::blocks::barotropic_equilibrium_form>;
|
||||
|
||||
class PreparedStellarEquilibriumOperator final : public mfem::Operator {
|
||||
public:
|
||||
PreparedStellarEquilibriumOperator(
|
||||
fem::FEM &f,
|
||||
const mapping::DomainMapperStateless &domainMapper,
|
||||
const eos::Polytrope &equationOfState,
|
||||
double targetMass
|
||||
);
|
||||
|
||||
PreparedStellarEquilibriumOperator(
|
||||
fem::FEM &f,
|
||||
const mapping::DomainMapperStateless &domainMapper,
|
||||
const eos::Polytrope &equationOfState,
|
||||
const models::StellarModel &stellarModel
|
||||
);
|
||||
|
||||
PreparedStellarEquilibriumOperator(const PreparedStellarEquilibriumOperator &) = delete;
|
||||
PreparedStellarEquilibriumOperator &operator=(const PreparedStellarEquilibriumOperator &) = delete;
|
||||
PreparedStellarEquilibriumOperator(PreparedStellarEquilibriumOperator &&) = delete;
|
||||
PreparedStellarEquilibriumOperator &operator=(PreparedStellarEquilibriumOperator &&) = delete;
|
||||
|
||||
PreparedStellarEquilibriumReport Prepare(
|
||||
const mfem::Vector &state,
|
||||
const StellarEquilibriumDependencies &dependencies,
|
||||
const physics::RigidRotation &rotation
|
||||
);
|
||||
|
||||
void BuildResidual(mfem::Vector &residual) const;
|
||||
|
||||
void Mult(
|
||||
const mfem::Vector &direction,
|
||||
mfem::Vector &action
|
||||
) const override;
|
||||
|
||||
[[nodiscard]] bool IsPrepared() const noexcept;
|
||||
[[nodiscard]] double GetTargetMass() const noexcept;
|
||||
[[nodiscard]] const StellarEquilibriumLayout &GetLayout() const noexcept;
|
||||
[[nodiscard]] const StellarEquilibriumDependencies &GetDependencies() const;
|
||||
[[nodiscard]] const PreparedStellarEquilibriumStatistics &GetStatistics() const noexcept;
|
||||
|
||||
[[nodiscard]] const context::gravity_field::GravityFieldLinearizationContext &
|
||||
GetGravityContext() const noexcept;
|
||||
[[nodiscard]] const GravityFieldOperator &GetGravityOperator() const noexcept;
|
||||
[[nodiscard]] const GravityFieldJacobianOperator &GetGravityJacobianOperator() const noexcept;
|
||||
[[nodiscard]] const PreparedBarotropicClosureOperator &GetBarotropicClosureOperator() const noexcept;
|
||||
[[nodiscard]] const context::barotropic::BarotropicClosureLinearizationContext &
|
||||
GetBarotropicClosureContext() const noexcept;
|
||||
[[nodiscard]] const PreparedHydrostaticEquilibriumOperator &GetHydrostaticOperator() const noexcept;
|
||||
[[nodiscard]] const PreparedDisplacementResidualOperator &GetDisplacementOperator() const noexcept;
|
||||
[[nodiscard]] const PreparedMassNormalizationOperator &GetMassNormalizationOperator() const noexcept;
|
||||
|
||||
private:
|
||||
struct ConstructionData;
|
||||
|
||||
static ConstructionData MakeConstructionData(fem::FEM &f);
|
||||
|
||||
PreparedStellarEquilibriumOperator(
|
||||
fem::FEM &f,
|
||||
const mapping::DomainMapperStateless &domainMapper,
|
||||
const eos::Polytrope &equationOfState,
|
||||
double targetMass,
|
||||
ConstructionData constructionData
|
||||
);
|
||||
|
||||
void AssembleResidual();
|
||||
void VerifyPrepared() const;
|
||||
|
||||
StellarEquilibriumLayout m_layout;
|
||||
mfem::Array<int> m_gravityStateOffsets;
|
||||
|
||||
context::gravity_field::GravityFieldLinearizationContext m_gravityContext;
|
||||
GravityFieldJacobianOperator m_gravityJacobianOperator;
|
||||
GravityFieldOperator m_gravityOperator;
|
||||
|
||||
PreparedBarotropicClosureOperator m_barotropicClosureOperator;
|
||||
PreparedHydrostaticEquilibriumOperator m_hydrostaticOperator;
|
||||
PreparedDisplacementResidualOperator m_displacementOperator;
|
||||
PreparedMassNormalizationOperator m_massNormalizationOperator;
|
||||
|
||||
StellarEquilibriumDependencies m_preparedDependencies;
|
||||
mfem::Vector m_cachedResidual;
|
||||
double m_targetMass{0.0};
|
||||
|
||||
mutable PreparedStellarEquilibriumStatistics m_statistics;
|
||||
bool m_isPrepared{false};
|
||||
|
||||
field::FieldDofMap m_densityMap;
|
||||
field::FieldDofMap m_displacementMap;
|
||||
field::FieldDofMap m_gravityFluxMap;
|
||||
field::FieldDofMap m_gravityPotentialMap;
|
||||
field::FieldDofMap m_enthalpyMap;
|
||||
|
||||
mfem::Vector m_fullDensity;
|
||||
mfem::Vector m_fullEnthalpy;
|
||||
mfem::Vector m_fullGravityState;
|
||||
|
||||
mutable mfem::Vector m_fullDensityVariation;
|
||||
mutable mfem::Vector m_fullEnthalpyVariation;
|
||||
mutable mfem::Vector m_fullGravityDirection;
|
||||
|
||||
mutable mfem::Vector m_fullEnthalpyAction;
|
||||
};
|
||||
} // namespace mean_field::operators
|
||||
@@ -27,8 +27,7 @@ export namespace mean_field::physics {
|
||||
);
|
||||
}
|
||||
|
||||
if (!std::isfinite(polytropic_constant) ||
|
||||
polytropic_constant <= 0.0) {
|
||||
if (!std::isfinite(polytropic_constant) || polytropic_constant <= 0.0) {
|
||||
throw std::invalid_argument(
|
||||
std::format(
|
||||
"The polytropic constant must be finite and positive. "
|
||||
@@ -57,8 +56,7 @@ export namespace mean_field::physics {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
return m_polytropic_constant *
|
||||
std::pow(density, 1.0 + 1.0 / m_polytropic_index);
|
||||
return m_polytropic_constant * std::pow(density, 1.0 + 1.0 / m_polytropic_index);
|
||||
}
|
||||
|
||||
[[nodiscard]] double enthalpy_from_density(const double density) const {
|
||||
@@ -67,12 +65,10 @@ export namespace mean_field::physics {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
return m_enthalpy_scale *
|
||||
std::pow(density, 1.0 / m_polytropic_index);
|
||||
return m_enthalpy_scale * std::pow(density, 1.0 / m_polytropic_index);
|
||||
}
|
||||
|
||||
[[nodiscard]] double
|
||||
density_from_enthalpy(const double enthalpy) const {
|
||||
[[nodiscard]] double density_from_enthalpy(const double enthalpy) const {
|
||||
validate_finite(enthalpy, "enthalpy");
|
||||
|
||||
if (enthalpy <= 0.0) {
|
||||
@@ -82,20 +78,17 @@ export namespace mean_field::physics {
|
||||
return std::pow(enthalpy / m_enthalpy_scale, m_polytropic_index);
|
||||
}
|
||||
|
||||
[[nodiscard]] double
|
||||
pressure_from_enthalpy(const double enthalpy) const {
|
||||
[[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);
|
||||
return density_from_enthalpy(enthalpy) * enthalpy / (m_polytropic_index + 1.0);
|
||||
}
|
||||
|
||||
[[nodiscard]] double
|
||||
density_derivative_from_enthalpy(const double enthalpy) const {
|
||||
[[nodiscard]] double density_derivative_from_enthalpy(const double enthalpy) const {
|
||||
validate_finite(enthalpy, "enthalpy");
|
||||
if (enthalpy < 0.0) {
|
||||
return 0.0;
|
||||
@@ -106,13 +99,10 @@ export namespace mean_field::physics {
|
||||
}
|
||||
|
||||
return m_polytropic_index / m_enthalpy_scale *
|
||||
std::pow(
|
||||
enthalpy / m_enthalpy_scale, m_polytropic_index - 1.0
|
||||
);
|
||||
std::pow(enthalpy / m_enthalpy_scale, m_polytropic_index - 1.0);
|
||||
}
|
||||
|
||||
[[nodiscard]] double
|
||||
pressure_derivative_from_enthalpy(const double enthalpy) const {
|
||||
[[nodiscard]] double pressure_derivative_from_enthalpy(const double enthalpy) const {
|
||||
validate_finite(enthalpy, "enthalpy");
|
||||
|
||||
if (enthalpy <= 0.0) {
|
||||
@@ -122,8 +112,7 @@ export namespace mean_field::physics {
|
||||
return density_from_enthalpy(enthalpy);
|
||||
}
|
||||
|
||||
[[nodiscard]] double
|
||||
pressure_derivative_from_density(const double density) const {
|
||||
[[nodiscard]] double pressure_derivative_from_density(const double density) const {
|
||||
validate_nonnegativity(density, "density");
|
||||
if (density == 0.0) {
|
||||
return 0.0;
|
||||
|
||||
@@ -15,36 +15,28 @@ export namespace mean_field::physics {
|
||||
: m_angularVelocity(angularVelocity),
|
||||
m_center(center) {
|
||||
MFEM_VERIFY(
|
||||
m_angularVelocity.Size() == 3,
|
||||
"RigidRotation requires a three-dimensional "
|
||||
"angular-velocity vector."
|
||||
m_angularVelocity.Size() == 3, "RigidRotation requires a three-dimensional "
|
||||
"angular-velocity vector."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
m_center.Size() == 3,
|
||||
"RigidRotation requires a three-dimensional center."
|
||||
);
|
||||
MFEM_VERIFY(m_center.Size() == 3, "RigidRotation requires a three-dimensional center.");
|
||||
|
||||
for (int component = 0; component < 3; ++component) {
|
||||
MFEM_VERIFY(
|
||||
std::isfinite(m_angularVelocity(component)),
|
||||
"RigidRotation received a non-finite "
|
||||
"angular-velocity component."
|
||||
std::isfinite(m_angularVelocity(component)), "RigidRotation received a non-finite "
|
||||
"angular-velocity component."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
std::isfinite(m_center(component)),
|
||||
"RigidRotation received a non-finite center component."
|
||||
std::isfinite(m_center(component)), "RigidRotation received a non-finite center component."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] double
|
||||
potential(const mfem::Vector &physicalPosition) const {
|
||||
[[nodiscard]] double potential(const mfem::Vector &physicalPosition) const {
|
||||
MFEM_VERIFY(
|
||||
physicalPosition.Size() == 3,
|
||||
"RigidRotation::potential requires a "
|
||||
"three-dimensional position."
|
||||
physicalPosition.Size() == 3, "RigidRotation::potential requires a "
|
||||
"three-dimensional position."
|
||||
);
|
||||
|
||||
const double relativeX = physicalPosition(0) - m_center(0);
|
||||
@@ -53,14 +45,11 @@ export namespace mean_field::physics {
|
||||
|
||||
const double relativeZ = physicalPosition(2) - m_center(2);
|
||||
|
||||
const double crossX = m_angularVelocity(1) * relativeZ -
|
||||
m_angularVelocity(2) * relativeY;
|
||||
const double crossX = m_angularVelocity(1) * relativeZ - m_angularVelocity(2) * relativeY;
|
||||
|
||||
const double crossY = m_angularVelocity(2) * relativeX -
|
||||
m_angularVelocity(0) * relativeZ;
|
||||
const double crossY = m_angularVelocity(2) * relativeX - m_angularVelocity(0) * relativeZ;
|
||||
|
||||
const double crossZ = m_angularVelocity(0) * relativeY -
|
||||
m_angularVelocity(1) * relativeX;
|
||||
const double crossZ = m_angularVelocity(0) * relativeY - m_angularVelocity(1) * relativeX;
|
||||
|
||||
return 0.5 * (crossX * crossX + crossY * crossY + crossZ * crossZ);
|
||||
}
|
||||
@@ -70,48 +59,101 @@ export namespace mean_field::physics {
|
||||
const mfem::Vector &physicalPositionVariation
|
||||
) const {
|
||||
MFEM_VERIFY(
|
||||
physicalPosition.Size() == 3,
|
||||
"RigidRotation derivative requires a "
|
||||
"three-dimensional position."
|
||||
physicalPosition.Size() == 3, "RigidRotation derivative requires a "
|
||||
"three-dimensional position."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
physicalPositionVariation.Size() == 3,
|
||||
"RigidRotation derivative requires a "
|
||||
"three-dimensional direction."
|
||||
physicalPositionVariation.Size() == 3, "RigidRotation derivative requires a "
|
||||
"three-dimensional direction."
|
||||
);
|
||||
|
||||
double angularVelocitySquared = 0.0;
|
||||
double angularVelocityDotPosition = 0.0;
|
||||
|
||||
for (int component = 0; component < 3; ++component) {
|
||||
const double relativePosition =
|
||||
physicalPosition(component) - m_center(component);
|
||||
const double relativePosition = physicalPosition(component) - m_center(component);
|
||||
|
||||
angularVelocitySquared +=
|
||||
m_angularVelocity(component) * m_angularVelocity(component);
|
||||
angularVelocitySquared += m_angularVelocity(component) * m_angularVelocity(component);
|
||||
|
||||
angularVelocityDotPosition +=
|
||||
m_angularVelocity(component) * relativePosition;
|
||||
angularVelocityDotPosition += m_angularVelocity(component) * relativePosition;
|
||||
}
|
||||
|
||||
double derivative = 0.0;
|
||||
|
||||
for (int component = 0; component < 3; ++component) {
|
||||
const double relativePosition =
|
||||
physicalPosition(component) - m_center(component);
|
||||
const double relativePosition = physicalPosition(component) - m_center(component);
|
||||
|
||||
const double gradientComponent =
|
||||
angularVelocitySquared * relativePosition -
|
||||
angularVelocityDotPosition * m_angularVelocity(component);
|
||||
const double gradientComponent = angularVelocitySquared * relativePosition -
|
||||
angularVelocityDotPosition * m_angularVelocity(component);
|
||||
|
||||
derivative +=
|
||||
gradientComponent * physicalPositionVariation(component);
|
||||
derivative += gradientComponent * physicalPositionVariation(component);
|
||||
}
|
||||
|
||||
return derivative;
|
||||
}
|
||||
|
||||
/*
|
||||
* Gradient of the positive rigid-rotation potential
|
||||
*
|
||||
* Psi = 0.5 |Omega x (x - x_0)|^2.
|
||||
*
|
||||
* This points away from the rotation axis. The rotational
|
||||
* displacement residual uses its negative.
|
||||
*/
|
||||
void potential_gradient(
|
||||
const mfem::Vector &physicalPosition,
|
||||
mfem::Vector &gradient
|
||||
) const {
|
||||
MFEM_VERIFY(
|
||||
physicalPosition.Size() == 3, "RigidRotation::potential_gradient requires a "
|
||||
"three-dimensional position."
|
||||
);
|
||||
|
||||
double angularVelocitySquared = 0.0;
|
||||
double angularVelocityDotPosition = 0.0;
|
||||
|
||||
for (int component = 0; component < 3; ++component) {
|
||||
const double relativePosition = physicalPosition(component) - m_center(component);
|
||||
|
||||
angularVelocitySquared += m_angularVelocity(component) * m_angularVelocity(component);
|
||||
|
||||
angularVelocityDotPosition += m_angularVelocity(component) * relativePosition;
|
||||
}
|
||||
|
||||
gradient.SetSize(3);
|
||||
|
||||
for (int component = 0; component < 3; ++component) {
|
||||
const double relativePosition = physicalPosition(component) - m_center(component);
|
||||
|
||||
gradient(component) = angularVelocitySquared * relativePosition -
|
||||
angularVelocityDotPosition * m_angularVelocity(component);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Hessian action of Psi. The Hessian is constant for rigid
|
||||
* rotation, so only the physical-position direction is required.
|
||||
*/
|
||||
void potential_gradient_directional_derivative(
|
||||
const mfem::Vector &physicalPositionVariation,
|
||||
mfem::Vector &gradientVariation
|
||||
) const {
|
||||
MFEM_VERIFY(
|
||||
physicalPositionVariation.Size() == 3, "RigidRotation gradient derivative requires a "
|
||||
"three-dimensional direction."
|
||||
);
|
||||
|
||||
const double angularVelocitySquared = m_angularVelocity * m_angularVelocity;
|
||||
|
||||
const double angularVelocityDotVariation = m_angularVelocity * physicalPositionVariation;
|
||||
|
||||
gradientVariation.SetSize(3);
|
||||
gradientVariation = physicalPositionVariation;
|
||||
gradientVariation *= angularVelocitySquared;
|
||||
gradientVariation.Add(-angularVelocityDotVariation, m_angularVelocity);
|
||||
}
|
||||
|
||||
[[nodiscard]] const mfem::Vector &angular_velocity() const noexcept {
|
||||
return m_angularVelocity;
|
||||
}
|
||||
|
||||
@@ -108,12 +108,9 @@ export namespace mean_field::quadrature {
|
||||
const Query &query,
|
||||
const mfem::Geometry::Type geometry
|
||||
) const {
|
||||
const Resolution resolution = policy.resolve(query);
|
||||
const mfem::IntegrationRule &integration_rule =
|
||||
mfem::IntRules.Get(geometry, resolution.order);
|
||||
return {
|
||||
.resolution = resolution, .integration_rule = &integration_rule
|
||||
};
|
||||
const Resolution resolution = policy.resolve(query);
|
||||
const mfem::IntegrationRule &integration_rule = mfem::IntRules.Get(geometry, resolution.order);
|
||||
return {.resolution = resolution, .integration_rule = &integration_rule};
|
||||
}
|
||||
|
||||
MfemRule RuleFactory::get(
|
||||
@@ -146,12 +143,10 @@ export namespace mean_field::quadrature {
|
||||
"The H(div) element order does not match the registered gravity "
|
||||
"flux."
|
||||
);
|
||||
const Query query =
|
||||
GravityField::make_query<field::Gravity::Form::HDivMass>(
|
||||
role, transformation.OrderW(), {}, domain, mapping
|
||||
);
|
||||
const auto [resolution, integration_rule] =
|
||||
get(query, element.GetGeomType());
|
||||
const Query query = GravityField::make_query<field::Gravity::Form::HDivMass>(
|
||||
role, transformation.OrderW(), {}, domain, mapping
|
||||
);
|
||||
const auto [resolution, integration_rule] = get(query, element.GetGeomType());
|
||||
integrator.SetIntegrationRule(*integration_rule);
|
||||
return resolution;
|
||||
}
|
||||
@@ -176,13 +171,11 @@ export namespace mean_field::quadrature {
|
||||
"The divergence test element does not match the registered "
|
||||
"gravity potential."
|
||||
);
|
||||
const Query query =
|
||||
GravityField::make_query<field::Gravity::Form::DivergenceCoupling>(
|
||||
role, transformation.OrderW(), {}, domain, mapping
|
||||
);
|
||||
const Query query = GravityField::make_query<field::Gravity::Form::DivergenceCoupling>(
|
||||
role, transformation.OrderW(), {}, domain, mapping
|
||||
);
|
||||
|
||||
const auto [resolution, integration_rule] =
|
||||
get(query, trial_element.GetGeomType());
|
||||
const auto [resolution, integration_rule] = get(query, trial_element.GetGeomType());
|
||||
integrator.SetIntegrationRule(*integration_rule);
|
||||
return resolution;
|
||||
}
|
||||
@@ -200,12 +193,8 @@ export namespace mean_field::quadrature {
|
||||
"The boundary element does not match the registered gravity-flux "
|
||||
"normal trace."
|
||||
);
|
||||
const Query query =
|
||||
GravityField::make_query<field::Gravity::Form::Boundary>(
|
||||
role, 0, {}, domain, mapping
|
||||
);
|
||||
const auto [resolution, integration_rule] =
|
||||
get(query, boundary_element.GetGeomType());
|
||||
const Query query = GravityField::make_query<field::Gravity::Form::Boundary>(role, 0, {}, domain, mapping);
|
||||
const auto [resolution, integration_rule] = get(query, boundary_element.GetGeomType());
|
||||
integrator.SetIntegrationRule(*integration_rule);
|
||||
return resolution;
|
||||
}
|
||||
@@ -230,13 +219,11 @@ export namespace mean_field::quadrature {
|
||||
"The gravity-source coefficient order does not match the "
|
||||
"registered density field."
|
||||
);
|
||||
const Query query =
|
||||
GravityField::make_query<field::Gravity::Form::SourceLinear>(
|
||||
role, transformation.OrderW(), {}, domain, mapping
|
||||
);
|
||||
const Query query = GravityField::make_query<field::Gravity::Form::SourceLinear>(
|
||||
role, transformation.OrderW(), {}, domain, mapping
|
||||
);
|
||||
|
||||
const auto [resolution, integration_rule] =
|
||||
get(query, test_element.GetGeomType());
|
||||
const auto [resolution, integration_rule] = get(query, test_element.GetGeomType());
|
||||
integrator.SetIntegrationRule(*integration_rule);
|
||||
return resolution;
|
||||
}
|
||||
@@ -271,17 +258,14 @@ export namespace mean_field::quadrature {
|
||||
"gravity potential."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
coefficient_order == 0,
|
||||
"The mapped gravity-source coefficient order must be zero; "
|
||||
"density order is supplied by the registered trial field."
|
||||
coefficient_order == 0, "The mapped gravity-source coefficient order must be zero; "
|
||||
"density order is supplied by the registered trial field."
|
||||
);
|
||||
const Query query = GravityField::make_query<field::Gravity::Form::SourceProjection>(
|
||||
role, transformation.OrderW(), {}, domain, mapping
|
||||
);
|
||||
const Query query =
|
||||
GravityField::make_query<field::Gravity::Form::SourceProjection>(
|
||||
role, transformation.OrderW(), {}, domain, mapping
|
||||
);
|
||||
|
||||
const auto [resolution, integration_rule] =
|
||||
get(query, transformation.GetGeometryType());
|
||||
const auto [resolution, integration_rule] = get(query, transformation.GetGeometryType());
|
||||
integrator.SetIntRule(integration_rule);
|
||||
return resolution;
|
||||
}
|
||||
@@ -307,8 +291,7 @@ export namespace mean_field::quadrature {
|
||||
.geometry_weight_order = transformation.OrderW()
|
||||
};
|
||||
|
||||
const auto [resolution, integration_rule] =
|
||||
get(query, velocity_element.GetGeomType());
|
||||
const auto [resolution, integration_rule] = get(query, velocity_element.GetGeomType());
|
||||
integrator.SetIntegrationRule(*integration_rule);
|
||||
return resolution;
|
||||
}
|
||||
@@ -323,8 +306,7 @@ export namespace mean_field::quadrature {
|
||||
const utils::DOMAINS domain,
|
||||
const MappingKind mapping
|
||||
) const {
|
||||
const auto [resolution, integration_rule] =
|
||||
get(term, role, geometry, base_order, domain, mapping);
|
||||
const auto [resolution, integration_rule] = get(term, role, geometry, base_order, domain, mapping);
|
||||
integrator.SetIntegrationRule(*integration_rule);
|
||||
return resolution;
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ export namespace mean_field::quadrature {
|
||||
gravity_hdiv_mass,
|
||||
gravity_divergence,
|
||||
gravity_source,
|
||||
gravity_force,
|
||||
gravity_boundary,
|
||||
centrifugal,
|
||||
density_projection,
|
||||
@@ -32,12 +33,7 @@ export namespace mean_field::quadrature {
|
||||
error_norm
|
||||
};
|
||||
|
||||
enum class QuadratureRole {
|
||||
discretization,
|
||||
preconditioner,
|
||||
diagnostic,
|
||||
projection
|
||||
};
|
||||
enum class QuadratureRole { discretization, preconditioner, diagnostic, projection };
|
||||
|
||||
enum class MappingKind { none, affine, general, kelvin };
|
||||
|
||||
@@ -59,6 +55,7 @@ export namespace mean_field::quadrature {
|
||||
RuleControl gravity_hdiv_mass;
|
||||
RuleControl gravity_divergence;
|
||||
RuleControl gravity_source;
|
||||
RuleControl gravity_force;
|
||||
RuleControl gravity_boundary;
|
||||
RuleControl centrifugal;
|
||||
RuleControl density_projection;
|
||||
@@ -130,6 +127,7 @@ export namespace mean_field::quadrature {
|
||||
QuadratureTermOptions gravity_hdiv_mass;
|
||||
QuadratureTermOptions gravity_divergence;
|
||||
QuadratureTermOptions gravity_source;
|
||||
QuadratureTermOptions gravity_force;
|
||||
QuadratureTermOptions gravity_boundary;
|
||||
QuadratureTermOptions centrifugal;
|
||||
QuadratureTermOptions density_projection;
|
||||
@@ -211,35 +209,20 @@ export namespace mean_field::quadrature {
|
||||
|
||||
if (fixed_order.has_value()) {
|
||||
if (*fixed_order < 0) {
|
||||
throw std::invalid_argument(
|
||||
"Quadrature fixed order cannot be negative."
|
||||
);
|
||||
throw std::invalid_argument("Quadrature fixed order cannot be negative.");
|
||||
}
|
||||
|
||||
return {
|
||||
.base_order = base_order,
|
||||
.boost = 0,
|
||||
.order = *fixed_order,
|
||||
.used_fixed_order = true
|
||||
};
|
||||
return {.base_order = base_order, .boost = 0, .order = *fixed_order, .used_fixed_order = true};
|
||||
}
|
||||
|
||||
const int boost =
|
||||
rule_set.fallback.boost + role_control.boost + term_control.boost;
|
||||
const int boost = rule_set.fallback.boost + role_control.boost + term_control.boost;
|
||||
const int order = base_order + boost;
|
||||
|
||||
if (order < 0) {
|
||||
throw std::invalid_argument(
|
||||
"Resolved quadrature order cannot be negative."
|
||||
);
|
||||
throw std::invalid_argument("Resolved quadrature order cannot be negative.");
|
||||
}
|
||||
|
||||
return {
|
||||
.base_order = base_order,
|
||||
.boost = boost,
|
||||
.order = order,
|
||||
.used_fixed_order = false
|
||||
};
|
||||
return {.base_order = base_order, .boost = boost, .order = order, .used_fixed_order = false};
|
||||
}
|
||||
const RuleControl &Policy::get_control(const Term term) const {
|
||||
switch (term) {
|
||||
@@ -249,6 +232,8 @@ export namespace mean_field::quadrature {
|
||||
return rule_set.gravity_divergence;
|
||||
case Term::gravity_source:
|
||||
return rule_set.gravity_source;
|
||||
case Term::gravity_force:
|
||||
return rule_set.gravity_force;
|
||||
case Term::gravity_boundary:
|
||||
return rule_set.gravity_boundary;
|
||||
case Term::centrifugal:
|
||||
@@ -289,18 +274,14 @@ export namespace mean_field::quadrature {
|
||||
int Policy::compute_base_order(const Query &query) {
|
||||
if (query.base_order.has_value()) {
|
||||
if (*query.base_order < 0) {
|
||||
throw std::invalid_argument(
|
||||
"Quadrature base order cannot be negative."
|
||||
);
|
||||
throw std::invalid_argument("Quadrature base order cannot be negative.");
|
||||
}
|
||||
return *query.base_order;
|
||||
}
|
||||
|
||||
if (query.trial_order < 0 || query.test_order < 0 ||
|
||||
query.coefficient_order < 0 || query.geometry_weight_order < 0) {
|
||||
throw std::invalid_argument(
|
||||
"Quadrature query orders cannot be negative."
|
||||
);
|
||||
if (query.trial_order < 0 || query.test_order < 0 || query.coefficient_order < 0 ||
|
||||
query.geometry_weight_order < 0) {
|
||||
throw std::invalid_argument("Quadrature query orders cannot be negative.");
|
||||
}
|
||||
|
||||
int trial_order = query.trial_order;
|
||||
@@ -308,12 +289,10 @@ export namespace mean_field::quadrature {
|
||||
trial_order = std::max(0, trial_order - 1);
|
||||
}
|
||||
|
||||
return trial_order + query.test_order + query.coefficient_order +
|
||||
query.geometry_weight_order;
|
||||
return trial_order + query.test_order + query.coefficient_order + query.geometry_weight_order;
|
||||
}
|
||||
|
||||
const RuleControl &
|
||||
Policy::get_role_control(const QuadratureRole role) const {
|
||||
const RuleControl &Policy::get_role_control(const QuadratureRole role) const {
|
||||
switch (role) {
|
||||
case QuadratureRole::discretization:
|
||||
return rule_set.roles.discretization;
|
||||
|
||||
64
libmeanfield/interface/surface/isobaric.cppm
Normal file
64
libmeanfield/interface/surface/isobaric.cppm
Normal file
@@ -0,0 +1,64 @@
|
||||
module;
|
||||
|
||||
#include <cmath>
|
||||
#include <format>
|
||||
#include <stdexcept>
|
||||
|
||||
export module mean_field:surface.isobaric;
|
||||
|
||||
export import :surface.base;
|
||||
|
||||
export namespace mean_field::surface {
|
||||
class Isobaric final : public SurfaceBase {
|
||||
public:
|
||||
explicit Isobaric(const double targetPressure = 0.0) : m_targetPressure(targetPressure) {
|
||||
validateTargetPressure();
|
||||
}
|
||||
|
||||
[[nodiscard]] double targetPressure() const noexcept {
|
||||
return m_targetPressure;
|
||||
}
|
||||
|
||||
[[nodiscard]] ResolvedSurfaceCondition
|
||||
resolve(const mean_field::eos::EquationOfState &equationOfState) const override {
|
||||
return ResolvedSurfaceCondition{resolveTargetEnthalpy(equationOfState)};
|
||||
}
|
||||
|
||||
void validate(const mean_field::eos::EquationOfState &equationOfState) const override {
|
||||
static_cast<void>(resolveTargetEnthalpy(equationOfState));
|
||||
}
|
||||
|
||||
private:
|
||||
[[nodiscard]] double resolveTargetEnthalpy(const mean_field::eos::EquationOfState &equationOfState) const {
|
||||
validateTargetPressure();
|
||||
|
||||
const double targetEnthalpy = equationOfState.enthalpy_from_pressure(m_targetPressure);
|
||||
|
||||
if (!std::isfinite(targetEnthalpy) || targetEnthalpy < 0.0) {
|
||||
throw std::domain_error(
|
||||
std::format(
|
||||
"The equation of state resolved the isobaric "
|
||||
"target P = {} to the invalid enthalpy h = {}.",
|
||||
m_targetPressure, targetEnthalpy
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return targetEnthalpy;
|
||||
}
|
||||
|
||||
void validateTargetPressure() const {
|
||||
if (!std::isfinite(m_targetPressure) || m_targetPressure < 0.0) {
|
||||
throw std::invalid_argument(
|
||||
std::format(
|
||||
"The target surface pressure must be finite and "
|
||||
"non-negative. Instead P = {} was provided.",
|
||||
m_targetPressure
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
double m_targetPressure;
|
||||
};
|
||||
} // namespace mean_field::surface
|
||||
53
libmeanfield/interface/surface/surface_base.cppm
Normal file
53
libmeanfield/interface/surface/surface_base.cppm
Normal file
@@ -0,0 +1,53 @@
|
||||
module;
|
||||
|
||||
#include <cmath>
|
||||
#include <stdexcept>
|
||||
|
||||
export module mean_field:surface.base;
|
||||
|
||||
export import :eos.base;
|
||||
|
||||
export namespace mean_field::surface {
|
||||
struct ResolvedSurfaceCondition final {
|
||||
double targetEnthalpy{0.0};
|
||||
|
||||
explicit ResolvedSurfaceCondition(const double requestedTargetEnthalpy)
|
||||
: targetEnthalpy(requestedTargetEnthalpy) {
|
||||
if (!std::isfinite(targetEnthalpy) || targetEnthalpy < 0.0) {
|
||||
throw std::invalid_argument(
|
||||
"A resolved surface enthalpy must be finite and "
|
||||
"non-negative."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] double residual(const double enthalpy) const {
|
||||
if (!std::isfinite(enthalpy)) {
|
||||
throw std::invalid_argument("A surface enthalpy value must be finite.");
|
||||
}
|
||||
|
||||
return enthalpy - targetEnthalpy;
|
||||
}
|
||||
|
||||
[[nodiscard]] static double jacobianAction(const double enthalpyVariation) {
|
||||
if (!std::isfinite(enthalpyVariation)) {
|
||||
throw std::invalid_argument("A surface enthalpy variation must be finite.");
|
||||
}
|
||||
|
||||
return enthalpyVariation;
|
||||
}
|
||||
};
|
||||
|
||||
class SurfaceBase {
|
||||
public:
|
||||
virtual ~SurfaceBase() = default;
|
||||
|
||||
[[nodiscard]] virtual ResolvedSurfaceCondition
|
||||
resolve(const mean_field::eos::EquationOfState &equationOfState) const = 0;
|
||||
|
||||
virtual void validate(const mean_field::eos::EquationOfState &equationOfState) const = 0;
|
||||
|
||||
protected:
|
||||
SurfaceBase() = default;
|
||||
};
|
||||
} // namespace mean_field::surface
|
||||
@@ -23,8 +23,7 @@ export namespace mean_field::utils::blocks {
|
||||
struct field { };
|
||||
|
||||
template <typename Residual, typename... Values> struct block_row { };
|
||||
template <int index_value>
|
||||
struct residual_block final : residual_block_base {
|
||||
template <int index_value> struct residual_block final : residual_block_base {
|
||||
static constexpr int index = index_value;
|
||||
|
||||
// ReSharper disable once CppNonExplicitConversionOperator
|
||||
@@ -110,43 +109,33 @@ export namespace mean_field::utils::blocks {
|
||||
|
||||
template <typename Query, typename List> struct contains_type;
|
||||
|
||||
template <typename Query>
|
||||
struct contains_type<Query, type_list<>> : std::false_type { };
|
||||
template <typename Query> struct contains_type<Query, type_list<>> : std::false_type { };
|
||||
|
||||
template <typename Query, typename Head, typename... Tail>
|
||||
struct contains_type<Query, type_list<Head, Tail...>>
|
||||
: std::conditional_t<
|
||||
std::is_same_v<Query, Head>,
|
||||
std::true_type,
|
||||
contains_type<Query, type_list<Tail...>>> { };
|
||||
: std::conditional_t<std::is_same_v<Query, Head>, std::true_type, contains_type<Query, type_list<Tail...>>> { };
|
||||
|
||||
template <typename Query, typename List>
|
||||
inline constexpr bool contains_type_v = contains_type<Query, List>::value;
|
||||
template <typename Query, typename List> inline constexpr bool contains_type_v = contains_type<Query, List>::value;
|
||||
|
||||
template <typename Query, typename List> struct type_count;
|
||||
|
||||
template <typename Query>
|
||||
struct type_count<Query, type_list<>> : std::integral_constant<int, 0> { };
|
||||
template <typename Query> struct type_count<Query, type_list<>> : std::integral_constant<int, 0> { };
|
||||
|
||||
template <typename Query, typename Head, typename... Tail>
|
||||
struct type_count<Query, type_list<Head, Tail...>>
|
||||
: std::integral_constant<
|
||||
int,
|
||||
(std::is_same_v<Query, Head> ? 1 : 0) +
|
||||
type_count<Query, type_list<Tail...>>::value> { };
|
||||
(std::is_same_v<Query, Head> ? 1 : 0) + type_count<Query, type_list<Tail...>>::value> { };
|
||||
|
||||
template <typename Query, typename List>
|
||||
inline constexpr int type_count_v = type_count<Query, List>::value;
|
||||
template <typename Query, typename List> inline constexpr int type_count_v = type_count<Query, List>::value;
|
||||
|
||||
template <typename List> struct types_are_unique;
|
||||
|
||||
template <typename... Types>
|
||||
struct types_are_unique<type_list<Types...>>
|
||||
: std::bool_constant<
|
||||
((type_count_v<Types, type_list<Types...>> == 1) && ...)> { };
|
||||
: std::bool_constant<((type_count_v<Types, type_list<Types...>> == 1) && ...)> { };
|
||||
|
||||
template <typename List>
|
||||
inline constexpr bool types_are_unique_v = types_are_unique<List>::value;
|
||||
template <typename List> inline constexpr bool types_are_unique_v = types_are_unique<List>::value;
|
||||
|
||||
template <typename Row> struct block_row_traits {
|
||||
using residual = void;
|
||||
@@ -156,8 +145,7 @@ export namespace mean_field::utils::blocks {
|
||||
static constexpr bool is_block_row = false;
|
||||
};
|
||||
|
||||
template <typename Residual, typename... Values>
|
||||
struct block_row_traits<block_row<Residual, Values...>> {
|
||||
template <typename Residual, typename... Values> struct block_row_traits<block_row<Residual, Values...>> {
|
||||
using residual = Residual;
|
||||
using values = type_list<Values...>;
|
||||
|
||||
@@ -167,19 +155,15 @@ export namespace mean_field::utils::blocks {
|
||||
|
||||
template <typename Query, typename List> struct type_index;
|
||||
|
||||
template <typename Query, typename... Tail>
|
||||
struct type_index<Query, type_list<Query, Tail...>> {
|
||||
template <typename Query, typename... Tail> struct type_index<Query, type_list<Query, Tail...>> {
|
||||
static constexpr int value = 0;
|
||||
};
|
||||
|
||||
template <typename Query, typename Head, typename... Tail>
|
||||
struct type_index<Query, type_list<Head, Tail...>> {
|
||||
static constexpr int value =
|
||||
1 + type_index<Query, type_list<Tail...>>::value;
|
||||
template <typename Query, typename Head, typename... Tail> struct type_index<Query, type_list<Head, Tail...>> {
|
||||
static constexpr int value = 1 + type_index<Query, type_list<Tail...>>::value;
|
||||
};
|
||||
|
||||
template <typename Query, typename List>
|
||||
inline constexpr int type_index_v = type_index<Query, List>::value;
|
||||
template <typename Query, typename List> inline constexpr int type_index_v = type_index<Query, List>::value;
|
||||
|
||||
template <typename ValueBlocks, typename ResidualBlocks> struct block_form {
|
||||
using value_blocks = ValueBlocks;
|
||||
@@ -192,36 +176,22 @@ export namespace mean_field::utils::blocks {
|
||||
template <typename Form> struct block_form_is_valid : std::false_type { };
|
||||
|
||||
template <typename... Values, typename... Residuals>
|
||||
struct block_form_is_valid<
|
||||
block_form<type_list<Values...>, type_list<Residuals...>>>
|
||||
struct block_form_is_valid<block_form<type_list<Values...>, type_list<Residuals...>>>
|
||||
: std::bool_constant<
|
||||
(std::is_base_of_v<value_block_base, Values> && ...) &&
|
||||
(std::is_base_of_v<residual_block_base, Residuals> && ...) &&
|
||||
types_are_unique_v<type_list<Values...>> &&
|
||||
(std::is_base_of_v<residual_block_base, Residuals> && ...) && types_are_unique_v<type_list<Values...>> &&
|
||||
types_are_unique_v<type_list<Residuals...>>> { };
|
||||
|
||||
template <typename Form>
|
||||
inline constexpr bool block_form_is_valid_v =
|
||||
block_form_is_valid<Form>::value;
|
||||
template <typename Form> inline constexpr bool block_form_is_valid_v = block_form_is_valid<Form>::value;
|
||||
|
||||
template <typename Row, typename ValueBlocks, typename ResidualBlocks>
|
||||
struct block_row_is_valid : std::false_type { };
|
||||
|
||||
template <
|
||||
typename Residual,
|
||||
typename... Values,
|
||||
typename ValueBlocks,
|
||||
typename ResidualBlocks>
|
||||
struct block_row_is_valid<
|
||||
block_row<Residual, Values...>,
|
||||
ValueBlocks,
|
||||
ResidualBlocks>
|
||||
template <typename Residual, typename... Values, typename ValueBlocks, typename ResidualBlocks>
|
||||
struct block_row_is_valid<block_row<Residual, Values...>, ValueBlocks, ResidualBlocks>
|
||||
: std::bool_constant<
|
||||
std::is_base_of_v<residual_block_base, Residual> &&
|
||||
contains_type_v<Residual, ResidualBlocks> &&
|
||||
((std::is_base_of_v<value_block_base, Values> &&
|
||||
contains_type_v<Values, ValueBlocks>) &&
|
||||
...) &&
|
||||
std::is_base_of_v<residual_block_base, Residual> && contains_type_v<Residual, ResidualBlocks> &&
|
||||
((std::is_base_of_v<value_block_base, Values> && contains_type_v<Values, ValueBlocks>) && ...) &&
|
||||
types_are_unique_v<type_list<Values...>>> { };
|
||||
|
||||
template <typename Rows> struct row_residual_list;
|
||||
@@ -230,73 +200,50 @@ export namespace mean_field::utils::blocks {
|
||||
using type = type_list<typename block_row_traits<Rows>::residual...>;
|
||||
};
|
||||
|
||||
template <typename Rows>
|
||||
using row_residual_list_t = typename row_residual_list<Rows>::type;
|
||||
template <typename Rows> using row_residual_list_t = typename row_residual_list<Rows>::type;
|
||||
|
||||
template <typename Form, typename JacobianForm>
|
||||
struct jacobian_form_is_valid : std::false_type { };
|
||||
template <typename Form, typename JacobianForm> struct jacobian_form_is_valid : std::false_type { };
|
||||
|
||||
template <typename... Values, typename... Residuals, typename... Rows>
|
||||
struct jacobian_form_is_valid<
|
||||
block_form<type_list<Values...>, type_list<Residuals...>>,
|
||||
type_list<Rows...>> {
|
||||
using form_type =
|
||||
block_form<type_list<Values...>, type_list<Residuals...>>;
|
||||
struct jacobian_form_is_valid<block_form<type_list<Values...>, type_list<Residuals...>>, type_list<Rows...>> {
|
||||
using form_type = block_form<type_list<Values...>, type_list<Residuals...>>;
|
||||
|
||||
using value_blocks = type_list<Values...>;
|
||||
using residual_blocks = type_list<Residuals...>;
|
||||
using rows = type_list<Rows...>;
|
||||
using value_blocks = type_list<Values...>;
|
||||
using residual_blocks = type_list<Residuals...>;
|
||||
using rows = type_list<Rows...>;
|
||||
|
||||
static constexpr bool value =
|
||||
block_form_is_valid_v<form_type> &&
|
||||
(block_row_is_valid<Rows, value_blocks, residual_blocks>::value &&
|
||||
...) &&
|
||||
std::is_same_v<row_residual_list_t<rows>, residual_blocks>;
|
||||
static constexpr bool value = block_form_is_valid_v<form_type> &&
|
||||
(block_row_is_valid<Rows, value_blocks, residual_blocks>::value && ...) &&
|
||||
std::is_same_v<row_residual_list_t<rows>, residual_blocks>;
|
||||
};
|
||||
|
||||
template <typename Form, typename JacobianForm>
|
||||
inline constexpr bool jacobian_form_is_valid_v =
|
||||
jacobian_form_is_valid<Form, JacobianForm>::value;
|
||||
inline constexpr bool jacobian_form_is_valid_v = jacobian_form_is_valid<Form, JacobianForm>::value;
|
||||
|
||||
template <typename Form, typename JacobianForm>
|
||||
concept valid_jacobian_form = jacobian_form_is_valid_v<Form, JacobianForm>;
|
||||
|
||||
template <typename Residual, typename Value, typename JacobianForm>
|
||||
struct has_jacobian_coupling;
|
||||
template <typename Residual, typename Value, typename JacobianForm> struct has_jacobian_coupling;
|
||||
|
||||
template <typename Residual, typename Value>
|
||||
struct has_jacobian_coupling<Residual, Value, type_list<>>
|
||||
: std::false_type { };
|
||||
struct has_jacobian_coupling<Residual, Value, type_list<>> : std::false_type { };
|
||||
|
||||
template <
|
||||
typename Residual,
|
||||
typename Value,
|
||||
typename RowResidual,
|
||||
typename... RowValues,
|
||||
typename... RemainingRows>
|
||||
struct has_jacobian_coupling<
|
||||
Residual,
|
||||
Value,
|
||||
type_list<block_row<RowResidual, RowValues...>, RemainingRows...>>
|
||||
template <typename Residual, typename Value, typename RowResidual, typename... RowValues, typename... RemainingRows>
|
||||
struct has_jacobian_coupling<Residual, Value, type_list<block_row<RowResidual, RowValues...>, RemainingRows...>>
|
||||
: std::conditional_t<
|
||||
std::is_same_v<Residual, RowResidual>,
|
||||
std::bool_constant<(std::is_same_v<Value, RowValues> || ...)>,
|
||||
has_jacobian_coupling<
|
||||
Residual,
|
||||
Value,
|
||||
type_list<RemainingRows...>>> { };
|
||||
has_jacobian_coupling<Residual, Value, type_list<RemainingRows...>>> { };
|
||||
|
||||
template <typename Residual, typename Value, typename JacobianForm>
|
||||
inline constexpr bool has_jacobian_coupling_v =
|
||||
has_jacobian_coupling<Residual, Value, JacobianForm>::value;
|
||||
inline constexpr bool has_jacobian_coupling_v = has_jacobian_coupling<Residual, Value, JacobianForm>::value;
|
||||
|
||||
template <
|
||||
typename Form,
|
||||
typename Term>
|
||||
consteval auto get_value_block(const Term &) {
|
||||
using value_type = typename Term::value;
|
||||
constexpr int index =
|
||||
type_index_v<value_type, typename Form::value_blocks>;
|
||||
using value_type = typename Term::value;
|
||||
constexpr int index = type_index_v<value_type, typename Form::value_blocks>;
|
||||
return value_block<index>{};
|
||||
}
|
||||
|
||||
@@ -305,8 +252,7 @@ export namespace mean_field::utils::blocks {
|
||||
typename Term>
|
||||
consteval auto get_residual_block(const Term &) {
|
||||
using residual_type = typename Term::residual;
|
||||
constexpr int index =
|
||||
type_index_v<residual_type, typename Form::residual_blocks>;
|
||||
constexpr int index = type_index_v<residual_type, typename Form::residual_blocks>;
|
||||
return residual_block<index>{};
|
||||
}
|
||||
|
||||
@@ -320,32 +266,24 @@ export namespace mean_field::utils::blocks {
|
||||
int,
|
||||
Form::residual_block_count> &residual_sizes
|
||||
) {
|
||||
build_offsets(
|
||||
m_value_offsets, value_sizes, typename Form::value_blocks{}
|
||||
);
|
||||
build_offsets(m_value_offsets, value_sizes, typename Form::value_blocks{});
|
||||
|
||||
build_offsets(
|
||||
m_residual_offsets, residual_sizes,
|
||||
typename Form::residual_blocks{}
|
||||
);
|
||||
build_offsets(m_residual_offsets, residual_sizes, typename Form::residual_blocks{});
|
||||
}
|
||||
|
||||
template <int index> [[nodiscard]] int size(value_block<index>) const {
|
||||
return m_value_offsets[index + 1] - m_value_offsets[index];
|
||||
}
|
||||
|
||||
template <int index>
|
||||
[[nodiscard]] int size(residual_block<index>) const {
|
||||
template <int index> [[nodiscard]] int size(residual_block<index>) const {
|
||||
return m_residual_offsets[index + 1] - m_residual_offsets[index];
|
||||
}
|
||||
|
||||
template <int index>
|
||||
[[nodiscard]] int offset(value_block<index>) const {
|
||||
template <int index> [[nodiscard]] int offset(value_block<index>) const {
|
||||
return m_value_offsets[index];
|
||||
}
|
||||
|
||||
template <int index>
|
||||
[[nodiscard]] int offset(residual_block<index>) const {
|
||||
template <int index> [[nodiscard]] int offset(residual_block<index>) const {
|
||||
return m_residual_offsets[index];
|
||||
}
|
||||
|
||||
@@ -391,8 +329,7 @@ export namespace mean_field::utils::blocks {
|
||||
int block_index = 0;
|
||||
|
||||
((offsets[block_index + 1] =
|
||||
offsets[block_index] +
|
||||
resolve_block_size<BlockTypes>(requested_sizes[block_index]),
|
||||
offsets[block_index] + resolve_block_size<BlockTypes>(requested_sizes[block_index]),
|
||||
++block_index),
|
||||
...);
|
||||
}
|
||||
@@ -463,10 +400,12 @@ export namespace mean_field::utils::blocks {
|
||||
enthalpy::specific::value,
|
||||
displacement::geometry::value>,
|
||||
|
||||
// R_d(d, h)
|
||||
// R_d(rho, d, g, h)
|
||||
block_row<
|
||||
displacement::geometry::residual,
|
||||
density::mass::value,
|
||||
displacement::geometry::value,
|
||||
gravity::gradient::value,
|
||||
enthalpy::specific::value>,
|
||||
|
||||
// R_h(h, Phi, d, C)
|
||||
@@ -483,6 +422,15 @@ export namespace mean_field::utils::blocks {
|
||||
density::mass::value,
|
||||
displacement::geometry::value>>;
|
||||
|
||||
// Columns: [d, h]
|
||||
// Rows: [R_d]
|
||||
using pressure_force_form = block_form<
|
||||
type_list<displacement::geometry::value, enthalpy::specific::value>,
|
||||
type_list<displacement::geometry::residual>>;
|
||||
|
||||
using pressure_force_jacobian_form = type_list<
|
||||
block_row<displacement::geometry::residual, displacement::geometry::value, enthalpy::specific::value>>;
|
||||
|
||||
static_assert(valid_jacobian_form<
|
||||
gravity_field_form,
|
||||
gravity_jacobian_form>);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -66,15 +66,13 @@ 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>;
|
||||
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,
|
||||
|
||||
@@ -32,8 +32,7 @@ export namespace mean_field::utils {
|
||||
double mass{};
|
||||
double c{};
|
||||
DomainMapperStatelessOptions domain_mapper_options{};
|
||||
mapping::compactification::options::KelvinCompactificationOptions
|
||||
kelvin_options{};
|
||||
mapping::compactification::options::KelvinCompactificationOptions kelvin_options{};
|
||||
int max_iters{};
|
||||
double tol{};
|
||||
|
||||
|
||||
414
tests/field/field_base.cpp
Normal file
414
tests/field/field_base.cpp
Normal file
@@ -0,0 +1,414 @@
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include <concepts>
|
||||
#include <cstddef>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
|
||||
import mean_field;
|
||||
import test_helpers;
|
||||
|
||||
namespace field_base_test_utils {
|
||||
namespace field = mean_field::field;
|
||||
namespace domain = mean_field::utils::domain;
|
||||
|
||||
using IndependentL2Scalar = field::ScalarQ<field::FieldRelation::Independent, field::Disc<field::L2, 2>>;
|
||||
|
||||
using IndependentH1Scalar = field::ScalarQ<field::FieldRelation::Independent, field::Disc<field::H1, 3>>;
|
||||
|
||||
using IndependentH1Vector = field::VectorQ<field::FieldRelation::Independent, field::Disc<field::H1, 3>>;
|
||||
|
||||
using IndependentL2Vector = field::VectorQ<field::FieldRelation::Independent, field::Disc<field::L2, 2>>;
|
||||
|
||||
using Potential = field::ScalarQ<field::FieldRelation::Independent, field::Disc<field::L2, 2>>;
|
||||
|
||||
using Flux = field::VectorQ<field::FieldRelation::Gradient<Potential>, field::Disc<field::RT, 2>>;
|
||||
|
||||
using CurlSource = field::VectorQ<field::FieldRelation::Independent, field::Disc<field::ND, 2>>;
|
||||
|
||||
using CurlQuantity = field::VectorQ<field::FieldRelation::Curl<CurlSource>, field::Disc<field::ND, 2>>;
|
||||
|
||||
using SampleOperand = field::Operand<IndependentH1Scalar>;
|
||||
|
||||
using SampleGradientOperand = field::Operand<IndependentH1Scalar, field::FieldOperation::Gradient>;
|
||||
|
||||
using SampleForm = field::FormSpec<17, 2, SampleOperand, SampleGradientOperand>;
|
||||
|
||||
struct StellarSupportedObject {
|
||||
using Support = field::DomainSupport<domain::Stellar>;
|
||||
};
|
||||
|
||||
struct AllSupportedObject {
|
||||
using Support = field::DomainSupport<domain::All>;
|
||||
};
|
||||
|
||||
struct NonSpatialObject {
|
||||
using Support = field::NonSpatialSupport;
|
||||
};
|
||||
} // namespace field_base_test_utils
|
||||
|
||||
TEST_CASE(
|
||||
"Field Base Type Lists Track Compile Time Membership",
|
||||
tags::unit &tags::field
|
||||
) {
|
||||
namespace field = mean_field::field;
|
||||
|
||||
using List = field::TypeList<int, double, char>;
|
||||
|
||||
STATIC_REQUIRE(field::typeListContains<int, List>);
|
||||
|
||||
STATIC_REQUIRE(field::typeListContains<double, List>);
|
||||
|
||||
STATIC_REQUIRE(field::typeListContains<char, List>);
|
||||
|
||||
STATIC_REQUIRE_FALSE(field::typeListContains<float, List>);
|
||||
|
||||
STATIC_REQUIRE_FALSE(field::typeListContains<int, field::TypeList<>>);
|
||||
|
||||
CHECK(true);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Field Base Function Space Tags Encode Supported Tensor Ranks",
|
||||
tags::unit &tags::field
|
||||
) {
|
||||
namespace field = mean_field::field;
|
||||
|
||||
STATIC_REQUIRE(field::SpaceTag<field::L2>);
|
||||
|
||||
STATIC_REQUIRE(field::SpaceTag<field::H1>);
|
||||
|
||||
STATIC_REQUIRE(field::SpaceTag<field::RT>);
|
||||
|
||||
STATIC_REQUIRE(field::SpaceTag<field::ND>);
|
||||
|
||||
STATIC_REQUIRE_FALSE(field::SpaceTag<int>);
|
||||
|
||||
STATIC_REQUIRE(field::spaceSupportsRank<field::L2, 0>);
|
||||
|
||||
STATIC_REQUIRE(field::spaceSupportsRank<field::L2, 1>);
|
||||
|
||||
STATIC_REQUIRE(field::spaceSupportsRank<field::H1, 0>);
|
||||
|
||||
STATIC_REQUIRE(field::spaceSupportsRank<field::H1, 1>);
|
||||
|
||||
STATIC_REQUIRE_FALSE(field::spaceSupportsRank<field::RT, 0>);
|
||||
|
||||
STATIC_REQUIRE(field::spaceSupportsRank<field::RT, 1>);
|
||||
|
||||
STATIC_REQUIRE_FALSE(field::spaceSupportsRank<field::ND, 0>);
|
||||
|
||||
STATIC_REQUIRE(field::spaceSupportsRank<field::ND, 1>);
|
||||
|
||||
CHECK(field::L2::name == std::string_view{"L2"});
|
||||
|
||||
CHECK(field::H1::name == std::string_view{"H1"});
|
||||
|
||||
CHECK(field::RT::name == std::string_view{"RT"});
|
||||
|
||||
CHECK(field::ND::name == std::string_view{"ND"});
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Field Base Discretization Descriptors Preserve Space And Family Order",
|
||||
tags::unit &tags::field
|
||||
) {
|
||||
namespace field = mean_field::field;
|
||||
|
||||
using L2Disc = field::Disc<field::L2, 2>;
|
||||
|
||||
using H1Disc = field::Disc<field::H1, 4>;
|
||||
|
||||
using RTDisc = field::Disc<field::RT, 1>;
|
||||
|
||||
using NDDisc = field::Disc<field::ND, 3>;
|
||||
|
||||
STATIC_REQUIRE(field::DiscretizationTag<L2Disc>);
|
||||
|
||||
STATIC_REQUIRE(field::DiscretizationTag<H1Disc>);
|
||||
|
||||
STATIC_REQUIRE(field::DiscretizationTag<RTDisc>);
|
||||
|
||||
STATIC_REQUIRE(field::DiscretizationTag<NDDisc>);
|
||||
|
||||
STATIC_REQUIRE(std::same_as<typename L2Disc::Space, field::L2>);
|
||||
|
||||
STATIC_REQUIRE(std::same_as<typename H1Disc::Space, field::H1>);
|
||||
|
||||
STATIC_REQUIRE(L2Disc::familyOrder == 2);
|
||||
|
||||
STATIC_REQUIRE(H1Disc::familyOrder == 4);
|
||||
|
||||
STATIC_REQUIRE(RTDisc::familyOrder == 1);
|
||||
|
||||
STATIC_REQUIRE(NDDisc::familyOrder == 3);
|
||||
|
||||
CHECK(true);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Field Base Relations Preserve Their Source Quantities",
|
||||
tags::unit &tags::field
|
||||
) {
|
||||
namespace field = mean_field::field;
|
||||
|
||||
using Source = field_base_test_utils::IndependentL2Scalar;
|
||||
|
||||
using Gradient = field::FieldRelation::Gradient<Source>;
|
||||
|
||||
using Divergence = field::FieldRelation::Divergence<Source>;
|
||||
|
||||
using Curl = field::FieldRelation::Curl<Source>;
|
||||
|
||||
STATIC_REQUIRE(field::ValidRelation<field::FieldRelation::Independent>);
|
||||
|
||||
STATIC_REQUIRE(field::ValidRelation<Gradient>);
|
||||
|
||||
STATIC_REQUIRE(field::ValidRelation<Divergence>);
|
||||
|
||||
STATIC_REQUIRE(field::ValidRelation<Curl>);
|
||||
|
||||
STATIC_REQUIRE_FALSE(field::ValidRelation<int>);
|
||||
|
||||
STATIC_REQUIRE(field::IsGradient<Gradient>::value);
|
||||
|
||||
STATIC_REQUIRE(field::IsDivergence<Divergence>::value);
|
||||
|
||||
STATIC_REQUIRE(field::IsCurl<Curl>::value);
|
||||
|
||||
STATIC_REQUIRE(std::same_as<typename field::RelationTarget<Gradient>::Type, Source>);
|
||||
|
||||
STATIC_REQUIRE(std::same_as<typename field::RelationTarget<Divergence>::Type, Source>);
|
||||
|
||||
STATIC_REQUIRE(std::same_as<typename field::RelationTarget<Curl>::Type, Source>);
|
||||
|
||||
STATIC_REQUIRE(std::same_as<typename field::RelationTarget<field::FieldRelation::Independent>::Type, void>);
|
||||
|
||||
CHECK(true);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Field Base Finite Element Quantities Preserve Rank Storage Space And Order",
|
||||
tags::unit &tags::field
|
||||
) {
|
||||
namespace field = mean_field::field;
|
||||
|
||||
using Scalar = field_base_test_utils::IndependentL2Scalar;
|
||||
|
||||
using Vector = field_base_test_utils::IndependentH1Vector;
|
||||
|
||||
STATIC_REQUIRE(field::FieldQuantity<Scalar>);
|
||||
|
||||
STATIC_REQUIRE(field::FieldQuantity<Vector>);
|
||||
|
||||
STATIC_REQUIRE(field::RegisteredQuantity<Scalar>);
|
||||
|
||||
STATIC_REQUIRE(field::RegisteredQuantity<Vector>);
|
||||
|
||||
STATIC_REQUIRE(Scalar::rankValue == 0);
|
||||
|
||||
STATIC_REQUIRE(Vector::rankValue == 1);
|
||||
|
||||
STATIC_REQUIRE(Scalar::familyOrder == 2);
|
||||
|
||||
STATIC_REQUIRE(Vector::familyOrder == 3);
|
||||
|
||||
STATIC_REQUIRE(std::same_as<typename Scalar::Space, field::L2>);
|
||||
|
||||
STATIC_REQUIRE(std::same_as<typename Vector::Space, field::H1>);
|
||||
|
||||
STATIC_REQUIRE(Scalar::storageKind == field::StorageKind::finite_element);
|
||||
|
||||
STATIC_REQUIRE(Vector::storageKind == field::StorageKind::finite_element);
|
||||
|
||||
STATIC_REQUIRE(Scalar::staticBlockSize == field::dynamicBlockSize);
|
||||
|
||||
STATIC_REQUIRE(Vector::staticBlockSize == field::dynamicBlockSize);
|
||||
|
||||
CHECK(true);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Field Base Global Scalars Are Registered But Are Not Finite Element Quantities",
|
||||
tags::unit &tags::field
|
||||
) {
|
||||
namespace field = mean_field::field;
|
||||
|
||||
STATIC_REQUIRE(field::GlobalScalarQuantity<field::GlobalScalarQ>);
|
||||
|
||||
STATIC_REQUIRE(field::RegisteredQuantity<field::GlobalScalarQ>);
|
||||
|
||||
STATIC_REQUIRE_FALSE(field::FieldQuantity<field::GlobalScalarQ>);
|
||||
|
||||
STATIC_REQUIRE(field::GlobalScalarQ::rankValue == 0);
|
||||
|
||||
STATIC_REQUIRE(field::GlobalScalarQ::storageKind == field::StorageKind::global_scalar);
|
||||
|
||||
STATIC_REQUIRE(field::GlobalScalarQ::staticBlockSize == 1);
|
||||
|
||||
STATIC_REQUIRE(std::same_as<typename field::GlobalScalarQ::Relation, field::FieldRelation::Independent>);
|
||||
|
||||
CHECK(true);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Field Base Derived Quantity Detection Follows Physical Relations",
|
||||
tags::unit &tags::field
|
||||
) {
|
||||
namespace field = mean_field::field;
|
||||
|
||||
using Independent = field_base_test_utils::IndependentL2Scalar;
|
||||
|
||||
using Flux = field_base_test_utils::Flux;
|
||||
|
||||
using CurlQuantity = field_base_test_utils::CurlQuantity;
|
||||
|
||||
STATIC_REQUIRE_FALSE(field::DerivedQuantity<Independent>);
|
||||
|
||||
STATIC_REQUIRE(field::DerivedQuantity<Flux>);
|
||||
|
||||
STATIC_REQUIRE(field::DerivedQuantity<CurlQuantity>);
|
||||
|
||||
STATIC_REQUIRE(std::same_as<field::RelationTargetT<Flux>, field_base_test_utils::Potential>);
|
||||
|
||||
STATIC_REQUIRE(std::same_as<field::RelationTargetT<CurlQuantity>, field_base_test_utils::CurlSource>);
|
||||
|
||||
CHECK(true);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Field Base RT L2 Constraint Accepts The Registered Stable Pair Contract",
|
||||
tags::unit &tags::field
|
||||
) {
|
||||
namespace field = mean_field::field;
|
||||
|
||||
using Potential = field_base_test_utils::Potential;
|
||||
|
||||
using Flux = field_base_test_utils::Flux;
|
||||
|
||||
using Constraint = field::RtL2StablePair<Flux, Potential>;
|
||||
|
||||
STATIC_REQUIRE(field::validate_constraints(field::TypeList<Constraint>{}));
|
||||
|
||||
STATIC_REQUIRE(field::validate_constraints(field::TypeList<>{}));
|
||||
|
||||
CHECK(true);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Field Base Operations And Operands Preserve Mathematical Intent",
|
||||
tags::unit &tags::field
|
||||
) {
|
||||
namespace field = mean_field::field;
|
||||
|
||||
using Quantity = field_base_test_utils::IndependentH1Scalar;
|
||||
|
||||
using ValueOperand = field::Operand<Quantity>;
|
||||
|
||||
using GradientOperand = field::Operand<Quantity, field::FieldOperation::Gradient>;
|
||||
|
||||
STATIC_REQUIRE(field::FieldOperationTag<field::FieldOperation::Value>);
|
||||
|
||||
STATIC_REQUIRE(field::FieldOperationTag<field::FieldOperation::Gradient>);
|
||||
|
||||
STATIC_REQUIRE(field::FieldOperationTag<field::FieldOperation::Divergence>);
|
||||
|
||||
STATIC_REQUIRE(field::FieldOperationTag<field::FieldOperation::Curl>);
|
||||
|
||||
STATIC_REQUIRE(field::FieldOperationTag<field::FieldOperation::NormalTrace>);
|
||||
|
||||
STATIC_REQUIRE_FALSE(field::FieldOperationTag<int>);
|
||||
|
||||
STATIC_REQUIRE(field::FieldOperand<ValueOperand>);
|
||||
|
||||
STATIC_REQUIRE(field::FieldOperand<GradientOperand>);
|
||||
|
||||
STATIC_REQUIRE(std::same_as<typename ValueOperand::Quantity, Quantity>);
|
||||
|
||||
STATIC_REQUIRE(std::same_as<typename ValueOperand::Operation, field::FieldOperation::Value>);
|
||||
|
||||
STATIC_REQUIRE(std::same_as<typename GradientOperand::Operation, field::FieldOperation::Gradient>);
|
||||
|
||||
CHECK(true);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Field Base Form Specifications Preserve Policy Dynamic Orders And Operands",
|
||||
tags::unit &tags::field
|
||||
) {
|
||||
namespace field = mean_field::field;
|
||||
|
||||
using Form = field_base_test_utils::SampleForm;
|
||||
|
||||
STATIC_REQUIRE(field::FieldForm<Form>);
|
||||
|
||||
STATIC_REQUIRE(Form::policyKey == 17);
|
||||
|
||||
STATIC_REQUIRE(Form::dynamicOrderCount == 2);
|
||||
|
||||
STATIC_REQUIRE(
|
||||
std::same_as<
|
||||
typename Form::Operands,
|
||||
field::TypeList<field_base_test_utils::SampleOperand, field_base_test_utils::SampleGradientOperand>>
|
||||
);
|
||||
|
||||
STATIC_REQUIRE(
|
||||
field::isRegisteredQuantityList<field::TypeList<
|
||||
field_base_test_utils::IndependentL2Scalar, field_base_test_utils::IndependentH1Vector,
|
||||
field::GlobalScalarQ>>
|
||||
);
|
||||
|
||||
STATIC_REQUIRE_FALSE(field::isRegisteredQuantityList<field::TypeList<int>>);
|
||||
|
||||
STATIC_REQUIRE(field::isFieldFormList<field::TypeList<Form>>);
|
||||
|
||||
STATIC_REQUIRE(field::isFieldFormList<field::TypeList<>>);
|
||||
|
||||
STATIC_REQUIRE_FALSE(field::isFieldFormList<field::TypeList<int>>);
|
||||
|
||||
CHECK(true);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Field Base Support Types Distinguish Domain And Non Spatial Fields",
|
||||
tags::unit &tags::field
|
||||
) {
|
||||
namespace field = mean_field::field;
|
||||
namespace domain = mean_field::utils::domain;
|
||||
|
||||
using StellarSupport = field::DomainSupport<domain::Stellar>;
|
||||
|
||||
using AllSupport = field::DomainSupport<domain::All>;
|
||||
|
||||
STATIC_REQUIRE(field::IsFieldSupport<StellarSupport>);
|
||||
|
||||
STATIC_REQUIRE(field::IsFieldSupport<AllSupport>);
|
||||
|
||||
STATIC_REQUIRE(field::IsFieldSupport<field::NonSpatialSupport>);
|
||||
|
||||
STATIC_REQUIRE(field::IsDomainSupport<StellarSupport>);
|
||||
|
||||
STATIC_REQUIRE(field::IsDomainSupport<AllSupport>);
|
||||
|
||||
STATIC_REQUIRE_FALSE(field::IsDomainSupport<field::NonSpatialSupport>);
|
||||
|
||||
STATIC_REQUIRE(std::same_as<typename StellarSupport::Domain, domain::Stellar>);
|
||||
|
||||
STATIC_REQUIRE(std::same_as<typename AllSupport::Domain, domain::All>);
|
||||
|
||||
STATIC_REQUIRE(field::DomainSupportedField<field_base_test_utils::StellarSupportedObject>);
|
||||
|
||||
STATIC_REQUIRE(field::DomainSupportedField<field_base_test_utils::AllSupportedObject>);
|
||||
|
||||
STATIC_REQUIRE_FALSE(field::DomainSupportedField<field_base_test_utils::NonSpatialObject>);
|
||||
|
||||
STATIC_REQUIRE(field::NonSpatialField<field_base_test_utils::NonSpatialObject>);
|
||||
|
||||
STATIC_REQUIRE_FALSE(field::NonSpatialField<field_base_test_utils::StellarSupportedObject>);
|
||||
|
||||
STATIC_REQUIRE(std::same_as<field::FieldSupportT<field_base_test_utils::StellarSupportedObject>, StellarSupport>);
|
||||
|
||||
STATIC_REQUIRE(std::same_as<field::FieldDomainT<field_base_test_utils::StellarSupportedObject>, domain::Stellar>);
|
||||
|
||||
CHECK(true);
|
||||
}
|
||||
724
tests/field/field_dof_map.cpp
Normal file
724
tests/field/field_dof_map.cpp
Normal file
@@ -0,0 +1,724 @@
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <cstddef>
|
||||
#include <mfem.hpp>
|
||||
#include <mpi.h>
|
||||
#include <optional>
|
||||
#include <stdexcept>
|
||||
#include <vector>
|
||||
|
||||
import mean_field;
|
||||
import test_helpers;
|
||||
|
||||
namespace field_dof_map_test_utils {
|
||||
namespace field = mean_field::field;
|
||||
|
||||
namespace domain = mean_field::utils::domain;
|
||||
|
||||
using Schema = domain::CoreEnvelopeVacuumDomainSchema;
|
||||
|
||||
[[nodiscard]]
|
||||
mfem::Array<int> make_array(const std::initializer_list<int> values) {
|
||||
mfem::Array<int> result(static_cast<int>(values.size()));
|
||||
|
||||
int index = 0;
|
||||
|
||||
for (const int value : values) {
|
||||
result[index++] = value;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
mfem::Mesh make_split_mesh(
|
||||
const int stellarAttribute = 2,
|
||||
const int vacuumAttribute = 3
|
||||
) {
|
||||
int communicatorSize = 1;
|
||||
|
||||
MPI_Comm_size(MPI_COMM_WORLD, &communicatorSize);
|
||||
|
||||
/*
|
||||
* Ensure there are enough cells that every reasonable MPI test
|
||||
* configuration has useful work available.
|
||||
*/
|
||||
const int xElementCount = std::max(4, 2 * communicatorSize);
|
||||
|
||||
constexpr int yElementCount = 2;
|
||||
|
||||
mfem::Mesh mesh = mfem::Mesh::MakeCartesian2D(
|
||||
xElementCount, yElementCount, mfem::Element::QUADRILATERAL, true, static_cast<double>(xElementCount),
|
||||
static_cast<double>(yElementCount)
|
||||
);
|
||||
|
||||
for (int elementId = 0; elementId < mesh.GetNE(); ++elementId) {
|
||||
const int xIndex = elementId % xElementCount;
|
||||
|
||||
mesh.GetElement(elementId)->SetAttribute(xIndex < xElementCount / 2 ? stellarAttribute : vacuumAttribute);
|
||||
}
|
||||
|
||||
mesh.SetAttributes();
|
||||
|
||||
return mesh;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
long long global_sum(const int localValue) {
|
||||
const long long local = static_cast<long long>(localValue);
|
||||
|
||||
long long global = 0;
|
||||
|
||||
MPI_Allreduce(&local, &global, 1, MPI_LONG_LONG, MPI_SUM, MPI_COMM_WORLD);
|
||||
|
||||
return global;
|
||||
}
|
||||
|
||||
template <typename FieldT>
|
||||
concept CanMakeFieldDofMap =
|
||||
requires(const mfem::ParFiniteElementSpace &space) { field::make_field_dof_map<FieldT, Schema>(space); };
|
||||
|
||||
using AlternateSchema = domain::DomainSchema<
|
||||
domain::MaterialList<
|
||||
domain::Material<domain::Core, 11>,
|
||||
domain::Material<domain::Envelope, 17>,
|
||||
domain::Material<domain::Vacuum, 29>>,
|
||||
domain::BoundaryList<>,
|
||||
domain::RelationList<>>;
|
||||
} // namespace field_dof_map_test_utils
|
||||
|
||||
TEST_CASE(
|
||||
"Field DOF Map Preserves Canonical Bidirectional Indexing",
|
||||
tags::unit &tags::field
|
||||
) {
|
||||
namespace field = mean_field::field;
|
||||
|
||||
const mfem::Array<int> active = field_dof_map_test_utils::make_array({0, 2, 5, 7});
|
||||
|
||||
const field::FieldDofMap map(9, active);
|
||||
|
||||
CHECK(map.full_size() == 9);
|
||||
|
||||
CHECK(map.reduced_size() == 4);
|
||||
|
||||
CHECK(map.inactive_size() == 5);
|
||||
|
||||
CHECK_FALSE(map.is_identity());
|
||||
|
||||
CHECK(map.true_dof(0) == 0);
|
||||
|
||||
CHECK(map.true_dof(1) == 2);
|
||||
|
||||
CHECK(map.true_dof(2) == 5);
|
||||
|
||||
CHECK(map.true_dof(3) == 7);
|
||||
|
||||
REQUIRE(map.reduced_dof(0).has_value());
|
||||
|
||||
REQUIRE(map.reduced_dof(2).has_value());
|
||||
|
||||
REQUIRE(map.reduced_dof(5).has_value());
|
||||
|
||||
REQUIRE(map.reduced_dof(7).has_value());
|
||||
|
||||
CHECK(*map.reduced_dof(0) == 0);
|
||||
|
||||
CHECK(*map.reduced_dof(2) == 1);
|
||||
|
||||
CHECK(*map.reduced_dof(5) == 2);
|
||||
|
||||
CHECK(*map.reduced_dof(7) == 3);
|
||||
|
||||
CHECK_FALSE(map.reduced_dof(1).has_value());
|
||||
|
||||
CHECK_FALSE(map.reduced_dof(3).has_value());
|
||||
|
||||
CHECK(map.contains_true_dof(0));
|
||||
|
||||
CHECK(map.contains_true_dof(2));
|
||||
|
||||
CHECK_FALSE(map.contains_true_dof(1));
|
||||
|
||||
const mfem::Array<int> &forward = map.reduced_to_true();
|
||||
|
||||
const mfem::Array<int> &inverse = map.true_to_reduced();
|
||||
|
||||
REQUIRE(forward.Size() == 4);
|
||||
|
||||
REQUIRE(inverse.Size() == 9);
|
||||
|
||||
CHECK(forward[0] == 0);
|
||||
CHECK(forward[1] == 2);
|
||||
CHECK(forward[2] == 5);
|
||||
CHECK(forward[3] == 7);
|
||||
|
||||
CHECK(inverse[0] == 0);
|
||||
CHECK(inverse[1] == -1);
|
||||
CHECK(inverse[2] == 1);
|
||||
CHECK(inverse[3] == -1);
|
||||
CHECK(inverse[4] == -1);
|
||||
CHECK(inverse[5] == 2);
|
||||
CHECK(inverse[6] == -1);
|
||||
CHECK(inverse[7] == 3);
|
||||
CHECK(inverse[8] == -1);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Field DOF Map Rejects Invalid Canonical Mappings",
|
||||
tags::unit &tags::field
|
||||
) {
|
||||
namespace field = mean_field::field;
|
||||
|
||||
const mfem::Array<int> empty;
|
||||
|
||||
CHECK_THROWS_AS((field::FieldDofMap(-1, empty)), std::invalid_argument);
|
||||
|
||||
CHECK_THROWS_AS((field::FieldDofMap(4, field_dof_map_test_utils::make_array({-1, 2}))), std::invalid_argument);
|
||||
|
||||
CHECK_THROWS_AS((field::FieldDofMap(4, field_dof_map_test_utils::make_array({1, 4}))), std::invalid_argument);
|
||||
|
||||
/*
|
||||
* Duplicate true DOF.
|
||||
*/
|
||||
CHECK_THROWS_AS((field::FieldDofMap(5, field_dof_map_test_utils::make_array({1, 1, 3}))), std::invalid_argument);
|
||||
|
||||
/*
|
||||
* Non-canonical unsorted ordering.
|
||||
*/
|
||||
CHECK_THROWS_AS((field::FieldDofMap(5, field_dof_map_test_utils::make_array({1, 3, 2}))), std::invalid_argument);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Field DOF Map Rejects Out Of Range Index Queries",
|
||||
tags::unit &tags::field
|
||||
) {
|
||||
namespace field = mean_field::field;
|
||||
|
||||
const field::FieldDofMap map(5, field_dof_map_test_utils::make_array({1, 3}));
|
||||
|
||||
CHECK_THROWS_AS(map.true_dof(-1), std::out_of_range);
|
||||
|
||||
CHECK_THROWS_AS(map.true_dof(2), std::out_of_range);
|
||||
|
||||
CHECK_THROWS_AS(map.reduced_dof(-1), std::out_of_range);
|
||||
|
||||
CHECK_THROWS_AS(map.reduced_dof(5), std::out_of_range);
|
||||
|
||||
CHECK_THROWS_AS(map.contains_true_dof(-1), std::out_of_range);
|
||||
|
||||
CHECK_THROWS_AS(map.contains_true_dof(5), std::out_of_range);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Field DOF Map Gather Selects Exactly The Active True DOFs",
|
||||
tags::unit &tags::field
|
||||
) {
|
||||
namespace field = mean_field::field;
|
||||
|
||||
const field::FieldDofMap map(6, field_dof_map_test_utils::make_array({1, 3, 5}));
|
||||
|
||||
mfem::Vector full(6);
|
||||
|
||||
for (int trueDof = 0; trueDof < full.Size(); ++trueDof) {
|
||||
full(trueDof) = 10.0 + static_cast<double>(trueDof);
|
||||
}
|
||||
|
||||
const mfem::Vector reduced = map.gather(full);
|
||||
|
||||
REQUIRE(reduced.Size() == 3);
|
||||
|
||||
CHECK(reduced(0) == 11.0);
|
||||
|
||||
CHECK(reduced(1) == 13.0);
|
||||
|
||||
CHECK(reduced(2) == 15.0);
|
||||
|
||||
mfem::Vector output(3);
|
||||
|
||||
map.gather(full, output);
|
||||
|
||||
CHECK(output(0) == 11.0);
|
||||
|
||||
CHECK(output(1) == 13.0);
|
||||
|
||||
CHECK(output(2) == 15.0);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Field DOF Map Scatter Produces The Canonical Supported Projection",
|
||||
tags::unit &tags::field
|
||||
) {
|
||||
namespace field = mean_field::field;
|
||||
|
||||
const field::FieldDofMap map(6, field_dof_map_test_utils::make_array({1, 3, 5}));
|
||||
|
||||
mfem::Vector reduced(3);
|
||||
|
||||
reduced(0) = 2.0;
|
||||
reduced(1) = 4.0;
|
||||
reduced(2) = 6.0;
|
||||
|
||||
const mfem::Vector full = map.scatter(reduced);
|
||||
|
||||
REQUIRE(full.Size() == 6);
|
||||
|
||||
CHECK(full(0) == 0.0);
|
||||
CHECK(full(1) == 2.0);
|
||||
CHECK(full(2) == 0.0);
|
||||
CHECK(full(3) == 4.0);
|
||||
CHECK(full(4) == 0.0);
|
||||
CHECK(full(5) == 6.0);
|
||||
|
||||
const mfem::Vector roundTrip = map.gather(full);
|
||||
|
||||
REQUIRE(roundTrip.Size() == reduced.Size());
|
||||
|
||||
for (int index = 0; index < reduced.Size(); ++index) {
|
||||
CHECK(roundTrip(index) == reduced(index));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Field DOF Map Gather Scatter Projects A Full Vector Onto Field Support",
|
||||
tags::unit &tags::field
|
||||
) {
|
||||
namespace field = mean_field::field;
|
||||
|
||||
const field::FieldDofMap map(7, field_dof_map_test_utils::make_array({0, 2, 3, 6}));
|
||||
|
||||
mfem::Vector original(7);
|
||||
|
||||
for (int index = 0; index < original.Size(); ++index) {
|
||||
original(index) = 0.25 + static_cast<double>(index);
|
||||
}
|
||||
|
||||
const mfem::Vector reduced = map.gather(original);
|
||||
|
||||
const mfem::Vector projected = map.scatter(reduced);
|
||||
|
||||
for (int trueDof = 0; trueDof < original.Size(); ++trueDof) {
|
||||
CAPTURE(trueDof);
|
||||
|
||||
if (map.contains_true_dof(trueDof)) {
|
||||
CHECK(projected(trueDof) == original(trueDof));
|
||||
} else {
|
||||
CHECK(projected(trueDof) == 0.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Field DOF Map Scatter Into Preserves Unsupported True DOFs",
|
||||
tags::unit &tags::field
|
||||
) {
|
||||
namespace field = mean_field::field;
|
||||
|
||||
const field::FieldDofMap map(6, field_dof_map_test_utils::make_array({1, 4}));
|
||||
|
||||
mfem::Vector reduced(2);
|
||||
|
||||
reduced(0) = 7.0;
|
||||
reduced(1) = 9.0;
|
||||
|
||||
mfem::Vector full(6);
|
||||
|
||||
full = -3.0;
|
||||
|
||||
map.scatter_into(reduced, full);
|
||||
|
||||
CHECK(full(0) == -3.0);
|
||||
CHECK(full(1) == 7.0);
|
||||
CHECK(full(2) == -3.0);
|
||||
CHECK(full(3) == -3.0);
|
||||
CHECK(full(4) == 9.0);
|
||||
CHECK(full(5) == -3.0);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Field DOF Map Scatter Add Accumulates Only Onto Active True DOFs",
|
||||
tags::unit &tags::field
|
||||
) {
|
||||
namespace field = mean_field::field;
|
||||
|
||||
const field::FieldDofMap map(5, field_dof_map_test_utils::make_array({0, 2, 4}));
|
||||
|
||||
mfem::Vector reduced(3);
|
||||
|
||||
reduced(0) = 1.0;
|
||||
reduced(1) = 2.0;
|
||||
reduced(2) = 3.0;
|
||||
|
||||
mfem::Vector full(5);
|
||||
|
||||
full = 10.0;
|
||||
|
||||
map.scatter_add(reduced, full, 2.0);
|
||||
|
||||
CHECK(full(0) == 12.0);
|
||||
CHECK(full(1) == 10.0);
|
||||
CHECK(full(2) == 14.0);
|
||||
CHECK(full(3) == 10.0);
|
||||
CHECK(full(4) == 16.0);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Field DOF Map Operations Support MFEM Vector Views Without Resizing",
|
||||
tags::unit &tags::field
|
||||
) {
|
||||
namespace field = mean_field::field;
|
||||
|
||||
const field::FieldDofMap map(5, field_dof_map_test_utils::make_array({1, 3}));
|
||||
|
||||
mfem::Vector storage(9);
|
||||
|
||||
storage = -8.0;
|
||||
|
||||
/*
|
||||
* View [2, 7) of the parent vector.
|
||||
*/
|
||||
mfem::Vector fullView(storage.GetData() + 2, 5);
|
||||
|
||||
mfem::Vector reduced(2);
|
||||
|
||||
reduced(0) = 4.0;
|
||||
reduced(1) = 6.0;
|
||||
|
||||
map.scatter_into(reduced, fullView);
|
||||
|
||||
/*
|
||||
* Storage outside the view must remain untouched.
|
||||
*/
|
||||
CHECK(storage(0) == -8.0);
|
||||
CHECK(storage(1) == -8.0);
|
||||
CHECK(storage(7) == -8.0);
|
||||
CHECK(storage(8) == -8.0);
|
||||
|
||||
/*
|
||||
* Within the view, only active true DOFs change.
|
||||
*/
|
||||
CHECK(storage(2) == -8.0);
|
||||
CHECK(storage(3) == 4.0);
|
||||
CHECK(storage(4) == -8.0);
|
||||
CHECK(storage(5) == 6.0);
|
||||
CHECK(storage(6) == -8.0);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Field DOF Map Operations Reject Incompatible Vector Sizes",
|
||||
tags::unit &tags::field
|
||||
) {
|
||||
namespace field = mean_field::field;
|
||||
|
||||
const field::FieldDofMap map(5, field_dof_map_test_utils::make_array({1, 3}));
|
||||
|
||||
mfem::Vector correctFull(5);
|
||||
mfem::Vector wrongFull(4);
|
||||
|
||||
mfem::Vector correctReduced(2);
|
||||
mfem::Vector wrongReduced(3);
|
||||
|
||||
CHECK_THROWS_AS(map.gather(wrongFull), std::invalid_argument);
|
||||
|
||||
CHECK_THROWS_AS(map.gather(correctFull, wrongReduced), std::invalid_argument);
|
||||
|
||||
CHECK_THROWS_AS(map.scatter(wrongReduced), std::invalid_argument);
|
||||
|
||||
CHECK_THROWS_AS(map.scatter(correctReduced, wrongFull), std::invalid_argument);
|
||||
|
||||
CHECK_THROWS_AS(map.scatter_into(wrongReduced, correctFull), std::invalid_argument);
|
||||
|
||||
CHECK_THROWS_AS(map.scatter_add(correctReduced, wrongFull), std::invalid_argument);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Field DOF Map Identity Mapping Is An Exact Vector Identity",
|
||||
tags::unit &tags::field
|
||||
) {
|
||||
namespace field = mean_field::field;
|
||||
|
||||
const field::FieldDofMap map(4, field_dof_map_test_utils::make_array({0, 1, 2, 3}));
|
||||
|
||||
REQUIRE(map.is_identity());
|
||||
|
||||
REQUIRE(map.inactive_size() == 0);
|
||||
|
||||
mfem::Vector full(4);
|
||||
|
||||
full(0) = 0.1;
|
||||
full(1) = -0.2;
|
||||
full(2) = 3.7;
|
||||
full(3) = 8.1;
|
||||
|
||||
const mfem::Vector reduced = map.gather(full);
|
||||
|
||||
const mfem::Vector restored = map.scatter(reduced);
|
||||
|
||||
for (int index = 0; index < full.Size(); ++index) {
|
||||
CHECK(reduced(index) == full(index));
|
||||
|
||||
CHECK(restored(index) == full(index));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Field DOF Map Validates Field DOF Support Consistency",
|
||||
tags::unit &tags::field
|
||||
) {
|
||||
namespace field = mean_field::field;
|
||||
|
||||
field::FieldDofSupport support;
|
||||
|
||||
support.activeTrueDofMarker.SetSize(5);
|
||||
|
||||
support.activeTrueDofMarker = 0;
|
||||
|
||||
support.activeTrueDofMarker[1] = 1;
|
||||
|
||||
support.activeTrueDofMarker[3] = 1;
|
||||
|
||||
support.activeTrueDofs = field_dof_map_test_utils::make_array({1, 3});
|
||||
|
||||
const field::FieldDofMap validMap(support);
|
||||
|
||||
CHECK(validMap.full_size() == 5);
|
||||
|
||||
CHECK(validMap.reduced_size() == 2);
|
||||
|
||||
/*
|
||||
* Make the marker disagree with the list.
|
||||
*/
|
||||
support.activeTrueDofMarker[3] = 0;
|
||||
|
||||
CHECK_THROWS_AS((field::FieldDofMap(support)), std::invalid_argument);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Field DOF Map Factory Is Available Only For Spatial Registered Fields",
|
||||
tags::unit &tags::field
|
||||
) {
|
||||
namespace field = mean_field::field;
|
||||
|
||||
STATIC_REQUIRE(field_dof_map_test_utils::CanMakeFieldDofMap<field::Density>);
|
||||
|
||||
STATIC_REQUIRE(field_dof_map_test_utils::CanMakeFieldDofMap<field::Enthalpy>);
|
||||
|
||||
STATIC_REQUIRE(field_dof_map_test_utils::CanMakeFieldDofMap<field::Gravity>);
|
||||
|
||||
STATIC_REQUIRE(field_dof_map_test_utils::CanMakeFieldDofMap<field::Displacement>);
|
||||
|
||||
STATIC_REQUIRE_FALSE(field_dof_map_test_utils::CanMakeFieldDofMap<field::BarotropicConstant>);
|
||||
|
||||
CHECK(true);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Field DOF Map Factory Exactly Preserves Density Support",
|
||||
tags::integration &tags::field
|
||||
) {
|
||||
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::FieldDofSupport support =
|
||||
field::resolve_field_dof_support<field::Density, field_dof_map_test_utils::Schema>(*finiteElementSpace);
|
||||
|
||||
const field::FieldDofMap map =
|
||||
field::make_field_dof_map<field::Density, field_dof_map_test_utils::Schema>(*finiteElementSpace);
|
||||
|
||||
REQUIRE(map.full_size() == finiteElementSpace->GetTrueVSize());
|
||||
|
||||
REQUIRE(map.reduced_size() == support.activeTrueDofs.Size());
|
||||
|
||||
REQUIRE(map.full_size() == support.activeTrueDofMarker.Size());
|
||||
|
||||
for (int reducedDof = 0; reducedDof < map.reduced_size(); ++reducedDof) {
|
||||
CAPTURE(reducedDof);
|
||||
|
||||
CHECK(map.true_dof(reducedDof) == support.activeTrueDofs[reducedDof]);
|
||||
}
|
||||
|
||||
for (int trueDof = 0; trueDof < map.full_size(); ++trueDof) {
|
||||
CAPTURE(trueDof);
|
||||
|
||||
CHECK(map.contains_true_dof(trueDof) == (support.activeTrueDofMarker[trueDof] != 0));
|
||||
}
|
||||
|
||||
const long long globalFullSize = field_dof_map_test_utils::global_sum(map.full_size());
|
||||
|
||||
const long long globalReducedSize = field_dof_map_test_utils::global_sum(map.reduced_size());
|
||||
|
||||
/*
|
||||
* L2 density has independent vacuum element DOFs, so removing vacuum
|
||||
* support must genuinely reduce the global nonlinear block.
|
||||
*/
|
||||
CHECK(globalReducedSize > 0);
|
||||
|
||||
CHECK(globalReducedSize < globalFullSize);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Field DOF Map Factory Exactly Preserves H1 Enthalpy Support",
|
||||
tags::integration &tags::field
|
||||
) {
|
||||
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::FieldDofSupport support =
|
||||
field::resolve_field_dof_support<field::Enthalpy, field_dof_map_test_utils::Schema>(*finiteElementSpace);
|
||||
|
||||
const field::FieldDofMap map =
|
||||
field::make_field_dof_map<field::Enthalpy, field_dof_map_test_utils::Schema>(*finiteElementSpace);
|
||||
|
||||
REQUIRE(map.reduced_size() == support.activeTrueDofs.Size());
|
||||
|
||||
for (int reducedDof = 0; reducedDof < map.reduced_size(); ++reducedDof) {
|
||||
CHECK(map.true_dof(reducedDof) == support.activeTrueDofs[reducedDof]);
|
||||
}
|
||||
|
||||
/*
|
||||
* The separate field_mfem support tests already establish that shared
|
||||
* Stellar/Vacuum H1 trace DOFs are active. This test establishes that
|
||||
* FieldDofMap preserves that active set exactly, rather than applying
|
||||
* a second reduction or reinterpretation.
|
||||
*/
|
||||
for (int trueDof = 0; trueDof < map.full_size(); ++trueDof) {
|
||||
CHECK(map.contains_true_dof(trueDof) == (support.activeTrueDofMarker[trueDof] != 0));
|
||||
}
|
||||
|
||||
const long long globalFullSize = field_dof_map_test_utils::global_sum(map.full_size());
|
||||
|
||||
const long long globalReducedSize = field_dof_map_test_utils::global_sum(map.reduced_size());
|
||||
|
||||
CHECK(globalReducedSize > 0);
|
||||
|
||||
CHECK(globalReducedSize < globalFullSize);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Field DOF Map Factory Produces Identity Maps For All Supported Fields",
|
||||
tags::integration &tags::field
|
||||
) {
|
||||
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::FieldDofMap map =
|
||||
field::make_field_dof_map<field::Displacement, field_dof_map_test_utils::Schema>(*finiteElementSpace);
|
||||
|
||||
CHECK(map.is_identity());
|
||||
|
||||
CHECK(map.full_size() == finiteElementSpace->GetTrueVSize());
|
||||
|
||||
CHECK(map.reduced_size() == finiteElementSpace->GetTrueVSize());
|
||||
|
||||
CHECK(map.inactive_size() == 0);
|
||||
|
||||
for (int trueDof = 0; trueDof < map.full_size(); ++trueDof) {
|
||||
CHECK(map.true_dof(trueDof) == trueDof);
|
||||
|
||||
CHECK(map.contains_true_dof(trueDof));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Field DOF Map Factory Uses Schema Material Bindings Rather Than Numeric Conventions",
|
||||
tags::integration &tags::field
|
||||
) {
|
||||
namespace field = mean_field::field;
|
||||
|
||||
mfem::Mesh serialMesh = field_dof_map_test_utils::make_split_mesh(17, 29);
|
||||
|
||||
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::FieldDofMap map =
|
||||
field::make_field_dof_map<field::Density, field_dof_map_test_utils::AlternateSchema>(*finiteElementSpace);
|
||||
|
||||
const field::FieldDofSupport support =
|
||||
field::resolve_field_dof_support<field::Density, field_dof_map_test_utils::AlternateSchema>(
|
||||
*finiteElementSpace
|
||||
);
|
||||
|
||||
CHECK(map.full_size() == support.activeTrueDofMarker.Size());
|
||||
|
||||
CHECK(map.reduced_size() == support.activeTrueDofs.Size());
|
||||
|
||||
for (int trueDof = 0; trueDof < map.full_size(); ++trueDof) {
|
||||
CHECK(map.contains_true_dof(trueDof) == (support.activeTrueDofMarker[trueDof] != 0));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Field DOF Map Reduced Vectors Round Trip Through Real Field Support",
|
||||
tags::integration &tags::field
|
||||
) {
|
||||
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::FieldDofMap map =
|
||||
field::make_field_dof_map<field::Enthalpy, field_dof_map_test_utils::Schema>(*finiteElementSpace);
|
||||
|
||||
mfem::Vector reduced(map.reduced_size());
|
||||
|
||||
for (int reducedDof = 0; reducedDof < reduced.Size(); ++reducedDof) {
|
||||
reduced(reducedDof) = 0.125 + 0.031 * static_cast<double>(reducedDof + 1);
|
||||
}
|
||||
|
||||
const mfem::Vector full = map.scatter(reduced);
|
||||
|
||||
const mfem::Vector recovered = map.gather(full);
|
||||
|
||||
REQUIRE(recovered.Size() == reduced.Size());
|
||||
|
||||
for (int reducedDof = 0; reducedDof < reduced.Size(); ++reducedDof) {
|
||||
CAPTURE(reducedDof);
|
||||
|
||||
CHECK(recovered(reducedDof) == reduced(reducedDof));
|
||||
}
|
||||
|
||||
for (int trueDof = 0; trueDof < full.Size(); ++trueDof) {
|
||||
if (!map.contains_true_dof(trueDof)) {
|
||||
CHECK(full(trueDof) == 0.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
778
tests/field/field_mfem.cpp
Normal file
778
tests/field/field_mfem.cpp
Normal file
@@ -0,0 +1,778 @@
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <concepts>
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
#include <mpi.h>
|
||||
#include <set>
|
||||
#include <stdexcept>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#include <mfem.hpp>
|
||||
|
||||
import mean_field;
|
||||
import test_helpers;
|
||||
|
||||
namespace field_mfem_test_utils {
|
||||
namespace field = mean_field::field;
|
||||
namespace domain = mean_field::utils::domain;
|
||||
namespace quadrature = mean_field::quadrature;
|
||||
|
||||
using Schema = domain::CoreEnvelopeVacuumDomainSchema;
|
||||
|
||||
struct VectorL2Field {
|
||||
static constexpr std::string_view name = "test_vector_l2";
|
||||
|
||||
using Support = field::DomainSupport<domain::All>;
|
||||
|
||||
struct Vector final : field::VectorQ<field::FieldRelation::Independent, field::Disc<field::L2, 2>> { };
|
||||
|
||||
using Quantities = field::TypeList<Vector>;
|
||||
|
||||
using Constraints = field::TypeList<>;
|
||||
|
||||
using FormList = field::TypeList<>;
|
||||
|
||||
static constexpr bool constraintsAreValid = field::validate_constraints(Constraints{});
|
||||
|
||||
static_assert(constraintsAreValid);
|
||||
};
|
||||
|
||||
struct NdField {
|
||||
static constexpr std::string_view name = "test_nd";
|
||||
|
||||
using Support = field::DomainSupport<domain::All>;
|
||||
|
||||
struct Vector final : field::VectorQ<field::FieldRelation::Independent, field::Disc<field::ND, 2>> { };
|
||||
|
||||
using Quantities = field::TypeList<Vector>;
|
||||
|
||||
using Constraints = field::TypeList<>;
|
||||
|
||||
using FormList = field::TypeList<>;
|
||||
|
||||
static constexpr bool constraintsAreValid = field::validate_constraints(Constraints{});
|
||||
|
||||
static_assert(constraintsAreValid);
|
||||
};
|
||||
|
||||
using AlternateSchema = domain::DomainSchema<
|
||||
domain::MaterialList<
|
||||
domain::Material<domain::Core, 11>,
|
||||
domain::Material<domain::Envelope, 17>,
|
||||
domain::Material<domain::Vacuum, 29>>,
|
||||
domain::BoundaryList<>,
|
||||
domain::RelationList<>>;
|
||||
|
||||
template <typename FieldT>
|
||||
concept CanResolveLocalSupport = requires(const mfem::FiniteElementSpace &space) {
|
||||
field::resolve_field_local_dof_support<FieldT, Schema>(space);
|
||||
};
|
||||
|
||||
[[nodiscard]]
|
||||
mfem::Mesh make_two_domain_mesh(
|
||||
const int leftAttribute = 2,
|
||||
const int rightAttribute = 3
|
||||
) {
|
||||
mfem::Mesh mesh = mfem::Mesh::MakeCartesian2D(2, 1, mfem::Element::QUADRILATERAL, true, 2.0, 1.0);
|
||||
|
||||
mesh.GetElement(0)->SetAttribute(leftAttribute);
|
||||
|
||||
mesh.GetElement(1)->SetAttribute(rightAttribute);
|
||||
|
||||
mesh.SetAttributes();
|
||||
|
||||
return mesh;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
mfem::Mesh make_parallel_split_mesh() {
|
||||
constexpr int xElementCount = 4;
|
||||
constexpr int yElementCount = 2;
|
||||
|
||||
mfem::Mesh mesh =
|
||||
mfem::Mesh::MakeCartesian2D(xElementCount, yElementCount, mfem::Element::QUADRILATERAL, true, 4.0, 2.0);
|
||||
|
||||
for (int elementId = 0; elementId < mesh.GetNE(); ++elementId) {
|
||||
const int xIndex = elementId % xElementCount;
|
||||
|
||||
const int attribute = xIndex < 2 ? 2 : 3;
|
||||
|
||||
mesh.GetElement(elementId)->SetAttribute(attribute);
|
||||
}
|
||||
|
||||
mesh.SetAttributes();
|
||||
|
||||
return mesh;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
std::vector<int> decoded_element_vdofs(
|
||||
const mfem::FiniteElementSpace &space,
|
||||
const int elementId
|
||||
) {
|
||||
mfem::Array<int> signedVDofs;
|
||||
|
||||
space.GetElementVDofs(elementId, signedVDofs);
|
||||
|
||||
std::vector<int> result;
|
||||
|
||||
result.reserve(static_cast<std::size_t>(signedVDofs.Size()));
|
||||
|
||||
for (int index = 0; index < signedVDofs.Size(); ++index) {
|
||||
result.push_back(mfem::FiniteElementSpace::DecodeDof(signedVDofs[index]));
|
||||
}
|
||||
|
||||
std::ranges::sort(result);
|
||||
|
||||
result.erase(std::unique(result.begin(), result.end()), result.end());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
bool contains(
|
||||
const mfem::Array<int> &values,
|
||||
const int value
|
||||
) {
|
||||
for (int index = 0; index < values.Size(); ++index) {
|
||||
if (values[index] == value) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
std::vector<int> intersection(
|
||||
const std::vector<int> &first,
|
||||
const std::vector<int> &second
|
||||
) {
|
||||
std::vector<int> result;
|
||||
|
||||
std::set_intersection(first.begin(), first.end(), second.begin(), second.end(), std::back_inserter(result));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
std::vector<int> difference(
|
||||
const std::vector<int> &first,
|
||||
const std::vector<int> &second
|
||||
) {
|
||||
std::vector<int> result;
|
||||
|
||||
std::set_difference(first.begin(), first.end(), second.begin(), second.end(), std::back_inserter(result));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
long long global_sum(const int localValue) {
|
||||
const long long local = static_cast<long long>(localValue);
|
||||
|
||||
long long global = 0;
|
||||
|
||||
MPI_Allreduce(&local, &global, 1, MPI_LONG_LONG, MPI_SUM, MPI_COMM_WORLD);
|
||||
|
||||
return global;
|
||||
}
|
||||
} // namespace field_mfem_test_utils
|
||||
|
||||
TEST_CASE(
|
||||
"Field MFEM Support Resolution Is Available Only For Domain Supported Fields",
|
||||
tags::unit &tags::field
|
||||
) {
|
||||
namespace field = mean_field::field;
|
||||
|
||||
STATIC_REQUIRE(field::MfemDomainField<field::Density>);
|
||||
|
||||
STATIC_REQUIRE(field::MfemDomainField<field::Enthalpy>);
|
||||
|
||||
STATIC_REQUIRE(field::MfemDomainField<field::Gravity>);
|
||||
|
||||
STATIC_REQUIRE(field::MfemDomainField<field::Displacement>);
|
||||
|
||||
STATIC_REQUIRE_FALSE(field::MfemDomainField<field::BarotropicConstant>);
|
||||
|
||||
STATIC_REQUIRE(field_mfem_test_utils::CanResolveLocalSupport<field::Density>);
|
||||
|
||||
STATIC_REQUIRE_FALSE(field_mfem_test_utils::CanResolveLocalSupport<field::BarotropicConstant>);
|
||||
|
||||
CHECK(true);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Field MFEM Creates The Registered Finite Element Collection Families",
|
||||
tags::unit &tags::field
|
||||
) {
|
||||
namespace field = mean_field::field;
|
||||
|
||||
using DensityField = field::Field<field::Density>;
|
||||
|
||||
using GravityField = field::Field<field::Gravity>;
|
||||
|
||||
using DisplacementField = field::Field<field::Displacement>;
|
||||
|
||||
using EnthalpyField = field::Field<field::Enthalpy>;
|
||||
|
||||
auto densityCollection = DensityField::make_fec<field::Density::Scalar>(3);
|
||||
|
||||
auto potentialCollection = GravityField::make_fec<field::Gravity::Potential>(3);
|
||||
|
||||
auto fluxCollection = GravityField::make_fec<field::Gravity::Flux>(3);
|
||||
|
||||
auto displacementCollection = DisplacementField::make_fec<field::Displacement::Vector>(3);
|
||||
|
||||
auto enthalpyCollection = EnthalpyField::make_fec<field::Enthalpy::Scalar>(3);
|
||||
|
||||
auto vectorL2Collection =
|
||||
field::Field<field_mfem_test_utils::VectorL2Field>::make_fec<field_mfem_test_utils::VectorL2Field::Vector>(3);
|
||||
|
||||
auto ndCollection =
|
||||
field::Field<field_mfem_test_utils::NdField>::make_fec<field_mfem_test_utils::NdField::Vector>(3);
|
||||
|
||||
REQUIRE(densityCollection != nullptr);
|
||||
|
||||
REQUIRE(potentialCollection != nullptr);
|
||||
|
||||
REQUIRE(fluxCollection != nullptr);
|
||||
|
||||
REQUIRE(displacementCollection != nullptr);
|
||||
|
||||
REQUIRE(enthalpyCollection != nullptr);
|
||||
|
||||
REQUIRE(vectorL2Collection != nullptr);
|
||||
|
||||
REQUIRE(ndCollection != nullptr);
|
||||
|
||||
CHECK(dynamic_cast<mfem::L2_FECollection *>(densityCollection.get()) != nullptr);
|
||||
|
||||
CHECK(dynamic_cast<mfem::L2_FECollection *>(potentialCollection.get()) != nullptr);
|
||||
|
||||
CHECK(dynamic_cast<mfem::RT_FECollection *>(fluxCollection.get()) != nullptr);
|
||||
|
||||
CHECK(dynamic_cast<mfem::H1_FECollection *>(displacementCollection.get()) != nullptr);
|
||||
|
||||
CHECK(dynamic_cast<mfem::H1_FECollection *>(enthalpyCollection.get()) != nullptr);
|
||||
|
||||
CHECK(dynamic_cast<mfem::L2_FECollection *>(vectorL2Collection.get()) != nullptr);
|
||||
|
||||
CHECK(dynamic_cast<mfem::ND_FECollection *>(ndCollection.get()) != nullptr);
|
||||
|
||||
CHECK_THROWS_AS((DensityField::make_fec<field::Density::Scalar>(0)), std::invalid_argument);
|
||||
|
||||
CHECK_THROWS_AS((GravityField::make_fec<field::Gravity::Flux>(-1)), std::invalid_argument);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Field MFEM Creates Parallel Spaces With Registered Dimensions Orders And Ordering",
|
||||
tags::integration &tags::field
|
||||
) {
|
||||
namespace field = mean_field::field;
|
||||
|
||||
mfem::Mesh serialMesh = mfem::Mesh::MakeCartesian2D(4, 2, mfem::Element::QUADRILATERAL, true, 4.0, 2.0);
|
||||
|
||||
mfem::ParMesh mesh(MPI_COMM_WORLD, serialMesh);
|
||||
|
||||
auto densityFec = field::Field<field::Density>::make_fec<field::Density::Scalar>(2);
|
||||
|
||||
auto potentialFec = field::Field<field::Gravity>::make_fec<field::Gravity::Potential>(2);
|
||||
|
||||
auto fluxFec = field::Field<field::Gravity>::make_fec<field::Gravity::Flux>(2);
|
||||
|
||||
auto displacementFec = field::Field<field::Displacement>::make_fec<field::Displacement::Vector>(2);
|
||||
|
||||
auto enthalpyFec = field::Field<field::Enthalpy>::make_fec<field::Enthalpy::Scalar>(2);
|
||||
|
||||
auto vectorL2Fec =
|
||||
field::Field<field_mfem_test_utils::VectorL2Field>::make_fec<field_mfem_test_utils::VectorL2Field::Vector>(2);
|
||||
|
||||
auto ndFec = field::Field<field_mfem_test_utils::NdField>::make_fec<field_mfem_test_utils::NdField::Vector>(2);
|
||||
|
||||
auto densitySpace = field::Field<field::Density>::make_fespace<field::Density::Scalar>(mesh, *densityFec);
|
||||
|
||||
auto potentialSpace = field::Field<field::Gravity>::make_fespace<field::Gravity::Potential>(mesh, *potentialFec);
|
||||
|
||||
auto fluxSpace = field::Field<field::Gravity>::make_fespace<field::Gravity::Flux>(mesh, *fluxFec);
|
||||
|
||||
auto displacementSpace =
|
||||
field::Field<field::Displacement>::make_fespace<field::Displacement::Vector>(mesh, *displacementFec);
|
||||
|
||||
auto enthalpySpace = field::Field<field::Enthalpy>::make_fespace<field::Enthalpy::Scalar>(mesh, *enthalpyFec);
|
||||
|
||||
auto vectorL2Space =
|
||||
field::Field<field_mfem_test_utils::VectorL2Field>::make_fespace<field_mfem_test_utils::VectorL2Field::Vector>(
|
||||
mesh, *vectorL2Fec
|
||||
);
|
||||
|
||||
auto ndSpace = field::Field<field_mfem_test_utils::NdField>::make_fespace<field_mfem_test_utils::NdField::Vector>(
|
||||
mesh, *ndFec
|
||||
);
|
||||
|
||||
REQUIRE(densitySpace != nullptr);
|
||||
REQUIRE(potentialSpace != nullptr);
|
||||
REQUIRE(fluxSpace != nullptr);
|
||||
REQUIRE(displacementSpace != nullptr);
|
||||
REQUIRE(enthalpySpace != nullptr);
|
||||
REQUIRE(vectorL2Space != nullptr);
|
||||
REQUIRE(ndSpace != nullptr);
|
||||
|
||||
CHECK(densitySpace->GetVDim() == 1);
|
||||
|
||||
CHECK(potentialSpace->GetVDim() == 1);
|
||||
|
||||
CHECK(fluxSpace->GetVDim() == 1);
|
||||
|
||||
CHECK(enthalpySpace->GetVDim() == 1);
|
||||
|
||||
CHECK(displacementSpace->GetVDim() == mesh.SpaceDimension());
|
||||
|
||||
CHECK(vectorL2Space->GetVDim() == mesh.SpaceDimension());
|
||||
|
||||
CHECK(ndSpace->GetVDim() == 1);
|
||||
|
||||
CHECK(densitySpace->GetOrdering() == mfem::Ordering::byNODES);
|
||||
|
||||
CHECK(potentialSpace->GetOrdering() == mfem::Ordering::byNODES);
|
||||
|
||||
CHECK(fluxSpace->GetOrdering() == mfem::Ordering::byNODES);
|
||||
|
||||
CHECK(enthalpySpace->GetOrdering() == mfem::Ordering::byNODES);
|
||||
|
||||
/*
|
||||
* Displacement deliberately overrides the generic
|
||||
* vector-H1 rule and is part of the project's block/indexing
|
||||
* contract.
|
||||
*/
|
||||
CHECK(displacementSpace->GetOrdering() == mfem::Ordering::byNODES);
|
||||
|
||||
/*
|
||||
* A generic vector L2 quantity retains the ordinary backend
|
||||
* realization, demonstrating that the displacement behavior is
|
||||
* an intentional specialization rather than a global accident.
|
||||
*/
|
||||
CHECK(vectorL2Space->GetOrdering() == mfem::Ordering::byVDIM);
|
||||
|
||||
CHECK(ndSpace->GetOrdering() == mfem::Ordering::byNODES);
|
||||
|
||||
CHECK(densitySpace->GetMaxElementOrder() == field::Density::Scalar::familyOrder);
|
||||
|
||||
CHECK(potentialSpace->GetMaxElementOrder() == field::Gravity::Potential::familyOrder);
|
||||
|
||||
CHECK(fluxSpace->GetMaxElementOrder() == field::Gravity::Flux::familyOrder + 1);
|
||||
|
||||
CHECK(displacementSpace->GetMaxElementOrder() == field::Displacement::Vector::familyOrder);
|
||||
|
||||
CHECK(enthalpySpace->GetMaxElementOrder() == field::Enthalpy::Scalar::familyOrder);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Field MFEM Typed Queries Preserve Backend Polynomial Order Semantics",
|
||||
tags::unit &tags::field
|
||||
) {
|
||||
namespace field = mean_field::field;
|
||||
namespace quadrature = mean_field::quadrature;
|
||||
namespace utils = mean_field::utils;
|
||||
|
||||
using DensityField = field::Field<field::Density>;
|
||||
|
||||
using GravityField = field::Field<field::Gravity>;
|
||||
|
||||
using EnthalpyField = field::Field<field::Enthalpy>;
|
||||
|
||||
const quadrature::Query densitySource = DensityField::make_query<field::Density::Form::ProjectionSource>(
|
||||
quadrature::QuadratureRole::projection, 3, std::array<int, 1>{4}, utils::DOMAINS::STELLAR,
|
||||
quadrature::MappingKind::general
|
||||
);
|
||||
|
||||
REQUIRE(densitySource.base_order.has_value());
|
||||
|
||||
/*
|
||||
* L2_2 value order 2
|
||||
* + geometry order 3
|
||||
* + dynamic coefficient order 4.
|
||||
*/
|
||||
CHECK(*densitySource.base_order == 9);
|
||||
|
||||
CHECK(densitySource.term == quadrature::Term::density_projection);
|
||||
|
||||
CHECK(densitySource.role == quadrature::QuadratureRole::projection);
|
||||
|
||||
CHECK(densitySource.domain == utils::DOMAINS::STELLAR);
|
||||
|
||||
CHECK(densitySource.mapping == quadrature::MappingKind::general);
|
||||
|
||||
CHECK(densitySource.geometry_weight_order == 3);
|
||||
|
||||
const quadrature::Query hdivMass =
|
||||
GravityField::make_query<field::Gravity::Form::HDivMass>(quadrature::QuadratureRole::discretization, 2);
|
||||
|
||||
REQUIRE(hdivMass.base_order.has_value());
|
||||
|
||||
/*
|
||||
* RT_2 value order is 3, hence
|
||||
* 3 + 3 + geometry 2 = 8.
|
||||
*/
|
||||
CHECK(*hdivMass.base_order == 8);
|
||||
|
||||
const quadrature::Query divergence = GravityField::make_query<field::Gravity::Form::DivergenceCoupling>(
|
||||
quadrature::QuadratureRole::discretization, 2
|
||||
);
|
||||
|
||||
REQUIRE(divergence.base_order.has_value());
|
||||
|
||||
/*
|
||||
* div(RT_2) order 2
|
||||
* + L2_2 order 2
|
||||
* + geometry 2.
|
||||
*/
|
||||
CHECK(*divergence.base_order == 6);
|
||||
|
||||
const quadrature::Query pressureForce = EnthalpyField::make_query<field::Enthalpy::Form::PressureForce>(
|
||||
quadrature::QuadratureRole::discretization, 2, std::array<int, 1>{9}, utils::DOMAINS::STELLAR,
|
||||
quadrature::MappingKind::general
|
||||
);
|
||||
|
||||
REQUIRE(pressureForce.base_order.has_value());
|
||||
|
||||
/*
|
||||
* h value order 3
|
||||
* + grad(d) order 2
|
||||
* + geometry 2
|
||||
* + n=3 pressure extra order 9
|
||||
* = 16.
|
||||
*/
|
||||
CHECK(*pressureForce.base_order == 16);
|
||||
|
||||
const quadrature::Query equilibriumConstant = EnthalpyField::make_query<field::Enthalpy::Form::EquilibriumConstant>(
|
||||
quadrature::QuadratureRole::discretization, 2
|
||||
);
|
||||
|
||||
REQUIRE(equilibriumConstant.base_order.has_value());
|
||||
|
||||
/*
|
||||
* Global scalar C contributes zero polynomial order,
|
||||
* h contributes 3, and geometry contributes 2.
|
||||
*/
|
||||
CHECK(*equilibriumConstant.base_order == 5);
|
||||
|
||||
CHECK_THROWS_AS(
|
||||
(DensityField::make_query<field::Density::Form::ProjectionMass>(quadrature::QuadratureRole::projection, -1)),
|
||||
std::invalid_argument
|
||||
);
|
||||
|
||||
const std::array<int, 1> negativeDynamicOrder{-1};
|
||||
|
||||
CHECK_THROWS_AS(
|
||||
(EnthalpyField::make_query<field::Enthalpy::Form::PressureForce>(
|
||||
quadrature::QuadratureRole::discretization, 2, negativeDynamicOrder
|
||||
)),
|
||||
std::invalid_argument
|
||||
);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Field MFEM Element Support Resolves Semantic Domains Through The Schema",
|
||||
tags::unit &tags::field
|
||||
) {
|
||||
namespace field = mean_field::field;
|
||||
|
||||
const mfem::Mesh mesh = field_mfem_test_utils::make_two_domain_mesh();
|
||||
|
||||
CHECK((field::element_is_in_field_support<field::Density, field_mfem_test_utils::Schema>(mesh, 0)));
|
||||
|
||||
CHECK_FALSE((field::element_is_in_field_support<field::Density, field_mfem_test_utils::Schema>(mesh, 1)));
|
||||
|
||||
CHECK((field::element_is_in_field_support<field::Enthalpy, field_mfem_test_utils::Schema>(mesh, 0)));
|
||||
|
||||
CHECK_FALSE((field::element_is_in_field_support<field::Enthalpy, field_mfem_test_utils::Schema>(mesh, 1)));
|
||||
|
||||
CHECK((field::element_is_in_field_support<field::Gravity, field_mfem_test_utils::Schema>(mesh, 0)));
|
||||
|
||||
CHECK((field::element_is_in_field_support<field::Gravity, field_mfem_test_utils::Schema>(mesh, 1)));
|
||||
|
||||
CHECK((field::element_is_in_field_support<field::Displacement, field_mfem_test_utils::Schema>(mesh, 0)));
|
||||
|
||||
CHECK((field::element_is_in_field_support<field::Displacement, field_mfem_test_utils::Schema>(mesh, 1)));
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Field MFEM L2 Stellar Support Selects Exactly Stellar Element DOFs",
|
||||
tags::unit &tags::field
|
||||
) {
|
||||
namespace field = mean_field::field;
|
||||
|
||||
mfem::Mesh mesh = field_mfem_test_utils::make_two_domain_mesh();
|
||||
|
||||
auto fec = field::Field<field::Density>::make_fec<field::Density::Scalar>(2);
|
||||
|
||||
mfem::FiniteElementSpace space(&mesh, fec.get());
|
||||
|
||||
const auto support = field::resolve_field_local_dof_support<field::Density, field_mfem_test_utils::Schema>(space);
|
||||
|
||||
const std::vector<int> stellarVDofs = field_mfem_test_utils::decoded_element_vdofs(space, 0);
|
||||
|
||||
const std::vector<int> vacuumVDofs = field_mfem_test_utils::decoded_element_vdofs(space, 1);
|
||||
|
||||
REQUIRE_FALSE(stellarVDofs.empty());
|
||||
|
||||
REQUIRE_FALSE(vacuumVDofs.empty());
|
||||
|
||||
CHECK(support.activeVDofMarker.Size() == space.GetVSize());
|
||||
|
||||
CHECK(support.activeVDofs.Size() + support.inactiveVDofs.Size() == space.GetVSize());
|
||||
|
||||
for (const int vdof : stellarVDofs) {
|
||||
CAPTURE(vdof);
|
||||
|
||||
CHECK(support.activeVDofMarker[vdof] == 1);
|
||||
|
||||
CHECK(field_mfem_test_utils::contains(support.activeVDofs, vdof));
|
||||
|
||||
CHECK_FALSE(field_mfem_test_utils::contains(support.inactiveVDofs, vdof));
|
||||
}
|
||||
|
||||
for (const int vdof : vacuumVDofs) {
|
||||
CAPTURE(vdof);
|
||||
|
||||
CHECK(support.activeVDofMarker[vdof] == 0);
|
||||
|
||||
CHECK_FALSE(field_mfem_test_utils::contains(support.activeVDofs, vdof));
|
||||
|
||||
CHECK(field_mfem_test_utils::contains(support.inactiveVDofs, vdof));
|
||||
}
|
||||
|
||||
CHECK(support.activeVDofs.Size() == static_cast<int>(stellarVDofs.size()));
|
||||
|
||||
CHECK(support.inactiveVDofs.Size() == static_cast<int>(vacuumVDofs.size()));
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Field MFEM H1 Stellar Support Keeps Shared Stellar Vacuum Trace DOFs Active",
|
||||
tags::unit &tags::field
|
||||
) {
|
||||
namespace field = mean_field::field;
|
||||
|
||||
mfem::Mesh mesh = field_mfem_test_utils::make_two_domain_mesh();
|
||||
|
||||
auto fec = field::Field<field::Enthalpy>::make_fec<field::Enthalpy::Scalar>(2);
|
||||
|
||||
mfem::FiniteElementSpace space(&mesh, fec.get());
|
||||
|
||||
const auto support = field::resolve_field_local_dof_support<field::Enthalpy, field_mfem_test_utils::Schema>(space);
|
||||
|
||||
const std::vector<int> stellarVDofs = field_mfem_test_utils::decoded_element_vdofs(space, 0);
|
||||
|
||||
const std::vector<int> vacuumVDofs = field_mfem_test_utils::decoded_element_vdofs(space, 1);
|
||||
|
||||
const std::vector<int> interfaceVDofs = field_mfem_test_utils::intersection(stellarVDofs, vacuumVDofs);
|
||||
|
||||
const std::vector<int> vacuumOnlyVDofs = field_mfem_test_utils::difference(vacuumVDofs, stellarVDofs);
|
||||
|
||||
REQUIRE_FALSE(interfaceVDofs.empty());
|
||||
|
||||
REQUIRE_FALSE(vacuumOnlyVDofs.empty());
|
||||
|
||||
for (const int vdof : stellarVDofs) {
|
||||
CAPTURE(vdof);
|
||||
|
||||
CHECK(support.activeVDofMarker[vdof] == 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* This is the central support invariant:
|
||||
*
|
||||
* shared interface DOFs are active because they are touched
|
||||
* by a supported stellar element, even though they are also
|
||||
* touched by a vacuum element.
|
||||
*/
|
||||
for (const int vdof : interfaceVDofs) {
|
||||
CAPTURE(vdof);
|
||||
|
||||
CHECK(support.activeVDofMarker[vdof] == 1);
|
||||
|
||||
CHECK(field_mfem_test_utils::contains(support.activeVDofs, vdof));
|
||||
}
|
||||
|
||||
for (const int vdof : vacuumOnlyVDofs) {
|
||||
CAPTURE(vdof);
|
||||
|
||||
CHECK(support.activeVDofMarker[vdof] == 0);
|
||||
|
||||
CHECK(field_mfem_test_utils::contains(support.inactiveVDofs, vdof));
|
||||
}
|
||||
|
||||
CHECK(support.activeVDofs.Size() + support.inactiveVDofs.Size() == space.GetVSize());
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Field MFEM All Domain Support Activates Every L2 And RT DOF",
|
||||
tags::unit &tags::field
|
||||
) {
|
||||
namespace field = mean_field::field;
|
||||
|
||||
mfem::Mesh mesh = field_mfem_test_utils::make_two_domain_mesh();
|
||||
|
||||
auto potentialFec = field::Field<field::Gravity>::make_fec<field::Gravity::Potential>(2);
|
||||
|
||||
mfem::FiniteElementSpace potentialSpace(&mesh, potentialFec.get());
|
||||
|
||||
const auto potentialSupport =
|
||||
field::resolve_field_local_dof_support<field::Gravity, field_mfem_test_utils::Schema>(potentialSpace);
|
||||
|
||||
CHECK(potentialSupport.activeVDofs.Size() == potentialSpace.GetVSize());
|
||||
|
||||
CHECK(potentialSupport.inactiveVDofs.Size() == 0);
|
||||
|
||||
for (int vdof = 0; vdof < potentialSupport.activeVDofMarker.Size(); ++vdof) {
|
||||
CHECK(potentialSupport.activeVDofMarker[vdof] == 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Exercise signed/oriented MFEM element VDofs through RT as
|
||||
* well. The support resolver must DecodeDof() correctly.
|
||||
*/
|
||||
auto fluxFec = field::Field<field::Gravity>::make_fec<field::Gravity::Flux>(2);
|
||||
|
||||
mfem::FiniteElementSpace fluxSpace(&mesh, fluxFec.get());
|
||||
|
||||
const auto fluxSupport =
|
||||
field::resolve_field_local_dof_support<field::Gravity, field_mfem_test_utils::Schema>(fluxSpace);
|
||||
|
||||
CHECK(fluxSupport.activeVDofs.Size() == fluxSpace.GetVSize());
|
||||
|
||||
CHECK(fluxSupport.inactiveVDofs.Size() == 0);
|
||||
|
||||
for (int vdof = 0; vdof < fluxSupport.activeVDofMarker.Size(); ++vdof) {
|
||||
CHECK(fluxSupport.activeVDofMarker[vdof] == 1);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Field MFEM Support Resolution Uses Schema Material Bindings Rather Than Hard Coded IDs",
|
||||
tags::unit &tags::field
|
||||
) {
|
||||
namespace field = mean_field::field;
|
||||
|
||||
mfem::Mesh mesh = field_mfem_test_utils::make_two_domain_mesh(17, 29);
|
||||
|
||||
auto fec = field::Field<field::Density>::make_fec<field::Density::Scalar>(2);
|
||||
|
||||
mfem::FiniteElementSpace space(&mesh, fec.get());
|
||||
|
||||
const auto support =
|
||||
field::resolve_field_local_dof_support<field::Density, field_mfem_test_utils::AlternateSchema>(space);
|
||||
|
||||
const std::vector<int> stellarVDofs = field_mfem_test_utils::decoded_element_vdofs(space, 0);
|
||||
|
||||
const std::vector<int> vacuumVDofs = field_mfem_test_utils::decoded_element_vdofs(space, 1);
|
||||
|
||||
for (const int vdof : stellarVDofs) {
|
||||
CHECK(support.activeVDofMarker[vdof] == 1);
|
||||
}
|
||||
|
||||
for (const int vdof : vacuumVDofs) {
|
||||
CHECK(support.activeVDofMarker[vdof] == 0);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Field MFEM Parallel Stellar Support Produces Consistent Local And True DOF Partitions",
|
||||
tags::integration &tags::field
|
||||
) {
|
||||
namespace field = mean_field::field;
|
||||
|
||||
mfem::Mesh serialMesh = field_mfem_test_utils::make_parallel_split_mesh();
|
||||
|
||||
mfem::ParMesh mesh(MPI_COMM_WORLD, serialMesh);
|
||||
|
||||
auto fec = field::Field<field::Enthalpy>::make_fec<field::Enthalpy::Scalar>(2);
|
||||
|
||||
auto space = field::Field<field::Enthalpy>::make_fespace<field::Enthalpy::Scalar>(mesh, *fec);
|
||||
|
||||
REQUIRE(space != nullptr);
|
||||
|
||||
const auto support = field::resolve_field_dof_support<field::Enthalpy, field_mfem_test_utils::Schema>(*space);
|
||||
|
||||
CHECK(support.activeVDofMarker.Size() == space->GetVSize());
|
||||
|
||||
CHECK(support.activeVDofs.Size() + support.inactiveVDofs.Size() == space->GetVSize());
|
||||
|
||||
CHECK(support.activeTrueDofMarker.Size() == space->GetTrueVSize());
|
||||
|
||||
CHECK(support.activeTrueDofs.Size() + support.inactiveTrueDofs.Size() == space->GetTrueVSize());
|
||||
|
||||
/*
|
||||
* Every local DOF touched by a supported element must be active
|
||||
* after shared-DOF synchronization.
|
||||
*/
|
||||
for (int elementId = 0; elementId < mesh.GetNE(); ++elementId) {
|
||||
const int materialId = mesh.GetAttribute(elementId);
|
||||
|
||||
const bool stellar =
|
||||
field_mfem_test_utils::Schema::template attribute_belongs_to<mean_field::utils::domain::Stellar>(
|
||||
materialId
|
||||
);
|
||||
|
||||
if (!stellar) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const std::vector<int> vdofs = field_mfem_test_utils::decoded_element_vdofs(*space, elementId);
|
||||
|
||||
for (const int vdof : vdofs) {
|
||||
CAPTURE(elementId, vdof);
|
||||
|
||||
CHECK(support.activeVDofMarker[vdof] == 1);
|
||||
}
|
||||
}
|
||||
|
||||
const long long globalActiveTrueDofs = field_mfem_test_utils::global_sum(support.activeTrueDofs.Size());
|
||||
|
||||
const long long globalInactiveTrueDofs = field_mfem_test_utils::global_sum(support.inactiveTrueDofs.Size());
|
||||
|
||||
/*
|
||||
* The split mesh contains a finite stellar region and a finite
|
||||
* vacuum region with order-three H1 structure, so both categories
|
||||
* must genuinely exist globally.
|
||||
*/
|
||||
CHECK(globalActiveTrueDofs > 0);
|
||||
|
||||
CHECK(globalInactiveTrueDofs > 0);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Field MFEM Parallel All Support Activates Every True Displacement DOF",
|
||||
tags::integration &tags::field
|
||||
) {
|
||||
namespace field = mean_field::field;
|
||||
|
||||
mfem::Mesh serialMesh = field_mfem_test_utils::make_parallel_split_mesh();
|
||||
|
||||
mfem::ParMesh mesh(MPI_COMM_WORLD, serialMesh);
|
||||
|
||||
auto fec = field::Field<field::Displacement>::make_fec<field::Displacement::Vector>(2);
|
||||
|
||||
auto space = field::Field<field::Displacement>::make_fespace<field::Displacement::Vector>(mesh, *fec);
|
||||
|
||||
REQUIRE(space != nullptr);
|
||||
|
||||
const auto support = field::resolve_field_dof_support<field::Displacement, field_mfem_test_utils::Schema>(*space);
|
||||
|
||||
CHECK(support.inactiveVDofs.Size() == 0);
|
||||
|
||||
CHECK(support.activeVDofs.Size() == space->GetVSize());
|
||||
|
||||
CHECK(support.inactiveTrueDofs.Size() == 0);
|
||||
|
||||
CHECK(support.activeTrueDofs.Size() == space->GetTrueVSize());
|
||||
|
||||
for (int index = 0; index < support.activeTrueDofMarker.Size(); ++index) {
|
||||
CHECK(support.activeTrueDofMarker[index] == 1);
|
||||
}
|
||||
}
|
||||
427
tests/field/field_registry.cpp
Normal file
427
tests/field/field_registry.cpp
Normal file
@@ -0,0 +1,427 @@
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include <concepts>
|
||||
#include <cstddef>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
|
||||
import mean_field;
|
||||
import test_helpers;
|
||||
|
||||
namespace field_registry_test_utils {
|
||||
namespace field = mean_field::field;
|
||||
namespace domain = mean_field::utils::domain;
|
||||
namespace quadrature = mean_field::quadrature;
|
||||
|
||||
template <typename ListT> struct TypeListSize;
|
||||
|
||||
template <typename... Ts>
|
||||
struct TypeListSize<field::TypeList<Ts...>> : std::integral_constant<std::size_t, sizeof...(Ts)> { };
|
||||
|
||||
template <typename ListT> inline constexpr std::size_t typeListSize = TypeListSize<ListT>::value;
|
||||
|
||||
struct MissingSupportField {
|
||||
static constexpr std::string_view name = "missing_support";
|
||||
|
||||
using Quantities = field::TypeList<field::GlobalScalarQ>;
|
||||
|
||||
using Constraints = field::TypeList<>;
|
||||
|
||||
using FormList = field::TypeList<>;
|
||||
};
|
||||
|
||||
struct InvalidSupportField {
|
||||
static constexpr std::string_view name = "invalid_support";
|
||||
|
||||
struct InvalidSupport { };
|
||||
|
||||
using Support = InvalidSupport;
|
||||
|
||||
using Quantities = field::TypeList<field::GlobalScalarQ>;
|
||||
|
||||
using Constraints = field::TypeList<>;
|
||||
|
||||
using FormList = field::TypeList<>;
|
||||
};
|
||||
|
||||
struct InvalidQuantityListField {
|
||||
static constexpr std::string_view name = "invalid_quantity_list";
|
||||
|
||||
using Support = field::NonSpatialSupport;
|
||||
|
||||
using Quantities = field::TypeList<int>;
|
||||
|
||||
using Constraints = field::TypeList<>;
|
||||
|
||||
using FormList = field::TypeList<>;
|
||||
};
|
||||
|
||||
struct InvalidFormListField {
|
||||
static constexpr std::string_view name = "invalid_form_list";
|
||||
|
||||
using Support = field::NonSpatialSupport;
|
||||
|
||||
using Quantities = field::TypeList<field::GlobalScalarQ>;
|
||||
|
||||
using Constraints = field::TypeList<>;
|
||||
|
||||
using FormList = field::TypeList<int>;
|
||||
};
|
||||
} // namespace field_registry_test_utils
|
||||
|
||||
TEST_CASE(
|
||||
"Field Registry Recognizes Every Production Field And Rejects Incomplete Definitions",
|
||||
tags::unit &tags::field
|
||||
) {
|
||||
namespace field = mean_field::field;
|
||||
|
||||
STATIC_REQUIRE(field::FieldTag<field::Density>);
|
||||
|
||||
STATIC_REQUIRE(field::FieldTag<field::Gravity>);
|
||||
|
||||
STATIC_REQUIRE(field::FieldTag<field::Displacement>);
|
||||
|
||||
STATIC_REQUIRE(field::FieldTag<field::Enthalpy>);
|
||||
|
||||
STATIC_REQUIRE(field::FieldTag<field::BarotropicConstant>);
|
||||
|
||||
STATIC_REQUIRE_FALSE(field::FieldTag<field_registry_test_utils::MissingSupportField>);
|
||||
|
||||
STATIC_REQUIRE_FALSE(field::FieldTag<field_registry_test_utils::InvalidSupportField>);
|
||||
|
||||
STATIC_REQUIRE_FALSE(field::FieldTag<field_registry_test_utils::InvalidQuantityListField>);
|
||||
|
||||
STATIC_REQUIRE_FALSE(field::FieldTag<field_registry_test_utils::InvalidFormListField>);
|
||||
|
||||
CHECK(true);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Field Registry Assigns The Intended Semantic Support To Every Production Field",
|
||||
tags::unit &tags::field
|
||||
) {
|
||||
namespace field = mean_field::field;
|
||||
namespace domain = mean_field::utils::domain;
|
||||
|
||||
STATIC_REQUIRE(field::DomainSupportedField<field::Density>);
|
||||
|
||||
STATIC_REQUIRE(field::DomainSupportedField<field::Enthalpy>);
|
||||
|
||||
STATIC_REQUIRE(field::DomainSupportedField<field::Gravity>);
|
||||
|
||||
STATIC_REQUIRE(field::DomainSupportedField<field::Displacement>);
|
||||
|
||||
STATIC_REQUIRE(field::NonSpatialField<field::BarotropicConstant>);
|
||||
|
||||
STATIC_REQUIRE(std::same_as<field::FieldDomainT<field::Density>, domain::Stellar>);
|
||||
|
||||
STATIC_REQUIRE(std::same_as<field::FieldDomainT<field::Enthalpy>, domain::Stellar>);
|
||||
|
||||
STATIC_REQUIRE(std::same_as<field::FieldDomainT<field::Gravity>, domain::All>);
|
||||
|
||||
STATIC_REQUIRE(std::same_as<field::FieldDomainT<field::Displacement>, domain::All>);
|
||||
|
||||
STATIC_REQUIRE(std::same_as<field::FieldSupportT<field::BarotropicConstant>, field::NonSpatialSupport>);
|
||||
|
||||
CHECK(true);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Density Registry Definition Is Complete And Self Consistent",
|
||||
tags::unit &tags::field
|
||||
) {
|
||||
namespace field = mean_field::field;
|
||||
namespace quadrature = mean_field::quadrature;
|
||||
|
||||
CHECK(field::Density::name == std::string_view{"density"});
|
||||
|
||||
CHECK(field::Density::Scalar::symbol == std::string_view{"ρ"});
|
||||
|
||||
STATIC_REQUIRE(field::Density::scalarOrder == 2);
|
||||
|
||||
STATIC_REQUIRE(field::Density::Scalar::rankValue == 0);
|
||||
|
||||
STATIC_REQUIRE(field::Density::Scalar::familyOrder == field::Density::scalarOrder);
|
||||
|
||||
STATIC_REQUIRE(std::same_as<typename field::Density::Scalar::Space, field::L2>);
|
||||
|
||||
STATIC_REQUIRE(std::same_as<typename field::Density::Scalar::Relation, field::FieldRelation::Independent>);
|
||||
|
||||
STATIC_REQUIRE(field_registry_test_utils::typeListSize<field::Density::Quantities> == 1);
|
||||
|
||||
STATIC_REQUIRE(field_registry_test_utils::typeListSize<field::Density::Constraints> == 0);
|
||||
|
||||
STATIC_REQUIRE(field_registry_test_utils::typeListSize<field::Density::FormList> == 8);
|
||||
|
||||
STATIC_REQUIRE(field::Density::constraintsAreValid);
|
||||
|
||||
STATIC_REQUIRE(field::typeListContains<field::Density::Form::ProjectionMass, field::Density::FormList>);
|
||||
|
||||
STATIC_REQUIRE(field::typeListContains<field::Density::Form::ProjectionSource, field::Density::FormList>);
|
||||
|
||||
STATIC_REQUIRE(field::typeListContains<field::Density::Form::EosClosureMass, field::Density::FormList>);
|
||||
|
||||
STATIC_REQUIRE(field::typeListContains<field::Density::Form::MassConservation, field::Density::FormList>);
|
||||
|
||||
STATIC_REQUIRE(field::typeListContains<field::Density::Form::MassNormalization, field::Density::FormList>);
|
||||
|
||||
STATIC_REQUIRE(field::typeListContains<field::Density::Form::CenterOfMass, field::Density::FormList>);
|
||||
|
||||
STATIC_REQUIRE(field::typeListContains<field::Density::Form::Quadrupole, field::Density::FormList>);
|
||||
|
||||
STATIC_REQUIRE(field::typeListContains<field::Density::Form::ErrorNorm, field::Density::FormList>);
|
||||
|
||||
STATIC_REQUIRE(field::Density::Form::ProjectionMass::policyKey == quadrature::Term::density_projection);
|
||||
|
||||
STATIC_REQUIRE(field::Density::Form::ProjectionMass::dynamicOrderCount == 0);
|
||||
|
||||
STATIC_REQUIRE(
|
||||
std::same_as<
|
||||
typename field::Density::Form::ProjectionMass::Operands,
|
||||
field::TypeList<field::Operand<field::Density::Scalar>, field::Operand<field::Density::Scalar>>>
|
||||
);
|
||||
|
||||
STATIC_REQUIRE(field::Density::Form::ProjectionSource::dynamicOrderCount == 1);
|
||||
|
||||
STATIC_REQUIRE(field::Density::Form::EosClosureMass::policyKey == quadrature::Term::eos_closure);
|
||||
|
||||
STATIC_REQUIRE(field::Density::Form::MassConservation::policyKey == quadrature::Term::mass_conservation);
|
||||
|
||||
STATIC_REQUIRE(field::Density::Form::MassNormalization::policyKey == quadrature::Term::mass_normalization);
|
||||
|
||||
STATIC_REQUIRE(field::Density::Form::CenterOfMass::dynamicOrderCount == 1);
|
||||
|
||||
STATIC_REQUIRE(field::Density::Form::Quadrupole::dynamicOrderCount == 1);
|
||||
|
||||
CHECK(true);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Gravity Registry Defines A Stable Mixed RT L2 Pair And All Registered Forms",
|
||||
tags::unit &tags::field
|
||||
) {
|
||||
namespace field = mean_field::field;
|
||||
namespace quadrature = mean_field::quadrature;
|
||||
|
||||
CHECK(field::Gravity::name == std::string_view{"gravity"});
|
||||
|
||||
CHECK(field::Gravity::Potential::symbol == std::string_view{"φ"});
|
||||
|
||||
CHECK(field::Gravity::Flux::symbol == std::string_view{"∇φ"});
|
||||
|
||||
STATIC_REQUIRE(field::Gravity::potentialOrder == 2);
|
||||
|
||||
STATIC_REQUIRE(field::Gravity::fluxOrder == 2);
|
||||
|
||||
STATIC_REQUIRE(std::same_as<typename field::Gravity::Potential::Space, field::L2>);
|
||||
|
||||
STATIC_REQUIRE(std::same_as<typename field::Gravity::Flux::Space, field::RT>);
|
||||
|
||||
STATIC_REQUIRE(field::Gravity::Potential::rankValue == 0);
|
||||
|
||||
STATIC_REQUIRE(field::Gravity::Flux::rankValue == 1);
|
||||
|
||||
STATIC_REQUIRE(field::DerivedQuantity<field::Gravity::Flux>);
|
||||
|
||||
STATIC_REQUIRE(std::same_as<field::RelationTargetT<field::Gravity::Flux>, field::Gravity::Potential>);
|
||||
|
||||
STATIC_REQUIRE(field_registry_test_utils::typeListSize<field::Gravity::Quantities> == 2);
|
||||
|
||||
STATIC_REQUIRE(field_registry_test_utils::typeListSize<field::Gravity::Constraints> == 1);
|
||||
|
||||
STATIC_REQUIRE(field_registry_test_utils::typeListSize<field::Gravity::FormList> == 7);
|
||||
|
||||
STATIC_REQUIRE(field::Gravity::constraintsAreValid);
|
||||
|
||||
STATIC_REQUIRE(field::typeListContains<field::Gravity::Form::HDivMass, field::Gravity::FormList>);
|
||||
|
||||
STATIC_REQUIRE(field::typeListContains<field::Gravity::Form::DivergenceCoupling, field::Gravity::FormList>);
|
||||
|
||||
STATIC_REQUIRE(field::typeListContains<field::Gravity::Form::Boundary, field::Gravity::FormList>);
|
||||
|
||||
STATIC_REQUIRE(field::typeListContains<field::Gravity::Form::SourceLinear, field::Gravity::FormList>);
|
||||
|
||||
STATIC_REQUIRE(field::typeListContains<field::Gravity::Form::SourceProjection, field::Gravity::FormList>);
|
||||
|
||||
STATIC_REQUIRE(field::typeListContains<field::Gravity::Form::PotentialErrorNorm, field::Gravity::FormList>);
|
||||
|
||||
STATIC_REQUIRE(field::typeListContains<field::Gravity::Form::FluxErrorNorm, field::Gravity::FormList>);
|
||||
|
||||
STATIC_REQUIRE(field::Gravity::Form::HDivMass::policyKey == quadrature::Term::gravity_hdiv_mass);
|
||||
|
||||
STATIC_REQUIRE(
|
||||
std::same_as<
|
||||
typename field::Gravity::Form::HDivMass::Operands,
|
||||
field::TypeList<field::Operand<field::Gravity::Flux>, field::Operand<field::Gravity::Flux>>>
|
||||
);
|
||||
|
||||
STATIC_REQUIRE(
|
||||
std::same_as<
|
||||
typename field::Gravity::Form::DivergenceCoupling::Operands,
|
||||
field::TypeList<
|
||||
field::Operand<field::Gravity::Flux, field::FieldOperation::Divergence>,
|
||||
field::Operand<field::Gravity::Potential>>>
|
||||
);
|
||||
|
||||
STATIC_REQUIRE(
|
||||
std::same_as<
|
||||
typename field::Gravity::Form::Boundary::Operands,
|
||||
field::TypeList<
|
||||
field::Operand<field::Gravity::Flux, field::FieldOperation::NormalTrace>,
|
||||
field::Operand<field::Gravity::Flux, field::FieldOperation::NormalTrace>>>
|
||||
);
|
||||
|
||||
STATIC_REQUIRE(
|
||||
std::same_as<
|
||||
typename field::Gravity::Form::SourceLinear::Operands,
|
||||
field::TypeList<field::Operand<field::Density::Scalar>, field::Operand<field::Gravity::Potential>>>
|
||||
);
|
||||
|
||||
CHECK(true);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Displacement Registry Preserves Vector H1 Geometry And Force Forms",
|
||||
tags::unit &tags::field
|
||||
) {
|
||||
namespace field = mean_field::field;
|
||||
namespace quadrature = mean_field::quadrature;
|
||||
|
||||
CHECK(field::Displacement::name == std::string_view{"displacement"});
|
||||
|
||||
CHECK(field::Displacement::Vector::symbol == std::string_view{"d"});
|
||||
|
||||
STATIC_REQUIRE(field::Displacement::vectorOrder == 3);
|
||||
|
||||
STATIC_REQUIRE(field::Displacement::Vector::rankValue == 1);
|
||||
|
||||
STATIC_REQUIRE(std::same_as<typename field::Displacement::Vector::Space, field::H1>);
|
||||
|
||||
STATIC_REQUIRE(field_registry_test_utils::typeListSize<field::Displacement::FormList> == 4);
|
||||
|
||||
STATIC_REQUIRE(field::Displacement::constraintsAreValid);
|
||||
|
||||
STATIC_REQUIRE(field::Displacement::Form::MeshExtension::policyKey == quadrature::Term::mesh_extension);
|
||||
|
||||
STATIC_REQUIRE(
|
||||
std::same_as<
|
||||
typename field::Displacement::Form::MeshExtension::Operands,
|
||||
field::TypeList<
|
||||
field::Operand<field::Displacement::Vector, field::FieldOperation::Gradient>,
|
||||
field::Operand<field::Displacement::Vector, field::FieldOperation::Gradient>>>
|
||||
);
|
||||
|
||||
STATIC_REQUIRE(
|
||||
std::same_as<
|
||||
typename field::Displacement::Form::GravityForce::Operands,
|
||||
field::TypeList<
|
||||
field::Operand<field::Density::Scalar>, field::Operand<field::Gravity::Flux>,
|
||||
field::Operand<field::Displacement::Vector, field::FieldOperation::Gradient>,
|
||||
field::Operand<field::Displacement::Vector>>>
|
||||
);
|
||||
|
||||
STATIC_REQUIRE(field::Displacement::Form::CentrifugalForce::dynamicOrderCount == 1);
|
||||
|
||||
STATIC_REQUIRE(field::Displacement::Form::CentrifugalForce::policyKey == quadrature::Term::centrifugal);
|
||||
|
||||
CHECK(true);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Enthalpy Registry Preserves Continuous Stellar Field And Coupled Forms",
|
||||
tags::unit &tags::field
|
||||
) {
|
||||
namespace field = mean_field::field;
|
||||
namespace quadrature = mean_field::quadrature;
|
||||
|
||||
CHECK(field::Enthalpy::name == std::string_view{"specific_enthalpy"});
|
||||
|
||||
CHECK(field::Enthalpy::Scalar::symbol == std::string_view{"h"});
|
||||
|
||||
STATIC_REQUIRE(field::Enthalpy::scalarOrder == 3);
|
||||
|
||||
STATIC_REQUIRE(field::Enthalpy::Scalar::rankValue == 0);
|
||||
|
||||
STATIC_REQUIRE(std::same_as<typename field::Enthalpy::Scalar::Space, field::H1>);
|
||||
|
||||
STATIC_REQUIRE(field_registry_test_utils::typeListSize<field::Enthalpy::FormList> == 9);
|
||||
|
||||
STATIC_REQUIRE(field::typeListContains<field::Enthalpy::Form::EosClosureSource, field::Enthalpy::FormList>);
|
||||
|
||||
STATIC_REQUIRE(field::typeListContains<field::Enthalpy::Form::EquilibriumEnthalpy, field::Enthalpy::FormList>);
|
||||
|
||||
STATIC_REQUIRE(field::typeListContains<field::Enthalpy::Form::EquilibriumGravity, field::Enthalpy::FormList>);
|
||||
|
||||
STATIC_REQUIRE(field::typeListContains<field::Enthalpy::Form::EquilibriumRotation, field::Enthalpy::FormList>);
|
||||
|
||||
STATIC_REQUIRE(field::typeListContains<field::Enthalpy::Form::EquilibriumConstant, field::Enthalpy::FormList>);
|
||||
|
||||
STATIC_REQUIRE(field::typeListContains<field::Enthalpy::Form::IsobaricSurface, field::Enthalpy::FormList>);
|
||||
|
||||
STATIC_REQUIRE(field::typeListContains<field::Enthalpy::Form::PressureIntegral, field::Enthalpy::FormList>);
|
||||
|
||||
STATIC_REQUIRE(field::typeListContains<field::Enthalpy::Form::PressureForce, field::Enthalpy::FormList>);
|
||||
|
||||
STATIC_REQUIRE(field::typeListContains<field::Enthalpy::Form::ErrorNorm, field::Enthalpy::FormList>);
|
||||
|
||||
STATIC_REQUIRE(field::Enthalpy::Form::EosClosureSource::dynamicOrderCount == 1);
|
||||
|
||||
STATIC_REQUIRE(
|
||||
std::same_as<
|
||||
typename field::Enthalpy::Form::EosClosureSource::Operands,
|
||||
field::TypeList<field::Operand<field::Enthalpy::Scalar>, field::Operand<field::Density::Scalar>>>
|
||||
);
|
||||
|
||||
STATIC_REQUIRE(
|
||||
std::same_as<
|
||||
typename field::Enthalpy::Form::EquilibriumGravity::Operands,
|
||||
field::TypeList<field::Operand<field::Gravity::Potential>, field::Operand<field::Enthalpy::Scalar>>>
|
||||
);
|
||||
|
||||
STATIC_REQUIRE(
|
||||
std::same_as<
|
||||
typename field::Enthalpy::Form::EquilibriumConstant::Operands,
|
||||
field::TypeList<field::Operand<field::BarotropicConstant::Scalar>, field::Operand<field::Enthalpy::Scalar>>>
|
||||
);
|
||||
|
||||
STATIC_REQUIRE(field::Enthalpy::Form::IsobaricSurface::policyKey == quadrature::Term::isobaric_surface);
|
||||
|
||||
STATIC_REQUIRE(field::Enthalpy::Form::PressureIntegral::policyKey == quadrature::Term::pressure_integral);
|
||||
|
||||
STATIC_REQUIRE(field::Enthalpy::Form::PressureForce::policyKey == quadrature::Term::pressure_force);
|
||||
|
||||
STATIC_REQUIRE(
|
||||
std::same_as<
|
||||
typename field::Enthalpy::Form::PressureForce::Operands,
|
||||
field::TypeList<
|
||||
field::Operand<field::Enthalpy::Scalar>,
|
||||
field::Operand<field::Displacement::Vector, field::FieldOperation::Gradient>>>
|
||||
);
|
||||
|
||||
CHECK(true);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Barotropic Constant Registry Is A Non Spatial Unit Sized Scalar",
|
||||
tags::unit &tags::field
|
||||
) {
|
||||
namespace field = mean_field::field;
|
||||
|
||||
CHECK(field::BarotropicConstant::name == std::string_view{"barotropic_constant"});
|
||||
|
||||
CHECK(field::BarotropicConstant::Scalar::symbol == std::string_view{"C"});
|
||||
|
||||
STATIC_REQUIRE(field::GlobalScalarQuantity<field::BarotropicConstant::Scalar>);
|
||||
|
||||
STATIC_REQUIRE(field::BarotropicConstant::Scalar::staticBlockSize == 1);
|
||||
|
||||
STATIC_REQUIRE(field_registry_test_utils::typeListSize<field::BarotropicConstant::Quantities> == 1);
|
||||
|
||||
STATIC_REQUIRE(field_registry_test_utils::typeListSize<field::BarotropicConstant::Constraints> == 0);
|
||||
|
||||
STATIC_REQUIRE(field_registry_test_utils::typeListSize<field::BarotropicConstant::FormList> == 0);
|
||||
|
||||
STATIC_REQUIRE(field::BarotropicConstant::constraintsAreValid);
|
||||
|
||||
CHECK(true);
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -17,21 +17,14 @@ TEST_CASE(
|
||||
constexpr double jacobian_tolerance = 1.0e-8;
|
||||
constexpr double zero_tolerance = 1.0e-14;
|
||||
|
||||
constexpr int velocity_block =
|
||||
solver::block_index(solver::FieldBlock::velocity);
|
||||
constexpr int density_block =
|
||||
solver::block_index(solver::FieldBlock::density);
|
||||
constexpr int gravity_gradient_block =
|
||||
solver::block_index(solver::FieldBlock::gravity_gradient);
|
||||
constexpr int gravity_potential_block =
|
||||
solver::block_index(solver::FieldBlock::gravity_potential);
|
||||
constexpr int displacement_block =
|
||||
solver::block_index(solver::FieldBlock::displacement);
|
||||
constexpr int block_count = solver::field_block_count;
|
||||
constexpr int velocity_block = solver::block_index(solver::FieldBlock::velocity);
|
||||
constexpr int density_block = solver::block_index(solver::FieldBlock::density);
|
||||
constexpr int gravity_gradient_block = solver::block_index(solver::FieldBlock::gravity_gradient);
|
||||
constexpr int gravity_potential_block = solver::block_index(solver::FieldBlock::gravity_potential);
|
||||
constexpr int displacement_block = solver::block_index(solver::FieldBlock::displacement);
|
||||
constexpr int block_count = solver::field_block_count;
|
||||
|
||||
mfem::Mesh mesh = mfem::Mesh::MakeCartesian3D(
|
||||
1, 1, 1, mfem::Element::HEXAHEDRON, 1.0, 1.0, 1.0
|
||||
);
|
||||
mfem::Mesh mesh = mfem::Mesh::MakeCartesian3D(1, 1, 1, mfem::Element::HEXAHEDRON, 1.0, 1.0, 1.0);
|
||||
|
||||
mfem::H1_FECollection velocity_fec(2, dim);
|
||||
mfem::L2_FECollection density_fec(1, dim);
|
||||
@@ -39,54 +32,35 @@ TEST_CASE(
|
||||
mfem::L2_FECollection gravity_potential_fec(1, dim);
|
||||
mfem::H1_FECollection displacement_fec(2, dim);
|
||||
|
||||
mfem::FiniteElementSpace velocity_fes(
|
||||
&mesh, &velocity_fec, dim, mfem::Ordering::byVDIM
|
||||
);
|
||||
mfem::FiniteElementSpace velocity_fes(&mesh, &velocity_fec, dim, mfem::Ordering::byVDIM);
|
||||
mfem::FiniteElementSpace density_fes(&mesh, &density_fec);
|
||||
mfem::FiniteElementSpace 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 gravity_potential_fes(&mesh, &gravity_potential_fec);
|
||||
mfem::FiniteElementSpace displacement_fes(&mesh, &displacement_fec, dim, mfem::Ordering::byVDIM);
|
||||
|
||||
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()
|
||||
)
|
||||
);
|
||||
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());
|
||||
|
||||
const mfem::FiniteElement *velocity_element = velocity_fes.GetFE(0);
|
||||
const mfem::FiniteElement *density_element = density_fes.GetFE(0);
|
||||
const mfem::FiniteElement *gravity_gradient_element =
|
||||
gravity_gradient_fes.GetFE(0);
|
||||
const mfem::FiniteElement *gravity_potential_element =
|
||||
gravity_potential_fes.GetFE(0);
|
||||
const mfem::FiniteElement *displacement_element = displacement_fes.GetFE(0);
|
||||
mfem::ElementTransformation *transformation =
|
||||
mesh.GetElementTransformation(0);
|
||||
const mfem::FiniteElement *velocity_element = velocity_fes.GetFE(0);
|
||||
const mfem::FiniteElement *density_element = density_fes.GetFE(0);
|
||||
const mfem::FiniteElement *gravity_gradient_element = gravity_gradient_fes.GetFE(0);
|
||||
const mfem::FiniteElement *gravity_potential_element = gravity_potential_fes.GetFE(0);
|
||||
const mfem::FiniteElement *displacement_element = displacement_fes.GetFE(0);
|
||||
mfem::ElementTransformation *transformation = mesh.GetElementTransformation(0);
|
||||
|
||||
const int velocity_dofs_count = velocity_element->GetDof();
|
||||
const int density_dofs_count = density_element->GetDof();
|
||||
const int gravity_gradient_dofs_count = gravity_gradient_element->GetDof();
|
||||
const int gravity_potential_dofs_count =
|
||||
gravity_potential_element->GetDof();
|
||||
const int displacement_dofs_count = displacement_element->GetDof();
|
||||
const int velocity_size = dim * velocity_dofs_count;
|
||||
const int displacement_size = dim * displacement_dofs_count;
|
||||
const int velocity_dofs_count = velocity_element->GetDof();
|
||||
const int density_dofs_count = density_element->GetDof();
|
||||
const int gravity_gradient_dofs_count = gravity_gradient_element->GetDof();
|
||||
const int gravity_potential_dofs_count = gravity_potential_element->GetDof();
|
||||
const int displacement_dofs_count = displacement_element->GetDof();
|
||||
const int velocity_size = dim * velocity_dofs_count;
|
||||
const int displacement_size = dim * displacement_dofs_count;
|
||||
|
||||
mfem::Vector velocity_dofs(velocity_size);
|
||||
mfem::Vector density_dofs(density_dofs_count);
|
||||
@@ -116,9 +90,8 @@ TEST_CASE(
|
||||
}
|
||||
|
||||
for (int i = 0; i < gravity_gradient_dofs_count; ++i) {
|
||||
const double sign = i % 3 == 0 ? -1.0 : 1.0;
|
||||
gravity_gradient_direction(i) =
|
||||
sign * (0.03 + 0.005 * static_cast<double>(i));
|
||||
const double sign = i % 3 == 0 ? -1.0 : 1.0;
|
||||
gravity_gradient_direction(i) = sign * (0.03 + 0.005 * static_cast<double>(i));
|
||||
}
|
||||
|
||||
for (int i = 0; i < velocity_size; ++i) {
|
||||
@@ -161,14 +134,10 @@ TEST_CASE(
|
||||
);
|
||||
|
||||
const int maximum_order = std::max(
|
||||
velocity_element->GetOrder(),
|
||||
std::max(
|
||||
density_element->GetOrder(), gravity_gradient_element->GetOrder()
|
||||
)
|
||||
);
|
||||
const mfem::IntegrationRule &integration_rule = mfem::IntRules.Get(
|
||||
velocity_element->GetGeomType(), 2 * maximum_order + 8
|
||||
velocity_element->GetOrder(), std::max(density_element->GetOrder(), gravity_gradient_element->GetOrder())
|
||||
);
|
||||
const mfem::IntegrationRule &integration_rule =
|
||||
mfem::IntRules.Get(velocity_element->GetGeomType(), 2 * maximum_order + 8);
|
||||
integrator.SetIntegrationRule(integration_rule);
|
||||
|
||||
mfem::DenseMatrix dv_dv(velocity_size, velocity_size);
|
||||
@@ -180,9 +149,7 @@ TEST_CASE(
|
||||
dv_dgrad_phi = 1.0;
|
||||
dv_ddisplacement = 1.0;
|
||||
|
||||
mfem::Array2D<mfem::DenseMatrix *> element_matrices(
|
||||
block_count, block_count
|
||||
);
|
||||
mfem::Array2D<mfem::DenseMatrix *> element_matrices(block_count, block_count);
|
||||
|
||||
for (int row = 0; row < block_count; ++row) {
|
||||
for (int column = 0; column < block_count; ++column) {
|
||||
@@ -193,25 +160,19 @@ TEST_CASE(
|
||||
element_matrices(velocity_block, velocity_block) = &dv_dv;
|
||||
element_matrices(velocity_block, density_block) = &dv_drho;
|
||||
element_matrices(velocity_block, gravity_gradient_block) = &dv_dgrad_phi;
|
||||
element_matrices(velocity_block, displacement_block) = &dv_ddisplacement;
|
||||
element_matrices(velocity_block, displacement_block) = &dv_ddisplacement;
|
||||
|
||||
integrator.AssembleElementGrad(
|
||||
elements, *transformation, element_state, element_matrices
|
||||
);
|
||||
integrator.AssembleElementGrad(elements, *transformation, element_state, element_matrices);
|
||||
|
||||
auto assemble_velocity_residual =
|
||||
[&](const mfem::Vector &density_state,
|
||||
const mfem::Vector &gravity_gradient_state) {
|
||||
element_state[density_block] = &density_state;
|
||||
element_state[gravity_gradient_block] = &gravity_gradient_state;
|
||||
integrator.AssembleElementVector(
|
||||
elements, *transformation, element_state, element_residual
|
||||
);
|
||||
return mfem::Vector(velocity_residual);
|
||||
};
|
||||
auto assemble_velocity_residual = [&](const mfem::Vector &density_state,
|
||||
const mfem::Vector &gravity_gradient_state) {
|
||||
element_state[density_block] = &density_state;
|
||||
element_state[gravity_gradient_block] = &gravity_gradient_state;
|
||||
integrator.AssembleElementVector(elements, *transformation, element_state, element_residual);
|
||||
return mfem::Vector(velocity_residual);
|
||||
};
|
||||
|
||||
auto relative_error = [](mfem::Vector computed,
|
||||
const mfem::Vector &reference) {
|
||||
auto relative_error = [](mfem::Vector computed, const mfem::Vector &reference) {
|
||||
computed -= reference;
|
||||
return computed.Norml2() / std::max(reference.Norml2(), 1.0e-30);
|
||||
};
|
||||
@@ -221,10 +182,8 @@ TEST_CASE(
|
||||
density_plus.Add(finite_difference_step, density_direction);
|
||||
density_minus.Add(-finite_difference_step, density_direction);
|
||||
|
||||
mfem::Vector density_residual_plus =
|
||||
assemble_velocity_residual(density_plus, gravity_gradient_dofs);
|
||||
mfem::Vector density_residual_minus =
|
||||
assemble_velocity_residual(density_minus, gravity_gradient_dofs);
|
||||
mfem::Vector density_residual_plus = assemble_velocity_residual(density_plus, gravity_gradient_dofs);
|
||||
mfem::Vector density_residual_minus = assemble_velocity_residual(density_minus, gravity_gradient_dofs);
|
||||
mfem::Vector density_finite_difference(density_residual_plus);
|
||||
density_finite_difference -= density_residual_minus;
|
||||
density_finite_difference *= 0.5 / finite_difference_step;
|
||||
@@ -234,17 +193,11 @@ TEST_CASE(
|
||||
|
||||
mfem::Vector gravity_gradient_plus(gravity_gradient_dofs);
|
||||
mfem::Vector gravity_gradient_minus(gravity_gradient_dofs);
|
||||
gravity_gradient_plus.Add(
|
||||
finite_difference_step, gravity_gradient_direction
|
||||
);
|
||||
gravity_gradient_minus.Add(
|
||||
-finite_difference_step, gravity_gradient_direction
|
||||
);
|
||||
gravity_gradient_plus.Add(finite_difference_step, gravity_gradient_direction);
|
||||
gravity_gradient_minus.Add(-finite_difference_step, gravity_gradient_direction);
|
||||
|
||||
mfem::Vector gravity_residual_plus =
|
||||
assemble_velocity_residual(density_dofs, gravity_gradient_plus);
|
||||
mfem::Vector gravity_residual_minus =
|
||||
assemble_velocity_residual(density_dofs, gravity_gradient_minus);
|
||||
mfem::Vector gravity_residual_plus = assemble_velocity_residual(density_dofs, gravity_gradient_plus);
|
||||
mfem::Vector gravity_residual_minus = assemble_velocity_residual(density_dofs, gravity_gradient_minus);
|
||||
mfem::Vector gravity_finite_difference(gravity_residual_plus);
|
||||
gravity_finite_difference -= gravity_residual_minus;
|
||||
gravity_finite_difference *= 0.5 / finite_difference_step;
|
||||
@@ -258,19 +211,11 @@ TEST_CASE(
|
||||
mfem::Vector combined_gravity_minus(gravity_gradient_dofs);
|
||||
combined_density_plus.Add(finite_difference_step, density_direction);
|
||||
combined_density_minus.Add(-finite_difference_step, density_direction);
|
||||
combined_gravity_plus.Add(
|
||||
finite_difference_step, gravity_gradient_direction
|
||||
);
|
||||
combined_gravity_minus.Add(
|
||||
-finite_difference_step, gravity_gradient_direction
|
||||
);
|
||||
combined_gravity_plus.Add(finite_difference_step, gravity_gradient_direction);
|
||||
combined_gravity_minus.Add(-finite_difference_step, gravity_gradient_direction);
|
||||
|
||||
mfem::Vector combined_residual_plus = assemble_velocity_residual(
|
||||
combined_density_plus, combined_gravity_plus
|
||||
);
|
||||
mfem::Vector combined_residual_minus = assemble_velocity_residual(
|
||||
combined_density_minus, combined_gravity_minus
|
||||
);
|
||||
mfem::Vector combined_residual_plus = assemble_velocity_residual(combined_density_plus, combined_gravity_plus);
|
||||
mfem::Vector combined_residual_minus = assemble_velocity_residual(combined_density_minus, combined_gravity_minus);
|
||||
mfem::Vector combined_finite_difference(combined_residual_plus);
|
||||
combined_finite_difference -= combined_residual_minus;
|
||||
combined_finite_difference *= 0.5 / finite_difference_step;
|
||||
@@ -283,39 +228,19 @@ TEST_CASE(
|
||||
dv_dv.Mult(velocity_direction, inactive_velocity_action);
|
||||
dv_ddisplacement.Mult(displacement_direction, inactive_displacement_action);
|
||||
|
||||
const double density_relative_error =
|
||||
relative_error(density_finite_difference, density_jacobian_action);
|
||||
const double gravity_relative_error =
|
||||
relative_error(gravity_finite_difference, gravity_jacobian_action);
|
||||
const double combined_relative_error =
|
||||
relative_error(combined_finite_difference, combined_jacobian_action);
|
||||
const double density_relative_error = relative_error(density_finite_difference, density_jacobian_action);
|
||||
const double gravity_relative_error = relative_error(gravity_finite_difference, gravity_jacobian_action);
|
||||
const double combined_relative_error = relative_error(combined_finite_difference, combined_jacobian_action);
|
||||
|
||||
INFO("Density Jacobian relative error = " << density_relative_error);
|
||||
INFO(
|
||||
"Gravity-gradient Jacobian relative error = " << gravity_relative_error
|
||||
);
|
||||
INFO("Gravity-gradient Jacobian relative error = " << gravity_relative_error);
|
||||
INFO("Combined Jacobian relative error = " << combined_relative_error);
|
||||
|
||||
CHECK_THAT(
|
||||
density_relative_error,
|
||||
Catch::Matchers::WithinAbs(0.0, jacobian_tolerance)
|
||||
);
|
||||
CHECK_THAT(
|
||||
gravity_relative_error,
|
||||
Catch::Matchers::WithinAbs(0.0, jacobian_tolerance)
|
||||
);
|
||||
CHECK_THAT(
|
||||
combined_relative_error,
|
||||
Catch::Matchers::WithinAbs(0.0, jacobian_tolerance)
|
||||
);
|
||||
CHECK_THAT(
|
||||
inactive_velocity_action.Norml2(),
|
||||
Catch::Matchers::WithinAbs(0.0, zero_tolerance)
|
||||
);
|
||||
CHECK_THAT(
|
||||
inactive_displacement_action.Norml2(),
|
||||
Catch::Matchers::WithinAbs(0.0, zero_tolerance)
|
||||
);
|
||||
CHECK_THAT(density_relative_error, Catch::Matchers::WithinAbs(0.0, jacobian_tolerance));
|
||||
CHECK_THAT(gravity_relative_error, Catch::Matchers::WithinAbs(0.0, jacobian_tolerance));
|
||||
CHECK_THAT(combined_relative_error, Catch::Matchers::WithinAbs(0.0, jacobian_tolerance));
|
||||
CHECK_THAT(inactive_velocity_action.Norml2(), Catch::Matchers::WithinAbs(0.0, zero_tolerance));
|
||||
CHECK_THAT(inactive_displacement_action.Norml2(), Catch::Matchers::WithinAbs(0.0, zero_tolerance));
|
||||
|
||||
mfem::Vector field_coupled_density_action(density_jacobian_action);
|
||||
|
||||
@@ -326,68 +251,46 @@ TEST_CASE(
|
||||
dv_ddisplacement = 1.0;
|
||||
element_state[density_block] = &density_dofs;
|
||||
element_state[gravity_gradient_block] = &gravity_gradient_dofs;
|
||||
integrator.AssembleElementGrad(
|
||||
elements, *transformation, element_state, element_matrices
|
||||
);
|
||||
integrator.AssembleElementGrad(elements, *transformation, element_state, element_matrices);
|
||||
|
||||
mfem::Vector minimal_density_action(velocity_size);
|
||||
mfem::Vector minimal_gravity_action(velocity_size);
|
||||
dv_drho.Mult(density_direction, minimal_density_action);
|
||||
dv_dgrad_phi.Mult(gravity_gradient_direction, minimal_gravity_action);
|
||||
|
||||
const double minimal_density_difference =
|
||||
relative_error(minimal_density_action, field_coupled_density_action);
|
||||
const double minimal_density_difference = relative_error(minimal_density_action, field_coupled_density_action);
|
||||
|
||||
INFO(
|
||||
"Minimal-mode density-block difference = " << minimal_density_difference
|
||||
);
|
||||
INFO("Minimal-mode density-block difference = " << minimal_density_difference);
|
||||
|
||||
CHECK_THAT(
|
||||
minimal_density_difference,
|
||||
Catch::Matchers::WithinAbs(0.0, zero_tolerance)
|
||||
);
|
||||
CHECK_THAT(
|
||||
minimal_gravity_action.Norml2(),
|
||||
Catch::Matchers::WithinAbs(0.0, zero_tolerance)
|
||||
);
|
||||
CHECK_THAT(minimal_density_difference, Catch::Matchers::WithinAbs(0.0, zero_tolerance));
|
||||
CHECK_THAT(minimal_gravity_action.Norml2(), Catch::Matchers::WithinAbs(0.0, zero_tolerance));
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Gravity Force Integrator Matches Manufactured Cartesian Load",
|
||||
tags::unit &tags::solver &tags::integrator &tags::gravity
|
||||
) {
|
||||
constexpr int dim = 3;
|
||||
constexpr double tolerance = 1.0e-12;
|
||||
constexpr int dim = 3;
|
||||
constexpr double tolerance = 1.0e-12;
|
||||
|
||||
constexpr int velocity_block =
|
||||
solver::block_index(solver::FieldBlock::velocity);
|
||||
constexpr int density_block =
|
||||
solver::block_index(solver::FieldBlock::density);
|
||||
constexpr int gravity_gradient_block =
|
||||
solver::block_index(solver::FieldBlock::gravity_gradient);
|
||||
constexpr int gravity_potential_block =
|
||||
solver::block_index(solver::FieldBlock::gravity_potential);
|
||||
constexpr int displacement_block =
|
||||
solver::block_index(solver::FieldBlock::displacement);
|
||||
constexpr int block_count = solver::field_block_count;
|
||||
constexpr int velocity_block = solver::block_index(solver::FieldBlock::velocity);
|
||||
constexpr int density_block = solver::block_index(solver::FieldBlock::density);
|
||||
constexpr int gravity_gradient_block = solver::block_index(solver::FieldBlock::gravity_gradient);
|
||||
constexpr int gravity_potential_block = solver::block_index(solver::FieldBlock::gravity_potential);
|
||||
constexpr int displacement_block = solver::block_index(solver::FieldBlock::displacement);
|
||||
constexpr int block_count = solver::field_block_count;
|
||||
|
||||
mfem::Mesh mesh = mfem::Mesh::MakeCartesian3D(
|
||||
1, 1, 1, mfem::Element::HEXAHEDRON, 1.0, 1.0, 1.0
|
||||
);
|
||||
mfem::Mesh mesh = mfem::Mesh::MakeCartesian3D(1, 1, 1, mfem::Element::HEXAHEDRON, 1.0, 1.0, 1.0);
|
||||
|
||||
mfem::H1_FECollection velocity_fec(1, dim);
|
||||
mfem::L2_FECollection density_fec(1, dim);
|
||||
mfem::RT_FECollection gravity_gradient_fec(0, dim);
|
||||
mfem::H1_FECollection displacement_fec(1, dim);
|
||||
|
||||
mfem::FiniteElementSpace velocity_fes(
|
||||
&mesh, &velocity_fec, dim, mfem::Ordering::byVDIM
|
||||
);
|
||||
mfem::FiniteElementSpace velocity_fes(&mesh, &velocity_fec, dim, mfem::Ordering::byVDIM);
|
||||
mfem::FiniteElementSpace density_fes(&mesh, &density_fec);
|
||||
mfem::FiniteElementSpace gravity_gradient_fes(&mesh, &gravity_gradient_fec);
|
||||
mfem::FiniteElementSpace displacement_fes(
|
||||
&mesh, &displacement_fec, dim, mfem::Ordering::byVDIM
|
||||
);
|
||||
mfem::FiniteElementSpace displacement_fes(&mesh, &displacement_fec, dim, mfem::Ordering::byVDIM);
|
||||
|
||||
mfem::GridFunction displacement(&displacement_fes);
|
||||
displacement = 0.0;
|
||||
@@ -396,10 +299,9 @@ TEST_CASE(
|
||||
|
||||
REQUIRE(domain_mapper.CalcIsIdentity());
|
||||
|
||||
auto reference_density = [](const mfem::Vector &x) { return 1.0 + x(0); };
|
||||
auto reference_density = [](const mfem::Vector &x) { return 1.0 + x(0); };
|
||||
|
||||
auto reference_gravity_gradient = [](const mfem::Vector &x,
|
||||
mfem::Vector &gradient) {
|
||||
auto reference_gravity_gradient = [](const mfem::Vector &x, mfem::Vector &gradient) {
|
||||
gradient.SetSize(3);
|
||||
gradient(0) = 2.0 * x(0);
|
||||
gradient(1) = 3.0 * x(1);
|
||||
@@ -407,29 +309,25 @@ TEST_CASE(
|
||||
};
|
||||
|
||||
mfem::FunctionCoefficient density_coefficient(reference_density);
|
||||
mfem::VectorFunctionCoefficient gravity_gradient_coefficient(
|
||||
dim, reference_gravity_gradient
|
||||
);
|
||||
mfem::VectorFunctionCoefficient gravity_gradient_coefficient(dim, reference_gravity_gradient);
|
||||
|
||||
mfem::GridFunction density(&density_fes);
|
||||
mfem::GridFunction gravity_gradient(&gravity_gradient_fes);
|
||||
density.ProjectCoefficient(density_coefficient);
|
||||
gravity_gradient.ProjectCoefficient(gravity_gradient_coefficient);
|
||||
|
||||
const mfem::FiniteElement *velocity_element = velocity_fes.GetFE(0);
|
||||
const mfem::FiniteElement *density_element = density_fes.GetFE(0);
|
||||
const mfem::FiniteElement *gravity_gradient_element =
|
||||
gravity_gradient_fes.GetFE(0);
|
||||
const mfem::FiniteElement *displacement_element = displacement_fes.GetFE(0);
|
||||
mfem::ElementTransformation *transformation =
|
||||
mesh.GetElementTransformation(0);
|
||||
const mfem::FiniteElement *velocity_element = velocity_fes.GetFE(0);
|
||||
const mfem::FiniteElement *density_element = density_fes.GetFE(0);
|
||||
const mfem::FiniteElement *gravity_gradient_element = gravity_gradient_fes.GetFE(0);
|
||||
const mfem::FiniteElement *displacement_element = displacement_fes.GetFE(0);
|
||||
mfem::ElementTransformation *transformation = mesh.GetElementTransformation(0);
|
||||
|
||||
const int velocity_dofs_count = velocity_element->GetDof();
|
||||
const int density_dofs_count = density_element->GetDof();
|
||||
const int gravity_gradient_dofs_count = gravity_gradient_element->GetDof();
|
||||
const int displacement_dofs_count = displacement_element->GetDof();
|
||||
const int velocity_size = dim * velocity_dofs_count;
|
||||
const int displacement_size = dim * displacement_dofs_count;
|
||||
const int velocity_dofs_count = velocity_element->GetDof();
|
||||
const int density_dofs_count = density_element->GetDof();
|
||||
const int gravity_gradient_dofs_count = gravity_gradient_element->GetDof();
|
||||
const int displacement_dofs_count = displacement_element->GetDof();
|
||||
const int velocity_size = dim * velocity_dofs_count;
|
||||
const int displacement_size = dim * displacement_dofs_count;
|
||||
|
||||
mfem::Array<int> density_dof_indices;
|
||||
mfem::Array<int> gravity_gradient_dof_indices;
|
||||
@@ -438,9 +336,7 @@ TEST_CASE(
|
||||
density_fes.GetElementDofs(0, density_dof_indices);
|
||||
gravity_gradient_fes.GetElementVDofs(0, gravity_gradient_dof_indices);
|
||||
density.GetSubVector(density_dof_indices, density_dofs);
|
||||
gravity_gradient.GetSubVector(
|
||||
gravity_gradient_dof_indices, gravity_gradient_dofs
|
||||
);
|
||||
gravity_gradient.GetSubVector(gravity_gradient_dof_indices, gravity_gradient_dofs);
|
||||
|
||||
REQUIRE(density_dofs.Size() == density_dofs_count);
|
||||
REQUIRE(gravity_gradient_dofs.Size() == gravity_gradient_dofs_count);
|
||||
@@ -483,23 +379,18 @@ TEST_CASE(
|
||||
domain_mapper, integrators::GravityForceJacobianMode::field_coupled
|
||||
);
|
||||
|
||||
const mfem::IntegrationRule &integration_rule =
|
||||
mfem::IntRules.Get(velocity_element->GetGeomType(), 8);
|
||||
const mfem::IntegrationRule &integration_rule = mfem::IntRules.Get(velocity_element->GetGeomType(), 8);
|
||||
integrator.SetIntegrationRule(integration_rule);
|
||||
integrator.AssembleElementVector(
|
||||
elements, *transformation, element_state, element_residual
|
||||
);
|
||||
integrator.AssembleElementVector(elements, *transformation, element_state, element_residual);
|
||||
|
||||
mfem::Vector reference_velocity_residual(velocity_residual);
|
||||
|
||||
auto residual_action = [&](const int component,
|
||||
const int coordinate_weight) {
|
||||
auto residual_action = [&](const int component, const int coordinate_weight) {
|
||||
mfem::Vector test_dofs(velocity_size);
|
||||
mfem::Vector x_physical(dim);
|
||||
test_dofs = 0.0;
|
||||
test_dofs = 0.0;
|
||||
|
||||
const mfem::IntegrationRule &velocity_nodes =
|
||||
velocity_element->GetNodes();
|
||||
const mfem::IntegrationRule &velocity_nodes = velocity_element->GetNodes();
|
||||
|
||||
for (int i = 0; i < velocity_dofs_count; ++i) {
|
||||
const mfem::IntegrationPoint &node = velocity_nodes.IntPoint(i);
|
||||
@@ -512,106 +403,62 @@ TEST_CASE(
|
||||
return test_dofs * velocity_residual;
|
||||
};
|
||||
|
||||
CHECK_THAT(
|
||||
residual_action(0, -1), Catch::Matchers::WithinAbs(5.0 / 3.0, tolerance)
|
||||
);
|
||||
CHECK_THAT(
|
||||
residual_action(1, -1), Catch::Matchers::WithinAbs(9.0 / 4.0, tolerance)
|
||||
);
|
||||
CHECK_THAT(
|
||||
residual_action(2, -1), Catch::Matchers::WithinAbs(3.0, tolerance)
|
||||
);
|
||||
CHECK_THAT(
|
||||
residual_action(0, 0), Catch::Matchers::WithinAbs(7.0 / 6.0, tolerance)
|
||||
);
|
||||
CHECK_THAT(
|
||||
residual_action(1, 1), Catch::Matchers::WithinAbs(3.0 / 2.0, tolerance)
|
||||
);
|
||||
CHECK_THAT(
|
||||
residual_action(2, 2), Catch::Matchers::WithinAbs(2.0, tolerance)
|
||||
);
|
||||
CHECK_THAT(
|
||||
residual_action(1, 0), Catch::Matchers::WithinAbs(5.0 / 4.0, tolerance)
|
||||
);
|
||||
CHECK_THAT(residual_action(0, -1), Catch::Matchers::WithinAbs(5.0 / 3.0, tolerance));
|
||||
CHECK_THAT(residual_action(1, -1), Catch::Matchers::WithinAbs(9.0 / 4.0, tolerance));
|
||||
CHECK_THAT(residual_action(2, -1), Catch::Matchers::WithinAbs(3.0, tolerance));
|
||||
CHECK_THAT(residual_action(0, 0), Catch::Matchers::WithinAbs(7.0 / 6.0, tolerance));
|
||||
CHECK_THAT(residual_action(1, 1), Catch::Matchers::WithinAbs(3.0 / 2.0, tolerance));
|
||||
CHECK_THAT(residual_action(2, 2), Catch::Matchers::WithinAbs(2.0, tolerance));
|
||||
CHECK_THAT(residual_action(1, 0), Catch::Matchers::WithinAbs(5.0 / 4.0, tolerance));
|
||||
|
||||
CHECK_THAT(
|
||||
density_residual.Norml2(), Catch::Matchers::WithinAbs(0.0, tolerance)
|
||||
);
|
||||
CHECK_THAT(
|
||||
gravity_gradient_residual.Norml2(),
|
||||
Catch::Matchers::WithinAbs(0.0, tolerance)
|
||||
);
|
||||
CHECK_THAT(
|
||||
gravity_potential_residual.Norml2(),
|
||||
Catch::Matchers::WithinAbs(0.0, tolerance)
|
||||
);
|
||||
CHECK_THAT(
|
||||
displacement_residual.Norml2(),
|
||||
Catch::Matchers::WithinAbs(0.0, tolerance)
|
||||
);
|
||||
CHECK_THAT(density_residual.Norml2(), Catch::Matchers::WithinAbs(0.0, tolerance));
|
||||
CHECK_THAT(gravity_gradient_residual.Norml2(), Catch::Matchers::WithinAbs(0.0, tolerance));
|
||||
CHECK_THAT(gravity_potential_residual.Norml2(), Catch::Matchers::WithinAbs(0.0, tolerance));
|
||||
CHECK_THAT(displacement_residual.Norml2(), Catch::Matchers::WithinAbs(0.0, tolerance));
|
||||
|
||||
integrator.SetJacobianMode(integrators::GravityForceJacobianMode::minimal);
|
||||
integrator.AssembleElementVector(
|
||||
elements, *transformation, element_state, element_residual
|
||||
);
|
||||
integrator.AssembleElementVector(elements, *transformation, element_state, element_residual);
|
||||
|
||||
mfem::Vector minimal_difference(velocity_residual);
|
||||
minimal_difference -= reference_velocity_residual;
|
||||
|
||||
integrator.SetJacobianMode(integrators::GravityForceJacobianMode::exact);
|
||||
integrator.AssembleElementVector(
|
||||
elements, *transformation, element_state, element_residual
|
||||
);
|
||||
integrator.AssembleElementVector(elements, *transformation, element_state, element_residual);
|
||||
|
||||
mfem::Vector exact_difference(velocity_residual);
|
||||
exact_difference -= reference_velocity_residual;
|
||||
|
||||
CHECK_THAT(
|
||||
minimal_difference.Norml2(), Catch::Matchers::WithinAbs(0.0, tolerance)
|
||||
);
|
||||
CHECK_THAT(
|
||||
exact_difference.Norml2(), Catch::Matchers::WithinAbs(0.0, tolerance)
|
||||
);
|
||||
CHECK_THAT(minimal_difference.Norml2(), Catch::Matchers::WithinAbs(0.0, tolerance));
|
||||
CHECK_THAT(exact_difference.Norml2(), Catch::Matchers::WithinAbs(0.0, tolerance));
|
||||
}
|
||||
TEST_CASE(
|
||||
"Gravity Force Integrator Preserves Gravity Identities",
|
||||
tags::unit &tags::solver &tags::integrator &tags::gravity
|
||||
) {
|
||||
constexpr int dim = 3;
|
||||
constexpr double density_value = 1.7;
|
||||
constexpr double gravity_scale = 2.4;
|
||||
constexpr double density_scale = 0.6;
|
||||
constexpr double tolerance = 1.0e-12;
|
||||
constexpr int dim = 3;
|
||||
constexpr double density_value = 1.7;
|
||||
constexpr double gravity_scale = 2.4;
|
||||
constexpr double density_scale = 0.6;
|
||||
constexpr double tolerance = 1.0e-12;
|
||||
|
||||
constexpr int velocity_block =
|
||||
solver::block_index(solver::FieldBlock::velocity);
|
||||
constexpr int density_block =
|
||||
solver::block_index(solver::FieldBlock::density);
|
||||
constexpr int gravity_gradient_block =
|
||||
solver::block_index(solver::FieldBlock::gravity_gradient);
|
||||
constexpr int gravity_potential_block =
|
||||
solver::block_index(solver::FieldBlock::gravity_potential);
|
||||
constexpr int displacement_block =
|
||||
solver::block_index(solver::FieldBlock::displacement);
|
||||
constexpr int block_count = solver::field_block_count;
|
||||
constexpr int velocity_block = solver::block_index(solver::FieldBlock::velocity);
|
||||
constexpr int density_block = solver::block_index(solver::FieldBlock::density);
|
||||
constexpr int gravity_gradient_block = solver::block_index(solver::FieldBlock::gravity_gradient);
|
||||
constexpr int gravity_potential_block = solver::block_index(solver::FieldBlock::gravity_potential);
|
||||
constexpr int displacement_block = solver::block_index(solver::FieldBlock::displacement);
|
||||
constexpr int block_count = solver::field_block_count;
|
||||
|
||||
mfem::Mesh mesh = mfem::Mesh::MakeCartesian3D(
|
||||
1, 1, 1, mfem::Element::HEXAHEDRON, 1.0, 1.0, 1.0
|
||||
);
|
||||
mfem::Mesh mesh = mfem::Mesh::MakeCartesian3D(1, 1, 1, mfem::Element::HEXAHEDRON, 1.0, 1.0, 1.0);
|
||||
|
||||
mfem::H1_FECollection velocity_fec(1, dim);
|
||||
mfem::L2_FECollection density_fec(0, dim);
|
||||
mfem::RT_FECollection gravity_gradient_fec(0, dim);
|
||||
mfem::H1_FECollection displacement_fec(1, dim);
|
||||
|
||||
mfem::FiniteElementSpace velocity_fes(
|
||||
&mesh, &velocity_fec, dim, mfem::Ordering::byVDIM
|
||||
);
|
||||
mfem::FiniteElementSpace velocity_fes(&mesh, &velocity_fec, dim, mfem::Ordering::byVDIM);
|
||||
mfem::FiniteElementSpace density_fes(&mesh, &density_fec);
|
||||
mfem::FiniteElementSpace gravity_gradient_fes(&mesh, &gravity_gradient_fec);
|
||||
mfem::FiniteElementSpace displacement_fes(
|
||||
&mesh, &displacement_fec, dim, mfem::Ordering::byVDIM
|
||||
);
|
||||
mfem::FiniteElementSpace displacement_fes(&mesh, &displacement_fec, dim, mfem::Ordering::byVDIM);
|
||||
|
||||
mfem::GridFunction displacement(&displacement_fes);
|
||||
displacement = 0.0;
|
||||
@@ -635,20 +482,18 @@ TEST_CASE(
|
||||
density.ProjectCoefficient(density_coefficient);
|
||||
gravity_gradient.ProjectCoefficient(gravity_coefficient);
|
||||
|
||||
const mfem::FiniteElement *velocity_element = velocity_fes.GetFE(0);
|
||||
const mfem::FiniteElement *density_element = density_fes.GetFE(0);
|
||||
const mfem::FiniteElement *gravity_gradient_element =
|
||||
gravity_gradient_fes.GetFE(0);
|
||||
const mfem::FiniteElement *displacement_element = displacement_fes.GetFE(0);
|
||||
mfem::ElementTransformation *transformation =
|
||||
mesh.GetElementTransformation(0);
|
||||
const mfem::FiniteElement *velocity_element = velocity_fes.GetFE(0);
|
||||
const mfem::FiniteElement *density_element = density_fes.GetFE(0);
|
||||
const mfem::FiniteElement *gravity_gradient_element = gravity_gradient_fes.GetFE(0);
|
||||
const mfem::FiniteElement *displacement_element = displacement_fes.GetFE(0);
|
||||
mfem::ElementTransformation *transformation = mesh.GetElementTransformation(0);
|
||||
|
||||
const int velocity_dofs_count = velocity_element->GetDof();
|
||||
const int density_dofs_count = density_element->GetDof();
|
||||
const int gravity_gradient_dofs_count = gravity_gradient_element->GetDof();
|
||||
const int displacement_dofs_count = displacement_element->GetDof();
|
||||
const int velocity_size = dim * velocity_dofs_count;
|
||||
const int displacement_size = dim * displacement_dofs_count;
|
||||
const int velocity_dofs_count = velocity_element->GetDof();
|
||||
const int density_dofs_count = density_element->GetDof();
|
||||
const int gravity_gradient_dofs_count = gravity_gradient_element->GetDof();
|
||||
const int displacement_dofs_count = displacement_element->GetDof();
|
||||
const int velocity_size = dim * velocity_dofs_count;
|
||||
const int displacement_size = dim * displacement_dofs_count;
|
||||
|
||||
mfem::Array<int> density_dof_indices;
|
||||
mfem::Array<int> gravity_gradient_dof_indices;
|
||||
@@ -657,9 +502,7 @@ TEST_CASE(
|
||||
density_fes.GetElementDofs(0, density_dof_indices);
|
||||
gravity_gradient_fes.GetElementVDofs(0, gravity_gradient_dof_indices);
|
||||
density.GetSubVector(density_dof_indices, density_dofs);
|
||||
gravity_gradient.GetSubVector(
|
||||
gravity_gradient_dof_indices, gravity_gradient_dofs
|
||||
);
|
||||
gravity_gradient.GetSubVector(gravity_gradient_dof_indices, gravity_gradient_dofs);
|
||||
|
||||
mfem::Vector zero_density(density_dofs_count);
|
||||
mfem::Vector zero_gravity(gravity_gradient_dofs_count);
|
||||
@@ -703,76 +546,54 @@ TEST_CASE(
|
||||
domain_mapper, integrators::GravityForceJacobianMode::field_coupled
|
||||
);
|
||||
|
||||
const mfem::IntegrationRule &integration_rule =
|
||||
mfem::IntRules.Get(velocity_element->GetGeomType(), 8);
|
||||
const mfem::IntegrationRule &integration_rule = mfem::IntRules.Get(velocity_element->GetGeomType(), 8);
|
||||
integrator.SetIntegrationRule(integration_rule);
|
||||
|
||||
auto assemble_velocity_residual = [&](const mfem::Vector &density_state,
|
||||
const mfem::Vector &gravity_state) {
|
||||
auto assemble_velocity_residual = [&](const mfem::Vector &density_state, const mfem::Vector &gravity_state) {
|
||||
element_state[density_block] = &density_state;
|
||||
element_state[gravity_gradient_block] = &gravity_state;
|
||||
integrator.AssembleElementVector(
|
||||
elements, *transformation, element_state, element_residual
|
||||
);
|
||||
integrator.AssembleElementVector(elements, *transformation, element_state, element_residual);
|
||||
return mfem::Vector(velocity_residual);
|
||||
};
|
||||
|
||||
auto scaled_difference_norm = [](mfem::Vector computed,
|
||||
const mfem::Vector &reference,
|
||||
const double scale) {
|
||||
auto scaled_difference_norm = [](mfem::Vector computed, const mfem::Vector &reference, const double scale) {
|
||||
computed.Add(-scale, reference);
|
||||
return computed.Norml2();
|
||||
};
|
||||
|
||||
const mfem::Vector base_residual =
|
||||
assemble_velocity_residual(density_dofs, gravity_gradient_dofs);
|
||||
const mfem::Vector base_residual = assemble_velocity_residual(density_dofs, gravity_gradient_dofs);
|
||||
|
||||
REQUIRE(base_residual.Norml2() > tolerance);
|
||||
|
||||
const mfem::Vector zero_field_residual =
|
||||
assemble_velocity_residual(density_dofs, zero_gravity);
|
||||
const mfem::Vector zero_field_residual = assemble_velocity_residual(density_dofs, zero_gravity);
|
||||
|
||||
mfem::Vector reversed_gravity(gravity_gradient_dofs);
|
||||
reversed_gravity *= -1.0;
|
||||
const mfem::Vector reversed_residual =
|
||||
assemble_velocity_residual(density_dofs, reversed_gravity);
|
||||
const mfem::Vector reversed_residual = assemble_velocity_residual(density_dofs, reversed_gravity);
|
||||
|
||||
mfem::Vector scaled_gravity(gravity_gradient_dofs);
|
||||
scaled_gravity *= gravity_scale;
|
||||
const mfem::Vector gravity_scaled_residual =
|
||||
assemble_velocity_residual(density_dofs, scaled_gravity);
|
||||
const mfem::Vector gravity_scaled_residual = assemble_velocity_residual(density_dofs, scaled_gravity);
|
||||
|
||||
mfem::Vector scaled_density(density_dofs);
|
||||
scaled_density *= density_scale;
|
||||
const mfem::Vector density_scaled_residual =
|
||||
assemble_velocity_residual(scaled_density, gravity_gradient_dofs);
|
||||
const mfem::Vector jointly_scaled_residual =
|
||||
assemble_velocity_residual(scaled_density, scaled_gravity);
|
||||
const mfem::Vector density_scaled_residual = assemble_velocity_residual(scaled_density, gravity_gradient_dofs);
|
||||
const mfem::Vector jointly_scaled_residual = assemble_velocity_residual(scaled_density, scaled_gravity);
|
||||
|
||||
CHECK_THAT(zero_field_residual.Norml2(), Catch::Matchers::WithinAbs(0.0, tolerance));
|
||||
CHECK_THAT(
|
||||
zero_field_residual.Norml2(), Catch::Matchers::WithinAbs(0.0, tolerance)
|
||||
scaled_difference_norm(reversed_residual, base_residual, -1.0), Catch::Matchers::WithinAbs(0.0, tolerance)
|
||||
);
|
||||
CHECK_THAT(
|
||||
scaled_difference_norm(reversed_residual, base_residual, -1.0),
|
||||
scaled_difference_norm(gravity_scaled_residual, base_residual, gravity_scale),
|
||||
Catch::Matchers::WithinAbs(0.0, tolerance)
|
||||
);
|
||||
CHECK_THAT(
|
||||
scaled_difference_norm(
|
||||
gravity_scaled_residual, base_residual, gravity_scale
|
||||
),
|
||||
scaled_difference_norm(density_scaled_residual, base_residual, density_scale),
|
||||
Catch::Matchers::WithinAbs(0.0, tolerance)
|
||||
);
|
||||
CHECK_THAT(
|
||||
scaled_difference_norm(
|
||||
density_scaled_residual, base_residual, density_scale
|
||||
),
|
||||
Catch::Matchers::WithinAbs(0.0, tolerance)
|
||||
);
|
||||
CHECK_THAT(
|
||||
scaled_difference_norm(
|
||||
jointly_scaled_residual, base_residual,
|
||||
density_scale * gravity_scale
|
||||
),
|
||||
scaled_difference_norm(jointly_scaled_residual, base_residual, density_scale * gravity_scale),
|
||||
Catch::Matchers::WithinAbs(0.0, tolerance)
|
||||
);
|
||||
|
||||
@@ -781,10 +602,9 @@ TEST_CASE(
|
||||
mfem::Vector x_physical(dim);
|
||||
mfem::Vector centered_position(dim);
|
||||
mfem::Vector test_value(dim);
|
||||
test_dofs = 0.0;
|
||||
test_dofs = 0.0;
|
||||
|
||||
const mfem::IntegrationRule &velocity_nodes =
|
||||
velocity_element->GetNodes();
|
||||
const mfem::IntegrationRule &velocity_nodes = velocity_element->GetNodes();
|
||||
|
||||
for (int i = 0; i < velocity_dofs_count; ++i) {
|
||||
const mfem::IntegrationPoint &node = velocity_nodes.IntPoint(i);
|
||||
@@ -798,87 +618,63 @@ TEST_CASE(
|
||||
test_function(centered_position, test_value);
|
||||
|
||||
for (int component = 0; component < dim; ++component) {
|
||||
test_dofs(i + component * velocity_dofs_count) =
|
||||
test_value(component);
|
||||
test_dofs(i + component * velocity_dofs_count) = test_value(component);
|
||||
}
|
||||
}
|
||||
|
||||
return test_dofs;
|
||||
};
|
||||
|
||||
const mfem::Vector force_x_test =
|
||||
make_test_dofs([](const mfem::Vector &, mfem::Vector &value) {
|
||||
value.SetSize(3);
|
||||
value = 0.0;
|
||||
value(0) = 1.0;
|
||||
});
|
||||
const mfem::Vector force_x_test = make_test_dofs([](const mfem::Vector &, mfem::Vector &value) {
|
||||
value.SetSize(3);
|
||||
value = 0.0;
|
||||
value(0) = 1.0;
|
||||
});
|
||||
|
||||
const mfem::Vector force_y_test =
|
||||
make_test_dofs([](const mfem::Vector &, mfem::Vector &value) {
|
||||
value.SetSize(3);
|
||||
value = 0.0;
|
||||
value(1) = 1.0;
|
||||
});
|
||||
const mfem::Vector force_y_test = make_test_dofs([](const mfem::Vector &, mfem::Vector &value) {
|
||||
value.SetSize(3);
|
||||
value = 0.0;
|
||||
value(1) = 1.0;
|
||||
});
|
||||
|
||||
const mfem::Vector force_z_test =
|
||||
make_test_dofs([](const mfem::Vector &, mfem::Vector &value) {
|
||||
value.SetSize(3);
|
||||
value = 0.0;
|
||||
value(2) = 1.0;
|
||||
});
|
||||
const mfem::Vector force_z_test = make_test_dofs([](const mfem::Vector &, mfem::Vector &value) {
|
||||
value.SetSize(3);
|
||||
value = 0.0;
|
||||
value(2) = 1.0;
|
||||
});
|
||||
|
||||
const mfem::Vector torque_x_test =
|
||||
make_test_dofs([](const mfem::Vector &position, mfem::Vector &value) {
|
||||
value.SetSize(3);
|
||||
value(0) = 0.0;
|
||||
value(1) = -position(2);
|
||||
value(2) = position(1);
|
||||
});
|
||||
const mfem::Vector torque_x_test = make_test_dofs([](const mfem::Vector &position, mfem::Vector &value) {
|
||||
value.SetSize(3);
|
||||
value(0) = 0.0;
|
||||
value(1) = -position(2);
|
||||
value(2) = position(1);
|
||||
});
|
||||
|
||||
const mfem::Vector torque_y_test =
|
||||
make_test_dofs([](const mfem::Vector &position, mfem::Vector &value) {
|
||||
value.SetSize(3);
|
||||
value(0) = position(2);
|
||||
value(1) = 0.0;
|
||||
value(2) = -position(0);
|
||||
});
|
||||
const mfem::Vector torque_y_test = make_test_dofs([](const mfem::Vector &position, mfem::Vector &value) {
|
||||
value.SetSize(3);
|
||||
value(0) = position(2);
|
||||
value(1) = 0.0;
|
||||
value(2) = -position(0);
|
||||
});
|
||||
|
||||
const mfem::Vector torque_z_test =
|
||||
make_test_dofs([](const mfem::Vector &position, mfem::Vector &value) {
|
||||
value.SetSize(3);
|
||||
value(0) = -position(1);
|
||||
value(1) = position(0);
|
||||
value(2) = 0.0;
|
||||
});
|
||||
const mfem::Vector torque_z_test = make_test_dofs([](const mfem::Vector &position, mfem::Vector &value) {
|
||||
value.SetSize(3);
|
||||
value(0) = -position(1);
|
||||
value(1) = position(0);
|
||||
value(2) = 0.0;
|
||||
});
|
||||
|
||||
CHECK_THAT(
|
||||
force_x_test * base_residual, Catch::Matchers::WithinAbs(0.0, tolerance)
|
||||
);
|
||||
CHECK_THAT(
|
||||
force_y_test * base_residual, Catch::Matchers::WithinAbs(0.0, tolerance)
|
||||
);
|
||||
CHECK_THAT(
|
||||
force_z_test * base_residual, Catch::Matchers::WithinAbs(0.0, tolerance)
|
||||
);
|
||||
CHECK_THAT(
|
||||
torque_x_test * base_residual,
|
||||
Catch::Matchers::WithinAbs(0.0, tolerance)
|
||||
);
|
||||
CHECK_THAT(
|
||||
torque_y_test * base_residual,
|
||||
Catch::Matchers::WithinAbs(0.0, tolerance)
|
||||
);
|
||||
CHECK_THAT(
|
||||
torque_z_test * base_residual,
|
||||
Catch::Matchers::WithinAbs(0.0, tolerance)
|
||||
);
|
||||
CHECK_THAT(force_x_test * base_residual, Catch::Matchers::WithinAbs(0.0, tolerance));
|
||||
CHECK_THAT(force_y_test * base_residual, Catch::Matchers::WithinAbs(0.0, tolerance));
|
||||
CHECK_THAT(force_z_test * base_residual, Catch::Matchers::WithinAbs(0.0, tolerance));
|
||||
CHECK_THAT(torque_x_test * base_residual, Catch::Matchers::WithinAbs(0.0, tolerance));
|
||||
CHECK_THAT(torque_y_test * base_residual, Catch::Matchers::WithinAbs(0.0, tolerance));
|
||||
CHECK_THAT(torque_z_test * base_residual, Catch::Matchers::WithinAbs(0.0, tolerance));
|
||||
|
||||
mfem::DenseMatrix dv_drho(velocity_size, density_dofs_count);
|
||||
mfem::DenseMatrix dv_dgrad_phi(velocity_size, gravity_gradient_dofs_count);
|
||||
|
||||
mfem::Array2D<mfem::DenseMatrix *> element_matrices(
|
||||
block_count, block_count
|
||||
);
|
||||
mfem::Array2D<mfem::DenseMatrix *> element_matrices(block_count, block_count);
|
||||
|
||||
for (int row = 0; row < block_count; ++row) {
|
||||
for (int column = 0; column < block_count; ++column) {
|
||||
@@ -891,19 +687,14 @@ TEST_CASE(
|
||||
|
||||
element_state[density_block] = &density_dofs;
|
||||
element_state[gravity_gradient_block] = &zero_gravity;
|
||||
integrator.AssembleElementGrad(
|
||||
elements, *transformation, element_state, element_matrices
|
||||
);
|
||||
integrator.AssembleElementGrad(elements, *transformation, element_state, element_matrices);
|
||||
|
||||
mfem::Vector zero_gravity_density_action(velocity_size);
|
||||
mfem::Vector zero_gravity_field_action(velocity_size);
|
||||
dv_drho.Mult(density_dofs, zero_gravity_density_action);
|
||||
dv_dgrad_phi.Mult(gravity_gradient_dofs, zero_gravity_field_action);
|
||||
|
||||
CHECK_THAT(
|
||||
zero_gravity_density_action.Norml2(),
|
||||
Catch::Matchers::WithinAbs(0.0, tolerance)
|
||||
);
|
||||
CHECK_THAT(zero_gravity_density_action.Norml2(), Catch::Matchers::WithinAbs(0.0, tolerance));
|
||||
CHECK_THAT(
|
||||
scaled_difference_norm(zero_gravity_field_action, base_residual, 1.0),
|
||||
Catch::Matchers::WithinAbs(0.0, tolerance)
|
||||
@@ -911,9 +702,7 @@ TEST_CASE(
|
||||
|
||||
element_state[density_block] = &zero_density;
|
||||
element_state[gravity_gradient_block] = &gravity_gradient_dofs;
|
||||
integrator.AssembleElementGrad(
|
||||
elements, *transformation, element_state, element_matrices
|
||||
);
|
||||
integrator.AssembleElementGrad(elements, *transformation, element_state, element_matrices);
|
||||
|
||||
mfem::Vector zero_density_density_action(velocity_size);
|
||||
mfem::Vector zero_density_field_action(velocity_size);
|
||||
@@ -924,8 +713,5 @@ TEST_CASE(
|
||||
scaled_difference_norm(zero_density_density_action, base_residual, 1.0),
|
||||
Catch::Matchers::WithinAbs(0.0, tolerance)
|
||||
);
|
||||
CHECK_THAT(
|
||||
zero_density_field_action.Norml2(),
|
||||
Catch::Matchers::WithinAbs(0.0, tolerance)
|
||||
);
|
||||
CHECK_THAT(zero_density_field_action.Norml2(), Catch::Matchers::WithinAbs(0.0, tolerance));
|
||||
}
|
||||
@@ -72,16 +72,9 @@ namespace {
|
||||
) {
|
||||
const double radial_extent = r_inf - r_star;
|
||||
const double computational_radius = r_star + coordinate * radial_extent;
|
||||
const double scale =
|
||||
r_star / (computational_radius * (1.0 - coordinate));
|
||||
const double scale_derivative =
|
||||
scale *
|
||||
(1.0 / (1.0 - coordinate) - radial_extent / computational_radius);
|
||||
return {
|
||||
.computational_radius = computational_radius,
|
||||
.scale = scale,
|
||||
.scale_derivative = scale_derivative
|
||||
};
|
||||
const double scale = r_star / (computational_radius * (1.0 - coordinate));
|
||||
const double scale_derivative = scale * (1.0 / (1.0 - coordinate) - radial_extent / computational_radius);
|
||||
return {.computational_radius = computational_radius, .scale = scale, .scale_derivative = scale_derivative};
|
||||
}
|
||||
|
||||
mapping::MappingStatus evaluate_affine_map(
|
||||
@@ -98,12 +91,11 @@ namespace {
|
||||
displaced_position += offset;
|
||||
|
||||
const mapping::compactification::ExteriorMapInput input{
|
||||
.reference_position = reference_position,
|
||||
.displaced_position = displaced_position,
|
||||
.displacement_jacobian = affine_jacobian,
|
||||
.compactification_coordinate = compactification_coordinate,
|
||||
.compactification_coordinate_gradient =
|
||||
compactification_coordinate_gradient
|
||||
.reference_position = reference_position,
|
||||
.displaced_position = displaced_position,
|
||||
.displacement_jacobian = affine_jacobian,
|
||||
.compactification_coordinate = compactification_coordinate,
|
||||
.compactification_coordinate_gradient = compactification_coordinate_gradient
|
||||
};
|
||||
|
||||
return exterior_map.Evaluate(input, result);
|
||||
@@ -114,47 +106,28 @@ TEST_CASE(
|
||||
"Kelvin Compactification Validates Its Configuration",
|
||||
tags::unit &tags::mapping &tags::kelvin
|
||||
) {
|
||||
CHECK_NOTHROW(
|
||||
mapping::compactification::KelvinCompactification(
|
||||
{.r_star_ref = 1.0, .r_inf_ref = 4.0}
|
||||
)
|
||||
CHECK_NOTHROW(mapping::compactification::KelvinCompactification({.r_star_ref = 1.0, .r_inf_ref = 4.0}));
|
||||
CHECK_THROWS_AS(
|
||||
mapping::compactification::KelvinCompactification({.r_star_ref = 0.0, .r_inf_ref = 4.0}), std::invalid_argument
|
||||
);
|
||||
CHECK_THROWS_AS(
|
||||
mapping::compactification::KelvinCompactification({.r_star_ref = -1.0, .r_inf_ref = 4.0}), std::invalid_argument
|
||||
);
|
||||
CHECK_THROWS_AS(
|
||||
mapping::compactification::KelvinCompactification({.r_star_ref = 2.0, .r_inf_ref = 2.0}), std::invalid_argument
|
||||
);
|
||||
CHECK_THROWS_AS(
|
||||
mapping::compactification::KelvinCompactification({.r_star_ref = 3.0, .r_inf_ref = 2.0}), std::invalid_argument
|
||||
);
|
||||
CHECK_THROWS_AS(
|
||||
mapping::compactification::KelvinCompactification(
|
||||
{.r_star_ref = 0.0, .r_inf_ref = 4.0}
|
||||
{.r_star_ref = 1.0, .r_inf_ref = std::numeric_limits<double>::infinity()}
|
||||
),
|
||||
std::invalid_argument
|
||||
);
|
||||
CHECK_THROWS_AS(
|
||||
mapping::compactification::KelvinCompactification(
|
||||
{.r_star_ref = -1.0, .r_inf_ref = 4.0}
|
||||
),
|
||||
std::invalid_argument
|
||||
);
|
||||
CHECK_THROWS_AS(
|
||||
mapping::compactification::KelvinCompactification(
|
||||
{.r_star_ref = 2.0, .r_inf_ref = 2.0}
|
||||
),
|
||||
std::invalid_argument
|
||||
);
|
||||
CHECK_THROWS_AS(
|
||||
mapping::compactification::KelvinCompactification(
|
||||
{.r_star_ref = 3.0, .r_inf_ref = 2.0}
|
||||
),
|
||||
std::invalid_argument
|
||||
);
|
||||
CHECK_THROWS_AS(
|
||||
mapping::compactification::KelvinCompactification(
|
||||
{.r_star_ref = 1.0,
|
||||
.r_inf_ref = std::numeric_limits<double>::infinity()}
|
||||
),
|
||||
std::invalid_argument
|
||||
);
|
||||
CHECK_THROWS_AS(
|
||||
mapping::compactification::KelvinCompactification(
|
||||
{.r_star_ref = 1.0,
|
||||
.r_inf_ref = 4.0,
|
||||
.coordinate_tolerance = -1.0e-12}
|
||||
{.r_star_ref = 1.0, .r_inf_ref = 4.0, .coordinate_tolerance = -1.0e-12}
|
||||
),
|
||||
std::invalid_argument
|
||||
);
|
||||
@@ -166,9 +139,7 @@ TEST_CASE(
|
||||
);
|
||||
CHECK_THROWS_AS(
|
||||
mapping::compactification::KelvinCompactification(
|
||||
{.r_star_ref = 1.0,
|
||||
.r_inf_ref = 4.0,
|
||||
.coordinate_tolerance = std::numeric_limits<double>::quiet_NaN()}
|
||||
{.r_star_ref = 1.0, .r_inf_ref = 4.0, .coordinate_tolerance = std::numeric_limits<double>::quiet_NaN()}
|
||||
),
|
||||
std::invalid_argument
|
||||
);
|
||||
@@ -180,22 +151,13 @@ TEST_CASE(
|
||||
) {
|
||||
constexpr double coordinate_tolerance = 3.0e-11;
|
||||
mapping::compactification::KelvinCompactification compactification(
|
||||
{.r_star_ref = 1.25,
|
||||
.r_inf_ref = 5.5,
|
||||
.coordinate_tolerance = coordinate_tolerance}
|
||||
{.r_star_ref = 1.25, .r_inf_ref = 5.5, .coordinate_tolerance = coordinate_tolerance}
|
||||
);
|
||||
|
||||
CHECK(compactification.GetName() == "KelvinCompactification");
|
||||
CHECK_THAT(
|
||||
compactification.GetReferenceStellarRadius(), WithinAbs(1.25, 0.0)
|
||||
);
|
||||
CHECK_THAT(
|
||||
compactification.GetReferenceInfinityRadius(), WithinAbs(5.5, 0.0)
|
||||
);
|
||||
CHECK_THAT(
|
||||
compactification.GetCoordinateTolerance(),
|
||||
WithinAbs(coordinate_tolerance, 0.0)
|
||||
);
|
||||
CHECK_THAT(compactification.GetReferenceStellarRadius(), WithinAbs(1.25, 0.0));
|
||||
CHECK_THAT(compactification.GetReferenceInfinityRadius(), WithinAbs(5.5, 0.0));
|
||||
CHECK_THAT(compactification.GetCoordinateTolerance(), WithinAbs(coordinate_tolerance, 0.0));
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
@@ -206,42 +168,28 @@ TEST_CASE(
|
||||
constexpr double tolerance = 0.0;
|
||||
constexpr double coordinate = 0.37;
|
||||
|
||||
mapping::compactification::KelvinCompactification compactification(
|
||||
{.r_star_ref = 1.0, .r_inf_ref = 4.0}
|
||||
);
|
||||
mapping::compactification::KelvinCompactification compactification({.r_star_ref = 1.0, .r_inf_ref = 4.0});
|
||||
const mfem::DenseMatrix displacement_jacobian = make_identity();
|
||||
const mfem::Vector displaced_position = make_vector(1.4, -0.2, 0.3);
|
||||
const mfem::Vector coordinate_gradient = make_vector(0.2, -0.1, 0.05);
|
||||
const mfem::Vector reference_a = make_vector(0.2, 0.1, -0.1);
|
||||
const mfem::Vector reference_b = make_vector(12.0, -7.0, 4.0);
|
||||
const mfem::Vector coordinate_gradient = make_vector(0.2, -0.1, 0.05);
|
||||
const mfem::Vector reference_a = make_vector(0.2, 0.1, -0.1);
|
||||
const mfem::Vector reference_b = make_vector(12.0, -7.0, 4.0);
|
||||
|
||||
const mapping::compactification::ExteriorMapInput input_a{
|
||||
reference_a, displaced_position, displacement_jacobian, coordinate,
|
||||
coordinate_gradient
|
||||
reference_a, displaced_position, displacement_jacobian, coordinate, coordinate_gradient
|
||||
};
|
||||
const mapping::compactification::ExteriorMapInput input_b{
|
||||
reference_b, displaced_position, displacement_jacobian, coordinate,
|
||||
coordinate_gradient
|
||||
reference_b, displaced_position, displacement_jacobian, coordinate, coordinate_gradient
|
||||
};
|
||||
|
||||
mapping::compactification::ExteriorMapResult result_a;
|
||||
mapping::compactification::ExteriorMapResult result_b;
|
||||
|
||||
REQUIRE(
|
||||
compactification.Evaluate(input_a, result_a) ==
|
||||
mapping::MappingStatus::valid
|
||||
);
|
||||
REQUIRE(
|
||||
compactification.Evaluate(input_b, result_b) ==
|
||||
mapping::MappingStatus::valid
|
||||
);
|
||||
REQUIRE(compactification.Evaluate(input_a, result_a) == mapping::MappingStatus::valid);
|
||||
REQUIRE(compactification.Evaluate(input_b, result_b) == mapping::MappingStatus::valid);
|
||||
|
||||
check_vector(
|
||||
result_a.physical_position, result_b.physical_position, tolerance
|
||||
);
|
||||
check_matrix(
|
||||
result_a.mapping_jacobian, result_b.mapping_jacobian, tolerance
|
||||
);
|
||||
check_vector(result_a.physical_position, result_b.physical_position, tolerance);
|
||||
check_matrix(result_a.mapping_jacobian, result_b.mapping_jacobian, tolerance);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
@@ -253,43 +201,28 @@ TEST_CASE(
|
||||
constexpr double radial_extent = r_inf - r_star;
|
||||
constexpr double tolerance = 2.0e-12;
|
||||
|
||||
mapping::compactification::KelvinCompactification compactification(
|
||||
{.r_star_ref = r_star, .r_inf_ref = r_inf}
|
||||
);
|
||||
const mfem::DenseMatrix identity = make_identity();
|
||||
const mfem::Vector coordinate_gradient =
|
||||
make_vector(1.0 / radial_extent, 0.0, 0.0);
|
||||
mapping::compactification::KelvinCompactification compactification({.r_star_ref = r_star, .r_inf_ref = r_inf});
|
||||
const mfem::DenseMatrix identity = make_identity();
|
||||
const mfem::Vector coordinate_gradient = make_vector(1.0 / radial_extent, 0.0, 0.0);
|
||||
|
||||
for (const double computational_radius :
|
||||
std::array{1.0, 1.25, 2.0, 3.0, 3.75}) {
|
||||
for (const double computational_radius : std::array{1.0, 1.25, 2.0, 3.0, 3.75}) {
|
||||
CAPTURE(computational_radius);
|
||||
|
||||
const double coordinate =
|
||||
(computational_radius - r_star) / radial_extent;
|
||||
const mfem::Vector reference_position =
|
||||
make_vector(computational_radius, 0.0, 0.0);
|
||||
const double coordinate = (computational_radius - r_star) / radial_extent;
|
||||
const mfem::Vector reference_position = make_vector(computational_radius, 0.0, 0.0);
|
||||
const mfem::Vector displaced_position(reference_position);
|
||||
const mapping::compactification::ExteriorMapInput input{
|
||||
reference_position, displaced_position, identity, coordinate,
|
||||
coordinate_gradient
|
||||
reference_position, displaced_position, identity, coordinate, coordinate_gradient
|
||||
};
|
||||
mapping::compactification::ExteriorMapResult result;
|
||||
|
||||
REQUIRE(
|
||||
compactification.Evaluate(input, result) ==
|
||||
mapping::MappingStatus::valid
|
||||
);
|
||||
REQUIRE(compactification.Evaluate(input, result) == mapping::MappingStatus::valid);
|
||||
|
||||
const double expected_radius =
|
||||
r_star * radial_extent / (r_inf - computational_radius);
|
||||
const double expected_radial_derivative =
|
||||
r_star * radial_extent /
|
||||
std::pow(r_inf - computational_radius, 2.0);
|
||||
const double expected_tangential_scale =
|
||||
expected_radius / computational_radius;
|
||||
const double expected_radius = r_star * radial_extent / (r_inf - computational_radius);
|
||||
const double expected_radial_derivative = r_star * radial_extent / std::pow(r_inf - computational_radius, 2.0);
|
||||
const double expected_tangential_scale = expected_radius / computational_radius;
|
||||
|
||||
const mfem::Vector expected_position =
|
||||
make_vector(expected_radius, 0.0, 0.0);
|
||||
const mfem::Vector expected_position = make_vector(expected_radius, 0.0, 0.0);
|
||||
mfem::DenseMatrix expected_jacobian(dimension);
|
||||
expected_jacobian = 0.0;
|
||||
expected_jacobian(0, 0) = expected_radial_derivative;
|
||||
@@ -311,9 +244,7 @@ TEST_CASE(
|
||||
constexpr double coordinate = 0.42;
|
||||
constexpr double tolerance = 1.0e-12;
|
||||
|
||||
mapping::compactification::KelvinCompactification compactification(
|
||||
{.r_star_ref = r_star, .r_inf_ref = r_inf}
|
||||
);
|
||||
mapping::compactification::KelvinCompactification compactification({.r_star_ref = r_star, .r_inf_ref = r_inf});
|
||||
const mfem::Vector reference_position = make_vector(0.8, 0.4, -0.2);
|
||||
const mfem::Vector displaced_position = make_vector(1.1, 0.5, -0.1);
|
||||
const mfem::Vector coordinate_gradient = make_vector(0.20, -0.10, 0.05);
|
||||
@@ -330,18 +261,13 @@ TEST_CASE(
|
||||
displacement_jacobian(2, 2) = 1.05;
|
||||
|
||||
const mapping::compactification::ExteriorMapInput input{
|
||||
reference_position, displaced_position, displacement_jacobian,
|
||||
coordinate, coordinate_gradient
|
||||
reference_position, displaced_position, displacement_jacobian, coordinate, coordinate_gradient
|
||||
};
|
||||
mapping::compactification::ExteriorMapResult result;
|
||||
|
||||
REQUIRE(
|
||||
compactification.Evaluate(input, result) ==
|
||||
mapping::MappingStatus::valid
|
||||
);
|
||||
REQUIRE(compactification.Evaluate(input, result) == mapping::MappingStatus::valid);
|
||||
|
||||
const AnalyticFactors factors =
|
||||
compute_analytic_factors(r_star, r_inf, coordinate);
|
||||
const AnalyticFactors factors = compute_analytic_factors(r_star, r_inf, coordinate);
|
||||
mfem::Vector expected_position(displaced_position);
|
||||
expected_position *= factors.scale;
|
||||
|
||||
@@ -350,9 +276,7 @@ TEST_CASE(
|
||||
|
||||
for (int i = 0; i < dimension; ++i) {
|
||||
for (int j = 0; j < dimension; ++j)
|
||||
expected_jacobian(i, j) += displaced_position(i) *
|
||||
factors.scale_derivative *
|
||||
coordinate_gradient(j);
|
||||
expected_jacobian(i, j) += displaced_position(i) * factors.scale_derivative * coordinate_gradient(j);
|
||||
}
|
||||
|
||||
check_vector(result.physical_position, expected_position, tolerance);
|
||||
@@ -367,9 +291,7 @@ TEST_CASE(
|
||||
constexpr double difference_step = 1.0e-6;
|
||||
constexpr double tolerance = 3.0e-9;
|
||||
|
||||
mapping::compactification::KelvinCompactification compactification(
|
||||
{.r_star_ref = 1.0, .r_inf_ref = 4.0}
|
||||
);
|
||||
mapping::compactification::KelvinCompactification compactification({.r_star_ref = 1.0, .r_inf_ref = 4.0});
|
||||
|
||||
mfem::DenseMatrix affine_jacobian(dimension);
|
||||
affine_jacobian(0, 0) = 1.05;
|
||||
@@ -389,8 +311,8 @@ TEST_CASE(
|
||||
mapping::compactification::ExteriorMapResult base_result;
|
||||
REQUIRE(
|
||||
evaluate_affine_map(
|
||||
compactification, reference_position, affine_jacobian, offset,
|
||||
base_coordinate, coordinate_gradient, base_result
|
||||
compactification, reference_position, affine_jacobian, offset, base_coordinate, coordinate_gradient,
|
||||
base_result
|
||||
) == mapping::MappingStatus::valid
|
||||
);
|
||||
|
||||
@@ -400,39 +322,30 @@ TEST_CASE(
|
||||
reference_plus(coordinate) += difference_step;
|
||||
reference_minus(coordinate) -= difference_step;
|
||||
|
||||
const double compactification_plus =
|
||||
base_coordinate + difference_step * coordinate_gradient(coordinate);
|
||||
const double compactification_minus =
|
||||
base_coordinate - difference_step * coordinate_gradient(coordinate);
|
||||
const double compactification_plus = base_coordinate + difference_step * coordinate_gradient(coordinate);
|
||||
const double compactification_minus = base_coordinate - difference_step * coordinate_gradient(coordinate);
|
||||
|
||||
mapping::compactification::ExteriorMapResult result_plus;
|
||||
mapping::compactification::ExteriorMapResult result_minus;
|
||||
|
||||
REQUIRE(
|
||||
evaluate_affine_map(
|
||||
compactification, reference_plus, affine_jacobian, offset,
|
||||
compactification_plus, coordinate_gradient, result_plus
|
||||
compactification, reference_plus, affine_jacobian, offset, compactification_plus, coordinate_gradient,
|
||||
result_plus
|
||||
) == mapping::MappingStatus::valid
|
||||
);
|
||||
REQUIRE(
|
||||
evaluate_affine_map(
|
||||
compactification, reference_minus, affine_jacobian, offset,
|
||||
compactification_minus, coordinate_gradient, result_minus
|
||||
compactification, reference_minus, affine_jacobian, offset, compactification_minus, coordinate_gradient,
|
||||
result_minus
|
||||
) == mapping::MappingStatus::valid
|
||||
);
|
||||
|
||||
for (int component = 0; component < dimension; ++component) {
|
||||
const double finite_difference =
|
||||
(result_plus.physical_position(component) -
|
||||
result_minus.physical_position(component)) /
|
||||
(result_plus.physical_position(component) - result_minus.physical_position(component)) /
|
||||
(2.0 * difference_step);
|
||||
CHECK_THAT(
|
||||
finite_difference,
|
||||
WithinAbs(
|
||||
base_result.mapping_jacobian(component, coordinate),
|
||||
tolerance
|
||||
)
|
||||
);
|
||||
CHECK_THAT(finite_difference, WithinAbs(base_result.mapping_jacobian(component, coordinate), tolerance));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -445,9 +358,7 @@ TEST_CASE(
|
||||
constexpr double difference_step = 1.0e-6;
|
||||
constexpr double tolerance = 2.0e-10;
|
||||
|
||||
mapping::compactification::KelvinCompactification compactification(
|
||||
{.r_star_ref = 1.0, .r_inf_ref = 4.0}
|
||||
);
|
||||
mapping::compactification::KelvinCompactification compactification({.r_star_ref = 1.0, .r_inf_ref = 4.0});
|
||||
const mfem::Vector reference_position = make_vector(1.3, -0.2, 0.4);
|
||||
const mfem::Vector displaced_position = make_vector(1.4, -0.1, 0.35);
|
||||
const mfem::Vector coordinate_gradient = make_vector(0.12, -0.04, 0.08);
|
||||
@@ -470,24 +381,16 @@ TEST_CASE(
|
||||
jacobian_direction(2, 2) = 0.01;
|
||||
|
||||
const mapping::compactification::ExteriorMapInput input{
|
||||
reference_position, displaced_position, displacement_jacobian,
|
||||
coordinate, coordinate_gradient
|
||||
};
|
||||
const mapping::compactification::ExteriorMapDirection direction{
|
||||
position_direction, jacobian_direction
|
||||
reference_position, displaced_position, displacement_jacobian, coordinate, coordinate_gradient
|
||||
};
|
||||
const mapping::compactification::ExteriorMapDirection direction{position_direction, jacobian_direction};
|
||||
|
||||
mapping::compactification::ExteriorMapResult base_result;
|
||||
mapping::compactification::ExteriorMapVariation variation;
|
||||
|
||||
REQUIRE(compactification.Evaluate(input, base_result) == mapping::MappingStatus::valid);
|
||||
REQUIRE(
|
||||
compactification.Evaluate(input, base_result) ==
|
||||
mapping::MappingStatus::valid
|
||||
);
|
||||
REQUIRE(
|
||||
compactification.EvaluateVariation(
|
||||
input, base_result, direction, variation
|
||||
) == mapping::MappingStatus::valid
|
||||
compactification.EvaluateVariation(input, base_result, direction, variation) == mapping::MappingStatus::valid
|
||||
);
|
||||
|
||||
mfem::Vector displaced_plus(displaced_position);
|
||||
@@ -501,45 +404,27 @@ TEST_CASE(
|
||||
jacobian_minus.Add(-difference_step, jacobian_direction);
|
||||
|
||||
const mapping::compactification::ExteriorMapInput input_plus{
|
||||
reference_position, displaced_plus, jacobian_plus, coordinate,
|
||||
coordinate_gradient
|
||||
reference_position, displaced_plus, jacobian_plus, coordinate, coordinate_gradient
|
||||
};
|
||||
const mapping::compactification::ExteriorMapInput input_minus{
|
||||
reference_position, displaced_minus, jacobian_minus, coordinate,
|
||||
coordinate_gradient
|
||||
reference_position, displaced_minus, jacobian_minus, coordinate, coordinate_gradient
|
||||
};
|
||||
|
||||
mapping::compactification::ExteriorMapResult result_plus;
|
||||
mapping::compactification::ExteriorMapResult result_minus;
|
||||
|
||||
REQUIRE(
|
||||
compactification.Evaluate(input_plus, result_plus) ==
|
||||
mapping::MappingStatus::valid
|
||||
);
|
||||
REQUIRE(
|
||||
compactification.Evaluate(input_minus, result_minus) ==
|
||||
mapping::MappingStatus::valid
|
||||
);
|
||||
REQUIRE(compactification.Evaluate(input_plus, result_plus) == mapping::MappingStatus::valid);
|
||||
REQUIRE(compactification.Evaluate(input_minus, result_minus) == mapping::MappingStatus::valid);
|
||||
|
||||
for (int i = 0; i < dimension; ++i) {
|
||||
const double position_finite_difference =
|
||||
(result_plus.physical_position(i) -
|
||||
result_minus.physical_position(i)) /
|
||||
(2.0 * difference_step);
|
||||
CHECK_THAT(
|
||||
position_finite_difference,
|
||||
WithinAbs(variation.physical_position_variation(i), tolerance)
|
||||
);
|
||||
(result_plus.physical_position(i) - result_minus.physical_position(i)) / (2.0 * difference_step);
|
||||
CHECK_THAT(position_finite_difference, WithinAbs(variation.physical_position_variation(i), tolerance));
|
||||
|
||||
for (int j = 0; j < dimension; ++j) {
|
||||
const double jacobian_finite_difference =
|
||||
(result_plus.mapping_jacobian(i, j) -
|
||||
result_minus.mapping_jacobian(i, j)) /
|
||||
(2.0 * difference_step);
|
||||
CHECK_THAT(
|
||||
jacobian_finite_difference,
|
||||
WithinAbs(variation.mapping_jacobian_variation(i, j), tolerance)
|
||||
);
|
||||
(result_plus.mapping_jacobian(i, j) - result_minus.mapping_jacobian(i, j)) / (2.0 * difference_step);
|
||||
CHECK_THAT(jacobian_finite_difference, WithinAbs(variation.mapping_jacobian_variation(i, j), tolerance));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -551,9 +436,7 @@ TEST_CASE(
|
||||
constexpr double coordinate = 0.31;
|
||||
constexpr double tolerance = 1.0e-12;
|
||||
|
||||
mapping::compactification::KelvinCompactification compactification(
|
||||
{.r_star_ref = 1.0, .r_inf_ref = 4.0}
|
||||
);
|
||||
mapping::compactification::KelvinCompactification compactification({.r_star_ref = 1.0, .r_inf_ref = 4.0});
|
||||
const mfem::Vector reference_position = make_vector(1.3, 0.4, -0.2);
|
||||
const mfem::Vector displaced_position = make_vector(1.4, 0.2, -0.1);
|
||||
const mfem::Vector coordinate_gradient = make_vector(0.16, -0.08, 0.03);
|
||||
@@ -564,14 +447,10 @@ TEST_CASE(
|
||||
displacement_jacobian(2, 1) = 0.03;
|
||||
|
||||
const mapping::compactification::ExteriorMapInput input{
|
||||
reference_position, displaced_position, displacement_jacobian,
|
||||
coordinate, coordinate_gradient
|
||||
reference_position, displaced_position, displacement_jacobian, coordinate, coordinate_gradient
|
||||
};
|
||||
mapping::compactification::ExteriorMapResult result;
|
||||
REQUIRE(
|
||||
compactification.Evaluate(input, result) ==
|
||||
mapping::MappingStatus::valid
|
||||
);
|
||||
REQUIRE(compactification.Evaluate(input, result) == mapping::MappingStatus::valid);
|
||||
|
||||
mfem::DenseMatrix rotation(dimension);
|
||||
rotation = 0.0;
|
||||
@@ -592,14 +471,10 @@ TEST_CASE(
|
||||
mfem::MultABt(temporary, rotation, rotated_displacement_jacobian);
|
||||
|
||||
const mapping::compactification::ExteriorMapInput rotated_input{
|
||||
rotated_reference, rotated_displaced, rotated_displacement_jacobian,
|
||||
coordinate, rotated_coordinate_gradient
|
||||
rotated_reference, rotated_displaced, rotated_displacement_jacobian, coordinate, rotated_coordinate_gradient
|
||||
};
|
||||
mapping::compactification::ExteriorMapResult rotated_result;
|
||||
REQUIRE(
|
||||
compactification.Evaluate(rotated_input, rotated_result) ==
|
||||
mapping::MappingStatus::valid
|
||||
);
|
||||
REQUIRE(compactification.Evaluate(rotated_input, rotated_result) == mapping::MappingStatus::valid);
|
||||
|
||||
mfem::Vector expected_position(dimension);
|
||||
rotation.Mult(result.physical_position, expected_position);
|
||||
@@ -608,9 +483,7 @@ TEST_CASE(
|
||||
mfem::Mult(rotation, result.mapping_jacobian, temporary);
|
||||
mfem::MultABt(temporary, rotation, expected_jacobian);
|
||||
|
||||
check_vector(
|
||||
rotated_result.physical_position, expected_position, tolerance
|
||||
);
|
||||
check_vector(rotated_result.physical_position, expected_position, tolerance);
|
||||
check_matrix(rotated_result.mapping_jacobian, expected_jacobian, tolerance);
|
||||
}
|
||||
|
||||
@@ -620,12 +493,8 @@ TEST_CASE(
|
||||
) {
|
||||
constexpr double tolerance = 1.0e-14;
|
||||
|
||||
mapping::compactification::KelvinCompactification compactification(
|
||||
{.r_star_ref = 1.0, .r_inf_ref = 4.0}
|
||||
);
|
||||
const mfem::Vector reference_position = make_vector(
|
||||
-0.5260553366425769, 0.5260553366425769, -0.6553163792879153
|
||||
);
|
||||
mapping::compactification::KelvinCompactification compactification({.r_star_ref = 1.0, .r_inf_ref = 4.0});
|
||||
const mfem::Vector reference_position = make_vector(-0.5260553366425769, 0.5260553366425769, -0.6553163792879153);
|
||||
const mfem::Vector displaced_position = make_vector(-0.55, 0.51, -0.63);
|
||||
const mfem::Vector coordinate_gradient = make_vector(-0.18, 0.18, -0.22);
|
||||
const mfem::DenseMatrix displacement_jacobian = make_identity();
|
||||
@@ -633,15 +502,11 @@ TEST_CASE(
|
||||
REQUIRE(reference_position.Norml2() < 1.0);
|
||||
|
||||
const mapping::compactification::ExteriorMapInput input{
|
||||
reference_position, displaced_position, displacement_jacobian, 0.0,
|
||||
coordinate_gradient
|
||||
reference_position, displaced_position, displacement_jacobian, 0.0, coordinate_gradient
|
||||
};
|
||||
mapping::compactification::ExteriorMapResult result;
|
||||
|
||||
REQUIRE(
|
||||
compactification.Evaluate(input, result) ==
|
||||
mapping::MappingStatus::valid
|
||||
);
|
||||
REQUIRE(compactification.Evaluate(input, result) == mapping::MappingStatus::valid);
|
||||
check_vector(result.physical_position, displaced_position, tolerance);
|
||||
CHECK(result.mapping_jacobian.Det() > 0.0);
|
||||
}
|
||||
@@ -658,35 +523,23 @@ TEST_CASE(
|
||||
constexpr double tolerance = 2.0e-11;
|
||||
|
||||
mapping::compactification::KelvinCompactification compactification(
|
||||
{.r_star_ref = r_star,
|
||||
.r_inf_ref = r_inf,
|
||||
.coordinate_tolerance = coordinate_tolerance}
|
||||
{.r_star_ref = r_star, .r_inf_ref = r_inf, .coordinate_tolerance = coordinate_tolerance}
|
||||
);
|
||||
const mfem::DenseMatrix identity = make_identity();
|
||||
const mfem::Vector coordinate_gradient =
|
||||
make_vector(1.0 / radial_extent, 0.0, 0.0);
|
||||
const mfem::DenseMatrix identity = make_identity();
|
||||
const mfem::Vector coordinate_gradient = make_vector(1.0 / radial_extent, 0.0, 0.0);
|
||||
|
||||
for (const double coordinate :
|
||||
std::array{0.0, 0.25, 0.75, 0.95, 0.99, 0.999}) {
|
||||
for (const double coordinate : std::array{0.0, 0.25, 0.75, 0.95, 0.99, 0.999}) {
|
||||
CAPTURE(coordinate);
|
||||
|
||||
const double computational_radius = r_star + coordinate * radial_extent;
|
||||
const mfem::Vector reference_position =
|
||||
make_vector(computational_radius, 0.0, 0.0);
|
||||
const double computational_radius = r_star + coordinate * radial_extent;
|
||||
const mfem::Vector reference_position = make_vector(computational_radius, 0.0, 0.0);
|
||||
const mapping::compactification::ExteriorMapInput input{
|
||||
reference_position, reference_position, identity, coordinate,
|
||||
coordinate_gradient
|
||||
reference_position, reference_position, identity, coordinate, coordinate_gradient
|
||||
};
|
||||
mapping::compactification::ExteriorMapResult result;
|
||||
|
||||
REQUIRE(
|
||||
compactification.Evaluate(input, result) ==
|
||||
mapping::MappingStatus::valid
|
||||
);
|
||||
CHECK_THAT(
|
||||
result.physical_position.Norml2() * (1.0 - coordinate),
|
||||
WithinAbs(r_star, tolerance)
|
||||
);
|
||||
REQUIRE(compactification.Evaluate(input, result) == mapping::MappingStatus::valid);
|
||||
CHECK_THAT(result.physical_position.Norml2() * (1.0 - coordinate), WithinAbs(r_star, tolerance));
|
||||
}
|
||||
|
||||
const mfem::Vector reference_position = make_vector(r_inf, 0.0, 0.0);
|
||||
@@ -694,46 +547,37 @@ TEST_CASE(
|
||||
|
||||
CHECK(
|
||||
compactification.Evaluate(
|
||||
{reference_position, reference_position, identity, 1.0,
|
||||
coordinate_gradient},
|
||||
{reference_position, reference_position, identity, 1.0, coordinate_gradient}, result
|
||||
) == mapping::MappingStatus::at_compactified_infinity
|
||||
);
|
||||
CHECK(
|
||||
compactification.Evaluate(
|
||||
{reference_position, reference_position, identity, 1.0 - 0.5 * coordinate_tolerance, coordinate_gradient},
|
||||
result
|
||||
) == mapping::MappingStatus::at_compactified_infinity
|
||||
);
|
||||
CHECK(
|
||||
compactification.Evaluate(
|
||||
{reference_position, reference_position, identity,
|
||||
1.0 - 0.5 * coordinate_tolerance, coordinate_gradient},
|
||||
{reference_position, reference_position, identity, 1.0 + 0.5 * coordinate_tolerance, coordinate_gradient},
|
||||
result
|
||||
) == mapping::MappingStatus::at_compactified_infinity
|
||||
);
|
||||
CHECK(
|
||||
compactification.Evaluate(
|
||||
{reference_position, reference_position, identity,
|
||||
1.0 + 0.5 * coordinate_tolerance, coordinate_gradient},
|
||||
result
|
||||
) == mapping::MappingStatus::at_compactified_infinity
|
||||
);
|
||||
CHECK(
|
||||
compactification.Evaluate(
|
||||
{reference_position, reference_position, identity,
|
||||
1.0 + 2.0 * coordinate_tolerance, coordinate_gradient},
|
||||
{reference_position, reference_position, identity, 1.0 + 2.0 * coordinate_tolerance, coordinate_gradient},
|
||||
result
|
||||
) == mapping::MappingStatus::outside_reference_domain
|
||||
);
|
||||
CHECK(
|
||||
compactification.Evaluate(
|
||||
{reference_position, reference_position, identity,
|
||||
-2.0 * coordinate_tolerance, coordinate_gradient},
|
||||
result
|
||||
{reference_position, reference_position, identity, -2.0 * coordinate_tolerance, coordinate_gradient}, result
|
||||
) == mapping::MappingStatus::outside_reference_domain
|
||||
);
|
||||
|
||||
const mfem::Vector surface_position = make_vector(0.97, 0.0, 0.0);
|
||||
CHECK(
|
||||
compactification.Evaluate(
|
||||
{surface_position, surface_position, identity,
|
||||
-0.5 * coordinate_tolerance, coordinate_gradient},
|
||||
result
|
||||
{surface_position, surface_position, identity, -0.5 * coordinate_tolerance, coordinate_gradient}, result
|
||||
) == mapping::MappingStatus::valid
|
||||
);
|
||||
check_vector(result.physical_position, surface_position, tolerance);
|
||||
@@ -743,9 +587,7 @@ TEST_CASE(
|
||||
"Kelvin Compactification Rejects Invalid Inputs And Inverted Maps",
|
||||
tags::unit &tags::mapping &tags::kelvin
|
||||
) {
|
||||
mapping::compactification::KelvinCompactification compactification(
|
||||
{.r_star_ref = 1.0, .r_inf_ref = 4.0}
|
||||
);
|
||||
mapping::compactification::KelvinCompactification compactification({.r_star_ref = 1.0, .r_inf_ref = 4.0});
|
||||
|
||||
const mfem::Vector reference_position = make_vector(2.0, 0.0, 0.0);
|
||||
const mfem::Vector displaced_position(reference_position);
|
||||
@@ -758,16 +600,12 @@ TEST_CASE(
|
||||
wrong_dimension = 1.0;
|
||||
CHECK(
|
||||
compactification.Evaluate(
|
||||
{wrong_dimension, displaced_position, identity, 1.0 / 3.0,
|
||||
coordinate_gradient},
|
||||
result
|
||||
{wrong_dimension, displaced_position, identity, 1.0 / 3.0, coordinate_gradient}, result
|
||||
) == mapping::MappingStatus::invalid_dimension
|
||||
);
|
||||
CHECK(
|
||||
compactification.Evaluate(
|
||||
{reference_position, displaced_position, identity, 1.0 / 3.0,
|
||||
wrong_dimension},
|
||||
result
|
||||
{reference_position, displaced_position, identity, 1.0 / 3.0, wrong_dimension}, result
|
||||
) == mapping::MappingStatus::invalid_dimension
|
||||
);
|
||||
|
||||
@@ -775,9 +613,7 @@ TEST_CASE(
|
||||
non_finite_position(1) = std::numeric_limits<double>::quiet_NaN();
|
||||
CHECK(
|
||||
compactification.Evaluate(
|
||||
{non_finite_position, displaced_position, identity, 1.0 / 3.0,
|
||||
coordinate_gradient},
|
||||
result
|
||||
{non_finite_position, displaced_position, identity, 1.0 / 3.0, coordinate_gradient}, result
|
||||
) == mapping::MappingStatus::non_finite_input
|
||||
);
|
||||
|
||||
@@ -785,15 +621,13 @@ TEST_CASE(
|
||||
non_finite_gradient(2) = std::numeric_limits<double>::infinity();
|
||||
CHECK(
|
||||
compactification.Evaluate(
|
||||
{reference_position, displaced_position, identity, 1.0 / 3.0,
|
||||
non_finite_gradient},
|
||||
result
|
||||
{reference_position, displaced_position, identity, 1.0 / 3.0, non_finite_gradient}, result
|
||||
) == mapping::MappingStatus::non_finite_input
|
||||
);
|
||||
CHECK(
|
||||
compactification.Evaluate(
|
||||
{reference_position, displaced_position, identity,
|
||||
std::numeric_limits<double>::quiet_NaN(), coordinate_gradient},
|
||||
{reference_position, displaced_position, identity, std::numeric_limits<double>::quiet_NaN(),
|
||||
coordinate_gradient},
|
||||
result
|
||||
) == mapping::MappingStatus::non_finite_input
|
||||
);
|
||||
@@ -802,9 +636,7 @@ TEST_CASE(
|
||||
singular_displacement_jacobian = 0.0;
|
||||
CHECK(
|
||||
compactification.Evaluate(
|
||||
{reference_position, displaced_position,
|
||||
singular_displacement_jacobian, 1.0 / 3.0, zero_gradient},
|
||||
result
|
||||
{reference_position, displaced_position, singular_displacement_jacobian, 1.0 / 3.0, zero_gradient}, result
|
||||
) == mapping::MappingStatus::non_positive_determinant
|
||||
);
|
||||
|
||||
@@ -812,9 +644,7 @@ TEST_CASE(
|
||||
inverted_displacement_jacobian(0, 0) = -1.0;
|
||||
CHECK(
|
||||
compactification.Evaluate(
|
||||
{reference_position, displaced_position,
|
||||
inverted_displacement_jacobian, 1.0 / 3.0, zero_gradient},
|
||||
result
|
||||
{reference_position, displaced_position, inverted_displacement_jacobian, 1.0 / 3.0, zero_gradient}, result
|
||||
) == mapping::MappingStatus::non_positive_determinant
|
||||
);
|
||||
}
|
||||
@@ -823,44 +653,34 @@ TEST_CASE(
|
||||
"Kelvin Compactification Variation Rejects Invalid Inputs",
|
||||
tags::unit &tags::mapping &tags::kelvin
|
||||
) {
|
||||
mapping::compactification::KelvinCompactification compactification(
|
||||
{.r_star_ref = 1.0, .r_inf_ref = 4.0}
|
||||
);
|
||||
mapping::compactification::KelvinCompactification compactification({.r_star_ref = 1.0, .r_inf_ref = 4.0});
|
||||
const mfem::Vector reference_position = make_vector(2.0, 0.0, 0.0);
|
||||
const mfem::Vector displaced_position(reference_position);
|
||||
const mfem::Vector coordinate_gradient = make_vector(1.0 / 3.0, 0.0, 0.0);
|
||||
const mfem::DenseMatrix identity = make_identity();
|
||||
const mapping::compactification::ExteriorMapInput input{
|
||||
reference_position, displaced_position, identity, 1.0 / 3.0,
|
||||
coordinate_gradient
|
||||
reference_position, displaced_position, identity, 1.0 / 3.0, coordinate_gradient
|
||||
};
|
||||
|
||||
mapping::compactification::ExteriorMapResult result;
|
||||
REQUIRE(
|
||||
compactification.Evaluate(input, result) ==
|
||||
mapping::MappingStatus::valid
|
||||
);
|
||||
REQUIRE(compactification.Evaluate(input, result) == mapping::MappingStatus::valid);
|
||||
|
||||
const mfem::Vector valid_position_direction =
|
||||
make_vector(0.01, -0.02, 0.03);
|
||||
const mfem::Vector valid_position_direction = make_vector(0.01, -0.02, 0.03);
|
||||
const mfem::DenseMatrix valid_jacobian_direction = make_identity();
|
||||
mapping::compactification::ExteriorMapVariation variation;
|
||||
|
||||
mfem::Vector wrong_dimension(2);
|
||||
wrong_dimension = 0.0;
|
||||
CHECK(
|
||||
compactification.EvaluateVariation(
|
||||
input, result, {wrong_dimension, valid_jacobian_direction},
|
||||
variation
|
||||
) == mapping::MappingStatus::invalid_dimension
|
||||
compactification.EvaluateVariation(input, result, {wrong_dimension, valid_jacobian_direction}, variation) ==
|
||||
mapping::MappingStatus::invalid_dimension
|
||||
);
|
||||
|
||||
mfem::DenseMatrix wrong_jacobian_dimension(2);
|
||||
wrong_jacobian_dimension = 0.0;
|
||||
CHECK(
|
||||
compactification.EvaluateVariation(
|
||||
input, result, {valid_position_direction, wrong_jacobian_dimension},
|
||||
variation
|
||||
input, result, {valid_position_direction, wrong_jacobian_dimension}, variation
|
||||
) == mapping::MappingStatus::invalid_dimension
|
||||
);
|
||||
|
||||
@@ -868,8 +688,7 @@ TEST_CASE(
|
||||
non_finite_direction(0) = std::numeric_limits<double>::quiet_NaN();
|
||||
CHECK(
|
||||
compactification.EvaluateVariation(
|
||||
input, result, {non_finite_direction, valid_jacobian_direction},
|
||||
variation
|
||||
input, result, {non_finite_direction, valid_jacobian_direction}, variation
|
||||
) == mapping::MappingStatus::non_finite_input
|
||||
);
|
||||
}
|
||||
@@ -880,43 +699,24 @@ TEST_CASE(
|
||||
) {
|
||||
constexpr double tolerance = 0.0;
|
||||
|
||||
mapping::compactification::KelvinCompactification compactification(
|
||||
{.r_star_ref = 1.0, .r_inf_ref = 4.0}
|
||||
);
|
||||
mapping::compactification::KelvinCompactification compactification({.r_star_ref = 1.0, .r_inf_ref = 4.0});
|
||||
const mfem::DenseMatrix identity = make_identity();
|
||||
const mfem::Vector gradient_a = make_vector(0.12, 0.03, -0.02);
|
||||
const mfem::Vector gradient_b = make_vector(-0.04, 0.15, 0.01);
|
||||
const mfem::Vector reference_a = make_vector(1.5, 0.2, 0.1);
|
||||
const mfem::Vector reference_b = make_vector(2.5, -0.3, 0.4);
|
||||
|
||||
const mapping::compactification::ExteriorMapInput input_a{
|
||||
reference_a, reference_a, identity, 0.25, gradient_a
|
||||
};
|
||||
const mapping::compactification::ExteriorMapInput input_b{
|
||||
reference_b, reference_b, identity, 0.70, gradient_b
|
||||
};
|
||||
const mapping::compactification::ExteriorMapInput input_a{reference_a, reference_a, identity, 0.25, gradient_a};
|
||||
const mapping::compactification::ExteriorMapInput input_b{reference_b, reference_b, identity, 0.70, gradient_b};
|
||||
|
||||
mapping::compactification::ExteriorMapResult first_a;
|
||||
mapping::compactification::ExteriorMapResult result_b;
|
||||
mapping::compactification::ExteriorMapResult second_a;
|
||||
|
||||
REQUIRE(
|
||||
compactification.Evaluate(input_a, first_a) ==
|
||||
mapping::MappingStatus::valid
|
||||
);
|
||||
REQUIRE(
|
||||
compactification.Evaluate(input_b, result_b) ==
|
||||
mapping::MappingStatus::valid
|
||||
);
|
||||
REQUIRE(
|
||||
compactification.Evaluate(input_a, second_a) ==
|
||||
mapping::MappingStatus::valid
|
||||
);
|
||||
REQUIRE(compactification.Evaluate(input_a, first_a) == mapping::MappingStatus::valid);
|
||||
REQUIRE(compactification.Evaluate(input_b, result_b) == mapping::MappingStatus::valid);
|
||||
REQUIRE(compactification.Evaluate(input_a, second_a) == mapping::MappingStatus::valid);
|
||||
|
||||
check_vector(
|
||||
first_a.physical_position, second_a.physical_position, tolerance
|
||||
);
|
||||
check_matrix(
|
||||
first_a.mapping_jacobian, second_a.mapping_jacobian, tolerance
|
||||
);
|
||||
check_vector(first_a.physical_position, second_a.physical_position, tolerance);
|
||||
check_matrix(first_a.mapping_jacobian, second_a.mapping_jacobian, tolerance);
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -38,8 +38,7 @@ namespace {
|
||||
return identity;
|
||||
}
|
||||
|
||||
mapping::MappingPointContext
|
||||
make_context(const mfem::DenseMatrix &jacobian) {
|
||||
mapping::MappingPointContext make_context(const mfem::DenseMatrix &jacobian) {
|
||||
mapping::MappingPointContext context;
|
||||
context.mapping_jacobian = jacobian;
|
||||
context.mapping_determinant = jacobian.Det();
|
||||
@@ -70,9 +69,8 @@ namespace {
|
||||
const mfem::DenseMatrix &jacobian_variation
|
||||
) {
|
||||
mapping::MappingPointVariation variation;
|
||||
variation.mapping_jacobian_variation = jacobian_variation;
|
||||
variation.mapping_determinant_variation =
|
||||
determinant_variation(jacobian, jacobian_variation);
|
||||
variation.mapping_jacobian_variation = jacobian_variation;
|
||||
variation.mapping_determinant_variation = determinant_variation(jacobian, jacobian_variation);
|
||||
variation.physical_position_variation.SetSize(dimension);
|
||||
variation.physical_position_variation = 0.0;
|
||||
return variation;
|
||||
@@ -99,11 +97,7 @@ namespace {
|
||||
mfem::DenseMatrix difference(computed);
|
||||
difference -= reference;
|
||||
|
||||
return matrix_norm(difference) /
|
||||
std::max(
|
||||
matrix_norm(reference),
|
||||
std::numeric_limits<double>::epsilon()
|
||||
);
|
||||
return matrix_norm(difference) / std::max(matrix_norm(reference), std::numeric_limits<double>::epsilon());
|
||||
}
|
||||
|
||||
double matrix_asymmetry(const mfem::DenseMatrix &matrix) {
|
||||
@@ -111,8 +105,7 @@ namespace {
|
||||
|
||||
for (int row = 0; row < matrix.Height(); ++row) {
|
||||
for (int column = 0; column < matrix.Width(); ++column) {
|
||||
const double difference =
|
||||
matrix(row, column) - matrix(column, row);
|
||||
const double difference = matrix(row, column) - matrix(column, row);
|
||||
asymmetry_squared += difference * difference;
|
||||
}
|
||||
}
|
||||
@@ -130,20 +123,16 @@ namespace {
|
||||
|
||||
for (int row = 0; row < dimension; ++row) {
|
||||
for (int column = 0; column < dimension; ++column) {
|
||||
plus_jacobian(row, column) +=
|
||||
step * jacobian_variation(row, column);
|
||||
minus_jacobian(row, column) -=
|
||||
step * jacobian_variation(row, column);
|
||||
plus_jacobian(row, column) += step * jacobian_variation(row, column);
|
||||
minus_jacobian(row, column) -= step * jacobian_variation(row, column);
|
||||
}
|
||||
}
|
||||
|
||||
REQUIRE(plus_jacobian.Det() > 0.0);
|
||||
REQUIRE(minus_jacobian.Det() > 0.0);
|
||||
|
||||
const mapping::MappingPointContext plus_context =
|
||||
make_context(plus_jacobian);
|
||||
const mapping::MappingPointContext minus_context =
|
||||
make_context(minus_jacobian);
|
||||
const mapping::MappingPointContext plus_context = make_context(plus_jacobian);
|
||||
const mapping::MappingPointContext minus_context = make_context(minus_jacobian);
|
||||
|
||||
mfem::DenseMatrix plus_tensor;
|
||||
mfem::DenseMatrix minus_tensor;
|
||||
@@ -184,22 +173,17 @@ TEST_CASE(
|
||||
);
|
||||
|
||||
cases.push_back(
|
||||
{"anisotropic stretch",
|
||||
make_matrix({1.20, 0.00, 0.00, 0.00, 0.85, 0.00, 0.00, 0.00, 1.10}),
|
||||
{"anisotropic stretch", make_matrix({1.20, 0.00, 0.00, 0.00, 0.85, 0.00, 0.00, 0.00, 1.10}),
|
||||
make_matrix({0.08, 0.01, -0.03, 0.02, -0.05, 0.04, 0.01, -0.02, 0.07})}
|
||||
);
|
||||
|
||||
cases.push_back(
|
||||
{"sheared mapping",
|
||||
make_matrix({1.10, 0.20, -0.05, 0.04, 0.90, 0.12, -0.03, 0.08, 1.15}),
|
||||
make_matrix(
|
||||
{0.06, -0.04, 0.02, 0.03, 0.05, -0.07, -0.01, 0.04, -0.02}
|
||||
)}
|
||||
{"sheared mapping", make_matrix({1.10, 0.20, -0.05, 0.04, 0.90, 0.12, -0.03, 0.08, 1.15}),
|
||||
make_matrix({0.06, -0.04, 0.02, 0.03, 0.05, -0.07, -0.01, 0.04, -0.02})}
|
||||
);
|
||||
|
||||
cases.push_back(
|
||||
{"strong general mapping",
|
||||
make_matrix({1.35, 0.31, -0.18, -0.12, 0.78, 0.22, 0.09, -0.16, 1.27}),
|
||||
{"strong general mapping", make_matrix({1.35, 0.31, -0.18, -0.12, 0.78, 0.22, 0.09, -0.16, 1.27}),
|
||||
make_matrix({-0.11, 0.08, 0.05, 0.07, 0.09, -0.04, -0.06, 0.03, 0.12})}
|
||||
);
|
||||
|
||||
@@ -207,37 +191,22 @@ TEST_CASE(
|
||||
DYNAMIC_SECTION(test_case.name) {
|
||||
REQUIRE(test_case.jacobian.Det() > 0.0);
|
||||
|
||||
const mapping::MappingPointContext context =
|
||||
make_context(test_case.jacobian);
|
||||
const mapping::MappingPointVariation variation = make_variation(
|
||||
test_case.jacobian, test_case.jacobian_variation
|
||||
);
|
||||
const mapping::MappingPointContext context = make_context(test_case.jacobian);
|
||||
const mapping::MappingPointVariation variation =
|
||||
make_variation(test_case.jacobian, test_case.jacobian_variation);
|
||||
|
||||
mfem::DenseMatrix analytic_variation;
|
||||
mapping::ComputeHDivMassTensorVariation(
|
||||
context, variation, analytic_variation
|
||||
);
|
||||
mapping::ComputeHDivMassTensorVariation(context, variation, analytic_variation);
|
||||
|
||||
const mfem::DenseMatrix finite_difference =
|
||||
centered_mass_tensor_difference(
|
||||
test_case.jacobian, test_case.jacobian_variation, 1.0e-6
|
||||
);
|
||||
const double relative_error =
|
||||
relative_matrix_error(analytic_variation, finite_difference);
|
||||
const double asymmetry = matrix_asymmetry(analytic_variation);
|
||||
centered_mass_tensor_difference(test_case.jacobian, test_case.jacobian_variation, 1.0e-6);
|
||||
const double relative_error = relative_matrix_error(analytic_variation, finite_difference);
|
||||
const double asymmetry = matrix_asymmetry(analytic_variation);
|
||||
|
||||
INFO("Mapping determinant = " << context.mapping_determinant);
|
||||
INFO(
|
||||
"Determinant variation = "
|
||||
<< variation.mapping_determinant_variation
|
||||
);
|
||||
INFO(
|
||||
"Analytic variation norm = " << matrix_norm(analytic_variation)
|
||||
);
|
||||
INFO(
|
||||
"Finite-difference variation norm = "
|
||||
<< matrix_norm(finite_difference)
|
||||
);
|
||||
INFO("Determinant variation = " << variation.mapping_determinant_variation);
|
||||
INFO("Analytic variation norm = " << matrix_norm(analytic_variation));
|
||||
INFO("Finite-difference variation norm = " << matrix_norm(finite_difference));
|
||||
INFO("Relative tensor-variation error = " << relative_error);
|
||||
INFO("Tensor-variation asymmetry = " << asymmetry);
|
||||
|
||||
@@ -252,31 +221,24 @@ TEST_CASE(
|
||||
"Convergence",
|
||||
tags::unit &tags::transformations &tags::convergence
|
||||
) {
|
||||
const mfem::DenseMatrix jacobian =
|
||||
make_matrix({1.18, 0.17, -0.09, -0.04, 0.92, 0.14, 0.07, -0.11, 1.23});
|
||||
const mfem::DenseMatrix jacobian = make_matrix({1.18, 0.17, -0.09, -0.04, 0.92, 0.14, 0.07, -0.11, 1.23});
|
||||
|
||||
const mfem::DenseMatrix jacobian_variation =
|
||||
make_matrix({0.09, -0.06, 0.04, 0.03, 0.07, -0.05, -0.02, 0.08, -0.03});
|
||||
|
||||
const mapping::MappingPointContext context = make_context(jacobian);
|
||||
const mapping::MappingPointVariation variation =
|
||||
make_variation(jacobian, jacobian_variation);
|
||||
const mapping::MappingPointContext context = make_context(jacobian);
|
||||
const mapping::MappingPointVariation variation = make_variation(jacobian, jacobian_variation);
|
||||
|
||||
mfem::DenseMatrix analytic_variation;
|
||||
mapping::ComputeHDivMassTensorVariation(
|
||||
context, variation, analytic_variation
|
||||
);
|
||||
mapping::ComputeHDivMassTensorVariation(context, variation, analytic_variation);
|
||||
|
||||
const std::array<double, 3> steps{4.0e-2, 2.0e-2, 1.0e-2};
|
||||
std::array<double, 3> errors{};
|
||||
|
||||
for (int i = 0; i < static_cast<int>(steps.size()); ++i) {
|
||||
const mfem::DenseMatrix finite_difference =
|
||||
centered_mass_tensor_difference(
|
||||
jacobian, jacobian_variation, steps[i]
|
||||
);
|
||||
errors[i] =
|
||||
relative_matrix_error(finite_difference, analytic_variation);
|
||||
centered_mass_tensor_difference(jacobian, jacobian_variation, steps[i]);
|
||||
errors[i] = relative_matrix_error(finite_difference, analytic_variation);
|
||||
INFO("Step = " << steps[i] << ", relative error = " << errors[i]);
|
||||
}
|
||||
|
||||
@@ -295,24 +257,20 @@ TEST_CASE(
|
||||
"Hdiv Mass Tensor Variation Vanishes For Translation",
|
||||
tags::unit &tags::transformations
|
||||
) {
|
||||
const mfem::DenseMatrix jacobian =
|
||||
make_matrix({1.12, 0.08, -0.03, 0.02, 0.94, 0.07, -0.01, 0.05, 1.09});
|
||||
const mfem::DenseMatrix jacobian = make_matrix({1.12, 0.08, -0.03, 0.02, 0.94, 0.07, -0.01, 0.05, 1.09});
|
||||
|
||||
mfem::DenseMatrix zero_jacobian_variation(dimension);
|
||||
zero_jacobian_variation = 0.0;
|
||||
zero_jacobian_variation = 0.0;
|
||||
|
||||
mapping::MappingPointContext context = make_context(jacobian);
|
||||
mapping::MappingPointVariation variation =
|
||||
make_variation(jacobian, zero_jacobian_variation);
|
||||
mapping::MappingPointContext context = make_context(jacobian);
|
||||
mapping::MappingPointVariation variation = make_variation(jacobian, zero_jacobian_variation);
|
||||
variation.physical_position_variation.SetSize(dimension);
|
||||
variation.physical_position_variation(0) = 0.7;
|
||||
variation.physical_position_variation(1) = -0.4;
|
||||
variation.physical_position_variation(2) = 0.9;
|
||||
|
||||
mfem::DenseMatrix tensor_variation;
|
||||
mapping::ComputeHDivMassTensorVariation(
|
||||
context, variation, tensor_variation
|
||||
);
|
||||
mapping::ComputeHDivMassTensorVariation(context, variation, tensor_variation);
|
||||
|
||||
CHECK_THAT(variation.mapping_determinant_variation, WithinAbs(0.0, 0.0));
|
||||
check_zero_matrix(tensor_variation, 1.0e-14);
|
||||
@@ -323,23 +281,17 @@ TEST_CASE(
|
||||
"Identity",
|
||||
tags::unit &tags::transformations
|
||||
) {
|
||||
const mfem::DenseMatrix identity = make_identity_matrix();
|
||||
const mfem::DenseMatrix identity = make_identity_matrix();
|
||||
|
||||
const mfem::DenseMatrix rotation_variation =
|
||||
make_matrix({0.0, -0.30, 0.20, 0.30, 0.0, -0.15, -0.20, 0.15, 0.0});
|
||||
const mfem::DenseMatrix rotation_variation = make_matrix({0.0, -0.30, 0.20, 0.30, 0.0, -0.15, -0.20, 0.15, 0.0});
|
||||
|
||||
const mapping::MappingPointContext context = make_context(identity);
|
||||
const mapping::MappingPointVariation variation =
|
||||
make_variation(identity, rotation_variation);
|
||||
const mapping::MappingPointVariation variation = make_variation(identity, rotation_variation);
|
||||
|
||||
mfem::DenseMatrix tensor_variation;
|
||||
mapping::ComputeHDivMassTensorVariation(
|
||||
context, variation, tensor_variation
|
||||
);
|
||||
mapping::ComputeHDivMassTensorVariation(context, variation, tensor_variation);
|
||||
|
||||
CHECK_THAT(
|
||||
variation.mapping_determinant_variation, WithinAbs(0.0, 1.0e-15)
|
||||
);
|
||||
CHECK_THAT(variation.mapping_determinant_variation, WithinAbs(0.0, 1.0e-15));
|
||||
check_zero_matrix(tensor_variation, 1.0e-14);
|
||||
}
|
||||
|
||||
@@ -355,26 +307,18 @@ TEST_CASE(
|
||||
for (int i = 0; i < dimension; ++i)
|
||||
jacobian_variation(i, i) = scaling_variation;
|
||||
|
||||
const mapping::MappingPointContext context = make_context(identity);
|
||||
const mapping::MappingPointVariation variation =
|
||||
make_variation(identity, jacobian_variation);
|
||||
const mapping::MappingPointContext context = make_context(identity);
|
||||
const mapping::MappingPointVariation variation = make_variation(identity, jacobian_variation);
|
||||
|
||||
mfem::DenseMatrix tensor_variation;
|
||||
mapping::ComputeHDivMassTensorVariation(
|
||||
context, variation, tensor_variation
|
||||
);
|
||||
mapping::ComputeHDivMassTensorVariation(context, variation, tensor_variation);
|
||||
|
||||
CHECK_THAT(
|
||||
variation.mapping_determinant_variation,
|
||||
WithinAbs(3.0 * scaling_variation, 1.0e-14)
|
||||
);
|
||||
CHECK_THAT(variation.mapping_determinant_variation, WithinAbs(3.0 * scaling_variation, 1.0e-14));
|
||||
|
||||
for (int row = 0; row < dimension; ++row) {
|
||||
for (int column = 0; column < dimension; ++column) {
|
||||
const double expected = row == column ? -scaling_variation : 0.0;
|
||||
CHECK_THAT(
|
||||
tensor_variation(row, column), WithinAbs(expected, 1.0e-14)
|
||||
);
|
||||
CHECK_THAT(tensor_variation(row, column), WithinAbs(expected, 1.0e-14));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -387,21 +331,16 @@ TEST_CASE(
|
||||
|
||||
const mfem::DenseMatrix identity = make_identity_matrix();
|
||||
mfem::DenseMatrix jacobian_variation(dimension);
|
||||
jacobian_variation = 0.0;
|
||||
jacobian_variation(0, 1) = shear_variation;
|
||||
jacobian_variation = 0.0;
|
||||
jacobian_variation(0, 1) = shear_variation;
|
||||
|
||||
const mapping::MappingPointContext context = make_context(identity);
|
||||
const mapping::MappingPointVariation variation =
|
||||
make_variation(identity, jacobian_variation);
|
||||
const mapping::MappingPointContext context = make_context(identity);
|
||||
const mapping::MappingPointVariation variation = make_variation(identity, jacobian_variation);
|
||||
|
||||
mfem::DenseMatrix tensor_variation;
|
||||
mapping::ComputeHDivMassTensorVariation(
|
||||
context, variation, tensor_variation
|
||||
);
|
||||
mapping::ComputeHDivMassTensorVariation(context, variation, tensor_variation);
|
||||
|
||||
CHECK_THAT(
|
||||
variation.mapping_determinant_variation, WithinAbs(0.0, 1.0e-15)
|
||||
);
|
||||
CHECK_THAT(variation.mapping_determinant_variation, WithinAbs(0.0, 1.0e-15));
|
||||
CHECK_THAT(tensor_variation(0, 1), WithinAbs(shear_variation, 1.0e-14));
|
||||
CHECK_THAT(tensor_variation(1, 0), WithinAbs(shear_variation, 1.0e-14));
|
||||
|
||||
@@ -418,16 +357,14 @@ TEST_CASE(
|
||||
"Mapping Determinant Variation Matches Jacobi Formula",
|
||||
tags::unit &tags::transformations
|
||||
) {
|
||||
const mfem::DenseMatrix jacobian =
|
||||
make_matrix({1.24, 0.19, -0.07, -0.06, 0.88, 0.16, 0.04, -0.12, 1.19});
|
||||
const mfem::DenseMatrix jacobian = make_matrix({1.24, 0.19, -0.07, -0.06, 0.88, 0.16, 0.04, -0.12, 1.19});
|
||||
|
||||
const mfem::DenseMatrix jacobian_variation =
|
||||
make_matrix({0.08, -0.03, 0.05, 0.02, 0.06, -0.04, -0.01, 0.07, -0.02});
|
||||
|
||||
const mapping::MappingPointContext context = make_context(jacobian);
|
||||
const mapping::MappingPointVariation variation =
|
||||
make_variation(jacobian, jacobian_variation);
|
||||
constexpr double difference_step = 1.0e-3;
|
||||
const mapping::MappingPointContext context = make_context(jacobian);
|
||||
const mapping::MappingPointVariation variation = make_variation(jacobian, jacobian_variation);
|
||||
constexpr double difference_step = 1.0e-3;
|
||||
|
||||
mfem::DenseMatrix plus_one(context.mapping_jacobian);
|
||||
mfem::DenseMatrix plus_two(context.mapping_jacobian);
|
||||
@@ -439,12 +376,10 @@ TEST_CASE(
|
||||
minus_one.Add(-difference_step, variation.mapping_jacobian_variation);
|
||||
minus_two.Add(-2.0 * difference_step, variation.mapping_jacobian_variation);
|
||||
|
||||
const double finite_difference = (minus_two.Det() - 8.0 * minus_one.Det() +
|
||||
8.0 * plus_one.Det() - plus_two.Det()) /
|
||||
(12.0 * difference_step);
|
||||
const double analytic = variation.mapping_determinant_variation;
|
||||
const double relative_error = std::abs(finite_difference - analytic) /
|
||||
std::max(std::abs(analytic), 1.0e-14);
|
||||
const double finite_difference =
|
||||
(minus_two.Det() - 8.0 * minus_one.Det() + 8.0 * plus_one.Det() - plus_two.Det()) / (12.0 * difference_step);
|
||||
const double analytic = variation.mapping_determinant_variation;
|
||||
const double relative_error = std::abs(finite_difference - analytic) / std::max(std::abs(analytic), 1.0e-14);
|
||||
|
||||
INFO("Analytic determinant variation = " << analytic);
|
||||
INFO("Finite-difference determinant variation = " << finite_difference);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user