feat(mapper): finished porting over mapper

This commit is contained in:
2026-09-23 09:29:05 -04:00
parent 9e0e1f5c18
commit 52c42ec7cd
21 changed files with 1414 additions and 30 deletions

View File

@@ -0,0 +1,34 @@
#pragma once
#include <functional>
#include "mfem/fem/gridfunc.hpp"
#include "mfem/fem/coefficient.hpp"
#include "mfem/fem/eltrans.hpp"
#include "mfem/fem/intrules.hpp"
#include "mfem/linalg/vector.hpp"
#include "serif/mapping/domain_mapper.hpp"
#include "serif/mapping/evaluator.hpp"
namespace serif::mapping {
class PhysicalPositionFunctionCoefficient : public mfem::Coefficient {
public:
using Func = std::function<double(const mfem::Vector& x)>;
PhysicalPositionFunctionCoefficient(
const DomainMapper& mapper,
const mfem::GridFunction& displacement,
const mfem::GridFunction& compactification_coordinate,
Func func
);
double Eval(
mfem::ElementTransformation& transformation,
const mfem::IntegrationPoint& ip
) override;
private:
Func m_f;
GridFunctionMappingEvaluator m_mapping;
};
}

View File

@@ -1,6 +1,7 @@
#pragma once
#include <string_view>
#include <memory>
#include "serif/mapping/status.hpp"
#include "serif/mapping/compactification/input_result.hpp"
@@ -12,21 +13,18 @@ namespace serif::mapping::compactification {
public:
virtual ~ExteriorDomainMap() = default;
[[nodiscard]] virtual bool is_affine_in_displacement() const noexcept;
[[nodiscard]] virtual bool is_affine_in_displacement() const noexcept = 0;
[[nodiscard]] virtual MappingStatus evaluate(
const ExteriorMapInput &input,
ExteriorMapResult &result
) const = 0;
[[nodiscard]] virtual MappingStatus evaluate_variation(
const ExteriorMapInput &input,
const ExteriorMapResult &result,
const ExteriorMapDirection &direction,
ExteriorMapVariation &variation
) const = 0;
[[nodiscard]] virtual std::string_view get_name() const noexcept = 0;
[[nodiscard]] virtual std::unique_ptr<const ExteriorDomainMap> clone() const = 0;
};
}

View File

@@ -27,6 +27,7 @@ namespace serif::mapping::compactification {
) const override;
[[nodiscard]] std::string_view get_name() const noexcept override;
[[nodiscard]] std::unique_ptr<const ExteriorDomainMap> clone() const override;
[[nodiscard]] double get_reference_stellar_radius() const noexcept;
[[nodiscard]] double get_reference_infinity_radius() const noexcept;

View File

