feat(libmeanfield): centrifugal + pressure

This commit is contained in:
2026-08-04 14:24:55 -04:00
parent 9bc4f2758a
commit dc912fd15e
115 changed files with 260058 additions and 163261 deletions

View File

@@ -10,30 +10,38 @@ namespace mean_field::mapping {
//////////////////////////////
MappedScalarCoefficient::MappedScalarCoefficient(
const DomainMapper &map,
mfem::Coefficient &coeff,
Coefficient &coeff,
const COORDINATE_SPACE coord_space
) : m_map(map),
m_coeff(coeff),
m_coord_space(coord_space) {};
)
: m_map(map),
m_coeff(coeff),
m_coord_space(coord_space) { };
double MappedScalarCoefficient::Eval(mfem::ElementTransformation &T, const mfem::IntegrationPoint &ip) {
double MappedScalarCoefficient::Eval(
mfem::ElementTransformation &T,
const mfem::IntegrationPoint &ip
) {
T.SetIntPoint(&ip);
double f_val = 0.0;
switch (m_coord_space) {
case COORDINATE_SPACE::PHYSICAL: {
f_val = eval_at_point(m_coeff, T, ip);
const double detJ = m_map.ComputeDetJ(T, ip);
return f_val * fabs(detJ);
}
case COORDINATE_SPACE::REFERENCE: {
f_val = m_coeff.Eval(T, ip);
return f_val;
}
case COORDINATE_SPACE::PHYSICAL: {
f_val = eval_at_point(m_coeff, T, ip);
const double detJ = m_map.ComputeDetJ(T, ip);
return f_val * fabs(detJ);
}
case COORDINATE_SPACE::REFERENCE: {
f_val = m_coeff.Eval(T, ip);
return f_val;
}
}
}
double MappedScalarCoefficient::eval_at_point(mfem::Coefficient &c, mfem::ElementTransformation &T, const mfem::IntegrationPoint &ip) {
double MappedScalarCoefficient::eval_at_point(
Coefficient &c,
mfem::ElementTransformation &T,
const mfem::IntegrationPoint &ip
) {
return c.Eval(T, ip);
}
@@ -45,22 +53,26 @@ namespace mean_field::mapping {
const DomainMapper &map,
mfem::Coefficient &sigma,
const int dim
) : mfem::MatrixCoefficient(dim),
m_map(map),
m_scalar(&sigma),
m_tensor(nullptr) {
};
)
: MatrixCoefficient(dim),
m_map(map),
m_scalar(&sigma),
m_tensor(nullptr) { };
MappedDiffusionCoefficient::MappedDiffusionCoefficient(
const DomainMapper &map,
mfem::MatrixCoefficient &sigma
) : mfem::MatrixCoefficient(sigma.GetHeight()),
m_map(map),
m_scalar(nullptr),
m_tensor(&sigma) {
};
MatrixCoefficient &sigma
)
: MatrixCoefficient(sigma.GetHeight()),
m_map(map),
m_scalar(nullptr),
m_tensor(&sigma) { };
void MappedDiffusionCoefficient::Eval(mfem::DenseMatrix &K, mfem::ElementTransformation &T, const mfem::IntegrationPoint &ip) {
void MappedDiffusionCoefficient::Eval(
mfem::DenseMatrix &K,
mfem::ElementTransformation &T,
const mfem::IntegrationPoint &ip
) {
const int dim = height;
T.SetIntPoint(&ip);
@@ -90,13 +102,17 @@ namespace mean_field::mapping {
///////////////////////////////
MappedVectorCoefficient::MappedVectorCoefficient(
const DomainMapper &map,
mfem::VectorCoefficient &coeff
) : mfem::VectorCoefficient(coeff.GetVDim()),
m_map(map),
m_coeff(coeff) {
};
VectorCoefficient &coeff
)
: VectorCoefficient(coeff.GetVDim()),
m_map(map),
m_coeff(coeff) { };
void MappedVectorCoefficient::Eval(mfem::Vector &V, mfem::ElementTransformation &T, const mfem::IntegrationPoint &ip) {
void MappedVectorCoefficient::Eval(
mfem::Vector &V,
mfem::ElementTransformation &T,
const mfem::IntegrationPoint &ip
) {
const int dim = vdim;
T.SetIntPoint(&ip);
@@ -118,24 +134,32 @@ namespace mean_field::mapping {
PhysicalPositionFunctionCoefficient::PhysicalPositionFunctionCoefficient(
const DomainMapper &map,
Func f // std::function<double(const mfem::Vector&)>
) : m_f(std::move(f)),
m_map(map) {};
)
: m_f(std::move(f)),
m_map(map) { };
double PhysicalPositionFunctionCoefficient::Eval(mfem::ElementTransformation &T, const mfem::IntegrationPoint &ip) {
double PhysicalPositionFunctionCoefficient::Eval(
mfem::ElementTransformation &T,
const mfem::IntegrationPoint &ip
) {
T.SetIntPoint(&ip);
mfem::Vector x;
m_map.GetPhysicalPoint(T, ip, x);
return m_f(x);
}
MappedHDivMassCoefficient::MappedHDivMassCoefficient(const DomainMapper& map, const int dim)
: mfem::MatrixCoefficient(dim),
m_map(map) {}
MappedHDivMassCoefficient::MappedHDivMassCoefficient(
const DomainMapper &map,
const int dim
)
: MatrixCoefficient(dim),
m_map(map) {
}
void MappedHDivMassCoefficient::Eval(
mfem::DenseMatrix& matrix,
mfem::ElementTransformation& transformation,
const mfem::IntegrationPoint& integration_point
mfem::DenseMatrix &matrix,
mfem::ElementTransformation &transformation,
const mfem::IntegrationPoint &integration_point
) {
transformation.SetIntPoint(&integration_point);
@@ -144,9 +168,12 @@ namespace mean_field::mapping {
const double map_determinant = map_jacobian.Det();
MFEM_VERIFY(map_determinant > 0.0, "Domain mapping has a non-positive Jacobian determinant.");
MFEM_VERIFY(
map_determinant > 0.0,
"Domain mapping has a non-positive Jacobian determinant."
);
mfem::MultAtB(map_jacobian, map_jacobian, matrix);
matrix *= 1.0 / std::abs(map_determinant);
}
}
} // namespace mean_field::mapping

View File

@@ -0,0 +1,266 @@
module;
#include <cmath>
#include <mfem.hpp>
#include <stdexcept>
module mean_field;
namespace {
bool vector_is_finite(const mfem::Vector &vector) {
for (int i = 0; i < vector.Size(); ++i) {
if (!std::isfinite(vector(i)))
return false;
}
return true;
}
bool matrix_is_finite(const mfem::DenseMatrix &matrix) {
for (int i = 0; i < matrix.Height(); ++i) {
for (int j = 0; j < matrix.Width(); ++j) {
if (!std::isfinite(matrix(i, j)))
return false;
}
}
return true;
}
} // namespace
namespace mean_field::mapping::compactification {
KelvinCompactification::KelvinCompactification(
options::KelvinCompactificationOptions options
)
: m_options(options) {
if (!std::isfinite(m_options.r_star_ref) ||
!std::isfinite(m_options.r_inf_ref)) {
throw std::invalid_argument(
"Kelvin compactification radii must be finite."
);
}
if (m_options.r_star_ref <= 0.0 ||
m_options.r_inf_ref <= m_options.r_star_ref) {
throw std::invalid_argument(
"Kelvin compactification requires 0 < r_star_ref < r_inf_ref."
);
}
if (!std::isfinite(m_options.coordinate_tolerance) ||
m_options.coordinate_tolerance < 0.0 ||
m_options.coordinate_tolerance >= 1.0) {
throw std::invalid_argument(
"Kelvin compactification coordinate tolerance must be finite "
"and lie "
"in [0, 1)."
);
}
}
MappingStatus KelvinCompactification::ComputeRadialFactors(
const double compactification_coordinate,
RadialFactors &factors
) const {
if (!std::isfinite(compactification_coordinate))
return MappingStatus::non_finite_input;
const double tolerance = m_options.coordinate_tolerance;
if (compactification_coordinate < -tolerance ||
compactification_coordinate > 1.0 + tolerance) {
return MappingStatus::outside_reference_domain;
}
double coordinate = compactification_coordinate;
if (coordinate < 0.0)
coordinate = 0.0;
if (coordinate >= 1.0 - tolerance) {
return MappingStatus::at_compactified_infinity;
}
const double radial_extent = m_options.r_inf_ref - m_options.r_star_ref;
const double computational_radius =
m_options.r_star_ref + coordinate * radial_extent;
if (!std::isfinite(computational_radius) ||
computational_radius <= 0.0) {
return MappingStatus::invalid_reference_radius;
}
const double one_minus_coordinate = 1.0 - coordinate;
const double denominator = computational_radius * one_minus_coordinate;
if (!std::isfinite(denominator) || denominator <= 0.0) {
return MappingStatus::non_finite_result;
}
const double scale = m_options.r_star_ref / denominator;
const double scale_derivative =
scale *
(1.0 / one_minus_coordinate - radial_extent / computational_radius);
if (!std::isfinite(scale) || !std::isfinite(scale_derivative)) {
return MappingStatus::non_finite_result;
}
factors.coordinate = coordinate;
factors.computational_radius = computational_radius;
factors.scale = scale;
factors.scale_derivative = scale_derivative;
return MappingStatus::valid;
}
MappingStatus KelvinCompactification::Evaluate(
const ExteriorMapInput &input,
ExteriorMapResult &result
) const {
const int dimension = input.reference_position.Size();
if (dimension <= 0 || input.displaced_position.Size() != dimension ||
input.compactification_coordinate_gradient.Size() != dimension) {
return MappingStatus::invalid_dimension;
}
if (input.displacement_jacobian.Height() != dimension ||
input.displacement_jacobian.Width() != dimension) {
return MappingStatus::invalid_dimension;
}
if (!vector_is_finite(input.reference_position) ||
!vector_is_finite(input.displaced_position) ||
!vector_is_finite(input.compactification_coordinate_gradient) ||
!matrix_is_finite(input.displacement_jacobian)) {
return MappingStatus::non_finite_input;
}
RadialFactors factors;
const MappingStatus factor_status =
ComputeRadialFactors(input.compactification_coordinate, factors);
if (factor_status != MappingStatus::valid)
return factor_status;
result.physical_position.SetSize(dimension);
result.mapping_jacobian.SetSize(dimension, dimension);
for (int i = 0; i < dimension; ++i) {
result.physical_position(i) =
factors.scale * input.displaced_position(i);
for (int j = 0; j < dimension; ++j) {
const double scale_gradient =
factors.scale_derivative *
input.compactification_coordinate_gradient(j);
result.mapping_jacobian(i, j) =
factors.scale * input.displacement_jacobian(i, j) +
input.displaced_position(i) * scale_gradient;
}
}
if (!vector_is_finite(result.physical_position) ||
!matrix_is_finite(result.mapping_jacobian)) {
return MappingStatus::non_finite_result;
}
const double mapping_determinant = result.mapping_jacobian.Det();
if (!std::isfinite(mapping_determinant))
return MappingStatus::non_finite_result;
if (mapping_determinant <= 0.0)
return MappingStatus::non_positive_determinant;
return MappingStatus::valid;
}
MappingStatus KelvinCompactification::EvaluateVariation(
const ExteriorMapInput &input,
const ExteriorMapResult &result,
const ExteriorMapDirection &direction,
ExteriorMapVariation &variation
) const {
const int dimension = input.reference_position.Size();
if (dimension <= 0 || input.displaced_position.Size() != dimension ||
input.compactification_coordinate_gradient.Size() != dimension) {
return MappingStatus::invalid_dimension;
}
if (input.displacement_jacobian.Height() != dimension ||
input.displacement_jacobian.Width() != dimension) {
return MappingStatus::invalid_dimension;
}
if (result.physical_position.Size() != dimension ||
result.mapping_jacobian.Height() != dimension ||
result.mapping_jacobian.Width() != dimension) {
return MappingStatus::invalid_dimension;
}
if (direction.displaced_position_variation.Size() != dimension ||
direction.displacement_jacobian_variation.Height() != dimension ||
direction.displacement_jacobian_variation.Width() != dimension) {
return MappingStatus::invalid_dimension;
}
if (!vector_is_finite(input.reference_position) ||
!vector_is_finite(input.displaced_position) ||
!vector_is_finite(input.compactification_coordinate_gradient) ||
!matrix_is_finite(input.displacement_jacobian)) {
return MappingStatus::non_finite_input;
}
if (!vector_is_finite(result.physical_position) ||
!matrix_is_finite(result.mapping_jacobian) ||
!vector_is_finite(direction.displaced_position_variation) ||
!matrix_is_finite(direction.displacement_jacobian_variation)) {
return MappingStatus::non_finite_input;
}
RadialFactors factors;
const MappingStatus factor_status =
ComputeRadialFactors(input.compactification_coordinate, factors);
if (factor_status != MappingStatus::valid)
return factor_status;
variation.physical_position_variation.SetSize(dimension);
variation.mapping_jacobian_variation.SetSize(dimension, dimension);
for (int i = 0; i < dimension; ++i) {
variation.physical_position_variation(i) =
factors.scale * direction.displaced_position_variation(i);
for (int j = 0; j < dimension; ++j) {
const double scale_gradient =
factors.scale_derivative *
input.compactification_coordinate_gradient(j);
variation.mapping_jacobian_variation(i, j) =
factors.scale *
direction.displacement_jacobian_variation(i, j) +
direction.displaced_position_variation(i) * scale_gradient;
}
}
if (!vector_is_finite(variation.physical_position_variation) ||
!matrix_is_finite(variation.mapping_jacobian_variation)) {
return MappingStatus::non_finite_result;
}
return MappingStatus::valid;
}
std::string_view KelvinCompactification::GetName() const noexcept {
return "KelvinCompactification";
}
double KelvinCompactification::GetReferenceStellarRadius() const noexcept {
return m_options.r_star_ref;
}
double KelvinCompactification::GetReferenceInfinityRadius() const noexcept {
return m_options.r_inf_ref;
}
double KelvinCompactification::GetCoordinateTolerance() const noexcept {
return m_options.coordinate_tolerance;
}
} // namespace mean_field::mapping::compactification

View File

@@ -2,26 +2,52 @@ module;
#include <mfem.hpp>
module mean_field;
import :mapping.types;
namespace {
double get_positive_map_jacobian(
const mean_field::mapping::DomainMapper &domain_mapper,
mfem::ElementTransformation &transformation,
const mfem::IntegrationPoint &integration_point,
mfem::DenseMatrix &map_jacobian
) {
transformation.SetIntPoint(&integration_point);
domain_mapper.ComputeJacobian(transformation, map_jacobian);
const double map_determinant = map_jacobian.Det();
MFEM_VERIFY(
map_determinant > 0.0,
"Domain mapping has a non-positive Jacobian determinant."
);
return map_determinant;
}
} // namespace
namespace mean_field::mapping {
DomainMapper::DomainMapper(
const double r_star_ref,
const double r_inf_ref
) : m_d(nullptr),
m_r_star_ref(r_star_ref),
m_r_inf_ref(r_inf_ref) {
)
: m_d(nullptr),
m_r_star_ref(r_star_ref),
m_r_inf_ref(r_inf_ref) {
InitAllScratchSpaces();
CalcIsIdentity() ? m_displacement_is_identity = true
: m_displacement_is_identity = false;
}
DomainMapper::DomainMapper(
const mfem::GridFunction &d,
const double r_star_ref,
const double r_inf_ref
) : m_d(&d),
m_dim(d.FESpace()->GetMesh()->Dimension()),
m_r_star_ref(r_star_ref),
m_r_inf_ref(r_inf_ref) {
)
: m_d(&d),
m_dim(d.FESpace()->GetMesh()->Dimension()),
m_r_star_ref(r_star_ref),
m_r_inf_ref(r_inf_ref) {
InitAllScratchSpaces();
CalcIsIdentity() ? m_displacement_is_identity = true
: m_displacement_is_identity = false;
}
bool DomainMapper::is_vacuum(const mfem::ElementTransformation &T) const {
@@ -29,7 +55,9 @@ namespace mean_field::mapping {
return T.Attribute == m_vacuum_attr;
} else if (T.ElementType == mfem::ElementTransformation::BDR_ELEMENT) {
return T.Attribute == m_vacuum_attr - 1;
// TODO: In a more robust code this should really be read from the stroid API to ensure that the vacuum boundary is really 1 - the vacuum material attribute
// TODO: In a more robust code this should really be read from the
// stroid API to ensure that the vacuum boundary is really 1 - the
// vacuum material attribute
}
return false;
}
@@ -37,28 +65,69 @@ namespace mean_field::mapping {
void DomainMapper::SetDisplacement(const mfem::GridFunction &d) {
if (m_dim != d.FESpace()->GetMesh()->Dimension()) {
const std::string err_msg = std::format(
"Dimension mismatch: DomainMapper is initialized for dimension {}, but provided displacement field has dimension {}.",
m_dim, d.FESpace()->GetMesh()->Dimension());
"Dimension mismatch: DomainMapper is initialized for dimension "
"{}, "
"but provided displacement field has "
"dimension {}.",
m_dim, d.FESpace()->GetMesh()->Dimension()
);
throw std::invalid_argument(err_msg);
}
m_d = &d;
InvalidateCache();
CalcIsIdentity() ? m_displacement_is_identity = true
: m_displacement_is_identity = false;
}
bool DomainMapper::IsIdentity() const {
return (m_d == nullptr);
bool DomainMapper::HasCompactification() const noexcept {
return std::isfinite(m_r_star_ref) && std::isfinite(m_r_inf_ref) &&
m_r_star_ref > 0.0 && m_r_inf_ref > m_r_star_ref &&
m_xi_clamp > 0.0 && m_xi_clamp < 1.0;
}
bool DomainMapper::HasDisplacementField() const noexcept {
return m_d != nullptr;
}
bool DomainMapper::CalcIsIdentity() const {
if (m_d == nullptr) {
return true;
}
const int local_identity = m_d->Normlinf() == 0.0 ? 1 : 0;
const auto *parallel_displacement =
dynamic_cast<const mfem::ParGridFunction *>(m_d);
if (parallel_displacement == nullptr) {
return local_identity == 1;
}
int global_identity = 0;
MPI_Allreduce(
&local_identity, &global_identity, 1, MPI_INT, MPI_MIN,
parallel_displacement->ParFESpace()->GetComm()
);
return global_identity == 1;
}
void DomainMapper::ResetDisplacement() {
m_d = nullptr;
InvalidateCache();
CalcIsIdentity() ? m_displacement_is_identity = true
: m_displacement_is_identity = false;
}
void DomainMapper::ComputeJacobian(mfem::ElementTransformation &T, mfem::DenseMatrix &J) const {
void DomainMapper::ComputeJacobian(
mfem::ElementTransformation &T,
mfem::DenseMatrix &J
) const {
J.SetSize(m_dim, m_dim);
J = 0.0;
J = 0.0;
m_J_D = 0.0;
if (IsIdentity()) {
if (!HasDisplacementField()) {
for (int i = 0; i < m_dim; ++i) {
m_J_D(i, i) = 1.0; // Identity mapping
}
@@ -76,7 +145,7 @@ namespace mean_field::mapping {
if (is_vacuum(T)) {
T.Transform(T.GetIntPoint(), m_x_ref);
if (IsIdentity()) {
if (!HasDisplacementField()) {
m_x_disp = m_x_ref;
} else {
m_shape.SetSize(m_fe->GetDof());
@@ -91,15 +160,22 @@ namespace mean_field::mapping {
}
}
double DomainMapper::ComputeDetJ(mfem::ElementTransformation &T, const mfem::IntegrationPoint &ip) const {
if (IsIdentity() && !is_vacuum(T)) return 1.0; // If no mapping, the determinant of the Jacobian is 1
double DomainMapper::ComputeDetJ(
mfem::ElementTransformation &T,
const mfem::IntegrationPoint &ip
) const {
if (!HasDisplacementField() && !is_vacuum(T))
return 1.0; // If no mapping, the determinant of the Jacobian is 1
T.SetIntPoint(&ip);
mfem::DenseMatrix J;
ComputeJacobian(T, J);
return J.Det();
}
void DomainMapper::ComputeMappedDiffusionTensor(mfem::ElementTransformation &T, mfem::DenseMatrix &D) const {
void DomainMapper::ComputeMappedDiffusionTensor(
mfem::ElementTransformation &T,
mfem::DenseMatrix &D
) const {
ComputeJacobian(T, m_J_temp);
const double detJ = m_J_temp.Det();
mfem::CalcInverse(m_J_temp, m_JInv_temp);
@@ -108,41 +184,56 @@ namespace mean_field::mapping {
D *= fabs(detJ);
}
void DomainMapper::ComputeInverseJacobian(mfem::ElementTransformation &T, mfem::DenseMatrix &JInv) const {
void DomainMapper::ComputeInverseJacobian(
mfem::ElementTransformation &T,
mfem::DenseMatrix &JInv
) const {
ComputeJacobian(T, m_J_temp);
JInv.SetSize(m_dim, m_dim);
mfem::CalcInverse(m_J_temp, JInv);
}
DomainMapper::VolumeQuadratureContext DomainMapper::GetQuadratureContext(mfem::ElementTransformation &T, const mfem::IntegrationPoint &ip) const {
VolumeQuadratureContext DomainMapper::GetQuadratureContext(
mfem::ElementTransformation &T,
const mfem::IntegrationPoint &ip
) const {
const int dim = T.GetSpaceDim();
mfem::DenseMatrix J_map(dim, dim), J_inv(dim, dim);
ComputeJacobian(T, J_map);
mfem::DenseMatrix J_full(dim, dim);
mfem::Mult(J_map, T.Jacobian(), J_full);
mfem::CalcInverse(J_full, J_inv);
const double detJ = std::fabs(ComputeDetJ(T, ip));
const double detJ = std::fabs(ComputeDetJ(T, ip));
const double weight = ip.weight * T.Weight() * detJ;
return {.J_inv = J_inv, .detJ = detJ, .weight = weight};
}
DomainMapper::FaceQuadratureContext DomainMapper::GetFaceQuadratureContext(mfem::FaceElementTransformations &T, const mfem::IntegrationPoint &ip) const {
FaceQuadratureContext DomainMapper::GetFaceQuadratureContext(
mfem::FaceElementTransformations &T,
const mfem::IntegrationPoint &ip
) const {
const int dim = T.GetSpaceDim();
T.SetAllIntPoints(&ip);
mfem::Vector n_raw(dim);
mfem::CalcOrtho(T.Jacobian(), n_raw);
if (IsIdentity()) {
if (!HasDisplacementField() && !is_vacuum(T)) {
const double n_raw_mag = n_raw.Norml2();
mfem::Vector n_unit(dim);
n_unit = n_raw;
n_unit /= n_raw_mag;
return FaceQuadratureContext{.normal=n_unit, .ds=ip.weight * n_raw_mag, .v_dot_n_scale = 1.0};
return FaceQuadratureContext{
.normal = n_unit,
.ds = ip.weight * n_raw_mag,
.v_dot_n_scale = 1.0
};
}
// Nanson's Formula (https://en.wikiversity.org/wiki/Continuum_mechanics/Volume_change_and_area_change)
// Since the displacement field lives in H1 it should be irrelevant if we pick Elem1 or Elem2
// Nanson's Formula
// (https://en.wikiversity.org/wiki/Continuum_mechanics/Volume_change_and_area_change)
// Since the displacement field lives in H1 it should be irrelevant if
// we pick Elem1 or Elem2
mfem::DenseMatrix J_map(dim, dim);
ComputeJacobian(*T.Elem1, J_map);
const double detJ_map = J_map.Det();
@@ -162,18 +253,21 @@ namespace mean_field::mapping {
const double n_raw_mag = n_raw.Norml2();
return FaceQuadratureContext{
.normal = n_unit,
.ds = ip.weight * n_raw_mag,
.normal = n_unit,
.ds = ip.weight * n_raw_mag,
.v_dot_n_scale = n_phys_mag / n_raw_mag
};
}
void DomainMapper::GetPhysicalPoint(mfem::ElementTransformation &T, const mfem::IntegrationPoint &ip, mfem::Vector &x_phys) const {
void DomainMapper::GetPhysicalPoint(
mfem::ElementTransformation &T,
const mfem::IntegrationPoint &ip,
mfem::Vector &x_phys
) const {
x_phys.SetSize(m_dim);
T.Transform(ip, m_x_ref);
if (IsIdentity()) {
if (!HasDisplacementField()) {
x_phys = m_x_ref;
} else {
UpdateElementCache(T);
@@ -189,11 +283,91 @@ namespace mean_field::mapping {
}
}
void DomainMapper::GetVectorValue(const int i, const mfem::IntegrationPoint &ip, mfem::Vector &val) const {
void DomainMapper::GetVectorValue(
const int i,
const mfem::IntegrationPoint &ip,
mfem::Vector &val
) const {
m_d->GetVectorValue(i, ip, val);
}
const mfem::GridFunction *DomainMapper::GetDisplacement() const { return m_d; }
void DomainMapper::MapHDivFluxToPhysical(
mfem::ElementTransformation &transformation,
const mfem::IntegrationPoint &integration_point,
const mfem::Vector &reference_flux,
mfem::Vector &physical_flux
) const {
MFEM_VERIFY(
reference_flux.Size() == m_dim,
"The reference H(div) flux has the wrong dimension."
);
mfem::DenseMatrix map_jacobian(m_dim, m_dim);
const double map_determinant = get_positive_map_jacobian(
*this, transformation, integration_point, map_jacobian
);
mfem::Vector mapped_flux(m_dim);
map_jacobian.Mult(reference_flux, mapped_flux);
mapped_flux /= map_determinant;
physical_flux = mapped_flux;
}
void DomainMapper::MapPhysicalFluxToHDivReference(
mfem::ElementTransformation &transformation,
const mfem::IntegrationPoint &integration_point,
const mfem::Vector &physical_flux,
mfem::Vector &reference_flux
) const {
MFEM_VERIFY(
physical_flux.Size() == m_dim,
"The physical flux has the wrong dimension."
);
mfem::DenseMatrix map_jacobian(m_dim, m_dim);
const double map_determinant = get_positive_map_jacobian(
*this, transformation, integration_point, map_jacobian
);
mfem::DenseMatrix inverse_map_jacobian(m_dim, m_dim);
mfem::CalcInverse(map_jacobian, inverse_map_jacobian);
mfem::Vector mapped_flux(m_dim);
inverse_map_jacobian.Mult(physical_flux, mapped_flux);
mapped_flux *= map_determinant;
reference_flux = mapped_flux;
}
void DomainMapper::MapReferenceGradientToPhysical(
mfem::ElementTransformation &transformation,
const mfem::IntegrationPoint &integration_point,
const mfem::Vector &reference_gradient,
mfem::Vector &physical_gradient
) const {
MFEM_VERIFY(
reference_gradient.Size() == m_dim,
"The reference gradient has the wrong dimension."
);
mfem::DenseMatrix map_jacobian(m_dim, m_dim);
get_positive_map_jacobian(
*this, transformation, integration_point, map_jacobian
);
mfem::DenseMatrix inverse_map_jacobian(m_dim, m_dim);
mfem::CalcInverse(map_jacobian, inverse_map_jacobian);
mfem::Vector mapped_gradient(m_dim);
inverse_map_jacobian.MultTranspose(reference_gradient, mapped_gradient);
physical_gradient = mapped_gradient;
}
const mfem::GridFunction *DomainMapper::GetDisplacement() const {
return m_d;
}
double DomainMapper::GetPhysInfRadius() const {
return 1.0 - m_xi_clamp;
@@ -208,11 +382,12 @@ namespace mean_field::mapping {
}
double DomainMapper::GetCacheHitRate() const {
return (static_cast<double>(m_cache_hits)) / static_cast<double>(m_cache_misses + m_cache_hits);
return (static_cast<double>(m_cache_hits)) /
static_cast<double>(m_cache_misses + m_cache_hits);
}
void DomainMapper::ResetCacheStats() const {
m_cache_hits = 0;
m_cache_hits = 0;
m_cache_misses = 0;
}
@@ -225,28 +400,36 @@ namespace mean_field::mapping {
m_d_val.SetSize(m_dim);
}
void DomainMapper::ApplyKelvinMapping(const mfem::Vector &x_ref, mfem::Vector &x_phys) const {
void DomainMapper::ApplyKelvinMapping(
const mfem::Vector &x_ref,
mfem::Vector &x_phys
) const {
const double r_ref = x_ref.Norml2();
double xi = (r_ref - m_r_star_ref) / (m_r_inf_ref - m_r_star_ref);
xi = std::clamp(xi, 0.0, m_xi_clamp);
xi = std::clamp(xi, 0.0, m_xi_clamp);
const double factor = m_r_star_ref / (r_ref * (1 - xi));
x_phys *= factor;
}
void DomainMapper::ComputeKelvinJacobian(const mfem::Vector &x_ref, const mfem::Vector &x_disp, const mfem::DenseMatrix &J_D,
mfem::DenseMatrix &J) const {
const double r_ref = x_ref.Norml2();
void DomainMapper::ComputeKelvinJacobian(
const mfem::Vector &x_ref,
const mfem::Vector &x_disp,
const mfem::DenseMatrix &J_D,
mfem::DenseMatrix &J
) const {
const double r_ref = x_ref.Norml2();
const double delta_R = m_r_inf_ref - m_r_star_ref;
double xi = (r_ref - m_r_star_ref) / delta_R;
xi = std::clamp(xi, 0.0, m_xi_clamp);
double xi = (r_ref - m_r_star_ref) / delta_R;
xi = std::clamp(xi, 0.0, m_xi_clamp);
const double denom = 1.0 - xi;
const double denom = 1.0 - xi;
const double k = m_r_star_ref / (r_ref * denom);
const double k = m_r_star_ref / (r_ref * denom);
const double dk_dr = m_r_star_ref * ((1.0 / (delta_R * r_ref * denom * denom)) - (
1.0 / (r_ref * r_ref * denom)));
const double dk_dr =
m_r_star_ref * ((1.0 / (delta_R * r_ref * denom * denom)) -
(1.0 / (r_ref * r_ref * denom)));
J.SetSize(m_dim, m_dim);
const double outer_factor = dk_dr / r_ref;
@@ -262,13 +445,17 @@ namespace mean_field::mapping {
m_cached_elem_id = -1;
}
void DomainMapper::UpdateElementCache(const mfem::ElementTransformation &T) const {
if (IsIdentity()) return;
void DomainMapper::UpdateElementCache(
const mfem::ElementTransformation &T
) const {
if (!HasDisplacementField())
return;
if (T.ElementNo != m_cached_elem_id || T.ElementType != m_cached_elem_type) {
if (T.ElementNo != m_cached_elem_id ||
T.ElementType != m_cached_elem_type) {
m_cache_misses++;
m_cached_elem_id = T.ElementNo;
m_cached_elem_type = T.ElementType;
m_cached_elem_id = T.ElementNo;
m_cached_elem_type = T.ElementType;
const mfem::FiniteElementSpace *fes = m_d->FESpace();
mfem::Array<int> vdofs;
@@ -291,4 +478,4 @@ namespace mean_field::mapping {
m_cache_hits++;
}
}
}
} // namespace mean_field::mapping

View File

@@ -0,0 +1,916 @@
module;
#include <cmath>
#include <memory>
#include <mfem.hpp>
#include <stdexcept>
#include <utility>
module mean_field;
import :mapping.types;
import :mapping.compactification;
import :utils.user;
namespace {
bool vector_is_finite(const mfem::Vector &vector) {
for (int i = 0; i < vector.Size(); ++i) {
if (!std::isfinite(vector(i)))
return false;
}
return true;
}
bool matrix_is_finite(const mfem::DenseMatrix &matrix) {
for (int i = 0; i < matrix.Height(); ++i) {
for (int j = 0; j < matrix.Width(); ++j) {
if (!std::isfinite(matrix(i, j)))
return false;
}
}
return true;
}
} // namespace
namespace mean_field::mapping {
ElementCompactificationData::ElementCompactificationData(
const mfem::FiniteElement &element,
const mfem::Vector &dofs
)
: m_element(&element),
m_dofs(dofs) {
if (element.GetRangeType() != mfem::FiniteElement::SCALAR) {
throw std::invalid_argument(
"Compactification coordinate requires a scalar finite element."
);
}
if (element.GetMapType() != mfem::FiniteElement::VALUE) {
throw std::invalid_argument(
"Compactification coordinate requires a value-mapped scalar "
"finite "
"element."
);
}
if (element.GetDerivType() != mfem::FiniteElement::GRAD) {
throw std::invalid_argument(
"Compactification coordinate finite element must provide a "
"gradient."
);
}
if (element.GetDof() <= 0) {
throw std::invalid_argument(
"Compactification coordinate finite element has no degrees of "
"freedom."
);
}
if (dofs.Size() != element.GetDof()) {
throw std::invalid_argument(
"Compactification coordinate DOF count does not match its "
"finite "
"element."
);
}
}
const mfem::FiniteElement &
ElementCompactificationData::GetElement() const noexcept {
return *m_element;
}
const mfem::Vector &ElementCompactificationData::GetDofs() const noexcept {
return m_dofs;
}
int ElementCompactificationData::GetDofCount() const noexcept {
return m_dofs.Size();
}
ElementDisplacementData::ElementDisplacementData(
const mfem::FiniteElement &element,
const mfem::Vector &displacement_dofs,
const mfem::Ordering::Type ordering
)
: m_element(&element),
m_dimension(0),
m_ordering(ordering) {
const int dof_count = element.GetDof();
if (dof_count <= 0)
throw std::invalid_argument(
"The displacement element must have at least one degree of "
"freedom."
);
if (displacement_dofs.Size() <= 0 ||
displacement_dofs.Size() % dof_count != 0) {
throw std::invalid_argument(
"The displacement vector size must be a positive multiple of "
"the "
"element degree-of-freedom count."
);
}
m_dimension = displacement_dofs.Size() / dof_count;
m_dof_matrix.SetSize(dof_count, m_dimension);
if (ordering == mfem::Ordering::byNODES) {
for (int component = 0; component < m_dimension; ++component) {
for (int i = 0; i < dof_count; ++i) {
m_dof_matrix(i, component) =
displacement_dofs(i + component * dof_count);
}
}
} else if (ordering == mfem::Ordering::byVDIM) {
for (int i = 0; i < dof_count; ++i) {
for (int component = 0; component < m_dimension; ++component) {
m_dof_matrix(i, component) =
displacement_dofs(component + i * m_dimension);
}
}
} else {
throw std::invalid_argument(
"Unsupported MFEM displacement ordering."
);
}
}
const mfem::FiniteElement &
ElementDisplacementData::GetElement() const noexcept {
return *m_element;
}
const mfem::DenseMatrix &
ElementDisplacementData::GetDofMatrix() const noexcept {
return m_dof_matrix;
}
int ElementDisplacementData::GetDimension() const noexcept {
return m_dimension;
}
int ElementDisplacementData::GetDofCount() const noexcept {
return m_element->GetDof();
}
mfem::Ordering::Type ElementDisplacementData::GetOrdering() const noexcept {
return m_ordering;
}
ElementDisplacementData ElementDisplacementDataFromElementVDofs(
const mfem::FiniteElement &element,
const mfem::Vector &displacement_dofs
) {
return ElementDisplacementData(
element, displacement_dofs, mfem::Ordering::byNODES
);
}
DomainMapperStateless::Workspace::Workspace(const int dimension) {
SetDimension(dimension);
}
void DomainMapperStateless::Workspace::SetDimension(const int dimension) {
if (dimension <= 0) {
throw std::invalid_argument(
"Domain mapping workspace dimension must be positive."
);
}
m_dimension = dimension;
m_field_value.SetSize(dimension);
m_field_jacobian.SetSize(dimension, dimension);
m_compactification_point.coordinate = 0.0;
m_compactification_point.coordinate_gradient.SetSize(dimension);
m_reference_normal.SetSize(dimension);
m_mapped_normal.SetSize(dimension);
m_full_element_jacobian.SetSize(dimension, dimension);
m_vector_temp.SetSize(dimension);
m_matrix_temp_1.SetSize(dimension, dimension);
m_matrix_temp_2.SetSize(dimension, dimension);
m_exterior_result.physical_position.SetSize(dimension);
m_exterior_result.mapping_jacobian.SetSize(dimension, dimension);
m_exterior_variation.physical_position_variation.SetSize(dimension);
m_exterior_variation.mapping_jacobian_variation.SetSize(
dimension, dimension
);
}
int DomainMapperStateless::Workspace::GetDimension() const noexcept {
return m_dimension;
}
DomainMapperStateless::DomainMapperStateless(
const utils::DomainMapperStatelessOptions options,
std::unique_ptr<const compactification::ExteriorDomainMap> exterior_map
)
: m_options(options),
m_exterior_map(std::move(exterior_map)) {
if (m_options.dimension <= 0)
throw std::invalid_argument(
"The domain-mapping dimension must be positive."
);
if (m_options.vacuum_element_attribute <= 0)
throw std::invalid_argument(
"The vacuum element attribute must be positive."
);
if (!m_exterior_map)
throw std::invalid_argument(
"DomainMapperStateless requires an exterior-domain mapping."
);
}
bool DomainMapperStateless::IsCompactifiedElement(
const mfem::ElementTransformation &transformation
) const noexcept {
return transformation.Attribute == m_options.vacuum_element_attribute;
}
int DomainMapperStateless::GetDimension() const noexcept {
return m_options.dimension;
}
int DomainMapperStateless::GetVacuumElementAttribute() const noexcept {
return m_options.vacuum_element_attribute;
}
const compactification::ExteriorDomainMap &
DomainMapperStateless::GetExteriorMap() const noexcept {
return *m_exterior_map;
}
void DomainMapperStateless::ValidateElementData(
const ElementMappingData &element_data
) const {
const ElementDisplacementData &displacement = element_data.displacement;
const ElementCompactificationData &compactification =
element_data.compactification;
if (displacement.GetDimension() != m_options.dimension) {
throw std::invalid_argument(
"Displacement field dimension does not match the domain mapper "
"dimension."
);
}
if (displacement.GetElement().GetDim() != m_options.dimension) {
throw std::invalid_argument(
"Displacement finite element dimension does not match the "
"domain "
"mapper dimension."
);
}
if (compactification.GetElement().GetDim() != m_options.dimension) {
throw std::invalid_argument(
"Compactification finite element dimension does not match the "
"domain "
"mapper dimension."
);
}
if (displacement.GetElement().GetGeomType() !=
compactification.GetElement().GetGeomType()) {
throw std::invalid_argument(
"Displacement and compactification finite elements have "
"different "
"geometries."
);
}
if (compactification.GetElement().GetRangeType() !=
mfem::FiniteElement::SCALAR) {
throw std::invalid_argument(
"Compactification coordinate requires a scalar finite element."
);
}
if (compactification.GetElement().GetMapType() !=
mfem::FiniteElement::VALUE) {
throw std::invalid_argument(
"Compactification coordinate requires a value-mapped finite "
"element."
);
}
if (compactification.GetElement().GetDerivType() !=
mfem::FiniteElement::GRAD) {
throw std::invalid_argument(
"Compactification coordinate finite element does not provide a "
"gradient."
);
}
if (compactification.GetDofCount() !=
compactification.GetElement().GetDof()) {
throw std::invalid_argument(
"Compactification coordinate DOF count does not match its "
"finite "
"element."
);
}
}
MappingStatus DomainMapperStateless::EvaluateCompactificationCoordinate(
const ElementCompactificationData &compactification,
mfem::ElementTransformation &transformation,
const mfem::IntegrationPoint &integration_point,
Workspace &workspace,
CompactificationPointData &point_data
) const {
const mfem::FiniteElement &element = compactification.GetElement();
const mfem::Vector &dofs = compactification.GetDofs();
const int dof_count = element.GetDof();
if (workspace.GetDimension() != m_options.dimension ||
transformation.GetSpaceDim() != m_options.dimension ||
element.GetDim() != m_options.dimension) {
return MappingStatus::invalid_dimension;
}
if (dofs.Size() != dof_count) {
return MappingStatus::invalid_dimension;
}
for (int i = 0; i < dofs.Size(); ++i) {
if (!std::isfinite(dofs(i)))
return MappingStatus::non_finite_input;
}
transformation.SetIntPoint(&integration_point);
workspace.m_compactification_shape.SetSize(dof_count);
workspace.m_compactification_dshape.SetSize(
dof_count, m_options.dimension
);
element.CalcShape(
integration_point, workspace.m_compactification_shape
);
element.CalcPhysDShape(
transformation, workspace.m_compactification_dshape
);
point_data.coordinate = dofs * workspace.m_compactification_shape;
point_data.coordinate_gradient.SetSize(m_options.dimension);
workspace.m_compactification_dshape.MultTranspose(
dofs, point_data.coordinate_gradient
);
if (!std::isfinite(point_data.coordinate)) {
return MappingStatus::non_finite_result;
}
for (int d = 0; d < point_data.coordinate_gradient.Size(); ++d) {
if (!std::isfinite(point_data.coordinate_gradient(d)))
return MappingStatus::non_finite_result;
}
return MappingStatus::valid;
}
void DomainMapperStateless::EvaluateField(
const ElementDisplacementData &field,
mfem::ElementTransformation &transformation,
const mfem::IntegrationPoint &integration_point,
Workspace &workspace,
mfem::Vector &value,
mfem::DenseMatrix &jacobian
) const {
transformation.SetIntPoint(&integration_point);
const mfem::FiniteElement &element = field.GetElement();
const mfem::DenseMatrix &dof_matrix = field.GetDofMatrix();
workspace.m_shape.SetSize(element.GetDof());
workspace.m_mesh_dshape.SetSize(element.GetDof(), m_options.dimension);
element.CalcShape(integration_point, workspace.m_shape);
element.CalcPhysDShape(transformation, workspace.m_mesh_dshape);
value.SetSize(m_options.dimension);
dof_matrix.MultTranspose(workspace.m_shape, value);
jacobian.SetSize(m_options.dimension, m_options.dimension);
mfem::MultAtB(dof_matrix, workspace.m_mesh_dshape, jacobian);
}
MappingStatus DomainMapperStateless::EvaluatePoint(
const ElementMappingData &element_data,
mfem::ElementTransformation &transformation,
const mfem::IntegrationPoint &integration_point,
Workspace &workspace,
MappingPointContext &context
) const {
ValidateElementData(element_data);
if (workspace.GetDimension() != m_options.dimension)
throw std::invalid_argument(
"The mapping workspace has the wrong dimension."
);
if (transformation.GetSpaceDim() != m_options.dimension)
throw std::invalid_argument(
"The element transformation has the wrong spatial dimension."
);
if (transformation.GetGeometryType() !=
element_data.displacement.GetElement().GetGeomType())
throw std::invalid_argument(
"The element transformation geometry does not match the "
"supplied "
"element data."
);
transformation.SetIntPoint(&integration_point);
context.reference_position.SetSize(m_options.dimension);
transformation.Transform(integration_point, context.reference_position);
EvaluateField(
element_data.displacement, transformation, integration_point,
workspace, workspace.m_field_value, workspace.m_field_jacobian
);
if (!vector_is_finite(context.reference_position) ||
!vector_is_finite(workspace.m_field_value) ||
!matrix_is_finite(workspace.m_field_jacobian)) {
return MappingStatus::non_finite_input;
}
context.displaced_position.SetSize(m_options.dimension);
context.displaced_position = context.reference_position;
context.displaced_position += workspace.m_field_value;
context.displacement_jacobian.SetSize(
m_options.dimension, m_options.dimension
);
context.displacement_jacobian = workspace.m_field_jacobian;
for (int i = 0; i < m_options.dimension; ++i)
context.displacement_jacobian(i, i) += 1.0;
context.compactified = IsCompactifiedElement(transformation);
if (context.compactified) {
const MappingStatus coordinate_status =
EvaluateCompactificationCoordinate(
element_data.compactification, transformation,
integration_point, workspace,
workspace.m_compactification_point
);
if (coordinate_status != MappingStatus::valid)
return coordinate_status;
const compactification::ExteriorMapInput exterior_input{
.reference_position = context.reference_position,
.displaced_position = context.displaced_position,
.displacement_jacobian = context.displacement_jacobian,
.compactification_coordinate =
workspace.m_compactification_point.coordinate,
.compactification_coordinate_gradient =
workspace.m_compactification_point.coordinate_gradient
};
const MappingStatus exterior_status = m_exterior_map->Evaluate(
exterior_input, workspace.m_exterior_result
);
if (exterior_status != MappingStatus::valid)
return exterior_status;
context.physical_position =
workspace.m_exterior_result.physical_position;
context.mapping_jacobian =
workspace.m_exterior_result.mapping_jacobian;
} else {
context.physical_position = context.displaced_position;
context.mapping_jacobian = context.displacement_jacobian;
}
if (!vector_is_finite(context.physical_position) ||
!matrix_is_finite(context.mapping_jacobian))
return MappingStatus::non_finite_result;
context.mapping_determinant = context.mapping_jacobian.Det();
if (!std::isfinite(context.mapping_determinant))
return MappingStatus::non_finite_result;
if (context.mapping_determinant <= 0.0)
return MappingStatus::non_positive_determinant;
context.inverse_mapping_jacobian.SetSize(
m_options.dimension, m_options.dimension
);
mfem::CalcInverse(
context.mapping_jacobian, context.inverse_mapping_jacobian
);
if (!matrix_is_finite(context.inverse_mapping_jacobian))
return MappingStatus::non_finite_result;
return MappingStatus::valid;
}
MappingStatus DomainMapperStateless::EvaluateVolume(
const ElementMappingData &element_data,
mfem::ElementTransformation &transformation,
const mfem::IntegrationPoint &integration_point,
Workspace &workspace,
VolumeMappingContext &context
) const {
const MappingStatus point_status = EvaluatePoint(
element_data, transformation, integration_point, workspace,
context.mapping
);
if (point_status != MappingStatus::valid)
return point_status;
transformation.SetIntPoint(&integration_point);
mfem::Mult(
context.mapping.mapping_jacobian, transformation.Jacobian(),
workspace.m_full_element_jacobian
);
context.quadrature.J_inv.SetSize(
m_options.dimension, m_options.dimension
);
mfem::CalcInverse(
workspace.m_full_element_jacobian, context.quadrature.J_inv
);
context.quadrature.detJ = context.mapping.mapping_determinant;
context.quadrature.weight = integration_point.weight *
transformation.Weight() *
context.mapping.mapping_determinant;
if (!matrix_is_finite(context.quadrature.J_inv) ||
!std::isfinite(context.quadrature.weight))
return MappingStatus::non_finite_result;
if (context.quadrature.weight <= 0.0)
return MappingStatus::non_positive_determinant;
return MappingStatus::valid;
}
mfem::ElementTransformation &
DomainMapperStateless::SelectFaceElementTransformation(
mfem::FaceElementTransformations &transformation,
const FaceElementSide side
) {
if (side == FaceElementSide::element_1) {
MFEM_VERIFY(
transformation.Elem1 != nullptr,
"The face does not have an element-1 transformation."
);
return *transformation.Elem1;
}
MFEM_VERIFY(
transformation.Elem2 != nullptr,
"The face does not have an element-2 transformation."
);
return *transformation.Elem2;
}
const mfem::IntegrationPoint &
DomainMapperStateless::SelectFaceElementIntegrationPoint(
mfem::FaceElementTransformations &transformation,
const FaceElementSide side
) {
mfem::ElementTransformation &element_transformation =
SelectFaceElementTransformation(transformation, side);
return element_transformation.GetIntPoint();
}
MappingStatus DomainMapperStateless::EvaluateFace(
const ElementMappingData &element_data,
mfem::FaceElementTransformations &transformation,
const FaceElementSide side,
const mfem::IntegrationPoint &integration_point,
Workspace &workspace,
FaceMappingContext &context
) const {
transformation.SetAllIntPoints(&integration_point);
mfem::ElementTransformation &element_transformation =
SelectFaceElementTransformation(transformation, side);
const mfem::IntegrationPoint &element_integration_point =
SelectFaceElementIntegrationPoint(transformation, side);
const MappingStatus point_status = EvaluatePoint(
element_data, element_transformation, element_integration_point,
workspace, context.mapping
);
if (point_status != MappingStatus::valid)
return point_status;
workspace.m_reference_normal.SetSize(m_options.dimension);
mfem::CalcOrtho(
transformation.Jacobian(), workspace.m_reference_normal
);
if (side == FaceElementSide::element_2)
workspace.m_reference_normal *= -1.0;
const double reference_normal_magnitude =
workspace.m_reference_normal.Norml2();
if (!std::isfinite(reference_normal_magnitude) ||
reference_normal_magnitude <= 0.0)
return MappingStatus::non_finite_result;
context.reference_normal.SetSize(m_options.dimension);
context.reference_normal = workspace.m_reference_normal;
context.reference_normal /= reference_normal_magnitude;
context.mapping.inverse_mapping_jacobian.MultTranspose(
workspace.m_reference_normal, workspace.m_mapped_normal
);
workspace.m_mapped_normal *= context.mapping.mapping_determinant;
const double mapped_normal_magnitude =
workspace.m_mapped_normal.Norml2();
if (!std::isfinite(mapped_normal_magnitude) ||
mapped_normal_magnitude <= 0.0)
return MappingStatus::non_finite_result;
context.quadrature.normal.SetSize(m_options.dimension);
context.quadrature.normal = workspace.m_mapped_normal;
context.quadrature.normal /= mapped_normal_magnitude;
context.reference_surface_weight =
integration_point.weight * reference_normal_magnitude;
context.physical_surface_weight =
integration_point.weight * mapped_normal_magnitude;
context.quadrature.ds = context.reference_surface_weight;
context.quadrature.v_dot_n_scale =
mapped_normal_magnitude / reference_normal_magnitude;
if (!vector_is_finite(context.quadrature.normal) ||
!std::isfinite(context.reference_surface_weight) ||
!std::isfinite(context.physical_surface_weight) ||
!std::isfinite(context.quadrature.v_dot_n_scale)) {
return MappingStatus::non_finite_result;
}
return MappingStatus::valid;
}
MappingStatus DomainMapperStateless::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 {
ValidateElementData(element_data);
const ElementMappingData direction_data{
.displacement = direction,
.compactification = element_data.compactification
};
ValidateElementData(direction_data);
if (element_data.displacement.GetDofCount() != direction.GetDofCount())
throw std::invalid_argument(
"The displacement and direction elements have different "
"degree-of-freedom counts."
);
if (workspace.GetDimension() != m_options.dimension)
throw std::invalid_argument(
"The mapping workspace has the wrong dimension."
);
if (base_context.compactified != IsCompactifiedElement(transformation))
throw std::invalid_argument(
"The base mapping context does not match the current element "
"domain."
);
EvaluateField(
direction, transformation, integration_point, workspace,
workspace.m_field_value, workspace.m_field_jacobian
);
if (!vector_is_finite(workspace.m_field_value) ||
!matrix_is_finite(workspace.m_field_jacobian))
return MappingStatus::non_finite_input;
variation.displacement_variation = workspace.m_field_value;
variation.displacement_jacobian_variation = workspace.m_field_jacobian;
if (base_context.compactified) {
const MappingStatus coordinate_status =
EvaluateCompactificationCoordinate(
element_data.compactification, transformation,
integration_point, workspace,
workspace.m_compactification_point
);
if (coordinate_status != MappingStatus::valid)
return coordinate_status;
const compactification::ExteriorMapInput exterior_input{
.reference_position = base_context.reference_position,
.displaced_position = base_context.displaced_position,
.displacement_jacobian = base_context.displacement_jacobian,
.compactification_coordinate =
workspace.m_compactification_point.coordinate,
.compactification_coordinate_gradient =
workspace.m_compactification_point.coordinate_gradient
};
workspace.m_exterior_result.physical_position =
base_context.physical_position;
workspace.m_exterior_result.mapping_jacobian =
base_context.mapping_jacobian;
const compactification::ExteriorMapDirection exterior_direction{
.displaced_position_variation =
variation.displacement_variation,
.displacement_jacobian_variation =
variation.displacement_jacobian_variation
};
// ReSharper disable once CppTooWideScopeInitStatement
const MappingStatus exterior_status =
m_exterior_map->EvaluateVariation(
exterior_input, workspace.m_exterior_result,
exterior_direction, workspace.m_exterior_variation
);
if (exterior_status != MappingStatus::valid) {
return exterior_status;
}
variation.physical_position_variation =
workspace.m_exterior_variation.physical_position_variation;
variation.mapping_jacobian_variation =
workspace.m_exterior_variation.mapping_jacobian_variation;
} else {
variation.physical_position_variation =
variation.displacement_variation;
variation.mapping_jacobian_variation =
variation.displacement_jacobian_variation;
}
mfem::Mult(
base_context.inverse_mapping_jacobian,
variation.mapping_jacobian_variation, workspace.m_matrix_temp_1
);
double trace = 0.0;
for (int i = 0; i < m_options.dimension; ++i)
trace += workspace.m_matrix_temp_1(i, i);
variation.mapping_determinant_variation =
base_context.mapping_determinant * trace;
variation.inverse_mapping_jacobian_variation.SetSize(
m_options.dimension, m_options.dimension
);
mfem::Mult(
workspace.m_matrix_temp_1, base_context.inverse_mapping_jacobian,
variation.inverse_mapping_jacobian_variation
);
variation.inverse_mapping_jacobian_variation *= -1.0;
if (!vector_is_finite(variation.physical_position_variation) ||
!matrix_is_finite(variation.mapping_jacobian_variation) ||
!matrix_is_finite(variation.inverse_mapping_jacobian_variation) ||
!std::isfinite(variation.mapping_determinant_variation)) {
return MappingStatus::non_finite_result;
}
return MappingStatus::valid;
}
MappingStatus DomainMapperStateless::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 {
const MappingStatus point_status = EvaluatePointVariation(
element_data, direction, transformation, integration_point,
base_context.mapping, workspace, variation.mapping
);
if (point_status != MappingStatus::valid)
return point_status;
transformation.SetIntPoint(&integration_point);
mfem::Mult(
variation.mapping.mapping_jacobian_variation,
transformation.Jacobian(), workspace.m_full_element_jacobian
);
mfem::Mult(
base_context.quadrature.J_inv, workspace.m_full_element_jacobian,
workspace.m_matrix_temp_1
);
variation.inverse_element_jacobian_variation.SetSize(
m_options.dimension, m_options.dimension
);
mfem::Mult(
workspace.m_matrix_temp_1, base_context.quadrature.J_inv,
variation.inverse_element_jacobian_variation
);
variation.inverse_element_jacobian_variation *= -1.0;
variation.weight_variation =
integration_point.weight * transformation.Weight() *
variation.mapping.mapping_determinant_variation;
if (!matrix_is_finite(variation.inverse_element_jacobian_variation) ||
!std::isfinite(variation.weight_variation))
return MappingStatus::non_finite_result;
return MappingStatus::valid;
}
MappingStatus DomainMapperStateless::EvaluateFaceVariation(
const ElementMappingData &element_data,
const ElementDisplacementData &direction,
mfem::FaceElementTransformations &transformation,
const FaceElementSide side,
const mfem::IntegrationPoint &integration_point,
const FaceMappingContext &base_context,
Workspace &workspace,
FaceMappingVariation &variation
) const {
transformation.SetAllIntPoints(&integration_point);
mfem::ElementTransformation &element_transformation =
SelectFaceElementTransformation(transformation, side);
const mfem::IntegrationPoint &element_integration_point =
SelectFaceElementIntegrationPoint(transformation, side);
const MappingStatus point_status = EvaluatePointVariation(
element_data, direction, element_transformation,
element_integration_point, base_context.mapping, workspace,
variation.mapping
);
if (point_status != MappingStatus::valid)
return point_status;
workspace.m_reference_normal.SetSize(m_options.dimension);
mfem::CalcOrtho(
transformation.Jacobian(), workspace.m_reference_normal
);
if (side == FaceElementSide::element_2)
workspace.m_reference_normal *= -1.0;
const double reference_normal_magnitude =
workspace.m_reference_normal.Norml2();
if (!std::isfinite(reference_normal_magnitude) ||
reference_normal_magnitude <= 0.0)
return MappingStatus::non_finite_result;
base_context.mapping.inverse_mapping_jacobian.MultTranspose(
workspace.m_reference_normal, workspace.m_vector_temp
);
workspace.m_mapped_normal = workspace.m_vector_temp;
workspace.m_mapped_normal *= base_context.mapping.mapping_determinant;
variation.physical_normal_variation.SetSize(m_options.dimension);
variation.mapping.inverse_mapping_jacobian_variation.MultTranspose(
workspace.m_reference_normal, variation.physical_normal_variation
);
variation.physical_normal_variation *=
base_context.mapping.mapping_determinant;
variation.physical_normal_variation.Add(
variation.mapping.mapping_determinant_variation,
workspace.m_vector_temp
);
const double mapped_normal_magnitude =
workspace.m_mapped_normal.Norml2();
if (!std::isfinite(mapped_normal_magnitude) ||
mapped_normal_magnitude <= 0.0)
return MappingStatus::non_finite_result;
const double mapped_normal_magnitude_variation =
base_context.quadrature.normal *
variation.physical_normal_variation;
variation.physical_normal_variation.Add(
-mapped_normal_magnitude_variation, base_context.quadrature.normal
);
variation.physical_normal_variation /= mapped_normal_magnitude;
variation.physical_surface_weight_variation =
integration_point.weight * mapped_normal_magnitude_variation;
variation.normal_flux_scale_variation =
mapped_normal_magnitude_variation / reference_normal_magnitude;
if (!vector_is_finite(variation.physical_normal_variation) ||
!std::isfinite(variation.physical_surface_weight_variation) ||
!std::isfinite(variation.normal_flux_scale_variation)) {
return MappingStatus::non_finite_result;
}
return MappingStatus::valid;
}
} // namespace mean_field::mapping

View File

@@ -0,0 +1,276 @@
module;
#include <mfem.hpp>
module mean_field;
import :mapping.types;
namespace mean_field::mapping {
void MapHDivFluxToPhysical(
const MappingPointContext &context,
const mfem::Vector &reference_flux,
mfem::Vector &physical_flux
) {
MFEM_VERIFY(
reference_flux.Size() == context.mapping_jacobian.Width(),
"The reference H(div) flux has the wrong dimension."
);
physical_flux.SetSize(reference_flux.Size());
context.mapping_jacobian.Mult(reference_flux, physical_flux);
physical_flux /= context.mapping_determinant;
}
void MapPhysicalFluxToHDivReference(
const MappingPointContext &context,
const mfem::Vector &physical_flux,
mfem::Vector &reference_flux
) {
MFEM_VERIFY(
physical_flux.Size() == context.inverse_mapping_jacobian.Width(),
"The physical H(div) flux has the wrong dimension."
);
reference_flux.SetSize(physical_flux.Size());
context.inverse_mapping_jacobian.Mult(physical_flux, reference_flux);
reference_flux *= context.mapping_determinant;
}
void MapReferenceGradientToPhysical(
const MappingPointContext &context,
const mfem::Vector &reference_gradient,
mfem::Vector &physical_gradient
) {
MFEM_VERIFY(
reference_gradient.Size() ==
context.inverse_mapping_jacobian.Height(),
"The reference scalar gradient has the wrong dimension."
);
physical_gradient.SetSize(reference_gradient.Size());
context.inverse_mapping_jacobian.MultTranspose(
reference_gradient, physical_gradient
);
}
void MapPhysicalGradientToReference(
const MappingPointContext &context,
const mfem::Vector &physical_gradient,
mfem::Vector &reference_gradient
) {
MFEM_VERIFY(
physical_gradient.Size() == context.mapping_jacobian.Height(),
"The physical scalar gradient has the wrong dimension."
);
reference_gradient.SetSize(physical_gradient.Size());
context.mapping_jacobian.MultTranspose(
physical_gradient, reference_gradient
);
}
void MapReferenceVectorGradientToPhysical(
const MappingPointContext &context,
const mfem::DenseMatrix &reference_gradient,
mfem::DenseMatrix &physical_gradient
) {
MFEM_VERIFY(
reference_gradient.Width() ==
context.inverse_mapping_jacobian.Height(),
"The reference vector gradient has the wrong dimension."
);
physical_gradient.SetSize(
reference_gradient.Height(),
context.inverse_mapping_jacobian.Width()
);
mfem::Mult(
reference_gradient, context.inverse_mapping_jacobian,
physical_gradient
);
}
void MapPhysicalVectorGradientToReference(
const MappingPointContext &context,
const mfem::DenseMatrix &physical_gradient,
mfem::DenseMatrix &reference_gradient
) {
MFEM_VERIFY(
physical_gradient.Width() == context.mapping_jacobian.Height(),
"The physical vector gradient has the wrong dimension."
);
reference_gradient.SetSize(
physical_gradient.Height(), context.mapping_jacobian.Width()
);
mfem::Mult(
physical_gradient, context.mapping_jacobian, reference_gradient
);
}
double MapHDivDivergenceToPhysical(
const MappingPointContext &context,
const double reference_divergence
) {
return reference_divergence / context.mapping_determinant;
}
void ComputeHDivMassTensor(
const MappingPointContext &context,
mfem::DenseMatrix &mass_tensor
) {
const int dimension = context.mapping_jacobian.Height();
MFEM_VERIFY(
context.mapping_jacobian.Width() == dimension,
"The mapping Jacobian must be square."
);
MFEM_VERIFY(
context.mapping_determinant > 0.0,
"The mapping determinant must be positive."
);
mass_tensor.SetSize(dimension, dimension);
mfem::MultAtB(
context.mapping_jacobian, context.mapping_jacobian, mass_tensor
);
mass_tensor *= 1 / context.mapping_determinant;
}
void ComputeScalarDiffusionTensor(
const MappingPointContext &context,
mfem::DenseMatrix &diffusion_tensor
) {
const int dimension = context.inverse_mapping_jacobian.Height();
MFEM_VERIFY(
context.inverse_mapping_jacobian.Width() == dimension,
"The inverse mapping Jacobian must be square."
);
MFEM_VERIFY(
context.mapping_determinant > 0.0,
"The mapping determinant must be positive."
);
diffusion_tensor.SetSize(dimension, dimension);
mfem::MultABt(
context.inverse_mapping_jacobian, context.inverse_mapping_jacobian,
diffusion_tensor
);
diffusion_tensor *= context.mapping_determinant;
}
void MapHCurlFieldToPhysical(
const MappingPointContext &context,
const mfem::Vector &reference_field,
mfem::Vector &physical_field
) {
MFEM_VERIFY(
reference_field.Size() == context.inverse_mapping_jacobian.Height(),
"The reference H(curl) field has the wrong dimension."
);
physical_field.SetSize(reference_field.Size());
context.inverse_mapping_jacobian.MultTranspose(
reference_field, physical_field
);
}
void MapPhysicalFieldToHCurlReference(
const MappingPointContext &context,
const mfem::Vector &physical_field,
mfem::Vector &reference_field
) {
MFEM_VERIFY(
physical_field.Size() == context.mapping_jacobian.Height(),
"The physical H(curl) field has the wrong dimension."
);
reference_field.SetSize(physical_field.Size());
context.mapping_jacobian.MultTranspose(physical_field, reference_field);
}
void MapHCurlCurlToPhysical(
const MappingPointContext &context,
const mfem::Vector &reference_curl,
mfem::Vector &physical_curl
) {
MFEM_VERIFY(
reference_curl.Size() == context.mapping_jacobian.Width(),
"The reference H(curl) curl has the wrong dimension."
);
physical_curl.SetSize(reference_curl.Size());
context.mapping_jacobian.Mult(reference_curl, physical_curl);
physical_curl /= context.mapping_determinant;
}
void MapPhysicalCurlToHCurlReference(
const MappingPointContext &context,
const mfem::Vector &physical_curl,
mfem::Vector &reference_curl
) {
MFEM_VERIFY(
physical_curl.Size() == context.inverse_mapping_jacobian.Width(),
"The physical H(curl) curl has the wrong dimension."
);
reference_curl.SetSize(physical_curl.Size());
context.inverse_mapping_jacobian.Mult(physical_curl, reference_curl);
reference_curl *= context.mapping_determinant;
}
// TODO: Investigate these
void ComputeHCurlMassTensor(
const MappingPointContext &context,
mfem::DenseMatrix &mass_tensor
) {
ComputeScalarDiffusionTensor(context, mass_tensor);
}
void ComputeHCurlCurlTensor(
const MappingPointContext &context,
mfem::DenseMatrix &curl_tensor
) {
ComputeHDivMassTensor(context, curl_tensor);
}
void ComputeHDivMassTensorVariation(
const MappingPointContext &context,
const MappingPointVariation &variation,
mfem::DenseMatrix &mass_tensor_variation
) {
const double determinant = context.mapping_determinant;
const double determinant_variation =
variation.mapping_determinant_variation;
const int dimension = context.inverse_mapping_jacobian.Width();
mass_tensor_variation.SetSize(dimension, dimension);
MFEM_VERIFY(
std::isfinite(determinant) && determinant > 0.0,
"The mapping determinant must be positive and finite."
);
MFEM_VERIFY(
std::isfinite(determinant_variation),
"The mapping determinant variation must be finite."
);
mfem::DenseMatrix determinant_correction(dimension, dimension);
ComputeHDivMassTensor(context, determinant_correction);
determinant_correction *= determinant_variation / determinant;
mfem::DenseMatrix right_jacobian_variation(dimension, dimension);
mfem::MultAtB(
context.mapping_jacobian, variation.mapping_jacobian_variation,
right_jacobian_variation
);
mfem::MultAtB(
variation.mapping_jacobian_variation, context.mapping_jacobian,
mass_tensor_variation
);
mass_tensor_variation += right_jacobian_variation;
mass_tensor_variation *= 1 / determinant;
mass_tensor_variation -= determinant_correction;
}
} // namespace mean_field::mapping