feat(mean_field): added initial implementation

note this implementation lacks many tests
This commit is contained in:
2026-07-15 09:44:43 -04:00
commit 9bc4f2758a
49 changed files with 171811 additions and 0 deletions

View File

@@ -0,0 +1,41 @@
module;
#include "mean_field.h"
export module mean_field:analysis.integral;
export import :fem;
export import :utils.domain;
export import :mapping.domain_mapper;
export import :mapping.types;
export namespace mean_field::analysis {
double domain_integrate_grid_function(
const fem::FEM &fem,
const mfem::GridFunction &gf,
utils::DOMAINS domain = utils::DOMAINS::ALL,
mapping::COORDINATE_SPACE coord_space = mapping::COORDINATE_SPACE::PHYSICAL
);
mfem::Vector get_com(
const fem::FEM &fem,
const mfem::GridFunction &rho
);
void conserve_mass(
const fem::FEM &fem,
mfem::GridFunction &rho,
double target_mass
);
double get_moment_of_inertia(
const fem::FEM &fem,
const mfem::GridFunction &rho
);
double get_mesh_volume(
const fem::FEM& fem,
mapping::COORDINATE_SPACE coordinate_space = mapping::COORDINATE_SPACE::PHYSICAL,
utils::DOMAINS domain = utils::DOMAINS::STELLAR
);
}

View File

@@ -0,0 +1,34 @@
module;
#include "mean_field.h"
export module mean_field:boundary.contexts;
export namespace mean_field::boundary {
struct BoundaryContext {
mfem::Array<int> inf_bounds;
mfem::Array<int> stellar_bounds;
};
enum class Boundaries : uint8_t {
STELLAR_SURFACE = 1,
INF_SURFACE = 2
};
int operator-(
Boundaries b,
const int a
) {
return static_cast<int>(static_cast<uint8_t>(b) - static_cast<uint8_t>(a));
}
struct Bounds {
double r_star_ref;
double r_inf_ref;
};
enum BoundsError : uint8_t {
CANNOT_FIND_VACUUM
};
}

View File

@@ -0,0 +1,95 @@
module;
#include <stroid/stroid.h>
#include <memory>
#include <mfem.hpp>
export module mean_field:fem;
export import :physics.contexts;
export import :boundary.contexts;
export import :mapping.domain_mapper;
export import :utils.misc;
export import :utils.user;
export import :quadrature.mfem;
export namespace mean_field::fem {
struct FEM {
stroid::StroidMesh smesh;
std::unique_ptr<mfem::ParMesh> mesh;
// =====================================
// 2. Finite Element Collections
// =====================================
// H1 (Continuous): For Gravitational Potential (Phi) and Velocity (v)
std::unique_ptr<mfem::FiniteElementCollection> H1_fec;
// L2 (Discontinuous): For Density (rho) to fix O-grid boundary scalloping
std::unique_ptr<mfem::FiniteElementCollection> L2_fec;
// H(div)/RT space for gravitational field
std::unique_ptr<mfem::RT_FECollection> RT_fec;
// =====================================
// 3. Finite Element Spaces
// =====================================
std::unique_ptr<mfem::ParFiniteElementSpace> H1_fes; // Scalar continuous (Gravity)
std::unique_ptr<mfem::ParFiniteElementSpace> Vec_H1_fes; // Vector continuous (Velocity field)
std::unique_ptr<mfem::ParFiniteElementSpace> L2_fes; // Scalar discontinuous (Density)
std::unique_ptr<mfem::ParFiniteElementSpace> RT_fes; // H(div)/RT space for gravitational field
// Preconditioning for Gravity
std::unique_ptr<mfem::ParLORDiscretization> H1_lor_disc;
const mfem::ParFiniteElementSpace *H1_lor_fes{nullptr};
// =====================================
// 4. Domain Mapping
// =====================================
std::unique_ptr<mapping::DomainMapper> mapping;
// =====================================
// 5. Global System Tracking
// =====================================
// [ Velocity | Density | Mapping Parameters (Surface) ]
mfem::Array<int> block_true_offsets;
mfem::Array<int> gravity_block_true_offsets;
// Essential Boundary Conditions for the fluid (e.g., surface stress-free)
mfem::Array<int> ess_v_tdofs;
// Elements entirely in the vacuum domain where fluid equations are not solved
mfem::Array<int> vacuum_tdof_rho;
mfem::Array<int> vacuum_tdof_v;
// =====================================
// 6. Multiphysics State & Integration
// =====================================
mfem::Vector com;
mfem::DenseMatrix Q;
int int_order{3};
std::unique_ptr<mfem::IntegrationRule> int_rule;
physics::GravityContext gravity_context;
boundary::BoundaryContext boundary_context;
std::unique_ptr<quadrature::RuleFactory> quadrature_factory;
// =====================================
// 7. Utilities
// =====================================
[[nodiscard]] bool okay() const {
return (mesh != nullptr) &&
(H1_fec != nullptr) && (L2_fec != nullptr) && (RT_fec != nullptr) &&
(H1_fes != nullptr) && (Vec_H1_fes != nullptr) && (L2_fes != nullptr) && (RT_fes != nullptr);
}
[[nodiscard]] bool has_mapping() const { return mapping != nullptr; }
};
FEM setup_fem(const std::string &filename, const utils::Args &args, int extra_refine = 0);
}

View File