@@ -3,11 +3,15 @@
#include "mfem/linalg/densemat.hpp"
#include "mfem/linalg/vector.hpp"
// TODO: There is likely some major readability improvements that could be gained by naming these things
// less confusing names
// TODO [Emily]: Put more comments in this file describing what the various terms are
namespace serif::mapping {
struct VolumeQuadratureContext {
mfem::DenseMatrix J_inv;
double detJ;
double weight;
mfem::DenseMatrix J_element_to_physical_inverse;
double mapping_determinate;
double physical_integration_weight;
};
struct FaceQuadratureContext {

View File

@@ -6,7 +6,7 @@
#include "mfem/mesh/mesh.hpp"
#include <cstdint>
#include <unordered_map>
#include <map>
#include <tuple>
#include "serif/mapping/compactification/kelvin.hpp"
@@ -73,7 +73,7 @@ namespace serif::mapping {
[[nodiscard]] int get_dimension() const noexcept;
public: // This is a workspace class, these members are kept public so we do not need indirection through getters to access them
mfem::Vector m_shape;
mfem::DenseMatrix m_reference_shape;
mfem::DenseMatrix m_reference_dshape;
mfem::DenseMatrix m_mesh_dshape;
mfem::DenseMatrix m_reference_field_jacobian;
mfem::Vector m_field_value;
@@ -120,7 +120,7 @@ namespace serif::mapping {
const mfem::Mesh* mesh{nullptr};
long sequence{-1}; // We explicitly use the non explicltiy sized versions here since that what MFEM uses fort these members. Alternative we could use a int64_t and verify that on the compiled system that is the same as a long
long nodesSequence{-1};
std::unordered_map<std::tuple<int, double, double, double>, DMFixedMeshGeometry> points;
std::map<std::tuple<int, double, double, double>, DMFixedMeshGeometry> points; // Currently this is an regular map since to use an unordered map we would need to add a std::hash specialization for tuple<int, double, double, double>. Some small future optimizations may be available if we implement a fast hashing algorithm for that type.
uint64_t hits{0};
uint64_t misses{0};
};

View File

@@ -19,11 +19,6 @@
namespace serif::mapping {
class DomainMapper {
public:
DomainMapper(
utils::user_options::DomainMapperOptions options,
std::unique_ptr<compactification::ExteriorDomainMap> exterior_map
);
DomainMapper(const DomainMapper&) = delete;
DomainMapper& operator=(const DomainMapper&) = delete;
@@ -31,6 +26,14 @@ namespace serif::mapping {
DomainMapper(DomainMapper&&) = default;
DomainMapper& operator=(DomainMapper&&) = default;
DomainMapper(
utils::user_options::DomainMapperOptions options,
std::unique_ptr<const compactification::ExteriorDomainMap> exterior_map
);
std::unique_ptr<DomainMapper> clone() const;
void set_geometry_cache_enabled(bool enabled) const;
[[nodiscard]] bool is_geometry_cache_enabled() const noexcept;
void clear_geometry_cache() const;
@@ -57,7 +60,7 @@ namespace serif::mapping {
[[nodiscard]] MappingStatus evaluate_face(
const ElementMappingData& element_data,
mfem::ElementTransformation& transformation,
mfem::FaceElementTransformations& transformation,
FaceElementSide side,
const mfem::IntegrationPoint& ip,
DMWorkspace& workspace,
@@ -72,7 +75,7 @@ namespace serif::mapping {
const MappingPointContext& context,
DMWorkspace& workspace,
MappingPointVariation& variation
);
) const;
[[nodiscard]] MappingStatus evaluate_volume_variation(
const ElementMappingData& element_data,
@@ -82,18 +85,18 @@ namespace serif::mapping {
const VolumeMappingContext& context,
DMWorkspace& workspace,
VolumeMappingVariation& variation
);
) const;
[[nodiscard]] MappingStatus evaluate_face_variation(
const ElementMappingData& element_data,
const ElementDisplacementData& direction,
mfem::ElementTransformation& transformation,
mfem::FaceElementTransformations &transformation,
FaceElementSide side,
const mfem::IntegrationPoint& ip,
const FaceMappingContext& context,
DMWorkspace& workspace,
FaceMappingVariation& variation
);
) const;
[[nodiscard]] bool is_compactified_element(const mfem::ElementTransformation& transformation) const noexcept;
[[nodiscard]] int get_dimension() const noexcept;
@@ -113,7 +116,7 @@ namespace serif::mapping {
const ElementMappingData& element_data
) const;
void evaluate_field(
void evaluate_displacement_field(
const ElementDisplacementData& field,
mfem::ElementTransformation& transformation,
const mfem::IntegrationPoint& ip,
@@ -146,12 +149,12 @@ namespace serif::mapping {
// These are static since they do not use any class members or workspace. Really these could and
// maybe should be free functions...
[[nodiscard]] static mfem::ElementTransformation& select_face_element_transformation(
mfem::FaceElementTransformations& transformations,
const mfem::FaceElementTransformations& transformations,
FaceElementSide side
);
[[nodiscard]] static const mfem::IntegrationPoint& select_face_element_integration_point(
mfem::FaceElementTransformations& transformations,
const mfem::FaceElementTransformations& transformations,
FaceElementSide side
);

View File

@@ -0,0 +1,93 @@
#pragma once
#include <memory>
#include "mfem/fem/gridfunc.hpp"
#include "mfem/fem/eltrans.hpp"
#include "mfem/fem/intrules.hpp"
#include "mfem/linalg/vector.hpp"
#include "mfem/general/array.hpp"
#include "serif/mapping/domain_mapper.hpp"
#include "serif/mapping/context.hpp"
#include "serif/mapping/status.hpp"
namespace serif::mapping {
// Note this is NOT thread safe.
class GridFunctionMappingEvaluator {
public:
/*
* The evaluator references the supplied grid functions and caches copies of
* their element-local DOFs. You need to make sure you call InvalidateCache() or Refresh() after either
* grid function's values are modified. Changes to the finite-element-space sequence
* should be detected automatically.
*
* This object owns mutable workspace and cache state and *is not* thread-safe.
*/
GridFunctionMappingEvaluator(
const DomainMapper& mapper,
const mfem::GridFunction& displacement,
const mfem::GridFunction& compactification_coordinate
);
void invalidate_cache() noexcept;
void refresh();
[[nodiscard]] MappingStatus evaluate_point(
mfem::ElementTransformation &transformation,
const mfem::IntegrationPoint &integration_point,
MappingPointContext &context
);
[[nodiscard]] MappingStatus evaluate_volume(
mfem::ElementTransformation &transformation,
const mfem::IntegrationPoint &integration_point,
VolumeMappingContext &context
);
[[nodiscard]] MappingStatus evaluate_face(
mfem::FaceElementTransformations &transformation,
FaceElementSide side,
const mfem::IntegrationPoint &integration_point,
FaceMappingContext &context
);
[[nodiscard]] VolumeQuadratureContext get_quadrature_context(
mfem::ElementTransformation &transformation,
const mfem::IntegrationPoint &integration_point
);
[[nodiscard]] FaceQuadratureContext get_face_quadrature_context(
mfem::FaceElementTransformations &transformation,
const mfem::IntegrationPoint &integration_point,
FaceElementSide side = FaceElementSide::ELEMENT_1
);
void get_physical_point(
mfem::ElementTransformation &transformation,
const mfem::IntegrationPoint &integration_point,
mfem::Vector &physical_position
);
private:
void validate_field_bindings() const;
[[nodiscard]] bool invalidate_for_changed_spaces();
void load_element(int element_id);
private:
const DomainMapper &m_mapper;
const mfem::GridFunction &m_displacement;
const mfem::GridFunction &m_compactification_coordinate;
const mfem::FiniteElementSpace *m_displacement_space;
const mfem::FiniteElementSpace *m_compactification_space;
long m_displacement_space_sequence;
long m_compactification_space_sequence;
DMWorkspace m_workspace;
mfem::Array<int> m_displacement_dofs;
mfem::Array<int> m_compactification_dofs;
mfem::Vector m_element_displacement;
mfem::Vector m_element_compactification;
std::unique_ptr<ElementDisplacementData> m_displacement_data;
std::unique_ptr<ElementCompactificationData> m_compactification_data;
int m_cached_element_id{-1};
};
}

View File

@@ -0,0 +1,87 @@
#pragma once
#include "serif/mapping/variation.hpp"
#include "serif/mapping/context.hpp"
namespace serif::mapping {
void map_hdiv_flux_to_physical(
const MappingPointContext& context,
const mfem::Vector& reference_flux,
mfem::Vector& physical_flux
);
void map_physical_flux_to_hdiv_reference(
const MappingPointContext& context,
const mfem::Vector& physical_flux,
mfem::Vector& reference_flux
);
void map_reference_gradient_to_physical(
const MappingPointContext& context,
const mfem::Vector& reference_gradient,
mfem::Vector& physical_gradient
);
void map_physical_gradient_to_reference(
const MappingPointContext& context,
const mfem::Vector& physical_gradient,
mfem::Vector& reference_gradient
);
void map_reference_vector_gradient_to_physical(
const MappingPointContext& context,
const mfem::DenseMatrix& reference_vector_gradient,
mfem::DenseMatrix& physical_vector_gradient
);
void map_physical_vector_gradient_to_reference(
const MappingPointContext& context,
const mfem::DenseMatrix& physical_vector_gradient,
mfem::DenseMatrix& reference_vector_gradient
);
[[nodiscard]] double map_hdiv_divergence_to_physical(
const MappingPointContext& context,
double reference_divergence
);
void compute_hdiv_mass_tensor(
const MappingPointContext& context,
mfem::DenseMatrix& mass_tensor
);
void compute_scalar_diffusion_tensor(
const MappingPointContext& context,
mfem::DenseMatrix& diffusion_tensor
);
void map_hcurl_field_to_physical(
const MappingPointContext& context,
const mfem::Vector& reference_field,
mfem::Vector& physical_field
);
void map_physical_field_to_hcurl_reference(
const MappingPointContext& context,
const mfem::Vector& physical_field,
mfem::Vector& reference_field
);
void map_hcurl_curl_to_physical(
const MappingPointContext& context,
const mfem::Vector &reference_curl,
mfem::Vector &physical_curl
);
void map_physical_curl_to_hcurl_reference(
const MappingPointContext& context,
const mfem::Vector& physical_curl,
mfem::Vector& reference_curl
);
void compute_hdiv_mass_tensor_variation(
const MappingPointContext& context,
const MappingPointVariation& variation,
mfem::DenseMatrix& mass_tensor_variation
);
}

View File

@@ -0,0 +1,28 @@
#pragma once
#include "mfem/linalg/densemat.hpp"
#include "serif/mapping/context.hpp"
#include <vector>
namespace serif::mapping {
class VolumeMappingCache {
public:
void set_size(int point_count, int dimension);
void store(int point, VolumeMappingContext& context);
void load(int point, VolumeMappingContext& context) const;
void load_inverse_jacobian(int point, mfem::DenseMatrix& inverse) const;
[[nodiscard]] int get_point_count() const noexcept;
[[nodiscard]] int get_dimension() const noexcept;
private:
void validate_point(int point) const;
[[nodiscard]] const double* get_point_data(int point) const;
std::vector<double> m_data;
int m_point_count{0};
int m_dimension{0};
int m_point_stride{0};
};
}

View File

@@ -5,6 +5,8 @@
#include "mfem/linalg/vector.hpp"
#include "mfem/linalg/densemat.hpp"
#include "serif/utils/misc/concepts/numeric.hpp"
namespace serif::utils::checks {
// If we have a new backend / new vector and matrix type in the future we can add new overloads
inline bool is_finite(const mfem::Vector& vector) {
@@ -28,4 +30,10 @@ namespace serif::utils::checks {
return true;
}
template <typename Scalar>
requires requires (Scalar x) {{std::isfinite(x)} -> std::convertible_to<bool>; }
bool is_finite(const Scalar scalar) {
return std::isfinite(scalar);
}
}

View File

@@ -15,7 +15,7 @@ namespace serif::utils::errors {
// stay consistent here.
// At some point someone should go through and come up with some well defined schema for all this
enum class SERiFErrorCode : uint16_t {
enum class SERiFErrorCode : uint16_t { // 16 bit backing type, so 65535 possible error codes, which should be far far far more than enough for now
UNKNOWN_ERROR = 0,
// RUNTIME FAILURES (>100)
@@ -30,6 +30,7 @@ namespace serif::utils::errors {
// Jacobian Errors
JACOBIAN_ERROR = 200,
INVERTED_JACOBIAN,
NON_FINITE_DETERMINATE,
// EOS Errors
EOS_ERROR = 300,
@@ -50,16 +51,35 @@ namespace serif::utils::errors {
OUTSIDE_REFERENCE_DOMAIN,
NON_FINITE_MAPPING_RESULT,
NON_POSITIVE_DETERMINATE,
INVALID_MAPPING,
MISC_MAPPING_ERROR=410,
INVALID_DOF_COUNT,
INVALID_ELEMENT_GEOMETRY,
INVALID_DOF_ORDERING,
INVALID_COMPACTIFICATION_RANK,
INVALID_COMPACTIFICATION_TYPE,
INVALID_COMPACTIFICATION_DERIVATIVE_VALUE,
INVALID_COMPACTIFICATION_DERIVATIVE_TYPE,
INVALID_VACUUM_ELEMENT_ATTRIBUTE_ID,
MISSING_EXTERIOR_MAP,
COMPACTIFICATION_MISMATCH,
MISSING_FACE_ELEMENT,
JACOBIAN_SHAPE_ERROR,
MISSING_DISPLACEMENT_FINITE_ELEMENT_SPACE,
MISMATCHED_MESHING_MAPS,
IMPROPERLY_ORDERED_REFERENCE_RADII = 450,
INVALID_COORDINATE_TOLERANCE,
EVALUATOR_ERROR=460,
CACHE_FE_BINDING_ERROR,
INVALID_ELEMENT_ID_ERROR,
ELEMENT_NULLPTR_ERROR,
//As yet uncategorized errors
MATRIX_SHAPE_ERROR = 500,
OUT_OF_RANGE_ERROR,
// COMPILE TIME FAILURES (>1000)
// Type Invariant Failures
FAILED_TYPE_INVARIANT = 1000

View File

@@ -38,7 +38,7 @@ namespace serif::utils::errors {
throw ExceptionT(error_msg);
}
template <SERiFErrorCode code>
template <SERiFErrorCode code, misc::concepts::IsException ExceptionT = std::logic_error>
void serif_assert(const bool condition, const std::string_view message, auto... args) {
// collapse to a noop if NDEBUG is defined
#ifndef NDEBUG
@@ -48,7 +48,7 @@ namespace serif::utils::errors {
#endif
if constexpr(is_debug_build) {
if (!condition) {
serif_error<std::logic_error, code>(message, args...);
serif_error<ExceptionT, code>(message, args...);
}
}
}
@@ -75,4 +75,25 @@ namespace serif::utils::errors {
}
}
}
// The difference between serif_assert and serif_ensure is that serif_assert is a noop in release builds, while serif_ensure is always active.
// Use an ensure when you want to always check things. Use an assert when you want to check things that are true bugs
// and should not manifest in release builds.
template <SERiFErrorCode code, misc::concepts::IsException ExceptionT = std::logic_error>
void serif_ensure(const bool condition, const std::string_view message, auto... args) {
if (!condition) {
serif_error<ExceptionT, code>(message, args...);
}
}
template <SERiFErrorCode code, types::ct_error_string message, bool condition>
constexpr void serif_ensure() {
if constexpr (!condition) {
// Trigger an intentional error to display the message and code at compile time
// Note if we move to C++26 we can replace this with a more standard and less convoluted
// formated static_assert.
[[maybe_unused]] std::size_t error_kick = sizeof(serif_error_printer<message, code>);
}
}
}