Snapshot optimized solver and audit diagnostics before numerical experiments
This commit is contained in:
@@ -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(
|
||||
|
||||
Reference in New Issue
Block a user