@@ -0,0 +1,28 @@
module;
#include <mfem.hpp>
export module mean_field:integrators.advection;
import :mapping.domain_mapper;
export namespace mean_field::integrators {
class AdvectionIntegrator : public mfem::BlockNonlinearFormIntegrator {
public:
explicit AdvectionIntegrator(const mapping::DomainMapper &map);
void AssembleElementVector(
const mfem::Array<const mfem::FiniteElement *> &el,
mfem::ElementTransformation &Tr,
const mfem::Array<const mfem::Vector *> &elfun,
const mfem::Array<mfem::Vector *> &elvec
) override;
void AssembleElementGrad(
const mfem::Array<const mfem::FiniteElement *> &el,
mfem::ElementTransformation &Tr,
const mfem::Array<const mfem::Vector *> &elfun,
const mfem::Array2D<mfem::DenseMatrix *> &elmats
) override;
private:
const mapping::DomainMapper &m_map;
};
}

View File

@@ -0,0 +1,32 @@
module;
#include <mfem.hpp>
export module mean_field:integrators.centrifugal;
import :mapping.domain_mapper;
export namespace mean_field::integrators {
class CentrifugalForceIntegrator : public mfem::BlockNonlinearFormIntegrator {
public:
CentrifugalForceIntegrator(const mapping::DomainMapper& map, const mfem::Vector& omega);
void SetOmega(const mfem::Vector& omega);
void AssembleElementVector(
const mfem::Array<const mfem::FiniteElement *> &el,
mfem::ElementTransformation &Tr,
const mfem::Array<const mfem::Vector *> &elfun,
const mfem::Array<mfem::Vector *> &elvec
) override;
void AssembleElementGrad(
const mfem::Array<const mfem::FiniteElement *> &el,
mfem::ElementTransformation &Tr,
const mfem::Array<const mfem::Vector *> &elfun,
const mfem::Array2D<mfem::DenseMatrix *> &elmats
) override;
private:
const mapping::DomainMapper& m_map;
mfem::Vector m_omega;
};
}

View File

@@ -0,0 +1,28 @@
module;
#include <mfem.hpp>
export module mean_field:integrators.coriolis;
import :mapping.domain_mapper;
export namespace mean_field::integrators {
class CoriolisIntegrator : public mfem::BlockNonlinearFormIntegrator {
public:
CoriolisIntegrator(const mapping::DomainMapper& map, const mfem::Vector& omega);
void AssembleElementVector(const mfem::Array<const mfem::FiniteElement *> &el,
mfem::ElementTransformation &Tr,
const mfem::Array<const mfem::Vector *> &elfun,
const mfem::Array<mfem::Vector *> &elvec) override;
void AssembleElementGrad(const mfem::Array<const mfem::FiniteElement*> &el,
mfem::ElementTransformation &Tr,
const mfem::Array<const mfem::Vector *> &elfun,
const mfem::Array2D<mfem::DenseMatrix *> &elmats) override;
private:
const mapping::DomainMapper& m_map;
mfem::Vector m_omega;
mfem::DenseMatrix m_omega_mat;
};
}

View File

@@ -0,0 +1,32 @@
module;
#include <mfem.hpp>
export module mean_field:integrators.gravity;
import :mapping.domain_mapper;
export namespace mean_field::integrators {
class GravityForceIntegrator : public mfem::BlockNonlinearFormIntegrator {
public:
GravityForceIntegrator(const mapping::DomainMapper& map, const mfem::GridFunction& phi);
void SetPotential(const mfem::GridFunction& phi);
void AssembleElementVector(
const mfem::Array<const mfem::FiniteElement *> &el,
mfem::ElementTransformation &Tr,
const mfem::Array<const mfem::Vector *> &elfun,
const mfem::Array<mfem::Vector *> &elvec
) override;
void AssembleElementGrad(
const mfem::Array<const mfem::FiniteElement *> &el,
mfem::ElementTransformation &Tr,
const mfem::Array<const mfem::Vector *> &elfun,
const mfem::Array2D<mfem::DenseMatrix *> &elmats
) override;
private:
const mapping::DomainMapper& m_map;
const mfem::GridFunction* m_phi;
};
}

View File

@@ -0,0 +1,62 @@
module;
#include <mfem.hpp>
export module mean_field:integrators.mass_continuity;
import :mapping.domain_mapper;
export namespace mean_field::integrators {
class ContinuityVolumeIntegrator : public mfem::BlockNonlinearFormIntegrator {
public:
explicit ContinuityVolumeIntegrator(const mapping::DomainMapper& map);
void AssembleElementVector(
const mfem::Array<const mfem::FiniteElement *> &el,
mfem::ElementTransformation &Tr,
const mfem::Array<const mfem::Vector *> &elfun,
const mfem::Array<mfem::Vector *> &elvec
) override;
void AssembleElementGrad(
const mfem::Array<const mfem::FiniteElement *> &el,
mfem::ElementTransformation &Tr,
const mfem::Array<const mfem::Vector *> &elfun,
const mfem::Array2D<mfem::DenseMatrix *> &elmats
) override;
private:
const mapping::DomainMapper& m_map;
};
class ContinuityFaceIntegrator : public mfem::BlockNonlinearFormIntegrator {
public:
explicit ContinuityFaceIntegrator(const mapping::DomainMapper& map);
void AssembleFaceVector(
const mfem::Array<const mfem::FiniteElement *> &el1,
const mfem::Array<const mfem::FiniteElement *> &el2,
mfem::FaceElementTransformations &Tr,
const mfem::Array<const mfem::Vector *> &elfun,
const mfem::Array<mfem::Vector *> &elvect
) override;
void AssembleFaceGrad(
const mfem::Array<const mfem::FiniteElement *> &el1,
const mfem::Array<const mfem::FiniteElement *> &el2,
mfem::FaceElementTransformations &Tr,
const mfem::Array<const mfem::Vector *> &elfun,
const mfem::Array2D<mfem::DenseMatrix *> &elmats
) override;
private:
static bool skip_face(const mfem::FaceElementTransformations& Tr);
static double compute_u_n(
const mfem::Vector& v_dofs,
const mfem::Vector& shape_v_minus,
const mfem::Vector& n_unit,
int dof_v_minus,
int dim
);
private:
const mapping::DomainMapper& m_map;
};
}

