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
|
||||
|
||||
100
libmeanfield/interface/eos/concepts.cppm
Normal file
100
libmeanfield/interface/eos/concepts.cppm
Normal file
@@ -0,0 +1,100 @@
|
||||
module;
|
||||
|
||||
#include <concepts>
|
||||
#include <type_traits>
|
||||
|
||||
export module mean_field:eos.concepts;
|
||||
export import :eos.relations;
|
||||
|
||||
export namespace mean_field::eos {
|
||||
namespace detail {
|
||||
template <typename EquationOfState, typename RelationType> struct ImplementsRelation : std::false_type { };
|
||||
|
||||
template <
|
||||
typename EquationOfState,
|
||||
typename Output,
|
||||
typename... Inputs>
|
||||
struct ImplementsRelation<
|
||||
EquationOfState,
|
||||
Relation<
|
||||
Output,
|
||||
Inputs...>> : std::bool_constant <
|
||||
requires(
|
||||
const std::remove_cvref_t<EquationOfState> &equationOfState,
|
||||
QuantityValue<Inputs>... inputValues
|
||||
) {
|
||||
{equationOfState.evaluate(Relation<Output, Inputs...>{}, inputValues...)}
|
||||
->std::same_as<QuantityValue<Output>>;
|
||||
}>{};
|
||||
|
||||
template <typename EquationOfState, typename Catalog> struct ImplementsRelationCatalog : std::false_type { };
|
||||
|
||||
template <typename EquationOfState, typename... Relations>
|
||||
struct ImplementsRelationCatalog<EquationOfState, RelationCatalog<Relations...>>
|
||||
: std::bool_constant<(ImplementsRelation<EquationOfState, Relations>::value && ...)> { };
|
||||
|
||||
template <typename Candidate, typename = void> struct IsEquationOfStateModel : std::false_type { };
|
||||
|
||||
template <typename Candidate>
|
||||
struct IsEquationOfStateModel<Candidate, std::void_t<typename std::remove_cvref_t<Candidate>::Relations>>
|
||||
: std::bool_constant<
|
||||
ValidRelationCatalog<typename std::remove_cvref_t<Candidate>::Relations> &&
|
||||
ImplementsRelationCatalog<
|
||||
std::remove_cvref_t<Candidate>,
|
||||
typename std::remove_cvref_t<Candidate>::Relations>::value> { };
|
||||
|
||||
template <typename EquationOfState, typename RelationType, typename InputQuantity>
|
||||
struct ImplementsPartialDerivative : std::false_type { };
|
||||
|
||||
template <
|
||||
typename EquationOfState,
|
||||
typename Output,
|
||||
typename... Inputs,
|
||||
typename InputQuantity>
|
||||
struct ImplementsPartialDerivative<
|
||||
EquationOfState,
|
||||
Relation<
|
||||
Output,
|
||||
Inputs...>,
|
||||
InputQuantity> : std::bool_constant <
|
||||
(std::same_as<
|
||||
InputQuantity,
|
||||
Inputs> ||
|
||||
...) &&
|
||||
requires(
|
||||
const std::remove_cvref_t<EquationOfState> &equationOfState,
|
||||
QuantityValue<Inputs>... inputValues
|
||||
) {
|
||||
{equationOfState
|
||||
.partialDerivative(Relation<Output, Inputs...>{}, WithRespectTo<InputQuantity>{}, inputValues...)}
|
||||
->std::same_as<PartialDerivative<Output, InputQuantity>>;
|
||||
}>{};
|
||||
} // namespace detail
|
||||
|
||||
template <typename Candidate>
|
||||
concept EquationOfStateModel = detail::IsEquationOfStateModel<Candidate>::value;
|
||||
|
||||
template <typename EquationOfState, typename RelationType>
|
||||
concept SupportsRelation =
|
||||
EquationOfStateModel<EquationOfState> && ThermodynamicRelationType<RelationType> &&
|
||||
relationCatalogContains<typename std::remove_cvref_t<EquationOfState>::Relations, RelationType>;
|
||||
|
||||
template <typename EquationOfState, typename RelationType, typename InputQuantity>
|
||||
concept SupportsPartialDerivative =
|
||||
SupportsRelation<EquationOfState, RelationType> && ThermodynamicQuantityType<InputQuantity> &&
|
||||
detail::ImplementsPartialDerivative<EquationOfState, RelationType, InputQuantity>::value;
|
||||
|
||||
template <typename Candidate>
|
||||
concept StructureSeedEquationOfState =
|
||||
EquationOfStateModel<Candidate> && SupportsRelation<Candidate, SpecificEnthalpyFromDensity>;
|
||||
|
||||
template <typename Candidate>
|
||||
concept BarotropicClosureEquationOfState =
|
||||
EquationOfStateModel<Candidate> && SupportsRelation<Candidate, DensityFromSpecificEnthalpy> &&
|
||||
SupportsPartialDerivative<Candidate, DensityFromSpecificEnthalpy, quantity::SpecificEnthalpy>;
|
||||
|
||||
template <typename Candidate>
|
||||
concept PressureForceEquationOfState =
|
||||
EquationOfStateModel<Candidate> && SupportsRelation<Candidate, PressureFromSpecificEnthalpy> &&
|
||||
SupportsPartialDerivative<Candidate, PressureFromSpecificEnthalpy, quantity::SpecificEnthalpy>;
|
||||
} // namespace mean_field::eos
|
||||
@@ -1,16 +0,0 @@
|
||||
export module mean_field:eos.base;
|
||||
|
||||
export namespace mean_field::eos {
|
||||
class EquationOfState {
|
||||
public:
|
||||
virtual ~EquationOfState() = default;
|
||||
[[nodiscard]] virtual double pressure_from_density(double density) const = 0;
|
||||
[[nodiscard]] virtual double pressure_from_enthalpy(double enthalpy) const = 0;
|
||||
[[nodiscard]] virtual double enthalpy_from_density(double density) const = 0;
|
||||
[[nodiscard]] virtual double enthalpy_from_pressure(double pressure) const = 0;
|
||||
[[nodiscard]] virtual double density_from_enthalpy(double enthalpy) const = 0;
|
||||
[[nodiscard]] virtual double density_derivative_from_enthalpy(double enthalpy) const = 0;
|
||||
[[nodiscard]] virtual double pressure_derivative_from_enthalpy(double enthalpy) const = 0;
|
||||
[[nodiscard]] virtual double pressure_derivative_from_density(double density) const = 0;
|
||||
};
|
||||
} // namespace mean_field::eos
|
||||
90
libmeanfield/interface/eos/evaluation.cppm
Normal file
90
libmeanfield/interface/eos/evaluation.cppm
Normal file
@@ -0,0 +1,90 @@
|
||||
module;
|
||||
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
export module mean_field:eos.evaluation;
|
||||
export import :eos.concepts;
|
||||
|
||||
export namespace mean_field::eos {
|
||||
enum class EvaluationErrorCode {
|
||||
unsupported_relation,
|
||||
unsupported_derivative,
|
||||
wrong_input_count,
|
||||
wrong_input_quantity,
|
||||
nonfinite_input,
|
||||
outside_domain,
|
||||
nonfinite_result
|
||||
};
|
||||
|
||||
class EvaluationError final : public std::domain_error {
|
||||
public:
|
||||
explicit EvaluationError(
|
||||
const EvaluationErrorCode code,
|
||||
std::string message
|
||||
)
|
||||
: std::domain_error(std::move(message)),
|
||||
m_code(code) {
|
||||
}
|
||||
|
||||
[[nodiscard]] EvaluationErrorCode code() const noexcept {
|
||||
return m_code;
|
||||
}
|
||||
|
||||
private:
|
||||
EvaluationErrorCode m_code;
|
||||
};
|
||||
|
||||
template <
|
||||
ThermodynamicQuantityType OutputQuantity,
|
||||
EquationOfStateModel EquationOfState,
|
||||
QuantityValueType... InputValues>
|
||||
requires SupportsRelation<
|
||||
EquationOfState,
|
||||
Relation<
|
||||
OutputQuantity,
|
||||
QuantityOfT<InputValues>...>>
|
||||
[[nodiscard]] constexpr QuantityValue<OutputQuantity> evaluate(
|
||||
const EquationOfState &equationOfState,
|
||||
const InputValues... inputValues
|
||||
) noexcept(noexcept(equationOfState
|
||||
.evaluate(
|
||||
Relation<
|
||||
OutputQuantity,
|
||||
QuantityOfT<InputValues>...>{},
|
||||
inputValues...
|
||||
))) {
|
||||
return equationOfState.evaluate(Relation<OutputQuantity, QuantityOfT<InputValues>...>{}, inputValues...);
|
||||
}
|
||||
|
||||
template <
|
||||
ThermodynamicQuantityType OutputQuantity,
|
||||
ThermodynamicQuantityType InputQuantity,
|
||||
EquationOfStateModel EquationOfState,
|
||||
QuantityValueType... InputValues>
|
||||
requires SupportsPartialDerivative<
|
||||
EquationOfState,
|
||||
Relation<
|
||||
OutputQuantity,
|
||||
QuantityOfT<InputValues>...>,
|
||||
InputQuantity>
|
||||
[[nodiscard]] constexpr PartialDerivative<
|
||||
OutputQuantity,
|
||||
InputQuantity>
|
||||
partialDerivative(
|
||||
const EquationOfState &equationOfState,
|
||||
const InputValues... inputValues
|
||||
) noexcept(noexcept(equationOfState
|
||||
.partialDerivative(
|
||||
Relation<
|
||||
OutputQuantity,
|
||||
QuantityOfT<InputValues>...>{},
|
||||
WithRespectTo<InputQuantity>{},
|
||||
inputValues...
|
||||
))) {
|
||||
return equationOfState.partialDerivative(
|
||||
Relation<OutputQuantity, QuantityOfT<InputValues>...>{}, WithRespectTo<InputQuantity>{}, inputValues...
|
||||
);
|
||||
}
|
||||
} // namespace mean_field::eos
|
||||
@@ -3,11 +3,18 @@ module;
|
||||
#include <format>
|
||||
#include <stdexcept>
|
||||
export module mean_field:eos.polytrope;
|
||||
export import :eos.base;
|
||||
export import :eos.evaluation;
|
||||
|
||||
export namespace mean_field::eos {
|
||||
class Polytrope final : public EquationOfState {
|
||||
class Polytrope final {
|
||||
public:
|
||||
using Relations = RelationCatalog<
|
||||
PressureFromDensity,
|
||||
PressureFromSpecificEnthalpy,
|
||||
SpecificEnthalpyFromDensity,
|
||||
SpecificEnthalpyFromPressure,
|
||||
DensityFromSpecificEnthalpy>;
|
||||
|
||||
Polytrope(
|
||||
const double polytropic_index,
|
||||
const double polytropic_constant
|
||||
@@ -49,82 +56,128 @@ export namespace mean_field::eos {
|
||||
return m_enthalpy_scale;
|
||||
}
|
||||
|
||||
[[nodiscard]] double pressure_from_density(const double density) const override {
|
||||
validate_nonnegativity(density, "density");
|
||||
if (density == 0.0) {
|
||||
return 0.0;
|
||||
[[nodiscard]] PressureValue evaluate(
|
||||
PressureFromDensity,
|
||||
const DensityValue density
|
||||
) const {
|
||||
validate_nonnegativity(density.value(), "density");
|
||||
if (density.value() == 0.0) {
|
||||
return PressureValue{0.0};
|
||||
}
|
||||
|
||||
return m_polytropic_constant * std::pow(density, 1.0 + 1.0 / m_polytropic_index);
|
||||
return PressureValue{m_polytropic_constant * std::pow(density.value(), 1.0 + 1.0 / m_polytropic_index)};
|
||||
}
|
||||
|
||||
[[nodiscard]] double enthalpy_from_density(const double density) const override {
|
||||
validate_nonnegativity(density, "density");
|
||||
if (density == 0.0) {
|
||||
return 0.0;
|
||||
[[nodiscard]] SpecificEnthalpyValue evaluate(
|
||||
SpecificEnthalpyFromDensity,
|
||||
const DensityValue density
|
||||
) const {
|
||||
validate_nonnegativity(density.value(), "density");
|
||||
if (density.value() == 0.0) {
|
||||
return SpecificEnthalpyValue{0.0};
|
||||
}
|
||||
|
||||
return m_enthalpy_scale * std::pow(density, 1.0 / m_polytropic_index);
|
||||
return SpecificEnthalpyValue{m_enthalpy_scale * std::pow(density.value(), 1.0 / m_polytropic_index)};
|
||||
}
|
||||
|
||||
[[nodiscard]] double density_from_enthalpy(const double enthalpy) const override {
|
||||
validate_finite(enthalpy, "enthalpy");
|
||||
[[nodiscard]] DensityValue evaluate(
|
||||
DensityFromSpecificEnthalpy,
|
||||
const SpecificEnthalpyValue specificEnthalpy
|
||||
) const {
|
||||
validate_finite(specificEnthalpy.value(), "specific enthalpy");
|
||||
|
||||
if (enthalpy <= 0.0) {
|
||||
return 0.0;
|
||||
if (specificEnthalpy.value() <= 0.0) {
|
||||
return DensityValue{0.0};
|
||||
}
|
||||
|
||||
return std::pow(enthalpy / m_enthalpy_scale, m_polytropic_index);
|
||||
return DensityValue{std::pow(specificEnthalpy.value() / m_enthalpy_scale, m_polytropic_index)};
|
||||
}
|
||||
|
||||
[[nodiscard]] double pressure_from_enthalpy(const double enthalpy) const override {
|
||||
validate_finite(enthalpy, "enthalpy");
|
||||
[[nodiscard]] PressureValue evaluate(
|
||||
PressureFromSpecificEnthalpy,
|
||||
const SpecificEnthalpyValue specificEnthalpy
|
||||
) const {
|
||||
const DensityValue density = evaluate(DensityFromSpecificEnthalpy{}, specificEnthalpy);
|
||||
|
||||
if (enthalpy <= 0.0) {
|
||||
return 0.0;
|
||||
if (specificEnthalpy.value() <= 0.0) {
|
||||
return PressureValue{0.0};
|
||||
}
|
||||
|
||||
return density_from_enthalpy(enthalpy) * enthalpy / (m_polytropic_index + 1.0);
|
||||
return PressureValue{density.value() * specificEnthalpy.value() / (m_polytropic_index + 1.0)};
|
||||
}
|
||||
|
||||
[[nodiscard]] double density_derivative_from_enthalpy(const double enthalpy) const override {
|
||||
validate_finite(enthalpy, "enthalpy");
|
||||
if (enthalpy < 0.0) {
|
||||
return 0.0;
|
||||
[[nodiscard]] SpecificEnthalpyValue evaluate(
|
||||
SpecificEnthalpyFromPressure,
|
||||
const PressureValue pressure
|
||||
) const {
|
||||
validate_nonnegativity(pressure.value(), "pressure");
|
||||
if (pressure.value() == 0.0) {
|
||||
return SpecificEnthalpyValue{0.0};
|
||||
}
|
||||
|
||||
if (enthalpy == 0.0) {
|
||||
return m_polytropic_index == 1.0 ? 1.0 / m_enthalpy_scale : 0.0;
|
||||
}
|
||||
const double indexPlusOne = m_polytropic_index + 1.0;
|
||||
|
||||
return m_polytropic_index / m_enthalpy_scale *
|
||||
std::pow(enthalpy / m_enthalpy_scale, m_polytropic_index - 1.0);
|
||||
return SpecificEnthalpyValue{
|
||||
indexPlusOne * std::pow(m_polytropic_constant, m_polytropic_index / indexPlusOne) *
|
||||
std::pow(pressure.value(), 1.0 / indexPlusOne)
|
||||
};
|
||||
}
|
||||
|
||||
[[nodiscard]] double pressure_derivative_from_enthalpy(const double enthalpy) const override {
|
||||
validate_finite(enthalpy, "enthalpy");
|
||||
|
||||
if (enthalpy <= 0.0) {
|
||||
return 0.0;
|
||||
[[nodiscard]] PartialDerivative<
|
||||
quantity::Density,
|
||||
quantity::SpecificEnthalpy>
|
||||
partialDerivative(
|
||||
DensityFromSpecificEnthalpy,
|
||||
WithRespectTo<quantity::SpecificEnthalpy>,
|
||||
const SpecificEnthalpyValue specificEnthalpy
|
||||
) const {
|
||||
validate_finite(specificEnthalpy.value(), "specific enthalpy");
|
||||
if (specificEnthalpy.value() < 0.0) {
|
||||
return PartialDerivative<quantity::Density, quantity::SpecificEnthalpy>{0.0};
|
||||
}
|
||||
|
||||
return density_from_enthalpy(enthalpy);
|
||||
}
|
||||
|
||||
[[nodiscard]] double pressure_derivative_from_density(const double density) const override {
|
||||
validate_nonnegativity(density, "density");
|
||||
if (density == 0.0) {
|
||||
return 0.0;
|
||||
if (specificEnthalpy.value() == 0.0) {
|
||||
return PartialDerivative<quantity::Density, quantity::SpecificEnthalpy>{
|
||||
m_polytropic_index == 1.0 ? 1.0 / m_enthalpy_scale : 0.0
|
||||
};
|
||||
}
|
||||
|
||||
return m_polytropic_constant * (1.0 + 1.0 / m_polytropic_index) *
|
||||
std::pow(density, 1.0 / m_polytropic_index);
|
||||
return PartialDerivative<quantity::Density, quantity::SpecificEnthalpy>{
|
||||
m_polytropic_index / m_enthalpy_scale *
|
||||
std::pow(specificEnthalpy.value() / m_enthalpy_scale, m_polytropic_index - 1.0)
|
||||
};
|
||||
}
|
||||
|
||||
[[nodiscard]] double enthalpy_from_pressure(const double pressure) const override {
|
||||
validate_nonnegativity(pressure, "pressure");
|
||||
const double np1 = m_polytropic_index + 1;
|
||||
return np1 * std::pow(m_polytropic_constant, m_polytropic_index / np1) * std::pow(pressure, 1.0 / np1);
|
||||
[[nodiscard]] PartialDerivative<
|
||||
quantity::Pressure,
|
||||
quantity::SpecificEnthalpy>
|
||||
partialDerivative(
|
||||
PressureFromSpecificEnthalpy,
|
||||
WithRespectTo<quantity::SpecificEnthalpy>,
|
||||
const SpecificEnthalpyValue specificEnthalpy
|
||||
) const {
|
||||
const DensityValue density = evaluate(DensityFromSpecificEnthalpy{}, specificEnthalpy);
|
||||
|
||||
return PartialDerivative<quantity::Pressure, quantity::SpecificEnthalpy>{density.value()};
|
||||
}
|
||||
|
||||
[[nodiscard]] PartialDerivative<
|
||||
quantity::Pressure,
|
||||
quantity::Density>
|
||||
partialDerivative(
|
||||
PressureFromDensity,
|
||||
WithRespectTo<quantity::Density>,
|
||||
const DensityValue density
|
||||
) const {
|
||||
validate_nonnegativity(density.value(), "density");
|
||||
if (density.value() == 0.0) {
|
||||
return PartialDerivative<quantity::Pressure, quantity::Density>{0.0};
|
||||
}
|
||||
|
||||
return PartialDerivative<quantity::Pressure, quantity::Density>{
|
||||
m_polytropic_constant * (1.0 + 1.0 / m_polytropic_index) *
|
||||
std::pow(density.value(), 1.0 / m_polytropic_index)
|
||||
};
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -133,12 +186,12 @@ export namespace mean_field::eos {
|
||||
const char *quantity
|
||||
) {
|
||||
if (!std::isfinite(value)) {
|
||||
throw std::domain_error(
|
||||
std::format(
|
||||
"The {} must be finite. Instead a value of {} has been "
|
||||
"provided",
|
||||
quantity, value
|
||||
)
|
||||
throw EvaluationError(
|
||||
EvaluationErrorCode::nonfinite_input, std::format(
|
||||
"The {} must be finite. Instead a value of {} has been "
|
||||
"provided",
|
||||
quantity, value
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -149,13 +202,13 @@ export namespace mean_field::eos {
|
||||
) {
|
||||
validate_finite(value, quantity);
|
||||
if (value < 0.0) {
|
||||
throw std::domain_error(
|
||||
std::format(
|
||||
"The {} must be non-negative. Instead a value of {} "
|
||||
"has been "
|
||||
"provided",
|
||||
quantity, value
|
||||
)
|
||||
throw EvaluationError(
|
||||
EvaluationErrorCode::outside_domain, std::format(
|
||||
"The {} must be non-negative. Instead a value of {} "
|
||||
"has been "
|
||||
"provided",
|
||||
quantity, value
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
128
libmeanfield/interface/eos/pressure_surface.cppm
Normal file
128
libmeanfield/interface/eos/pressure_surface.cppm
Normal file
@@ -0,0 +1,128 @@
|
||||
module;
|
||||
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
|
||||
export module mean_field:eos.pressure_surface;
|
||||
|
||||
export import :eos.evaluation;
|
||||
|
||||
export namespace mean_field::eos {
|
||||
namespace detail {
|
||||
template <
|
||||
ThermodynamicQuantityType InputQuantity,
|
||||
typename SurfaceState>
|
||||
[[nodiscard]] constexpr auto pressureSurfaceRelationInput(
|
||||
const PressureValue targetPressure,
|
||||
const SurfaceState &state
|
||||
) {
|
||||
if constexpr (std::same_as<InputQuantity, quantity::Pressure>) {
|
||||
return targetPressure;
|
||||
} else {
|
||||
return state.value(InputQuantity{});
|
||||
}
|
||||
}
|
||||
|
||||
template <typename RelationType> struct PressureSurfaceRelationOperations;
|
||||
|
||||
template <typename CarrierQuantity, typename... InputQuantities>
|
||||
struct PressureSurfaceRelationOperations<Relation<CarrierQuantity, InputQuantities...>> {
|
||||
template <
|
||||
typename EquationOfState,
|
||||
typename SurfaceState>
|
||||
[[nodiscard]] static QuantityValue<CarrierQuantity> requiredCarrierValue(
|
||||
const EquationOfState &equationOfState,
|
||||
const PressureValue targetPressure,
|
||||
const SurfaceState &state
|
||||
) {
|
||||
return evaluate<CarrierQuantity>(
|
||||
equationOfState, pressureSurfaceRelationInput<InputQuantities>(targetPressure, state)...
|
||||
);
|
||||
}
|
||||
|
||||
template <
|
||||
typename InputQuantity,
|
||||
typename EquationOfState,
|
||||
typename SurfaceState,
|
||||
typename SurfaceVariation>
|
||||
[[nodiscard]] static double inputJacobianContribution(
|
||||
const EquationOfState &equationOfState,
|
||||
const PressureValue targetPressure,
|
||||
const SurfaceState &state,
|
||||
const SurfaceVariation &variation
|
||||
) {
|
||||
if constexpr (std::same_as<InputQuantity, quantity::Pressure>) {
|
||||
return 0.0;
|
||||
} else {
|
||||
const auto derivative = partialDerivative<CarrierQuantity, InputQuantity>(
|
||||
equationOfState, pressureSurfaceRelationInput<InputQuantities>(targetPressure, state)...
|
||||
);
|
||||
return derivative.value() * variation.value(InputQuantity{}).value();
|
||||
}
|
||||
}
|
||||
|
||||
template <
|
||||
typename EquationOfState,
|
||||
typename SurfaceState,
|
||||
typename SurfaceVariation>
|
||||
[[nodiscard]] static double carrierCorrectionJacobianAction(
|
||||
const EquationOfState &equationOfState,
|
||||
const PressureValue targetPressure,
|
||||
const SurfaceState &state,
|
||||
const SurfaceVariation &variation
|
||||
) {
|
||||
return (
|
||||
0.0 + ... +
|
||||
inputJacobianContribution<InputQuantities>(equationOfState, targetPressure, state, variation)
|
||||
);
|
||||
}
|
||||
};
|
||||
} // namespace detail
|
||||
|
||||
/*
|
||||
* EOS-owned resolution of a constant-pressure condition into the carrier
|
||||
* quantity used by an equation formulation. No field or solver concepts
|
||||
* enter this type.
|
||||
*/
|
||||
template <EquationOfStateModel EquationOfState, ThermodynamicRelationType SelectedRelation>
|
||||
class ResolvedPressureSurfaceRelation final {
|
||||
public:
|
||||
using RelationType = SelectedRelation;
|
||||
using CarrierQuantity = RelationOutputT<RelationType>;
|
||||
|
||||
ResolvedPressureSurfaceRelation(
|
||||
const EquationOfState &equationOfState,
|
||||
const PressureValue targetPressure
|
||||
) noexcept
|
||||
: m_equationOfState(std::addressof(equationOfState)),
|
||||
m_targetPressure(targetPressure) {
|
||||
}
|
||||
|
||||
[[nodiscard]] PressureValue targetPressure() const noexcept {
|
||||
return m_targetPressure;
|
||||
}
|
||||
|
||||
template <typename SurfaceState>
|
||||
[[nodiscard]] QuantityValue<CarrierQuantity> requiredCarrierValue(const SurfaceState &state) const {
|
||||
return detail::PressureSurfaceRelationOperations<RelationType>::requiredCarrierValue(
|
||||
*m_equationOfState, m_targetPressure, state
|
||||
);
|
||||
}
|
||||
|
||||
template <
|
||||
typename SurfaceState,
|
||||
typename SurfaceVariation>
|
||||
[[nodiscard]] double carrierCorrectionJacobianAction(
|
||||
const SurfaceState &state,
|
||||
const SurfaceVariation &variation
|
||||
) const {
|
||||
return detail::PressureSurfaceRelationOperations<RelationType>::carrierCorrectionJacobianAction(
|
||||
*m_equationOfState, m_targetPressure, state, variation
|
||||
);
|
||||
}
|
||||
|
||||
private:
|
||||
const EquationOfState *m_equationOfState;
|
||||
PressureValue m_targetPressure;
|
||||
};
|
||||
} // namespace mean_field::eos
|
||||
235
libmeanfield/interface/eos/quantities.cppm
Normal file
235
libmeanfield/interface/eos/quantities.cppm
Normal file
@@ -0,0 +1,235 @@
|
||||
module;
|
||||
|
||||
#include <compare>
|
||||
#include <concepts>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
|
||||
export module mean_field:eos.quantities;
|
||||
|
||||
export namespace mean_field::eos {
|
||||
struct ThermodynamicQuantity { };
|
||||
|
||||
template <typename Candidate>
|
||||
concept ThermodynamicQuantityType =
|
||||
std::same_as<Candidate, std::remove_cv_t<Candidate>> && std::derived_from<Candidate, ThermodynamicQuantity>;
|
||||
|
||||
namespace quantity {
|
||||
struct Density final : ThermodynamicQuantity {
|
||||
static constexpr std::string_view identifier = "density";
|
||||
};
|
||||
|
||||
struct Pressure final : ThermodynamicQuantity {
|
||||
static constexpr std::string_view identifier = "pressure";
|
||||
};
|
||||
|
||||
struct SpecificEnthalpy final : ThermodynamicQuantity {
|
||||
static constexpr std::string_view identifier = "specific_enthalpy";
|
||||
};
|
||||
} // namespace quantity
|
||||
|
||||
template <typename T>
|
||||
concept Numeric = std::integral<T> || std::floating_point<T>;
|
||||
|
||||
template <ThermodynamicQuantityType Quantity> class QuantityValue final {
|
||||
public:
|
||||
explicit constexpr QuantityValue(const double value) noexcept : m_value(value) {
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr double value() const noexcept {
|
||||
return m_value;
|
||||
}
|
||||
|
||||
[[nodiscard]] friend constexpr bool operator==(
|
||||
const QuantityValue &,
|
||||
const QuantityValue &
|
||||
) noexcept = default;
|
||||
|
||||
friend constexpr QuantityValue<Quantity> operator+(
|
||||
const QuantityValue<Quantity> &lhs,
|
||||
const QuantityValue<Quantity> &rhs
|
||||
) noexcept {
|
||||
return QuantityValue<Quantity>{lhs.m_value + rhs.m_value};
|
||||
}
|
||||
|
||||
friend constexpr QuantityValue<Quantity> operator-(
|
||||
const QuantityValue<Quantity> &lhs,
|
||||
const QuantityValue<Quantity> &rhs
|
||||
) noexcept {
|
||||
return QuantityValue<Quantity>{lhs.m_value - rhs.m_value};
|
||||
}
|
||||
|
||||
template <Numeric rhsT>
|
||||
friend constexpr QuantityValue<Quantity> operator*(
|
||||
const QuantityValue<Quantity> &lhs,
|
||||
rhsT rhs
|
||||
) noexcept {
|
||||
return QuantityValue<Quantity>{lhs.m_value * static_cast<double>(rhs)};
|
||||
}
|
||||
|
||||
template <Numeric lhsT>
|
||||
friend constexpr QuantityValue<Quantity> operator*(
|
||||
lhsT lhs,
|
||||
const QuantityValue<Quantity> &rhs
|
||||
) noexcept {
|
||||
return QuantityValue<Quantity>{static_cast<double>(lhs) * rhs.m_value};
|
||||
}
|
||||
|
||||
template <Numeric rhsT>
|
||||
friend constexpr QuantityValue<Quantity> operator/(
|
||||
const QuantityValue<Quantity> &lhs,
|
||||
rhsT rhs
|
||||
) noexcept {
|
||||
return QuantityValue<Quantity>{lhs.m_value / static_cast<double>(rhs)};
|
||||
}
|
||||
|
||||
template <Numeric compT>
|
||||
friend constexpr std::partial_ordering operator<=>(
|
||||
const QuantityValue<Quantity> &lhs,
|
||||
compT rhs
|
||||
) noexcept {
|
||||
return lhs.m_value <=> static_cast<double>(rhs);
|
||||
}
|
||||
|
||||
template <Numeric compT>
|
||||
friend constexpr std::partial_ordering operator<=>(
|
||||
compT lhs,
|
||||
const QuantityValue<Quantity> &rhs
|
||||
) noexcept {
|
||||
return static_cast<double>(lhs) <=> rhs.m_value;
|
||||
}
|
||||
|
||||
friend constexpr std::partial_ordering operator<=>(
|
||||
const QuantityValue<Quantity> &lhs,
|
||||
const QuantityValue<Quantity> &rhs
|
||||
) noexcept {
|
||||
return lhs.m_value <=> rhs.m_value;
|
||||
}
|
||||
|
||||
private:
|
||||
double m_value;
|
||||
};
|
||||
|
||||
using DensityValue = QuantityValue<quantity::Density>;
|
||||
using PressureValue = QuantityValue<quantity::Pressure>;
|
||||
using SpecificEnthalpyValue = QuantityValue<quantity::SpecificEnthalpy>;
|
||||
|
||||
template <typename Candidate> struct IsQuantityValue : std::false_type { };
|
||||
|
||||
template <ThermodynamicQuantityType Quantity> struct IsQuantityValue<QuantityValue<Quantity>> : std::true_type { };
|
||||
|
||||
template <typename Candidate>
|
||||
concept QuantityValueType = IsQuantityValue<std::remove_cvref_t<Candidate>>::value;
|
||||
|
||||
template <typename Candidate> struct QuantityOf;
|
||||
|
||||
template <ThermodynamicQuantityType Quantity> struct QuantityOf<QuantityValue<Quantity>> {
|
||||
using Type = Quantity;
|
||||
};
|
||||
|
||||
template <QuantityValueType Value> using QuantityOfT = typename QuantityOf<std::remove_cvref_t<Value>>::Type;
|
||||
|
||||
template <ThermodynamicQuantityType OutputQuantity, ThermodynamicQuantityType InputQuantity>
|
||||
class PartialDerivative final {
|
||||
public:
|
||||
explicit constexpr PartialDerivative(const double value) noexcept : m_value(value) {
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr double value() const noexcept {
|
||||
return m_value;
|
||||
}
|
||||
|
||||
friend constexpr PartialDerivative<
|
||||
OutputQuantity,
|
||||
InputQuantity>
|
||||
operator+(
|
||||
const PartialDerivative<
|
||||
OutputQuantity,
|
||||
InputQuantity> &lhs,
|
||||
const PartialDerivative<
|
||||
OutputQuantity,
|
||||
InputQuantity> &rhs
|
||||
) noexcept;
|
||||
|
||||
friend constexpr PartialDerivative<
|
||||
OutputQuantity,
|
||||
InputQuantity>
|
||||
operator-(
|
||||
const PartialDerivative<
|
||||
OutputQuantity,
|
||||
InputQuantity> &lhs,
|
||||
const PartialDerivative<
|
||||
OutputQuantity,
|
||||
InputQuantity> &rhs
|
||||
) noexcept;
|
||||
|
||||
template <Numeric rhsT>
|
||||
friend constexpr PartialDerivative<
|
||||
OutputQuantity,
|
||||
InputQuantity>
|
||||
operator*(
|
||||
const PartialDerivative<
|
||||
OutputQuantity,
|
||||
InputQuantity> &,
|
||||
rhsT
|
||||
) noexcept;
|
||||
|
||||
template <Numeric lhsT>
|
||||
friend constexpr PartialDerivative<
|
||||
OutputQuantity,
|
||||
InputQuantity>
|
||||
operator*(
|
||||
lhsT,
|
||||
const PartialDerivative<
|
||||
OutputQuantity,
|
||||
InputQuantity> &
|
||||
) noexcept;
|
||||
|
||||
template <Numeric rhsT>
|
||||
friend constexpr PartialDerivative<
|
||||
OutputQuantity,
|
||||
InputQuantity>
|
||||
operator/(
|
||||
const PartialDerivative<
|
||||
OutputQuantity,
|
||||
InputQuantity> &,
|
||||
rhsT
|
||||
) noexcept;
|
||||
|
||||
template <Numeric cmpT>
|
||||
friend constexpr std::partial_ordering operator<=>(
|
||||
const PartialDerivative<
|
||||
OutputQuantity,
|
||||
InputQuantity> &lhs,
|
||||
cmpT rhs
|
||||
) noexcept {
|
||||
return lhs.m_value <=> static_cast<double>(rhs);
|
||||
}
|
||||
|
||||
template <Numeric cmpT>
|
||||
friend constexpr std::partial_ordering operator<=>(
|
||||
cmpT lhs,
|
||||
const PartialDerivative<
|
||||
OutputQuantity,
|
||||
InputQuantity> &rhs
|
||||
) noexcept {
|
||||
return static_cast<double>(lhs) <=> rhs.m_value;
|
||||
}
|
||||
|
||||
friend constexpr std::partial_ordering operator<=>(
|
||||
const PartialDerivative<
|
||||
OutputQuantity,
|
||||
InputQuantity> &lhs,
|
||||
const PartialDerivative<
|
||||
OutputQuantity,
|
||||
InputQuantity> &rhs
|
||||
) noexcept {
|
||||
return lhs.m_value <=> rhs.m_value;
|
||||
}
|
||||
|
||||
private:
|
||||
double m_value;
|
||||
};
|
||||
|
||||
template <ThermodynamicQuantityType Quantity> struct WithRespectTo final { };
|
||||
} // namespace mean_field::eos
|
||||
93
libmeanfield/interface/eos/relations.cppm
Normal file
93
libmeanfield/interface/eos/relations.cppm
Normal file
@@ -0,0 +1,93 @@
|
||||
module;
|
||||
|
||||
#include <concepts>
|
||||
#include <cstddef>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
|
||||
export module mean_field:eos.relations;
|
||||
export import :eos.quantities;
|
||||
|
||||
export namespace mean_field::eos {
|
||||
template <typename... Quantities> struct QuantityList final { };
|
||||
|
||||
template <typename Output, typename... Inputs> struct Relation final {
|
||||
using OutputQuantity = Output;
|
||||
using InputQuantities = QuantityList<Inputs...>;
|
||||
|
||||
static constexpr std::size_t inputCount = sizeof...(Inputs);
|
||||
};
|
||||
|
||||
template <typename... Relations> struct RelationCatalog final {
|
||||
static constexpr std::size_t size = sizeof...(Relations);
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
template <typename... Types> struct TypesAreUnique;
|
||||
|
||||
template <typename Candidate> struct IsThermodynamicRelation : std::false_type { };
|
||||
|
||||
template <typename Output, typename... Inputs>
|
||||
struct IsThermodynamicRelation<Relation<Output, Inputs...>>
|
||||
: std::bool_constant<
|
||||
ThermodynamicQuantityType<Output> && (ThermodynamicQuantityType<Inputs> && ...) &&
|
||||
TypesAreUnique<Inputs...>::value> { };
|
||||
|
||||
template <typename... Types> struct TypesAreUnique : std::true_type { };
|
||||
|
||||
template <typename First, typename... Remaining>
|
||||
struct TypesAreUnique<First, Remaining...>
|
||||
: std::bool_constant<(!std::same_as<First, Remaining> && ...) && TypesAreUnique<Remaining...>::value> { };
|
||||
|
||||
template <typename Candidate> struct IsValidRelationCatalog : std::false_type { };
|
||||
|
||||
template <typename... Relations>
|
||||
struct IsValidRelationCatalog<RelationCatalog<Relations...>>
|
||||
: std::bool_constant<
|
||||
(sizeof...(Relations) > 0) && (IsThermodynamicRelation<Relations>::value && ...) &&
|
||||
TypesAreUnique<Relations...>::value> { };
|
||||
|
||||
template <typename Catalog, typename RelationType> struct CatalogContainsRelation : std::false_type { };
|
||||
|
||||
template <typename... Relations, typename RelationType>
|
||||
struct CatalogContainsRelation<RelationCatalog<Relations...>, RelationType>
|
||||
: std::bool_constant<(std::same_as<RelationType, Relations> || ...)> { };
|
||||
|
||||
template <typename RelationType, typename Quantity> struct RelationContainsInput : std::false_type { };
|
||||
|
||||
template <typename Output, typename... Inputs, typename Quantity>
|
||||
struct RelationContainsInput<Relation<Output, Inputs...>, Quantity>
|
||||
: std::bool_constant<(std::same_as<Quantity, Inputs> || ...)> { };
|
||||
|
||||
template <std::size_t Index, typename Quantities> struct QuantityAt;
|
||||
|
||||
template <std::size_t Index, typename... Quantities> struct QuantityAt<Index, QuantityList<Quantities...>> {
|
||||
using Type = std::tuple_element_t<Index, std::tuple<Quantities...>>;
|
||||
};
|
||||
} // namespace detail
|
||||
|
||||
template <typename Candidate>
|
||||
concept ThermodynamicRelationType = detail::IsThermodynamicRelation<std::remove_cv_t<Candidate>>::value;
|
||||
|
||||
template <typename Candidate>
|
||||
concept ValidRelationCatalog = detail::IsValidRelationCatalog<std::remove_cv_t<Candidate>>::value;
|
||||
|
||||
template <typename Catalog, typename RelationType>
|
||||
inline constexpr bool relationCatalogContains =
|
||||
detail::CatalogContainsRelation<std::remove_cv_t<Catalog>, std::remove_cv_t<RelationType>>::value;
|
||||
|
||||
template <typename RelationType, typename Quantity>
|
||||
inline constexpr bool relationContainsInput =
|
||||
detail::RelationContainsInput<std::remove_cv_t<RelationType>, std::remove_cv_t<Quantity>>::value;
|
||||
|
||||
template <ThermodynamicRelationType RelationType> using RelationOutputT = typename RelationType::OutputQuantity;
|
||||
|
||||
template <std::size_t Index, ThermodynamicRelationType RelationType>
|
||||
using RelationInputT = typename detail::QuantityAt<Index, typename RelationType::InputQuantities>::Type;
|
||||
|
||||
using PressureFromDensity = Relation<quantity::Pressure, quantity::Density>;
|
||||
using PressureFromSpecificEnthalpy = Relation<quantity::Pressure, quantity::SpecificEnthalpy>;
|
||||
using SpecificEnthalpyFromDensity = Relation<quantity::SpecificEnthalpy, quantity::Density>;
|
||||
using SpecificEnthalpyFromPressure = Relation<quantity::SpecificEnthalpy, quantity::Pressure>;
|
||||
using DensityFromSpecificEnthalpy = Relation<quantity::Density, quantity::SpecificEnthalpy>;
|
||||
} // namespace mean_field::eos
|
||||
645
libmeanfield/interface/eos/runtime.cppm
Normal file
645
libmeanfield/interface/eos/runtime.cppm
Normal file
@@ -0,0 +1,645 @@
|
||||
module;
|
||||
|
||||
#include <array>
|
||||
#include <concepts>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <expected>
|
||||
#include <memory>
|
||||
#include <span>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
export module mean_field:eos.runtime;
|
||||
export import :eos.evaluation;
|
||||
|
||||
export namespace mean_field::eos {
|
||||
class ThermodynamicQuantityId final {
|
||||
public:
|
||||
explicit constexpr ThermodynamicQuantityId(const std::string_view name) noexcept : m_name(name) {
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr std::string_view name() const noexcept {
|
||||
return m_name;
|
||||
}
|
||||
|
||||
[[nodiscard]] friend constexpr bool operator==(
|
||||
const ThermodynamicQuantityId &,
|
||||
const ThermodynamicQuantityId &
|
||||
) noexcept = default;
|
||||
|
||||
private:
|
||||
std::string_view m_name;
|
||||
};
|
||||
|
||||
template <typename Quantity>
|
||||
concept RuntimeIdentifiedThermodynamicQuantity = ThermodynamicQuantityType<Quantity> && requires {
|
||||
{ Quantity::identifier } -> std::convertible_to<std::string_view>;
|
||||
} && (std::string_view{Quantity::identifier}.size() > 0);
|
||||
|
||||
template <RuntimeIdentifiedThermodynamicQuantity Quantity>
|
||||
inline constexpr ThermodynamicQuantityId thermodynamicQuantityId{std::string_view{Quantity::identifier}};
|
||||
|
||||
struct RuntimeQuantityValue final {
|
||||
ThermodynamicQuantityId quantity;
|
||||
double value;
|
||||
};
|
||||
|
||||
struct RuntimeRelationDescriptor final {
|
||||
ThermodynamicQuantityId outputQuantity;
|
||||
std::span<const ThermodynamicQuantityId> inputQuantities;
|
||||
std::uint64_t partialDerivativeMask;
|
||||
|
||||
[[nodiscard]] constexpr bool hasPartialDerivative(const std::size_t inputIndex) const noexcept {
|
||||
return inputIndex < inputQuantities.size() &&
|
||||
(partialDerivativeMask & (std::uint64_t{1} << inputIndex)) != 0;
|
||||
}
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
template <typename RelationType> struct HasRuntimeQuantityIdentifiers : std::false_type { };
|
||||
|
||||
template <typename Output, typename... Inputs>
|
||||
struct HasRuntimeQuantityIdentifiers<Relation<Output, Inputs...>>
|
||||
: std::bool_constant<
|
||||
RuntimeIdentifiedThermodynamicQuantity<Output> &&
|
||||
(RuntimeIdentifiedThermodynamicQuantity<Inputs> && ...)> { };
|
||||
|
||||
template <typename RelationType> struct RuntimeRelationQuantities;
|
||||
|
||||
template <typename Output, typename... Inputs> struct RuntimeRelationQuantities<Relation<Output, Inputs...>> {
|
||||
using Type = std::tuple<Output, Inputs...>;
|
||||
};
|
||||
|
||||
template <typename... Relations>
|
||||
using RuntimeCatalogQuantityTuple =
|
||||
decltype(std::tuple_cat(std::declval<typename RuntimeRelationQuantities<Relations>::Type>()...));
|
||||
|
||||
template <
|
||||
typename FirstQuantity,
|
||||
typename SecondQuantity>
|
||||
[[nodiscard]] consteval bool runtimeQuantityIdentifiersAreCompatible() {
|
||||
if constexpr (std::same_as<FirstQuantity, SecondQuantity>) {
|
||||
return true;
|
||||
} else {
|
||||
return thermodynamicQuantityId<FirstQuantity> != thermodynamicQuantityId<SecondQuantity>;
|
||||
}
|
||||
}
|
||||
|
||||
template <
|
||||
typename QuantityTuple,
|
||||
std::size_t First,
|
||||
std::size_t... Offsets>
|
||||
[[nodiscard]] consteval bool runtimeQuantityIdentifierIsUnambiguous(std::index_sequence<Offsets...>) {
|
||||
return (
|
||||
runtimeQuantityIdentifiersAreCompatible<
|
||||
std::tuple_element_t<First, QuantityTuple>,
|
||||
std::tuple_element_t<First + 1 + Offsets, QuantityTuple>>() &&
|
||||
...
|
||||
);
|
||||
}
|
||||
|
||||
template <
|
||||
typename QuantityTuple,
|
||||
std::size_t... Indices>
|
||||
[[nodiscard]] consteval bool runtimeQuantityIdentifiersAreUnambiguous(std::index_sequence<Indices...>) {
|
||||
return (
|
||||
runtimeQuantityIdentifierIsUnambiguous<QuantityTuple, Indices>(
|
||||
std::make_index_sequence<std::tuple_size_v<QuantityTuple> - Indices - 1>{}
|
||||
) &&
|
||||
...
|
||||
);
|
||||
}
|
||||
|
||||
template <bool QuantitiesAreIdentified, typename... Relations>
|
||||
struct RuntimeRelationsAreSupported : std::false_type { };
|
||||
|
||||
template <typename... Relations>
|
||||
struct RuntimeRelationsAreSupported<true, Relations...>
|
||||
: std::bool_constant<runtimeQuantityIdentifiersAreUnambiguous<RuntimeCatalogQuantityTuple<Relations...>>(
|
||||
std::make_index_sequence<std::tuple_size_v<RuntimeCatalogQuantityTuple<Relations...>>>{}
|
||||
)> { };
|
||||
|
||||
template <typename Catalog> struct RuntimeCatalogIsSupported : std::false_type { };
|
||||
|
||||
template <typename... Relations>
|
||||
struct RuntimeCatalogIsSupported<RelationCatalog<Relations...>>
|
||||
: RuntimeRelationsAreSupported<(HasRuntimeQuantityIdentifiers<Relations>::value && ...), Relations...> { };
|
||||
} // namespace detail
|
||||
|
||||
template <typename Candidate>
|
||||
concept RuntimeEquationOfStateModel =
|
||||
EquationOfStateModel<Candidate> &&
|
||||
detail::RuntimeCatalogIsSupported<typename std::remove_cvref_t<Candidate>::Relations>::value;
|
||||
|
||||
namespace detail {
|
||||
template <typename EquationOfState, typename RelationType> struct RuntimeRelationStorage;
|
||||
|
||||
template <typename EquationOfState, typename Output, typename... Inputs>
|
||||
struct RuntimeRelationStorage<EquationOfState, Relation<Output, Inputs...>> {
|
||||
using RelationType = Relation<Output, Inputs...>;
|
||||
|
||||
static_assert(
|
||||
sizeof...(Inputs) <= 64,
|
||||
"Runtime EOS relation descriptors support at most 64 inputs."
|
||||
);
|
||||
|
||||
inline static constexpr std::array<ThermodynamicQuantityId, sizeof...(Inputs)> inputQuantityIds{
|
||||
thermodynamicQuantityId<Inputs>...
|
||||
};
|
||||
|
||||
template <std::size_t... Indices>
|
||||
[[nodiscard]] static consteval std::uint64_t makePartialDerivativeMask(std::index_sequence<Indices...>) {
|
||||
using InputTuple = std::tuple<Inputs...>;
|
||||
|
||||
return (
|
||||
std::uint64_t{0} | ... |
|
||||
(SupportsPartialDerivative<EquationOfState, RelationType, std::tuple_element_t<Indices, InputTuple>>
|
||||
? (std::uint64_t{1} << Indices)
|
||||
: std::uint64_t{0})
|
||||
);
|
||||
}
|
||||
|
||||
inline static constexpr std::uint64_t partialDerivativeMask =
|
||||
makePartialDerivativeMask(std::index_sequence_for<Inputs...>{});
|
||||
|
||||
inline static constexpr RuntimeRelationDescriptor descriptor{
|
||||
thermodynamicQuantityId<Output>, std::span<const ThermodynamicQuantityId>{inputQuantityIds},
|
||||
partialDerivativeMask
|
||||
};
|
||||
};
|
||||
|
||||
template <typename EquationOfState, typename Catalog> struct RuntimeCatalogStorage;
|
||||
|
||||
template <typename EquationOfState, typename... Relations>
|
||||
struct RuntimeCatalogStorage<EquationOfState, RelationCatalog<Relations...>> {
|
||||
inline static constexpr std::array descriptors{
|
||||
RuntimeRelationStorage<EquationOfState, Relations>::descriptor...
|
||||
};
|
||||
};
|
||||
|
||||
[[nodiscard]] inline std::expected<
|
||||
double,
|
||||
EvaluationError>
|
||||
runtimeEvaluationFailure(
|
||||
const EvaluationErrorCode code,
|
||||
std::string message
|
||||
) {
|
||||
return std::unexpected<EvaluationError>{EvaluationError{code, std::move(message)}};
|
||||
}
|
||||
|
||||
template <
|
||||
typename EquationOfState,
|
||||
typename Output,
|
||||
typename... Inputs>
|
||||
[[nodiscard]] std::expected<
|
||||
double,
|
||||
EvaluationError>
|
||||
evaluateRuntimeRelation(
|
||||
const EquationOfState &equationOfState,
|
||||
Relation<
|
||||
Output,
|
||||
Inputs...>,
|
||||
const std::span<const RuntimeQuantityValue> inputValues
|
||||
) {
|
||||
const auto invoke = [&]<std::size_t... Indices>(std::index_sequence<Indices...>) {
|
||||
return eos::evaluate<Output>(equationOfState, QuantityValue<Inputs>{inputValues[Indices].value}...)
|
||||
.value();
|
||||
};
|
||||
|
||||
try {
|
||||
return invoke(std::index_sequence_for<Inputs...>{});
|
||||
} catch (const EvaluationError &error) {
|
||||
return std::unexpected<EvaluationError>{error};
|
||||
}
|
||||
}
|
||||
|
||||
template <
|
||||
typename InputQuantity,
|
||||
typename EquationOfState,
|
||||
typename Output,
|
||||
typename... Inputs>
|
||||
[[nodiscard]] bool tryRuntimePartialDerivative(
|
||||
const EquationOfState &equationOfState,
|
||||
Relation<
|
||||
Output,
|
||||
Inputs...> relation,
|
||||
const ThermodynamicQuantityId withRespectTo,
|
||||
const std::span<const RuntimeQuantityValue> inputValues,
|
||||
std::expected<
|
||||
double,
|
||||
EvaluationError> &result
|
||||
) {
|
||||
if (withRespectTo != thermodynamicQuantityId<InputQuantity>) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if constexpr (SupportsPartialDerivative<EquationOfState, Relation<Output, Inputs...>, InputQuantity>) {
|
||||
const auto invoke = [&]<std::size_t... Indices>(std::index_sequence<Indices...>) {
|
||||
return eos::partialDerivative<Output, InputQuantity>(
|
||||
equationOfState, QuantityValue<Inputs>{inputValues[Indices].value}...
|
||||
)
|
||||
.value();
|
||||
};
|
||||
|
||||
try {
|
||||
result = invoke(std::index_sequence_for<Inputs...>{});
|
||||
} catch (const EvaluationError &error) {
|
||||
result = std::unexpected<EvaluationError>{error};
|
||||
}
|
||||
} else {
|
||||
result = runtimeEvaluationFailure(
|
||||
EvaluationErrorCode::unsupported_derivative,
|
||||
"The requested EOS partial derivative is not available."
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <
|
||||
typename EquationOfState,
|
||||
typename Output,
|
||||
typename... Inputs>
|
||||
[[nodiscard]] std::expected<
|
||||
double,
|
||||
EvaluationError>
|
||||
evaluateRuntimePartialDerivative(
|
||||
const EquationOfState &equationOfState,
|
||||
Relation<
|
||||
Output,
|
||||
Inputs...> relation,
|
||||
const ThermodynamicQuantityId withRespectTo,
|
||||
const std::span<const RuntimeQuantityValue> inputValues
|
||||
) {
|
||||
std::expected<double, EvaluationError> result = runtimeEvaluationFailure(
|
||||
EvaluationErrorCode::unsupported_derivative,
|
||||
"The requested quantity is not an input to the EOS relation."
|
||||
);
|
||||
|
||||
const bool matched =
|
||||
(tryRuntimePartialDerivative<Inputs>(equationOfState, relation, withRespectTo, inputValues, result) ||
|
||||
...);
|
||||
|
||||
static_cast<void>(matched);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <
|
||||
typename EquationOfState,
|
||||
typename RelationType>
|
||||
[[nodiscard]] bool runtimeRelationMatches(
|
||||
const ThermodynamicQuantityId outputQuantity,
|
||||
const std::span<const RuntimeQuantityValue> inputValues
|
||||
) {
|
||||
const RuntimeRelationDescriptor &descriptor =
|
||||
RuntimeRelationStorage<EquationOfState, RelationType>::descriptor;
|
||||
|
||||
if (descriptor.outputQuantity != outputQuantity ||
|
||||
descriptor.inputQuantities.size() != inputValues.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (std::size_t index = 0; index < inputValues.size(); ++index) {
|
||||
if (descriptor.inputQuantities[index] != inputValues[index].quantity) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename EquationOfState, typename Catalog> struct RuntimeCatalogDispatch;
|
||||
|
||||
template <typename EquationOfState, typename... Relations>
|
||||
struct RuntimeCatalogDispatch<EquationOfState, RelationCatalog<Relations...>> {
|
||||
[[nodiscard]] static std::expected<
|
||||
double,
|
||||
EvaluationError>
|
||||
evaluate(
|
||||
const void *object,
|
||||
const ThermodynamicQuantityId outputQuantity,
|
||||
const std::span<const RuntimeQuantityValue> inputValues
|
||||
) {
|
||||
const auto &equationOfState = *static_cast<const EquationOfState *>(object);
|
||||
|
||||
std::expected<double, EvaluationError> result = runtimeEvaluationFailure(
|
||||
EvaluationErrorCode::unsupported_relation, "The requested EOS relation is not available."
|
||||
);
|
||||
|
||||
const bool matched =
|
||||
((runtimeRelationMatches<EquationOfState, Relations>(outputQuantity, inputValues)
|
||||
? (result = evaluateRuntimeRelation(equationOfState, Relations{}, inputValues), true)
|
||||
: false) ||
|
||||
...);
|
||||
|
||||
static_cast<void>(matched);
|
||||
return result;
|
||||
}
|
||||
|
||||
[[nodiscard]] static std::expected<
|
||||
double,
|
||||
EvaluationError>
|
||||
partialDerivative(
|
||||
const void *object,
|
||||
const ThermodynamicQuantityId outputQuantity,
|
||||
const ThermodynamicQuantityId withRespectTo,
|
||||
const std::span<const RuntimeQuantityValue> inputValues
|
||||
) {
|
||||
const auto &equationOfState = *static_cast<const EquationOfState *>(object);
|
||||
|
||||
std::expected<double, EvaluationError> result = runtimeEvaluationFailure(
|
||||
EvaluationErrorCode::unsupported_relation, "The requested EOS relation is not available."
|
||||
);
|
||||
|
||||
const bool matched =
|
||||
((runtimeRelationMatches<EquationOfState, Relations>(outputQuantity, inputValues)
|
||||
? (result = evaluateRuntimePartialDerivative(
|
||||
equationOfState, Relations{}, withRespectTo, inputValues
|
||||
),
|
||||
true)
|
||||
: false) ||
|
||||
...);
|
||||
|
||||
static_cast<void>(matched);
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
template <RuntimeEquationOfStateModel EquationOfState>
|
||||
using RuntimeAdapter = RuntimeCatalogDispatch<EquationOfState, typename EquationOfState::Relations>;
|
||||
|
||||
template <RuntimeEquationOfStateModel EquationOfState>
|
||||
[[nodiscard]] constexpr std::span<const RuntimeRelationDescriptor> runtimeRelationDescriptors() noexcept {
|
||||
return RuntimeCatalogStorage<EquationOfState, typename EquationOfState::Relations>::descriptors;
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
class EquationOfStateView final {
|
||||
public:
|
||||
template <RuntimeEquationOfStateModel EquationOfState>
|
||||
explicit EquationOfStateView(EquationOfState &equationOfState) noexcept
|
||||
: m_object(std::addressof(equationOfState)),
|
||||
m_relations(detail::runtimeRelationDescriptors<std::remove_cv_t<EquationOfState>>()),
|
||||
m_evaluate(&detail::RuntimeAdapter<std::remove_cv_t<EquationOfState>>::evaluate),
|
||||
m_partialDerivative(&detail::RuntimeAdapter<std::remove_cv_t<EquationOfState>>::partialDerivative) {
|
||||
}
|
||||
|
||||
[[nodiscard]] std::span<const RuntimeRelationDescriptor> relations() const noexcept {
|
||||
return m_relations;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool supports(
|
||||
const ThermodynamicQuantityId outputQuantity,
|
||||
const std::span<const ThermodynamicQuantityId> inputQuantities
|
||||
) const noexcept {
|
||||
return findRelation(outputQuantity, inputQuantities) != nullptr;
|
||||
}
|
||||
|
||||
template <
|
||||
RuntimeIdentifiedThermodynamicQuantity OutputQuantity,
|
||||
RuntimeIdentifiedThermodynamicQuantity... InputQuantities>
|
||||
[[nodiscard]] bool supports() const noexcept {
|
||||
constexpr std::array<ThermodynamicQuantityId, sizeof...(InputQuantities)> inputs{
|
||||
thermodynamicQuantityId<InputQuantities>...
|
||||
};
|
||||
|
||||
return supports(thermodynamicQuantityId<OutputQuantity>, std::span<const ThermodynamicQuantityId>{inputs});
|
||||
}
|
||||
|
||||
[[nodiscard]] std::expected<
|
||||
RuntimeQuantityValue,
|
||||
EvaluationError>
|
||||
tryEvaluate(
|
||||
const ThermodynamicQuantityId outputQuantity,
|
||||
const std::span<const RuntimeQuantityValue> inputValues
|
||||
) const {
|
||||
const auto validation = validateRelationRequest(outputQuantity, inputValues);
|
||||
|
||||
if (!validation.has_value()) {
|
||||
return std::unexpected<EvaluationError>{validation.error()};
|
||||
}
|
||||
|
||||
auto result = m_evaluate(m_object, outputQuantity, inputValues);
|
||||
if (!result.has_value()) {
|
||||
return std::unexpected<EvaluationError>{result.error()};
|
||||
}
|
||||
|
||||
return RuntimeQuantityValue{outputQuantity, *result};
|
||||
}
|
||||
|
||||
template <
|
||||
RuntimeIdentifiedThermodynamicQuantity OutputQuantity,
|
||||
QuantityValueType... InputValues>
|
||||
[[nodiscard]] std::expected<
|
||||
QuantityValue<OutputQuantity>,
|
||||
EvaluationError>
|
||||
tryEvaluate(const InputValues... inputValues) const {
|
||||
constexpr bool inputsHaveRuntimeIdentifiers =
|
||||
(RuntimeIdentifiedThermodynamicQuantity<QuantityOfT<InputValues>> && ...);
|
||||
|
||||
static_assert(inputsHaveRuntimeIdentifiers, "Every runtime EOS input quantity needs a stable identifier.");
|
||||
|
||||
const std::array<RuntimeQuantityValue, sizeof...(InputValues)> runtimeInputs{
|
||||
RuntimeQuantityValue{thermodynamicQuantityId<QuantityOfT<InputValues>>, inputValues.value()}...
|
||||
};
|
||||
|
||||
auto result = tryEvaluate(
|
||||
thermodynamicQuantityId<OutputQuantity>, std::span<const RuntimeQuantityValue>{runtimeInputs}
|
||||
);
|
||||
|
||||
if (!result.has_value()) {
|
||||
return std::unexpected<EvaluationError>{result.error()};
|
||||
}
|
||||
|
||||
return QuantityValue<OutputQuantity>{result->value};
|
||||
}
|
||||
|
||||
[[nodiscard]] std::expected<
|
||||
double,
|
||||
EvaluationError>
|
||||
tryPartialDerivative(
|
||||
const ThermodynamicQuantityId outputQuantity,
|
||||
const ThermodynamicQuantityId withRespectTo,
|
||||
const std::span<const RuntimeQuantityValue> inputValues
|
||||
) const {
|
||||
const auto validation = validateRelationRequest(outputQuantity, inputValues);
|
||||
|
||||
if (!validation.has_value()) {
|
||||
return std::unexpected<EvaluationError>{validation.error()};
|
||||
}
|
||||
|
||||
const RuntimeRelationDescriptor &descriptor = **validation;
|
||||
bool derivativeAvailable = false;
|
||||
|
||||
for (std::size_t index = 0; index < descriptor.inputQuantities.size(); ++index) {
|
||||
if (descriptor.inputQuantities[index] == withRespectTo) {
|
||||
derivativeAvailable = descriptor.hasPartialDerivative(index);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!derivativeAvailable) {
|
||||
return runtimeFailure<double>(
|
||||
EvaluationErrorCode::unsupported_derivative,
|
||||
"The requested EOS partial derivative is not available."
|
||||
);
|
||||
}
|
||||
|
||||
return m_partialDerivative(m_object, outputQuantity, withRespectTo, inputValues);
|
||||
}
|
||||
|
||||
template <
|
||||
RuntimeIdentifiedThermodynamicQuantity OutputQuantity,
|
||||
RuntimeIdentifiedThermodynamicQuantity InputQuantity,
|
||||
QuantityValueType... InputValues>
|
||||
[[nodiscard]] std::expected<
|
||||
PartialDerivative<
|
||||
OutputQuantity,
|
||||
InputQuantity>,
|
||||
EvaluationError>
|
||||
tryPartialDerivative(const InputValues... inputValues) const {
|
||||
constexpr bool inputsHaveRuntimeIdentifiers =
|
||||
(RuntimeIdentifiedThermodynamicQuantity<QuantityOfT<InputValues>> && ...);
|
||||
|
||||
static_assert(inputsHaveRuntimeIdentifiers, "Every runtime EOS input quantity needs a stable identifier.");
|
||||
|
||||
const std::array<RuntimeQuantityValue, sizeof...(InputValues)> runtimeInputs{
|
||||
RuntimeQuantityValue{thermodynamicQuantityId<QuantityOfT<InputValues>>, inputValues.value()}...
|
||||
};
|
||||
|
||||
auto result = tryPartialDerivative(
|
||||
thermodynamicQuantityId<OutputQuantity>, thermodynamicQuantityId<InputQuantity>,
|
||||
std::span<const RuntimeQuantityValue>{runtimeInputs}
|
||||
);
|
||||
|
||||
if (!result.has_value()) {
|
||||
return std::unexpected<EvaluationError>{result.error()};
|
||||
}
|
||||
|
||||
return PartialDerivative<OutputQuantity, InputQuantity>{*result};
|
||||
}
|
||||
|
||||
private:
|
||||
using RuntimeEvaluateFunction = std::expected<
|
||||
double,
|
||||
EvaluationError> (*)(
|
||||
const void *,
|
||||
ThermodynamicQuantityId,
|
||||
std::span<const RuntimeQuantityValue>
|
||||
);
|
||||
|
||||
using RuntimePartialDerivativeFunction = std::expected<
|
||||
double,
|
||||
EvaluationError> (*)(
|
||||
const void *,
|
||||
ThermodynamicQuantityId,
|
||||
ThermodynamicQuantityId,
|
||||
std::span<const RuntimeQuantityValue>
|
||||
);
|
||||
|
||||
[[nodiscard]] const RuntimeRelationDescriptor *findRelation(
|
||||
const ThermodynamicQuantityId outputQuantity,
|
||||
const std::span<const ThermodynamicQuantityId> inputQuantities
|
||||
) const noexcept {
|
||||
for (const RuntimeRelationDescriptor &descriptor : m_relations) {
|
||||
if (descriptor.outputQuantity != outputQuantity ||
|
||||
descriptor.inputQuantities.size() != inputQuantities.size()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
bool matches = true;
|
||||
for (std::size_t index = 0; index < inputQuantities.size(); ++index) {
|
||||
if (descriptor.inputQuantities[index] != inputQuantities[index]) {
|
||||
matches = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (matches) {
|
||||
return std::addressof(descriptor);
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::expected<
|
||||
const RuntimeRelationDescriptor *,
|
||||
EvaluationError>
|
||||
validateRelationRequest(
|
||||
const ThermodynamicQuantityId outputQuantity,
|
||||
const std::span<const RuntimeQuantityValue> inputValues
|
||||
) const {
|
||||
bool outputAvailable = false;
|
||||
bool inputCountAvailable = false;
|
||||
|
||||
for (const RuntimeRelationDescriptor &descriptor : m_relations) {
|
||||
if (descriptor.outputQuantity != outputQuantity) {
|
||||
continue;
|
||||
}
|
||||
|
||||
outputAvailable = true;
|
||||
|
||||
if (descriptor.inputQuantities.size() != inputValues.size()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
inputCountAvailable = true;
|
||||
bool matches = true;
|
||||
|
||||
for (std::size_t index = 0; index < inputValues.size(); ++index) {
|
||||
if (descriptor.inputQuantities[index] != inputValues[index].quantity) {
|
||||
matches = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (matches) {
|
||||
return std::addressof(descriptor);
|
||||
}
|
||||
}
|
||||
|
||||
if (!outputAvailable) {
|
||||
return runtimeFailure<const RuntimeRelationDescriptor *>(
|
||||
EvaluationErrorCode::unsupported_relation,
|
||||
"The EOS does not provide a relation for output quantity '" + std::string{outputQuantity.name()} +
|
||||
"'."
|
||||
);
|
||||
}
|
||||
|
||||
if (!inputCountAvailable) {
|
||||
return runtimeFailure<const RuntimeRelationDescriptor *>(
|
||||
EvaluationErrorCode::wrong_input_count, "No EOS relation for output quantity '" +
|
||||
std::string{outputQuantity.name()} +
|
||||
"' accepts the supplied number of inputs."
|
||||
);
|
||||
}
|
||||
|
||||
return runtimeFailure<const RuntimeRelationDescriptor *>(
|
||||
EvaluationErrorCode::wrong_input_quantity, "No EOS relation for output quantity '" +
|
||||
std::string{outputQuantity.name()} +
|
||||
"' accepts the supplied input quantities."
|
||||
);
|
||||
}
|
||||
|
||||
template <typename Value>
|
||||
[[nodiscard]] static std::expected<
|
||||
Value,
|
||||
EvaluationError>
|
||||
runtimeFailure(
|
||||
const EvaluationErrorCode code,
|
||||
std::string message
|
||||
) {
|
||||
return std::unexpected<EvaluationError>{EvaluationError{code, std::move(message)}};
|
||||
}
|
||||
|
||||
const void *m_object;
|
||||
std::span<const RuntimeRelationDescriptor> m_relations;
|
||||
RuntimeEvaluateFunction m_evaluate;
|
||||
RuntimePartialDerivativeFunction m_partialDerivative;
|
||||
};
|
||||
} // namespace mean_field::eos
|
||||
@@ -1,6 +1,7 @@
|
||||
module;
|
||||
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <concepts>
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
@@ -888,6 +889,282 @@ export namespace mean_field::field {
|
||||
mfem::Array<int> m_trueToReduced;
|
||||
};
|
||||
|
||||
/*
|
||||
* Boundary rows expressed in a field's reduced solver ordering.
|
||||
*
|
||||
* This object is deliberately independent of any particular physical
|
||||
* surface condition. Its template constructor below combines a field,
|
||||
* a semantic boundary, and a domain schema. Consequently the same
|
||||
* topology machinery can be used by any compiled surface formulation;
|
||||
* it is not tied to enthalpy or pressure.
|
||||
*/
|
||||
class FieldBoundaryDofMap final {
|
||||
public:
|
||||
FieldBoundaryDofMap() = default;
|
||||
|
||||
FieldBoundaryDofMap(
|
||||
const int fieldReducedSize,
|
||||
const mfem::Array<int> &boundaryReducedDofs
|
||||
)
|
||||
: m_fieldReducedSize(fieldReducedSize),
|
||||
m_boundaryReducedDofs(boundaryReducedDofs) {
|
||||
if (m_fieldReducedSize < 0) {
|
||||
throw std::invalid_argument("FieldBoundaryDofMap requires a non-negative field size.");
|
||||
}
|
||||
|
||||
m_boundaryReducedDofMarker.SetSize(m_fieldReducedSize);
|
||||
m_boundaryReducedDofMarker = 0;
|
||||
|
||||
int previousReducedDof = -1;
|
||||
for (const int reducedDof : m_boundaryReducedDofs) {
|
||||
if (reducedDof < 0 || reducedDof >= m_fieldReducedSize) {
|
||||
throw std::invalid_argument("FieldBoundaryDofMap contains a DOF outside the reduced field vector.");
|
||||
}
|
||||
if (reducedDof <= previousReducedDof) {
|
||||
throw std::invalid_argument("FieldBoundaryDofMap indices must be strictly increasing and unique.");
|
||||
}
|
||||
|
||||
m_boundaryReducedDofMarker[reducedDof] = 1;
|
||||
previousReducedDof = reducedDof;
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] int field_size() const noexcept {
|
||||
return m_fieldReducedSize;
|
||||
}
|
||||
|
||||
[[nodiscard]] int size() const noexcept {
|
||||
return m_boundaryReducedDofs.Size();
|
||||
}
|
||||
|
||||
[[nodiscard]] bool empty() const noexcept {
|
||||
return size() == 0;
|
||||
}
|
||||
|
||||
[[nodiscard]] const mfem::Array<int> &reduced_dofs() const noexcept {
|
||||
return m_boundaryReducedDofs;
|
||||
}
|
||||
|
||||
[[nodiscard]] const mfem::Array<int> &reduced_dof_marker() const noexcept {
|
||||
return m_boundaryReducedDofMarker;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool contains(const int reducedDof) const {
|
||||
if (reducedDof < 0 || reducedDof >= m_fieldReducedSize) {
|
||||
throw std::out_of_range("Reduced DOF index is outside FieldBoundaryDofMap.");
|
||||
}
|
||||
return m_boundaryReducedDofMarker[reducedDof] != 0;
|
||||
}
|
||||
|
||||
private:
|
||||
int m_fieldReducedSize{0};
|
||||
mfem::Array<int> m_boundaryReducedDofs;
|
||||
mfem::Array<int> m_boundaryReducedDofMarker;
|
||||
};
|
||||
|
||||
/* Point-supported rows in a field's reduced solver ordering. */
|
||||
class FieldPointDofMap final {
|
||||
public:
|
||||
FieldPointDofMap() = default;
|
||||
|
||||
FieldPointDofMap(
|
||||
const int fieldReducedSize,
|
||||
const mfem::Array<int> &pointReducedDofs
|
||||
)
|
||||
: m_selectedDofs(
|
||||
fieldReducedSize,
|
||||
pointReducedDofs
|
||||
) {
|
||||
}
|
||||
|
||||
[[nodiscard]] int field_size() const noexcept {
|
||||
return m_selectedDofs.field_size();
|
||||
}
|
||||
|
||||
[[nodiscard]] int size() const noexcept {
|
||||
return m_selectedDofs.size();
|
||||
}
|
||||
|
||||
[[nodiscard]] bool empty() const noexcept {
|
||||
return m_selectedDofs.empty();
|
||||
}
|
||||
|
||||
[[nodiscard]] const mfem::Array<int> &reduced_dofs() const noexcept {
|
||||
return m_selectedDofs.reduced_dofs();
|
||||
}
|
||||
|
||||
[[nodiscard]] const mfem::Array<int> &reduced_dof_marker() const noexcept {
|
||||
return m_selectedDofs.reduced_dof_marker();
|
||||
}
|
||||
|
||||
[[nodiscard]] bool contains(const int reducedDof) const {
|
||||
return m_selectedDofs.contains(reducedDof);
|
||||
}
|
||||
|
||||
private:
|
||||
FieldBoundaryDofMap m_selectedDofs;
|
||||
};
|
||||
|
||||
template <
|
||||
MfemDomainField FieldT,
|
||||
utils::domain::IsBoundary BoundaryT,
|
||||
utils::domain::IsSchema SchemaT>
|
||||
[[nodiscard]] FieldBoundaryDofMap make_field_boundary_dof_map(
|
||||
const mfem::ParFiniteElementSpace &finiteElementSpace,
|
||||
const FieldDofMap &fieldDofMap
|
||||
) {
|
||||
static_assert(
|
||||
SchemaT::template contains_boundary<BoundaryT>(),
|
||||
"The requested boundary is not registered in the supplied DomainSchema."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
!finiteElementSpace.Nonconforming(),
|
||||
"Field boundary true-DOF resolution currently requires a conforming mfem::ParFiniteElementSpace."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
fieldDofMap.full_size() == finiteElementSpace.GetTrueVSize(),
|
||||
"The field map and finite-element space have incompatible true-DOF sizes."
|
||||
);
|
||||
|
||||
const mfem::Mesh *mesh = finiteElementSpace.GetMesh();
|
||||
MFEM_VERIFY(mesh != nullptr, "Field boundary DOF resolution requires an MFEM mesh.");
|
||||
|
||||
mfem::Array<int> boundaryVDofMarker(finiteElementSpace.GetVSize());
|
||||
boundaryVDofMarker = 0;
|
||||
|
||||
mfem::Array<int> boundaryElementVDofs;
|
||||
for (int boundaryElement = 0; boundaryElement < mesh->GetNBE(); ++boundaryElement) {
|
||||
if (!SchemaT::template boundary_attribute_matches<BoundaryT>(mesh->GetBdrAttribute(boundaryElement))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
finiteElementSpace.GetBdrElementVDofs(boundaryElement, boundaryElementVDofs);
|
||||
for (const int encodedVDof : boundaryElementVDofs) {
|
||||
const int vdof = mfem::FiniteElementSpace::DecodeDof(encodedVDof);
|
||||
MFEM_VERIFY(
|
||||
vdof >= 0 && vdof < finiteElementSpace.GetVSize(), "MFEM returned an invalid boundary vector DOF."
|
||||
);
|
||||
boundaryVDofMarker[vdof] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
finiteElementSpace.Synchronize(boundaryVDofMarker);
|
||||
|
||||
mfem::Array<int> boundaryReducedDofMarker(fieldDofMap.reduced_size());
|
||||
boundaryReducedDofMarker = 0;
|
||||
|
||||
for (int vdof = 0; vdof < boundaryVDofMarker.Size(); ++vdof) {
|
||||
if (boundaryVDofMarker[vdof] == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const int trueDof = finiteElementSpace.GetLocalTDofNumber(vdof);
|
||||
if (trueDof < 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const std::optional<int> reducedDof = fieldDofMap.reduced_dof(trueDof);
|
||||
MFEM_VERIFY(
|
||||
reducedDof.has_value(),
|
||||
"A boundary DOF selected for the field is absent from that field's reduced solver map."
|
||||
);
|
||||
boundaryReducedDofMarker[*reducedDof] = 1;
|
||||
}
|
||||
|
||||
mfem::Array<int> boundaryReducedDofs;
|
||||
mfem::FiniteElementSpace::MarkerToList(boundaryReducedDofMarker, boundaryReducedDofs);
|
||||
return FieldBoundaryDofMap(fieldDofMap.reduced_size(), boundaryReducedDofs);
|
||||
}
|
||||
|
||||
template <MfemDomainField FieldT>
|
||||
[[nodiscard]] FieldPointDofMap make_field_point_dof_map(
|
||||
const mfem::ParFiniteElementSpace &finiteElementSpace,
|
||||
const FieldDofMap &fieldDofMap,
|
||||
const mfem::Vector &point,
|
||||
const double tolerance
|
||||
) {
|
||||
MFEM_VERIFY(
|
||||
!finiteElementSpace.Nonconforming(),
|
||||
"Field point true-DOF resolution currently requires a conforming mfem::ParFiniteElementSpace."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
fieldDofMap.full_size() == finiteElementSpace.GetTrueVSize(),
|
||||
"The field map and finite-element space have incompatible true-DOF sizes."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
std::isfinite(tolerance) && tolerance >= 0.0, "The field point tolerance must be finite and non-negative."
|
||||
);
|
||||
|
||||
const mfem::Mesh *mesh = finiteElementSpace.GetMesh();
|
||||
MFEM_VERIFY(mesh != nullptr, "Field point DOF resolution requires an MFEM mesh.");
|
||||
MFEM_VERIFY(
|
||||
point.Size() == mesh->SpaceDimension(), "The requested field point has the wrong coordinate dimension."
|
||||
);
|
||||
|
||||
mfem::Array<int> pointVDofMarker(finiteElementSpace.GetVSize());
|
||||
pointVDofMarker = 0;
|
||||
|
||||
mfem::Array<int> vertexVDofs;
|
||||
for (int vertex = 0; vertex < mesh->GetNV(); ++vertex) {
|
||||
const mfem::real_t *coordinates = mesh->GetVertex(vertex);
|
||||
double distanceSquared = 0.0;
|
||||
for (int component = 0; component < point.Size(); ++component) {
|
||||
const double difference = coordinates[component] - point(component);
|
||||
distanceSquared += difference * difference;
|
||||
}
|
||||
if (std::sqrt(distanceSquared) > tolerance) {
|
||||
continue;
|
||||
}
|
||||
|
||||
finiteElementSpace.GetVertexVDofs(vertex, vertexVDofs);
|
||||
for (const int encodedVDof : vertexVDofs) {
|
||||
const int vdof = mfem::FiniteElementSpace::DecodeDof(encodedVDof);
|
||||
MFEM_VERIFY(
|
||||
vdof >= 0 && vdof < finiteElementSpace.GetVSize(), "MFEM returned an invalid point vector DOF."
|
||||
);
|
||||
pointVDofMarker[vdof] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
finiteElementSpace.Synchronize(pointVDofMarker);
|
||||
|
||||
mfem::Array<int> pointReducedDofMarker(fieldDofMap.reduced_size());
|
||||
pointReducedDofMarker = 0;
|
||||
for (int vdof = 0; vdof < pointVDofMarker.Size(); ++vdof) {
|
||||
if (pointVDofMarker[vdof] == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const int trueDof = finiteElementSpace.GetLocalTDofNumber(vdof);
|
||||
if (trueDof < 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const std::optional<int> reducedDof = fieldDofMap.reduced_dof(trueDof);
|
||||
MFEM_VERIFY(
|
||||
reducedDof.has_value(),
|
||||
"A point DOF selected for the field is absent from that field's reduced solver map."
|
||||
);
|
||||
pointReducedDofMarker[*reducedDof] = 1;
|
||||
}
|
||||
|
||||
mfem::Array<int> pointReducedDofs;
|
||||
mfem::FiniteElementSpace::MarkerToList(pointReducedDofMarker, pointReducedDofs);
|
||||
|
||||
const long long localPointDofCount = pointReducedDofs.Size();
|
||||
long long globalPointDofCount = 0;
|
||||
MPI_Allreduce(
|
||||
&localPointDofCount, &globalPointDofCount, 1, MPI_LONG_LONG, MPI_SUM, finiteElementSpace.GetComm()
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
globalPointDofCount == finiteElementSpace.GetVDim(),
|
||||
"The requested geometric point must identify exactly one field vertex globally."
|
||||
);
|
||||
|
||||
return FieldPointDofMap(fieldDofMap.reduced_size(), pointReducedDofs);
|
||||
}
|
||||
|
||||
/*
|
||||
* Canonical adapter between an MFEM GridFunction and a reduced field
|
||||
* vector.
|
||||
@@ -1015,9 +1292,6 @@ export namespace mean_field::field {
|
||||
[[nodiscard]]
|
||||
FieldDofGridFunctionAdapter
|
||||
make_field_dof_grid_function_adapter(const mfem::ParFiniteElementSpace &finiteElementSpace) {
|
||||
return FieldDofGridFunctionAdapter(
|
||||
make_field_dof_map<FieldT, SchemaT>(finiteElementSpace),
|
||||
finiteElementSpace
|
||||
);
|
||||
return FieldDofGridFunctionAdapter(make_field_dof_map<FieldT, SchemaT>(finiteElementSpace), finiteElementSpace);
|
||||
}
|
||||
} // namespace mean_field::field
|
||||
|
||||
@@ -41,7 +41,11 @@ export namespace mean_field::integrators {
|
||||
const mfem::GridFunction &compactification_coordinate,
|
||||
utils::EOS_P<EOS_T> eos
|
||||
)
|
||||
: m_mapping(mapper, displacement, compactification_coordinate),
|
||||
: m_mapping(
|
||||
mapper,
|
||||
displacement,
|
||||
compactification_coordinate
|
||||
),
|
||||
m_eos(std::move(eos)) {
|
||||
}
|
||||
|
||||
|
||||
@@ -8,259 +8,288 @@ import :mapping.compactification;
|
||||
import :utils.user;
|
||||
|
||||
export namespace mean_field::mapping {
|
||||
enum class FaceElementSide : uint8_t { element_1, element_2 };
|
||||
enum class FaceElementSide : uint8_t { element_1, element_2 };
|
||||
|
||||
class ElementDisplacementData {
|
||||
public:
|
||||
ElementDisplacementData(
|
||||
const mfem::FiniteElement &element, const mfem::Vector &displacement_dofs,
|
||||
mfem::Ordering::Type ordering = mfem::Ordering::byNODES);
|
||||
class ElementDisplacementData {
|
||||
public:
|
||||
ElementDisplacementData(
|
||||
const mfem::FiniteElement &element,
|
||||
const mfem::Vector &displacement_dofs,
|
||||
mfem::Ordering::Type ordering = mfem::Ordering::byNODES
|
||||
);
|
||||
|
||||
[[nodiscard]] const mfem::FiniteElement &GetElement() const noexcept;
|
||||
[[nodiscard]] const mfem::DenseMatrix &GetDofMatrix() const noexcept;
|
||||
[[nodiscard]] int GetDimension() const noexcept;
|
||||
[[nodiscard]] int GetDofCount() const noexcept;
|
||||
[[nodiscard]] mfem::Ordering::Type GetOrdering() const noexcept;
|
||||
[[nodiscard]] const mfem::FiniteElement &GetElement() const noexcept;
|
||||
[[nodiscard]] const mfem::DenseMatrix &GetDofMatrix() const noexcept;
|
||||
[[nodiscard]] int GetDimension() const noexcept;
|
||||
[[nodiscard]] int GetDofCount() const noexcept;
|
||||
[[nodiscard]] mfem::Ordering::Type GetOrdering() const noexcept;
|
||||
|
||||
private:
|
||||
const mfem::FiniteElement *m_element;
|
||||
mfem::DenseMatrix m_dof_matrix;
|
||||
int m_dimension;
|
||||
mfem::Ordering::Type m_ordering;
|
||||
};
|
||||
private:
|
||||
const mfem::FiniteElement *m_element;
|
||||
mfem::DenseMatrix m_dof_matrix;
|
||||
int m_dimension;
|
||||
mfem::Ordering::Type m_ordering;
|
||||
};
|
||||
|
||||
struct CompactificationPointData {
|
||||
double coordinate{0.0};
|
||||
mfem::Vector coordinate_gradient;
|
||||
};
|
||||
struct CompactificationPointData {
|
||||
double coordinate{0.0};
|
||||
mfem::Vector coordinate_gradient;
|
||||
};
|
||||
|
||||
[[nodiscard]] ElementDisplacementData
|
||||
ElementDisplacementDataFromElementVDofs(const mfem::FiniteElement &element,
|
||||
const mfem::Vector &displacement_dofs);
|
||||
[[nodiscard]] ElementDisplacementData ElementDisplacementDataFromElementVDofs(
|
||||
const mfem::FiniteElement &element,
|
||||
const mfem::Vector &displacement_dofs
|
||||
);
|
||||
|
||||
class ElementCompactificationData {
|
||||
public:
|
||||
ElementCompactificationData(const mfem::FiniteElement &element,
|
||||
const mfem::Vector &dofs);
|
||||
class ElementCompactificationData {
|
||||
public:
|
||||
ElementCompactificationData(
|
||||
const mfem::FiniteElement &element,
|
||||
const mfem::Vector &dofs
|
||||
);
|
||||
|
||||
[[nodiscard]] const mfem::FiniteElement &GetElement() const noexcept;
|
||||
[[nodiscard]] const mfem::Vector &GetDofs() const noexcept;
|
||||
[[nodiscard]] int GetDofCount() const noexcept;
|
||||
[[nodiscard]] const mfem::FiniteElement &GetElement() const noexcept;
|
||||
[[nodiscard]] const mfem::Vector &GetDofs() const noexcept;
|
||||
[[nodiscard]] int GetDofCount() const noexcept;
|
||||
|
||||
private:
|
||||
const mfem::FiniteElement *m_element;
|
||||
mfem::Vector m_dofs;
|
||||
};
|
||||
private:
|
||||
const mfem::FiniteElement *m_element;
|
||||
mfem::Vector m_dofs;
|
||||
};
|
||||
|
||||
struct ElementMappingData {
|
||||
const ElementDisplacementData &displacement;
|
||||
const ElementCompactificationData &compactification;
|
||||
};
|
||||
struct ElementMappingData {
|
||||
const ElementDisplacementData &displacement;
|
||||
const ElementCompactificationData &compactification;
|
||||
};
|
||||
|
||||
class DomainMapper {
|
||||
public:
|
||||
class Workspace {
|
||||
public:
|
||||
explicit Workspace(int dimension = 3);
|
||||
class DomainMapper {
|
||||
public:
|
||||
class Workspace {
|
||||
public:
|
||||
explicit Workspace(int dimension = 3);
|
||||
|
||||
void SetDimension(int dimension);
|
||||
void SetDimension(int dimension);
|
||||
|
||||
[[nodiscard]] int GetDimension() const noexcept;
|
||||
[[nodiscard]] int GetDimension() const noexcept;
|
||||
|
||||
private:
|
||||
friend class DomainMapper;
|
||||
private:
|
||||
friend class DomainMapper;
|
||||
|
||||
int m_dimension;
|
||||
int m_dimension;
|
||||
|
||||
mfem::Vector m_shape;
|
||||
mfem::DenseMatrix m_mesh_dshape;
|
||||
mfem::Vector m_field_value;
|
||||
mfem::DenseMatrix m_field_jacobian;
|
||||
mfem::Vector m_shape;
|
||||
mfem::DenseMatrix m_mesh_dshape;
|
||||
mfem::Vector m_field_value;
|
||||
mfem::DenseMatrix m_field_jacobian;
|
||||
|
||||
mfem::Vector m_compactification_shape;
|
||||
mfem::DenseMatrix m_compactification_dshape;
|
||||
CompactificationPointData m_compactification_point;
|
||||
mfem::Vector m_compactification_shape;
|
||||
mfem::DenseMatrix m_compactification_dshape;
|
||||
CompactificationPointData m_compactification_point;
|
||||
|
||||
mfem::Vector m_reference_normal;
|
||||
mfem::Vector m_mapped_normal;
|
||||
mfem::DenseMatrix m_full_element_jacobian;
|
||||
mfem::Vector m_reference_normal;
|
||||
mfem::Vector m_mapped_normal;
|
||||
mfem::DenseMatrix m_full_element_jacobian;
|
||||
|
||||
mfem::Vector m_vector_temp;
|
||||
mfem::DenseMatrix m_matrix_temp_1;
|
||||
mfem::DenseMatrix m_matrix_temp_2;
|
||||
mfem::Vector m_vector_temp;
|
||||
mfem::DenseMatrix m_matrix_temp_1;
|
||||
mfem::DenseMatrix m_matrix_temp_2;
|
||||
|
||||
compactification::ExteriorMapResult m_exterior_result;
|
||||
compactification::ExteriorMapVariation m_exterior_variation;
|
||||
};
|
||||
compactification::ExteriorMapResult m_exterior_result;
|
||||
compactification::ExteriorMapVariation m_exterior_variation;
|
||||
};
|
||||
|
||||
public:
|
||||
DomainMapper(
|
||||
utils::DomainMapperOptions options,
|
||||
std::unique_ptr<const compactification::ExteriorDomainMap> exterior_map);
|
||||
public:
|
||||
DomainMapper(
|
||||
utils::DomainMapperOptions options,
|
||||
std::unique_ptr<const compactification::ExteriorDomainMap> exterior_map
|
||||
);
|
||||
|
||||
DomainMapper(const DomainMapper &) = delete;
|
||||
DomainMapper &operator=(const DomainMapper &) = delete;
|
||||
DomainMapper(DomainMapper &&) = default;
|
||||
DomainMapper &operator=(DomainMapper &&) = default;
|
||||
DomainMapper(const DomainMapper &) = delete;
|
||||
DomainMapper &operator=(const DomainMapper &) = delete;
|
||||
DomainMapper(DomainMapper &&) = default;
|
||||
DomainMapper &operator=(DomainMapper &&) = default;
|
||||
|
||||
[[nodiscard]] MappingStatus
|
||||
EvaluatePoint(const ElementMappingData &element_data,
|
||||
mfem::ElementTransformation &transformation,
|
||||
const mfem::IntegrationPoint &integration_point,
|
||||
Workspace &workspace, MappingPointContext &context) const;
|
||||
[[nodiscard]] MappingStatus EvaluatePoint(
|
||||
const ElementMappingData &element_data,
|
||||
mfem::ElementTransformation &transformation,
|
||||
const mfem::IntegrationPoint &integration_point,
|
||||
Workspace &workspace,
|
||||
MappingPointContext &context
|
||||
) const;
|
||||
|
||||
[[nodiscard]] MappingStatus
|
||||
EvaluateVolume(const ElementMappingData &element_data,
|
||||
mfem::ElementTransformation &transformation,
|
||||
const mfem::IntegrationPoint &integration_point,
|
||||
Workspace &workspace, VolumeMappingContext &context) const;
|
||||
[[nodiscard]] MappingStatus EvaluateVolume(
|
||||
const ElementMappingData &element_data,
|
||||
mfem::ElementTransformation &transformation,
|
||||
const mfem::IntegrationPoint &integration_point,
|
||||
Workspace &workspace,
|
||||
VolumeMappingContext &context
|
||||
) const;
|
||||
|
||||
[[nodiscard]] MappingStatus
|
||||
EvaluateFace(const ElementMappingData &element_data,
|
||||
mfem::FaceElementTransformations &transformation,
|
||||
FaceElementSide side,
|
||||
const mfem::IntegrationPoint &integration_point,
|
||||
Workspace &workspace, FaceMappingContext &context) const;
|
||||
[[nodiscard]] MappingStatus EvaluateFace(
|
||||
const ElementMappingData &element_data,
|
||||
mfem::FaceElementTransformations &transformation,
|
||||
FaceElementSide side,
|
||||
const mfem::IntegrationPoint &integration_point,
|
||||
Workspace &workspace,
|
||||
FaceMappingContext &context
|
||||
) const;
|
||||
|
||||
[[nodiscard]] MappingStatus
|
||||
EvaluatePointVariation(const ElementMappingData &element_data,
|
||||
const ElementDisplacementData &direction,
|
||||
mfem::ElementTransformation &transformation,
|
||||
const mfem::IntegrationPoint &integration_point,
|
||||
const MappingPointContext &base_context,
|
||||
Workspace &workspace,
|
||||
MappingPointVariation &variation) const;
|
||||
[[nodiscard]] MappingStatus EvaluatePointVariation(
|
||||
const ElementMappingData &element_data,
|
||||
const ElementDisplacementData &direction,
|
||||
mfem::ElementTransformation &transformation,
|
||||
const mfem::IntegrationPoint &integration_point,
|
||||
const MappingPointContext &base_context,
|
||||
Workspace &workspace,
|
||||
MappingPointVariation &variation
|
||||
) const;
|
||||
|
||||
[[nodiscard]] MappingStatus
|
||||
EvaluateVolumeVariation(const ElementMappingData &element_data,
|
||||
const ElementDisplacementData &direction,
|
||||
mfem::ElementTransformation &transformation,
|
||||
const mfem::IntegrationPoint &integration_point,
|
||||
const VolumeMappingContext &base_context,
|
||||
Workspace &workspace,
|
||||
VolumeMappingVariation &variation) const;
|
||||
[[nodiscard]] MappingStatus EvaluateVolumeVariation(
|
||||
const ElementMappingData &element_data,
|
||||
const ElementDisplacementData &direction,
|
||||
mfem::ElementTransformation &transformation,
|
||||
const mfem::IntegrationPoint &integration_point,
|
||||
const VolumeMappingContext &base_context,
|
||||
Workspace &workspace,
|
||||
VolumeMappingVariation &variation
|
||||
) const;
|
||||
|
||||
[[nodiscard]] MappingStatus EvaluateFaceVariation(
|
||||
const ElementMappingData &element_data,
|
||||
const ElementDisplacementData &direction,
|
||||
mfem::FaceElementTransformations &transformation, FaceElementSide side,
|
||||
const mfem::IntegrationPoint &integration_point,
|
||||
const FaceMappingContext &base_context, Workspace &workspace,
|
||||
FaceMappingVariation &variation) const;
|
||||
[[nodiscard]] MappingStatus EvaluateFaceVariation(
|
||||
const ElementMappingData &element_data,
|
||||
const ElementDisplacementData &direction,
|
||||
mfem::FaceElementTransformations &transformation,
|
||||
FaceElementSide side,
|
||||
const mfem::IntegrationPoint &integration_point,
|
||||
const FaceMappingContext &base_context,
|
||||
Workspace &workspace,
|
||||
FaceMappingVariation &variation
|
||||
) const;
|
||||
|
||||
[[nodiscard]] bool IsCompactifiedElement(
|
||||
const mfem::ElementTransformation &transformation) const noexcept;
|
||||
[[nodiscard]] int GetDimension() const noexcept;
|
||||
[[nodiscard]] const compactification::ExteriorDomainMap &
|
||||
GetExteriorMap() const noexcept;
|
||||
[[nodiscard]] bool IsCompactifiedElement(const mfem::ElementTransformation &transformation) const noexcept;
|
||||
[[nodiscard]] int GetDimension() const noexcept;
|
||||
[[nodiscard]] const compactification::ExteriorDomainMap &GetExteriorMap() const noexcept;
|
||||
|
||||
private:
|
||||
void ValidateElementData(const ElementMappingData &element_data) const;
|
||||
private:
|
||||
void ValidateElementData(const ElementMappingData &element_data) const;
|
||||
|
||||
void EvaluateField(const ElementDisplacementData &field,
|
||||
mfem::ElementTransformation &transformation,
|
||||
const mfem::IntegrationPoint &integration_point,
|
||||
Workspace &workspace, mfem::Vector &value,
|
||||
mfem::DenseMatrix &jacobian) const;
|
||||
void EvaluateField(
|
||||
const ElementDisplacementData &field,
|
||||
mfem::ElementTransformation &transformation,
|
||||
const mfem::IntegrationPoint &integration_point,
|
||||
Workspace &workspace,
|
||||
mfem::Vector &value,
|
||||
mfem::DenseMatrix &jacobian
|
||||
) const;
|
||||
|
||||
[[nodiscard]] MappingStatus EvaluateCompactificationCoordinate(
|
||||
const ElementCompactificationData &compactification,
|
||||
mfem::ElementTransformation &transformation,
|
||||
const mfem::IntegrationPoint &integration_point, Workspace &workspace,
|
||||
CompactificationPointData &point_data) const;
|
||||
[[nodiscard]] MappingStatus EvaluateCompactificationCoordinate(
|
||||
const ElementCompactificationData &compactification,
|
||||
mfem::ElementTransformation &transformation,
|
||||
const mfem::IntegrationPoint &integration_point,
|
||||
Workspace &workspace,
|
||||
CompactificationPointData &point_data
|
||||
) const;
|
||||
|
||||
[[nodiscard]] static mfem::ElementTransformation &
|
||||
SelectFaceElementTransformation(
|
||||
mfem::FaceElementTransformations &transformation, FaceElementSide side);
|
||||
[[nodiscard]] static mfem::ElementTransformation &SelectFaceElementTransformation(
|
||||
mfem::FaceElementTransformations &transformation,
|
||||
FaceElementSide side
|
||||
);
|
||||
|
||||
[[nodiscard]] static const mfem::IntegrationPoint &
|
||||
SelectFaceElementIntegrationPoint(
|
||||
mfem::FaceElementTransformations &transformation, FaceElementSide side);
|
||||
[[nodiscard]] static const mfem::IntegrationPoint &SelectFaceElementIntegrationPoint(
|
||||
mfem::FaceElementTransformations &transformation,
|
||||
FaceElementSide side
|
||||
);
|
||||
|
||||
utils::DomainMapperOptions m_options;
|
||||
std::unique_ptr<const compactification::ExteriorDomainMap> m_exterior_map;
|
||||
};
|
||||
utils::DomainMapperOptions m_options;
|
||||
std::unique_ptr<const compactification::ExteriorDomainMap> m_exterior_map;
|
||||
};
|
||||
|
||||
class GridFunctionMappingEvaluator {
|
||||
public:
|
||||
/*
|
||||
* The evaluator references the supplied grid functions and caches copies of
|
||||
* their element-local DOFs. Call InvalidateCache() or Refresh() after either
|
||||
* grid function's values are modified. Finite-element-space sequence changes
|
||||
* are detected automatically.
|
||||
*
|
||||
* This object owns mutable workspace and cache state and is not thread-safe.
|
||||
*/
|
||||
GridFunctionMappingEvaluator(
|
||||
const DomainMapper &mapper,
|
||||
const mfem::GridFunction &displacement,
|
||||
const mfem::GridFunction &compactification_coordinate);
|
||||
class GridFunctionMappingEvaluator {
|
||||
public:
|
||||
/*
|
||||
* The evaluator references the supplied grid functions and caches copies of
|
||||
* their element-local DOFs. Call InvalidateCache() or Refresh() after either
|
||||
* grid function's values are modified. Finite-element-space sequence changes
|
||||
* are detected automatically.
|
||||
*
|
||||
* This object owns mutable workspace and cache state and is not thread-safe.
|
||||
*/
|
||||
GridFunctionMappingEvaluator(
|
||||
const DomainMapper &mapper,
|
||||
const mfem::GridFunction &displacement,
|
||||
const mfem::GridFunction &compactification_coordinate
|
||||
);
|
||||
|
||||
/*
|
||||
* Discard all element-local field data. The next evaluation reloads its
|
||||
* requested element lazily. This operation is idempotent.
|
||||
*/
|
||||
void InvalidateCache() noexcept;
|
||||
/*
|
||||
* Discard all element-local field data. The next evaluation reloads its
|
||||
* requested element lazily. This operation is idempotent.
|
||||
*/
|
||||
void InvalidateCache() noexcept;
|
||||
|
||||
/*
|
||||
* Reload the currently cached element immediately. If no element has been
|
||||
* evaluated yet, Refresh() is a validated no-op. If either finite-element
|
||||
* space changed sequence, the old element ID is discarded and the next
|
||||
* evaluation reloads lazily against the updated spaces.
|
||||
*/
|
||||
void Refresh();
|
||||
/*
|
||||
* Reload the currently cached element immediately. If no element has been
|
||||
* evaluated yet, Refresh() is a validated no-op. If either finite-element
|
||||
* space changed sequence, the old element ID is discarded and the next
|
||||
* evaluation reloads lazily against the updated spaces.
|
||||
*/
|
||||
void Refresh();
|
||||
|
||||
[[nodiscard]] MappingStatus
|
||||
EvaluatePoint(mfem::ElementTransformation &transformation,
|
||||
const mfem::IntegrationPoint &integration_point,
|
||||
MappingPointContext &context);
|
||||
[[nodiscard]] MappingStatus EvaluatePoint(
|
||||
mfem::ElementTransformation &transformation,
|
||||
const mfem::IntegrationPoint &integration_point,
|
||||
MappingPointContext &context
|
||||
);
|
||||
|
||||
[[nodiscard]] MappingStatus
|
||||
EvaluateVolume(mfem::ElementTransformation &transformation,
|
||||
const mfem::IntegrationPoint &integration_point,
|
||||
VolumeMappingContext &context);
|
||||
[[nodiscard]] MappingStatus EvaluateVolume(
|
||||
mfem::ElementTransformation &transformation,
|
||||
const mfem::IntegrationPoint &integration_point,
|
||||
VolumeMappingContext &context
|
||||
);
|
||||
|
||||
[[nodiscard]] MappingStatus
|
||||
EvaluateFace(mfem::FaceElementTransformations &transformation,
|
||||
FaceElementSide side,
|
||||
const mfem::IntegrationPoint &integration_point,
|
||||
FaceMappingContext &context);
|
||||
[[nodiscard]] MappingStatus EvaluateFace(
|
||||
mfem::FaceElementTransformations &transformation,
|
||||
FaceElementSide side,
|
||||
const mfem::IntegrationPoint &integration_point,
|
||||
FaceMappingContext &context
|
||||
);
|
||||
|
||||
[[nodiscard]] VolumeQuadratureContext
|
||||
GetQuadratureContext(mfem::ElementTransformation &transformation,
|
||||
const mfem::IntegrationPoint &integration_point);
|
||||
[[nodiscard]] VolumeQuadratureContext GetQuadratureContext(
|
||||
mfem::ElementTransformation &transformation,
|
||||
const mfem::IntegrationPoint &integration_point
|
||||
);
|
||||
|
||||
[[nodiscard]] FaceQuadratureContext
|
||||
GetFaceQuadratureContext(
|
||||
mfem::FaceElementTransformations &transformation,
|
||||
const mfem::IntegrationPoint &integration_point,
|
||||
FaceElementSide side = FaceElementSide::element_1);
|
||||
[[nodiscard]] FaceQuadratureContext GetFaceQuadratureContext(
|
||||
mfem::FaceElementTransformations &transformation,
|
||||
const mfem::IntegrationPoint &integration_point,
|
||||
FaceElementSide side = FaceElementSide::element_1
|
||||
);
|
||||
|
||||
void GetPhysicalPoint(mfem::ElementTransformation &transformation,
|
||||
const mfem::IntegrationPoint &integration_point,
|
||||
mfem::Vector &physical_position);
|
||||
void GetPhysicalPoint(
|
||||
mfem::ElementTransformation &transformation,
|
||||
const mfem::IntegrationPoint &integration_point,
|
||||
mfem::Vector &physical_position
|
||||
);
|
||||
|
||||
private:
|
||||
void ValidateFieldBindings() const;
|
||||
[[nodiscard]] bool InvalidateForChangedSpaces();
|
||||
void LoadElement(int element_id);
|
||||
private:
|
||||
void ValidateFieldBindings() const;
|
||||
[[nodiscard]] bool InvalidateForChangedSpaces();
|
||||
void LoadElement(int element_id);
|
||||
|
||||
const DomainMapper &m_mapper;
|
||||
const mfem::GridFunction &m_displacement;
|
||||
const mfem::GridFunction &m_compactification_coordinate;
|
||||
const mfem::FiniteElementSpace *m_displacement_space;
|
||||
const mfem::FiniteElementSpace *m_compactification_space;
|
||||
long m_displacement_space_sequence;
|
||||
long m_compactification_space_sequence;
|
||||
DomainMapper::Workspace m_workspace;
|
||||
const DomainMapper &m_mapper;
|
||||
const mfem::GridFunction &m_displacement;
|
||||
const mfem::GridFunction &m_compactification_coordinate;
|
||||
const mfem::FiniteElementSpace *m_displacement_space;
|
||||
const mfem::FiniteElementSpace *m_compactification_space;
|
||||
long m_displacement_space_sequence;
|
||||
long m_compactification_space_sequence;
|
||||
DomainMapper::Workspace m_workspace;
|
||||
|
||||
mfem::Array<int> m_displacement_dofs;
|
||||
mfem::Array<int> m_compactification_dofs;
|
||||
mfem::Vector m_element_displacement;
|
||||
mfem::Vector m_element_compactification;
|
||||
std::unique_ptr<ElementDisplacementData> m_displacement_data;
|
||||
std::unique_ptr<ElementCompactificationData> m_compactification_data;
|
||||
int m_cached_element_id{-1};
|
||||
};
|
||||
mfem::Array<int> m_displacement_dofs;
|
||||
mfem::Array<int> m_compactification_dofs;
|
||||
mfem::Vector m_element_displacement;
|
||||
mfem::Vector m_element_compactification;
|
||||
std::unique_ptr<ElementDisplacementData> m_displacement_data;
|
||||
std::unique_ptr<ElementCompactificationData> m_compactification_data;
|
||||
int m_cached_element_id{-1};
|
||||
};
|
||||
|
||||
} // namespace mean_field::mapping
|
||||
|
||||
@@ -54,10 +54,19 @@ export import :operators.prepared_displacement_residual;
|
||||
export import :model.structure_profile;
|
||||
export import :model.structure.base;
|
||||
export import :model.structure.polytropic;
|
||||
export import :eos.base;
|
||||
export import :eos.quantities;
|
||||
export import :eos.relations;
|
||||
export import :eos.concepts;
|
||||
export import :eos.evaluation;
|
||||
export import :eos.pressure_surface;
|
||||
export import :eos.runtime;
|
||||
export import :eos.polytrope;
|
||||
export import :surface.base;
|
||||
export import :surface.isobaric;
|
||||
export import :surface.constant;
|
||||
export import :surface.dependencies;
|
||||
export import :surface.compiled;
|
||||
export import :surface.compiler;
|
||||
export import :model.stellar;
|
||||
export import :operators.prepared_mass_normalization;
|
||||
export import :operators.prepared_centering_constraint;
|
||||
export import :operators.prepared_surface_constraint;
|
||||
export import :operators.prepared_stellar_equilibrium;
|
||||
|
||||
@@ -7,62 +7,84 @@ module;
|
||||
|
||||
export module mean_field:model.stellar;
|
||||
|
||||
export import :eos.base;
|
||||
export import :eos.runtime;
|
||||
export import :model.structure.base;
|
||||
export import :surface.base;
|
||||
export import :surface.compiler;
|
||||
|
||||
export namespace mean_field::models {
|
||||
template <typename Candidate>
|
||||
concept StructurePrescription =
|
||||
std::derived_from<std::remove_cvref_t<Candidate>, mean_field::models::structure::StructureBase>;
|
||||
namespace detail {
|
||||
template <typename Candidate>
|
||||
concept ConstEquationOfStateReference =
|
||||
std::is_lvalue_reference_v<Candidate> && std::is_const_v<std::remove_reference_t<Candidate>> &&
|
||||
eos::EquationOfStateModel<std::remove_cvref_t<Candidate>>;
|
||||
} // namespace detail
|
||||
|
||||
template <typename Candidate>
|
||||
concept SurfacePrescription = std::derived_from<std::remove_cvref_t<Candidate>, mean_field::surface::SurfaceBase>;
|
||||
concept StructurePrescription = requires(
|
||||
const std::remove_cvref_t<Candidate> &structurePrescription,
|
||||
const structure::StructureSeedRequest &seedRequest
|
||||
) {
|
||||
{ structurePrescription.equationOfState() } noexcept -> detail::ConstEquationOfStateReference;
|
||||
{ structurePrescription.targetMass() } noexcept -> std::same_as<double>;
|
||||
{ structurePrescription.makeInitialSeed(seedRequest) } -> std::same_as<structure::StructureSeed>;
|
||||
{ structurePrescription.validate() } -> std::same_as<void>;
|
||||
};
|
||||
|
||||
/*
|
||||
* Public ownership facade for a physical structure prescription and its
|
||||
* stellar-surface prescription.
|
||||
*
|
||||
* The concrete prescriptions are allocated once at construction. Their
|
||||
* stable addresses allow future prepared operators and contexts to borrow
|
||||
* references without making ownership part of the user-facing API.
|
||||
*/
|
||||
template <StructurePrescription Candidate>
|
||||
using StructureEquationOfStateT =
|
||||
std::remove_cvref_t<decltype(std::declval<const std::remove_cvref_t<Candidate> &>().equationOfState())>;
|
||||
|
||||
template <typename Candidate, typename EquationOfState>
|
||||
concept SurfacePrescription =
|
||||
surface::ConstantPressureSurfaceType<Candidate> &&
|
||||
surface::PressureSurfaceCompilable<surface::BarotropicSurfaceFormulation, std::remove_cvref_t<EquationOfState>>;
|
||||
|
||||
template <StructurePrescription Structure>
|
||||
requires SurfacePrescription<surface::ConstantPressureSurface, StructureEquationOfStateT<Structure>>
|
||||
class StellarModel final {
|
||||
public:
|
||||
template <
|
||||
StructurePrescription StructureType,
|
||||
SurfacePrescription SurfaceType>
|
||||
using StructurePrescriptionType = Structure;
|
||||
using SurfacePrescriptionType = surface::ConstantPressureSurface;
|
||||
using EquationOfStateType = StructureEquationOfStateT<Structure>;
|
||||
using SurfaceConstraintType =
|
||||
surface::CompiledPressureSurfaceConstraintT<surface::BarotropicSurfaceFormulation, EquationOfStateType>;
|
||||
|
||||
template <typename StructureArgument>
|
||||
requires std::same_as<
|
||||
std::remove_cvref_t<StructureArgument>,
|
||||
Structure>
|
||||
explicit StellarModel(
|
||||
StructureType &&structurePrescription,
|
||||
SurfaceType &&surfacePrescription
|
||||
StructureArgument &&structurePrescription,
|
||||
const surface::ConstantPressureSurface surfacePrescription
|
||||
)
|
||||
: StellarModel(
|
||||
std::make_unique<std::remove_cvref_t<StructureType>>(
|
||||
std::forward<StructureType>(structurePrescription)
|
||||
),
|
||||
std::make_unique<std::remove_cvref_t<SurfaceType>>(std::forward<SurfaceType>(surfacePrescription))
|
||||
: m_structurePrescription(
|
||||
std::make_unique<Structure>(std::forward<StructureArgument>(structurePrescription))
|
||||
),
|
||||
m_surfacePrescription(std::make_unique<surface::ConstantPressureSurface>(surfacePrescription)),
|
||||
m_compiledSurfaceConstraint(
|
||||
std::make_unique<SurfaceConstraintType>(validateAndCompileSurface(
|
||||
*m_structurePrescription,
|
||||
*m_surfacePrescription
|
||||
))
|
||||
) {
|
||||
}
|
||||
|
||||
~StellarModel() = default;
|
||||
|
||||
StellarModel(const StellarModel &) = delete;
|
||||
|
||||
StellarModel &operator=(const StellarModel &) = delete;
|
||||
|
||||
StellarModel(StellarModel &&) noexcept = default;
|
||||
|
||||
StellarModel &operator=(StellarModel &&) noexcept = default;
|
||||
|
||||
[[nodiscard]] const mean_field::models::structure::StructureBase &structurePrescription() const noexcept {
|
||||
[[nodiscard]] const Structure &structurePrescription() const noexcept {
|
||||
return *m_structurePrescription;
|
||||
}
|
||||
|
||||
[[nodiscard]] const mean_field::surface::SurfaceBase &surfacePrescription() const noexcept {
|
||||
[[nodiscard]] const surface::ConstantPressureSurface &surfacePrescription() const noexcept {
|
||||
return *m_surfacePrescription;
|
||||
}
|
||||
|
||||
[[nodiscard]] const mean_field::eos::EquationOfState &equationOfState() const noexcept {
|
||||
[[nodiscard]] const EquationOfStateType &equationOfState() const noexcept {
|
||||
return m_structurePrescription->equationOfState();
|
||||
}
|
||||
|
||||
@@ -70,45 +92,99 @@ export namespace mean_field::models {
|
||||
return m_structurePrescription->targetMass();
|
||||
}
|
||||
|
||||
[[nodiscard]] mean_field::models::structure::StructureSeed
|
||||
makeInitialSeed(const mean_field::models::structure::StructureSeedRequest &request) const {
|
||||
[[nodiscard]] structure::StructureSeed makeInitialSeed(const structure::StructureSeedRequest &request) const {
|
||||
return m_structurePrescription->makeInitialSeed(request);
|
||||
}
|
||||
|
||||
[[nodiscard]] const mean_field::surface::ResolvedSurfaceCondition &resolvedSurfaceCondition() const noexcept {
|
||||
return m_resolvedSurfaceCondition;
|
||||
[[nodiscard]] const SurfaceConstraintType &compiledSurfaceConstraint() const noexcept {
|
||||
return *m_compiledSurfaceConstraint;
|
||||
}
|
||||
|
||||
private:
|
||||
explicit StellarModel(
|
||||
std::unique_ptr<mean_field::models::structure::StructureBase> structurePrescription,
|
||||
std::unique_ptr<mean_field::surface::SurfaceBase> surfacePrescription
|
||||
)
|
||||
: m_structurePrescription(std::move(structurePrescription)),
|
||||
m_surfacePrescription(std::move(surfacePrescription)),
|
||||
m_resolvedSurfaceCondition(validateAndResolve(
|
||||
*m_structurePrescription,
|
||||
*m_surfacePrescription
|
||||
)) {
|
||||
}
|
||||
|
||||
[[nodiscard]] static mean_field::surface::ResolvedSurfaceCondition validateAndResolve(
|
||||
const mean_field::models::structure::StructureBase &structurePrescription,
|
||||
const mean_field::surface::SurfaceBase &surfacePrescription
|
||||
[[nodiscard]] static SurfaceConstraintType validateAndCompileSurface(
|
||||
const Structure &structurePrescription,
|
||||
const surface::ConstantPressureSurface &surfacePrescription
|
||||
) {
|
||||
structurePrescription.validate();
|
||||
|
||||
const mean_field::eos::EquationOfState &equationOfState = structurePrescription.equationOfState();
|
||||
|
||||
surfacePrescription.validate(equationOfState);
|
||||
|
||||
return surfacePrescription.resolve(equationOfState);
|
||||
return surface::compilePressureSurfaceConstraint<surface::BarotropicSurfaceFormulation>(
|
||||
surfacePrescription, structurePrescription.equationOfState()
|
||||
);
|
||||
}
|
||||
|
||||
std::unique_ptr<mean_field::models::structure::StructureBase> m_structurePrescription;
|
||||
std::unique_ptr<Structure> m_structurePrescription;
|
||||
std::unique_ptr<surface::ConstantPressureSurface> m_surfacePrescription;
|
||||
std::unique_ptr<SurfaceConstraintType> m_compiledSurfaceConstraint;
|
||||
};
|
||||
|
||||
std::unique_ptr<mean_field::surface::SurfaceBase> m_surfacePrescription;
|
||||
template <typename Structure>
|
||||
StellarModel(
|
||||
Structure &&,
|
||||
surface::ConstantPressureSurface
|
||||
) -> StellarModel<std::remove_cvref_t<Structure>>;
|
||||
|
||||
mean_field::surface::ResolvedSurfaceCondition m_resolvedSurfaceCondition;
|
||||
namespace detail {
|
||||
template <typename Candidate> struct IsStellarModel : std::false_type { };
|
||||
|
||||
template <typename Structure> struct IsStellarModel<StellarModel<Structure>> : std::true_type { };
|
||||
} // namespace detail
|
||||
|
||||
template <typename Candidate>
|
||||
concept StellarModelType = detail::IsStellarModel<std::remove_cvref_t<Candidate>>::value;
|
||||
|
||||
class StellarModelView final {
|
||||
public:
|
||||
template <typename Model>
|
||||
requires StellarModelType<Model> &&
|
||||
eos::RuntimeEquationOfStateModel<typename std::remove_cvref_t<Model>::EquationOfStateType>
|
||||
explicit StellarModelView(Model &model) noexcept
|
||||
: m_equationOfState(model.equationOfState()),
|
||||
m_structurePrescription(std::addressof(model.structurePrescription())),
|
||||
m_makeInitialSeed(&makeInitialSeedFor<typename std::remove_cvref_t<Model>::StructurePrescriptionType>),
|
||||
m_targetMass(model.targetMass()),
|
||||
m_surfaceCondition(model.compiledSurfaceConstraint().descriptor()),
|
||||
m_surfaceDependencies(model.compiledSurfaceConstraint().runtimeDependencies()) {
|
||||
}
|
||||
|
||||
[[nodiscard]] eos::EquationOfStateView equationOfState() const noexcept {
|
||||
return m_equationOfState;
|
||||
}
|
||||
|
||||
[[nodiscard]] double targetMass() const noexcept {
|
||||
return m_targetMass;
|
||||
}
|
||||
|
||||
[[nodiscard]] structure::StructureSeed makeInitialSeed(const structure::StructureSeedRequest &request) const {
|
||||
return m_makeInitialSeed(m_structurePrescription, request);
|
||||
}
|
||||
|
||||
[[nodiscard]] surface::PressureSurfaceDescriptor surfaceCondition() const noexcept {
|
||||
return m_surfaceCondition;
|
||||
}
|
||||
|
||||
[[nodiscard]] surface::RuntimeSurfaceConstraintDependencies surfaceDependencies() const noexcept {
|
||||
return m_surfaceDependencies;
|
||||
}
|
||||
|
||||
private:
|
||||
using MakeInitialSeedFunction = structure::StructureSeed (*)(
|
||||
const void *,
|
||||
const structure::StructureSeedRequest &
|
||||
);
|
||||
|
||||
template <StructurePrescription Structure>
|
||||
[[nodiscard]] static structure::StructureSeed makeInitialSeedFor(
|
||||
const void *structurePrescription,
|
||||
const structure::StructureSeedRequest &request
|
||||
) {
|
||||
return static_cast<const Structure *>(structurePrescription)->makeInitialSeed(request);
|
||||
}
|
||||
|
||||
eos::EquationOfStateView m_equationOfState;
|
||||
const void *m_structurePrescription;
|
||||
MakeInitialSeedFunction m_makeInitialSeed;
|
||||
double m_targetMass;
|
||||
surface::PressureSurfaceDescriptor m_surfaceCondition;
|
||||
surface::RuntimeSurfaceConstraintDependencies m_surfaceDependencies;
|
||||
};
|
||||
} // namespace mean_field::models
|
||||
|
||||
@@ -12,20 +12,20 @@ export import :model.structure.base;
|
||||
import :utils.misc;
|
||||
|
||||
export namespace mean_field::models::structure {
|
||||
class PolytropicStructure final : public StructureBase {
|
||||
class PolytropicStructure final {
|
||||
public:
|
||||
explicit PolytropicStructure(
|
||||
eos::Polytrope equationOfState,
|
||||
double targetMass
|
||||
);
|
||||
|
||||
[[nodiscard]] const eos::EquationOfState &equationOfState() const noexcept override;
|
||||
[[nodiscard]] const eos::Polytrope &equationOfState() const noexcept;
|
||||
|
||||
[[nodiscard]] double targetMass() const noexcept override;
|
||||
[[nodiscard]] double targetMass() const noexcept;
|
||||
|
||||
[[nodiscard]] StructureSeed makeInitialSeed(const StructureSeedRequest &request) const override;
|
||||
[[nodiscard]] StructureSeed makeInitialSeed(const StructureSeedRequest &request) const;
|
||||
|
||||
void validate() const override;
|
||||
void validate() const;
|
||||
|
||||
private:
|
||||
struct LaneEmdenPoint {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
module;
|
||||
#include <mfem.hpp>
|
||||
export module mean_field:model.structure.base;
|
||||
export import :eos.base;
|
||||
export import :eos.runtime;
|
||||
|
||||
export namespace mean_field::models::structure {
|
||||
struct StructureSeed {
|
||||
@@ -23,7 +23,7 @@ export namespace mean_field::models::structure {
|
||||
public:
|
||||
virtual ~StructureBase() = default;
|
||||
|
||||
[[nodiscard]] virtual const eos::EquationOfState &equationOfState() const noexcept = 0;
|
||||
[[nodiscard]] virtual eos::EquationOfStateView equationOfState() const noexcept = 0;
|
||||
|
||||
[[nodiscard]] virtual double targetMass() const noexcept = 0;
|
||||
|
||||
@@ -34,4 +34,4 @@ export namespace mean_field::models::structure {
|
||||
protected:
|
||||
StructureBase() = default;
|
||||
};
|
||||
} // namespace mean_field::models::structure
|
||||
} // namespace mean_field::models::structure
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
module;
|
||||
|
||||
#include <cmath>
|
||||
#include <utility>
|
||||
|
||||
#include <mfem.hpp>
|
||||
|
||||
export module mean_field:operators.prepared_centering_constraint;
|
||||
|
||||
export import :field.mfem;
|
||||
|
||||
export namespace mean_field::operators {
|
||||
struct PreparedCenteringConstraintReport final {
|
||||
bool cachedCenterDisplacement{false};
|
||||
|
||||
[[nodiscard]] bool DidAnyWork() const noexcept {
|
||||
return cachedCenterDisplacement;
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Strong translational gauge: the material point at the computational
|
||||
* origin has zero displacement. The three corresponding displacement
|
||||
* residual rows replace redundant force-balance rows.
|
||||
*/
|
||||
class PreparedCenteringConstraint final {
|
||||
public:
|
||||
explicit PreparedCenteringConstraint(field::FieldPointDofMap centerRows)
|
||||
: m_centerRows(std::move(centerRows)),
|
||||
m_centerDisplacement(m_centerRows.size()) {
|
||||
}
|
||||
|
||||
[[nodiscard]] PreparedCenteringConstraintReport Prepare(
|
||||
const mfem::Vector &displacement,
|
||||
const bool displacementChanged
|
||||
) {
|
||||
MFEM_VERIFY(
|
||||
displacement.Size() == m_centerRows.field_size(),
|
||||
"The centering constraint received a displacement vector with the wrong size."
|
||||
);
|
||||
|
||||
PreparedCenteringConstraintReport report;
|
||||
if (!m_isPrepared || displacementChanged) {
|
||||
for (int centerIndex = 0; centerIndex < m_centerRows.size(); ++centerIndex) {
|
||||
const double value = displacement(m_centerRows.reduced_dofs()[centerIndex]);
|
||||
MFEM_VERIFY(
|
||||
std::isfinite(value), "The centering constraint received a non-finite center displacement."
|
||||
);
|
||||
m_centerDisplacement(centerIndex) = value;
|
||||
}
|
||||
report.cachedCenterDisplacement = true;
|
||||
}
|
||||
|
||||
m_isPrepared = true;
|
||||
return report;
|
||||
}
|
||||
|
||||
void ApplyResidualRows(mfem::Vector &displacementResidual) const {
|
||||
VerifyPrepared();
|
||||
MFEM_VERIFY(
|
||||
displacementResidual.Size() == m_centerRows.field_size(),
|
||||
"The centering constraint received a displacement residual with the wrong size."
|
||||
);
|
||||
|
||||
for (int centerIndex = 0; centerIndex < m_centerRows.size(); ++centerIndex) {
|
||||
displacementResidual(m_centerRows.reduced_dofs()[centerIndex]) = m_centerDisplacement(centerIndex);
|
||||
}
|
||||
}
|
||||
|
||||
void ApplyJacobianRows(
|
||||
const mfem::Vector &displacementVariation,
|
||||
mfem::Vector &displacementAction
|
||||
) const {
|
||||
VerifyPrepared();
|
||||
MFEM_VERIFY(
|
||||
displacementVariation.Size() == m_centerRows.field_size() &&
|
||||
displacementAction.Size() == m_centerRows.field_size(),
|
||||
"The centering constraint received a Jacobian vector with the wrong size."
|
||||
);
|
||||
|
||||
for (const int centerRow : m_centerRows.reduced_dofs()) {
|
||||
displacementAction(centerRow) = displacementVariation(centerRow);
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] bool IsPrepared() const noexcept {
|
||||
return m_isPrepared;
|
||||
}
|
||||
|
||||
[[nodiscard]] const field::FieldPointDofMap &GetCenterRows() const noexcept {
|
||||
return m_centerRows;
|
||||
}
|
||||
|
||||
private:
|
||||
void VerifyPrepared() const {
|
||||
MFEM_VERIFY(m_isPrepared, "The centering constraint must be prepared before row application.");
|
||||
}
|
||||
|
||||
field::FieldPointDofMap m_centerRows;
|
||||
mfem::Vector m_centerDisplacement;
|
||||
bool m_isPrepared{false};
|
||||
};
|
||||
} // namespace mean_field::operators
|
||||
@@ -1,7 +1,9 @@
|
||||
module;
|
||||
|
||||
#include <compare>
|
||||
#include <concepts>
|
||||
#include <cstdint>
|
||||
#include <type_traits>
|
||||
|
||||
#include <mfem.hpp>
|
||||
|
||||
@@ -16,9 +18,11 @@ export import :operators.context.gravity_field;
|
||||
export import :operators.gravity_field;
|
||||
export import :operators.gravity_field_jacobian;
|
||||
export import :operators.prepared_barotropic_closure;
|
||||
export import :operators.prepared_centering_constraint;
|
||||
export import :operators.prepared_displacement_residual;
|
||||
export import :operators.prepared_hydrostatic_equilibrium;
|
||||
export import :operators.prepared_mass_normalization;
|
||||
export import :operators.prepared_surface_constraint;
|
||||
export import :physics.rigid_rotation;
|
||||
export import :utils.blocks;
|
||||
|
||||
@@ -50,11 +54,14 @@ export namespace mean_field::operators {
|
||||
PreparedHydrostaticEquilibriumReport hydrostatic;
|
||||
PreparedDisplacementResidualReport displacement;
|
||||
PreparedMassNormalizationReport massNormalization;
|
||||
PreparedSurfaceConstraintReport surfaceConstraint;
|
||||
PreparedCenteringConstraintReport centeringConstraint;
|
||||
bool assembledResidual{false};
|
||||
|
||||
[[nodiscard]] bool DidAnyChildWork() const noexcept {
|
||||
return gravity.DidAnyWork() || barotropicClosure.DidAnyWork() || hydrostatic.DidAnyWork() ||
|
||||
displacement.DidAnyWork() || massNormalization.DidAnyWork();
|
||||
displacement.DidAnyWork() || massNormalization.DidAnyWork() || surfaceConstraint.DidAnyWork() ||
|
||||
centeringConstraint.DidAnyWork();
|
||||
}
|
||||
|
||||
[[nodiscard]] bool DidAnyWork() const noexcept {
|
||||
@@ -74,19 +81,27 @@ export namespace mean_field::operators {
|
||||
|
||||
class PreparedStellarEquilibriumOperator final : public mfem::Operator {
|
||||
public:
|
||||
template <models::StellarModelType Model>
|
||||
requires std::same_as<
|
||||
typename std::remove_cvref_t<Model>::EquationOfStateType,
|
||||
eos::Polytrope> &&
|
||||
SingleFieldPressureSurfaceConstraintFor<
|
||||
typename std::remove_cvref_t<Model>::SurfaceConstraintType,
|
||||
field::Enthalpy> &&
|
||||
std::is_lvalue_reference_v<Model &&>
|
||||
PreparedStellarEquilibriumOperator(
|
||||
fem::FEM &f,
|
||||
const mapping::DomainMapper &domainMapper,
|
||||
const eos::Polytrope &equationOfState,
|
||||
double targetMass
|
||||
);
|
||||
|
||||
PreparedStellarEquilibriumOperator(
|
||||
fem::FEM &f,
|
||||
const mapping::DomainMapper &domainMapper,
|
||||
const eos::Polytrope &equationOfState,
|
||||
const models::StellarModel &stellarModel
|
||||
);
|
||||
Model &&stellarModel
|
||||
)
|
||||
: PreparedStellarEquilibriumOperator(
|
||||
f,
|
||||
domainMapper,
|
||||
stellarModel.equationOfState(),
|
||||
stellarModel.targetMass(),
|
||||
PressureSurfaceConstraintView{stellarModel.compiledSurfaceConstraint()}
|
||||
) {
|
||||
}
|
||||
|
||||
PreparedStellarEquilibriumOperator(const PreparedStellarEquilibriumOperator &) = delete;
|
||||
PreparedStellarEquilibriumOperator &operator=(const PreparedStellarEquilibriumOperator &) = delete;
|
||||
@@ -122,6 +137,8 @@ export namespace mean_field::operators {
|
||||
[[nodiscard]] const PreparedHydrostaticEquilibriumOperator &GetHydrostaticOperator() const noexcept;
|
||||
[[nodiscard]] const PreparedDisplacementResidualOperator &GetDisplacementOperator() const noexcept;
|
||||
[[nodiscard]] const PreparedMassNormalizationOperator &GetMassNormalizationOperator() const noexcept;
|
||||
[[nodiscard]] const PreparedPressureSurfaceConstraint &GetSurfaceConstraintOperator() const noexcept;
|
||||
[[nodiscard]] const PreparedCenteringConstraint &GetCenteringConstraintOperator() const noexcept;
|
||||
|
||||
private:
|
||||
struct ConstructionData;
|
||||
@@ -133,6 +150,15 @@ export namespace mean_field::operators {
|
||||
const mapping::DomainMapper &domainMapper,
|
||||
const eos::Polytrope &equationOfState,
|
||||
double targetMass,
|
||||
PressureSurfaceConstraintView surfaceConstraint
|
||||
);
|
||||
|
||||
PreparedStellarEquilibriumOperator(
|
||||
fem::FEM &f,
|
||||
const mapping::DomainMapper &domainMapper,
|
||||
const eos::Polytrope &equationOfState,
|
||||
double targetMass,
|
||||
PressureSurfaceConstraintView surfaceConstraint,
|
||||
ConstructionData constructionData
|
||||
);
|
||||
|
||||
@@ -150,6 +176,8 @@ export namespace mean_field::operators {
|
||||
PreparedHydrostaticEquilibriumOperator m_hydrostaticOperator;
|
||||
PreparedDisplacementResidualOperator m_displacementOperator;
|
||||
PreparedMassNormalizationOperator m_massNormalizationOperator;
|
||||
PreparedPressureSurfaceConstraint m_surfaceConstraintOperator;
|
||||
PreparedCenteringConstraint m_centeringConstraintOperator;
|
||||
|
||||
StellarEquilibriumDependencies m_preparedDependencies;
|
||||
mfem::Vector m_cachedResidual;
|
||||
|
||||
@@ -0,0 +1,235 @@
|
||||
module;
|
||||
|
||||
#include <cmath>
|
||||
#include <concepts>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include <mfem.hpp>
|
||||
|
||||
export module mean_field:operators.prepared_surface_constraint;
|
||||
|
||||
export import :field.mfem;
|
||||
export import :surface.compiled;
|
||||
|
||||
namespace mean_field::operators::detail {
|
||||
template <eos::ThermodynamicQuantityType Quantity> struct SingleQuantitySurfaceState final {
|
||||
eos::QuantityValue<Quantity> quantityValue;
|
||||
|
||||
[[nodiscard]] eos::QuantityValue<Quantity> value(Quantity) const noexcept {
|
||||
return quantityValue;
|
||||
}
|
||||
};
|
||||
} // namespace mean_field::operators::detail
|
||||
|
||||
export namespace mean_field::operators {
|
||||
/*
|
||||
* Runtime enforcement currently supports a pointwise pressure constraint
|
||||
* whose row field is also its sole state field. The concept is expressed
|
||||
* entirely in compiled-constraint metadata: no thermodynamic carrier or
|
||||
* concrete field is selected by this prepared layer.
|
||||
*/
|
||||
template <typename Candidate>
|
||||
concept SingleFieldPressureSurfaceConstraint =
|
||||
requires {
|
||||
typename std::remove_cvref_t<Candidate>::PhysicalQuantity;
|
||||
typename std::remove_cvref_t<Candidate>::CarrierQuantity;
|
||||
typename std::remove_cvref_t<Candidate>::CarrierField;
|
||||
typename std::remove_cvref_t<Candidate>::SurfaceDependencies;
|
||||
} && std::same_as<typename std::remove_cvref_t<Candidate>::PhysicalQuantity, eos::quantity::Pressure> &&
|
||||
std::same_as<
|
||||
typename std::remove_cvref_t<Candidate>::SurfaceDependencies::RowField,
|
||||
typename std::remove_cvref_t<Candidate>::CarrierField> &&
|
||||
std::same_as<
|
||||
typename std::remove_cvref_t<Candidate>::SurfaceDependencies::StateFieldTypes,
|
||||
field::TypeList<typename std::remove_cvref_t<Candidate>::CarrierField>>;
|
||||
|
||||
template <typename Candidate, typename Field>
|
||||
concept SingleFieldPressureSurfaceConstraintFor =
|
||||
SingleFieldPressureSurfaceConstraint<Candidate> &&
|
||||
std::same_as<typename std::remove_cvref_t<Candidate>::SurfaceDependencies::RowField, Field>;
|
||||
|
||||
/*
|
||||
* Non-owning runtime bridge for a statically compiled pressure constraint.
|
||||
* There is one function-pointer dispatch per complete row application;
|
||||
* the concrete loop remains templated so EOS operations can be inlined.
|
||||
*/
|
||||
class PressureSurfaceConstraintView final {
|
||||
public:
|
||||
template <SingleFieldPressureSurfaceConstraint Constraint>
|
||||
explicit PressureSurfaceConstraintView(const Constraint &constraint) noexcept
|
||||
: m_constraint(std::addressof(constraint)),
|
||||
m_applyResidualRows(&applyResidualRows<Constraint>),
|
||||
m_applyJacobianRows(&applyJacobianRows<Constraint>),
|
||||
m_descriptor(constraint.descriptor()) {
|
||||
}
|
||||
|
||||
void ApplyResidualRows(
|
||||
const mfem::Vector &surfaceState,
|
||||
const field::FieldBoundaryDofMap &surfaceRows,
|
||||
mfem::Vector &rowResidual
|
||||
) const {
|
||||
m_applyResidualRows(m_constraint, surfaceState, surfaceRows, rowResidual);
|
||||
}
|
||||
|
||||
void ApplyJacobianRows(
|
||||
const mfem::Vector &surfaceState,
|
||||
const field::FieldBoundaryDofMap &surfaceRows,
|
||||
const mfem::Vector &stateVariation,
|
||||
mfem::Vector &rowAction
|
||||
) const {
|
||||
m_applyJacobianRows(m_constraint, surfaceState, surfaceRows, stateVariation, rowAction);
|
||||
}
|
||||
|
||||
[[nodiscard]] surface::PressureSurfaceDescriptor descriptor() const noexcept {
|
||||
return m_descriptor;
|
||||
}
|
||||
|
||||
private:
|
||||
using ApplyResidualRowsFunction = void (*)(
|
||||
const void *,
|
||||
const mfem::Vector &,
|
||||
const field::FieldBoundaryDofMap &,
|
||||
mfem::Vector &
|
||||
);
|
||||
using ApplyJacobianRowsFunction = void (*)(
|
||||
const void *,
|
||||
const mfem::Vector &,
|
||||
const field::FieldBoundaryDofMap &,
|
||||
const mfem::Vector &,
|
||||
mfem::Vector &
|
||||
);
|
||||
|
||||
template <SingleFieldPressureSurfaceConstraint Constraint>
|
||||
static void applyResidualRows(
|
||||
const void *constraint,
|
||||
const mfem::Vector &surfaceState,
|
||||
const field::FieldBoundaryDofMap &surfaceRows,
|
||||
mfem::Vector &rowResidual
|
||||
) {
|
||||
using CarrierQuantity = typename Constraint::CarrierQuantity;
|
||||
|
||||
for (int surfaceIndex = 0; surfaceIndex < surfaceRows.size(); ++surfaceIndex) {
|
||||
const detail::SingleQuantitySurfaceState<CarrierQuantity> state{
|
||||
eos::QuantityValue<CarrierQuantity>{surfaceState(surfaceIndex)}
|
||||
};
|
||||
rowResidual(surfaceRows.reduced_dofs()[surfaceIndex]) =
|
||||
static_cast<const Constraint *>(constraint)->residual(state);
|
||||
}
|
||||
}
|
||||
|
||||
template <SingleFieldPressureSurfaceConstraint Constraint>
|
||||
static void applyJacobianRows(
|
||||
const void *constraint,
|
||||
const mfem::Vector &surfaceState,
|
||||
const field::FieldBoundaryDofMap &surfaceRows,
|
||||
const mfem::Vector &stateVariation,
|
||||
mfem::Vector &rowAction
|
||||
) {
|
||||
using CarrierQuantity = typename Constraint::CarrierQuantity;
|
||||
|
||||
for (int surfaceIndex = 0; surfaceIndex < surfaceRows.size(); ++surfaceIndex) {
|
||||
const int reducedDof = surfaceRows.reduced_dofs()[surfaceIndex];
|
||||
const detail::SingleQuantitySurfaceState<CarrierQuantity> state{
|
||||
eos::QuantityValue<CarrierQuantity>{surfaceState(surfaceIndex)}
|
||||
};
|
||||
const detail::SingleQuantitySurfaceState<CarrierQuantity> variation{
|
||||
eos::QuantityValue<CarrierQuantity>{stateVariation(reducedDof)}
|
||||
};
|
||||
rowAction(reducedDof) = static_cast<const Constraint *>(constraint)->jacobianAction(state, variation);
|
||||
}
|
||||
}
|
||||
|
||||
const void *m_constraint;
|
||||
ApplyResidualRowsFunction m_applyResidualRows;
|
||||
ApplyJacobianRowsFunction m_applyJacobianRows;
|
||||
surface::PressureSurfaceDescriptor m_descriptor;
|
||||
};
|
||||
|
||||
struct PreparedSurfaceConstraintReport final {
|
||||
bool cachedSurfaceState{false};
|
||||
|
||||
[[nodiscard]] bool DidAnyWork() const noexcept {
|
||||
return cachedSurfaceState;
|
||||
}
|
||||
};
|
||||
|
||||
class PreparedPressureSurfaceConstraint final {
|
||||
public:
|
||||
PreparedPressureSurfaceConstraint(
|
||||
field::FieldBoundaryDofMap surfaceRows,
|
||||
const PressureSurfaceConstraintView constraint
|
||||
)
|
||||
: m_surfaceRows(std::move(surfaceRows)),
|
||||
m_constraint(constraint),
|
||||
m_surfaceState(m_surfaceRows.size()) {
|
||||
}
|
||||
|
||||
[[nodiscard]] PreparedSurfaceConstraintReport Prepare(
|
||||
const mfem::Vector &reducedState,
|
||||
const bool stateChanged
|
||||
) {
|
||||
MFEM_VERIFY(
|
||||
reducedState.Size() == m_surfaceRows.field_size(),
|
||||
"The pressure surface constraint received a state vector with the wrong size."
|
||||
);
|
||||
|
||||
PreparedSurfaceConstraintReport report;
|
||||
if (!m_isPrepared || stateChanged) {
|
||||
for (int surfaceIndex = 0; surfaceIndex < m_surfaceRows.size(); ++surfaceIndex) {
|
||||
const double value = reducedState(m_surfaceRows.reduced_dofs()[surfaceIndex]);
|
||||
MFEM_VERIFY(std::isfinite(value), "The pressure surface constraint received non-finite state.");
|
||||
m_surfaceState(surfaceIndex) = value;
|
||||
}
|
||||
report.cachedSurfaceState = true;
|
||||
}
|
||||
|
||||
m_isPrepared = true;
|
||||
return report;
|
||||
}
|
||||
|
||||
void ApplyResidualRows(mfem::Vector &rowResidual) const {
|
||||
VerifyPrepared();
|
||||
MFEM_VERIFY(
|
||||
rowResidual.Size() == m_surfaceRows.field_size(),
|
||||
"The pressure surface constraint received a residual vector with the wrong size."
|
||||
);
|
||||
m_constraint.ApplyResidualRows(m_surfaceState, m_surfaceRows, rowResidual);
|
||||
}
|
||||
|
||||
void ApplyJacobianRows(
|
||||
const mfem::Vector &stateVariation,
|
||||
mfem::Vector &rowAction
|
||||
) const {
|
||||
VerifyPrepared();
|
||||
MFEM_VERIFY(
|
||||
stateVariation.Size() == m_surfaceRows.field_size() && rowAction.Size() == m_surfaceRows.field_size(),
|
||||
"The pressure surface constraint received a Jacobian vector with the wrong size."
|
||||
);
|
||||
m_constraint.ApplyJacobianRows(m_surfaceState, m_surfaceRows, stateVariation, rowAction);
|
||||
}
|
||||
|
||||
[[nodiscard]] bool IsPrepared() const noexcept {
|
||||
return m_isPrepared;
|
||||
}
|
||||
|
||||
[[nodiscard]] const field::FieldBoundaryDofMap &GetSurfaceRows() const noexcept {
|
||||
return m_surfaceRows;
|
||||
}
|
||||
|
||||
[[nodiscard]] surface::PressureSurfaceDescriptor GetPhysicalCondition() const noexcept {
|
||||
return m_constraint.descriptor();
|
||||
}
|
||||
|
||||
private:
|
||||
void VerifyPrepared() const {
|
||||
MFEM_VERIFY(m_isPrepared, "The pressure surface constraint must be prepared before row application.");
|
||||
}
|
||||
|
||||
field::FieldBoundaryDofMap m_surfaceRows;
|
||||
PressureSurfaceConstraintView m_constraint;
|
||||
mfem::Vector m_surfaceState;
|
||||
bool m_isPrepared{false};
|
||||
};
|
||||
} // namespace mean_field::operators
|
||||
66
libmeanfield/interface/surface/compiled.cppm
Normal file
66
libmeanfield/interface/surface/compiled.cppm
Normal file
@@ -0,0 +1,66 @@
|
||||
module;
|
||||
|
||||
export module mean_field:surface.compiled;
|
||||
|
||||
export import :eos.pressure_surface;
|
||||
export import :surface.constant;
|
||||
export import :surface.dependencies;
|
||||
|
||||
export namespace mean_field::surface {
|
||||
template <
|
||||
eos::EquationOfStateModel EquationOfState,
|
||||
SurfaceConstraintFormulationType Formulation,
|
||||
eos::ThermodynamicRelationType SelectedRelation,
|
||||
typename Dependencies>
|
||||
class CompiledPressureSurfaceConstraint final {
|
||||
public:
|
||||
using PhysicalCondition = ConstantPressureSurface;
|
||||
using PhysicalQuantity = eos::quantity::Pressure;
|
||||
using CarrierQuantity = typename Formulation::CarrierQuantity;
|
||||
using CarrierField = typename Formulation::CarrierField;
|
||||
using Relation = SelectedRelation;
|
||||
using SurfaceDependencies = Dependencies;
|
||||
|
||||
CompiledPressureSurfaceConstraint(
|
||||
const ConstantPressureSurface condition,
|
||||
const EquationOfState &equationOfState
|
||||
) noexcept
|
||||
: m_condition(condition),
|
||||
m_resolvedRelation(
|
||||
equationOfState,
|
||||
condition.targetPressure()
|
||||
) {
|
||||
}
|
||||
|
||||
[[nodiscard]] eos::PressureValue targetPressure() const noexcept {
|
||||
return m_condition.targetPressure();
|
||||
}
|
||||
|
||||
[[nodiscard]] PressureSurfaceDescriptor descriptor() const noexcept {
|
||||
return m_condition.descriptor();
|
||||
}
|
||||
|
||||
[[nodiscard]] static constexpr RuntimeSurfaceConstraintDependencies runtimeDependencies() noexcept {
|
||||
return SurfaceDependencies::runtimeDescription();
|
||||
}
|
||||
|
||||
template <typename SurfaceState> [[nodiscard]] double residual(const SurfaceState &state) const {
|
||||
return state.value(CarrierQuantity{}).value() - m_resolvedRelation.requiredCarrierValue(state).value();
|
||||
}
|
||||
|
||||
template <
|
||||
typename SurfaceState,
|
||||
typename SurfaceVariation>
|
||||
[[nodiscard]] double jacobianAction(
|
||||
const SurfaceState &state,
|
||||
const SurfaceVariation &variation
|
||||
) const {
|
||||
return variation.value(CarrierQuantity{}).value() -
|
||||
m_resolvedRelation.carrierCorrectionJacobianAction(state, variation);
|
||||
}
|
||||
|
||||
private:
|
||||
ConstantPressureSurface m_condition;
|
||||
eos::ResolvedPressureSurfaceRelation<EquationOfState, Relation> m_resolvedRelation;
|
||||
};
|
||||
} // namespace mean_field::surface
|
||||
143
libmeanfield/interface/surface/compiler.cppm
Normal file
143
libmeanfield/interface/surface/compiler.cppm
Normal file
@@ -0,0 +1,143 @@
|
||||
module;
|
||||
|
||||
#include <cstddef>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
|
||||
export module mean_field:surface.compiler;
|
||||
|
||||
export import :surface.compiled;
|
||||
|
||||
export namespace mean_field::surface {
|
||||
namespace detail {
|
||||
template <typename RelationType, typename Formulation, typename EquationOfState>
|
||||
struct PressureSurfaceRelationMatches : std::false_type { };
|
||||
|
||||
template <typename OutputQuantity, typename... InputQuantities, typename Formulation, typename EquationOfState>
|
||||
struct PressureSurfaceRelationMatches<
|
||||
eos::Relation<OutputQuantity, InputQuantities...>,
|
||||
Formulation,
|
||||
EquationOfState>
|
||||
: std::bool_constant<
|
||||
std::same_as<OutputQuantity, typename Formulation::CarrierQuantity> &&
|
||||
(std::same_as<eos::quantity::Pressure, InputQuantities> || ...) &&
|
||||
((std::same_as<eos::quantity::Pressure, InputQuantities> ||
|
||||
(surfaceBindingCount<typename Formulation::StateBindings, InputQuantities> == 1 &&
|
||||
eos::SupportsPartialDerivative<
|
||||
EquationOfState,
|
||||
eos::Relation<OutputQuantity, InputQuantities...>,
|
||||
InputQuantities>)) &&
|
||||
...)> { };
|
||||
|
||||
template <typename Catalog, typename Formulation, typename EquationOfState>
|
||||
struct MatchingPressureSurfaceRelations;
|
||||
|
||||
template <typename... Relations, typename Formulation, typename EquationOfState>
|
||||
struct MatchingPressureSurfaceRelations<eos::RelationCatalog<Relations...>, Formulation, EquationOfState> {
|
||||
using Tuple = decltype(std::tuple_cat(
|
||||
std::conditional_t<
|
||||
PressureSurfaceRelationMatches<Relations, Formulation, EquationOfState>::value,
|
||||
std::tuple<Relations>,
|
||||
std::tuple<>>{}...
|
||||
));
|
||||
|
||||
static constexpr std::size_t count = std::tuple_size_v<Tuple>;
|
||||
};
|
||||
|
||||
template <std::size_t Count, typename Tuple> struct UniquePressureSurfaceRelation {
|
||||
using Type = void;
|
||||
};
|
||||
|
||||
template <typename Tuple> struct UniquePressureSurfaceRelation<1, Tuple> {
|
||||
using Type = std::tuple_element_t<0, Tuple>;
|
||||
};
|
||||
|
||||
template <typename Dependencies, typename Field> struct AppendSurfaceDependency;
|
||||
|
||||
template <typename RowField, typename... StateFields, typename Field>
|
||||
struct AppendSurfaceDependency<SurfaceConstraintDependencies<RowField, StateFields...>, Field> {
|
||||
using Type = SurfaceConstraintDependencies<RowField, StateFields..., Field>;
|
||||
};
|
||||
|
||||
template <typename Dependencies, typename InputQuantity, typename Bindings>
|
||||
struct AppendPressureSurfaceInputDependency {
|
||||
using Type =
|
||||
typename AppendSurfaceDependency<Dependencies, SurfaceFieldForQuantityT<Bindings, InputQuantity>>::Type;
|
||||
};
|
||||
|
||||
template <typename Dependencies, typename Bindings>
|
||||
struct AppendPressureSurfaceInputDependency<Dependencies, eos::quantity::Pressure, Bindings> {
|
||||
using Type = Dependencies;
|
||||
};
|
||||
|
||||
template <typename Dependencies, typename Bindings, typename... InputQuantities>
|
||||
struct AppendPressureSurfaceInputDependencies;
|
||||
|
||||
template <typename Dependencies, typename Bindings>
|
||||
struct AppendPressureSurfaceInputDependencies<Dependencies, Bindings> {
|
||||
using Type = Dependencies;
|
||||
};
|
||||
|
||||
template <typename Dependencies, typename Bindings, typename FirstInput, typename... RemainingInputs>
|
||||
struct AppendPressureSurfaceInputDependencies<Dependencies, Bindings, FirstInput, RemainingInputs...> {
|
||||
using WithFirst = typename AppendPressureSurfaceInputDependency<Dependencies, FirstInput, Bindings>::Type;
|
||||
using Type = typename AppendPressureSurfaceInputDependencies<WithFirst, Bindings, RemainingInputs...>::Type;
|
||||
};
|
||||
|
||||
template <typename RelationType, typename Formulation> struct PressureSurfaceDependenciesForRelation;
|
||||
|
||||
template <typename OutputQuantity, typename... InputQuantities, typename Formulation>
|
||||
struct PressureSurfaceDependenciesForRelation<eos::Relation<OutputQuantity, InputQuantities...>, Formulation> {
|
||||
using InitialDependencies =
|
||||
SurfaceConstraintDependencies<typename Formulation::CarrierField, typename Formulation::CarrierField>;
|
||||
using Type = typename AppendPressureSurfaceInputDependencies<
|
||||
InitialDependencies,
|
||||
typename Formulation::StateBindings,
|
||||
InputQuantities...>::Type;
|
||||
};
|
||||
|
||||
template <SurfaceConstraintFormulationType Formulation, eos::EquationOfStateModel EquationOfState>
|
||||
struct PressureSurfaceCompilation {
|
||||
using Matches =
|
||||
MatchingPressureSurfaceRelations<typename EquationOfState::Relations, Formulation, EquationOfState>;
|
||||
using Relation = typename UniquePressureSurfaceRelation<Matches::count, typename Matches::Tuple>::Type;
|
||||
};
|
||||
|
||||
template <SurfaceConstraintFormulationType Formulation, eos::EquationOfStateModel EquationOfState>
|
||||
requires(PressureSurfaceCompilation<Formulation, EquationOfState>::Matches::count == 1)
|
||||
struct CompiledPressureSurfaceConstraintType {
|
||||
using Compilation = PressureSurfaceCompilation<Formulation, EquationOfState>;
|
||||
using Relation = typename Compilation::Relation;
|
||||
using Dependencies = typename PressureSurfaceDependenciesForRelation<Relation, Formulation>::Type;
|
||||
using Type = CompiledPressureSurfaceConstraint<EquationOfState, Formulation, Relation, Dependencies>;
|
||||
};
|
||||
} // namespace detail
|
||||
|
||||
template <typename Formulation, typename EquationOfState>
|
||||
concept PressureSurfaceCompilable =
|
||||
SurfaceConstraintFormulationType<Formulation> && eos::EquationOfStateModel<EquationOfState> &&
|
||||
(detail::PressureSurfaceCompilation<std::remove_cvref_t<Formulation>, std::remove_cvref_t<EquationOfState>>::
|
||||
Matches::count == 1);
|
||||
|
||||
template <SurfaceConstraintFormulationType Formulation, eos::EquationOfStateModel EquationOfState>
|
||||
requires PressureSurfaceCompilable<Formulation, EquationOfState>
|
||||
using CompiledPressureSurfaceConstraintT = typename detail::CompiledPressureSurfaceConstraintType<
|
||||
std::remove_cvref_t<Formulation>,
|
||||
std::remove_cvref_t<EquationOfState>>::Type;
|
||||
|
||||
template <
|
||||
SurfaceConstraintFormulationType Formulation,
|
||||
eos::EquationOfStateModel EquationOfState>
|
||||
requires PressureSurfaceCompilable<
|
||||
Formulation,
|
||||
EquationOfState>
|
||||
[[nodiscard]] CompiledPressureSurfaceConstraintT<
|
||||
Formulation,
|
||||
EquationOfState>
|
||||
compilePressureSurfaceConstraint(
|
||||
const ConstantPressureSurface condition,
|
||||
const EquationOfState &equationOfState
|
||||
) noexcept {
|
||||
return CompiledPressureSurfaceConstraintT<Formulation, EquationOfState>{condition, equationOfState};
|
||||
}
|
||||
} // namespace mean_field::surface
|
||||
65
libmeanfield/interface/surface/constant.cppm
Normal file
65
libmeanfield/interface/surface/constant.cppm
Normal file
@@ -0,0 +1,65 @@
|
||||
module;
|
||||
|
||||
#include <cmath>
|
||||
#include <format>
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
|
||||
export module mean_field:surface.constant;
|
||||
|
||||
export import :eos.quantities;
|
||||
|
||||
export namespace mean_field::surface {
|
||||
struct PressureSurfaceDescriptor final {
|
||||
double targetPressure;
|
||||
};
|
||||
|
||||
/*
|
||||
* The only physical surface prescription currently supported by
|
||||
* MeanField. It says nothing about which thermodynamic variable appears
|
||||
* in a nonlinear state vector; resolving pressure into that representation
|
||||
* is an EOS responsibility.
|
||||
*/
|
||||
class ConstantPressureSurface final {
|
||||
public:
|
||||
using PhysicalQuantity = eos::quantity::Pressure;
|
||||
using TargetValue = eos::PressureValue;
|
||||
|
||||
explicit ConstantPressureSurface(const TargetValue targetPressure) : m_targetPressure(targetPressure) {
|
||||
if (!std::isfinite(targetPressure.value())) {
|
||||
throw std::invalid_argument(
|
||||
std::format(
|
||||
"The target surface pressure must be finite. Instead P = {} was provided.",
|
||||
targetPressure.value()
|
||||
)
|
||||
);
|
||||
}
|
||||
if (targetPressure.value() < 0.0) {
|
||||
throw std::invalid_argument(
|
||||
std::format(
|
||||
"The target surface pressure must be non-negative. Instead P = {} was provided.",
|
||||
targetPressure.value()
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] TargetValue targetPressure() const noexcept {
|
||||
return m_targetPressure;
|
||||
}
|
||||
|
||||
[[nodiscard]] PressureSurfaceDescriptor descriptor() const noexcept {
|
||||
return PressureSurfaceDescriptor{.targetPressure = m_targetPressure.value()};
|
||||
}
|
||||
|
||||
private:
|
||||
TargetValue m_targetPressure;
|
||||
};
|
||||
|
||||
template <typename Candidate>
|
||||
concept ConstantPressureSurfaceType = std::same_as<std::remove_cvref_t<Candidate>, ConstantPressureSurface>;
|
||||
|
||||
// Familiar physical terminology retained as a synonym, not as a second
|
||||
// surface-condition type.
|
||||
using Isobaric = ConstantPressureSurface;
|
||||
} // namespace mean_field::surface
|
||||
186
libmeanfield/interface/surface/dependencies.cppm
Normal file
186
libmeanfield/interface/surface/dependencies.cppm
Normal file
@@ -0,0 +1,186 @@
|
||||
module;
|
||||
|
||||
#include <array>
|
||||
#include <concepts>
|
||||
#include <cstddef>
|
||||
#include <span>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
|
||||
export module mean_field:surface.dependencies;
|
||||
|
||||
export import :eos.relations;
|
||||
export import :field.registry;
|
||||
|
||||
export namespace mean_field::surface {
|
||||
template <typename Candidate>
|
||||
concept SurfaceFieldType = requires {
|
||||
{ Candidate::name } -> std::convertible_to<std::string_view>;
|
||||
} && (std::string_view{Candidate::name}.size() > 0);
|
||||
|
||||
class SurfaceFieldId final {
|
||||
public:
|
||||
explicit constexpr SurfaceFieldId(const std::string_view name) noexcept : m_name(name) {
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr std::string_view name() const noexcept {
|
||||
return m_name;
|
||||
}
|
||||
|
||||
[[nodiscard]] friend constexpr bool operator==(
|
||||
const SurfaceFieldId &,
|
||||
const SurfaceFieldId &
|
||||
) noexcept = default;
|
||||
|
||||
private:
|
||||
std::string_view m_name;
|
||||
};
|
||||
|
||||
template <SurfaceFieldType Field> inline constexpr SurfaceFieldId surfaceFieldId{std::string_view{Field::name}};
|
||||
|
||||
template <eos::ThermodynamicQuantityType ThermodynamicQuantity, SurfaceFieldType Field>
|
||||
struct SurfaceStateBinding final {
|
||||
using Quantity = ThermodynamicQuantity;
|
||||
using FieldType = Field;
|
||||
};
|
||||
|
||||
template <typename... Bindings> struct SurfaceStateBindings final { };
|
||||
|
||||
namespace detail {
|
||||
template <typename... Types> struct SurfaceTypesAreUnique : std::true_type { };
|
||||
|
||||
template <typename First, typename... Remaining>
|
||||
struct SurfaceTypesAreUnique<First, Remaining...>
|
||||
: std::bool_constant<
|
||||
(!std::same_as<First, Remaining> && ...) && SurfaceTypesAreUnique<Remaining...>::value> { };
|
||||
|
||||
template <typename Bindings> struct SurfaceBindingsAreValid : std::false_type { };
|
||||
|
||||
template <typename... Bindings>
|
||||
struct SurfaceBindingsAreValid<SurfaceStateBindings<Bindings...>>
|
||||
: std::bool_constant<
|
||||
(sizeof...(Bindings) > 0) &&
|
||||
(requires {
|
||||
typename Bindings::Quantity;
|
||||
typename Bindings::FieldType;
|
||||
} && ...) &&
|
||||
(eos::ThermodynamicQuantityType<
|
||||
typename Bindings::Quantity> && ...) &&
|
||||
(SurfaceFieldType<typename Bindings::FieldType> && ...) &&
|
||||
SurfaceTypesAreUnique<
|
||||
typename Bindings::Quantity...>::value> { };
|
||||
|
||||
template <typename Bindings, typename Quantity> struct SurfaceBindingCount;
|
||||
|
||||
template <typename Quantity, typename... Bindings>
|
||||
struct SurfaceBindingCount<SurfaceStateBindings<Bindings...>, Quantity>
|
||||
: std::integral_constant<
|
||||
std::size_t,
|
||||
(std::size_t{0} + ... +
|
||||
(std::same_as<Quantity, typename Bindings::Quantity> ? std::size_t{1} : std::size_t{0}))> {
|
||||
};
|
||||
|
||||
template <typename Bindings, typename Quantity> struct SurfaceFieldForQuantity;
|
||||
|
||||
template <typename Quantity, typename First, typename... Remaining>
|
||||
struct SurfaceFieldForQuantity<SurfaceStateBindings<First, Remaining...>, Quantity>
|
||||
: std::conditional_t<
|
||||
std::same_as<Quantity, typename First::Quantity>,
|
||||
std::type_identity<typename First::FieldType>,
|
||||
SurfaceFieldForQuantity<SurfaceStateBindings<Remaining...>, Quantity>> { };
|
||||
|
||||
template <typename Candidate, std::size_t CarrierBindingCount>
|
||||
struct CarrierFieldMatchesSurfaceBinding : std::false_type { };
|
||||
|
||||
template <typename Candidate>
|
||||
struct CarrierFieldMatchesSurfaceBinding<Candidate, 1>
|
||||
: std::bool_constant<std::same_as<
|
||||
typename SurfaceFieldForQuantity<
|
||||
typename Candidate::StateBindings,
|
||||
typename Candidate::CarrierQuantity>::type,
|
||||
typename Candidate::CarrierField>> { };
|
||||
|
||||
template <
|
||||
typename Candidate,
|
||||
bool BindingsAreValid = SurfaceBindingsAreValid<typename Candidate::StateBindings>::value>
|
||||
struct FormulationBindingsMatchCarrier : std::false_type { };
|
||||
|
||||
template <typename Candidate>
|
||||
struct FormulationBindingsMatchCarrier<Candidate, true>
|
||||
: CarrierFieldMatchesSurfaceBinding<
|
||||
Candidate,
|
||||
SurfaceBindingCount<
|
||||
typename Candidate::StateBindings,
|
||||
typename Candidate::CarrierQuantity>::value> { };
|
||||
|
||||
template <typename Candidate, typename = void>
|
||||
struct IsSurfaceConstraintFormulation : std::false_type { };
|
||||
|
||||
template <typename Candidate>
|
||||
struct IsSurfaceConstraintFormulation<
|
||||
Candidate,
|
||||
std::void_t<
|
||||
typename Candidate::CarrierQuantity,
|
||||
typename Candidate::CarrierField,
|
||||
typename Candidate::StateBindings>>
|
||||
: std::bool_constant<
|
||||
eos::ThermodynamicQuantityType<typename Candidate::CarrierQuantity> &&
|
||||
SurfaceFieldType<typename Candidate::CarrierField> &&
|
||||
FormulationBindingsMatchCarrier<Candidate>::value> { };
|
||||
} // namespace detail
|
||||
|
||||
template <typename Candidate>
|
||||
concept ValidSurfaceStateBindings = detail::SurfaceBindingsAreValid<std::remove_cv_t<Candidate>>::value;
|
||||
|
||||
template <ValidSurfaceStateBindings Bindings, typename Quantity>
|
||||
inline constexpr std::size_t surfaceBindingCount = detail::SurfaceBindingCount<Bindings, Quantity>::value;
|
||||
|
||||
template <ValidSurfaceStateBindings Bindings, typename Quantity>
|
||||
requires(surfaceBindingCount<Bindings, Quantity> == 1)
|
||||
using SurfaceFieldForQuantityT = typename detail::SurfaceFieldForQuantity<Bindings, Quantity>::type;
|
||||
|
||||
template <
|
||||
eos::ThermodynamicQuantityType CarrierThermodynamicQuantity,
|
||||
SurfaceFieldType CarrierFieldType,
|
||||
ValidSurfaceStateBindings Bindings>
|
||||
requires(
|
||||
surfaceBindingCount<Bindings, CarrierThermodynamicQuantity> == 1 &&
|
||||
std::same_as<SurfaceFieldForQuantityT<Bindings, CarrierThermodynamicQuantity>, CarrierFieldType>
|
||||
)
|
||||
struct SurfaceConstraintFormulation final {
|
||||
using CarrierQuantity = CarrierThermodynamicQuantity;
|
||||
using CarrierField = CarrierFieldType;
|
||||
using StateBindings = Bindings;
|
||||
};
|
||||
|
||||
using BarotropicSurfaceFormulation = SurfaceConstraintFormulation<
|
||||
eos::quantity::SpecificEnthalpy,
|
||||
field::Enthalpy,
|
||||
SurfaceStateBindings<SurfaceStateBinding<eos::quantity::SpecificEnthalpy, field::Enthalpy>>>;
|
||||
|
||||
template <typename Candidate>
|
||||
concept SurfaceConstraintFormulationType =
|
||||
detail::IsSurfaceConstraintFormulation<std::remove_cv_t<Candidate>>::value;
|
||||
|
||||
struct RuntimeSurfaceConstraintDependencies final {
|
||||
SurfaceFieldId residualRowField;
|
||||
std::span<const SurfaceFieldId> stateFields;
|
||||
};
|
||||
|
||||
template <SurfaceFieldType ResidualField, SurfaceFieldType... StateFields>
|
||||
struct SurfaceConstraintDependencies final {
|
||||
using RowField = ResidualField;
|
||||
using StateFieldTypes = field::TypeList<StateFields...>;
|
||||
|
||||
inline static constexpr std::array<SurfaceFieldId, sizeof...(StateFields)> runtimeStateFields{
|
||||
surfaceFieldId<StateFields>...
|
||||
};
|
||||
|
||||
[[nodiscard]] static constexpr RuntimeSurfaceConstraintDependencies runtimeDescription() noexcept {
|
||||
return RuntimeSurfaceConstraintDependencies{
|
||||
.residualRowField = surfaceFieldId<ResidualField>,
|
||||
.stateFields = std::span<const SurfaceFieldId>{runtimeStateFields}
|
||||
};
|
||||
}
|
||||
};
|
||||
} // namespace mean_field::surface
|
||||
@@ -1,64 +0,0 @@
|
||||
module;
|
||||
|
||||
#include <cmath>
|
||||
#include <format>
|
||||
#include <stdexcept>
|
||||
|
||||
export module mean_field:surface.isobaric;
|
||||
|
||||
export import :surface.base;
|
||||
|
||||
export namespace mean_field::surface {
|
||||
class Isobaric final : public SurfaceBase {
|
||||
public:
|
||||
explicit Isobaric(const double targetPressure = 0.0) : m_targetPressure(targetPressure) {
|
||||
validateTargetPressure();
|
||||
}
|
||||
|
||||
[[nodiscard]] double targetPressure() const noexcept {
|
||||
return m_targetPressure;
|
||||
}
|
||||
|
||||
[[nodiscard]] ResolvedSurfaceCondition
|
||||
resolve(const mean_field::eos::EquationOfState &equationOfState) const override {
|
||||
return ResolvedSurfaceCondition{resolveTargetEnthalpy(equationOfState)};
|
||||
}
|
||||
|
||||
void validate(const mean_field::eos::EquationOfState &equationOfState) const override {
|
||||
static_cast<void>(resolveTargetEnthalpy(equationOfState));
|
||||
}
|
||||
|
||||
private:
|
||||
[[nodiscard]] double resolveTargetEnthalpy(const mean_field::eos::EquationOfState &equationOfState) const {
|
||||
validateTargetPressure();
|
||||
|
||||
const double targetEnthalpy = equationOfState.enthalpy_from_pressure(m_targetPressure);
|
||||
|
||||
if (!std::isfinite(targetEnthalpy) || targetEnthalpy < 0.0) {
|
||||
throw std::domain_error(
|
||||
std::format(
|
||||
"The equation of state resolved the isobaric "
|
||||
"target P = {} to the invalid enthalpy h = {}.",
|
||||
m_targetPressure, targetEnthalpy
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return targetEnthalpy;
|
||||
}
|
||||
|
||||
void validateTargetPressure() const {
|
||||
if (!std::isfinite(m_targetPressure) || m_targetPressure < 0.0) {
|
||||
throw std::invalid_argument(
|
||||
std::format(
|
||||
"The target surface pressure must be finite and "
|
||||
"non-negative. Instead P = {} was provided.",
|
||||
m_targetPressure
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
double m_targetPressure;
|
||||
};
|
||||
} // namespace mean_field::surface
|
||||
@@ -1,53 +0,0 @@
|
||||
module;
|
||||
|
||||
#include <cmath>
|
||||
#include <stdexcept>
|
||||
|
||||
export module mean_field:surface.base;
|
||||
|
||||
export import :eos.base;
|
||||
|
||||
export namespace mean_field::surface {
|
||||
struct ResolvedSurfaceCondition final {
|
||||
double targetEnthalpy{0.0};
|
||||
|
||||
explicit ResolvedSurfaceCondition(const double requestedTargetEnthalpy)
|
||||
: targetEnthalpy(requestedTargetEnthalpy) {
|
||||
if (!std::isfinite(targetEnthalpy) || targetEnthalpy < 0.0) {
|
||||
throw std::invalid_argument(
|
||||
"A resolved surface enthalpy must be finite and "
|
||||
"non-negative."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] double residual(const double enthalpy) const {
|
||||
if (!std::isfinite(enthalpy)) {
|
||||
throw std::invalid_argument("A surface enthalpy value must be finite.");
|
||||
}
|
||||
|
||||
return enthalpy - targetEnthalpy;
|
||||
}
|
||||
|
||||
[[nodiscard]] static double jacobianAction(const double enthalpyVariation) {
|
||||
if (!std::isfinite(enthalpyVariation)) {
|
||||
throw std::invalid_argument("A surface enthalpy variation must be finite.");
|
||||
}
|
||||
|
||||
return enthalpyVariation;
|
||||
}
|
||||
};
|
||||
|
||||
class SurfaceBase {
|
||||
public:
|
||||
virtual ~SurfaceBase() = default;
|
||||
|
||||
[[nodiscard]] virtual ResolvedSurfaceCondition
|
||||
resolve(const mean_field::eos::EquationOfState &equationOfState) const = 0;
|
||||
|
||||
virtual void validate(const mean_field::eos::EquationOfState &equationOfState) const = 0;
|
||||
|
||||
protected:
|
||||
SurfaceBase() = default;
|
||||
};
|
||||
} // namespace mean_field::surface
|
||||
File diff suppressed because it is too large
Load Diff
@@ -11,82 +11,90 @@ export module mean_field:utils.misc;
|
||||
import :utils.domain;
|
||||
|
||||
export namespace mean_field::utils {
|
||||
constexpr double APPROX_MAX_ACCEPTABLE_POTENTIAL_ERROR_SI_BURNING = 1e-4;
|
||||
constexpr double APPROX_MAX_ACCEPTABLE_POTENTIAL_ERROR_SI_BURNING = 1e-4;
|
||||
|
||||
bool is_vacuum(const mfem::ElementTransformation &Tr,
|
||||
mfem::Array<mfem::Vector *> elvec) {
|
||||
using Schema = domain::CoreEnvelopeVacuumDomainSchema;
|
||||
bool is_vacuum(
|
||||
const mfem::ElementTransformation &Tr,
|
||||
mfem::Array<mfem::Vector *> elvec
|
||||
) {
|
||||
using Schema = domain::CoreEnvelopeVacuumDomainSchema;
|
||||
|
||||
if (Schema::template attribute_belongs_to<domain::Vacuum>(Tr.Attribute)) {
|
||||
const int size_elvec = elvec.Size();
|
||||
for (int i = 0; i < size_elvec; i++) {
|
||||
if (elvec[i]) {
|
||||
*elvec[i] = 0.0;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool is_vacuum(const mfem::ElementTransformation &Tr,
|
||||
const mfem::Array2D<mfem::DenseMatrix *> &elmats) {
|
||||
using Schema = domain::CoreEnvelopeVacuumDomainSchema;
|
||||
|
||||
if (Schema::template attribute_belongs_to<domain::Vacuum>(Tr.Attribute)) {
|
||||
const int cols = elmats.NumCols();
|
||||
const int rows = elmats.NumRows();
|
||||
for (int rowID = 0; rowID < rows; rowID++) {
|
||||
for (int colID = 0; colID < cols; colID++) {
|
||||
if (elmats(rowID, colID)) {
|
||||
*elmats(rowID, colID) = 0.0;
|
||||
if (Schema::template attribute_belongs_to<domain::Vacuum>(Tr.Attribute)) {
|
||||
const int size_elvec = elvec.Size();
|
||||
for (int i = 0; i < size_elvec; i++) {
|
||||
if (elvec[i]) {
|
||||
*elvec[i] = 0.0;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
constexpr std::string_view ANSI_GREEN = "\033[32m";
|
||||
constexpr std::string_view ANSI_RED = "\033[31m";
|
||||
constexpr std::string_view ANSI_YELLOW = "\033[33m";
|
||||
constexpr std::string_view ANSI_BLUE = "\033[34m";
|
||||
constexpr std::string_view ANSI_MAGENTA = "\033[35m";
|
||||
constexpr std::string_view ANSI_CYAN = "\033[36m";
|
||||
constexpr std::string_view ANSI_RESET = "\033[0m";
|
||||
constexpr std::string_view ANSI_BCYAN = "\033[1;36m";
|
||||
bool is_vacuum(
|
||||
const mfem::ElementTransformation &Tr,
|
||||
const mfem::Array2D<mfem::DenseMatrix *> &elmats
|
||||
) {
|
||||
using Schema = domain::CoreEnvelopeVacuumDomainSchema;
|
||||
|
||||
constexpr double G = 1.0;
|
||||
constexpr double MASS = 1.0;
|
||||
constexpr double RADIUS = 1.0;
|
||||
if (Schema::template attribute_belongs_to<domain::Vacuum>(Tr.Attribute)) {
|
||||
const int cols = elmats.NumCols();
|
||||
const int rows = elmats.NumRows();
|
||||
for (int rowID = 0; rowID < rows; rowID++) {
|
||||
for (int colID = 0; colID < cols; colID++) {
|
||||
if (elmats(rowID, colID)) {
|
||||
*elmats(rowID, colID) = 0.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
[[maybe_unused]] constexpr char HOST[10] = "localhost";
|
||||
[[maybe_unused]] constexpr int PORT = 19916;
|
||||
constexpr std::string_view ANSI_GREEN = "\033[32m";
|
||||
constexpr std::string_view ANSI_RED = "\033[31m";
|
||||
constexpr std::string_view ANSI_YELLOW = "\033[33m";
|
||||
constexpr std::string_view ANSI_BLUE = "\033[34m";
|
||||
constexpr std::string_view ANSI_MAGENTA = "\033[35m";
|
||||
constexpr std::string_view ANSI_CYAN = "\033[36m";
|
||||
constexpr std::string_view ANSI_RESET = "\033[0m";
|
||||
constexpr std::string_view ANSI_BCYAN = "\033[1;36m";
|
||||
|
||||
template <typename T>
|
||||
concept is_xad = std::is_same_v<T, xad::AReal<long double>> ||
|
||||
std::is_same_v<T, xad::AReal<double>> ||
|
||||
std::is_same_v<T, xad::AReal<float>>;
|
||||
constexpr double G = 1.0;
|
||||
constexpr double MASS = 1.0;
|
||||
constexpr double RADIUS = 1.0;
|
||||
|
||||
template <typename T>
|
||||
concept is_real = std::is_floating_point_v<T> || is_xad<T>;
|
||||
[[maybe_unused]] constexpr char HOST[10] = "localhost";
|
||||
[[maybe_unused]] constexpr int PORT = 19916;
|
||||
|
||||
template <is_real T>
|
||||
using EOS_P = std::function<T(const T &rho, const T &temp)>;
|
||||
template <typename T>
|
||||
concept is_xad = std::is_same_v<T, xad::AReal<long double>> || std::is_same_v<T, xad::AReal<double>> ||
|
||||
std::is_same_v<T, xad::AReal<float>>;
|
||||
|
||||
enum class DOMAINS : uint8_t {
|
||||
CORE = 1 << 0,
|
||||
ENVELOPE = 1 << 1,
|
||||
VACUUM = 1 << 2,
|
||||
STELLAR = CORE | ENVELOPE,
|
||||
ALL = CORE | ENVELOPE | VACUUM
|
||||
};
|
||||
template <typename T>
|
||||
concept is_real = std::is_floating_point_v<T> || is_xad<T>;
|
||||
|
||||
DOMAINS operator|(DOMAINS lhs, DOMAINS rhs);
|
||||
template <is_real T> using EOS_P = std::function<T(const T &rho, const T &temp)>;
|
||||
|
||||
DOMAINS operator&(DOMAINS lhs, DOMAINS rhs);
|
||||
enum class DOMAINS : uint8_t {
|
||||
CORE = 1 << 0,
|
||||
ENVELOPE = 1 << 1,
|
||||
VACUUM = 1 << 2,
|
||||
STELLAR = CORE | ENVELOPE,
|
||||
ALL = CORE | ENVELOPE | VACUUM
|
||||
};
|
||||
|
||||
int get_mesh_order(const mfem::Mesh &mesh);
|
||||
DOMAINS operator|(
|
||||
DOMAINS lhs,
|
||||
DOMAINS rhs
|
||||
);
|
||||
|
||||
DOMAINS operator&(
|
||||
DOMAINS lhs,
|
||||
DOMAINS rhs
|
||||
);
|
||||
|
||||
int get_mesh_order(const mfem::Mesh &mesh);
|
||||
|
||||
} // namespace mean_field::utils
|
||||
|
||||
Reference in New Issue
Block a user