feat(libmeanfield): centrifugal + pressure
This commit is contained in:
@@ -1,190 +1,560 @@
|
||||
module;
|
||||
#include <string>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <expected>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include <mfem.hpp>
|
||||
#include <stroid/stroid.h>
|
||||
|
||||
module mean_field;
|
||||
|
||||
import :boundary.contexts;
|
||||
import :field.mfem;
|
||||
import :mapping.coefficients;
|
||||
import :utils.misc;
|
||||
import :utils.user;
|
||||
|
||||
|
||||
namespace mean_field::fem {
|
||||
FEM setup_fem(const std::string &filename, const utils::Args &args, const int extra_refine) {
|
||||
FEM setup_fem(
|
||||
const std::string &filename,
|
||||
const utils::Args &args,
|
||||
const int extraRefine
|
||||
) {
|
||||
FEM fem;
|
||||
|
||||
//==================================================================
|
||||
// Section 1: Mesh and FE Space Setup
|
||||
//==================================================================
|
||||
fem.smesh = stroid::IO::LoadStroidMesh(filename).value();
|
||||
if (extra_refine > 0) {
|
||||
stroid::refinement::UniformRefinement(fem.smesh, extra_refine);
|
||||
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;
|
||||
|
||||
// =====================================================================
|
||||
// Section 1: Mesh construction
|
||||
// =====================================================================
|
||||
|
||||
fem.smesh = stroid::IO::LoadStroidMesh(filename).value();
|
||||
|
||||
if (extraRefine > 0) {
|
||||
stroid::refinement::UniformRefinement(fem.smesh, extraRefine);
|
||||
}
|
||||
|
||||
fem.mesh = std::make_unique<mfem::ParMesh>(MPI_COMM_WORLD, *fem.smesh.mesh);
|
||||
int mpiSize = 1;
|
||||
MPI_Comm_size(MPI_COMM_WORLD, &mpiSize);
|
||||
|
||||
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->EnsureNodes();
|
||||
|
||||
const int geom_order = utils::get_mesh_order(*fem.mesh);
|
||||
const int dim = fem.mesh->Dimension();
|
||||
// =====================================================================
|
||||
// Section 2: Exterior compactification coordinate
|
||||
// =====================================================================
|
||||
|
||||
const int v_order = 2;
|
||||
const int rho_order = 2;
|
||||
const int p = rho_order ;
|
||||
if (fem.smesh.exterior_coordinate == nullptr) {
|
||||
throw std::runtime_error("Exterior coordinate not set.");
|
||||
}
|
||||
|
||||
const int cb_type = mfem::BasisType::GaussLobatto;
|
||||
const int ob_type = mfem::BasisType::IntegratedGLL;
|
||||
if (fem.smesh.exterior_coordinate->space == nullptr) {
|
||||
throw std::runtime_error("Space for exterior coordinate not set.");
|
||||
}
|
||||
|
||||
fem.RT_fec = std::make_unique<mfem::RT_FECollection>(p, dim, cb_type, ob_type);
|
||||
fem.RT_fes = std::make_unique<mfem::ParFiniteElementSpace>(fem.mesh.get(), fem.RT_fec.get());
|
||||
if (fem.smesh.exterior_coordinate->values == nullptr) {
|
||||
throw std::runtime_error("Values for exterior coordinate not set.");
|
||||
}
|
||||
|
||||
fem.H1_fec = std::make_unique<mfem::H1_FECollection>(v_order, dim);
|
||||
fem.L2_fec = std::make_unique<mfem::L2_FECollection>(rho_order, dim);
|
||||
const mfem::FiniteElementSpace &serialCoordinateSpace =
|
||||
*fem.smesh.exterior_coordinate->space;
|
||||
|
||||
// Gravity (Scalar H1) and Velocity (Vector H1)
|
||||
fem.H1_fes = std::make_unique<mfem::ParFiniteElementSpace>(fem.mesh.get(), fem.H1_fec.get());
|
||||
fem.Vec_H1_fes = std::make_unique<mfem::ParFiniteElementSpace>(fem.mesh.get(), fem.H1_fec.get(), dim,
|
||||
mfem::Ordering::byNODES);
|
||||
const mfem::GridFunction &serialCoordinate =
|
||||
*fem.smesh.exterior_coordinate->values;
|
||||
|
||||
// Density & Pressure (Scalar Discontinuous L2)
|
||||
fem.L2_fes = std::make_unique<mfem::ParFiniteElementSpace>(fem.mesh.get(), fem.L2_fec.get());
|
||||
if (serialCoordinate.FESpace() != &serialCoordinateSpace) {
|
||||
throw std::runtime_error(
|
||||
"Exterior coordinate values are not associated with the "
|
||||
"supplied finite-element space."
|
||||
);
|
||||
}
|
||||
|
||||
//==================================================================
|
||||
// Section 2: Domain Mapping
|
||||
//==================================================================
|
||||
auto [r_star_ref, r_inf_ref] = utils::discover_bounds(fem.mesh.get(), 3)
|
||||
.or_else([](const boundary::BoundsError &err)-> std::expected<boundary::Bounds, boundary::BoundsError> {
|
||||
throw std::runtime_error("Unable to determine vacuum domain reference boundary...");
|
||||
}).value();
|
||||
if (serialCoordinateSpace.GetMesh() != fem.smesh.mesh.get()) {
|
||||
throw std::runtime_error(
|
||||
"Exterior coordinate space is not associated with the "
|
||||
"loaded STROID mesh."
|
||||
);
|
||||
}
|
||||
|
||||
fem.mapping = std::make_unique<mapping::DomainMapper>(r_star_ref, r_inf_ref);
|
||||
if (serialCoordinateSpace.GetVDim() != 1) {
|
||||
throw std::runtime_error(
|
||||
"Exterior coordinate must be a scalar field."
|
||||
);
|
||||
}
|
||||
|
||||
//==================================================================
|
||||
// Section 3: Multi-physics Block-offsets
|
||||
//==================================================================
|
||||
fem.block_true_offsets.SetSize(3);
|
||||
fem.block_true_offsets[0] = 0;
|
||||
fem.block_true_offsets[1] = fem.Vec_H1_fes->GetTrueVSize();
|
||||
fem.block_true_offsets[2] = fem.block_true_offsets[1] + fem.L2_fes->GetTrueVSize();
|
||||
if (serialCoordinate.Size() != serialCoordinateSpace.GetVSize()) {
|
||||
throw std::runtime_error(
|
||||
"Exterior coordinate value count does not match its "
|
||||
"finite-element space."
|
||||
);
|
||||
}
|
||||
|
||||
fem.gravity_block_true_offsets.SetSize(3);
|
||||
fem.gravity_block_true_offsets[0] = 0;
|
||||
fem.gravity_block_true_offsets[1] = fem.RT_fes->GetTrueVSize();
|
||||
fem.gravity_block_true_offsets[2] = fem.gravity_block_true_offsets[1] + fem.L2_fes->GetTrueVSize();
|
||||
const int compactificationOrder =
|
||||
serialCoordinateSpace.GetMaxElementOrder();
|
||||
|
||||
//==================================================================
|
||||
// Section 4: Multipole BC setup.
|
||||
//==================================================================
|
||||
fem.com.SetSize(dim);
|
||||
const int dimension = fem.mesh->Dimension();
|
||||
|
||||
fem.compactificationFec = std::make_unique<mfem::H1_FECollection>(
|
||||
compactificationOrder, dimension
|
||||
);
|
||||
|
||||
fem.compactificationFes = std::make_unique<mfem::ParFiniteElementSpace>(
|
||||
fem.mesh.get(), fem.compactificationFec.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."
|
||||
);
|
||||
}
|
||||
|
||||
fem.compactificationCoordinate =
|
||||
std::make_unique<mfem::ParGridFunction>(
|
||||
fem.compactificationFes.get()
|
||||
);
|
||||
|
||||
*fem.compactificationCoordinate = distributedCoordinate;
|
||||
|
||||
double localMinimum = 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);
|
||||
|
||||
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: Domain mapping
|
||||
// =====================================================================
|
||||
|
||||
auto [stellarRadiusReference, infinityRadiusReference] =
|
||||
utils::discover_bounds(fem.mesh.get(), 3)
|
||||
.or_else(
|
||||
[](const boundary::BoundsError &)
|
||||
-> std::expected<
|
||||
boundary::Bounds, boundary::BoundsError> {
|
||||
throw std::runtime_error(
|
||||
"Unable to determine vacuum-domain reference "
|
||||
"boundaries."
|
||||
);
|
||||
}
|
||||
)
|
||||
.value();
|
||||
|
||||
fem.mapping = std::make_unique<mapping::DomainMapper>(
|
||||
*fem.displacement, stellarRadiusReference, infinityRadiusReference
|
||||
);
|
||||
|
||||
// =====================================================================
|
||||
// Section 5: Block offsets
|
||||
//
|
||||
// Legacy layouts only. New coupled operators use :utils.blocks forms.
|
||||
//
|
||||
// Main system: [Displacement | Density]
|
||||
// Gravity system: [Flux | Potential]
|
||||
// =====================================================================
|
||||
|
||||
fem.blockTrueOffsets.SetSize(3);
|
||||
fem.blockTrueOffsets[0] = 0;
|
||||
|
||||
fem.blockTrueOffsets[1] = fem.displacementFes->GetTrueVSize();
|
||||
|
||||
fem.blockTrueOffsets[2] =
|
||||
fem.blockTrueOffsets[1] + fem.densityFes->GetTrueVSize();
|
||||
|
||||
fem.gravityBlockTrueOffsets.SetSize(3);
|
||||
fem.gravityBlockTrueOffsets[0] = 0;
|
||||
|
||||
fem.gravityBlockTrueOffsets[1] = fem.gravityFluxFes->GetTrueVSize();
|
||||
|
||||
fem.gravityBlockTrueOffsets[2] =
|
||||
fem.gravityBlockTrueOffsets[1] +
|
||||
fem.gravityPotentialFes->GetTrueVSize();
|
||||
|
||||
// =====================================================================
|
||||
// Section 6: Multipole data
|
||||
// =====================================================================
|
||||
|
||||
fem.com.SetSize(dimension);
|
||||
fem.com = 0.0;
|
||||
fem.Q.SetSize(dim, dim);
|
||||
|
||||
fem.Q.SetSize(dimension, dimension);
|
||||
fem.Q = 0.0;
|
||||
|
||||
//==================================================================
|
||||
// Section 5: Integration Rules
|
||||
//==================================================================
|
||||
MFEM_ASSERT(fem.mesh->GetElementGeometry(0) == mfem::Geometry::CUBE,
|
||||
"Currently only hexahedral meshes are supported");
|
||||
const int element_order = fem.H1_fes->GetMaxElementOrder();
|
||||
fem.int_order = 2 * element_order + geom_order - 2 + args.quad_boost;
|
||||
// =====================================================================
|
||||
// Section 7: Essential boundaries and domain masks
|
||||
// =====================================================================
|
||||
|
||||
fem.int_rule = std::make_unique<mfem::IntegrationRule>(mfem::IntRules.Get(mfem::Geometry::CUBE, fem.int_order));
|
||||
fem.essentialDisplacementTdofs.SetSize(0);
|
||||
|
||||
//==================================================================
|
||||
// Section 6: Essential Boundaries & Domain Masks
|
||||
//==================================================================
|
||||
fem.ess_v_tdofs.SetSize(0);
|
||||
populate_element_mask(
|
||||
fem.mesh.get(), utils::DOMAINS::STELLAR,
|
||||
fem.gravityContext.stellar_mask
|
||||
);
|
||||
|
||||
populate_element_mask(fem.mesh.get(), utils::DOMAINS::STELLAR, fem.gravity_context.stellar_mask);
|
||||
const int boundaryAttributeCount = fem.mesh->bdr_attributes.Max();
|
||||
|
||||
const int n_bdr_attrs = fem.mesh->bdr_attributes.Max();
|
||||
fem.boundary_context.inf_bounds.SetSize(n_bdr_attrs);
|
||||
fem.boundary_context.stellar_bounds.SetSize(n_bdr_attrs);
|
||||
fem.boundaryContext.inf_bounds.SetSize(boundaryAttributeCount);
|
||||
|
||||
fem.boundary_context.inf_bounds = 0;
|
||||
fem.boundary_context.stellar_bounds = 0;
|
||||
fem.boundaryContext.stellar_bounds.SetSize(boundaryAttributeCount);
|
||||
|
||||
fem.boundary_context.inf_bounds[static_cast<int>(boundary::Boundaries::INF_SURFACE) - 1] = 1;
|
||||
fem.boundary_context.stellar_bounds[static_cast<int>(boundary::Boundaries::STELLAR_SURFACE) - 1] = 1;
|
||||
fem.boundaryContext.inf_bounds = 0;
|
||||
fem.boundaryContext.stellar_bounds = 0;
|
||||
|
||||
//==================================================================
|
||||
// Section 7: Gravity Context Setup
|
||||
//==================================================================
|
||||
fem.gravity_context.minres = std::make_unique<mfem::MINRESSolver>(fem.mesh->GetComm());
|
||||
fem.gravity_context.minres->SetRelTol(1e-12);
|
||||
fem.gravity_context.minres->SetAbsTol(1e-12);
|
||||
fem.gravity_context.minres->SetMaxIter(1000);
|
||||
fem.gravity_context.minres->SetPrintLevel(0);
|
||||
fem.boundaryContext.inf_bounds
|
||||
[static_cast<int>(boundary::Boundaries::INF_SURFACE) - 1] = 1;
|
||||
|
||||
fem.gravity_context.prec_Phi = std::make_unique<mfem::HypreBoomerAMG>();
|
||||
fem.gravity_context.prec_Phi->SetPrintLevel(0);
|
||||
fem.boundaryContext.stellar_bounds
|
||||
[static_cast<int>(boundary::Boundaries::STELLAR_SURFACE) - 1] = 1;
|
||||
|
||||
fem.gravity_context.block_prec = std::make_unique<mfem::BlockDiagonalPreconditioner>(fem.gravity_block_true_offsets);
|
||||
// =====================================================================
|
||||
// Section 8: Gravity solver context
|
||||
// =====================================================================
|
||||
|
||||
fem.gravityContext.minres =
|
||||
std::make_unique<mfem::MINRESSolver>(fem.mesh->GetComm());
|
||||
|
||||
fem.gravityContext.minres->SetRelTol(1.0e-12);
|
||||
fem.gravityContext.minres->SetAbsTol(1.0e-12);
|
||||
fem.gravityContext.minres->SetMaxIter(1000);
|
||||
fem.gravityContext.minres->SetPrintLevel(0);
|
||||
|
||||
fem.gravityContext.prec_Phi = std::make_unique<mfem::HypreBoomerAMG>();
|
||||
|
||||
fem.gravityContext.prec_Phi->SetPrintLevel(0);
|
||||
|
||||
fem.gravityContext.block_prec =
|
||||
std::make_unique<mfem::BlockDiagonalPreconditioner>(
|
||||
fem.gravityBlockTrueOffsets
|
||||
);
|
||||
|
||||
fem.gravityContext.minres->SetPreconditioner(
|
||||
*fem.gravityContext.block_prec
|
||||
);
|
||||
|
||||
// =====================================================================
|
||||
// Section 9: Vacuum true-DOF masks
|
||||
// =====================================================================
|
||||
|
||||
fem.gravity_context.minres->SetPreconditioner(*fem.gravity_context.block_prec);
|
||||
//=========================================================
|
||||
// Section 10: Set All vacuum elements true degrees of freedom
|
||||
//=========================================================
|
||||
{
|
||||
mfem::Array<int> vacuum_mask;
|
||||
utils::populate_element_mask(fem.mesh.get(), utils::DOMAINS::VACUUM, vacuum_mask);
|
||||
mfem::Array<int> vacuumMask;
|
||||
|
||||
utils::populate_domain_tdofs(fem.Vec_H1_fes.get(), vacuum_mask, fem.vacuum_tdof_v);
|
||||
utils::populate_domain_tdofs(fem.L2_fes.get(), vacuum_mask, fem.vacuum_tdof_rho);
|
||||
utils::populate_element_mask(
|
||||
fem.mesh.get(), utils::DOMAINS::VACUUM, vacuumMask
|
||||
);
|
||||
|
||||
utils::populate_domain_tdofs(
|
||||
fem.displacementFes.get(), vacuumMask,
|
||||
fem.vacuumDisplacementTdofs
|
||||
);
|
||||
|
||||
utils::populate_domain_tdofs(
|
||||
fem.densityFes.get(), vacuumMask, fem.vacuumDensityTdofs
|
||||
);
|
||||
|
||||
utils::populate_domain_tdofs(
|
||||
fem.enthalpyFes.get(), vacuumMask, fem.vacuumEnthalpyTdofs
|
||||
);
|
||||
}
|
||||
|
||||
const quadrature::QuadratureOptions& quadrature_options = args.quadrature;
|
||||
// =====================================================================
|
||||
// Section 10: Quadrature policy
|
||||
// =====================================================================
|
||||
|
||||
if (quadrature_options.validation.reject_negative_boosts && quadrature_options.global_boost < 0) {
|
||||
throw std::invalid_argument("Global quadrature boost cannot be negative.");
|
||||
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 quadrature_rule_set = quadrature::make_rule_set(quadrature_options.mode, quadrature_options.global_boost);
|
||||
quadrature::RuleSet quadratureRuleSet = quadrature::make_rule_set(
|
||||
quadratureOptions.mode, quadratureOptions.global_boost
|
||||
);
|
||||
|
||||
if (quadrature_options.fallback_fixed_order.has_value()) {
|
||||
if (*quadrature_options.fallback_fixed_order < 0) {
|
||||
throw std::invalid_argument("Fallback quadrature order cannot be negative.");
|
||||
if (quadratureOptions.fallback_fixed_order.has_value()) {
|
||||
if (*quadratureOptions.fallback_fixed_order < 0) {
|
||||
throw std::invalid_argument(
|
||||
"Fallback quadrature order cannot be negative."
|
||||
);
|
||||
}
|
||||
quadrature_rule_set.fallback.fixed_order = quadrature_options.fallback_fixed_order;
|
||||
|
||||
quadratureRuleSet.fallback.fixed_order =
|
||||
quadratureOptions.fallback_fixed_order;
|
||||
}
|
||||
|
||||
auto apply_quadrature_options = [&quadrature_options](quadrature::RuleControl& rule_control, const quadrature::QuadratureTermOptions& term_options) {
|
||||
if (term_options.fixed_order.has_value() && *term_options.fixed_order < 0) {
|
||||
throw std::invalid_argument("Fixed quadrature order cannot be negative.");
|
||||
}
|
||||
auto apply_quadrature_options =
|
||||
[&quadratureOptions](
|
||||
quadrature::RuleControl &ruleControl,
|
||||
const quadrature::QuadratureTermOptions &termOptions
|
||||
) {
|
||||
if (termOptions.fixed_order.has_value() &&
|
||||
*termOptions.fixed_order < 0) {
|
||||
throw std::invalid_argument(
|
||||
"Fixed quadrature order cannot be negative."
|
||||
);
|
||||
}
|
||||
|
||||
if (quadrature_options.validation.reject_negative_boosts && term_options.additional_boost < 0) {
|
||||
throw std::invalid_argument("Term quadrature boost cannot be negative.");
|
||||
}
|
||||
if (quadratureOptions.validation.reject_negative_boosts &&
|
||||
termOptions.additional_boost < 0) {
|
||||
throw std::invalid_argument(
|
||||
"Term quadrature boost cannot be negative."
|
||||
);
|
||||
}
|
||||
|
||||
rule_control.boost += term_options.additional_boost;
|
||||
ruleControl.boost += termOptions.additional_boost;
|
||||
|
||||
if (term_options.fixed_order.has_value()) {
|
||||
rule_control.fixed_order = term_options.fixed_order;
|
||||
}
|
||||
};
|
||||
if (termOptions.fixed_order.has_value()) {
|
||||
ruleControl.fixed_order = termOptions.fixed_order;
|
||||
}
|
||||
};
|
||||
|
||||
apply_quadrature_options(quadrature_rule_set.gravity_hdiv_mass, quadrature_options.gravity_hdiv_mass);
|
||||
apply_quadrature_options(quadrature_rule_set.gravity_divergence, quadrature_options.gravity_divergence);
|
||||
apply_quadrature_options(quadrature_rule_set.gravity_source, quadrature_options.gravity_source);
|
||||
apply_quadrature_options(quadrature_rule_set.gravity_boundary, quadrature_options.gravity_boundary);
|
||||
apply_quadrature_options(quadrature_rule_set.density_projection, quadrature_options.density_projection);
|
||||
apply_quadrature_options(quadrature_rule_set.mass_conservation, quadrature_options.mass_conservation);
|
||||
apply_quadrature_options(quadrature_rule_set.center_of_mass, quadrature_options.center_of_mass);
|
||||
apply_quadrature_options(quadrature_rule_set.quadrupole, quadrature_options.quadrupole);
|
||||
apply_quadrature_options(quadrature_rule_set.gravitational_energy, quadrature_options.gravitational_energy);
|
||||
apply_quadrature_options(quadrature_rule_set.virial, quadrature_options.virial);
|
||||
apply_quadrature_options(quadrature_rule_set.error_norm, quadrature_options.error_norm);
|
||||
apply_quadrature_options(
|
||||
quadratureRuleSet.gravity_hdiv_mass,
|
||||
quadratureOptions.gravity_hdiv_mass
|
||||
);
|
||||
|
||||
apply_quadrature_options(quadrature_rule_set.roles.discretization, quadrature_options.roles.discretization);
|
||||
apply_quadrature_options(quadrature_rule_set.roles.preconditioner, quadrature_options.roles.preconditioner);
|
||||
apply_quadrature_options(quadrature_rule_set.roles.diagnostic, quadrature_options.roles.diagnostic);
|
||||
apply_quadrature_options(quadrature_rule_set.roles.projection, quadrature_options.roles.projection);
|
||||
apply_quadrature_options(
|
||||
quadratureRuleSet.gravity_divergence,
|
||||
quadratureOptions.gravity_divergence
|
||||
);
|
||||
|
||||
apply_quadrature_options(
|
||||
quadratureRuleSet.gravity_source, quadratureOptions.gravity_source
|
||||
);
|
||||
|
||||
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
|
||||
);
|
||||
|
||||
fem.domainMapperStateless =
|
||||
std::make_unique<mapping::DomainMapperStateless>(
|
||||
args.domain_mapper_options, std::move(exteriorDomain)
|
||||
);
|
||||
|
||||
fem.quadrature_factory = std::make_unique<quadrature::RuleFactory>(quadrature::Policy(std::move(quadrature_rule_set)));
|
||||
return fem;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace mean_field::fem
|
||||
Reference in New Issue
Block a user