View File

@@ -0,0 +1,168 @@
module;
#include <mfem.hpp>
#include "xad_promote_polyfill.h"
#include <XAD/XAD.hpp>
export module mean_field:integrators.pressure_gradient;
import :mapping.domain_mapper;
import :utils.misc;
export namespace mean_field::integrators {
template <utils::is_xad EOS_T>
class PressureGradientIntegrator : public mfem::BlockNonlinearFormIntegrator {
public:
PressureGradientIntegrator(const mapping::DomainMapper& map, utils::EOS_P<EOS_T> eos);
void AssembleElementVector(const mfem::Array<const mfem::FiniteElement *> &el,
mfem::ElementTransformation &Tr,
const mfem::Array<const mfem::Vector *> &elfun,
const mfem::Array<mfem::Vector *> &elvec) override;
void AssembleElementGrad(const mfem::Array<const mfem::FiniteElement*> &el,
mfem::ElementTransformation &Tr,
const mfem::Array<const mfem::Vector *> &elfun,
const mfem::Array2D<mfem::DenseMatrix *> &elmats) override;
private:
const mapping::DomainMapper& m_map;
utils::EOS_P<EOS_T> m_eos;
};
template <utils::is_xad EOS_T>
PressureGradientIntegrator<EOS_T>::PressureGradientIntegrator(
const mapping::DomainMapper& map,
utils::EOS_P<EOS_T> eos
) : m_map(map), m_eos(std::move(eos)) {}
template <utils::is_xad EOS_T>
void PressureGradientIntegrator<EOS_T>::AssembleElementVector(
const mfem::Array<const mfem::FiniteElement *> &el,
mfem::ElementTransformation &Tr,
const mfem::Array<const mfem::Vector *> &elfun,
const mfem::Array<mfem::Vector *> &elvec
) {
if (utils::is_vacuum(Tr, elvec)) {
return;
}
const mfem::FiniteElement* fe_v = el[0];
const mfem::FiniteElement* fe_rho = el[1];
const int dof_v = fe_v->GetDof();
const int dof_rho = fe_rho->GetDof();
const int dim = Tr.GetSpaceDim();
const mfem::Vector& rho_dofs = *elfun[1];
mfem::Vector& r_v = *elvec[0];
r_v.SetSize(dof_v * dim);
r_v = 0.0;
if (elvec[1]) {
elvec[1]->SetSize(dof_rho);
*elvec[1] = 0.0;
}
mfem::DenseMatrix dshape_v_ref(dof_v, dim), dshape_v_phys(dof_v, dim);
mfem::Vector shape_rho(dof_rho);
const mfem::IntegrationRule* ir = &mfem::IntRules.Get(fe_v->GetGeomType(), 2 * fe_v->GetOrder());
for (int q = 0; q < ir->GetNPoints(); ++q) {
const mfem::IntegrationPoint& ip = ir->IntPoint(q);
Tr.SetIntPoint(&ip);
auto [J_inv, detJ, weight] = m_map.GetQuadratureContext(Tr, ip);
fe_v->CalcDShape(ip, dshape_v_ref);
mfem::Mult(dshape_v_ref, J_inv, dshape_v_phys);
fe_rho->CalcShape(ip, shape_rho);
double rho_val = 0.0;
for (int i = 0; i < dof_rho; ++i) rho_val += rho_dofs(i) * shape_rho(i);
// Guard against negative density from Newton solver overshoots
if (rho_val < 1e-15) rho_val = 1e-15;
// Evaluate the exact Equation of State Pressure
EOS_T x_rho = rho_val;
double P_val = m_eos(x_rho, EOS_T(0.0)).value();
for (int i = 0; i < dof_v; ++i) {
for (int c = 0; c < dim; ++c) {
r_v(i + c * dof_v) -= dshape_v_phys(i, c) * P_val * weight;
}
}
}
}
template <utils::is_xad EOS_T>
void PressureGradientIntegrator<EOS_T>::AssembleElementGrad(
const mfem::Array<const mfem::FiniteElement*> &el,
mfem::ElementTransformation &Tr,
const mfem::Array<const mfem::Vector *> &elfun,
const mfem::Array2D<mfem::DenseMatrix *> &elmats
) {
const mfem::FiniteElement* fe_v = el[0];
const mfem::FiniteElement* fe_rho = el[1];
const int dof_v = fe_v->GetDof();
const int dof_rho = fe_rho->GetDof();
const int dim = Tr.GetSpaceDim();
const mfem::Vector& rho_dofs = *elfun[1];
mfem::DenseMatrix* dv_dv = elmats(0, 0);
mfem::DenseMatrix* dv_drho = elmats(0, 1);
if (dv_dv) *dv_dv = 0.0;
if (dv_drho) *dv_drho = 0.0;
if (!dv_drho) return;
mfem::DenseMatrix dshape_v_ref(dof_v, dim), dshape_v_phys(dof_v, dim);
mfem::Vector shape_rho(dof_rho);
const mfem::IntegrationRule* ir = &mfem::IntRules.Get(fe_v->GetGeomType(), 2 * fe_v->GetOrder());
for (int q = 0; q < ir->GetNPoints(); ++q) {
using Scalar = EOS_T::value_type;
xad::Tape<Scalar> tape;
const mfem::IntegrationPoint& ip = ir->IntPoint(q);
Tr.SetIntPoint(&ip);
auto [J_inv, detJ, weight] = m_map.GetQuadratureContext(Tr, ip);
fe_v->CalcDShape(ip, dshape_v_ref);
mfem::Mult(dshape_v_ref, J_inv, dshape_v_phys);
fe_rho->CalcShape(ip, shape_rho);
EOS_T x_rho(0.0);
tape.registerInput(x_rho);
tape.newRecording();
for (int i = 0; i < dof_rho; ++i) {
x_rho += rho_dofs(i) * shape_rho(i);
}
if (x_rho < 1e-15) x_rho = EOS_T(1e-15);
EOS_T x_P = m_eos(x_rho, EOS_T(0.0));
tape.registerOutput(x_P);
x_P.setAdjoint(1.0);
tape.computeAdjoints();
double dP_drho = x_rho.getAdjoint();
double debug_K = 1.5;
double debug_n = 3.0;
double analytic_dp = debug_K * (1.0 + 1.0 / debug_n) * std::pow(xad::value(x_rho), 1.0 / debug_n);
double ad_err = std::abs(dP_drho - analytic_dp);
for (int i = 0; i < dof_v; ++i) {
for (int c = 0; c < dim; ++c) {
int row = i + c * dof_v;
for (int j = 0; j < dof_rho; ++j) {
int col = j;
double term = dshape_v_phys(i, c) * dP_drho * shape_rho(j);
(*dv_drho)(row, col) -= term * weight;
}
}
}
}
}
}

