feat(libmeanfield): centrifugal + pressure
This commit is contained in:
@@ -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
|
||||
|
||||
266
libmeanfield/impl/mapping/compactification/kelvin.cpp
Normal file
266
libmeanfield/impl/mapping/compactification/kelvin.cpp
Normal 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
|
||||
@@ -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
|
||||
|
||||
916
libmeanfield/impl/mapping/domain_mapper_new.cpp
Normal file
916
libmeanfield/impl/mapping/domain_mapper_new.cpp
Normal 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
|
||||
276
libmeanfield/impl/mapping/transformations.cpp
Normal file
276
libmeanfield/impl/mapping/transformations.cpp
Normal 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
|
||||
Reference in New Issue
Block a user