Snapshot optimized solver and audit diagnostics before numerical experiments

This commit is contained in:
2026-09-16 14:34:06 -04:00
parent 75cc638739
commit 80cc3d83d7
64 changed files with 11969 additions and 267 deletions

View File

@@ -1,6 +1,9 @@
module;
#include <cmath>
#include <array>
#include <map>
#include <tuple>
#include <memory>
#include <mfem.hpp>
#include <stdexcept>
@@ -32,6 +35,65 @@ namespace {
} // namespace
namespace mean_field::mapping {
struct DomainMapper::GeometryCache {
bool enabled{true};
const mfem::Mesh *mesh{nullptr};
long sequence{-1}, nodesSequence{-1};
std::map<std::tuple<int, double, double, double>, FixedMeshGeometry> points;
std::uint64_t hits{0}, misses{0};
};
DomainMapper::~DomainMapper() = default;
DomainMapper::DomainMapper(DomainMapper &&) noexcept = default;
DomainMapper &DomainMapper::operator=(DomainMapper &&) noexcept = default;
void DomainMapper::SetGeometryCacheEnabled(bool enabled) const { m_geometryCache->enabled = enabled; }
bool DomainMapper::IsGeometryCacheEnabled() const noexcept { return m_geometryCache->enabled; }
void DomainMapper::ClearGeometryCache() const {
auto &cache = *m_geometryCache;
cache.points.clear();
cache.mesh = nullptr;
cache.sequence = cache.nodesSequence = -1;
cache.hits = cache.misses = 0;
}
DomainMapper::GeometryCacheStatistics DomainMapper::GetGeometryCacheStatistics() const {
const auto &cache = *m_geometryCache;
return {.hits = cache.hits, .misses = cache.misses, .points = cache.points.size()};
}
const DomainMapper::FixedMeshGeometry *DomainMapper::GetFixedMeshGeometry(
mfem::ElementTransformation &transformation, const mfem::IntegrationPoint &point
) const {
transformation.SetIntPoint(&point);
auto &cache = *m_geometryCache;
const auto *mesh = transformation.mesh;
if (!cache.enabled || mesh == nullptr || transformation.ElementType != mfem::ElementTransformation::ELEMENT ||
transformation.ElementNo < 0 || transformation.ElementNo >= mesh->GetNE() ||
transformation.GetSpaceDim() != m_options.dimension) return nullptr;
const std::array<double, 3> coordinates{
point.x, m_options.dimension > 1 ? point.y : 0.0, m_options.dimension > 2 ? point.z : 0.0};
for (double coordinate : coordinates) if (!std::isfinite(coordinate)) return nullptr;
if (cache.mesh != mesh || cache.sequence != mesh->GetSequence() || cache.nodesSequence != mesh->GetNodesSequence()) {
cache.points.clear();
cache.mesh = mesh;
cache.sequence = mesh->GetSequence();
cache.nodesSequence = mesh->GetNodesSequence();
}
const auto key = std::tuple{transformation.ElementNo, coordinates[0], coordinates[1], coordinates[2]};
const auto found = cache.points.find(key);
if (found != cache.points.end()) { ++cache.hits; return &found->second; }
FixedMeshGeometry geometry;
transformation.Transform(point, geometry.position);
geometry.jacobian = transformation.Jacobian();
geometry.inverseJacobian = transformation.InverseJacobian();
geometry.weight = transformation.Weight();
++cache.misses;
if (!vector_is_finite(geometry.position) || !matrix_is_finite(geometry.jacobian) ||
!matrix_is_finite(geometry.inverseJacobian) || !std::isfinite(geometry.weight) || geometry.weight <= 0.0)
return nullptr;
return &cache.points.emplace(key, std::move(geometry)).first->second;
}
ElementCompactificationData::ElementCompactificationData(
const mfem::FiniteElement &element,
const mfem::Vector &dofs
@@ -195,7 +257,8 @@ namespace mean_field::mapping {
const utils::DomainMapperOptions options,
std::unique_ptr<const compactification::ExteriorDomainMap> exterior_map
)
: m_options(options),
: m_geometryCache(std::make_unique<GeometryCache>()),
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.");
@@ -588,6 +651,16 @@ namespace mean_field::mapping {
const mfem::IntegrationPoint &integration_point,
Workspace &workspace,
MappingPointContext &context
) const {
const auto *meshGeometry = GetFixedMeshGeometry(transformation, integration_point);
return EvaluatePointImpl(element_data, transformation, integration_point, workspace, context, meshGeometry);
}
MappingStatus DomainMapper::EvaluatePointImpl(
const ElementMappingData &element_data, mfem::ElementTransformation &transformation,
const mfem::IntegrationPoint &integration_point, Workspace &workspace,
MappingPointContext &context, const FixedMeshGeometry *meshGeometry,
const ReferenceDisplacementSample *displacementSample
) const {
ValidateElementData(element_data);
@@ -602,16 +675,28 @@ namespace mean_field::mapping {
"element data."
);
transformation.SetIntPoint(&integration_point);
context.reference_position.SetSize(m_options.dimension);
transformation.Transform(integration_point, context.reference_position);
if (meshGeometry) context.reference_position = meshGeometry->position;
else transformation.Transform(integration_point, context.reference_position);
// Get the displacement field value and its Jacobian at the integration point. Note these are in the workspace to avoid repeated allocations.
EvaluateField(
element_data.displacement, transformation, integration_point, workspace, workspace.m_field_value,
workspace.m_field_jacobian, nullptr
);
if (displacementSample) {
if (element_data.displacement.GetElement().GetMapType() != mfem::FiniteElement::VALUE ||
displacementSample->value.Size() != m_options.dimension ||
displacementSample->gradient.Height() != m_options.dimension ||
displacementSample->gradient.Width() != m_options.dimension)
return MappingStatus::invalid_dimension;
workspace.m_field_value = displacementSample->value;
workspace.m_field_jacobian.SetSize(m_options.dimension);
mfem::Mult(displacementSample->gradient,
meshGeometry ? meshGeometry->inverseJacobian : transformation.InverseJacobian(),
workspace.m_field_jacobian);
} else {
EvaluateField(
element_data.displacement, transformation, integration_point, workspace, workspace.m_field_value,
workspace.m_field_jacobian, meshGeometry ? &meshGeometry->inverseJacobian : nullptr
);
}
if (!vector_is_finite(context.reference_position) || !vector_is_finite(workspace.m_field_value) ||
!matrix_is_finite(workspace.m_field_jacobian)) {
@@ -647,7 +732,7 @@ namespace mean_field::mapping {
// the gradient of s as we move along the mesh coordinates. All of this is stashes within workspace.m_compactification_point.
const MappingStatus coordinate_status = EvaluateCompactificationCoordinate(
element_data.compactification, transformation, integration_point, workspace,
workspace.m_compactification_point, nullptr
workspace.m_compactification_point, meshGeometry ? &meshGeometry->inverseJacobian : nullptr
);
if (coordinate_status != MappingStatus::valid)
@@ -701,21 +786,25 @@ namespace mean_field::mapping {
mfem::ElementTransformation &transformation,
const mfem::IntegrationPoint &integration_point,
Workspace &workspace,
VolumeMappingContext &context
VolumeMappingContext &context,
const ReferenceDisplacementSample *displacementSample
) const {
const auto *meshGeometry = GetFixedMeshGeometry(transformation, integration_point);
const MappingStatus point_status =
EvaluatePoint(element_data, transformation, integration_point, workspace, context.mapping);
EvaluatePointImpl(element_data, transformation, integration_point, workspace, context.mapping, meshGeometry, displacementSample);
if (point_status != MappingStatus::valid)
return point_status;
mfem::Mult(context.mapping.mapping_jacobian, transformation.Jacobian(), workspace.m_full_element_jacobian);
mfem::Mult(context.mapping.mapping_jacobian,
meshGeometry ? meshGeometry->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;
integration_point.weight * (meshGeometry ? meshGeometry->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;
@@ -846,7 +935,8 @@ namespace mean_field::mapping {
);
if (inverse_mesh_jacobian == nullptr) {
transformation.SetIntPoint(&integration_point);
if (const auto *geometry = GetFixedMeshGeometry(transformation, integration_point))
inverse_mesh_jacobian = &geometry->inverseJacobian;
}
EvaluateField(