View File

@@ -0,0 +1,32 @@
module;
#include <mfem.hpp>
export module mean_field:integrators.viscosity;
import :mapping.domain_mapper;
export namespace mean_field::integrators {
class ViscosityIntegrator : public mfem::BlockNonlinearFormIntegrator {
public:
ViscosityIntegrator(const mapping::DomainMapper& map, double mu, int quad_boost);
void SetMu(const double mu);
void AssembleElementVector(
const mfem::Array<const mfem::FiniteElement *> &el,
mfem::ElementTransformation &Tr,
const mfem::Array<const mfem::Vector *> &elfun,
const mfem::Array<mfem::Vector *> &elvec
) override;
void AssembleElementGrad(
const mfem::Array<const mfem::FiniteElement *> &el,
mfem::ElementTransformation &Tr,
const mfem::Array<const mfem::Vector *> &elfun,
const mfem::Array2D<mfem::DenseMatrix *> &elmats
) override;
private:
const mapping::DomainMapper& m_map;
double m_mu;
int m_quad_boost;
};
}

View File

@@ -0,0 +1,95 @@
module;
#include "mean_field.h"
export module mean_field:mapping.coefficients;
export import :mapping.domain_mapper;
export import :mapping.types;
export namespace mean_field::mapping {
class MappedScalarCoefficient : public mfem::Coefficient {
public:
MappedScalarCoefficient(
const DomainMapper &map,
mfem::Coefficient &coeff,
COORDINATE_SPACE coord_space = COORDINATE_SPACE::PHYSICAL
);
double Eval(
mfem::ElementTransformation &T,
const mfem::IntegrationPoint &ip
) override;
private:
static double eval_at_point(
mfem::Coefficient &c,
mfem::ElementTransformation &T,
const mfem::IntegrationPoint &ip
);
private:
const DomainMapper &m_map;
mfem::Coefficient &m_coeff;
COORDINATE_SPACE m_coord_space;
};
class MappedDiffusionCoefficient : public mfem::MatrixCoefficient {
public:
MappedDiffusionCoefficient(
const DomainMapper &map,
mfem::Coefficient &sigma,
int dim
);
MappedDiffusionCoefficient(
const DomainMapper &map,
mfem::MatrixCoefficient &sigma
);
void Eval(mfem::DenseMatrix &K, mfem::ElementTransformation &T, const mfem::IntegrationPoint &ip) override;
private:
const DomainMapper &m_map;
mfem::Coefficient *m_scalar;
mfem::MatrixCoefficient *m_tensor;
};
class MappedVectorCoefficient : public mfem::VectorCoefficient {
public:
MappedVectorCoefficient(
const DomainMapper &map,
mfem::VectorCoefficient &coeff
);
void Eval(mfem::Vector &V, mfem::ElementTransformation &T, const mfem::IntegrationPoint &ip) override;
private:
const DomainMapper &m_map;
mfem::VectorCoefficient &m_coeff;
};
class PhysicalPositionFunctionCoefficient : public mfem::Coefficient {
public:
using Func = std::function<double(const mfem::Vector &x)>;
PhysicalPositionFunctionCoefficient(
const DomainMapper &map,
Func f
);
double Eval(mfem::ElementTransformation &T, const mfem::IntegrationPoint &ip) override;
private:
Func m_f;
const DomainMapper &m_map;
};
class MappedHDivMassCoefficient final : public mfem::MatrixCoefficient {
public:
MappedHDivMassCoefficient(const DomainMapper& map, const int dim);
void Eval(mfem::DenseMatrix& matrix, mfem::ElementTransformation& transformation, const mfem::IntegrationPoint& integration_point) override;
private:
const DomainMapper& m_map;
};
}

View File

