feat(surface): major work on implementing surface constraints in a presciption agnostic manner
This commit is contained in:
@@ -14,20 +14,23 @@ namespace {
|
||||
) {
|
||||
switch (domain) {
|
||||
case mean_field::utils::DOMAINS::CORE:
|
||||
return mean_field::utils::domain::make_attribute_marker<
|
||||
mean_field::utils::domain::Core, DomainSchema>(mesh);
|
||||
return mean_field::utils::domain::make_attribute_marker<mean_field::utils::domain::Core, DomainSchema>(
|
||||
mesh
|
||||
);
|
||||
case mean_field::utils::DOMAINS::ENVELOPE:
|
||||
return mean_field::utils::domain::make_attribute_marker<
|
||||
mean_field::utils::domain::Envelope, DomainSchema>(mesh);
|
||||
return mean_field::utils::domain::make_attribute_marker<mean_field::utils::domain::Envelope, DomainSchema>(
|
||||
mesh
|
||||
);
|
||||
case mean_field::utils::DOMAINS::ALL:
|
||||
return mean_field::utils::domain::make_attribute_marker<
|
||||
mean_field::utils::domain::All, DomainSchema>(mesh);
|
||||
return mean_field::utils::domain::make_attribute_marker<mean_field::utils::domain::All, DomainSchema>(mesh);
|
||||
case mean_field::utils::DOMAINS::STELLAR:
|
||||
return mean_field::utils::domain::make_attribute_marker<
|
||||
mean_field::utils::domain::Stellar, DomainSchema>(mesh);
|
||||
return mean_field::utils::domain::make_attribute_marker<mean_field::utils::domain::Stellar, DomainSchema>(
|
||||
mesh
|
||||
);
|
||||
case mean_field::utils::DOMAINS::VACUUM:
|
||||
return mean_field::utils::domain::make_attribute_marker<
|
||||
mean_field::utils::domain::Vacuum, DomainSchema>(mesh);
|
||||
return mean_field::utils::domain::make_attribute_marker<mean_field::utils::domain::Vacuum, DomainSchema>(
|
||||
mesh
|
||||
);
|
||||
}
|
||||
MFEM_ABORT("Unsupported integration domain.");
|
||||
}
|
||||
@@ -62,15 +65,14 @@ namespace mean_field::analysis {
|
||||
mfem::LinearForm lf(fem.densityFes.get());
|
||||
mfem::GridFunctionCoefficient gf_c(&gf);
|
||||
double local_integral;
|
||||
mfem::Array<int> elem_markers = make_domain_marker(*fem.mesh, domain);
|
||||
mfem::Array<int> elem_markers = make_domain_marker(*fem.mesh, domain);
|
||||
const mfem::ElementTransformation &representative_transformation = *fem.mesh->GetElementTransformation(0);
|
||||
const mfem::IntegrationRule &integration_rule =
|
||||
get_density_rule<field::Density::Form::MassConservation>(fem, representative_transformation, {}, domain);
|
||||
|
||||
if (fem.has_mapping() && coord_space == mapping::COORDINATE_SPACE::PHYSICAL) {
|
||||
mapping::MappedScalarCoefficient mapped_gf_c(
|
||||
*fem.domainMapperStateless, *fem.displacement,
|
||||
*fem.compactificationCoordinate, gf_c
|
||||
*fem.domainMapperStateless, *fem.displacement, *fem.compactificationCoordinate, gf_c
|
||||
);
|
||||
|
||||
// ReSharper disable once CppDFAMemoryLeak // Disabled because MFEM
|
||||
@@ -107,16 +109,14 @@ namespace mean_field::analysis {
|
||||
) {
|
||||
const int dim = fem.mesh->Dimension();
|
||||
mapping::GridFunctionMappingEvaluator mapping_evaluator(
|
||||
*fem.domainMapperStateless, *fem.displacement,
|
||||
*fem.compactificationCoordinate
|
||||
*fem.domainMapperStateless, *fem.displacement, *fem.compactificationCoordinate
|
||||
);
|
||||
mfem::Vector local_com(dim);
|
||||
local_com = 0.0;
|
||||
double local_mass = 0.0;
|
||||
|
||||
for (int i = 0; i < fem.mesh->GetNE(); ++i) {
|
||||
if (!DomainSchema::template attribute_belongs_to<utils::domain::Stellar>(
|
||||
fem.mesh->GetAttribute(i)))
|
||||
if (!DomainSchema::template attribute_belongs_to<utils::domain::Stellar>(fem.mesh->GetAttribute(i)))
|
||||
continue;
|
||||
mfem::ElementTransformation *trans = fem.mesh->GetElementTransformation(i);
|
||||
const mfem::IntegrationRule &ir = get_density_rule<field::Density::Form::CenterOfMass>(
|
||||
@@ -129,16 +129,15 @@ namespace mean_field::analysis {
|
||||
|
||||
mapping::VolumeMappingContext mapping_context;
|
||||
MFEM_VERIFY(
|
||||
mapping_evaluator.EvaluateVolume(*trans, ip, mapping_context) ==
|
||||
mapping::MappingStatus::valid,
|
||||
mapping_evaluator.EvaluateVolume(*trans, ip, mapping_context) == mapping::MappingStatus::valid,
|
||||
"Center-of-mass integration encountered an invalid mapping."
|
||||
);
|
||||
const double weight = mapping_context.quadrature.weight;
|
||||
double rho_val = rho.GetValue(i, ip);
|
||||
const double weight = mapping_context.quadrature.weight;
|
||||
double rho_val = rho.GetValue(i, ip);
|
||||
|
||||
const mfem::Vector &phys_point = mapping_context.mapping.physical_position;
|
||||
|
||||
const double mass_term = rho_val * weight;
|
||||
const double mass_term = rho_val * weight;
|
||||
local_mass += mass_term;
|
||||
|
||||
for (int d = 0; d < dim; ++d) {
|
||||
@@ -183,8 +182,7 @@ namespace mean_field::analysis {
|
||||
std::unique_ptr<mfem::Coefficient> s2_coeff;
|
||||
if (fem.has_mapping()) {
|
||||
s2_coeff = std::make_unique<mapping::PhysicalPositionFunctionCoefficient>(
|
||||
*fem.domainMapperStateless, *fem.displacement,
|
||||
*fem.compactificationCoordinate, s2_func
|
||||
*fem.domainMapperStateless, *fem.displacement, *fem.compactificationCoordinate, s2_func
|
||||
);
|
||||
} else {
|
||||
s2_coeff = std::make_unique<mfem::FunctionCoefficient>(s2_func);
|
||||
@@ -204,8 +202,7 @@ namespace mean_field::analysis {
|
||||
double local_I = 0.0;
|
||||
if (fem.has_mapping()) {
|
||||
mapping::MappedScalarCoefficient mapped_integrand(
|
||||
*fem.domainMapperStateless, *fem.displacement,
|
||||
*fem.compactificationCoordinate, I_integrand
|
||||
*fem.domainMapperStateless, *fem.displacement, *fem.compactificationCoordinate, I_integrand
|
||||
);
|
||||
auto *integrator = new mfem::DomainLFIntegrator(mapped_integrand);
|
||||
integrator->SetIntRule(&integration_rule);
|
||||
@@ -239,18 +236,16 @@ namespace mean_field::analysis {
|
||||
|
||||
double local_volume = 0.0;
|
||||
mapping::GridFunctionMappingEvaluator mapping_evaluator(
|
||||
*fem.domainMapperStateless, *fem.displacement,
|
||||
*fem.compactificationCoordinate
|
||||
*fem.domainMapperStateless, *fem.displacement, *fem.compactificationCoordinate
|
||||
);
|
||||
|
||||
for (int e = 0; e < mesh.GetNE(); ++e) {
|
||||
const int attr = mesh.GetAttribute(e);
|
||||
const bool selected =
|
||||
domain == utils::DOMAINS::ALL ||
|
||||
(domain == utils::DOMAINS::STELLAR &&
|
||||
DomainSchema::template attribute_belongs_to<utils::domain::Stellar>(attr)) ||
|
||||
(domain == utils::DOMAINS::VACUUM &&
|
||||
DomainSchema::template attribute_belongs_to<utils::domain::Vacuum>(attr));
|
||||
const int attr = mesh.GetAttribute(e);
|
||||
const bool selected = domain == utils::DOMAINS::ALL ||
|
||||
(domain == utils::DOMAINS::STELLAR &&
|
||||
DomainSchema::template attribute_belongs_to<utils::domain::Stellar>(attr)) ||
|
||||
(domain == utils::DOMAINS::VACUUM &&
|
||||
DomainSchema::template attribute_belongs_to<utils::domain::Vacuum>(attr));
|
||||
if (!selected)
|
||||
continue;
|
||||
mfem::ElementTransformation *T = mesh.GetElementTransformation(e);
|
||||
@@ -266,8 +261,7 @@ namespace mean_field::analysis {
|
||||
if (physical) {
|
||||
mapping::VolumeMappingContext context;
|
||||
MFEM_VERIFY(
|
||||
mapping_evaluator.EvaluateVolume(*T, ip, context) ==
|
||||
mapping::MappingStatus::valid,
|
||||
mapping_evaluator.EvaluateVolume(*T, ip, context) == mapping::MappingStatus::valid,
|
||||
"Mesh-volume integration encountered an invalid mapping."
|
||||
);
|
||||
dV = context.quadrature.weight;
|
||||
|
||||
@@ -21,361 +21,322 @@ import :utils.misc;
|
||||
import :utils.user;
|
||||
|
||||
namespace mean_field::fem {
|
||||
FEM setup_fem(const std::string &filename, const utils::Args &args,
|
||||
const int extraRefine) {
|
||||
FEM fem;
|
||||
FEM setup_fem(
|
||||
const std::string &filename,
|
||||
const utils::Args &args,
|
||||
const int extraRefine
|
||||
) {
|
||||
FEM fem;
|
||||
|
||||
using GravityPotential = field::Gravity::Potential;
|
||||
using GravityFlux = field::Gravity::Flux;
|
||||
using DisplacementVector = field::Displacement::Vector;
|
||||
using DensityScalar = field::Density::Scalar;
|
||||
using EnthalpyScalar = field::Enthalpy::Scalar;
|
||||
using DomainSchema = utils::domain::CoreEnvelopeVacuumDomainSchema;
|
||||
using GravityPotential = field::Gravity::Potential;
|
||||
using GravityFlux = field::Gravity::Flux;
|
||||
using DisplacementVector = field::Displacement::Vector;
|
||||
using DensityScalar = field::Density::Scalar;
|
||||
using EnthalpyScalar = field::Enthalpy::Scalar;
|
||||
using DomainSchema = utils::domain::CoreEnvelopeVacuumDomainSchema;
|
||||
|
||||
// =====================================================================
|
||||
// Section 1: Mesh construction
|
||||
// =====================================================================
|
||||
// =====================================================================
|
||||
// Section 1: Mesh construction
|
||||
// =====================================================================
|
||||
|
||||
fem.smesh = stroid::IO::LoadStroidMesh(filename).value();
|
||||
fem.smesh = stroid::IO::LoadStroidMesh(filename).value();
|
||||
|
||||
if (extraRefine > 0) {
|
||||
stroid::refinement::UniformRefinement(fem.smesh, extraRefine);
|
||||
}
|
||||
if (extraRefine > 0) {
|
||||
stroid::refinement::UniformRefinement(fem.smesh, extraRefine);
|
||||
}
|
||||
|
||||
int mpiSize = 1;
|
||||
MPI_Comm_size(MPI_COMM_WORLD, &mpiSize);
|
||||
int mpiSize = 1;
|
||||
MPI_Comm_size(MPI_COMM_WORLD, &mpiSize);
|
||||
|
||||
const std::unique_ptr<int[]> meshPartitioning(
|
||||
fem.smesh.mesh->GeneratePartitioning(mpiSize, 1));
|
||||
const std::unique_ptr<int[]> meshPartitioning(fem.smesh.mesh->GeneratePartitioning(mpiSize, 1));
|
||||
|
||||
fem.mesh = std::make_unique<mfem::ParMesh>(MPI_COMM_WORLD, *fem.smesh.mesh,
|
||||
meshPartitioning.get(), 1);
|
||||
fem.mesh = std::make_unique<mfem::ParMesh>(MPI_COMM_WORLD, *fem.smesh.mesh, meshPartitioning.get(), 1);
|
||||
|
||||
fem.mesh->EnsureNodes();
|
||||
fem.mesh->EnsureNodes();
|
||||
|
||||
// =====================================================================
|
||||
// Section 2: Exterior compactification coordinate
|
||||
// =====================================================================
|
||||
// =====================================================================
|
||||
// Section 2: Exterior compactification coordinate
|
||||
// =====================================================================
|
||||
|
||||
if (fem.smesh.exterior_coordinate == nullptr) {
|
||||
throw std::runtime_error("Exterior coordinate not set.");
|
||||
}
|
||||
if (fem.smesh.exterior_coordinate == nullptr) {
|
||||
throw std::runtime_error("Exterior coordinate not set.");
|
||||
}
|
||||
|
||||
if (fem.smesh.exterior_coordinate->space == nullptr) {
|
||||
throw std::runtime_error("Space for exterior coordinate not set.");
|
||||
}
|
||||
if (fem.smesh.exterior_coordinate->space == nullptr) {
|
||||
throw std::runtime_error("Space for exterior coordinate not set.");
|
||||
}
|
||||
|
||||
if (fem.smesh.exterior_coordinate->values == nullptr) {
|
||||
throw std::runtime_error("Values for exterior coordinate not set.");
|
||||
}
|
||||
if (fem.smesh.exterior_coordinate->values == nullptr) {
|
||||
throw std::runtime_error("Values for exterior coordinate not set.");
|
||||
}
|
||||
|
||||
const mfem::FiniteElementSpace &serialCoordinateSpace =
|
||||
*fem.smesh.exterior_coordinate->space;
|
||||
const mfem::FiniteElementSpace &serialCoordinateSpace = *fem.smesh.exterior_coordinate->space;
|
||||
|
||||
const mfem::GridFunction &serialCoordinate =
|
||||
*fem.smesh.exterior_coordinate->values;
|
||||
const mfem::GridFunction &serialCoordinate = *fem.smesh.exterior_coordinate->values;
|
||||
|
||||
if (serialCoordinate.FESpace() != &serialCoordinateSpace) {
|
||||
throw std::runtime_error(
|
||||
"Exterior coordinate values are not associated with the "
|
||||
"supplied finite-element space.");
|
||||
}
|
||||
if (serialCoordinate.FESpace() != &serialCoordinateSpace) {
|
||||
throw std::runtime_error(
|
||||
"Exterior coordinate values are not associated with the "
|
||||
"supplied finite-element space."
|
||||
);
|
||||
}
|
||||
|
||||
if (serialCoordinateSpace.GetMesh() != fem.smesh.mesh.get()) {
|
||||
throw std::runtime_error(
|
||||
"Exterior coordinate space is not associated with the "
|
||||
"loaded STROID mesh.");
|
||||
}
|
||||
if (serialCoordinateSpace.GetMesh() != fem.smesh.mesh.get()) {
|
||||
throw std::runtime_error(
|
||||
"Exterior coordinate space is not associated with the "
|
||||
"loaded STROID mesh."
|
||||
);
|
||||
}
|
||||
|
||||
if (serialCoordinateSpace.GetVDim() != 1) {
|
||||
throw std::runtime_error("Exterior coordinate must be a scalar field.");
|
||||
}
|
||||
if (serialCoordinateSpace.GetVDim() != 1) {
|
||||
throw std::runtime_error("Exterior coordinate must be a scalar field.");
|
||||
}
|
||||
|
||||
if (serialCoordinate.Size() != serialCoordinateSpace.GetVSize()) {
|
||||
throw std::runtime_error(
|
||||
"Exterior coordinate value count does not match its "
|
||||
"finite-element space.");
|
||||
}
|
||||
if (serialCoordinate.Size() != serialCoordinateSpace.GetVSize()) {
|
||||
throw std::runtime_error(
|
||||
"Exterior coordinate value count does not match its "
|
||||
"finite-element space."
|
||||
);
|
||||
}
|
||||
|
||||
const int compactificationOrder = serialCoordinateSpace.GetMaxElementOrder();
|
||||
const int compactificationOrder = serialCoordinateSpace.GetMaxElementOrder();
|
||||
|
||||
const int dimension = fem.mesh->Dimension();
|
||||
const int dimension = fem.mesh->Dimension();
|
||||
|
||||
fem.compactificationFec =
|
||||
std::make_unique<mfem::H1_FECollection>(compactificationOrder, dimension);
|
||||
fem.compactificationFec = std::make_unique<mfem::H1_FECollection>(compactificationOrder, dimension);
|
||||
|
||||
fem.compactificationFes = std::make_unique<mfem::ParFiniteElementSpace>(
|
||||
fem.mesh.get(), fem.compactificationFec.get());
|
||||
fem.compactificationFes =
|
||||
std::make_unique<mfem::ParFiniteElementSpace>(fem.mesh.get(), fem.compactificationFec.get());
|
||||
|
||||
mfem::ParGridFunction distributedCoordinate(fem.mesh.get(), &serialCoordinate,
|
||||
meshPartitioning.get());
|
||||
mfem::ParGridFunction distributedCoordinate(fem.mesh.get(), &serialCoordinate, meshPartitioning.get());
|
||||
|
||||
if (distributedCoordinate.Size() != fem.compactificationFes->GetVSize()) {
|
||||
throw std::runtime_error(
|
||||
"Distributed exterior coordinate does not match the "
|
||||
"constructed parallel finite-element space.");
|
||||
}
|
||||
if (distributedCoordinate.Size() != fem.compactificationFes->GetVSize()) {
|
||||
throw std::runtime_error(
|
||||
"Distributed exterior coordinate does not match the "
|
||||
"constructed parallel finite-element space."
|
||||
);
|
||||
}
|
||||
|
||||
fem.compactificationCoordinate =
|
||||
std::make_unique<mfem::ParGridFunction>(fem.compactificationFes.get());
|
||||
fem.compactificationCoordinate = std::make_unique<mfem::ParGridFunction>(fem.compactificationFes.get());
|
||||
|
||||
*fem.compactificationCoordinate = distributedCoordinate;
|
||||
*fem.compactificationCoordinate = distributedCoordinate;
|
||||
|
||||
double localMinimum = std::numeric_limits<double>::infinity();
|
||||
double localMinimum = std::numeric_limits<double>::infinity();
|
||||
|
||||
double localMaximum = -std::numeric_limits<double>::infinity();
|
||||
double localMaximum = -std::numeric_limits<double>::infinity();
|
||||
|
||||
for (int index = 0; index < fem.compactificationCoordinate->Size(); ++index) {
|
||||
const double value = (*fem.compactificationCoordinate)(index);
|
||||
for (int index = 0; index < fem.compactificationCoordinate->Size(); ++index) {
|
||||
const double value = (*fem.compactificationCoordinate)(index);
|
||||
|
||||
if (!std::isfinite(value)) {
|
||||
throw std::runtime_error(
|
||||
"Exterior coordinate contains a non-finite value.");
|
||||
if (!std::isfinite(value)) {
|
||||
throw std::runtime_error("Exterior coordinate contains a non-finite value.");
|
||||
}
|
||||
|
||||
localMinimum = std::min(localMinimum, value);
|
||||
|
||||
localMaximum = std::max(localMaximum, value);
|
||||
}
|
||||
|
||||
double globalMinimum = 0.0;
|
||||
double globalMaximum = 0.0;
|
||||
|
||||
MPI_Allreduce(&localMinimum, &globalMinimum, 1, MPI_DOUBLE, MPI_MIN, MPI_COMM_WORLD);
|
||||
|
||||
MPI_Allreduce(&localMaximum, &globalMaximum, 1, MPI_DOUBLE, MPI_MAX, MPI_COMM_WORLD);
|
||||
|
||||
constexpr double coordinateTolerance = 1.0e-12;
|
||||
|
||||
if (globalMinimum < -coordinateTolerance || globalMaximum > 1.0 + coordinateTolerance) {
|
||||
throw std::runtime_error(
|
||||
"Exterior coordinate lies outside the expected "
|
||||
"interval [0, 1]."
|
||||
);
|
||||
}
|
||||
|
||||
// =====================================================================
|
||||
// Section 3: Compile-time field realization
|
||||
// =====================================================================
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Gravity potential: scalar L2
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
fem.gravityPotentialFec = GravityField::make_fec<GravityPotential>(dimension);
|
||||
|
||||
fem.gravityPotentialFes = GravityField::make_fespace<GravityPotential>(*fem.mesh, *fem.gravityPotentialFec);
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Gravity flux: H(div)/RT. Basis choices are encoded by field.mfem.
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
fem.gravityFluxFec = GravityField::make_fec<GravityFlux>(dimension);
|
||||
|
||||
fem.gravityFluxFes = GravityField::make_fespace<GravityFlux>(*fem.mesh, *fem.gravityFluxFec);
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Displacement: vector H1. Ordering is encoded by field.mfem.
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
fem.displacementFec = DisplacementField::make_fec<DisplacementVector>(dimension);
|
||||
|
||||
fem.displacementFes = DisplacementField::make_fespace<DisplacementVector>(*fem.mesh, *fem.displacementFec);
|
||||
|
||||
fem.displacement = std::make_unique<mfem::ParGridFunction>(fem.displacementFes.get());
|
||||
|
||||
*fem.displacement = 0.0;
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Density: scalar discontinuous L2
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
fem.densityFec = DensityField::make_fec<DensityScalar>(dimension);
|
||||
|
||||
fem.densityFes = DensityField::make_fespace<DensityScalar>(*fem.mesh, *fem.densityFec);
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Specific enthalpy: scalar continuous H1
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
fem.enthalpyFec = EnthalpyField::make_fec<EnthalpyScalar>(dimension);
|
||||
|
||||
fem.enthalpyFes = EnthalpyField::make_fespace<EnthalpyScalar>(*fem.mesh, *fem.enthalpyFec);
|
||||
|
||||
// =====================================================================
|
||||
// Section 4: Multipole data
|
||||
// =====================================================================
|
||||
|
||||
fem.com.SetSize(dimension);
|
||||
fem.com = 0.0;
|
||||
|
||||
fem.Q.SetSize(dimension, dimension);
|
||||
fem.Q = 0.0;
|
||||
|
||||
// =====================================================================
|
||||
// Section 5: Boundary markers
|
||||
// =====================================================================
|
||||
|
||||
const int boundaryAttributeCount = fem.mesh->bdr_attributes.Max();
|
||||
|
||||
fem.boundaryContext.inf_bounds.SetSize(boundaryAttributeCount);
|
||||
|
||||
fem.boundaryContext.stellar_bounds.SetSize(boundaryAttributeCount);
|
||||
|
||||
fem.boundaryContext.inf_bounds = 0;
|
||||
fem.boundaryContext.stellar_bounds = 0;
|
||||
|
||||
fem.boundaryContext.inf_bounds[static_cast<int>(boundary::Boundaries::INF_SURFACE) - 1] = 1;
|
||||
|
||||
fem.boundaryContext.stellar_bounds[static_cast<int>(boundary::Boundaries::STELLAR_SURFACE) - 1] = 1;
|
||||
|
||||
// =====================================================================
|
||||
// Section 7: Quadrature policy
|
||||
// =====================================================================
|
||||
|
||||
const quadrature::QuadratureOptions &quadratureOptions = args.quadrature;
|
||||
|
||||
if (quadratureOptions.validation.reject_negative_boosts && quadratureOptions.global_boost < 0) {
|
||||
throw std::invalid_argument("Global quadrature boost cannot be negative.");
|
||||
}
|
||||
|
||||
quadrature::RuleSet quadratureRuleSet =
|
||||
quadrature::make_rule_set(quadratureOptions.mode, quadratureOptions.global_boost);
|
||||
|
||||
if (quadratureOptions.fallback_fixed_order.has_value()) {
|
||||
if (*quadratureOptions.fallback_fixed_order < 0) {
|
||||
throw std::invalid_argument("Fallback quadrature order cannot be negative.");
|
||||
}
|
||||
|
||||
quadratureRuleSet.fallback.fixed_order = quadratureOptions.fallback_fixed_order;
|
||||
}
|
||||
|
||||
auto apply_quadrature_options = [&quadratureOptions](
|
||||
quadrature::RuleControl &ruleControl,
|
||||
const quadrature::QuadratureTermOptions &termOptions
|
||||
) {
|
||||
if (termOptions.fixed_order.has_value() && *termOptions.fixed_order < 0) {
|
||||
throw std::invalid_argument("Fixed quadrature order cannot be negative.");
|
||||
}
|
||||
|
||||
if (quadratureOptions.validation.reject_negative_boosts && termOptions.additional_boost < 0) {
|
||||
throw std::invalid_argument("Term quadrature boost cannot be negative.");
|
||||
}
|
||||
|
||||
ruleControl.boost += termOptions.additional_boost;
|
||||
|
||||
if (termOptions.fixed_order.has_value()) {
|
||||
ruleControl.fixed_order = termOptions.fixed_order;
|
||||
}
|
||||
};
|
||||
|
||||
apply_quadrature_options(quadratureRuleSet.gravity_hdiv_mass, quadratureOptions.gravity_hdiv_mass);
|
||||
|
||||
apply_quadrature_options(quadratureRuleSet.gravity_divergence, quadratureOptions.gravity_divergence);
|
||||
|
||||
apply_quadrature_options(quadratureRuleSet.gravity_source, quadratureOptions.gravity_source);
|
||||
|
||||
apply_quadrature_options(quadratureRuleSet.gravity_force, quadratureOptions.gravity_force);
|
||||
|
||||
apply_quadrature_options(quadratureRuleSet.gravity_boundary, quadratureOptions.gravity_boundary);
|
||||
|
||||
apply_quadrature_options(quadratureRuleSet.centrifugal, quadratureOptions.centrifugal);
|
||||
|
||||
apply_quadrature_options(quadratureRuleSet.density_projection, quadratureOptions.density_projection);
|
||||
|
||||
apply_quadrature_options(quadratureRuleSet.eos_closure, quadratureOptions.eos_closure);
|
||||
|
||||
apply_quadrature_options(quadratureRuleSet.hydrostatic_equilibrium, quadratureOptions.hydrostatic_equilibrium);
|
||||
|
||||
apply_quadrature_options(quadratureRuleSet.isobaric_surface, quadratureOptions.isobaric_surface);
|
||||
|
||||
apply_quadrature_options(quadratureRuleSet.mesh_extension, quadratureOptions.mesh_extension);
|
||||
|
||||
apply_quadrature_options(quadratureRuleSet.mass_conservation, quadratureOptions.mass_conservation);
|
||||
|
||||
apply_quadrature_options(quadratureRuleSet.mass_normalization, quadratureOptions.mass_normalization);
|
||||
|
||||
apply_quadrature_options(quadratureRuleSet.center_of_mass, quadratureOptions.center_of_mass);
|
||||
|
||||
apply_quadrature_options(quadratureRuleSet.quadrupole, quadratureOptions.quadrupole);
|
||||
|
||||
apply_quadrature_options(quadratureRuleSet.gravitational_energy, quadratureOptions.gravitational_energy);
|
||||
|
||||
apply_quadrature_options(quadratureRuleSet.pressure_integral, quadratureOptions.pressure_integral);
|
||||
|
||||
apply_quadrature_options(quadratureRuleSet.pressure_force, quadratureOptions.pressure_force);
|
||||
|
||||
apply_quadrature_options(quadratureRuleSet.virial, quadratureOptions.virial);
|
||||
|
||||
apply_quadrature_options(quadratureRuleSet.error_norm, quadratureOptions.error_norm);
|
||||
|
||||
apply_quadrature_options(quadratureRuleSet.roles.discretization, quadratureOptions.roles.discretization);
|
||||
|
||||
apply_quadrature_options(quadratureRuleSet.roles.preconditioner, quadratureOptions.roles.preconditioner);
|
||||
|
||||
apply_quadrature_options(quadratureRuleSet.roles.diagnostic, quadratureOptions.roles.diagnostic);
|
||||
|
||||
apply_quadrature_options(quadratureRuleSet.roles.projection, quadratureOptions.roles.projection);
|
||||
|
||||
fem.quadratureFactory =
|
||||
std::make_unique<quadrature::RuleFactory>(quadrature::Policy(std::move(quadratureRuleSet)));
|
||||
|
||||
// =====================================================================
|
||||
// Section 11: Stateless domain mapper
|
||||
// =====================================================================
|
||||
|
||||
auto exteriorDomain =
|
||||
std::make_unique<const mapping::compactification::KelvinCompactification>(args.kelvin_options);
|
||||
|
||||
MFEM_VERIFY(
|
||||
args.domain_mapper_options.vacuum_element_attribute ==
|
||||
DomainSchema::template material_attribute<utils::domain::Vacuum>(),
|
||||
"The domain-mapper compactification attribute must match the vacuum "
|
||||
"material registered by the "
|
||||
"production domain schema."
|
||||
);
|
||||
|
||||
fem.domainMapperStateless =
|
||||
std::make_unique<mapping::DomainMapper>(args.domain_mapper_options, std::move(exteriorDomain));
|
||||
|
||||
return fem;
|
||||
}
|
||||
|
||||
localMinimum = std::min(localMinimum, value);
|
||||
|
||||
localMaximum = std::max(localMaximum, value);
|
||||
}
|
||||
|
||||
double globalMinimum = 0.0;
|
||||
double globalMaximum = 0.0;
|
||||
|
||||
MPI_Allreduce(&localMinimum, &globalMinimum, 1, MPI_DOUBLE, MPI_MIN,
|
||||
MPI_COMM_WORLD);
|
||||
|
||||
MPI_Allreduce(&localMaximum, &globalMaximum, 1, MPI_DOUBLE, MPI_MAX,
|
||||
MPI_COMM_WORLD);
|
||||
|
||||
constexpr double coordinateTolerance = 1.0e-12;
|
||||
|
||||
if (globalMinimum < -coordinateTolerance ||
|
||||
globalMaximum > 1.0 + coordinateTolerance) {
|
||||
throw std::runtime_error("Exterior coordinate lies outside the expected "
|
||||
"interval [0, 1].");
|
||||
}
|
||||
|
||||
// =====================================================================
|
||||
// Section 3: Compile-time field realization
|
||||
// =====================================================================
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Gravity potential: scalar L2
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
fem.gravityPotentialFec = GravityField::make_fec<GravityPotential>(dimension);
|
||||
|
||||
fem.gravityPotentialFes = GravityField::make_fespace<GravityPotential>(
|
||||
*fem.mesh, *fem.gravityPotentialFec);
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Gravity flux: H(div)/RT. Basis choices are encoded by field.mfem.
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
fem.gravityFluxFec = GravityField::make_fec<GravityFlux>(dimension);
|
||||
|
||||
fem.gravityFluxFes =
|
||||
GravityField::make_fespace<GravityFlux>(*fem.mesh, *fem.gravityFluxFec);
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Displacement: vector H1. Ordering is encoded by field.mfem.
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
fem.displacementFec =
|
||||
DisplacementField::make_fec<DisplacementVector>(dimension);
|
||||
|
||||
fem.displacementFes = DisplacementField::make_fespace<DisplacementVector>(
|
||||
*fem.mesh, *fem.displacementFec);
|
||||
|
||||
fem.displacement =
|
||||
std::make_unique<mfem::ParGridFunction>(fem.displacementFes.get());
|
||||
|
||||
*fem.displacement = 0.0;
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Density: scalar discontinuous L2
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
fem.densityFec = DensityField::make_fec<DensityScalar>(dimension);
|
||||
|
||||
fem.densityFes =
|
||||
DensityField::make_fespace<DensityScalar>(*fem.mesh, *fem.densityFec);
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Specific enthalpy: scalar continuous H1
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
fem.enthalpyFec = EnthalpyField::make_fec<EnthalpyScalar>(dimension);
|
||||
|
||||
fem.enthalpyFes =
|
||||
EnthalpyField::make_fespace<EnthalpyScalar>(*fem.mesh, *fem.enthalpyFec);
|
||||
|
||||
// =====================================================================
|
||||
// Section 4: Multipole data
|
||||
// =====================================================================
|
||||
|
||||
fem.com.SetSize(dimension);
|
||||
fem.com = 0.0;
|
||||
|
||||
fem.Q.SetSize(dimension, dimension);
|
||||
fem.Q = 0.0;
|
||||
|
||||
// =====================================================================
|
||||
// Section 5: Boundary markers
|
||||
// =====================================================================
|
||||
|
||||
const int boundaryAttributeCount = fem.mesh->bdr_attributes.Max();
|
||||
|
||||
fem.boundaryContext.inf_bounds.SetSize(boundaryAttributeCount);
|
||||
|
||||
fem.boundaryContext.stellar_bounds.SetSize(boundaryAttributeCount);
|
||||
|
||||
fem.boundaryContext.inf_bounds = 0;
|
||||
fem.boundaryContext.stellar_bounds = 0;
|
||||
|
||||
fem.boundaryContext
|
||||
.inf_bounds[static_cast<int>(boundary::Boundaries::INF_SURFACE) - 1] = 1;
|
||||
|
||||
fem.boundaryContext
|
||||
.stellar_bounds[static_cast<int>(boundary::Boundaries::STELLAR_SURFACE) -
|
||||
1] = 1;
|
||||
|
||||
// =====================================================================
|
||||
// Section 7: Quadrature policy
|
||||
// =====================================================================
|
||||
|
||||
const quadrature::QuadratureOptions &quadratureOptions = args.quadrature;
|
||||
|
||||
if (quadratureOptions.validation.reject_negative_boosts &&
|
||||
quadratureOptions.global_boost < 0) {
|
||||
throw std::invalid_argument("Global quadrature boost cannot be negative.");
|
||||
}
|
||||
|
||||
quadrature::RuleSet quadratureRuleSet = quadrature::make_rule_set(
|
||||
quadratureOptions.mode, quadratureOptions.global_boost);
|
||||
|
||||
if (quadratureOptions.fallback_fixed_order.has_value()) {
|
||||
if (*quadratureOptions.fallback_fixed_order < 0) {
|
||||
throw std::invalid_argument(
|
||||
"Fallback quadrature order cannot be negative.");
|
||||
}
|
||||
|
||||
quadratureRuleSet.fallback.fixed_order =
|
||||
quadratureOptions.fallback_fixed_order;
|
||||
}
|
||||
|
||||
auto apply_quadrature_options = [&quadratureOptions](
|
||||
quadrature::RuleControl &ruleControl,
|
||||
const quadrature::QuadratureTermOptions
|
||||
&termOptions) {
|
||||
if (termOptions.fixed_order.has_value() && *termOptions.fixed_order < 0) {
|
||||
throw std::invalid_argument("Fixed quadrature order cannot be negative.");
|
||||
}
|
||||
|
||||
if (quadratureOptions.validation.reject_negative_boosts &&
|
||||
termOptions.additional_boost < 0) {
|
||||
throw std::invalid_argument("Term quadrature boost cannot be negative.");
|
||||
}
|
||||
|
||||
ruleControl.boost += termOptions.additional_boost;
|
||||
|
||||
if (termOptions.fixed_order.has_value()) {
|
||||
ruleControl.fixed_order = termOptions.fixed_order;
|
||||
}
|
||||
};
|
||||
|
||||
apply_quadrature_options(quadratureRuleSet.gravity_hdiv_mass,
|
||||
quadratureOptions.gravity_hdiv_mass);
|
||||
|
||||
apply_quadrature_options(quadratureRuleSet.gravity_divergence,
|
||||
quadratureOptions.gravity_divergence);
|
||||
|
||||
apply_quadrature_options(quadratureRuleSet.gravity_source,
|
||||
quadratureOptions.gravity_source);
|
||||
|
||||
apply_quadrature_options(quadratureRuleSet.gravity_force,
|
||||
quadratureOptions.gravity_force);
|
||||
|
||||
apply_quadrature_options(quadratureRuleSet.gravity_boundary,
|
||||
quadratureOptions.gravity_boundary);
|
||||
|
||||
apply_quadrature_options(quadratureRuleSet.centrifugal,
|
||||
quadratureOptions.centrifugal);
|
||||
|
||||
apply_quadrature_options(quadratureRuleSet.density_projection,
|
||||
quadratureOptions.density_projection);
|
||||
|
||||
apply_quadrature_options(quadratureRuleSet.eos_closure,
|
||||
quadratureOptions.eos_closure);
|
||||
|
||||
apply_quadrature_options(quadratureRuleSet.hydrostatic_equilibrium,
|
||||
quadratureOptions.hydrostatic_equilibrium);
|
||||
|
||||
apply_quadrature_options(quadratureRuleSet.isobaric_surface,
|
||||
quadratureOptions.isobaric_surface);
|
||||
|
||||
apply_quadrature_options(quadratureRuleSet.mesh_extension,
|
||||
quadratureOptions.mesh_extension);
|
||||
|
||||
apply_quadrature_options(quadratureRuleSet.mass_conservation,
|
||||
quadratureOptions.mass_conservation);
|
||||
|
||||
apply_quadrature_options(quadratureRuleSet.mass_normalization,
|
||||
quadratureOptions.mass_normalization);
|
||||
|
||||
apply_quadrature_options(quadratureRuleSet.center_of_mass,
|
||||
quadratureOptions.center_of_mass);
|
||||
|
||||
apply_quadrature_options(quadratureRuleSet.quadrupole,
|
||||
quadratureOptions.quadrupole);
|
||||
|
||||
apply_quadrature_options(quadratureRuleSet.gravitational_energy,
|
||||
quadratureOptions.gravitational_energy);
|
||||
|
||||
apply_quadrature_options(quadratureRuleSet.pressure_integral,
|
||||
quadratureOptions.pressure_integral);
|
||||
|
||||
apply_quadrature_options(quadratureRuleSet.pressure_force,
|
||||
quadratureOptions.pressure_force);
|
||||
|
||||
apply_quadrature_options(quadratureRuleSet.virial, quadratureOptions.virial);
|
||||
|
||||
apply_quadrature_options(quadratureRuleSet.error_norm,
|
||||
quadratureOptions.error_norm);
|
||||
|
||||
apply_quadrature_options(quadratureRuleSet.roles.discretization,
|
||||
quadratureOptions.roles.discretization);
|
||||
|
||||
apply_quadrature_options(quadratureRuleSet.roles.preconditioner,
|
||||
quadratureOptions.roles.preconditioner);
|
||||
|
||||
apply_quadrature_options(quadratureRuleSet.roles.diagnostic,
|
||||
quadratureOptions.roles.diagnostic);
|
||||
|
||||
apply_quadrature_options(quadratureRuleSet.roles.projection,
|
||||
quadratureOptions.roles.projection);
|
||||
|
||||
fem.quadratureFactory = std::make_unique<quadrature::RuleFactory>(
|
||||
quadrature::Policy(std::move(quadratureRuleSet)));
|
||||
|
||||
// =====================================================================
|
||||
// Section 11: Stateless domain mapper
|
||||
// =====================================================================
|
||||
|
||||
auto exteriorDomain =
|
||||
std::make_unique<const mapping::compactification::KelvinCompactification>(
|
||||
args.kelvin_options);
|
||||
|
||||
MFEM_VERIFY(
|
||||
args.domain_mapper_options.vacuum_element_attribute ==
|
||||
DomainSchema::template material_attribute<utils::domain::Vacuum>(),
|
||||
"The domain-mapper compactification attribute must match the vacuum "
|
||||
"material registered by the "
|
||||
"production domain schema.");
|
||||
|
||||
fem.domainMapperStateless = std::make_unique<mapping::DomainMapper>(
|
||||
args.domain_mapper_options, std::move(exteriorDomain));
|
||||
|
||||
return fem;
|
||||
}
|
||||
} // namespace mean_field::fem
|
||||
|
||||
@@ -9,7 +9,11 @@ namespace mean_field::integrators {
|
||||
const mfem::GridFunction &displacement,
|
||||
const mfem::GridFunction &compactification_coordinate
|
||||
)
|
||||
: m_mapping(mapper, displacement, compactification_coordinate) {
|
||||
: m_mapping(
|
||||
mapper,
|
||||
displacement,
|
||||
compactification_coordinate
|
||||
) {
|
||||
}
|
||||
|
||||
void AdvectionIntegrator::AssembleElementVector(
|
||||
|
||||
@@ -9,7 +9,11 @@ namespace mean_field::integrators {
|
||||
const mfem::GridFunction &compactification_coordinate,
|
||||
const mfem::Vector &omega
|
||||
)
|
||||
: m_mapping(mapper, displacement, compactification_coordinate),
|
||||
: m_mapping(
|
||||
mapper,
|
||||
displacement,
|
||||
compactification_coordinate
|
||||
),
|
||||
m_omega(3) {
|
||||
MFEM_ASSERT(omega.Size() == 3, "Omega vector must be 3D");
|
||||
m_omega = omega;
|
||||
|
||||
@@ -10,7 +10,11 @@ namespace mean_field::integrators {
|
||||
const mfem::GridFunction &compactification_coordinate,
|
||||
const mfem::Vector &omega
|
||||
)
|
||||
: m_mapping(mapper, displacement, compactification_coordinate),
|
||||
: m_mapping(
|
||||
mapper,
|
||||
displacement,
|
||||
compactification_coordinate
|
||||
),
|
||||
m_omega(omega) {
|
||||
m_omega_mat.SetSize(3, 3);
|
||||
m_omega_mat = 0.0;
|
||||
|
||||
@@ -19,7 +19,11 @@ namespace mean_field::integrators {
|
||||
const mfem::GridFunction &compactification_coordinate,
|
||||
const GravityForceJacobianMode jacobian_mode
|
||||
)
|
||||
: m_mapping(mapper, displacement, compactification_coordinate),
|
||||
: m_mapping(
|
||||
mapper,
|
||||
displacement,
|
||||
compactification_coordinate
|
||||
),
|
||||
m_jacobian_mode(jacobian_mode) {
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,11 @@ namespace mean_field::integrators {
|
||||
const mfem::GridFunction &displacement,
|
||||
const mfem::GridFunction &compactification_coordinate
|
||||
)
|
||||
: m_mapping(mapper, displacement, compactification_coordinate) { };
|
||||
: m_mapping(
|
||||
mapper,
|
||||
displacement,
|
||||
compactification_coordinate
|
||||
) { };
|
||||
|
||||
void ContinuityVolumeIntegrator::AssembleElementVector(
|
||||
const mfem::Array<const mfem::FiniteElement *> &el,
|
||||
@@ -174,7 +178,11 @@ namespace mean_field::integrators {
|
||||
const mfem::GridFunction &displacement,
|
||||
const mfem::GridFunction &compactification_coordinate
|
||||
)
|
||||
: m_mapping(mapper, displacement, compactification_coordinate) {
|
||||
: m_mapping(
|
||||
mapper,
|
||||
displacement,
|
||||
compactification_coordinate
|
||||
) {
|
||||
}
|
||||
|
||||
void ContinuityFaceIntegrator::AssembleFaceVector(
|
||||
@@ -206,11 +214,11 @@ 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;
|
||||
using DomainSchema = utils::domain::CoreEnvelopeVacuumDomainSchema;
|
||||
const int attr_minus = Tr.Elem1->Attribute;
|
||||
const int attr_plus = (Tr.Elem2 != nullptr) ? Tr.Elem2->Attribute : -1;
|
||||
using DomainSchema = utils::domain::CoreEnvelopeVacuumDomainSchema;
|
||||
if (DomainSchema::template attribute_belongs_to<utils::domain::Vacuum>(attr_minus) ||
|
||||
DomainSchema::template attribute_belongs_to<utils::domain::Vacuum>(attr_plus)) {
|
||||
return; // No flux contribution for vacuum faces
|
||||
@@ -416,9 +424,9 @@ namespace mean_field::integrators {
|
||||
}
|
||||
|
||||
bool ContinuityFaceIntegrator::skip_face(const mfem::FaceElementTransformations &Tr) {
|
||||
const int attr_minus = Tr.Elem1->Attribute;
|
||||
const int attr_plus = (Tr.Elem2 != nullptr) ? Tr.Elem2->Attribute : -1;
|
||||
using DomainSchema = utils::domain::CoreEnvelopeVacuumDomainSchema;
|
||||
const int attr_minus = Tr.Elem1->Attribute;
|
||||
const int attr_plus = (Tr.Elem2 != nullptr) ? Tr.Elem2->Attribute : -1;
|
||||
using DomainSchema = utils::domain::CoreEnvelopeVacuumDomainSchema;
|
||||
if (DomainSchema::template attribute_belongs_to<utils::domain::Vacuum>(attr_minus) ||
|
||||
DomainSchema::template attribute_belongs_to<utils::domain::Vacuum>(attr_plus)) {
|
||||
return true; // No flux contribution for vacuum faces
|
||||
|
||||
@@ -10,7 +10,11 @@ namespace mean_field::integrators {
|
||||
const double mu,
|
||||
const int quad_boost
|
||||
)
|
||||
: m_mapping(mapper, displacement, compactification_coordinate),
|
||||
: m_mapping(
|
||||
mapper,
|
||||
displacement,
|
||||
compactification_coordinate
|
||||
),
|
||||
m_mu(mu),
|
||||
m_quad_boost(quad_boost) {
|
||||
}
|
||||
|
||||
@@ -15,7 +15,11 @@ namespace mean_field::mapping {
|
||||
Coefficient &coeff,
|
||||
const COORDINATE_SPACE coord_space
|
||||
)
|
||||
: m_mapping(mapper, displacement, compactification_coordinate),
|
||||
: m_mapping(
|
||||
mapper,
|
||||
displacement,
|
||||
compactification_coordinate
|
||||
),
|
||||
m_coeff(coeff),
|
||||
m_coord_space(coord_space) { };
|
||||
|
||||
@@ -28,7 +32,7 @@ namespace mean_field::mapping {
|
||||
|
||||
switch (m_coord_space) {
|
||||
case COORDINATE_SPACE::PHYSICAL: {
|
||||
f_val = eval_at_point(m_coeff, T, ip);
|
||||
f_val = eval_at_point(m_coeff, T, ip);
|
||||
VolumeMappingContext context;
|
||||
MFEM_VERIFY(
|
||||
m_mapping.EvaluateVolume(T, ip, context) == MappingStatus::valid,
|
||||
@@ -63,7 +67,11 @@ namespace mean_field::mapping {
|
||||
const int dim
|
||||
)
|
||||
: MatrixCoefficient(dim),
|
||||
m_mapping(mapper, displacement, compactification_coordinate),
|
||||
m_mapping(
|
||||
mapper,
|
||||
displacement,
|
||||
compactification_coordinate
|
||||
),
|
||||
m_scalar(&sigma),
|
||||
m_tensor(nullptr) { };
|
||||
|
||||
@@ -74,7 +82,11 @@ namespace mean_field::mapping {
|
||||
MatrixCoefficient &sigma
|
||||
)
|
||||
: MatrixCoefficient(sigma.GetHeight()),
|
||||
m_mapping(mapper, displacement, compactification_coordinate),
|
||||
m_mapping(
|
||||
mapper,
|
||||
displacement,
|
||||
compactification_coordinate
|
||||
),
|
||||
m_scalar(nullptr),
|
||||
m_tensor(&sigma) { };
|
||||
|
||||
@@ -92,7 +104,7 @@ namespace mean_field::mapping {
|
||||
"Mapped diffusion coefficient encountered an invalid mapping."
|
||||
);
|
||||
const mfem::DenseMatrix &JInv = context.mapping.inverse_mapping_jacobian;
|
||||
const double detJ = context.mapping.mapping_determinant;
|
||||
const double detJ = context.mapping.mapping_determinant;
|
||||
|
||||
if (m_scalar) {
|
||||
const double sig_val = m_scalar->Eval(T, ip);
|
||||
@@ -120,7 +132,11 @@ namespace mean_field::mapping {
|
||||
VectorCoefficient &coeff
|
||||
)
|
||||
: VectorCoefficient(coeff.GetVDim()),
|
||||
m_mapping(mapper, displacement, compactification_coordinate),
|
||||
m_mapping(
|
||||
mapper,
|
||||
displacement,
|
||||
compactification_coordinate
|
||||
),
|
||||
m_coeff(coeff) { };
|
||||
|
||||
void MappedVectorCoefficient::Eval(
|
||||
@@ -137,7 +153,7 @@ namespace mean_field::mapping {
|
||||
"Mapped vector coefficient encountered an invalid mapping."
|
||||
);
|
||||
const mfem::DenseMatrix &JInv = context.mapping.inverse_mapping_jacobian;
|
||||
const double detJ = context.mapping.mapping_determinant;
|
||||
const double detJ = context.mapping.mapping_determinant;
|
||||
|
||||
mfem::Vector C_phys(dim);
|
||||
m_coeff.Eval(C_phys, T, ip);
|
||||
@@ -157,7 +173,11 @@ namespace mean_field::mapping {
|
||||
Func f // std::function<double(const mfem::Vector&)>
|
||||
)
|
||||
: m_f(std::move(f)),
|
||||
m_mapping(mapper, displacement, compactification_coordinate) { };
|
||||
m_mapping(
|
||||
mapper,
|
||||
displacement,
|
||||
compactification_coordinate
|
||||
) { };
|
||||
|
||||
double PhysicalPositionFunctionCoefficient::Eval(
|
||||
mfem::ElementTransformation &T,
|
||||
@@ -179,7 +199,11 @@ namespace mean_field::mapping {
|
||||
const int dim
|
||||
)
|
||||
: MatrixCoefficient(dim),
|
||||
m_mapping(mapper, displacement, compactification_coordinate) {
|
||||
m_mapping(
|
||||
mapper,
|
||||
displacement,
|
||||
compactification_coordinate
|
||||
) {
|
||||
}
|
||||
|
||||
void MappedHDivMassCoefficient::Eval(
|
||||
@@ -195,7 +219,7 @@ namespace mean_field::mapping {
|
||||
"Mapped H(div) coefficient encountered an invalid mapping."
|
||||
);
|
||||
const mfem::DenseMatrix &map_jacobian = context.mapping.mapping_jacobian;
|
||||
const double map_determinant = context.mapping.mapping_determinant;
|
||||
const double map_determinant = context.mapping.mapping_determinant;
|
||||
|
||||
MFEM_VERIFY(map_determinant > 0.0, "Domain mapping has a non-positive Jacobian determinant.");
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -15,7 +15,7 @@ namespace mean_field::models::structure {
|
||||
validate();
|
||||
}
|
||||
|
||||
const eos::EquationOfState &PolytropicStructure::equationOfState() const noexcept {
|
||||
const eos::Polytrope &PolytropicStructure::equationOfState() const noexcept {
|
||||
return m_equationOfState;
|
||||
}
|
||||
|
||||
@@ -29,7 +29,9 @@ namespace mean_field::models::structure {
|
||||
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 centralEnthalpy =
|
||||
eos::evaluate<eos::quantity::SpecificEnthalpy>(m_equationOfState, eos::DensityValue{request.centralDensity})
|
||||
.value();
|
||||
const double radialScaleSquared =
|
||||
centralEnthalpy / (4.0 * std::numbers::pi_v<double> * mean_field::utils::G * request.centralDensity);
|
||||
|
||||
@@ -61,11 +63,12 @@ namespace mean_field::models::structure {
|
||||
const double dimensionlessRadius = sampleFraction * surfaceCoordinate;
|
||||
const double laneEmdenValue =
|
||||
interpolateLaneEmdenValue(laneEmdenSolution, dimensionlessRadius, interpolationIndex);
|
||||
const double density = request.centralDensity * std::pow(laneEmdenValue, polytropicIndex);
|
||||
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(sampleIndex) = radialScale * dimensionlessRadius;
|
||||
seed.density(sampleIndex) = density;
|
||||
seed.enthalpy(sampleIndex) =
|
||||
eos::evaluate<eos::quantity::SpecificEnthalpy>(m_equationOfState, eos::DensityValue{density}).value();
|
||||
}
|
||||
|
||||
seed.radius(0) = 0.0;
|
||||
|
||||
@@ -80,13 +80,19 @@ namespace mean_field::operators::context::hydrostatic {
|
||||
: m_f(f),
|
||||
m_domainMapper(domainMapper),
|
||||
m_enthalpyMap(
|
||||
field::make_field_dof_map<field::Enthalpy, DomainSchema>(*f.enthalpyFes)
|
||||
field::make_field_dof_map<
|
||||
field::Enthalpy,
|
||||
DomainSchema>(*f.enthalpyFes)
|
||||
),
|
||||
m_gravityPotentialMap(
|
||||
field::make_field_dof_map<field::Gravity, DomainSchema>(*f.gravityPotentialFes)
|
||||
field::make_field_dof_map<
|
||||
field::Gravity,
|
||||
DomainSchema>(*f.gravityPotentialFes)
|
||||
),
|
||||
m_displacementMap(
|
||||
field::make_field_dof_map<field::Displacement, DomainSchema>(*f.displacementFes)
|
||||
field::make_field_dof_map<
|
||||
field::Displacement,
|
||||
DomainSchema>(*f.displacementFes)
|
||||
) {
|
||||
MFEM_VERIFY(m_f.mesh != nullptr, "HydrostaticEquilibriumContext requires a mesh.");
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -12,6 +12,8 @@ import :field.registry;
|
||||
import :utils.domain;
|
||||
|
||||
namespace {
|
||||
namespace eos = mean_field::eos;
|
||||
|
||||
using DomainSchema = mean_field::utils::domain::CoreEnvelopeVacuumDomainSchema;
|
||||
using ClosureDomain = mean_field::field::FieldDomainT<mean_field::field::Density>;
|
||||
|
||||
@@ -336,11 +338,21 @@ namespace {
|
||||
if (closureAction == ClosureAction::residual) {
|
||||
const double density = elementDensityInput * densityShape;
|
||||
|
||||
integrand = density - barotrope.density_from_enthalpy(baseEnthalpy);
|
||||
const double equationOfStateDensity =
|
||||
eos::evaluate<eos::quantity::Density>(barotrope, eos::SpecificEnthalpyValue{baseEnthalpy})
|
||||
.value();
|
||||
|
||||
integrand = density - equationOfStateDensity;
|
||||
} else {
|
||||
const double enthalpyVariation = elementEnthalpyVariation * enthalpyShape;
|
||||
|
||||
integrand = -barotrope.density_derivative_from_enthalpy(baseEnthalpy) * enthalpyVariation;
|
||||
const double densityDerivative =
|
||||
eos::partialDerivative<eos::quantity::Density, eos::quantity::SpecificEnthalpy>(
|
||||
barotrope, eos::SpecificEnthalpyValue{baseEnthalpy}
|
||||
)
|
||||
.value();
|
||||
|
||||
integrand = -densityDerivative * enthalpyVariation;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -628,11 +640,14 @@ namespace mean_field::operators::kernels {
|
||||
|
||||
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 equationOfStateDensity =
|
||||
eos::evaluate<eos::quantity::Density>(barotrope, eos::SpecificEnthalpyValue{enthalpyValue}).value();
|
||||
|
||||
const double closureValue = densityValue - equationOfStateDensity;
|
||||
|
||||
const double geometryActionValue = closureValue * mappingVariation.weight_variation;
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -398,11 +398,17 @@ namespace mean_field::operators {
|
||||
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_equationOfState.density_from_enthalpy(enthalpy);
|
||||
const double enthalpyDerivative = m_equationOfState.density_derivative_from_enthalpy(enthalpy);
|
||||
const double density = elementBaseDensity * densityShape;
|
||||
const double enthalpy = elementBaseEnthalpy * enthalpyShape;
|
||||
const double quadratureWeight = mappingContext.quadrature.weight;
|
||||
const eos::SpecificEnthalpyValue specificEnthalpy{enthalpy};
|
||||
const double eosDensity =
|
||||
eos::evaluate<eos::quantity::Density>(m_equationOfState, specificEnthalpy).value();
|
||||
const double enthalpyDerivative =
|
||||
eos::partialDerivative<eos::quantity::Density, eos::quantity::SpecificEnthalpy>(
|
||||
m_equationOfState, specificEnthalpy
|
||||
)
|
||||
.value();
|
||||
|
||||
MFEM_VERIFY(
|
||||
std::isfinite(quadratureWeight) && quadratureWeight > 0.0 && std::isfinite(eosDensity) &&
|
||||
|
||||
@@ -9,531 +9,535 @@ module mean_field;
|
||||
import :operators.prepared_gravity_source;
|
||||
|
||||
namespace {
|
||||
using DomainSchema = mean_field::utils::domain::CoreEnvelopeVacuumDomainSchema;
|
||||
using DomainSchema = mean_field::utils::domain::CoreEnvelopeVacuumDomainSchema;
|
||||
|
||||
int get_operator_height(const mean_field::fem::FEM &f) {
|
||||
MFEM_VERIFY(f.gravityPotentialFes != nullptr,
|
||||
"PreparedMappedGravitySourceOperator requires the "
|
||||
"gravity-potential "
|
||||
"finite-element space.");
|
||||
return mean_field::field::make_field_dof_map<mean_field::field::Gravity,
|
||||
DomainSchema>(
|
||||
*f.gravityPotentialFes)
|
||||
.reduced_size();
|
||||
}
|
||||
int get_operator_height(const mean_field::fem::FEM &f) {
|
||||
MFEM_VERIFY(
|
||||
f.gravityPotentialFes != nullptr, "PreparedMappedGravitySourceOperator requires the "
|
||||
"gravity-potential "
|
||||
"finite-element space."
|
||||
);
|
||||
return mean_field::field::make_field_dof_map<mean_field::field::Gravity, DomainSchema>(*f.gravityPotentialFes)
|
||||
.reduced_size();
|
||||
}
|
||||
|
||||
int get_operator_width(const mean_field::fem::FEM &f) {
|
||||
MFEM_VERIFY(f.densityFes != nullptr,
|
||||
"PreparedMappedGravitySourceOperator requires the density "
|
||||
"finite-element space.");
|
||||
return mean_field::field::make_field_dof_map<mean_field::field::Density,
|
||||
DomainSchema>(*f.densityFes)
|
||||
.reduced_size();
|
||||
}
|
||||
int get_operator_width(const mean_field::fem::FEM &f) {
|
||||
MFEM_VERIFY(
|
||||
f.densityFes != nullptr, "PreparedMappedGravitySourceOperator requires the density "
|
||||
"finite-element space."
|
||||
);
|
||||
return mean_field::field::make_field_dof_map<mean_field::field::Density, DomainSchema>(*f.densityFes)
|
||||
.reduced_size();
|
||||
}
|
||||
|
||||
void true_to_local(const mfem::ParFiniteElementSpace &finite_element_space,
|
||||
const mfem::Vector &true_vector,
|
||||
mfem::Vector &local_vector) {
|
||||
local_vector.SetSize(finite_element_space.GetVSize());
|
||||
void true_to_local(
|
||||
const mfem::ParFiniteElementSpace &finite_element_space,
|
||||
const mfem::Vector &true_vector,
|
||||
mfem::Vector &local_vector
|
||||
) {
|
||||
local_vector.SetSize(finite_element_space.GetVSize());
|
||||
|
||||
const mfem::Operator *prolongation =
|
||||
finite_element_space.GetProlongationMatrix();
|
||||
const mfem::Operator *prolongation = finite_element_space.GetProlongationMatrix();
|
||||
|
||||
if (prolongation != nullptr) {
|
||||
prolongation->Mult(true_vector, local_vector);
|
||||
} else {
|
||||
local_vector = true_vector;
|
||||
}
|
||||
}
|
||||
if (prolongation != nullptr) {
|
||||
prolongation->Mult(true_vector, local_vector);
|
||||
} else {
|
||||
local_vector = true_vector;
|
||||
}
|
||||
}
|
||||
|
||||
void local_to_true(const mfem::ParFiniteElementSpace &finite_element_space,
|
||||
const mfem::Vector &local_vector,
|
||||
mfem::Vector &true_vector) {
|
||||
MFEM_VERIFY(local_vector.Size() == finite_element_space.GetVSize(),
|
||||
"Local vector has the wrong size.");
|
||||
void local_to_true(
|
||||
const mfem::ParFiniteElementSpace &finite_element_space,
|
||||
const mfem::Vector &local_vector,
|
||||
mfem::Vector &true_vector
|
||||
) {
|
||||
MFEM_VERIFY(local_vector.Size() == finite_element_space.GetVSize(), "Local vector has the wrong size.");
|
||||
|
||||
true_vector.SetSize(finite_element_space.GetTrueVSize());
|
||||
true_vector = 0.0;
|
||||
true_vector.SetSize(finite_element_space.GetTrueVSize());
|
||||
true_vector = 0.0;
|
||||
|
||||
const mfem::Operator *prolongation =
|
||||
finite_element_space.GetProlongationMatrix();
|
||||
const mfem::Operator *prolongation = finite_element_space.GetProlongationMatrix();
|
||||
|
||||
if (prolongation != nullptr) {
|
||||
prolongation->MultTranspose(local_vector, true_vector);
|
||||
} else {
|
||||
true_vector = local_vector;
|
||||
}
|
||||
}
|
||||
if (prolongation != nullptr) {
|
||||
prolongation->MultTranspose(local_vector, true_vector);
|
||||
} else {
|
||||
true_vector = local_vector;
|
||||
}
|
||||
}
|
||||
|
||||
const mfem::IntegrationRule &
|
||||
get_source_rule(const mean_field::fem::FEM &f,
|
||||
const mfem::FiniteElement &density_element,
|
||||
const mfem::FiniteElement &potential_element,
|
||||
const mfem::ElementTransformation &transformation) {
|
||||
using GravityField = mean_field::field::Field<mean_field::field::Gravity>;
|
||||
MFEM_VERIFY(density_element.GetOrder() ==
|
||||
mean_field::field::Density::Scalar::familyOrder,
|
||||
"The prepared source trial element does not match the registered "
|
||||
"density field.");
|
||||
MFEM_VERIFY(potential_element.GetOrder() ==
|
||||
mean_field::field::Gravity::Potential::familyOrder,
|
||||
"The prepared source test element does not match the registered "
|
||||
"gravity potential.");
|
||||
const mean_field::quadrature::Query query = GravityField::make_query<
|
||||
mean_field::field::Gravity::Form::SourceProjection>(
|
||||
mean_field::quadrature::QuadratureRole::discretization,
|
||||
transformation.OrderW(), {}, mean_field::utils::DOMAINS::STELLAR,
|
||||
mean_field::quadrature::MappingKind::general);
|
||||
const mfem::IntegrationRule &get_source_rule(
|
||||
const mean_field::fem::FEM &f,
|
||||
const mfem::FiniteElement &density_element,
|
||||
const mfem::FiniteElement &potential_element,
|
||||
const mfem::ElementTransformation &transformation
|
||||
) {
|
||||
using GravityField = mean_field::field::Field<mean_field::field::Gravity>;
|
||||
MFEM_VERIFY(
|
||||
density_element.GetOrder() == mean_field::field::Density::Scalar::familyOrder,
|
||||
"The prepared source trial element does not match the registered "
|
||||
"density field."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
potential_element.GetOrder() == mean_field::field::Gravity::Potential::familyOrder,
|
||||
"The prepared source test element does not match the registered "
|
||||
"gravity potential."
|
||||
);
|
||||
const mean_field::quadrature::Query query =
|
||||
GravityField::make_query<mean_field::field::Gravity::Form::SourceProjection>(
|
||||
mean_field::quadrature::QuadratureRole::discretization, transformation.OrderW(), {},
|
||||
mean_field::utils::DOMAINS::STELLAR, mean_field::quadrature::MappingKind::general
|
||||
);
|
||||
|
||||
return *f.quadratureFactory->get(query, transformation.GetGeometryType())
|
||||
.integration_rule;
|
||||
}
|
||||
return *f.quadratureFactory->get(query, transformation.GetGeometryType()).integration_rule;
|
||||
}
|
||||
|
||||
class FrozenMappedGravitySourceCoefficient final : public mfem::Coefficient {
|
||||
public:
|
||||
FrozenMappedGravitySourceCoefficient(
|
||||
const mean_field::fem::FEM &f,
|
||||
const mean_field::mapping::DomainMapper &domain_mapper,
|
||||
const mfem::Vector &displacement_true)
|
||||
: m_fem(f), m_domain_mapper(domain_mapper),
|
||||
m_workspace(domain_mapper.GetDimension()) {
|
||||
true_to_local(*m_fem.displacementFes, displacement_true,
|
||||
m_displacement_local);
|
||||
}
|
||||
class FrozenMappedGravitySourceCoefficient final : public mfem::Coefficient {
|
||||
public:
|
||||
FrozenMappedGravitySourceCoefficient(
|
||||
const mean_field::fem::FEM &f,
|
||||
const mean_field::mapping::DomainMapper &domain_mapper,
|
||||
const mfem::Vector &displacement_true
|
||||
)
|
||||
: m_fem(f),
|
||||
m_domain_mapper(domain_mapper),
|
||||
m_workspace(domain_mapper.GetDimension()) {
|
||||
true_to_local(*m_fem.displacementFes, displacement_true, m_displacement_local);
|
||||
}
|
||||
|
||||
double Eval(mfem::ElementTransformation &transformation,
|
||||
const mfem::IntegrationPoint &integration_point) override {
|
||||
transformation.SetIntPoint(&integration_point);
|
||||
double Eval(
|
||||
mfem::ElementTransformation &transformation,
|
||||
const mfem::IntegrationPoint &integration_point
|
||||
) override {
|
||||
transformation.SetIntPoint(&integration_point);
|
||||
|
||||
const int element_id = transformation.ElementNo;
|
||||
MFEM_VERIFY(element_id >= 0 && element_id < m_fem.mesh->GetNE(),
|
||||
const int element_id = transformation.ElementNo;
|
||||
MFEM_VERIFY(
|
||||
element_id >= 0 && element_id < m_fem.mesh->GetNE(),
|
||||
"Mapped gravity source coefficient received an invalid element "
|
||||
"ID.");
|
||||
if (DomainSchema::template attribute_belongs_to<
|
||||
mean_field::utils::domain::Vacuum>(transformation.Attribute)) {
|
||||
return 0.0;
|
||||
}
|
||||
"ID."
|
||||
);
|
||||
if (DomainSchema::template attribute_belongs_to<mean_field::utils::domain::Vacuum>(
|
||||
transformation.Attribute
|
||||
)) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
LoadElement(element_id);
|
||||
const mean_field::mapping::ElementMappingData mapping_data{
|
||||
.displacement = *m_displacement_data,
|
||||
.compactification = *m_compactification_data};
|
||||
LoadElement(element_id);
|
||||
const mean_field::mapping::ElementMappingData mapping_data{
|
||||
.displacement = *m_displacement_data, .compactification = *m_compactification_data
|
||||
};
|
||||
|
||||
mean_field::mapping::VolumeMappingContext mapping_context;
|
||||
mean_field::mapping::VolumeMappingContext mapping_context;
|
||||
|
||||
const mean_field::mapping::MappingStatus status =
|
||||
m_domain_mapper.EvaluateVolume(mapping_data, transformation,
|
||||
integration_point, m_workspace,
|
||||
mapping_context);
|
||||
const mean_field::mapping::MappingStatus status = m_domain_mapper.EvaluateVolume(
|
||||
mapping_data, transformation, integration_point, m_workspace, mapping_context
|
||||
);
|
||||
|
||||
if (status != mean_field::mapping::MappingStatus::valid) {
|
||||
const mfem::FiniteElement &displacement_element =
|
||||
*m_fem.displacementFes->GetFE(element_id);
|
||||
const mfem::FiniteElement &compactification_element =
|
||||
*m_fem.compactificationFes->GetFE(element_id);
|
||||
if (status != mean_field::mapping::MappingStatus::valid) {
|
||||
const mfem::FiniteElement &displacement_element = *m_fem.displacementFes->GetFE(element_id);
|
||||
const mfem::FiniteElement &compactification_element = *m_fem.compactificationFes->GetFE(element_id);
|
||||
|
||||
mfem::Vector displacement_shape(displacement_element.GetDof());
|
||||
mfem::Vector compactification_shape(compactification_element.GetDof());
|
||||
mfem::Vector reference_position(m_domain_mapper.GetDimension());
|
||||
mfem::Vector displacement_value(m_domain_mapper.GetDimension());
|
||||
mfem::Vector displacement_shape(displacement_element.GetDof());
|
||||
mfem::Vector compactification_shape(compactification_element.GetDof());
|
||||
mfem::Vector reference_position(m_domain_mapper.GetDimension());
|
||||
mfem::Vector displacement_value(m_domain_mapper.GetDimension());
|
||||
|
||||
displacement_element.CalcShape(integration_point, displacement_shape);
|
||||
compactification_element.CalcShape(integration_point,
|
||||
compactification_shape);
|
||||
transformation.Transform(integration_point, reference_position);
|
||||
m_displacement_data->GetDofMatrix().MultTranspose(displacement_shape,
|
||||
displacement_value);
|
||||
displacement_element.CalcShape(integration_point, displacement_shape);
|
||||
compactification_element.CalcShape(integration_point, compactification_shape);
|
||||
transformation.Transform(integration_point, reference_position);
|
||||
m_displacement_data->GetDofMatrix().MultTranspose(displacement_shape, displacement_value);
|
||||
|
||||
const double compactification_coordinate =
|
||||
m_compactification_data->GetDofs() * compactification_shape;
|
||||
const double compactification_coordinate = m_compactification_data->GetDofs() * compactification_shape;
|
||||
|
||||
MFEM_ABORT(
|
||||
"Stateless domain mapping failed while preparing the "
|
||||
"gravity "
|
||||
"source operator."
|
||||
<< "\nMapping status = " << static_cast<int>(status)
|
||||
<< "\nElement ID = " << element_id
|
||||
<< "\nElement attribute = " << transformation.Attribute
|
||||
<< "\nIntegration-point index = " << integration_point.index
|
||||
<< "\nIntegration point = <" << integration_point.x << ", "
|
||||
<< integration_point.y << ", " << integration_point.z << ">"
|
||||
<< "\nReference position = <" << reference_position(0) << ", "
|
||||
<< reference_position(1) << ", " << reference_position(2) << ">"
|
||||
<< "\nReference radius = " << reference_position.Norml2()
|
||||
<< "\nDisplacement value = <" << displacement_value(0) << ", "
|
||||
<< displacement_value(1) << ", " << displacement_value(2) << ">"
|
||||
<< "\nDisplacement magnitude = " << displacement_value.Norml2()
|
||||
<< "\nCompactification coordinate = " << compactification_coordinate
|
||||
<< "\nDisplacement ordering = "
|
||||
<< static_cast<int>(m_fem.displacementFes->GetOrdering()));
|
||||
}
|
||||
const double mapping_determinant =
|
||||
mapping_context.mapping.mapping_determinant;
|
||||
MFEM_VERIFY(std::isfinite(mapping_determinant) && mapping_determinant > 0.0,
|
||||
MFEM_ABORT(
|
||||
"Stateless domain mapping failed while preparing the "
|
||||
"gravity "
|
||||
"source operator."
|
||||
<< "\nMapping status = " << static_cast<int>(status) << "\nElement ID = " << element_id
|
||||
<< "\nElement attribute = " << transformation.Attribute
|
||||
<< "\nIntegration-point index = " << integration_point.index << "\nIntegration point = <"
|
||||
<< integration_point.x << ", " << integration_point.y << ", " << integration_point.z << ">"
|
||||
<< "\nReference position = <" << reference_position(0) << ", " << reference_position(1) << ", "
|
||||
<< reference_position(2) << ">"
|
||||
<< "\nReference radius = " << reference_position.Norml2() << "\nDisplacement value = <"
|
||||
<< displacement_value(0) << ", " << displacement_value(1) << ", " << displacement_value(2) << ">"
|
||||
<< "\nDisplacement magnitude = " << displacement_value.Norml2()
|
||||
<< "\nCompactification coordinate = " << compactification_coordinate
|
||||
<< "\nDisplacement ordering = " << static_cast<int>(m_fem.displacementFes->GetOrdering())
|
||||
);
|
||||
}
|
||||
const double mapping_determinant = mapping_context.mapping.mapping_determinant;
|
||||
MFEM_VERIFY(
|
||||
std::isfinite(mapping_determinant) && mapping_determinant > 0.0,
|
||||
"Prepared gravity source operator encountered a non-positive "
|
||||
"or "
|
||||
"non-finite mapping determinant.");
|
||||
"non-finite mapping determinant."
|
||||
);
|
||||
|
||||
return 4.0 * std::numbers::pi * mean_field::utils::G * mapping_determinant;
|
||||
}
|
||||
return 4.0 * std::numbers::pi * mean_field::utils::G * mapping_determinant;
|
||||
}
|
||||
|
||||
private:
|
||||
void LoadElement(const int element_id) {
|
||||
if (element_id == m_cached_element_id) {
|
||||
return;
|
||||
}
|
||||
private:
|
||||
void LoadElement(const int element_id) {
|
||||
if (element_id == m_cached_element_id) {
|
||||
return;
|
||||
}
|
||||
|
||||
const mfem::FiniteElement &displacement_element =
|
||||
*m_fem.displacementFes->GetFE(element_id);
|
||||
const mfem::FiniteElement &compactification_element =
|
||||
*m_fem.compactificationFes->GetFE(element_id);
|
||||
const mfem::FiniteElement &displacement_element = *m_fem.displacementFes->GetFE(element_id);
|
||||
const mfem::FiniteElement &compactification_element = *m_fem.compactificationFes->GetFE(element_id);
|
||||
|
||||
mfem::DofTransformation *displacement_dof_transformation =
|
||||
m_fem.displacementFes->GetElementVDofs(element_id, m_displacement_dofs);
|
||||
mfem::DofTransformation *compactification_dof_transformation =
|
||||
m_fem.compactificationFes->GetElementDofs(element_id,
|
||||
m_compactification_dofs);
|
||||
mfem::DofTransformation *displacement_dof_transformation =
|
||||
m_fem.displacementFes->GetElementVDofs(element_id, m_displacement_dofs);
|
||||
mfem::DofTransformation *compactification_dof_transformation =
|
||||
m_fem.compactificationFes->GetElementDofs(element_id, m_compactification_dofs);
|
||||
|
||||
m_displacement_local.GetSubVector(m_displacement_dofs,
|
||||
m_element_displacement);
|
||||
m_fem.compactificationCoordinate->GetSubVector(m_compactification_dofs,
|
||||
m_element_compactification);
|
||||
m_displacement_local.GetSubVector(m_displacement_dofs, m_element_displacement);
|
||||
m_fem.compactificationCoordinate->GetSubVector(m_compactification_dofs, m_element_compactification);
|
||||
|
||||
if (displacement_dof_transformation != nullptr) {
|
||||
displacement_dof_transformation->InvTransformPrimal(
|
||||
m_element_displacement);
|
||||
}
|
||||
if (displacement_dof_transformation != nullptr) {
|
||||
displacement_dof_transformation->InvTransformPrimal(m_element_displacement);
|
||||
}
|
||||
|
||||
if (compactification_dof_transformation != nullptr) {
|
||||
compactification_dof_transformation->InvTransformPrimal(
|
||||
m_element_compactification);
|
||||
}
|
||||
if (compactification_dof_transformation != nullptr) {
|
||||
compactification_dof_transformation->InvTransformPrimal(m_element_compactification);
|
||||
}
|
||||
|
||||
m_displacement_data =
|
||||
std::make_unique<mean_field::mapping::ElementDisplacementData>(
|
||||
mean_field::mapping::ElementDisplacementDataFromElementVDofs(
|
||||
displacement_element, m_element_displacement));
|
||||
m_displacement_data = std::make_unique<mean_field::mapping::ElementDisplacementData>(
|
||||
mean_field::mapping::ElementDisplacementDataFromElementVDofs(
|
||||
displacement_element, m_element_displacement
|
||||
)
|
||||
);
|
||||
|
||||
m_compactification_data =
|
||||
std::make_unique<mean_field::mapping::ElementCompactificationData>(
|
||||
compactification_element, m_element_compactification);
|
||||
m_compactification_data = std::make_unique<mean_field::mapping::ElementCompactificationData>(
|
||||
compactification_element, m_element_compactification
|
||||
);
|
||||
|
||||
m_cached_element_id = element_id;
|
||||
}
|
||||
m_cached_element_id = element_id;
|
||||
}
|
||||
|
||||
const mean_field::fem::FEM &m_fem;
|
||||
const mean_field::mapping::DomainMapper &m_domain_mapper;
|
||||
const mean_field::fem::FEM &m_fem;
|
||||
const mean_field::mapping::DomainMapper &m_domain_mapper;
|
||||
|
||||
mfem::Vector m_displacement_local;
|
||||
mfem::Vector m_displacement_local;
|
||||
|
||||
mfem::Array<int> m_displacement_dofs;
|
||||
mfem::Array<int> m_compactification_dofs;
|
||||
mfem::Array<int> m_displacement_dofs;
|
||||
mfem::Array<int> m_compactification_dofs;
|
||||
|
||||
mfem::Vector m_element_displacement;
|
||||
mfem::Vector m_element_compactification;
|
||||
mfem::Vector m_element_displacement;
|
||||
mfem::Vector m_element_compactification;
|
||||
|
||||
std::unique_ptr<mean_field::mapping::ElementDisplacementData>
|
||||
m_displacement_data;
|
||||
std::unique_ptr<mean_field::mapping::ElementCompactificationData>
|
||||
m_compactification_data;
|
||||
std::unique_ptr<mean_field::mapping::ElementDisplacementData> m_displacement_data;
|
||||
std::unique_ptr<mean_field::mapping::ElementCompactificationData> m_compactification_data;
|
||||
|
||||
mean_field::mapping::DomainMapper::Workspace m_workspace;
|
||||
int m_cached_element_id{-1};
|
||||
};
|
||||
mean_field::mapping::DomainMapper::Workspace m_workspace;
|
||||
int m_cached_element_id{-1};
|
||||
};
|
||||
} // namespace
|
||||
|
||||
namespace mean_field::operators {
|
||||
PreparedMappedGravitySourceOperator::PreparedMappedGravitySourceOperator(
|
||||
const fem::FEM &f, const mapping::DomainMapper &domain_mapper)
|
||||
: Operator(get_operator_height(f), get_operator_width(f)), m_fem(f),
|
||||
m_domain_mapper(domain_mapper),
|
||||
m_density_map(field::make_field_dof_map<field::Density, DomainSchema>(
|
||||
*f.densityFes)),
|
||||
m_potential_map(field::make_field_dof_map<field::Gravity, DomainSchema>(
|
||||
*f.gravityPotentialFes)),
|
||||
m_displacement_map(
|
||||
field::make_field_dof_map<field::Displacement, DomainSchema>(
|
||||
*f.displacementFes)) {
|
||||
MFEM_VERIFY(f.mesh != nullptr,
|
||||
"PreparedMappedGravitySourceOperator requires a mesh.");
|
||||
MFEM_VERIFY(f.densityFes != nullptr,
|
||||
"PreparedMappedGravitySourceOperator requires the density "
|
||||
"finite-element space.");
|
||||
MFEM_VERIFY(f.gravityPotentialFes != nullptr,
|
||||
"PreparedMappedGravitySourceOperator requires the "
|
||||
"gravity-potential "
|
||||
"finite-element space.");
|
||||
MFEM_VERIFY(f.displacementFes != nullptr,
|
||||
"PreparedMappedGravitySourceOperator requires "
|
||||
"the displacement finite-element space.");
|
||||
MFEM_VERIFY(
|
||||
f.compactificationFes != nullptr,
|
||||
"PreparedMappedGravitySourceOperator requires the compactification "
|
||||
"finite-element space.");
|
||||
MFEM_VERIFY(
|
||||
f.compactificationCoordinate != nullptr,
|
||||
"PreparedMappedGravitySourceOperator requires the compactification "
|
||||
"coordinate.");
|
||||
MFEM_VERIFY(f.quadratureFactory != nullptr,
|
||||
"PreparedMappedGravitySourceOperator "
|
||||
"requires the quadrature-rule factory.");
|
||||
MFEM_VERIFY(domain_mapper.GetDimension() == f.mesh->Dimension(),
|
||||
"The stateless domain-mapper dimension does not match the mesh "
|
||||
"dimension.");
|
||||
PreparedMappedGravitySourceOperator::PreparedMappedGravitySourceOperator(
|
||||
const fem::FEM &f,
|
||||
const mapping::DomainMapper &domain_mapper
|
||||
)
|
||||
: Operator(
|
||||
get_operator_height(f),
|
||||
get_operator_width(f)
|
||||
),
|
||||
m_fem(f),
|
||||
m_domain_mapper(domain_mapper),
|
||||
m_density_map(
|
||||
field::make_field_dof_map<
|
||||
field::Density,
|
||||
DomainSchema>(*f.densityFes)
|
||||
),
|
||||
m_potential_map(
|
||||
field::make_field_dof_map<
|
||||
field::Gravity,
|
||||
DomainSchema>(*f.gravityPotentialFes)
|
||||
),
|
||||
m_displacement_map(
|
||||
field::make_field_dof_map<
|
||||
field::Displacement,
|
||||
DomainSchema>(*f.displacementFes)
|
||||
) {
|
||||
MFEM_VERIFY(f.mesh != nullptr, "PreparedMappedGravitySourceOperator requires a mesh.");
|
||||
MFEM_VERIFY(
|
||||
f.densityFes != nullptr, "PreparedMappedGravitySourceOperator requires the density "
|
||||
"finite-element space."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.gravityPotentialFes != nullptr, "PreparedMappedGravitySourceOperator requires the "
|
||||
"gravity-potential "
|
||||
"finite-element space."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.displacementFes != nullptr, "PreparedMappedGravitySourceOperator requires "
|
||||
"the displacement finite-element space."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.compactificationFes != nullptr, "PreparedMappedGravitySourceOperator requires the compactification "
|
||||
"finite-element space."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.compactificationCoordinate != nullptr,
|
||||
"PreparedMappedGravitySourceOperator requires the compactification "
|
||||
"coordinate."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.quadratureFactory != nullptr, "PreparedMappedGravitySourceOperator "
|
||||
"requires the quadrature-rule factory."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
domain_mapper.GetDimension() == f.mesh->Dimension(),
|
||||
"The stateless domain-mapper dimension does not match the mesh "
|
||||
"dimension."
|
||||
);
|
||||
|
||||
m_stellar_marker =
|
||||
utils::domain::make_attribute_marker<utils::domain::Stellar,
|
||||
DomainSchema>(*f.mesh);
|
||||
}
|
||||
|
||||
void PreparedMappedGravitySourceOperator::Prepare(
|
||||
const mfem::Vector &displacement) {
|
||||
MFEM_VERIFY(displacement.Size() == m_displacement_map.reduced_size(),
|
||||
"PreparedMappedGravitySourceOperator received a displacement "
|
||||
"vector "
|
||||
"with the wrong size.");
|
||||
|
||||
for (int i = 0; i < displacement.Size(); ++i) {
|
||||
MFEM_VERIFY(std::isfinite(displacement(i)),
|
||||
"PreparedMappedGravitySourceOperator received a non-finite "
|
||||
"displacement value.");
|
||||
}
|
||||
|
||||
m_is_prepared = false;
|
||||
m_displacement_true.SetSize(m_displacement_map.full_size());
|
||||
m_displacement_map.scatter(displacement, m_displacement_true);
|
||||
m_elements.clear();
|
||||
m_elements.reserve(m_fem.mesh->GetNE());
|
||||
|
||||
FrozenMappedGravitySourceCoefficient source_coefficient(
|
||||
m_fem, m_domain_mapper, m_displacement_true);
|
||||
|
||||
for (int element_id = 0; element_id < m_fem.mesh->GetNE(); ++element_id) {
|
||||
const int attribute = m_fem.mesh->GetAttribute(element_id);
|
||||
|
||||
if (attribute <= 0 || attribute > m_stellar_marker.Size() ||
|
||||
m_stellar_marker[attribute - 1] == 0) {
|
||||
continue;
|
||||
m_stellar_marker = utils::domain::make_attribute_marker<utils::domain::Stellar, DomainSchema>(*f.mesh);
|
||||
}
|
||||
|
||||
m_elements.emplace_back();
|
||||
ElementPAData &data = m_elements.back();
|
||||
void PreparedMappedGravitySourceOperator::Prepare(const mfem::Vector &displacement) {
|
||||
MFEM_VERIFY(
|
||||
displacement.Size() == m_displacement_map.reduced_size(),
|
||||
"PreparedMappedGravitySourceOperator received a displacement "
|
||||
"vector "
|
||||
"with the wrong size."
|
||||
);
|
||||
|
||||
data.element_id = element_id;
|
||||
for (int i = 0; i < displacement.Size(); ++i) {
|
||||
MFEM_VERIFY(
|
||||
std::isfinite(displacement(i)), "PreparedMappedGravitySourceOperator received a non-finite "
|
||||
"displacement value."
|
||||
);
|
||||
}
|
||||
|
||||
data.density_dof_transformation =
|
||||
m_fem.densityFes->GetElementDofs(element_id, data.density_dofs);
|
||||
m_is_prepared = false;
|
||||
m_displacement_true.SetSize(m_displacement_map.full_size());
|
||||
m_displacement_map.scatter(displacement, m_displacement_true);
|
||||
m_elements.clear();
|
||||
m_elements.reserve(m_fem.mesh->GetNE());
|
||||
|
||||
data.potential_dof_transformation =
|
||||
m_fem.gravityPotentialFes->GetElementDofs(element_id,
|
||||
data.potential_dofs);
|
||||
FrozenMappedGravitySourceCoefficient source_coefficient(m_fem, m_domain_mapper, m_displacement_true);
|
||||
|
||||
const mfem::FiniteElement &density_element =
|
||||
*m_fem.densityFes->GetFE(element_id);
|
||||
for (int element_id = 0; element_id < m_fem.mesh->GetNE(); ++element_id) {
|
||||
const int attribute = m_fem.mesh->GetAttribute(element_id);
|
||||
|
||||
const mfem::FiniteElement &potential_element =
|
||||
*m_fem.gravityPotentialFes->GetFE(element_id);
|
||||
if (attribute <= 0 || attribute > m_stellar_marker.Size() || m_stellar_marker[attribute - 1] == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
mfem::ElementTransformation &transformation =
|
||||
*m_fem.mesh->GetElementTransformation(element_id);
|
||||
m_elements.emplace_back();
|
||||
ElementPAData &data = m_elements.back();
|
||||
|
||||
const mfem::IntegrationRule &integration_rule = get_source_rule(
|
||||
m_fem, density_element, potential_element, transformation);
|
||||
data.element_id = element_id;
|
||||
|
||||
const int quadrature_point_count = integration_rule.GetNPoints();
|
||||
data.density_dof_transformation = m_fem.densityFes->GetElementDofs(element_id, data.density_dofs);
|
||||
|
||||
const int density_dof_count = density_element.GetDof();
|
||||
data.potential_dof_transformation =
|
||||
m_fem.gravityPotentialFes->GetElementDofs(element_id, data.potential_dofs);
|
||||
|
||||
const int potential_dof_count = potential_element.GetDof();
|
||||
const mfem::FiniteElement &density_element = *m_fem.densityFes->GetFE(element_id);
|
||||
|
||||
data.density_basis.SetSize(quadrature_point_count, density_dof_count);
|
||||
const mfem::FiniteElement &potential_element = *m_fem.gravityPotentialFes->GetFE(element_id);
|
||||
|
||||
data.potential_basis.SetSize(quadrature_point_count, potential_dof_count);
|
||||
mfem::ElementTransformation &transformation = *m_fem.mesh->GetElementTransformation(element_id);
|
||||
|
||||
data.quadrature_data.SetSize(quadrature_point_count);
|
||||
const mfem::IntegrationRule &integration_rule =
|
||||
get_source_rule(m_fem, density_element, potential_element, transformation);
|
||||
|
||||
mfem::Vector density_shape(density_dof_count);
|
||||
mfem::Vector potential_shape(potential_dof_count);
|
||||
const int quadrature_point_count = integration_rule.GetNPoints();
|
||||
|
||||
for (int quadrature_point = 0; quadrature_point < quadrature_point_count;
|
||||
++quadrature_point) {
|
||||
const mfem::IntegrationPoint &integration_point =
|
||||
integration_rule.IntPoint(quadrature_point);
|
||||
const int density_dof_count = density_element.GetDof();
|
||||
|
||||
transformation.SetIntPoint(&integration_point);
|
||||
const int potential_dof_count = potential_element.GetDof();
|
||||
|
||||
// CalcPhysShape matches the scalar mixed-mass discretization,
|
||||
// including the finite-element map type.
|
||||
density_element.CalcPhysShape(transformation, density_shape);
|
||||
data.density_basis.SetSize(quadrature_point_count, density_dof_count);
|
||||
|
||||
potential_element.CalcPhysShape(transformation, potential_shape);
|
||||
data.potential_basis.SetSize(quadrature_point_count, potential_dof_count);
|
||||
|
||||
for (int i = 0; i < density_dof_count; ++i) {
|
||||
data.density_basis(quadrature_point, i) = density_shape(i);
|
||||
}
|
||||
data.quadrature_data.SetSize(quadrature_point_count);
|
||||
|
||||
for (int i = 0; i < potential_dof_count; ++i) {
|
||||
data.potential_basis(quadrature_point, i) = potential_shape(i);
|
||||
}
|
||||
mfem::Vector density_shape(density_dof_count);
|
||||
mfem::Vector potential_shape(potential_dof_count);
|
||||
|
||||
const double coefficient_value =
|
||||
source_coefficient.Eval(transformation, integration_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);
|
||||
transformation.SetIntPoint(&integration_point);
|
||||
|
||||
const double quadrature_value = integration_point.weight *
|
||||
transformation.Weight() *
|
||||
coefficient_value;
|
||||
// CalcPhysShape matches the scalar mixed-mass discretization,
|
||||
// including the finite-element map type.
|
||||
density_element.CalcPhysShape(transformation, density_shape);
|
||||
|
||||
MFEM_VERIFY(std::isfinite(quadrature_value) && quadrature_value > 0.0,
|
||||
"Prepared gravity source operator encountered invalid "
|
||||
"quadrature data on element "
|
||||
<< element_id << ", quadrature point " << quadrature_point
|
||||
<< ".");
|
||||
potential_element.CalcPhysShape(transformation, potential_shape);
|
||||
|
||||
data.quadrature_data(quadrature_point) = quadrature_value;
|
||||
for (int i = 0; i < density_dof_count; ++i) {
|
||||
data.density_basis(quadrature_point, i) = density_shape(i);
|
||||
}
|
||||
|
||||
for (int i = 0; i < potential_dof_count; ++i) {
|
||||
data.potential_basis(quadrature_point, i) = potential_shape(i);
|
||||
}
|
||||
|
||||
const double coefficient_value = source_coefficient.Eval(transformation, integration_point);
|
||||
|
||||
transformation.SetIntPoint(&integration_point);
|
||||
|
||||
const double quadrature_value = integration_point.weight * transformation.Weight() * coefficient_value;
|
||||
|
||||
MFEM_VERIFY(
|
||||
std::isfinite(quadrature_value) && quadrature_value > 0.0,
|
||||
"Prepared gravity source operator encountered invalid "
|
||||
"quadrature data on element "
|
||||
<< element_id << ", quadrature point " << quadrature_point << "."
|
||||
);
|
||||
|
||||
data.quadrature_data(quadrature_point) = quadrature_value;
|
||||
}
|
||||
}
|
||||
|
||||
MFEM_VERIFY(!m_elements.empty(), "PreparedMappedGravitySourceOperator found no stellar elements.");
|
||||
|
||||
m_is_prepared = true;
|
||||
++m_preparation_count;
|
||||
}
|
||||
}
|
||||
void PreparedMappedGravitySourceOperator::Mult(
|
||||
const mfem::Vector &density,
|
||||
mfem::Vector &action
|
||||
) const {
|
||||
MFEM_VERIFY(
|
||||
m_is_prepared, "PreparedMappedGravitySourceOperator must be prepared before "
|
||||
"Mult is called."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(!m_elements.empty(),
|
||||
"PreparedMappedGravitySourceOperator found no stellar elements.");
|
||||
MFEM_VERIFY(
|
||||
density.Size() == Width(), "PreparedMappedGravitySourceOperator received a density vector "
|
||||
"with the wrong size."
|
||||
);
|
||||
|
||||
m_is_prepared = true;
|
||||
++m_preparation_count;
|
||||
}
|
||||
void PreparedMappedGravitySourceOperator::Mult(const mfem::Vector &density,
|
||||
mfem::Vector &action) const {
|
||||
MFEM_VERIFY(m_is_prepared,
|
||||
"PreparedMappedGravitySourceOperator must be prepared before "
|
||||
"Mult is called.");
|
||||
m_density_true.SetSize(m_density_map.full_size());
|
||||
m_density_map.scatter(density, m_density_true);
|
||||
|
||||
MFEM_VERIFY(density.Size() == Width(),
|
||||
"PreparedMappedGravitySourceOperator received a density vector "
|
||||
"with the wrong size.");
|
||||
mfem::Vector density_local;
|
||||
|
||||
m_density_true.SetSize(m_density_map.full_size());
|
||||
m_density_map.scatter(density, m_density_true);
|
||||
true_to_local(*m_fem.densityFes, m_density_true, density_local);
|
||||
|
||||
mfem::Vector density_local;
|
||||
mfem::Vector local_action(m_fem.gravityPotentialFes->GetVSize());
|
||||
local_action = 0.0;
|
||||
|
||||
true_to_local(*m_fem.densityFes, m_density_true, density_local);
|
||||
mfem::Vector element_density;
|
||||
mfem::Vector quadrature_density;
|
||||
mfem::Vector element_action;
|
||||
|
||||
mfem::Vector local_action(m_fem.gravityPotentialFes->GetVSize());
|
||||
local_action = 0.0;
|
||||
for (const ElementPAData &data : m_elements) {
|
||||
density_local.GetSubVector(data.density_dofs, element_density);
|
||||
|
||||
mfem::Vector element_density;
|
||||
mfem::Vector quadrature_density;
|
||||
mfem::Vector element_action;
|
||||
if (data.density_dof_transformation != nullptr) {
|
||||
data.density_dof_transformation->InvTransformPrimal(element_density);
|
||||
}
|
||||
|
||||
for (const ElementPAData &data : m_elements) {
|
||||
density_local.GetSubVector(data.density_dofs, element_density);
|
||||
quadrature_density.SetSize(data.quadrature_data.Size());
|
||||
|
||||
if (data.density_dof_transformation != nullptr) {
|
||||
data.density_dof_transformation->InvTransformPrimal(element_density);
|
||||
// B_density * x_e
|
||||
data.density_basis.Mult(element_density, quadrature_density);
|
||||
|
||||
// D * B_density * x_e
|
||||
for (int q = 0; q < quadrature_density.Size(); ++q) {
|
||||
quadrature_density(q) *= data.quadrature_data(q);
|
||||
}
|
||||
|
||||
element_action.SetSize(data.potential_dofs.Size());
|
||||
|
||||
// B_potential^T * D * B_density * x_e
|
||||
data.potential_basis.MultTranspose(quadrature_density, element_action);
|
||||
|
||||
if (data.potential_dof_transformation != nullptr) {
|
||||
data.potential_dof_transformation->TransformDual(element_action);
|
||||
}
|
||||
|
||||
local_action.AddElementVector(data.potential_dofs, element_action);
|
||||
}
|
||||
|
||||
local_to_true(*m_fem.gravityPotentialFes, local_action, m_action_true);
|
||||
action.SetSize(Height());
|
||||
m_potential_map.gather(m_action_true, action);
|
||||
}
|
||||
|
||||
quadrature_density.SetSize(data.quadrature_data.Size());
|
||||
void PreparedMappedGravitySourceOperator::MultTranspose(
|
||||
const mfem::Vector &potential,
|
||||
mfem::Vector &action
|
||||
) const {
|
||||
MFEM_VERIFY(
|
||||
m_is_prepared, "PreparedMappedGravitySourceOperator must be prepared before "
|
||||
"MultTranspose is called."
|
||||
);
|
||||
|
||||
// B_density * x_e
|
||||
data.density_basis.Mult(element_density, quadrature_density);
|
||||
MFEM_VERIFY(
|
||||
potential.Size() == Height(), "PreparedMappedGravitySourceOperator received a potential vector "
|
||||
"with the wrong size."
|
||||
);
|
||||
|
||||
// D * B_density * x_e
|
||||
for (int q = 0; q < quadrature_density.Size(); ++q) {
|
||||
quadrature_density(q) *= data.quadrature_data(q);
|
||||
m_potential_true.SetSize(m_potential_map.full_size());
|
||||
m_potential_map.scatter(potential, m_potential_true);
|
||||
|
||||
mfem::Vector potential_local;
|
||||
|
||||
true_to_local(*m_fem.gravityPotentialFes, m_potential_true, potential_local);
|
||||
|
||||
mfem::Vector local_action(m_fem.densityFes->GetVSize());
|
||||
local_action = 0.0;
|
||||
|
||||
mfem::Vector element_potential;
|
||||
mfem::Vector quadrature_potential;
|
||||
mfem::Vector element_action;
|
||||
|
||||
for (const ElementPAData &data : m_elements) {
|
||||
potential_local.GetSubVector(data.potential_dofs, element_potential);
|
||||
|
||||
if (data.potential_dof_transformation != nullptr) {
|
||||
data.potential_dof_transformation->InvTransformPrimal(element_potential);
|
||||
}
|
||||
|
||||
quadrature_potential.SetSize(data.quadrature_data.Size());
|
||||
|
||||
data.potential_basis.Mult(element_potential, quadrature_potential);
|
||||
|
||||
for (int q = 0; q < quadrature_potential.Size(); ++q) {
|
||||
quadrature_potential(q) *= data.quadrature_data(q);
|
||||
}
|
||||
|
||||
element_action.SetSize(data.density_dofs.Size());
|
||||
|
||||
data.density_basis.MultTranspose(quadrature_potential, element_action);
|
||||
|
||||
if (data.density_dof_transformation != nullptr) {
|
||||
data.density_dof_transformation->TransformDual(element_action);
|
||||
}
|
||||
|
||||
local_action.AddElementVector(data.density_dofs, element_action);
|
||||
}
|
||||
|
||||
local_to_true(*m_fem.densityFes, local_action, m_action_true);
|
||||
action.SetSize(Width());
|
||||
m_density_map.gather(m_action_true, action);
|
||||
}
|
||||
bool PreparedMappedGravitySourceOperator::IsPrepared() const noexcept {
|
||||
return m_is_prepared;
|
||||
}
|
||||
|
||||
element_action.SetSize(data.potential_dofs.Size());
|
||||
|
||||
// B_potential^T * D * B_density * x_e
|
||||
data.potential_basis.MultTranspose(quadrature_density, element_action);
|
||||
|
||||
if (data.potential_dof_transformation != nullptr) {
|
||||
data.potential_dof_transformation->TransformDual(element_action);
|
||||
std::uint64_t PreparedMappedGravitySourceOperator::GetPreparationCount() const noexcept {
|
||||
return m_preparation_count;
|
||||
}
|
||||
|
||||
local_action.AddElementVector(data.potential_dofs, element_action);
|
||||
}
|
||||
|
||||
local_to_true(*m_fem.gravityPotentialFes, local_action, m_action_true);
|
||||
action.SetSize(Height());
|
||||
m_potential_map.gather(m_action_true, action);
|
||||
}
|
||||
|
||||
void PreparedMappedGravitySourceOperator::MultTranspose(
|
||||
const mfem::Vector &potential, mfem::Vector &action) const {
|
||||
MFEM_VERIFY(m_is_prepared,
|
||||
"PreparedMappedGravitySourceOperator must be prepared before "
|
||||
"MultTranspose is called.");
|
||||
|
||||
MFEM_VERIFY(potential.Size() == Height(),
|
||||
"PreparedMappedGravitySourceOperator received a potential vector "
|
||||
"with the wrong size.");
|
||||
|
||||
m_potential_true.SetSize(m_potential_map.full_size());
|
||||
m_potential_map.scatter(potential, m_potential_true);
|
||||
|
||||
mfem::Vector potential_local;
|
||||
|
||||
true_to_local(*m_fem.gravityPotentialFes, m_potential_true, potential_local);
|
||||
|
||||
mfem::Vector local_action(m_fem.densityFes->GetVSize());
|
||||
local_action = 0.0;
|
||||
|
||||
mfem::Vector element_potential;
|
||||
mfem::Vector quadrature_potential;
|
||||
mfem::Vector element_action;
|
||||
|
||||
for (const ElementPAData &data : m_elements) {
|
||||
potential_local.GetSubVector(data.potential_dofs, element_potential);
|
||||
|
||||
if (data.potential_dof_transformation != nullptr) {
|
||||
data.potential_dof_transformation->InvTransformPrimal(element_potential);
|
||||
const field::FieldDofMap &PreparedMappedGravitySourceOperator::GetDensityMap() const noexcept {
|
||||
return m_density_map;
|
||||
}
|
||||
|
||||
quadrature_potential.SetSize(data.quadrature_data.Size());
|
||||
|
||||
data.potential_basis.Mult(element_potential, quadrature_potential);
|
||||
|
||||
for (int q = 0; q < quadrature_potential.Size(); ++q) {
|
||||
quadrature_potential(q) *= data.quadrature_data(q);
|
||||
const field::FieldDofMap &PreparedMappedGravitySourceOperator::GetPotentialMap() const noexcept {
|
||||
return m_potential_map;
|
||||
}
|
||||
|
||||
element_action.SetSize(data.density_dofs.Size());
|
||||
|
||||
data.density_basis.MultTranspose(quadrature_potential, element_action);
|
||||
|
||||
if (data.density_dof_transformation != nullptr) {
|
||||
data.density_dof_transformation->TransformDual(element_action);
|
||||
const field::FieldDofMap &PreparedMappedGravitySourceOperator::GetDisplacementMap() const noexcept {
|
||||
return m_displacement_map;
|
||||
}
|
||||
|
||||
local_action.AddElementVector(data.density_dofs, element_action);
|
||||
}
|
||||
|
||||
local_to_true(*m_fem.densityFes, local_action, m_action_true);
|
||||
action.SetSize(Width());
|
||||
m_density_map.gather(m_action_true, action);
|
||||
}
|
||||
bool PreparedMappedGravitySourceOperator::IsPrepared() const noexcept {
|
||||
return m_is_prepared;
|
||||
}
|
||||
|
||||
std::uint64_t
|
||||
PreparedMappedGravitySourceOperator::GetPreparationCount() const noexcept {
|
||||
return m_preparation_count;
|
||||
}
|
||||
|
||||
const field::FieldDofMap &
|
||||
PreparedMappedGravitySourceOperator::GetDensityMap() const noexcept {
|
||||
return m_density_map;
|
||||
}
|
||||
|
||||
const field::FieldDofMap &
|
||||
PreparedMappedGravitySourceOperator::GetPotentialMap() const noexcept {
|
||||
return m_potential_map;
|
||||
}
|
||||
|
||||
const field::FieldDofMap &
|
||||
PreparedMappedGravitySourceOperator::GetDisplacementMap() const noexcept {
|
||||
return m_displacement_map;
|
||||
}
|
||||
} // namespace mean_field::operators
|
||||
|
||||
@@ -8,405 +8,413 @@ module mean_field;
|
||||
import :operators.prepared_hdiv_mass;
|
||||
|
||||
namespace {
|
||||
using DomainSchema = mean_field::utils::domain::CoreEnvelopeVacuumDomainSchema;
|
||||
using DomainSchema = mean_field::utils::domain::CoreEnvelopeVacuumDomainSchema;
|
||||
|
||||
int get_operator_size(const mean_field::fem::FEM &f) {
|
||||
MFEM_VERIFY(f.gravityFluxFes != nullptr,
|
||||
"PreparedMappedHDivMassOperator requires the "
|
||||
"gravity-gradient finite-element space.");
|
||||
return mean_field::field::make_field_dof_map<mean_field::field::Gravity,
|
||||
DomainSchema>(*f.gravityFluxFes)
|
||||
.reduced_size();
|
||||
}
|
||||
|
||||
void true_to_local(const mfem::ParFiniteElementSpace &finite_element_space,
|
||||
const mfem::Vector &true_vector,
|
||||
mfem::Vector &local_vector) {
|
||||
local_vector.SetSize(finite_element_space.GetVSize());
|
||||
|
||||
const mfem::Operator *prolongation =
|
||||
finite_element_space.GetProlongationMatrix();
|
||||
|
||||
if (prolongation != nullptr) {
|
||||
prolongation->Mult(true_vector, local_vector);
|
||||
} else {
|
||||
local_vector = true_vector;
|
||||
}
|
||||
}
|
||||
|
||||
int find_representative_element(const mean_field::fem::FEM &f,
|
||||
const mfem::Array<int> &marker) {
|
||||
for (int element_id = 0; element_id < f.mesh->GetNE(); ++element_id) {
|
||||
const int attribute = f.mesh->GetAttribute(element_id);
|
||||
|
||||
if (attribute > 0 && attribute <= marker.Size() &&
|
||||
marker[attribute - 1] != 0) {
|
||||
return element_id;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
void validate_uniform_domain_discretization(
|
||||
const mean_field::fem::FEM &f, const mfem::Array<int> &marker,
|
||||
const int representative_element_id) {
|
||||
const mfem::FiniteElement &representative_element =
|
||||
*f.gravityFluxFes->GetFE(representative_element_id);
|
||||
const mfem::ElementTransformation &representative_transformation =
|
||||
*f.mesh->GetElementTransformation(representative_element_id);
|
||||
|
||||
for (int element_id = 0; element_id < f.mesh->GetNE(); ++element_id) {
|
||||
const int attribute = f.mesh->GetAttribute(element_id);
|
||||
|
||||
if (attribute <= 0 || attribute > marker.Size() ||
|
||||
marker[attribute - 1] == 0) {
|
||||
continue;
|
||||
int get_operator_size(const mean_field::fem::FEM &f) {
|
||||
MFEM_VERIFY(
|
||||
f.gravityFluxFes != nullptr, "PreparedMappedHDivMassOperator requires the "
|
||||
"gravity-gradient finite-element space."
|
||||
);
|
||||
return mean_field::field::make_field_dof_map<mean_field::field::Gravity, DomainSchema>(*f.gravityFluxFes)
|
||||
.reduced_size();
|
||||
}
|
||||
|
||||
const mfem::FiniteElement &element = *f.gravityFluxFes->GetFE(element_id);
|
||||
const mfem::ElementTransformation &transformation =
|
||||
*f.mesh->GetElementTransformation(element_id);
|
||||
void true_to_local(
|
||||
const mfem::ParFiniteElementSpace &finite_element_space,
|
||||
const mfem::Vector &true_vector,
|
||||
mfem::Vector &local_vector
|
||||
) {
|
||||
local_vector.SetSize(finite_element_space.GetVSize());
|
||||
|
||||
MFEM_VERIFY(element.GetGeomType() == representative_element.GetGeomType(),
|
||||
const mfem::Operator *prolongation = finite_element_space.GetProlongationMatrix();
|
||||
|
||||
if (prolongation != nullptr) {
|
||||
prolongation->Mult(true_vector, local_vector);
|
||||
} else {
|
||||
local_vector = true_vector;
|
||||
}
|
||||
}
|
||||
|
||||
int find_representative_element(
|
||||
const mean_field::fem::FEM &f,
|
||||
const mfem::Array<int> &marker
|
||||
) {
|
||||
for (int element_id = 0; element_id < f.mesh->GetNE(); ++element_id) {
|
||||
const int attribute = f.mesh->GetAttribute(element_id);
|
||||
|
||||
if (attribute > 0 && attribute <= marker.Size() && marker[attribute - 1] != 0) {
|
||||
return element_id;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
void validate_uniform_domain_discretization(
|
||||
const mean_field::fem::FEM &f,
|
||||
const mfem::Array<int> &marker,
|
||||
const int representative_element_id
|
||||
) {
|
||||
const mfem::FiniteElement &representative_element = *f.gravityFluxFes->GetFE(representative_element_id);
|
||||
const mfem::ElementTransformation &representative_transformation =
|
||||
*f.mesh->GetElementTransformation(representative_element_id);
|
||||
|
||||
for (int element_id = 0; element_id < f.mesh->GetNE(); ++element_id) {
|
||||
const int attribute = f.mesh->GetAttribute(element_id);
|
||||
|
||||
if (attribute <= 0 || attribute > marker.Size() || marker[attribute - 1] == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const mfem::FiniteElement &element = *f.gravityFluxFes->GetFE(element_id);
|
||||
const mfem::ElementTransformation &transformation = *f.mesh->GetElementTransformation(element_id);
|
||||
|
||||
MFEM_VERIFY(
|
||||
element.GetGeomType() == representative_element.GetGeomType(),
|
||||
"Prepared H(div) mass domains currently require a uniform "
|
||||
"element "
|
||||
"geometry.");
|
||||
MFEM_VERIFY(element.GetOrder() == representative_element.GetOrder(),
|
||||
"geometry."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
element.GetOrder() == representative_element.GetOrder(),
|
||||
"Prepared H(div) mass domains currently require a uniform "
|
||||
"finite-element order.");
|
||||
MFEM_VERIFY(transformation.OrderW() ==
|
||||
representative_transformation.OrderW(),
|
||||
"finite-element order."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
transformation.OrderW() == representative_transformation.OrderW(),
|
||||
"Prepared H(div) mass domains currently require a uniform "
|
||||
"geometry-weight order.");
|
||||
}
|
||||
}
|
||||
|
||||
class FrozenMappedHDivMassCoefficient final : public mfem::MatrixCoefficient {
|
||||
public:
|
||||
FrozenMappedHDivMassCoefficient(
|
||||
const mean_field::fem::FEM &f,
|
||||
const mean_field::mapping::DomainMapper &domain_mapper,
|
||||
const mfem::Vector &displacement_true, bool elevates_vacuum)
|
||||
: MatrixCoefficient(domain_mapper.GetDimension()), m_fem(f),
|
||||
m_domain_mapper(domain_mapper),
|
||||
m_workspace(domain_mapper.GetDimension()),
|
||||
m_elevates_vacuum(elevates_vacuum) {
|
||||
true_to_local(*m_fem.displacementFes, displacement_true,
|
||||
m_displacement_local);
|
||||
}
|
||||
|
||||
void Eval(mfem::DenseMatrix &mass_tensor,
|
||||
mfem::ElementTransformation &transformation,
|
||||
const mfem::IntegrationPoint &integration_point) override {
|
||||
transformation.SetIntPoint(&integration_point);
|
||||
|
||||
const int element_id = transformation.ElementNo;
|
||||
MFEM_VERIFY(
|
||||
element_id >= 0 && element_id < m_fem.mesh->GetNE(),
|
||||
"Mapped H(div) mass coefficient received an invalid element ID.");
|
||||
|
||||
const bool element_is_vacuum = DomainSchema::template attribute_belongs_to<
|
||||
mean_field::utils::domain::Vacuum>(transformation.Attribute);
|
||||
|
||||
if (element_is_vacuum != m_elevates_vacuum) {
|
||||
mass_tensor.SetSize(m_domain_mapper.GetDimension());
|
||||
mass_tensor = 0.0;
|
||||
return;
|
||||
"geometry-weight order."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
LoadElement(element_id);
|
||||
class FrozenMappedHDivMassCoefficient final : public mfem::MatrixCoefficient {
|
||||
public:
|
||||
FrozenMappedHDivMassCoefficient(
|
||||
const mean_field::fem::FEM &f,
|
||||
const mean_field::mapping::DomainMapper &domain_mapper,
|
||||
const mfem::Vector &displacement_true,
|
||||
bool elevates_vacuum
|
||||
)
|
||||
: MatrixCoefficient(domain_mapper.GetDimension()),
|
||||
m_fem(f),
|
||||
m_domain_mapper(domain_mapper),
|
||||
m_workspace(domain_mapper.GetDimension()),
|
||||
m_elevates_vacuum(elevates_vacuum) {
|
||||
true_to_local(*m_fem.displacementFes, displacement_true, m_displacement_local);
|
||||
}
|
||||
|
||||
const mean_field::mapping::ElementMappingData mapping_data{
|
||||
.displacement = *m_displacement_data,
|
||||
.compactification = *m_compactification_data};
|
||||
void Eval(
|
||||
mfem::DenseMatrix &mass_tensor,
|
||||
mfem::ElementTransformation &transformation,
|
||||
const mfem::IntegrationPoint &integration_point
|
||||
) override {
|
||||
transformation.SetIntPoint(&integration_point);
|
||||
|
||||
mean_field::mapping::VolumeMappingContext mapping_context;
|
||||
const int element_id = transformation.ElementNo;
|
||||
MFEM_VERIFY(
|
||||
element_id >= 0 && element_id < m_fem.mesh->GetNE(),
|
||||
"Mapped H(div) mass coefficient received an invalid element ID."
|
||||
);
|
||||
|
||||
const mean_field::mapping::MappingStatus status =
|
||||
m_domain_mapper.EvaluateVolume(mapping_data, transformation,
|
||||
integration_point, m_workspace,
|
||||
mapping_context);
|
||||
const bool element_is_vacuum =
|
||||
DomainSchema::template attribute_belongs_to<mean_field::utils::domain::Vacuum>(
|
||||
transformation.Attribute
|
||||
);
|
||||
|
||||
MFEM_VERIFY(status == mean_field::mapping::MappingStatus::valid,
|
||||
if (element_is_vacuum != m_elevates_vacuum) {
|
||||
mass_tensor.SetSize(m_domain_mapper.GetDimension());
|
||||
mass_tensor = 0.0;
|
||||
return;
|
||||
}
|
||||
|
||||
LoadElement(element_id);
|
||||
|
||||
const mean_field::mapping::ElementMappingData mapping_data{
|
||||
.displacement = *m_displacement_data, .compactification = *m_compactification_data
|
||||
};
|
||||
|
||||
mean_field::mapping::VolumeMappingContext mapping_context;
|
||||
|
||||
const mean_field::mapping::MappingStatus status = m_domain_mapper.EvaluateVolume(
|
||||
mapping_data, transformation, integration_point, m_workspace, mapping_context
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
status == mean_field::mapping::MappingStatus::valid,
|
||||
"Stateless domain mapping failed while preparing the H(div) "
|
||||
"mass "
|
||||
"operator. Mapping status = "
|
||||
<< static_cast<int>(status)
|
||||
<< ", element ID = " << element_id
|
||||
<< static_cast<int>(status) << ", element ID = " << element_id
|
||||
<< ", element attribute = " << transformation.Attribute
|
||||
<< ", coefficient domain = "
|
||||
<< (m_elevates_vacuum ? "vacuum" : "stellar"));
|
||||
<< ", coefficient domain = " << (m_elevates_vacuum ? "vacuum" : "stellar")
|
||||
);
|
||||
|
||||
const mfem::DenseMatrix &mapping_jacobian =
|
||||
mapping_context.mapping.mapping_jacobian;
|
||||
const double mapping_determinant =
|
||||
mapping_context.mapping.mapping_determinant;
|
||||
const mfem::DenseMatrix &mapping_jacobian = mapping_context.mapping.mapping_jacobian;
|
||||
const double mapping_determinant = mapping_context.mapping.mapping_determinant;
|
||||
|
||||
MFEM_VERIFY(std::isfinite(mapping_determinant) && mapping_determinant > 0.0,
|
||||
MFEM_VERIFY(
|
||||
std::isfinite(mapping_determinant) && mapping_determinant > 0.0,
|
||||
"Prepared H(div) mass operator encountered a non-positive or "
|
||||
"non-finite mapping determinant.");
|
||||
"non-finite mapping determinant."
|
||||
);
|
||||
|
||||
mfem::MultAtB(mapping_jacobian, mapping_jacobian, mass_tensor);
|
||||
mass_tensor *= 1.0 / mapping_determinant;
|
||||
}
|
||||
mfem::MultAtB(mapping_jacobian, mapping_jacobian, mass_tensor);
|
||||
mass_tensor *= 1.0 / mapping_determinant;
|
||||
}
|
||||
|
||||
private:
|
||||
void LoadElement(const int element_id) {
|
||||
if (element_id == m_cached_element_id) {
|
||||
return;
|
||||
}
|
||||
private:
|
||||
void LoadElement(const int element_id) {
|
||||
if (element_id == m_cached_element_id) {
|
||||
return;
|
||||
}
|
||||
|
||||
const mfem::FiniteElement &displacement_element =
|
||||
*m_fem.displacementFes->GetFE(element_id);
|
||||
const mfem::FiniteElement &compactification_element =
|
||||
*m_fem.compactificationFes->GetFE(element_id);
|
||||
const mfem::FiniteElement &displacement_element = *m_fem.displacementFes->GetFE(element_id);
|
||||
const mfem::FiniteElement &compactification_element = *m_fem.compactificationFes->GetFE(element_id);
|
||||
|
||||
mfem::DofTransformation *displacement_dof_transformation =
|
||||
m_fem.displacementFes->GetElementVDofs(element_id, m_displacement_dofs);
|
||||
mfem::DofTransformation *compactification_dof_transformation =
|
||||
m_fem.compactificationFes->GetElementDofs(element_id,
|
||||
m_compactification_dofs);
|
||||
mfem::DofTransformation *displacement_dof_transformation =
|
||||
m_fem.displacementFes->GetElementVDofs(element_id, m_displacement_dofs);
|
||||
mfem::DofTransformation *compactification_dof_transformation =
|
||||
m_fem.compactificationFes->GetElementDofs(element_id, m_compactification_dofs);
|
||||
|
||||
m_displacement_local.GetSubVector(m_displacement_dofs,
|
||||
m_element_displacement);
|
||||
m_fem.compactificationCoordinate->GetSubVector(m_compactification_dofs,
|
||||
m_element_compactification);
|
||||
m_displacement_local.GetSubVector(m_displacement_dofs, m_element_displacement);
|
||||
m_fem.compactificationCoordinate->GetSubVector(m_compactification_dofs, m_element_compactification);
|
||||
|
||||
if (displacement_dof_transformation != nullptr) {
|
||||
displacement_dof_transformation->InvTransformPrimal(
|
||||
m_element_displacement);
|
||||
}
|
||||
if (displacement_dof_transformation != nullptr) {
|
||||
displacement_dof_transformation->InvTransformPrimal(m_element_displacement);
|
||||
}
|
||||
|
||||
if (compactification_dof_transformation != nullptr) {
|
||||
compactification_dof_transformation->InvTransformPrimal(
|
||||
m_element_compactification);
|
||||
}
|
||||
if (compactification_dof_transformation != nullptr) {
|
||||
compactification_dof_transformation->InvTransformPrimal(m_element_compactification);
|
||||
}
|
||||
|
||||
m_displacement_data =
|
||||
std::make_unique<mean_field::mapping::ElementDisplacementData>(
|
||||
mean_field::mapping::ElementDisplacementDataFromElementVDofs(
|
||||
displacement_element, m_element_displacement));
|
||||
m_displacement_data = std::make_unique<mean_field::mapping::ElementDisplacementData>(
|
||||
mean_field::mapping::ElementDisplacementDataFromElementVDofs(
|
||||
displacement_element, m_element_displacement
|
||||
)
|
||||
);
|
||||
|
||||
m_compactification_data =
|
||||
std::make_unique<mean_field::mapping::ElementCompactificationData>(
|
||||
compactification_element, m_element_compactification);
|
||||
m_compactification_data = std::make_unique<mean_field::mapping::ElementCompactificationData>(
|
||||
compactification_element, m_element_compactification
|
||||
);
|
||||
|
||||
m_cached_element_id = element_id;
|
||||
}
|
||||
m_cached_element_id = element_id;
|
||||
}
|
||||
|
||||
const mean_field::fem::FEM &m_fem;
|
||||
const mean_field::mapping::DomainMapper &m_domain_mapper;
|
||||
const mean_field::fem::FEM &m_fem;
|
||||
const mean_field::mapping::DomainMapper &m_domain_mapper;
|
||||
|
||||
mfem::Vector m_displacement_local;
|
||||
mfem::Vector m_displacement_local;
|
||||
|
||||
mfem::Array<int> m_displacement_dofs;
|
||||
mfem::Array<int> m_compactification_dofs;
|
||||
mfem::Array<int> m_displacement_dofs;
|
||||
mfem::Array<int> m_compactification_dofs;
|
||||
|
||||
mfem::Vector m_element_displacement;
|
||||
mfem::Vector m_element_compactification;
|
||||
mfem::Vector m_element_displacement;
|
||||
mfem::Vector m_element_compactification;
|
||||
|
||||
std::unique_ptr<mean_field::mapping::ElementDisplacementData>
|
||||
m_displacement_data;
|
||||
std::unique_ptr<mean_field::mapping::ElementCompactificationData>
|
||||
m_compactification_data;
|
||||
std::unique_ptr<mean_field::mapping::ElementDisplacementData> m_displacement_data;
|
||||
std::unique_ptr<mean_field::mapping::ElementCompactificationData> m_compactification_data;
|
||||
|
||||
mean_field::mapping::DomainMapper::Workspace m_workspace;
|
||||
int m_cached_element_id{-1};
|
||||
bool m_elevates_vacuum;
|
||||
};
|
||||
mean_field::mapping::DomainMapper::Workspace m_workspace;
|
||||
int m_cached_element_id{-1};
|
||||
bool m_elevates_vacuum;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
namespace mean_field::operators {
|
||||
PreparedMappedHDivMassOperator::PreparedMappedHDivMassOperator(
|
||||
const fem::FEM &f, const mapping::DomainMapper &domain_mapper)
|
||||
: Operator(get_operator_size(f)), m_fem(f), m_domain_mapper(domain_mapper),
|
||||
m_flux_map(field::make_field_dof_map<field::Gravity, DomainSchema>(
|
||||
*f.gravityFluxFes)),
|
||||
m_displacement_map(
|
||||
field::make_field_dof_map<field::Displacement, DomainSchema>(
|
||||
*f.displacementFes)) {
|
||||
MFEM_VERIFY(f.mesh != nullptr,
|
||||
"PreparedMappedHDivMassOperator requires a mesh.");
|
||||
MFEM_VERIFY(f.gravityFluxFes != nullptr,
|
||||
"PreparedMappedHDivMassOperator requires the "
|
||||
"gravity-gradient finite-element space.");
|
||||
MFEM_VERIFY(f.displacementFes != nullptr,
|
||||
"PreparedMappedHDivMassOperator requires the "
|
||||
"displacement finite-element space.");
|
||||
MFEM_VERIFY(f.compactificationFes != nullptr,
|
||||
"PreparedMappedHDivMassOperator requires the compactification "
|
||||
"finite-element space.");
|
||||
MFEM_VERIFY(f.compactificationCoordinate != nullptr,
|
||||
"PreparedMappedHDivMassOperator requires the compactification "
|
||||
"coordinate.");
|
||||
MFEM_VERIFY(f.quadratureFactory != nullptr,
|
||||
"PreparedMappedHDivMassOperator requires the quadrature-rule "
|
||||
"factory.");
|
||||
MFEM_VERIFY(domain_mapper.GetDimension() == f.mesh->Dimension(),
|
||||
"The stateless domain-mapper dimension does not match the mesh "
|
||||
"dimension.");
|
||||
PreparedMappedHDivMassOperator::PreparedMappedHDivMassOperator(
|
||||
const fem::FEM &f,
|
||||
const mapping::DomainMapper &domain_mapper
|
||||
)
|
||||
: Operator(get_operator_size(f)),
|
||||
m_fem(f),
|
||||
m_domain_mapper(domain_mapper),
|
||||
m_flux_map(
|
||||
field::make_field_dof_map<
|
||||
field::Gravity,
|
||||
DomainSchema>(*f.gravityFluxFes)
|
||||
),
|
||||
m_displacement_map(
|
||||
field::make_field_dof_map<
|
||||
field::Displacement,
|
||||
DomainSchema>(*f.displacementFes)
|
||||
) {
|
||||
MFEM_VERIFY(f.mesh != nullptr, "PreparedMappedHDivMassOperator requires a mesh.");
|
||||
MFEM_VERIFY(
|
||||
f.gravityFluxFes != nullptr, "PreparedMappedHDivMassOperator requires the "
|
||||
"gravity-gradient finite-element space."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.displacementFes != nullptr, "PreparedMappedHDivMassOperator requires the "
|
||||
"displacement finite-element space."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.compactificationFes != nullptr, "PreparedMappedHDivMassOperator requires the compactification "
|
||||
"finite-element space."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.compactificationCoordinate != nullptr, "PreparedMappedHDivMassOperator requires the compactification "
|
||||
"coordinate."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.quadratureFactory != nullptr, "PreparedMappedHDivMassOperator requires the quadrature-rule "
|
||||
"factory."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
domain_mapper.GetDimension() == f.mesh->Dimension(),
|
||||
"The stateless domain-mapper dimension does not match the mesh "
|
||||
"dimension."
|
||||
);
|
||||
|
||||
m_stellar_marker =
|
||||
utils::domain::make_attribute_marker<utils::domain::Stellar,
|
||||
DomainSchema>(*f.mesh);
|
||||
m_vacuum_marker =
|
||||
utils::domain::make_attribute_marker<utils::domain::Vacuum, DomainSchema>(
|
||||
*f.mesh);
|
||||
m_stellar_marker = utils::domain::make_attribute_marker<utils::domain::Stellar, DomainSchema>(*f.mesh);
|
||||
m_vacuum_marker = utils::domain::make_attribute_marker<utils::domain::Vacuum, DomainSchema>(*f.mesh);
|
||||
|
||||
const int stellar_element_id =
|
||||
find_representative_element(f, m_stellar_marker);
|
||||
const int vacuum_element_id = find_representative_element(f, m_vacuum_marker);
|
||||
const int stellar_element_id = find_representative_element(f, m_stellar_marker);
|
||||
const int vacuum_element_id = find_representative_element(f, m_vacuum_marker);
|
||||
|
||||
MFEM_VERIFY(stellar_element_id >= 0,
|
||||
"PreparedMappedHDivMassOperator requires "
|
||||
"at least one stellar element.");
|
||||
MFEM_VERIFY(vacuum_element_id >= 0,
|
||||
"PreparedMappedHDivMassOperator requires at "
|
||||
"least one compactified vacuum element.");
|
||||
MFEM_VERIFY(
|
||||
stellar_element_id >= 0, "PreparedMappedHDivMassOperator requires "
|
||||
"at least one stellar element."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
vacuum_element_id >= 0, "PreparedMappedHDivMassOperator requires at "
|
||||
"least one compactified vacuum element."
|
||||
);
|
||||
|
||||
validate_uniform_domain_discretization(f, m_stellar_marker,
|
||||
stellar_element_id);
|
||||
validate_uniform_domain_discretization(f, m_vacuum_marker, vacuum_element_id);
|
||||
}
|
||||
validate_uniform_domain_discretization(f, m_stellar_marker, stellar_element_id);
|
||||
validate_uniform_domain_discretization(f, m_vacuum_marker, vacuum_element_id);
|
||||
}
|
||||
|
||||
void PreparedMappedHDivMassOperator::Prepare(const mfem::Vector &displacement) {
|
||||
MFEM_VERIFY(displacement.Size() == m_displacement_map.reduced_size(),
|
||||
"PreparedMappedHDivMassOperator received a displacement vector "
|
||||
"with "
|
||||
"the wrong size.");
|
||||
void PreparedMappedHDivMassOperator::Prepare(const mfem::Vector &displacement) {
|
||||
MFEM_VERIFY(
|
||||
displacement.Size() == m_displacement_map.reduced_size(),
|
||||
"PreparedMappedHDivMassOperator received a displacement vector "
|
||||
"with "
|
||||
"the wrong size."
|
||||
);
|
||||
|
||||
for (int i = 0; i < displacement.Size(); ++i) {
|
||||
MFEM_VERIFY(std::isfinite(displacement(i)),
|
||||
"PreparedMappedHDivMassOperator received a non-finite "
|
||||
"displacement "
|
||||
"value.");
|
||||
}
|
||||
for (int i = 0; i < displacement.Size(); ++i) {
|
||||
MFEM_VERIFY(
|
||||
std::isfinite(displacement(i)), "PreparedMappedHDivMassOperator received a non-finite "
|
||||
"displacement "
|
||||
"value."
|
||||
);
|
||||
}
|
||||
|
||||
m_displacement_true.SetSize(m_displacement_map.full_size());
|
||||
m_displacement_map.scatter(displacement, m_displacement_true);
|
||||
m_displacement_true.SetSize(m_displacement_map.full_size());
|
||||
m_displacement_map.scatter(displacement, m_displacement_true);
|
||||
|
||||
const int stellar_element_id =
|
||||
find_representative_element(m_fem, m_stellar_marker);
|
||||
const int vacuum_element_id =
|
||||
find_representative_element(m_fem, m_vacuum_marker);
|
||||
const int stellar_element_id = find_representative_element(m_fem, m_stellar_marker);
|
||||
const int vacuum_element_id = find_representative_element(m_fem, m_vacuum_marker);
|
||||
|
||||
const mfem::FiniteElement &stellar_element =
|
||||
*m_fem.gravityFluxFes->GetFE(stellar_element_id);
|
||||
const mfem::FiniteElement &vacuum_element =
|
||||
*m_fem.gravityFluxFes->GetFE(vacuum_element_id);
|
||||
const mfem::FiniteElement &stellar_element = *m_fem.gravityFluxFes->GetFE(stellar_element_id);
|
||||
const mfem::FiniteElement &vacuum_element = *m_fem.gravityFluxFes->GetFE(vacuum_element_id);
|
||||
|
||||
mfem::ElementTransformation &stellar_transformation =
|
||||
*m_fem.mesh->GetElementTransformation(stellar_element_id);
|
||||
mfem::ElementTransformation &vacuum_transformation =
|
||||
*m_fem.mesh->GetElementTransformation(vacuum_element_id);
|
||||
mfem::ElementTransformation &stellar_transformation = *m_fem.mesh->GetElementTransformation(stellar_element_id);
|
||||
mfem::ElementTransformation &vacuum_transformation = *m_fem.mesh->GetElementTransformation(vacuum_element_id);
|
||||
|
||||
m_stellar_mass_form.reset();
|
||||
m_vacuum_mass_form.reset();
|
||||
m_stellar_mass_coefficient.reset();
|
||||
m_vacuum_mass_coefficient.reset();
|
||||
m_stellar_mass_form.reset();
|
||||
m_vacuum_mass_form.reset();
|
||||
m_stellar_mass_coefficient.reset();
|
||||
m_vacuum_mass_coefficient.reset();
|
||||
|
||||
m_stellar_mass_coefficient =
|
||||
std::make_unique<FrozenMappedHDivMassCoefficient>(
|
||||
m_fem, m_domain_mapper, m_displacement_true, false);
|
||||
m_vacuum_mass_coefficient = std::make_unique<FrozenMappedHDivMassCoefficient>(
|
||||
m_fem, m_domain_mapper, m_displacement_true, true);
|
||||
m_stellar_mass_coefficient =
|
||||
std::make_unique<FrozenMappedHDivMassCoefficient>(m_fem, m_domain_mapper, m_displacement_true, false);
|
||||
m_vacuum_mass_coefficient =
|
||||
std::make_unique<FrozenMappedHDivMassCoefficient>(m_fem, m_domain_mapper, m_displacement_true, true);
|
||||
|
||||
m_stellar_mass_form =
|
||||
std::make_unique<mfem::ParBilinearForm>(m_fem.gravityFluxFes.get());
|
||||
m_vacuum_mass_form =
|
||||
std::make_unique<mfem::ParBilinearForm>(m_fem.gravityFluxFes.get());
|
||||
m_stellar_mass_form->SetAssemblyLevel(mfem::AssemblyLevel::PARTIAL);
|
||||
m_vacuum_mass_form->SetAssemblyLevel(mfem::AssemblyLevel::PARTIAL);
|
||||
m_stellar_mass_form = std::make_unique<mfem::ParBilinearForm>(m_fem.gravityFluxFes.get());
|
||||
m_vacuum_mass_form = std::make_unique<mfem::ParBilinearForm>(m_fem.gravityFluxFes.get());
|
||||
m_stellar_mass_form->SetAssemblyLevel(mfem::AssemblyLevel::PARTIAL);
|
||||
m_vacuum_mass_form->SetAssemblyLevel(mfem::AssemblyLevel::PARTIAL);
|
||||
|
||||
auto stellar_integrator = std::make_unique<mfem::VectorFEMassIntegrator>(
|
||||
*m_stellar_mass_coefficient);
|
||||
auto vacuum_integrator = std::make_unique<mfem::VectorFEMassIntegrator>(
|
||||
*m_vacuum_mass_coefficient);
|
||||
auto stellar_integrator = std::make_unique<mfem::VectorFEMassIntegrator>(*m_stellar_mass_coefficient);
|
||||
auto vacuum_integrator = std::make_unique<mfem::VectorFEMassIntegrator>(*m_vacuum_mass_coefficient);
|
||||
|
||||
m_fem.quadratureFactory->configure_gravity_hdiv_mass(
|
||||
*stellar_integrator, quadrature::QuadratureRole::discretization,
|
||||
stellar_element, stellar_transformation, utils::DOMAINS::STELLAR,
|
||||
quadrature::MappingKind::general);
|
||||
m_fem.quadratureFactory->configure_gravity_hdiv_mass(
|
||||
*stellar_integrator, quadrature::QuadratureRole::discretization, stellar_element, stellar_transformation,
|
||||
utils::DOMAINS::STELLAR, quadrature::MappingKind::general
|
||||
);
|
||||
|
||||
m_fem.quadratureFactory->configure_gravity_hdiv_mass(
|
||||
*vacuum_integrator, quadrature::QuadratureRole::discretization,
|
||||
vacuum_element, vacuum_transformation, utils::DOMAINS::VACUUM,
|
||||
quadrature::MappingKind::kelvin);
|
||||
m_fem.quadratureFactory->configure_gravity_hdiv_mass(
|
||||
*vacuum_integrator, quadrature::QuadratureRole::discretization, vacuum_element, vacuum_transformation,
|
||||
utils::DOMAINS::VACUUM, quadrature::MappingKind::kelvin
|
||||
);
|
||||
|
||||
m_stellar_mass_form->AddDomainIntegrator(stellar_integrator.release(),
|
||||
m_stellar_marker);
|
||||
m_vacuum_mass_form->AddDomainIntegrator(vacuum_integrator.release(),
|
||||
m_vacuum_marker);
|
||||
m_stellar_mass_form->Assemble();
|
||||
m_vacuum_mass_form->Assemble();
|
||||
m_stellar_mass_form->AddDomainIntegrator(stellar_integrator.release(), m_stellar_marker);
|
||||
m_vacuum_mass_form->AddDomainIntegrator(vacuum_integrator.release(), m_vacuum_marker);
|
||||
m_stellar_mass_form->Assemble();
|
||||
m_vacuum_mass_form->Assemble();
|
||||
|
||||
m_is_prepared = true;
|
||||
++m_preparation_count;
|
||||
}
|
||||
m_is_prepared = true;
|
||||
++m_preparation_count;
|
||||
}
|
||||
|
||||
void PreparedMappedHDivMassOperator::Mult(const mfem::Vector &gravity_gradient,
|
||||
mfem::Vector &action) const {
|
||||
MFEM_VERIFY(m_is_prepared, "PreparedMappedHDivMassOperator must be prepared "
|
||||
"before Mult is called.");
|
||||
MFEM_VERIFY(
|
||||
m_stellar_mass_form != nullptr && m_vacuum_mass_form != nullptr,
|
||||
"PreparedMappedHDivMassOperator has incomplete domain mass forms.");
|
||||
MFEM_VERIFY(
|
||||
gravity_gradient.Size() == Width(),
|
||||
"PreparedMappedHDivMassOperator received a gravity-gradient vector "
|
||||
"with the wrong size.");
|
||||
void PreparedMappedHDivMassOperator::Mult(
|
||||
const mfem::Vector &gravity_gradient,
|
||||
mfem::Vector &action
|
||||
) const {
|
||||
MFEM_VERIFY(
|
||||
m_is_prepared, "PreparedMappedHDivMassOperator must be prepared "
|
||||
"before Mult is called."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
m_stellar_mass_form != nullptr && m_vacuum_mass_form != nullptr,
|
||||
"PreparedMappedHDivMassOperator has incomplete domain mass forms."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
gravity_gradient.Size() == Width(), "PreparedMappedHDivMassOperator received a gravity-gradient vector "
|
||||
"with the wrong size."
|
||||
);
|
||||
|
||||
m_flux_true.SetSize(m_flux_map.full_size());
|
||||
m_action_true.SetSize(m_flux_map.full_size());
|
||||
m_domain_action_true.SetSize(m_flux_map.full_size());
|
||||
m_flux_map.scatter(gravity_gradient, m_flux_true);
|
||||
m_stellar_mass_form->Mult(m_flux_true, m_action_true);
|
||||
m_vacuum_mass_form->Mult(m_flux_true, m_domain_action_true);
|
||||
m_action_true += m_domain_action_true;
|
||||
action.SetSize(Height());
|
||||
m_flux_map.gather(m_action_true, action);
|
||||
}
|
||||
m_flux_true.SetSize(m_flux_map.full_size());
|
||||
m_action_true.SetSize(m_flux_map.full_size());
|
||||
m_domain_action_true.SetSize(m_flux_map.full_size());
|
||||
m_flux_map.scatter(gravity_gradient, m_flux_true);
|
||||
m_stellar_mass_form->Mult(m_flux_true, m_action_true);
|
||||
m_vacuum_mass_form->Mult(m_flux_true, m_domain_action_true);
|
||||
m_action_true += m_domain_action_true;
|
||||
action.SetSize(Height());
|
||||
m_flux_map.gather(m_action_true, action);
|
||||
}
|
||||
|
||||
void PreparedMappedHDivMassOperator::AssembleDiagonal(
|
||||
mfem::Vector &diagonal) const {
|
||||
mfem::Vector true_diagonal;
|
||||
AssembleTrueDiagonal(true_diagonal);
|
||||
diagonal.SetSize(Height());
|
||||
m_flux_map.gather(true_diagonal, diagonal);
|
||||
}
|
||||
void PreparedMappedHDivMassOperator::AssembleDiagonal(mfem::Vector &diagonal) const {
|
||||
mfem::Vector true_diagonal;
|
||||
AssembleTrueDiagonal(true_diagonal);
|
||||
diagonal.SetSize(Height());
|
||||
m_flux_map.gather(true_diagonal, diagonal);
|
||||
}
|
||||
|
||||
void PreparedMappedHDivMassOperator::AssembleTrueDiagonal(
|
||||
mfem::Vector &diagonal) const {
|
||||
MFEM_VERIFY(m_is_prepared, "PreparedMappedHDivMassOperator must be prepared "
|
||||
"before assembling its diagonal.");
|
||||
MFEM_VERIFY(
|
||||
m_stellar_mass_form != nullptr && m_vacuum_mass_form != nullptr,
|
||||
"PreparedMappedHDivMassOperator has incomplete domain mass forms.");
|
||||
void PreparedMappedHDivMassOperator::AssembleTrueDiagonal(mfem::Vector &diagonal) const {
|
||||
MFEM_VERIFY(
|
||||
m_is_prepared, "PreparedMappedHDivMassOperator must be prepared "
|
||||
"before assembling its diagonal."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
m_stellar_mass_form != nullptr && m_vacuum_mass_form != nullptr,
|
||||
"PreparedMappedHDivMassOperator has incomplete domain mass forms."
|
||||
);
|
||||
|
||||
diagonal.SetSize(m_flux_map.full_size());
|
||||
mfem::Vector domain_diagonal(m_flux_map.full_size());
|
||||
m_stellar_mass_form->AssembleDiagonal(diagonal);
|
||||
m_vacuum_mass_form->AssembleDiagonal(domain_diagonal);
|
||||
diagonal += domain_diagonal;
|
||||
}
|
||||
diagonal.SetSize(m_flux_map.full_size());
|
||||
mfem::Vector domain_diagonal(m_flux_map.full_size());
|
||||
m_stellar_mass_form->AssembleDiagonal(diagonal);
|
||||
m_vacuum_mass_form->AssembleDiagonal(domain_diagonal);
|
||||
diagonal += domain_diagonal;
|
||||
}
|
||||
|
||||
bool PreparedMappedHDivMassOperator::IsPrepared() const noexcept {
|
||||
return m_is_prepared;
|
||||
}
|
||||
bool PreparedMappedHDivMassOperator::IsPrepared() const noexcept {
|
||||
return m_is_prepared;
|
||||
}
|
||||
|
||||
std::uint64_t
|
||||
PreparedMappedHDivMassOperator::GetPreparationCount() const noexcept {
|
||||
return m_preparation_count;
|
||||
}
|
||||
std::uint64_t PreparedMappedHDivMassOperator::GetPreparationCount() const noexcept {
|
||||
return m_preparation_count;
|
||||
}
|
||||
|
||||
const field::FieldDofMap &
|
||||
PreparedMappedHDivMassOperator::GetFluxMap() const noexcept {
|
||||
return m_flux_map;
|
||||
}
|
||||
const field::FieldDofMap &PreparedMappedHDivMassOperator::GetFluxMap() const noexcept {
|
||||
return m_flux_map;
|
||||
}
|
||||
|
||||
const field::FieldDofMap &
|
||||
PreparedMappedHDivMassOperator::GetDisplacementMap() const noexcept {
|
||||
return m_displacement_map;
|
||||
}
|
||||
const field::FieldDofMap &PreparedMappedHDivMassOperator::GetDisplacementMap() const noexcept {
|
||||
return m_displacement_map;
|
||||
}
|
||||
} // namespace mean_field::operators
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -617,13 +617,19 @@ namespace mean_field::operators {
|
||||
data.enthalpyJacobian = 0.0;
|
||||
|
||||
for (int quadraturePoint = 0; quadraturePoint < quadraturePointCount; ++quadraturePoint) {
|
||||
const double enthalpy = quadratureEnthalpy(quadraturePoint);
|
||||
const double enthalpy = quadratureEnthalpy(quadraturePoint);
|
||||
|
||||
const double pressure = m_equationOfState.pressure_from_enthalpy(enthalpy);
|
||||
const eos::SpecificEnthalpyValue specificEnthalpy{enthalpy};
|
||||
const double pressure =
|
||||
eos::evaluate<eos::quantity::Pressure>(m_equationOfState, specificEnthalpy).value();
|
||||
|
||||
const double pressureDerivative = m_equationOfState.pressure_derivative_from_enthalpy(enthalpy);
|
||||
const double pressureDerivative =
|
||||
eos::partialDerivative<eos::quantity::Pressure, eos::quantity::SpecificEnthalpy>(
|
||||
m_equationOfState, specificEnthalpy
|
||||
)
|
||||
.value();
|
||||
|
||||
const double quadratureWeight = data.quadratureWeights(quadraturePoint);
|
||||
const double quadratureWeight = data.quadratureWeights(quadraturePoint);
|
||||
|
||||
MFEM_VERIFY(
|
||||
std::isfinite(pressure) && std::isfinite(pressureDerivative),
|
||||
@@ -1134,4 +1140,4 @@ namespace mean_field::operators {
|
||||
const BarotropicEquilibriumLayout &PreparedPressureForceJacobianOperator::GetLayout() const noexcept {
|
||||
return m_layout;
|
||||
}
|
||||
} // namespace mean_field::operators
|
||||
} // namespace mean_field::operators
|
||||
|
||||
@@ -21,6 +21,12 @@ namespace {
|
||||
);
|
||||
}
|
||||
|
||||
[[nodiscard]] mfem::Vector make_computational_origin(const mfem::ParMesh &mesh) {
|
||||
mfem::Vector origin(mesh.SpaceDimension());
|
||||
origin = 0.0;
|
||||
return origin;
|
||||
}
|
||||
|
||||
[[nodiscard]] mean_field::operators::StellarEquilibriumLayout make_layout(
|
||||
const mean_field::field::FieldDofMap &densityMap,
|
||||
const mean_field::field::FieldDofMap &displacementMap,
|
||||
@@ -253,6 +259,8 @@ namespace mean_field::operators {
|
||||
field::FieldDofMap gravityFluxMap;
|
||||
field::FieldDofMap gravityPotentialMap;
|
||||
field::FieldDofMap enthalpyMap;
|
||||
field::FieldBoundaryDofMap pressureSurfaceRows;
|
||||
field::FieldPointDofMap centerDisplacementRows;
|
||||
|
||||
StellarEquilibriumLayout layout;
|
||||
mfem::Array<int> gravityStateOffsets;
|
||||
@@ -284,6 +292,23 @@ namespace mean_field::operators {
|
||||
field::Enthalpy,
|
||||
DomainSchema>(*f.enthalpyFes)
|
||||
),
|
||||
pressureSurfaceRows(
|
||||
field::make_field_boundary_dof_map<
|
||||
field::Enthalpy,
|
||||
utils::domain::StellarSurface,
|
||||
DomainSchema>(
|
||||
*f.enthalpyFes,
|
||||
enthalpyMap
|
||||
)
|
||||
),
|
||||
centerDisplacementRows(
|
||||
field::make_field_point_dof_map<field::Displacement>(
|
||||
*f.displacementFes,
|
||||
displacementMap,
|
||||
make_computational_origin(*f.mesh),
|
||||
1.0e-12
|
||||
)
|
||||
),
|
||||
layout(make_layout(
|
||||
densityMap,
|
||||
displacementMap,
|
||||
@@ -314,27 +339,15 @@ namespace mean_field::operators {
|
||||
fem::FEM &f,
|
||||
const mapping::DomainMapper &domainMapper,
|
||||
const eos::Polytrope &equationOfState,
|
||||
const models::StellarModel &stellarModel
|
||||
)
|
||||
: PreparedStellarEquilibriumOperator(
|
||||
f,
|
||||
domainMapper,
|
||||
equationOfState,
|
||||
stellarModel.targetMass()
|
||||
) {
|
||||
}
|
||||
|
||||
PreparedStellarEquilibriumOperator::PreparedStellarEquilibriumOperator(
|
||||
fem::FEM &f,
|
||||
const mapping::DomainMapper &domainMapper,
|
||||
const eos::Polytrope &equationOfState,
|
||||
const double targetMass
|
||||
const double targetMass,
|
||||
const PressureSurfaceConstraintView surfaceConstraint
|
||||
)
|
||||
: PreparedStellarEquilibriumOperator(
|
||||
f,
|
||||
domainMapper,
|
||||
equationOfState,
|
||||
targetMass,
|
||||
surfaceConstraint,
|
||||
MakeConstructionData(f)
|
||||
) {
|
||||
}
|
||||
@@ -344,6 +357,7 @@ namespace mean_field::operators {
|
||||
const mapping::DomainMapper &domainMapper,
|
||||
const eos::Polytrope &equationOfState,
|
||||
const double targetMass,
|
||||
const PressureSurfaceConstraintView surfaceConstraint,
|
||||
ConstructionData constructionData
|
||||
)
|
||||
: mfem::Operator(
|
||||
@@ -390,6 +404,11 @@ namespace mean_field::operators {
|
||||
domainMapper,
|
||||
m_gravityContext
|
||||
),
|
||||
m_surfaceConstraintOperator(
|
||||
constructionData.pressureSurfaceRows,
|
||||
surfaceConstraint
|
||||
),
|
||||
m_centeringConstraintOperator(constructionData.centerDisplacementRows),
|
||||
m_targetMass(targetMass) {
|
||||
MFEM_VERIFY(
|
||||
std::isfinite(m_targetMass) && m_targetMass > 0.0,
|
||||
@@ -506,6 +525,14 @@ namespace mean_field::operators {
|
||||
report.massNormalization =
|
||||
m_massNormalizationOperator.Prepare({.targetMass = m_targetMass}, make_mass_dependencies(dependencies));
|
||||
|
||||
report.surfaceConstraint = m_surfaceConstraintOperator.Prepare(
|
||||
reducedEnthalpy, !wasPrepared || dependencies.enthalpy != m_preparedDependencies.enthalpy
|
||||
);
|
||||
|
||||
report.centeringConstraint = m_centeringConstraintOperator.Prepare(
|
||||
displacement, !wasPrepared || dependencies.displacement != m_preparedDependencies.displacement
|
||||
);
|
||||
|
||||
const bool dependenciesChanged = !wasPrepared || dependencies != m_preparedDependencies;
|
||||
if (dependenciesChanged || report.DidAnyChildWork()) {
|
||||
AssembleResidual();
|
||||
@@ -542,7 +569,9 @@ namespace mean_field::operators {
|
||||
m_gravityOperator.Mult(m_gravityState, gravity);
|
||||
m_barotropicClosureOperator.BuildResidual(closure);
|
||||
m_displacementOperator.BuildResidual(displacement);
|
||||
m_centeringConstraintOperator.ApplyResidualRows(displacement);
|
||||
m_hydrostaticOperator.BuildResidual(hydrostatic);
|
||||
m_surfaceConstraintOperator.ApplyResidualRows(hydrostatic);
|
||||
m_massNormalizationOperator.BuildResidual(mass);
|
||||
|
||||
m_cachedResidual.SetSize(Height());
|
||||
@@ -659,11 +688,13 @@ namespace mean_field::operators {
|
||||
reducedDensityDirection, displacementDirection, gravityGradientDirection, reducedEnthalpyDirection,
|
||||
displacementAction
|
||||
);
|
||||
m_centeringConstraintOperator.ApplyJacobianRows(displacementDirection, displacementAction);
|
||||
|
||||
m_hydrostaticOperator.ApplyCompleteJacobianAction(
|
||||
reducedEnthalpyDirection, gravityPotentialDirection, bernoulliDirection(0), displacementDirection,
|
||||
hydrostaticAction
|
||||
);
|
||||
m_surfaceConstraintOperator.ApplyJacobianRows(reducedEnthalpyDirection, hydrostaticAction);
|
||||
|
||||
m_massNormalizationOperator.ApplyCompleteJacobianAction(
|
||||
reducedDensityDirection, displacementDirection, massAction
|
||||
@@ -712,7 +743,8 @@ namespace mean_field::operators {
|
||||
bool PreparedStellarEquilibriumOperator::IsPrepared() const noexcept {
|
||||
return m_isPrepared && m_gravityContext.IsPrepared() && m_barotropicClosureOperator.IsPrepared() &&
|
||||
m_hydrostaticOperator.IsPrepared() && m_displacementOperator.IsPrepared() &&
|
||||
m_massNormalizationOperator.IsPrepared();
|
||||
m_massNormalizationOperator.IsPrepared() && m_surfaceConstraintOperator.IsPrepared() &&
|
||||
m_centeringConstraintOperator.IsPrepared();
|
||||
}
|
||||
|
||||
double PreparedStellarEquilibriumOperator::GetTargetMass() const noexcept {
|
||||
@@ -771,6 +803,16 @@ namespace mean_field::operators {
|
||||
return m_massNormalizationOperator;
|
||||
}
|
||||
|
||||
const PreparedPressureSurfaceConstraint &
|
||||
PreparedStellarEquilibriumOperator::GetSurfaceConstraintOperator() const noexcept {
|
||||
return m_surfaceConstraintOperator;
|
||||
}
|
||||
|
||||
const PreparedCenteringConstraint &
|
||||
PreparedStellarEquilibriumOperator::GetCenteringConstraintOperator() const noexcept {
|
||||
return m_centeringConstraintOperator;
|
||||
}
|
||||
|
||||
void PreparedStellarEquilibriumOperator::VerifyPrepared() const {
|
||||
MFEM_VERIFY(
|
||||
IsPrepared(), "PreparedStellarEquilibriumOperator must be prepared before residual or Jacobian application."
|
||||
|
||||
@@ -13,16 +13,14 @@ namespace mean_field::physics {
|
||||
) {
|
||||
const int dim = fem.mesh->Dimension();
|
||||
mfem::DenseMatrix local_Q(dim, dim);
|
||||
local_Q = 0.0;
|
||||
local_Q = 0.0;
|
||||
using DomainSchema = utils::domain::CoreEnvelopeVacuumDomainSchema;
|
||||
mapping::GridFunctionMappingEvaluator mapping_evaluator(
|
||||
*fem.domainMapperStateless, *fem.displacement,
|
||||
*fem.compactificationCoordinate
|
||||
*fem.domainMapperStateless, *fem.displacement, *fem.compactificationCoordinate
|
||||
);
|
||||
|
||||
for (int i = 0; i < fem.mesh->GetNE(); ++i) {
|
||||
if (!DomainSchema::template attribute_belongs_to<utils::domain::Stellar>(
|
||||
fem.mesh->GetAttribute(i)))
|
||||
if (!DomainSchema::template attribute_belongs_to<utils::domain::Stellar>(fem.mesh->GetAttribute(i)))
|
||||
continue;
|
||||
|
||||
mfem::ElementTransformation *trans = fem.mesh->GetElementTransformation(i);
|
||||
@@ -40,13 +38,12 @@ namespace mean_field::physics {
|
||||
|
||||
mapping::VolumeMappingContext mapping_context;
|
||||
MFEM_VERIFY(
|
||||
mapping_evaluator.EvaluateVolume(*trans, ip, mapping_context) ==
|
||||
mapping::MappingStatus::valid,
|
||||
mapping_evaluator.EvaluateVolume(*trans, ip, mapping_context) == mapping::MappingStatus::valid,
|
||||
"Quadrupole integration encountered an invalid mapping."
|
||||
);
|
||||
const double weight = mapping_context.quadrature.weight;
|
||||
const double weight = mapping_context.quadrature.weight;
|
||||
|
||||
const double rho_val = rho.GetValue(i, ip);
|
||||
const double rho_val = rho.GetValue(i, ip);
|
||||
|
||||
const mfem::Vector &phys_point = mapping_context.mapping.physical_position;
|
||||
|
||||
@@ -145,7 +142,7 @@ namespace mean_field::physics {
|
||||
constexpr auto gravity_poisson_residual_block =
|
||||
utils::blocks::get_residual_block<form>(utils::blocks::gravity_field.poisson_term);
|
||||
|
||||
using DomainSchema = utils::domain::CoreEnvelopeVacuumDomainSchema;
|
||||
using DomainSchema = utils::domain::CoreEnvelopeVacuumDomainSchema;
|
||||
const field::FieldDofGridFunctionAdapter density_adapter =
|
||||
field::make_field_dof_grid_function_adapter<field::Density, DomainSchema>(*f.densityFes);
|
||||
const field::FieldDofGridFunctionAdapter displacement_adapter =
|
||||
@@ -220,12 +217,8 @@ namespace mean_field::physics {
|
||||
|
||||
GravitySolution solution(f);
|
||||
|
||||
gravity_flux_adapter.scatter(
|
||||
gravity_state.GetBlock(gravity_gradient_residual_block), solution.gradPhi
|
||||
);
|
||||
gravity_potential_adapter.scatter(
|
||||
gravity_state.GetBlock(gravity_poisson_residual_block), solution.phi
|
||||
);
|
||||
gravity_flux_adapter.scatter(gravity_state.GetBlock(gravity_gradient_residual_block), solution.gradPhi);
|
||||
gravity_potential_adapter.scatter(gravity_state.GetBlock(gravity_poisson_residual_block), solution.phi);
|
||||
|
||||
return solution;
|
||||
}
|
||||
|
||||
@@ -9,16 +9,14 @@ namespace mean_field::physics {
|
||||
const fem::FEM &fem,
|
||||
const mfem::GridFunction &rho_ref
|
||||
) {
|
||||
double local_I = 0.0;
|
||||
double local_I = 0.0;
|
||||
using DomainSchema = utils::domain::CoreEnvelopeVacuumDomainSchema;
|
||||
mapping::GridFunctionMappingEvaluator mapping_evaluator(
|
||||
*fem.domainMapperStateless, *fem.displacement,
|
||||
*fem.compactificationCoordinate
|
||||
*fem.domainMapperStateless, *fem.displacement, *fem.compactificationCoordinate
|
||||
);
|
||||
|
||||
for (int i = 0; i < fem.mesh->GetNE(); i++) {
|
||||
if (!DomainSchema::template attribute_belongs_to<utils::domain::Stellar>(
|
||||
fem.mesh->GetAttribute(i)))
|
||||
if (!DomainSchema::template attribute_belongs_to<utils::domain::Stellar>(fem.mesh->GetAttribute(i)))
|
||||
continue;
|
||||
|
||||
mfem::ElementTransformation *T = fem.mesh->GetElementTransformation(i);
|
||||
@@ -37,14 +35,13 @@ namespace mean_field::physics {
|
||||
|
||||
mapping::VolumeMappingContext mapping_context;
|
||||
MFEM_VERIFY(
|
||||
mapping_evaluator.EvaluateVolume(*T, ip, mapping_context) ==
|
||||
mapping::MappingStatus::valid,
|
||||
mapping_evaluator.EvaluateVolume(*T, ip, mapping_context) == mapping::MappingStatus::valid,
|
||||
"Moment-of-inertia integration encountered an invalid mapping."
|
||||
);
|
||||
const mfem::Vector &x_phys = mapping_context.mapping.physical_position;
|
||||
|
||||
const double r_cyl_sq = x_phys(0) * x_phys(0) + x_phys(1) * x_phys(1);
|
||||
const double weight = mapping_context.quadrature.weight;
|
||||
const double r_cyl_sq = x_phys(0) * x_phys(0) + x_phys(1) * x_phys(1);
|
||||
const double weight = mapping_context.quadrature.weight;
|
||||
|
||||
local_I += rho_hat * r_cyl_sq * weight;
|
||||
}
|
||||
|
||||
@@ -13,8 +13,7 @@ namespace mean_field::utils {
|
||||
const int dim = fem.mesh->Dimension();
|
||||
x_ref = x_phys_target;
|
||||
mapping::GridFunctionMappingEvaluator mapping_evaluator(
|
||||
*fem.domainMapperStateless, *fem.displacement,
|
||||
*fem.compactificationCoordinate
|
||||
*fem.domainMapperStateless, *fem.displacement, *fem.compactificationCoordinate
|
||||
);
|
||||
|
||||
mfem::Array<int> init_elem;
|
||||
@@ -39,8 +38,7 @@ namespace mean_field::utils {
|
||||
|
||||
mapping::MappingPointContext context;
|
||||
MFEM_VERIFY(
|
||||
mapping_evaluator.EvaluatePoint(*T0, origin_ip[0], context) ==
|
||||
mapping::MappingStatus::valid,
|
||||
mapping_evaluator.EvaluatePoint(*T0, origin_ip[0], context) == mapping::MappingStatus::valid,
|
||||
"Reference-point initialization encountered an invalid mapping."
|
||||
);
|
||||
|
||||
@@ -104,8 +102,7 @@ namespace mean_field::utils {
|
||||
T->SetIntPoint(&ip);
|
||||
|
||||
mapping::MappingPointContext context;
|
||||
if (mapping_evaluator.EvaluatePoint(*T, ip, context) !=
|
||||
mapping::MappingStatus::valid) {
|
||||
if (mapping_evaluator.EvaluatePoint(*T, ip, context) != mapping::MappingStatus::valid) {
|
||||
return false;
|
||||
}
|
||||
const mfem::Vector ¤t_x_phys = context.physical_position;
|
||||
|
||||
@@ -4,21 +4,25 @@ module;
|
||||
module mean_field;
|
||||
|
||||
namespace mean_field::utils {
|
||||
DOMAINS operator|(DOMAINS lhs, DOMAINS rhs) {
|
||||
return static_cast<DOMAINS>(static_cast<uint8_t>(lhs) |
|
||||
static_cast<uint8_t>(rhs));
|
||||
}
|
||||
DOMAINS operator|(
|
||||
DOMAINS lhs,
|
||||
DOMAINS rhs
|
||||
) {
|
||||
return static_cast<DOMAINS>(static_cast<uint8_t>(lhs) | static_cast<uint8_t>(rhs));
|
||||
}
|
||||
|
||||
DOMAINS operator&(DOMAINS lhs, DOMAINS rhs) {
|
||||
return static_cast<DOMAINS>(static_cast<uint8_t>(lhs) &
|
||||
static_cast<uint8_t>(rhs));
|
||||
}
|
||||
DOMAINS operator&(
|
||||
DOMAINS lhs,
|
||||
DOMAINS rhs
|
||||
) {
|
||||
return static_cast<DOMAINS>(static_cast<uint8_t>(lhs) & static_cast<uint8_t>(rhs));
|
||||
}
|
||||
|
||||
int get_mesh_order(const mfem::Mesh &mesh) {
|
||||
if (mesh.GetNodes() != nullptr) {
|
||||
return mesh.GetNodes()->FESpace()->GetMaxElementOrder();
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
int get_mesh_order(const mfem::Mesh &mesh) {
|
||||
if (mesh.GetNodes() != nullptr) {
|
||||
return mesh.GetNodes()->FESpace()->GetMaxElementOrder();
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
} // namespace mean_field::utils
|
||||
|
||||
Reference in New Issue
Block a user