@@ -0,0 +1,103 @@
module;
#include "mean_field.h"
export module mean_field:mapping.domain_mapper;
export namespace mean_field::mapping {
class DomainMapper {
public:
struct VolumeQuadratureContext {
mfem::DenseMatrix J_inv;
double detJ;
double weight;
};
struct FaceQuadratureContext {
mfem::Vector normal;
double ds;
double v_dot_n_scale;
};
public:
explicit DomainMapper(const double r_star_ref, const double r_inf_ref);
explicit DomainMapper(const mfem::GridFunction &d, const double r_star_ref, const double r_inf_ref);
[[nodiscard]] bool is_vacuum(const mfem::ElementTransformation &T) const;
void SetDisplacement(const mfem::GridFunction &d);
[[nodiscard]] bool IsIdentity() const;
void ResetDisplacement();
void ComputeJacobian(mfem::ElementTransformation &T, mfem::DenseMatrix &J) const;
double ComputeDetJ(mfem::ElementTransformation &T, const mfem::IntegrationPoint &ip) const;
void ComputeMappedDiffusionTensor(mfem::ElementTransformation &T, mfem::DenseMatrix &D) const;
void ComputeInverseJacobian(mfem::ElementTransformation &T, mfem::DenseMatrix &JInv) const;
VolumeQuadratureContext GetQuadratureContext(mfem::ElementTransformation &T, const mfem::IntegrationPoint &ip) const;
FaceQuadratureContext GetFaceQuadratureContext(mfem::FaceElementTransformations &T, const mfem::IntegrationPoint &ip) const;
void GetPhysicalPoint(mfem::ElementTransformation &T, const mfem::IntegrationPoint &ip, mfem::Vector &x_phys) const;
void GetVectorValue(const int i, const mfem::IntegrationPoint &ip, mfem::Vector &val) const;
[[nodiscard]] const mfem::GridFunction *GetDisplacement() const;
[[nodiscard]] double GetPhysInfRadius() const;
[[nodiscard]] size_t GetCacheHits() const;
[[nodiscard]] size_t GetCacheMisses() const;
[[nodiscard]] double GetCacheHitRate() const;
void ResetCacheStats() const;
private:
void InitAllScratchSpaces() const;
void ApplyKelvinMapping(const mfem::Vector &x_ref, mfem::Vector &x_phys) const;
void ComputeKelvinJacobian(const mfem::Vector &x_ref, const mfem::Vector &x_disp, const mfem::DenseMatrix &J_D, mfem::DenseMatrix &J) const;
void InvalidateCache() const;
void UpdateElementCache(const mfem::ElementTransformation &T) const;
private:
const mfem::GridFunction *m_d;
std::unique_ptr<mfem::GridFunction> m_internal_d;
const int m_dim{3};
const int m_vacuum_attr{3};
const double m_r_star_ref{1.0};
const double m_r_inf_ref{2.0};
const double m_xi_clamp{0.9999};
mutable int m_cached_elem_id{-1};
mutable int m_cached_elem_type{mfem::ElementTransformation::ELEMENT};
mutable const mfem::FiniteElement *m_fe{nullptr};
mutable mfem::Vector m_elem_dofs;
mutable mfem::DenseMatrix m_dof_mat;
mutable mfem::DenseMatrix m_dshape;
mutable mfem::Vector m_shape;
mutable size_t m_cache_hits{0};
mutable size_t m_cache_misses{0};
mutable mfem::DenseMatrix m_J_D;
mutable mfem::DenseMatrix m_J_temp;
mutable mfem::DenseMatrix m_JInv_temp;
mutable mfem::Vector m_x_ref;
mutable mfem::Vector m_x_disp;
mutable mfem::Vector m_d_val;
};
}

View File

@@ -0,0 +1,10 @@
module;
#include <cstdint>
export module mean_field:mapping.types;
namespace mean_field::mapping {
enum class COORDINATE_SPACE : uint8_t {
PHYSICAL,
REFERENCE
};
}

View File

@@ -0,0 +1,21 @@
export module mean_field;
export import :fem;
export import :utils.misc;
export import :utils.user;
export import :utils.domain;
export import :physics.gravity;
export import :physics.contexts;
export import :boundary.contexts;
export import :analysis.integral;
export import :mapping.domain_mapper;
export import :mapping.coefficients;
export import :integrators.advection;
export import :integrators.centrifugal;
export import :integrators.gravity;
export import :integrators.coriolis;
export import :integrators.mass_continuity;
export import :integrators.pressure_gradient;
export import :integrators.viscosity;
export import :quadrature.policy;
export import :quadrature.mfem;

View File

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

View File

@@ -0,0 +1,45 @@
module;
#include <mfem.hpp>
export module mean_field:physics.gravity;
export import :fem;
export namespace mean_field::physics {
struct GravitySolution {
mfem::ParGridFunction gradPhi;
mfem::ParGridFunction phi;
explicit GravitySolution(fem::FEM& fem): gradPhi(fem.RT_fes.get()), phi(fem.L2_fes.get()) {}
};
GravitySolution grav_potential(
fem::FEM &f,
const utils::Args &args,
const mfem::GridFunction &rho,
bool phi_warm = false
);
mfem::GridFunction get_potential(
fem::FEM &fem,
const utils::Args &args,
const mfem::GridFunction &rho,
bool warm = false
);
mfem::DenseMatrix compute_quadrupole_moment_tensor(
const fem::FEM &fem,
const mfem::GridFunction &rho,
const mfem::Vector &com
);
double l2_multipole_potential(
const fem::FEM &fem,
double total_mass,
const mfem::Vector &phys_x
);
void update_stiffness_matrix(fem::FEM &fem);
}

View File

@@ -0,0 +1,9 @@
module;
#include <mfem.hpp>
export module mean_field:physics.solid_body;
export import :fem;
export namespace mean_field::physics {
double compute_moment_of_inertia(const fem::FEM &fem, const mfem::GridFunction &rho_ref);
}

View File

@@ -0,0 +1,198 @@
module;
#include <mfem.hpp>
#include <utility>
export module mean_field:quadrature.mfem;
export import :quadrature.policy;
export namespace mean_field::quadrature {
struct MfemRule {
Resolution resolution;
const mfem::IntegrationRule* integration_rule;
};
class RuleFactory {
public:
explicit RuleFactory(
Policy policy
);
MfemRule get(
const Query& query,
mfem::Geometry::Type geometry
) const;
MfemRule get(
Term term,
QuadratureRole role,
mfem::Geometry::Type geometry,
int base_order,
utils::DOMAINS domain = utils::DOMAINS::ALL,
MappingKind mapping = MappingKind::none
) const;
Resolution configure_gravity_hdiv_mass(
mfem::VectorFEMassIntegrator& integrator,
QuadratureRole role,
const mfem::FiniteElement& element,
const mfem::ElementTransformation& transformation,
utils::DOMAINS domain = utils::DOMAINS::ALL,
MappingKind mapping = MappingKind::none
) const;
Resolution configure_gravity_divergence(
mfem::VectorFEDivergenceIntegrator& integrator,
QuadratureRole role,
const mfem::FiniteElement& trial_element,
const mfem::FiniteElement& test_element,
const mfem::ElementTransformation& transformation,
utils::DOMAINS domain = utils::DOMAINS::ALL,
MappingKind mapping = MappingKind::none
) const;
Resolution configure_gravity_boundary(
mfem::VectorFEBoundaryFluxLFIntegrator& integrator,
QuadratureRole role,
const mfem::FiniteElement& boundary_element,
utils::DOMAINS domain = utils::DOMAINS::VACUUM,
MappingKind mapping = MappingKind::none
) const;
Resolution configure_gravity_source(
mfem::DomainLFIntegrator& integrator,
QuadratureRole role,
const mfem::FiniteElement& test_element,
const mfem::ElementTransformation& transformation,
int coefficient_order,
utils::DOMAINS domain = utils::DOMAINS::STELLAR,
MappingKind mapping = MappingKind::none
) const;
template<typename IntegratorType>
Resolution configure(
IntegratorType& integrator,
Term term,
QuadratureRole role,
mfem::Geometry::Type geometry,
int base_order,
utils::DOMAINS domain = utils::DOMAINS::ALL,
MappingKind mapping = MappingKind::none
) const;
private:
Policy policy;
};
RuleFactory::RuleFactory(Policy policy) : policy(std::move(policy)) {}
MfemRule RuleFactory::get(
const Query& query,
const mfem::Geometry::Type geometry
) const {
const Resolution resolution = policy.resolve(query);
const mfem::IntegrationRule& integration_rule = mfem::IntRules.Get(geometry, resolution.order);
return {.resolution = resolution, .integration_rule = &integration_rule};
}
MfemRule RuleFactory::get(
const Term term,
const QuadratureRole role,
const mfem::Geometry::Type geometry,
const int base_order,
const utils::DOMAINS domain,
const MappingKind mapping
) const {
Query query{.term = term};
query.domain = domain;
query.mapping = mapping;
query.role = role;
query.base_order = base_order;
return get(query, geometry);
}
Resolution RuleFactory::configure_gravity_hdiv_mass(
mfem::VectorFEMassIntegrator& integrator,
const QuadratureRole role,
const mfem::FiniteElement& element,
const mfem::ElementTransformation& transformation,
const utils::DOMAINS domain,
const MappingKind mapping
) const {
const int base_order = 2 * element.GetOrder() + transformation.OrderW();
return configure(integrator, Term::gravity_hdiv_mass, role, element.GetGeomType(), base_order, domain, mapping);
}
Resolution RuleFactory::configure_gravity_divergence(
mfem::VectorFEDivergenceIntegrator& integrator,
const QuadratureRole role,
const mfem::FiniteElement& trial_element,
const mfem::FiniteElement& test_element,
const mfem::ElementTransformation& transformation,
const utils::DOMAINS domain,
const MappingKind mapping
) const {
const Query query = {
.term = Term::gravity_divergence,
.role = role,
.domain = domain,
.mapping = mapping,
.trial_order = trial_element.GetOrder(),
.test_order = test_element.GetOrder(),
.geometry_weight_order = transformation.OrderW()
};
const auto [resolution, integration_rule] = get(query, trial_element.GetGeomType());
integrator.SetIntegrationRule(*integration_rule);
return resolution;
}
Resolution RuleFactory::configure_gravity_boundary(
mfem::VectorFEBoundaryFluxLFIntegrator& integrator,
const QuadratureRole role,
const mfem::FiniteElement& boundary_element,
const utils::DOMAINS domain,
const MappingKind mapping
) const {
const int base_order = 2 * boundary_element.GetOrder();
return configure(integrator, Term::gravity_boundary, role, boundary_element.GetGeomType(), base_order, domain, mapping);
}
Resolution RuleFactory::configure_gravity_source(
mfem::DomainLFIntegrator& integrator,
const QuadratureRole role,
const mfem::FiniteElement& test_element,
const mfem::ElementTransformation& transformation,
const int coefficient_order,
const utils::DOMAINS domain,
const MappingKind mapping
) const {
const Query query = {
.term = Term::gravity_source,
.role = role,
.domain = domain,
.mapping = mapping,
.test_order = test_element.GetOrder(),
.coefficient_order = coefficient_order,
.geometry_weight_order = transformation.OrderW()
};
const auto [resolution, integration_rule] = get(query, test_element.GetGeomType());
integrator.SetIntegrationRule(*integration_rule);
return resolution;
}
template<typename IntegratorType>
Resolution RuleFactory::configure(
IntegratorType& integrator,
const Term term,
const QuadratureRole role,
const mfem::Geometry::Type geometry,
const int base_order,
const utils::DOMAINS domain,
const MappingKind mapping
) const {
const auto [resolution, integration_rule] = get(term, role, geometry, base_order, domain, mapping);
integrator.SetIntegrationRule(*integration_rule);
return resolution;
}
}

View File

@@ -0,0 +1,256 @@
module;
#include <algorithm>
#include <optional>
#include <stdexcept>
#include <utility>
export module mean_field:quadrature.policy;
export import :utils.misc;
export namespace mean_field::quadrature {
enum class Term {
gravity_hdiv_mass,
gravity_divergence,
gravity_source,
gravity_boundary,
density_projection,
mass_conservation,
center_of_mass,
quadrupole,
gravitational_energy,
virial,
error_norm
};
enum class QuadratureRole {
discretization,
preconditioner,
diagnostic,
projection
};
enum class MappingKind {
none,
affine,
general,
kelvin
};
enum class Mode {
fast,
production,
reference,
convergence
};
struct RuleControl {
std::optional<int> fixed_order;
int boost = 0;
};
struct RoleControls {
RuleControl discretization;
RuleControl preconditioner;
RuleControl diagnostic;
RuleControl projection;
};
struct RuleSet {
RuleControl gravity_hdiv_mass;
RuleControl gravity_divergence;
RuleControl gravity_source;
RuleControl gravity_boundary;
RuleControl density_projection;
RuleControl mass_conservation;
RuleControl center_of_mass;
RuleControl quadrupole;
RuleControl gravitational_energy;
RuleControl virial;
RuleControl error_norm;
RoleControls roles;
RuleControl fallback;
};
struct Query {
Term term;
QuadratureRole role = QuadratureRole::discretization;
utils::DOMAINS domain = utils::DOMAINS::ALL;
MappingKind mapping = MappingKind::none;
int trial_order = 0;
int test_order = 0;
int coefficient_order = 0;
int geometry_weight_order = 0;
std::optional<int> base_order;
};
struct Resolution {
int base_order;
int boost;
int order;
bool used_fixed_order;
};
struct QuadratureTermOptions {
std::optional<int> fixed_order;
int additional_boost = 0;
};
struct QuadratureManifestOptions {
bool enabled = false;
bool include_repeated_queries = false;
std::optional<std::string> output_file;
};
struct QuadratureValidationOptions {
bool require_explicit_base_order = false;
bool require_explicit_mfem_rule = false;
bool reject_negative_boosts = true;
bool report_unused_overrides = true;
};
struct QuadratureRoleOptions {
QuadratureTermOptions discretization;
QuadratureTermOptions preconditioner;
QuadratureTermOptions diagnostic;
QuadratureTermOptions projection;
};
struct QuadratureOptions {
Mode mode = Mode::production;
int global_boost = 0;
std::optional<int> fallback_fixed_order;
QuadratureTermOptions gravity_hdiv_mass;
QuadratureTermOptions gravity_divergence;
QuadratureTermOptions gravity_source;
QuadratureTermOptions gravity_boundary;
QuadratureTermOptions density_projection;
QuadratureTermOptions mass_conservation;
QuadratureTermOptions center_of_mass;
QuadratureTermOptions quadrupole;
QuadratureTermOptions gravitational_energy;
QuadratureTermOptions virial;
QuadratureTermOptions error_norm;
QuadratureRoleOptions roles;
std::vector<int> convergence_boosts = {0, 2, 4};
QuadratureManifestOptions manifest;
QuadratureValidationOptions validation;
};
RuleSet make_rule_set(Mode mode, int global_boost = 0);
class Policy {
public:
explicit Policy(RuleSet rule_set);
Resolution resolve(const Query& query) const;
private:
const RuleControl& get_control(Term term) const;
static int compute_base_order(const Query& query) ;
const RuleControl& get_role_control(QuadratureRole role) const;
RuleSet rule_set;
};
RuleSet make_rule_set(const Mode mode, const int global_boost) {
RuleSet rule_set;
switch (mode) {
case Mode::fast:
case Mode::production:
case Mode::convergence:
rule_set.fallback.boost = global_boost;
break;
case Mode::reference:
rule_set.fallback.boost = global_boost + 8;
break;
}
return rule_set;
}
Policy::Policy(RuleSet rule_set) : rule_set(std::move(rule_set)) {}
Resolution Policy::resolve(const Query& query) const {
const int base_order = compute_base_order(query);
const RuleControl& term_control = get_control(query.term);
const RuleControl& role_control = get_role_control(query.role);
std::optional<int> fixed_order;
if (term_control.fixed_order.has_value()) {
fixed_order = term_control.fixed_order;
} else if (role_control.fixed_order.has_value()) {
fixed_order = role_control.fixed_order;
} else {
fixed_order = rule_set.fallback.fixed_order;
}
if (fixed_order.has_value()) {
if (*fixed_order < 0) {
throw std::invalid_argument("Quadrature fixed order cannot be negative.");
}
return {.base_order = base_order, .boost = 0, .order = *fixed_order, .used_fixed_order = true};
}
const int boost = rule_set.fallback.boost + role_control.boost + term_control.boost;
const int order = base_order + boost;
if (order < 0) {
throw std::invalid_argument("Resolved quadrature order cannot be negative.");
}
return {.base_order = base_order, .boost = boost, .order = order, .used_fixed_order = false};
}
const RuleControl& Policy::get_control(const Term term) const {
switch (term) {
case Term::gravity_hdiv_mass: return rule_set.gravity_hdiv_mass;
case Term::gravity_divergence: return rule_set.gravity_divergence;
case Term::gravity_source: return rule_set.gravity_source;
case Term::gravity_boundary: return rule_set.gravity_boundary;
case Term::density_projection: return rule_set.density_projection;
case Term::mass_conservation: return rule_set.mass_conservation;
case Term::center_of_mass: return rule_set.center_of_mass;
case Term::quadrupole: return rule_set.quadrupole;
case Term::gravitational_energy: return rule_set.gravitational_energy;
case Term::virial: return rule_set.virial;
case Term::error_norm: return rule_set.error_norm;
}
throw std::logic_error("Unknown quadrature term.");
}
int Policy::compute_base_order(const Query& query) {
if (query.base_order.has_value()) {
if (*query.base_order < 0) {
throw std::invalid_argument("Quadrature base order cannot be negative.");
}
return *query.base_order;
}
if (query.trial_order < 0 || query.test_order < 0 || query.coefficient_order < 0 || query.geometry_weight_order < 0) {
throw std::invalid_argument("Quadrature query orders cannot be negative.");
}
int trial_order = query.trial_order;
if (query.term == Term::gravity_divergence) {
trial_order = std::max(0, trial_order - 1);
}
return trial_order + query.test_order + query.coefficient_order + query.geometry_weight_order;
}
const RuleControl& Policy::get_role_control(const QuadratureRole role) const {
switch (role) {
case QuadratureRole::discretization: return rule_set.roles.discretization;
case QuadratureRole::preconditioner: return rule_set.roles.preconditioner;
case QuadratureRole::diagnostic: return rule_set.roles.diagnostic;
case QuadratureRole::projection: return rule_set.roles.projection;
}
throw std::logic_error("Unknown quadrature role.");
}
}

View File

@@ -0,0 +1,24 @@
module;
#include <mfem.hpp>
export module mean_field:utils.domain;
export import :fem;
export import :mapping.domain_mapper;
export import :boundary.contexts;
export namespace mean_field::utils {
bool get_reference_point(
const fem::FEM &fem,
const mfem::Vector &x_phys_target,
mfem::Vector &x_ref
);
double eval_grid_function_at_point(
const fem::FEM &fem,
const mfem::GridFunction &u,
const mfem::Vector &x,
mapping::COORDINATE_SPACE vspace = mapping::COORDINATE_SPACE::REFERENCE,
mapping::COORDINATE_SPACE rspace = mapping::COORDINATE_SPACE::PHYSICAL
);
}

View File

@@ -0,0 +1,101 @@
module;
#include <string_view>
#include <functional>
#include <expected>
#include <mfem.hpp>
#include "xad_promote_polyfill.h"
#include <XAD/XAD.hpp>
export module mean_field:utils.misc;
import :boundary.contexts;
export namespace mean_field::utils {
constexpr double APPROX_MAX_ACCEPTABLE_POTENTIAL_ERROR_SI_BURNING = 1e-4;
bool is_vacuum(const mfem::ElementTransformation &Tr, mfem::Array<mfem::Vector*> elvec) {
if (Tr.Attribute == 3) {
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;
}
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";
constexpr double G = 1.0;
constexpr double MASS = 1.0;
constexpr double RADIUS = 1.0;
[[maybe_unused]] constexpr char HOST[10] = "localhost";
[[maybe_unused]] constexpr int PORT = 19916;
template<typename T>
concept is_xad =
std::is_same_v<T, xad::AReal<long double> >
|| std::is_same_v<T, xad::AReal<double> >
|| std::is_same_v<T, xad::AReal<float> >;
template<typename T>
concept is_real = std::is_floating_point_v<T> || is_xad<T>;
template<is_real T>
using EOS_P = std::function<T(const T& rho, const T& temp)>;
enum class DOMAINS : uint8_t {
CORE = 1 << 0,
ENVELOPE = 1 << 1,
VACUUM = 1 << 2,
STELLAR = CORE | ENVELOPE,
ALL = CORE | ENVELOPE | VACUUM
};
DOMAINS operator|(
DOMAINS lhs,
DOMAINS rhs
);
DOMAINS operator&(
DOMAINS lhs,
DOMAINS rhs
);
void populate_element_mask(
const mfem::Mesh* mesh,
DOMAINS domain,
mfem::Array<int> &mask
);
void populate_domain_tdofs(
const mfem::ParFiniteElementSpace *fes,
const mfem::Array<int> &element_mask,
mfem::Array<int> &ess_tdof
);
std::expected<boundary::Bounds, boundary::BoundsError> discover_bounds(
const mfem::Mesh *mesh,
int vacuum_attr
);
int get_mesh_order(
const mfem::Mesh &mesh
);
}

View File

@@ -0,0 +1,36 @@
module;
#include <string>
export module mean_field:utils.user;
export import :quadrature.policy;
export namespace mean_field::utils {
struct potential {
double rtol;
double atol;
int max_iters;
};
struct rot {
bool enabled;
double omega;
double L;
};
struct Args {
std::string mesh_file;
potential p{};
rot r{};
bool verbose{};
double index{};
double mass{};
double c{};
int quad_boost{0};
int max_iters{};
double tol{};
quadrature::QuadratureOptions quadrature{};
};
}