Snapshot optimized solver and audit diagnostics before numerical experiments
This commit is contained in:
@@ -23,6 +23,7 @@ export import :deformation.radial_extensions;
|
||||
export import :deformation.surface_prescription;
|
||||
export import :deformation.vacuum_extension;
|
||||
export import :fem;
|
||||
import :fem.reference_tables;
|
||||
export import :field.mfem;
|
||||
export import :utils.domain;
|
||||
|
||||
@@ -141,8 +142,7 @@ export namespace mean_field::deformation {
|
||||
m_interiorSurfacePullbackWorkspace(surfaceDisplacementSize()),
|
||||
m_vacuumSurfacePullbackWorkspace(surfaceDisplacementSize()),
|
||||
m_surfacePullbackWorkspace(surfaceDisplacementSize()),
|
||||
m_parameterPullbackWorkspace(parameterCount()),
|
||||
m_volumeGridFunctionWorkspace(std::make_unique<mfem::ParGridFunction>(&volumeDisplacementSpace)) {
|
||||
m_parameterPullbackWorkspace(parameterCount()) {
|
||||
validateCompatibility(surfaceScalarSpace, volumeDisplacementSpace, logicalReferenceMesh);
|
||||
compileOwnership();
|
||||
|
||||
@@ -322,55 +322,8 @@ export namespace mean_field::deformation {
|
||||
requireCurrentDiscretization();
|
||||
requireVolumeSize(volumeDisplacement);
|
||||
|
||||
m_volumeGridFunctionWorkspace->SetFromTrueDofs(volumeDisplacement);
|
||||
mfem::Mesh *mesh = m_volumeDisplacementSpace->GetMesh();
|
||||
double localMinimumDeterminant = std::numeric_limits<double>::infinity();
|
||||
int localGeometryIsFinite = 1;
|
||||
|
||||
for (int element = 0; element < mesh->GetNE(); ++element) {
|
||||
mfem::ElementTransformation *transformation = mesh->GetElementTransformation(element);
|
||||
const mfem::FiniteElement *finiteElement = m_volumeDisplacementSpace->GetFE(element);
|
||||
// Positivity is a pointwise geometry requirement, not an
|
||||
// integration-accuracy requirement. A rule only slightly
|
||||
// above the displacement order can miss a narrow negative
|
||||
// region of the determinant even when a downstream physics
|
||||
// rule samples it. The determinant of a d-dimensional
|
||||
// degree-p deformation gradient can vary at substantially
|
||||
// higher order, so inspect at a conservative d*p scale.
|
||||
const int geometryInspectionOrder =
|
||||
std::max(finiteElement->GetOrder() + 2, 2 * spatialDimension() * finiteElement->GetOrder());
|
||||
const mfem::IntegrationRule &rule =
|
||||
mfem::IntRules.Get(transformation->GetGeometryType(), geometryInspectionOrder);
|
||||
|
||||
for (int point = 0; point < rule.GetNPoints(); ++point) {
|
||||
transformation->SetIntPoint(&rule.IntPoint(point));
|
||||
mfem::DenseMatrix deformationGradient;
|
||||
m_volumeGridFunctionWorkspace->GetVectorGradient(*transformation, deformationGradient);
|
||||
for (int component = 0; component < spatialDimension(); ++component) {
|
||||
deformationGradient(component, component) += 1.0;
|
||||
}
|
||||
const double determinant = deformationGradient.Det();
|
||||
if (!std::isfinite(determinant)) {
|
||||
localGeometryIsFinite = 0;
|
||||
} else {
|
||||
localMinimumDeterminant = std::min(localMinimumDeterminant, determinant);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
double globalMinimumDeterminant = 0.0;
|
||||
int globalGeometryIsFinite = 0;
|
||||
MPI_Allreduce(
|
||||
&localMinimumDeterminant, &globalMinimumDeterminant, 1, MPI_DOUBLE, MPI_MIN,
|
||||
m_volumeDisplacementSpace->GetComm()
|
||||
);
|
||||
MPI_Allreduce(
|
||||
&localGeometryIsFinite, &globalGeometryIsFinite, 1, MPI_INT, MPI_MIN,
|
||||
m_volumeDisplacementSpace->GetComm()
|
||||
);
|
||||
if (globalGeometryIsFinite == 0) {
|
||||
globalMinimumDeterminant = std::numeric_limits<double>::quiet_NaN();
|
||||
}
|
||||
const double globalMinimumDeterminant =
|
||||
m_geometryInspector.Inspect(*m_volumeDisplacementSpace, volumeDisplacement);
|
||||
++m_actionStatistics.geometryInspections;
|
||||
return {.minimumJacobianDeterminant = globalMinimumDeterminant};
|
||||
}
|
||||
@@ -579,7 +532,7 @@ export namespace mean_field::deformation {
|
||||
mutable mfem::Vector m_vacuumSurfacePullbackWorkspace;
|
||||
mutable mfem::Vector m_surfacePullbackWorkspace;
|
||||
mutable mfem::Vector m_parameterPullbackWorkspace;
|
||||
mutable std::unique_ptr<mfem::ParGridFunction> m_volumeGridFunctionWorkspace;
|
||||
mutable fem::PreparedGeometryInspector m_geometryInspector;
|
||||
};
|
||||
|
||||
template <
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
module;
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
@@ -19,7 +21,37 @@ export namespace mean_field::fem {
|
||||
[[nodiscard]] int GetDofCount() const;
|
||||
[[nodiscard]] int GetDimension() const;
|
||||
|
||||
// Available only for a verified nodal H1/L2 hexahedron and Cartesian quadrature
|
||||
// ordering (x fastest). Results have columns [value, dx, dy, dz] for
|
||||
// each input component. Workspace is caller-owned; tables stay immutable.
|
||||
[[nodiscard]] bool HasTensorEvaluation3D() const noexcept;
|
||||
void EvaluateTensor3D(
|
||||
const mfem::DenseMatrix &elementDofs,
|
||||
mfem::DenseMatrix &valuesAndGradients,
|
||||
mfem::Vector &workspace
|
||||
) const;
|
||||
|
||||
// Transpose of the reference gradient. Input columns are [dx,dy,dz]
|
||||
// per output component. Callers supply all physical factors and weights.
|
||||
void ApplyGradientTransposeTensor3D(const mfem::DenseMatrix &quadratureDuals,
|
||||
mfem::DenseMatrix &elementDuals,
|
||||
mfem::Vector &workspace) const;
|
||||
|
||||
// Value-only scalar contractions avoid forming unused gradients.
|
||||
void EvaluateValuesTensor3D(const mfem::Vector &elementDofs, mfem::Vector &values,
|
||||
mfem::Vector &workspace) const;
|
||||
void ApplyTransposeTensor3D(const mfem::Vector &quadratureDuals, mfem::Vector &elementDuals,
|
||||
mfem::Vector &workspace) const;
|
||||
|
||||
// Apply the transpose of the value basis to quadrature duals. No weights
|
||||
// are introduced here; callers supply their already weighted duals.
|
||||
void ApplyTransposeTensor3D(
|
||||
const mfem::DenseMatrix &quadratureDuals, mfem::DenseMatrix &elementDuals,
|
||||
mfem::Vector &workspace
|
||||
) const;
|
||||
|
||||
private:
|
||||
void PrepareTensorEvaluation3D(const mfem::FiniteElement &element, const mfem::IntegrationRule &rule);
|
||||
friend class ReferenceTableCache;
|
||||
ScalarReferenceTable(
|
||||
const mfem::FiniteElement &element,
|
||||
@@ -29,6 +61,11 @@ export namespace mean_field::fem {
|
||||
mfem::DenseMatrix m_values;
|
||||
std::vector<mfem::DenseMatrix> m_gradients;
|
||||
int m_dimension;
|
||||
int m_tensorDofsPerAxis{0};
|
||||
int m_tensorPointsPerAxis{0};
|
||||
mfem::Array<int> m_tensorDofMap;
|
||||
std::array<mfem::DenseMatrix, 3> m_tensorValues;
|
||||
std::array<mfem::DenseMatrix, 3> m_tensorGradients;
|
||||
};
|
||||
|
||||
class VectorReferenceTable {
|
||||
@@ -37,8 +74,16 @@ export namespace mean_field::fem {
|
||||
[[nodiscard]] int GetPointCount() const;
|
||||
[[nodiscard]] int GetDofCount() const;
|
||||
[[nodiscard]] int GetDimension() const;
|
||||
[[nodiscard]] bool HasTensorEvaluation3D() const noexcept;
|
||||
void EvaluateTensor3D(
|
||||
const mfem::Vector &elementDofs, mfem::DenseMatrix &values, mfem::Vector &workspace
|
||||
) const;
|
||||
void ApplyTransposeTensor3D(
|
||||
const mfem::DenseMatrix &quadratureDuals, mfem::Vector &elementDuals, mfem::Vector &workspace
|
||||
) const;
|
||||
|
||||
private:
|
||||
void PrepareTensorEvaluation3D(const mfem::FiniteElement &element, const mfem::IntegrationRule &rule);
|
||||
friend class ReferenceTableCache;
|
||||
VectorReferenceTable(
|
||||
const mfem::FiniteElement &element,
|
||||
@@ -48,6 +93,9 @@ export namespace mean_field::fem {
|
||||
std::vector<mfem::DenseMatrix> m_values;
|
||||
int m_dof_count;
|
||||
int m_dimension;
|
||||
int m_tensorClosedDofs{0}, m_tensorPointsPerAxis{0};
|
||||
mfem::Array<int> m_tensorDofMap;
|
||||
std::array<std::array<mfem::DenseMatrix, 3>, 3> m_tensorValues;
|
||||
};
|
||||
|
||||
// Owned by one discretization, never process-global. Finite elements must
|
||||
@@ -75,4 +123,29 @@ export namespace mean_field::fem {
|
||||
struct Storage;
|
||||
std::unique_ptr<Storage> m_storage;
|
||||
};
|
||||
// The production mapped-geometry inspection, independently benchmarkable.
|
||||
// Keeps the original conservative rule and checks every volume element.
|
||||
// Mesh/FES/node sequences invalidate the fixed inverse-Jacobian cache;
|
||||
// raw node edits require Mesh::NodesUpdated() or ClearCache(). Displacement
|
||||
// is always re-evaluated. Like prepared operators, scratch is not thread-safe.
|
||||
class PreparedGeometryInspector {
|
||||
public:
|
||||
PreparedGeometryInspector();
|
||||
~PreparedGeometryInspector();
|
||||
PreparedGeometryInspector(const PreparedGeometryInspector &) = delete;
|
||||
PreparedGeometryInspector &operator=(const PreparedGeometryInspector &) = delete;
|
||||
PreparedGeometryInspector(PreparedGeometryInspector &&) noexcept;
|
||||
PreparedGeometryInspector &operator=(PreparedGeometryInspector &&) noexcept;
|
||||
|
||||
// Collective minimum of det(I + grad(u)); NaN if any sampled value is
|
||||
// non-finite. reference=true retains GridFunction::GetVectorGradient.
|
||||
[[nodiscard]] double Inspect(mfem::ParFiniteElementSpace &space,
|
||||
const mfem::Vector &displacement, bool reference = false) const;
|
||||
void ClearCache() const;
|
||||
[[nodiscard]] int GetTensorElementCount() const noexcept;
|
||||
[[nodiscard]] std::uint64_t GetGeometryBuildCount() const noexcept;
|
||||
private:
|
||||
struct Storage;
|
||||
std::unique_ptr<Storage> m_storage;
|
||||
};
|
||||
} // namespace mean_field::fem
|
||||
|
||||
@@ -34,6 +34,16 @@ export namespace mean_field::mapping::compactification {
|
||||
[[nodiscard]] double GetReferenceInfinityRadius() const noexcept;
|
||||
[[nodiscard]] double GetCoordinateTolerance() const noexcept;
|
||||
|
||||
// Factors in delta J = scale * grad(delta u) + delta u (grad scale)^T.
|
||||
// They depend only on the fixed compactification coordinate, so prepared
|
||||
// operators can compute them once instead of evaluating the full map.
|
||||
[[nodiscard]] MappingStatus EvaluateJacobianVariationFactors(
|
||||
double coordinate,
|
||||
const mfem::Vector &coordinateGradient,
|
||||
double &scale,
|
||||
mfem::Vector &scaleGradient
|
||||
) const;
|
||||
|
||||
private:
|
||||
struct RadialFactors {
|
||||
double coordinate;
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
module;
|
||||
|
||||
#include "mean_field.h"
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
export module mean_field:mapping.domain_mapper;
|
||||
export import :mapping.types;
|
||||
@@ -108,8 +110,35 @@ export namespace mean_field::mapping {
|
||||
|
||||
DomainMapper(const DomainMapper &) = delete;
|
||||
DomainMapper &operator=(const DomainMapper &) = delete;
|
||||
DomainMapper(DomainMapper &&) = default;
|
||||
DomainMapper &operator=(DomainMapper &&) = default;
|
||||
~DomainMapper();
|
||||
DomainMapper(DomainMapper &&) noexcept;
|
||||
DomainMapper &operator=(DomainMapper &&) noexcept;
|
||||
|
||||
struct FixedMeshGeometry {
|
||||
mfem::Vector position;
|
||||
mfem::DenseMatrix jacobian, inverseJacobian;
|
||||
double weight{0.0};
|
||||
};
|
||||
struct GeometryCacheStatistics {
|
||||
std::uint64_t hits{0}, misses{0};
|
||||
std::size_t points{0};
|
||||
};
|
||||
// Only computational-mesh geometry is cached, never displacement or
|
||||
// compactification state. MFEM mesh/node sequence changes invalidate it.
|
||||
// After raw coordinate edits call Mesh::NodesUpdated(), as required by
|
||||
// MFEM's own geometric-factor cache, or ClearGeometryCache(). A mesh
|
||||
// must outlive its cached entries; clear before destroying/replacing it.
|
||||
// Like MFEM element transformations, this cache is not thread-safe.
|
||||
void SetGeometryCacheEnabled(bool enabled) const;
|
||||
[[nodiscard]] bool IsGeometryCacheEnabled() const noexcept;
|
||||
void ClearGeometryCache() const;
|
||||
[[nodiscard]] GeometryCacheStatistics GetGeometryCacheStatistics() const;
|
||||
// Sets the transformation's integration point. nullptr means use MFEM
|
||||
// directly (disabled cache, non-mesh transformation, or unsupported data).
|
||||
// Returned data remains valid until cache invalidation or mapper destruction.
|
||||
[[nodiscard]] const FixedMeshGeometry *GetFixedMeshGeometry(
|
||||
mfem::ElementTransformation &transformation, const mfem::IntegrationPoint &point
|
||||
) const;
|
||||
|
||||
[[nodiscard]] MappingStatus EvaluatePoint(
|
||||
const ElementMappingData &element_data,
|
||||
@@ -119,12 +148,19 @@ export namespace mean_field::mapping {
|
||||
MappingPointContext &context
|
||||
) const;
|
||||
|
||||
// A sample from the same element DOFs and integration point. Its
|
||||
// gradient is with respect to finite-element coordinates, not the mesh.
|
||||
struct ReferenceDisplacementSample {
|
||||
const mfem::Vector &value;
|
||||
const mfem::DenseMatrix &gradient;
|
||||
};
|
||||
[[nodiscard]] MappingStatus EvaluateVolume(
|
||||
const ElementMappingData &element_data,
|
||||
mfem::ElementTransformation &transformation,
|
||||
const mfem::IntegrationPoint &integration_point,
|
||||
Workspace &workspace,
|
||||
VolumeMappingContext &context
|
||||
VolumeMappingContext &context,
|
||||
const ReferenceDisplacementSample *displacementSample = nullptr
|
||||
) const;
|
||||
|
||||
[[nodiscard]] MappingStatus EvaluateFace(
|
||||
@@ -172,6 +208,12 @@ export namespace mean_field::mapping {
|
||||
[[nodiscard]] const compactification::ExteriorDomainMap &GetExteriorMap() const noexcept;
|
||||
|
||||
private:
|
||||
[[nodiscard]] MappingStatus EvaluatePointImpl(
|
||||
const ElementMappingData &element_data, mfem::ElementTransformation &transformation,
|
||||
const mfem::IntegrationPoint &integration_point, Workspace &workspace,
|
||||
MappingPointContext &context, const FixedMeshGeometry *meshGeometry,
|
||||
const ReferenceDisplacementSample *displacementSample = nullptr
|
||||
) const;
|
||||
void ValidateElementData(const ElementMappingData &element_data) const;
|
||||
|
||||
void EvaluateField(
|
||||
@@ -214,6 +256,8 @@ export namespace mean_field::mapping {
|
||||
FaceElementSide side
|
||||
);
|
||||
|
||||
struct GeometryCache;
|
||||
mutable std::unique_ptr<GeometryCache> m_geometryCache;
|
||||
utils::DomainMapperOptions m_options;
|
||||
std::unique_ptr<const compactification::ExteriorDomainMap> m_exterior_map;
|
||||
};
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
module;
|
||||
|
||||
#include <concepts>
|
||||
#include <mpi.h>
|
||||
#include <cstdint>
|
||||
#include <expected>
|
||||
#include <memory>
|
||||
@@ -22,9 +23,45 @@ export import :utils.domain;
|
||||
namespace mean_field::normalization::detail {
|
||||
using DomainSchema = utils::domain::CoreEnvelopeVacuumDomainSchema;
|
||||
|
||||
[[nodiscard]] inline bool CanAssembleExactPartialDiagonal(mfem::ParFiniteElementSpace &space, bool hdiv) {
|
||||
// For hanging nodes, diag(P^T A P) contains cross terms. MFEM's PA
|
||||
// diagonal is approximate there, so retain assembled exact diagonals.
|
||||
int eligible = space.GetMesh()->Conforming() && !space.IsVariableOrder() && space.GetVDim() == 1;
|
||||
for (int e = 0; eligible && e < space.GetNE(); ++e) {
|
||||
const auto *fe = space.GetFE(e);
|
||||
eligible = fe->GetGeomType() == mfem::Geometry::CUBE &&
|
||||
(hdiv ? dynamic_cast<const mfem::RT_HexahedronElement *>(fe) != nullptr :
|
||||
dynamic_cast<const mfem::NodalTensorFiniteElement *>(fe) != nullptr);
|
||||
}
|
||||
int allEligible = 0;
|
||||
if (MPI_Allreduce(&eligible, &allEligible, 1, MPI_INT, MPI_MIN, space.GetComm()) != MPI_SUCCESS)
|
||||
throw std::runtime_error("Could not choose a collective normalization assembly path.");
|
||||
return allEligible != 0;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline mfem::Vector AssembleMassDiagonal(
|
||||
mfem::ParBilinearForm &mass, mfem::ParFiniteElementSpace &space, bool partial
|
||||
) {
|
||||
if (partial) {
|
||||
mass.SetAssemblyLevel(mfem::AssemblyLevel::PARTIAL);
|
||||
mass.Assemble();
|
||||
mfem::Vector diagonal(space.GetTrueVSize());
|
||||
mass.AssembleDiagonal(diagonal);
|
||||
return diagonal;
|
||||
}
|
||||
mass.Assemble();
|
||||
mass.Finalize();
|
||||
std::unique_ptr<mfem::HypreParMatrix> matrix(mass.ParallelAssemble());
|
||||
if (!matrix) throw std::runtime_error("Reference Riesz mass assembly failed.");
|
||||
mfem::Vector diagonal;
|
||||
matrix->GetDiag(diagonal);
|
||||
return diagonal;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline mfem::Vector AssembleScalarMassDiagonal(
|
||||
mfem::ParFiniteElementSpace &space,
|
||||
mfem::Array<int> *domainMarker = nullptr
|
||||
mfem::Array<int> *domainMarker = nullptr,
|
||||
bool reference = false
|
||||
) {
|
||||
mfem::ParBilinearForm mass(&space);
|
||||
if (domainMarker == nullptr) {
|
||||
@@ -32,34 +69,80 @@ namespace mean_field::normalization::detail {
|
||||
} else {
|
||||
mass.AddDomainIntegrator(new mfem::MassIntegrator(), *domainMarker);
|
||||
}
|
||||
mass.Assemble();
|
||||
mass.Finalize();
|
||||
std::unique_ptr<mfem::HypreParMatrix> matrix(mass.ParallelAssemble());
|
||||
if (matrix == nullptr) {
|
||||
throw std::runtime_error("Reference scalar Riesz mass assembly failed.");
|
||||
}
|
||||
mfem::Vector diagonal;
|
||||
matrix->GetDiag(diagonal);
|
||||
return diagonal;
|
||||
return AssembleMassDiagonal(mass, space, !reference && CanAssembleExactPartialDiagonal(space, false));
|
||||
}
|
||||
|
||||
[[nodiscard]] inline mfem::Vector AssembleHDivMassDiagonal(mfem::ParFiniteElementSpace &space) {
|
||||
[[nodiscard]] inline mfem::Vector AssembleHDivMassDiagonal(mfem::ParFiniteElementSpace &space, bool reference = false) {
|
||||
mfem::ParBilinearForm mass(&space);
|
||||
mass.AddDomainIntegrator(new mfem::VectorFEMassIntegrator());
|
||||
mass.Assemble();
|
||||
mass.Finalize();
|
||||
std::unique_ptr<mfem::HypreParMatrix> matrix(mass.ParallelAssemble());
|
||||
if (matrix == nullptr) {
|
||||
throw std::runtime_error("Reference H(div) Riesz mass assembly failed.");
|
||||
return AssembleMassDiagonal(mass, space, !reference && CanAssembleExactPartialDiagonal(space, true));
|
||||
}
|
||||
|
||||
[[nodiscard]] inline bool CanAssembleDirectBoundaryDiagonal(
|
||||
mfem::ParFiniteElementSpace &space, int attribute
|
||||
) {
|
||||
int eligible = !space.Nonconforming() && !space.IsVariableOrder() && space.GetVDim() == 1 &&
|
||||
dynamic_cast<const mfem::H1_FECollection *>(space.FEColl()) != nullptr;
|
||||
mfem::Array<int> dofs;
|
||||
for (int b = 0; eligible && b < space.GetNBE(); ++b) {
|
||||
if (space.GetBdrAttribute(b) != attribute) continue;
|
||||
// A general DOF transformation would require transforming the
|
||||
// full element matrix before extracting its diagonal.
|
||||
eligible = space.GetBdrElementVDofs(b, dofs) == nullptr;
|
||||
}
|
||||
mfem::Vector diagonal;
|
||||
matrix->GetDiag(diagonal);
|
||||
return diagonal;
|
||||
int allEligible = 0;
|
||||
if (MPI_Allreduce(&eligible, &allEligible, 1, MPI_INT, MPI_MIN, space.GetComm()) != MPI_SUCCESS)
|
||||
throw std::runtime_error("Could not choose a collective surface normalization assembly path.");
|
||||
return allEligible != 0;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline mfem::Vector AssembleDirectBoundaryDiagonal(
|
||||
mfem::ParFiniteElementSpace &space, int attribute
|
||||
) {
|
||||
mfem::Vector localDiagonal(space.GetVSize());
|
||||
localDiagonal = 0.0;
|
||||
mfem::Array<int> dofs;
|
||||
mfem::Vector shape, elementDiagonal;
|
||||
// GetNBE includes tagged internal interfaces. MFEM's boundary PA face
|
||||
// restriction uses FaceType::Boundary and omits the star/vacuum interface.
|
||||
for (int b = 0; b < space.GetNBE(); ++b) {
|
||||
if (space.GetBdrAttribute(b) != attribute) continue;
|
||||
const auto &element = *space.GetBE(b);
|
||||
auto &transformation = *space.GetBdrElementTransformation(b);
|
||||
space.GetBdrElementVDofs(b, dofs);
|
||||
shape.SetSize(element.GetDof());
|
||||
elementDiagonal.SetSize(element.GetDof());
|
||||
elementDiagonal = 0.0;
|
||||
// Identical rule and physical shapes to MassIntegrator's default
|
||||
// element assembly; only the diagonal of w * shape * shape^T is formed.
|
||||
const auto &rule = mfem::MassIntegrator::GetRule(element, element, transformation);
|
||||
for (int q = 0; q < rule.GetNPoints(); ++q) {
|
||||
const auto &point = rule.IntPoint(q);
|
||||
transformation.SetIntPoint(&point);
|
||||
element.CalcPhysShape(transformation, shape);
|
||||
const double weight = transformation.Weight() * point.weight;
|
||||
for (int i = 0; i < element.GetDof(); ++i)
|
||||
elementDiagonal(i) += weight * shape(i) * shape(i);
|
||||
}
|
||||
for (int i = 0; i < dofs.Size(); ++i) {
|
||||
const int dof = dofs[i] < 0 ? -1 - dofs[i] : dofs[i];
|
||||
localDiagonal(dof) += elementDiagonal(i); // orientation signs are squared
|
||||
}
|
||||
}
|
||||
mfem::Vector trueDiagonal(space.GetTrueVSize());
|
||||
// Conforming H1 prolongation only copies scalar DOFs, so summing the
|
||||
// local diagonal gives diag(P^T A P) exactly, including shared DOFs.
|
||||
if (const auto *prolongation = space.GetProlongationMatrix())
|
||||
prolongation->MultTranspose(localDiagonal, trueDiagonal);
|
||||
else
|
||||
trueDiagonal = localDiagonal;
|
||||
return trueDiagonal;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline mfem::Vector AssembleSurfaceMassDiagonal(
|
||||
const fem::FEM &finiteElements,
|
||||
const field::ScalarBoundaryDofMap &surfaceMap
|
||||
const field::ScalarBoundaryDofMap &surfaceMap,
|
||||
bool reference = false
|
||||
) {
|
||||
mfem::Array<int> marker(finiteElements.mesh->bdr_attributes.Max());
|
||||
marker = 0;
|
||||
@@ -69,16 +152,12 @@ namespace mean_field::normalization::detail {
|
||||
}
|
||||
marker[attribute - 1] = 1;
|
||||
|
||||
if (!reference && CanAssembleDirectBoundaryDiagonal(*finiteElements.surfaceDeformationFes, attribute))
|
||||
return surfaceMap.gather(AssembleDirectBoundaryDiagonal(*finiteElements.surfaceDeformationFes, attribute));
|
||||
|
||||
mfem::ParBilinearForm mass(finiteElements.surfaceDeformationFes.get());
|
||||
mass.AddBoundaryIntegrator(new mfem::MassIntegrator(), marker);
|
||||
mass.Assemble();
|
||||
mass.Finalize();
|
||||
std::unique_ptr<mfem::HypreParMatrix> matrix(mass.ParallelAssemble());
|
||||
if (matrix == nullptr) {
|
||||
throw std::runtime_error("Reference surface Riesz mass assembly failed.");
|
||||
}
|
||||
mfem::Vector ambientDiagonal;
|
||||
matrix->GetDiag(ambientDiagonal);
|
||||
auto ambientDiagonal = AssembleMassDiagonal(mass, *finiteElements.surfaceDeformationFes, false);
|
||||
return surfaceMap.gather(ambientDiagonal);
|
||||
}
|
||||
|
||||
@@ -100,6 +179,19 @@ namespace mean_field::normalization::detail {
|
||||
} // namespace mean_field::normalization::detail
|
||||
|
||||
export namespace mean_field::normalization {
|
||||
// Isolated verification/profiling entry points. reference=true retains the
|
||||
// full assembled true-DOF diagonal; production uses exact PA for supported
|
||||
// volume spaces and direct marked-boundary quadrature for supported surfaces.
|
||||
[[nodiscard]] inline mfem::Vector AssembleReferenceScalarMassDiagonal(
|
||||
mfem::ParFiniteElementSpace &space, mfem::Array<int> *domainMarker = nullptr, bool reference = false
|
||||
) { return detail::AssembleScalarMassDiagonal(space, domainMarker, reference); }
|
||||
[[nodiscard]] inline mfem::Vector AssembleReferenceHDivMassDiagonal(
|
||||
mfem::ParFiniteElementSpace &space, bool reference = false
|
||||
) { return detail::AssembleHDivMassDiagonal(space, reference); }
|
||||
[[nodiscard]] inline mfem::Vector AssembleReferenceSurfaceMassDiagonal(
|
||||
const fem::FEM &finiteElements, const field::ScalarBoundaryDofMap &surfaceMap, bool reference = false
|
||||
) { return detail::AssembleSurfaceMassDiagonal(finiteElements, surfaceMap, reference); }
|
||||
|
||||
/*
|
||||
* Runtime preparation paired with the compile-time normalization plan.
|
||||
* The operator compiler is the authority for which blocks a specification
|
||||
|
||||
@@ -156,6 +156,16 @@ export namespace mean_field::operators {
|
||||
|
||||
void BuildResidual(mfem::Vector &residual) const;
|
||||
|
||||
// Rows differentiate I_axis; changing Omega alone reuses both rows.
|
||||
[[nodiscard]] bool PrepareJacobianRows() const;
|
||||
[[nodiscard]] std::uint64_t GetJacobianRowPreparationCount() const noexcept;
|
||||
void ApplyCompleteJacobianActionReference(
|
||||
const mfem::Vector &densityVariation,
|
||||
const mfem::Vector &displacementVariation,
|
||||
double angularVelocityVariation,
|
||||
mfem::Vector &action
|
||||
) const;
|
||||
|
||||
void ApplyDensityJacobianAction(
|
||||
const mfem::Vector &densityVariation,
|
||||
mfem::Vector &action
|
||||
@@ -223,6 +233,8 @@ export namespace mean_field::operators {
|
||||
|
||||
[[nodiscard]] double EvaluateDensityMomentActionLocal(const mfem::Vector &densityVariation) const;
|
||||
[[nodiscard]] double EvaluateDisplacementMomentActionLocal(const mfem::Vector &displacementVariation) const;
|
||||
[[nodiscard]] double EvaluateDensityMomentActionLocalReference(const mfem::Vector &densityVariation) const;
|
||||
[[nodiscard]] double EvaluateDisplacementMomentActionLocalReference(const mfem::Vector &displacementVariation) const;
|
||||
[[nodiscard]] double CylindricalRadiusSquared(const mfem::Vector &physicalPosition) const noexcept;
|
||||
[[nodiscard]] double CylindricalRadiusSquaredVariation(
|
||||
const mfem::Vector &physicalPosition,
|
||||
@@ -238,6 +250,11 @@ export namespace mean_field::operators {
|
||||
std::vector<ElementPAData> m_elements;
|
||||
AngularMomentumDependencies m_preparedDependencies;
|
||||
mfem::Vector m_cachedResidual;
|
||||
mutable mfem::Vector m_densityJacobianLocal;
|
||||
mutable mfem::Vector m_displacementJacobianLocal;
|
||||
mutable bool m_jacobianRowsReady{false};
|
||||
mutable bool m_jacobianRowsSupported{false};
|
||||
mutable std::uint64_t m_jacobianRowPreparationCount{0};
|
||||
mutable mfem::Vector m_densityVariationTrue;
|
||||
mutable mfem::Vector m_displacementVariationTrue;
|
||||
mutable mfem::Vector m_densityVariationLocal;
|
||||
|
||||
@@ -82,6 +82,20 @@ export namespace mean_field::operators {
|
||||
mfem::Vector &action
|
||||
) const override;
|
||||
|
||||
// Supported-coordinate coupling actions; omit identically absent blocks.
|
||||
void ApplyEnthalpyJacobianAction(const mfem::Vector &enthalpyVariation, mfem::Vector &action,
|
||||
bool useTensorPath = true) const;
|
||||
void ApplyDisplacementJacobianAction(const mfem::Vector &displacementVariation, mfem::Vector &action,
|
||||
bool useTensorPath = true) const;
|
||||
// Retained complete action for isolation/reference comparisons.
|
||||
void MultReference(const mfem::Vector &densityVariation, const mfem::Vector &enthalpyVariation,
|
||||
const mfem::Vector &displacementVariation, mfem::Vector &action) const;
|
||||
|
||||
// Previous optimized pointwise actions, sharing exactly the same state.
|
||||
void MultCachedReference(const mfem::Vector &densityVariation, const mfem::Vector &enthalpyVariation,
|
||||
const mfem::Vector &displacementVariation, mfem::Vector &action) const;
|
||||
[[nodiscard]] int GetTensorElementCount() const noexcept;
|
||||
|
||||
void BuildResidual(mfem::Vector &residual) const;
|
||||
|
||||
// Exact diagonal of the prepared density-to-closure block, expressed
|
||||
@@ -118,15 +132,19 @@ export namespace mean_field::operators {
|
||||
|
||||
void VerifyPrepared() const;
|
||||
|
||||
void MultImpl(const mfem::Vector &densityVariation, const mfem::Vector &enthalpyVariation,
|
||||
const mfem::Vector &displacementVariation, mfem::Vector &action, bool useFastPath, bool useTensorPath) const;
|
||||
|
||||
void ApplyThermodynamicActionFull(
|
||||
const mfem::Vector &densityVariationTrue,
|
||||
const mfem::Vector *densityVariationTrue,
|
||||
const mfem::Vector &enthalpyVariationTrue,
|
||||
mfem::Vector &actionTrue
|
||||
mfem::Vector &actionTrue, bool useTensorPath = true
|
||||
) const;
|
||||
|
||||
void ApplyDisplacementActionFull(
|
||||
const mfem::Vector &displacementVariationTrue,
|
||||
mfem::Vector &actionTrue
|
||||
mfem::Vector &actionTrue,
|
||||
bool useFastPath = true, bool useTensorPath = true
|
||||
) const;
|
||||
|
||||
struct ElementPAData {
|
||||
@@ -180,6 +198,8 @@ export namespace mean_field::operators {
|
||||
mutable mfem::Vector m_quadratureDisplacementAction;
|
||||
mutable mfem::Vector m_elementDisplacementAction;
|
||||
mutable mfem::DenseMatrix m_referenceDisplacementJacobian;
|
||||
mutable mfem::DenseMatrix m_tensorDisplacement;
|
||||
mutable mfem::Vector m_tensorWorkspace;
|
||||
|
||||
std::uint64_t m_preparationCount{0};
|
||||
bool m_isPrepared{false};
|
||||
|
||||
@@ -66,6 +66,9 @@ export namespace mean_field::operators {
|
||||
kernels::GravityDisplacementForceRejection>
|
||||
TryPrepare();
|
||||
|
||||
// Retained two-pass preparation for isolation benchmarks and validation.
|
||||
PreparedGravityDisplacementForceReport PrepareReference();
|
||||
|
||||
void BuildResidual(mfem::Vector &residual) const;
|
||||
|
||||
void ApplyDensityJacobianAction(
|
||||
@@ -90,6 +93,31 @@ export namespace mean_field::operators {
|
||||
mfem::Vector &action
|
||||
) const;
|
||||
|
||||
void ApplyCompleteJacobianActionReference(
|
||||
const mfem::Vector &densityVariation,
|
||||
const mfem::Vector &displacementVariation,
|
||||
const mfem::Vector &gravityGradientVariation,
|
||||
mfem::Vector &action
|
||||
) const;
|
||||
|
||||
void ApplyCompleteJacobianActionCachedReference(
|
||||
const mfem::Vector &densityVariation, const mfem::Vector &displacementVariation,
|
||||
const mfem::Vector &gravityGradientVariation, mfem::Vector &action
|
||||
) const;
|
||||
[[nodiscard]] int GetTensorElementCount() const noexcept {
|
||||
int count = 0;
|
||||
for (const auto &data : m_elements)
|
||||
if (data.fastApplication && data.displacementReferenceTable->HasTensorEvaluation3D() &&
|
||||
data.gravityReferenceTable && data.gravityReferenceTable->HasTensorEvaluation3D()) ++count;
|
||||
return count;
|
||||
}
|
||||
|
||||
[[nodiscard]] int GetFastElementCount() const noexcept {
|
||||
int count = 0;
|
||||
for (const auto &data : m_elements) count += data.fastApplication;
|
||||
return count;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool IsPrepared() const noexcept;
|
||||
|
||||
[[nodiscard]] std::uint64_t GetResidualPreparationCount() const noexcept;
|
||||
@@ -124,16 +152,20 @@ export namespace mean_field::operators {
|
||||
[[nodiscard]] std::expected<
|
||||
void,
|
||||
kernels::GravityDisplacementForceRejection>
|
||||
TryPrepareElementData();
|
||||
TryPrepareElementData(bool assembleResidual);
|
||||
[[nodiscard]] std::expected<PreparedGravityDisplacementForceReport, kernels::GravityDisplacementForceRejection>
|
||||
TryPrepareImpl(bool useFastPreparation);
|
||||
void ApplyPreparedCompleteJacobianActionTrue(
|
||||
const mfem::Vector &densityVariationTrue,
|
||||
const mfem::Vector &displacementVariationTrue,
|
||||
const mfem::Vector &gravityGradientVariationTrue,
|
||||
mfem::Vector &actionTrue
|
||||
mfem::Vector &actionTrue,
|
||||
bool useFastPath = true, bool useTensorPath = true
|
||||
) const;
|
||||
|
||||
struct ElementPAData {
|
||||
int elementId{-1};
|
||||
bool fastApplication{false};
|
||||
mfem::Array<int> densityDofs;
|
||||
mfem::Array<int> gravityGradientDofs;
|
||||
mfem::Array<int> displacementDofs;
|
||||
@@ -168,6 +200,8 @@ export namespace mean_field::operators {
|
||||
mutable mfem::Vector m_gravityGradientVariationLocal;
|
||||
mutable mfem::Vector m_displacementVariationLocal;
|
||||
mutable mfem::Vector m_localAction;
|
||||
mutable mfem::Vector m_quadratureDensityVariation;
|
||||
mutable mfem::DenseMatrix m_quadratureForce;
|
||||
mutable mfem::Vector m_elementDensityVariation;
|
||||
mutable mfem::Vector m_elementGravityGradientVariation;
|
||||
mutable mfem::Vector m_elementDisplacementVariation;
|
||||
@@ -199,6 +233,9 @@ export namespace mean_field::operators {
|
||||
|
||||
mutable PreparedGravityDisplacementForceCompleteStatistics m_completeJacobianStatistics;
|
||||
|
||||
mutable mfem::DenseMatrix m_tensorDisplacement, m_tensorGravity, m_tensorElementDual;
|
||||
mutable mfem::Vector m_tensorWorkspace;
|
||||
bool m_preparedWithFastResidual{true};
|
||||
bool m_isPrepared{false};
|
||||
};
|
||||
|
||||
|
||||
@@ -38,6 +38,8 @@ export namespace mean_field::operators {
|
||||
|
||||
void Prepare(const mfem::Vector &displacement);
|
||||
void PreparePrimal(const mfem::Vector &displacement);
|
||||
// Previous weight-recomputation path for isolated preparation comparisons.
|
||||
void PrepareReference(const mfem::Vector &displacement);
|
||||
[[nodiscard]] GravitySourcePreparationResult TryPrepare(const mfem::Vector &displacement);
|
||||
[[nodiscard]] GravitySourcePreparationResult TryPreparePrimal(const mfem::Vector &displacement);
|
||||
void Mult(
|
||||
@@ -64,6 +66,11 @@ export namespace mean_field::operators {
|
||||
}
|
||||
}
|
||||
|
||||
void MultDisplacementVariationTrueReference(
|
||||
const mfem::Vector &densityTrue, const mfem::Vector &displacementVariationTrue,
|
||||
mfem::Vector &actionVariationTrue) const;
|
||||
[[nodiscard]] int GetTensorVariationElementCount() const noexcept;
|
||||
|
||||
void MultTranspose(
|
||||
const mfem::Vector &potential,
|
||||
mfem::Vector &action
|
||||
@@ -109,7 +116,7 @@ export namespace mean_field::operators {
|
||||
|
||||
[[nodiscard]] GravitySourcePreparationResult TryPrepareImpl(
|
||||
const mfem::Vector &displacement,
|
||||
PreparationMode mode
|
||||
PreparationMode mode, bool useCachedMeshWeight = true
|
||||
);
|
||||
|
||||
const fem::FEM &m_fem;
|
||||
@@ -137,6 +144,11 @@ export namespace mean_field::operators {
|
||||
mutable mfem::Vector m_element_displacement_variation;
|
||||
mutable mfem::Vector m_quadrature_variation_action;
|
||||
mutable mfem::Vector m_element_variation_action;
|
||||
void MultDisplacementVariationTrueImpl(const mfem::Vector &densityTrue,
|
||||
const mfem::Vector &displacementVariationTrue, mfem::Vector &actionVariationTrue,
|
||||
bool useTensorPath) const;
|
||||
mutable mfem::DenseMatrix m_tensorSamples;
|
||||
mutable mfem::Vector m_tensorWorkspace;
|
||||
mutable mfem::DenseMatrix m_reference_displacement_jacobian;
|
||||
mfem::Vector m_displacement_true;
|
||||
|
||||
|
||||
@@ -44,11 +44,29 @@ export namespace mean_field::operators {
|
||||
const mfem::Vector &gravity_gradient,
|
||||
mfem::Vector &action
|
||||
) const override;
|
||||
// Unmodified MFEM partial-assembly application on the same prepared data.
|
||||
void MultReference(const mfem::Vector &gravityGradient, mfem::Vector &action) const;
|
||||
[[nodiscard]] int GetFastMassElementCount() const noexcept { return m_fastMassElements; }
|
||||
|
||||
void MultDisplacementVariationTrue(
|
||||
const mfem::Vector &gravityGradientTrue,
|
||||
const mfem::Vector &displacementVariationTrue,
|
||||
mfem::Vector &actionVariationTrue
|
||||
) const;
|
||||
// Retained prepared implementation for isolated A/B validation and
|
||||
// profiling. Uses the same prepared state, quadrature and DOF maps.
|
||||
void MultDisplacementVariationTrueReference(
|
||||
const mfem::Vector &gravityGradientTrue,
|
||||
const mfem::Vector &displacementVariationTrue,
|
||||
mfem::Vector &actionVariationTrue
|
||||
) const;
|
||||
// Previous optimized pointwise path, excluding the new tensor contractions.
|
||||
void MultDisplacementVariationTrueCachedReference(
|
||||
const mfem::Vector &gravityGradientTrue, const mfem::Vector &displacementVariationTrue,
|
||||
mfem::Vector &actionVariationTrue
|
||||
) const;
|
||||
[[nodiscard]] int GetFastVariationElementCount() const noexcept;
|
||||
[[nodiscard]] int GetTensorVariationElementCount() const noexcept;
|
||||
void AssembleDiagonal(mfem::Vector &diagonal) const override;
|
||||
void AssembleTrueDiagonal(mfem::Vector &diagonal) const;
|
||||
|
||||
@@ -79,6 +97,11 @@ export namespace mean_field::operators {
|
||||
mfem::Vector compactification;
|
||||
const mfem::IntegrationRule *integrationRule{nullptr};
|
||||
std::shared_ptr<const fem::VectorReferenceTable> gravityReferenceTable;
|
||||
std::shared_ptr<const fem::ScalarReferenceTable> displacementReferenceTable;
|
||||
// One contiguous column per quadrature point: scale*J_mesh^-1
|
||||
// (row-major, nine entries), followed by grad(scale). 3D VALUE
|
||||
// displacement with an interior or Kelvin map; empty otherwise.
|
||||
mfem::DenseMatrix tangentFactors;
|
||||
// Fixed computational-mesh Piola factor, separate from J_map.
|
||||
mfem::DenseMatrix meshPiolaJacobians;
|
||||
mfem::Vector referenceWeights;
|
||||
@@ -86,6 +109,13 @@ export namespace mean_field::operators {
|
||||
};
|
||||
|
||||
[[nodiscard]] mapping::MappingStatus PrepareVariationData();
|
||||
void MultDisplacementVariationTrueImpl(
|
||||
const mfem::Vector &gravityGradientTrue,
|
||||
const mfem::Vector &displacementVariationTrue,
|
||||
mfem::Vector &actionVariationTrue,
|
||||
bool useFastPath, bool useTensorPath
|
||||
) const;
|
||||
void AddFastElementVariation(const ElementVariationData &data, bool useTensorPath) const;
|
||||
[[nodiscard]] HDivMassPreparationResult TryPrepareImpl(
|
||||
const mfem::Vector &displacement,
|
||||
PreparationMode mode
|
||||
@@ -129,9 +159,12 @@ export namespace mean_field::operators {
|
||||
mutable mfem::DenseMatrix m_meshPiolaJacobian;
|
||||
mutable mfem::DenseMatrix m_gravityGradientShape;
|
||||
mutable mfem::DenseMatrix m_massTensorVariation;
|
||||
mutable mfem::DenseMatrix m_tensorDisplacement, m_tensorGravity, m_tensorDual;
|
||||
mutable mfem::Vector m_tensorWorkspace;
|
||||
std::uint64_t m_preparation_count{0};
|
||||
bool m_is_prepared{false};
|
||||
bool m_has_variation_data{false};
|
||||
bool m_single_rank{true};
|
||||
int m_fastMassElements{0};
|
||||
};
|
||||
} // namespace mean_field::operators
|
||||
|
||||
@@ -142,6 +142,14 @@ export namespace mean_field::operators {
|
||||
const physics::RigidRotation &rotation
|
||||
);
|
||||
|
||||
HydrostaticEquilibriumPreparationResult TryPrepareReference(
|
||||
const context::hydrostatic::HydrostaticEquilibriumStateView &state,
|
||||
const context::hydrostatic::HydrostaticEquilibriumDependencies &dependencies,
|
||||
const physics::RigidRotation &rotation);
|
||||
PreparedHydrostaticEquilibriumReport PrepareReference(
|
||||
const context::hydrostatic::HydrostaticEquilibriumStateView &state,
|
||||
const context::hydrostatic::HydrostaticEquilibriumDependencies &dependencies,
|
||||
const physics::RigidRotation &rotation);
|
||||
void BuildResidual(mfem::Vector &residual) const;
|
||||
|
||||
void ApplyEnthalpyJacobianAction(
|
||||
@@ -179,6 +187,18 @@ export namespace mean_field::operators {
|
||||
mfem::Vector &action
|
||||
) const;
|
||||
|
||||
void ApplyDisplacementJacobianActionReference(
|
||||
const mfem::Vector &displacementVariation,
|
||||
mfem::Vector &action
|
||||
) const;
|
||||
// Previous cached pointwise implementation, retained for benchmarking
|
||||
// the tensor change separately from the older general mapping path.
|
||||
void ApplyDisplacementJacobianActionCachedReference(
|
||||
const mfem::Vector &displacementVariation, mfem::Vector &action
|
||||
) const;
|
||||
[[nodiscard]] int GetFastDisplacementElementCount() const noexcept;
|
||||
[[nodiscard]] int GetTensorDisplacementElementCount() const noexcept;
|
||||
|
||||
void ApplyCompleteJacobianAction(
|
||||
const mfem::Vector &enthalpyVariation,
|
||||
const mfem::Vector &gravityPotentialVariation,
|
||||
@@ -244,6 +264,10 @@ export namespace mean_field::operators {
|
||||
// Immutable reference values and gradients are shared by FE/rule.
|
||||
std::shared_ptr<const fem::ScalarReferenceTable> enthalpyReferenceTable;
|
||||
std::shared_ptr<const fem::ScalarReferenceTable> gravityPotentialReferenceTable;
|
||||
std::shared_ptr<const fem::ScalarReferenceTable> displacementReferenceTable;
|
||||
// Columns are quadrature points; each holds a column-major inverse
|
||||
// deformed element Jacobian. Empty for the general mapping path.
|
||||
mfem::DenseMatrix displacementInverseJacobians;
|
||||
|
||||
[[nodiscard]] const mfem::DenseMatrix &GetEnthalpyBasis() const {
|
||||
return enthalpyReferenceTable->GetValues();
|
||||
@@ -274,14 +298,24 @@ export namespace mean_field::operators {
|
||||
mfem::Vector bernoulliConstantJacobian;
|
||||
};
|
||||
|
||||
HydrostaticEquilibriumPreparationResult TryPrepareImpl(
|
||||
const context::hydrostatic::HydrostaticEquilibriumStateView &state,
|
||||
const context::hydrostatic::HydrostaticEquilibriumDependencies &dependencies,
|
||||
const physics::RigidRotation &rotation, bool useTensorPreparation);
|
||||
void PrepareStaticPlan();
|
||||
[[nodiscard]] std::optional<mapping::MappingStatus> PrepareGeometry();
|
||||
[[nodiscard]] bool PrepareAlgebraicJacobianBlocks();
|
||||
[[nodiscard]] std::optional<mapping::MappingStatus> PrepareGeometry(bool useTensorPreparation);
|
||||
[[nodiscard]] bool PrepareAlgebraicJacobianBlocks(bool useTensorPreparation);
|
||||
[[nodiscard]] bool PrepareRotation();
|
||||
[[nodiscard]] bool PrepareBaseState();
|
||||
[[nodiscard]] bool PrepareBaseState(bool useTensorPreparation);
|
||||
void FinalizeDisplacementJacobianPreparation();
|
||||
[[nodiscard]] bool AssembleCachedResidual();
|
||||
void VerifyPrepared() const;
|
||||
void ApplyDisplacementJacobianActionImpl(
|
||||
const mfem::Vector &displacementVariation, mfem::Vector &action, bool useFastPath, bool useTensorPath
|
||||
) const;
|
||||
|
||||
mutable mfem::DenseMatrix m_tensorDisplacementValuesAndGradients;
|
||||
mutable mfem::Vector m_tensorDisplacementWorkspace;
|
||||
|
||||
const fem::FEM &m_fem;
|
||||
const mapping::DomainMapper &m_domainMapper;
|
||||
@@ -307,6 +341,7 @@ export namespace mean_field::operators {
|
||||
|
||||
mutable PreparedHydrostaticCompleteJacobianStatistics m_completeJacobianStatistics;
|
||||
|
||||
bool m_preparedWithTensorPreparation{true};
|
||||
bool m_isPrepared{false};
|
||||
};
|
||||
|
||||
|
||||
@@ -147,6 +147,16 @@ export namespace mean_field::operators {
|
||||
|
||||
void BuildResidual(mfem::Vector &residual) const;
|
||||
|
||||
// Lazy, local-dual row assembly. Invalidated only by density/geometry.
|
||||
// Returns false when this discretization uses the general fallback.
|
||||
[[nodiscard]] bool PrepareJacobianRows() const;
|
||||
[[nodiscard]] std::uint64_t GetJacobianRowPreparationCount() const noexcept;
|
||||
void ApplyCompleteJacobianActionReference(
|
||||
const mfem::Vector &densityVariation,
|
||||
const mfem::Vector &displacementVariation,
|
||||
mfem::Vector &action
|
||||
) const;
|
||||
|
||||
void ApplyDensityJacobianAction(
|
||||
const mfem::Vector &densityVariation,
|
||||
mfem::Vector &action
|
||||
@@ -230,6 +240,9 @@ export namespace mean_field::operators {
|
||||
|
||||
[[nodiscard]] double EvaluateDisplacementActionLocal(const mfem::Vector &displacementVariation) const;
|
||||
|
||||
[[nodiscard]] double EvaluateDensityActionLocalReference(const mfem::Vector &densityVariation) const;
|
||||
[[nodiscard]] double EvaluateDisplacementActionLocalReference(const mfem::Vector &displacementVariation) const;
|
||||
|
||||
void AssembleDensityTransposeAction(
|
||||
double residualDual,
|
||||
mfem::Vector &densityDual
|
||||
@@ -250,8 +263,16 @@ export namespace mean_field::operators {
|
||||
MassNormalizationDependencies m_preparedDependencies;
|
||||
mfem::Vector m_cachedResidual;
|
||||
|
||||
mutable mfem::Vector m_densityJacobianLocal;
|
||||
mutable mfem::Vector m_displacementJacobianLocal;
|
||||
mutable bool m_jacobianRowsReady{false};
|
||||
mutable bool m_jacobianRowsSupported{false};
|
||||
mutable std::uint64_t m_jacobianRowPreparationCount{0};
|
||||
|
||||
mutable mfem::Vector m_densityVariationTrue;
|
||||
mutable mfem::Vector m_displacementVariationTrue;
|
||||
mutable mfem::Vector m_densityVariationLocal;
|
||||
mutable mfem::Vector m_displacementVariationLocal;
|
||||
|
||||
double m_currentMass{0.0};
|
||||
double m_targetMass{0.0};
|
||||
|
||||
@@ -116,6 +116,20 @@ export namespace mean_field::operators {
|
||||
const context::pressure_force::PressureForceDependencies &dependencies
|
||||
);
|
||||
|
||||
// Original quadrature-by-quadrature matrix assembly for validation.
|
||||
PreparedPressureForceReport PrepareReference(
|
||||
const context::pressure_force::PressureForceStateView &state,
|
||||
const context::pressure_force::PressureForceDependencies &dependencies
|
||||
);
|
||||
|
||||
// Previous blocked matrix preparation and pointwise field evaluation.
|
||||
PreparedPressureForceReport PrepareCachedReference(
|
||||
const context::pressure_force::PressureForceStateView &state,
|
||||
const context::pressure_force::PressureForceDependencies &dependencies);
|
||||
void ApplyDisplacementJacobianActionCachedReference(
|
||||
const mfem::Vector &displacementVariation, mfem::Vector &action) const;
|
||||
[[nodiscard]] int GetTensorElementCount() const noexcept;
|
||||
|
||||
void BuildResidual(mfem::Vector &residual) const;
|
||||
|
||||
void ApplyEnthalpyJacobianAction(
|
||||
@@ -128,6 +142,10 @@ export namespace mean_field::operators {
|
||||
mfem::Vector &action
|
||||
) const;
|
||||
|
||||
void ApplyDisplacementJacobianActionReference(
|
||||
const mfem::Vector &displacementVariation, mfem::Vector &action
|
||||
) const;
|
||||
|
||||
void ApplyCompleteJacobianAction(
|
||||
const mfem::Vector &enthalpyVariation,
|
||||
const mfem::Vector &displacementVariation,
|
||||
@@ -178,6 +196,8 @@ export namespace mean_field::operators {
|
||||
}
|
||||
|
||||
private:
|
||||
void ApplyDisplacementJacobianActionImpl(const mfem::Vector &displacementVariation,
|
||||
mfem::Vector &action, bool useFastPath, bool useTensorPath = true) const;
|
||||
struct ConstructionData;
|
||||
|
||||
[[nodiscard]]
|
||||
@@ -237,8 +257,14 @@ export namespace mean_field::operators {
|
||||
};
|
||||
|
||||
void PrepareStaticPlan();
|
||||
[[nodiscard]] std::optional<PressureForcePreparationRejection> PrepareGeometry();
|
||||
[[nodiscard]] std::optional<PressureForcePreparationRejection> PrepareMaterialState();
|
||||
[[nodiscard]] std::optional<PressureForcePreparationRejection> PrepareGeometry(bool useTensorPreparation);
|
||||
[[nodiscard]] std::optional<PressureForcePreparationRejection> PrepareMaterialState(bool useFastPreparation, bool useTensorPreparation);
|
||||
[[nodiscard]] std::expected<PreparedPressureForceReport, PressureForcePreparationRejection>
|
||||
TryPrepareImpl(
|
||||
const context::pressure_force::PressureForceStateView &state,
|
||||
const context::pressure_force::PressureForceDependencies &dependencies,
|
||||
bool useFastPreparation, bool useTensorPreparation = true
|
||||
);
|
||||
|
||||
void FinalizeDisplacementJacobianPreparation();
|
||||
|
||||
@@ -291,6 +317,10 @@ export namespace mean_field::operators {
|
||||
|
||||
mutable PreparedPressureForceCompleteJacobianStatistics m_completeJacobianStatistics;
|
||||
|
||||
mutable mfem::DenseMatrix m_tensorSamples, m_tensorDuals, m_tensorElement;
|
||||
mutable mfem::Vector m_tensorWorkspace;
|
||||
bool m_preparedWithTensorPreparation{true};
|
||||
bool m_preparedWithFastMaterialAssembly{true};
|
||||
bool m_isPrepared{false};
|
||||
};
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ module;
|
||||
#include <cstdint>
|
||||
#include <expected>
|
||||
#include <optional>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include <mfem.hpp>
|
||||
@@ -11,6 +12,7 @@ module;
|
||||
export module mean_field:operators.prepared_rotational_displacement_force;
|
||||
|
||||
export import :fem;
|
||||
import :fem.reference_tables;
|
||||
export import :mapping.domain_mapper;
|
||||
export import :operators.context.rotational_displacement_force;
|
||||
export import :operators.kernels.rotational_displacement_force;
|
||||
@@ -72,6 +74,13 @@ export namespace mean_field::operators {
|
||||
const physics::RigidRotation &rotation
|
||||
);
|
||||
|
||||
// Retained two-pass preparation for isolation benchmarks and validation.
|
||||
PreparedRotationalDisplacementForceReport PrepareReference(
|
||||
const context::rotational_displacement_force::RotationalDisplacementForceStateView &state,
|
||||
const context::rotational_displacement_force::RotationalDisplacementForceDependencies &dependencies,
|
||||
const physics::RigidRotation &rotation
|
||||
);
|
||||
|
||||
void BuildResidual(mfem::Vector &residual) const;
|
||||
|
||||
void ApplyDensityJacobianAction(
|
||||
@@ -90,6 +99,23 @@ export namespace mean_field::operators {
|
||||
mfem::Vector &action
|
||||
) const;
|
||||
|
||||
void ApplyCompleteJacobianActionReference(
|
||||
const mfem::Vector &densityVariation,
|
||||
const mfem::Vector &displacementVariation,
|
||||
mfem::Vector &action
|
||||
) const;
|
||||
|
||||
void ApplyCompleteJacobianActionCachedReference(
|
||||
const mfem::Vector &densityVariation, const mfem::Vector &displacementVariation, mfem::Vector &action
|
||||
) const;
|
||||
[[nodiscard]] int GetTensorElementCount() const noexcept;
|
||||
|
||||
[[nodiscard]] int GetFastElementCount() const noexcept {
|
||||
int count = 0;
|
||||
for (const auto &data : m_elements) count += data.fastApplication;
|
||||
return count;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool IsPrepared() const noexcept;
|
||||
|
||||
[[nodiscard]] const context::rotational_displacement_force::RotationalDisplacementForcePreparationStatistics &
|
||||
@@ -124,20 +150,31 @@ export namespace mean_field::operators {
|
||||
[[nodiscard]] std::expected<
|
||||
void,
|
||||
kernels::RotationalDisplacementForceRejection>
|
||||
TryPrepareElementData();
|
||||
TryPrepareElementData(bool assembleResidual);
|
||||
[[nodiscard]] std::expected<PreparedRotationalDisplacementForceReport, kernels::RotationalDisplacementForceRejection>
|
||||
TryPrepareImpl(
|
||||
const context::rotational_displacement_force::RotationalDisplacementForceStateView &state,
|
||||
const context::rotational_displacement_force::RotationalDisplacementForceDependencies &dependencies,
|
||||
const physics::RigidRotation &rotation,
|
||||
bool useFastPreparation
|
||||
);
|
||||
void ApplyPreparedCompleteJacobianActionTrue(
|
||||
const mfem::Vector &densityVariationTrue,
|
||||
const mfem::Vector &displacementVariationTrue,
|
||||
mfem::Vector &actionTrue
|
||||
mfem::Vector &actionTrue,
|
||||
bool useFastPath = true, bool useTensorPath = true
|
||||
) const;
|
||||
|
||||
struct ElementPAData {
|
||||
int elementId{-1};
|
||||
bool fastApplication{false};
|
||||
mfem::Array<int> densityDofs;
|
||||
mfem::Array<int> displacementDofs;
|
||||
mfem::DofTransformation *densityDofTransformation{nullptr};
|
||||
mfem::DofTransformation *displacementDofTransformation{nullptr};
|
||||
const mfem::IntegrationRule *integrationRule{nullptr};
|
||||
std::shared_ptr<const fem::ScalarReferenceTable> densityReferenceTable;
|
||||
std::shared_ptr<const fem::ScalarReferenceTable> displacementReferenceTable;
|
||||
mfem::DenseMatrix inverseElementJacobians;
|
||||
mfem::DenseMatrix centrifugalAccelerations;
|
||||
mfem::Vector baseDensityValues;
|
||||
@@ -150,6 +187,7 @@ export namespace mean_field::operators {
|
||||
context::rotational_displacement_force::RotationalDisplacementForceLinearizationContext m_context;
|
||||
|
||||
std::optional<physics::RigidRotation> m_rotation;
|
||||
mfem::DenseMatrix m_centrifugalJacobian;
|
||||
mfem::Vector m_cachedResidual;
|
||||
std::vector<ElementPAData> m_elements;
|
||||
mutable mfem::Vector m_densityVariationTrue;
|
||||
@@ -158,6 +196,11 @@ export namespace mean_field::operators {
|
||||
mutable mfem::Vector m_densityVariationLocal;
|
||||
mutable mfem::Vector m_displacementVariationLocal;
|
||||
mutable mfem::Vector m_localAction;
|
||||
mutable mfem::Vector m_quadratureDensityVariation;
|
||||
mutable mfem::DenseMatrix m_quadratureForce;
|
||||
mutable mfem::DenseMatrix m_quadraturePositionVariation;
|
||||
mutable mfem::DenseMatrix m_tensorDisplacement, m_tensorElement;
|
||||
mutable mfem::Vector m_tensorWorkspace;
|
||||
mutable mfem::Vector m_elementDensityVariation;
|
||||
mutable mfem::Vector m_elementDisplacementVariation;
|
||||
mutable mfem::Vector m_elementAction;
|
||||
@@ -181,6 +224,7 @@ export namespace mean_field::operators {
|
||||
|
||||
mutable PreparedRotationalDisplacementForceCompleteStatistics m_completeJacobianStatistics;
|
||||
|
||||
bool m_preparedWithFastResidual{true};
|
||||
bool m_isPrepared{false};
|
||||
};
|
||||
|
||||
|
||||
@@ -411,9 +411,27 @@ export namespace mean_field::preconditioning {
|
||||
|
||||
[[nodiscard]] std::unique_ptr<mfem::HypreParMatrix> assembleGravityDivergenceSurrogate(const fem::FEM &f);
|
||||
|
||||
// Fixed computational-mesh divergence, owned by one prepared block.
|
||||
// FES/mesh/node sequences and policy identity invalidate it. Raw node edits
|
||||
// require Mesh::NodesUpdated(). Clear explicitly if the policy changes in place.
|
||||
class PreparedGravityDivergenceCache {
|
||||
public:
|
||||
[[nodiscard]] const mfem::HypreParMatrix &Get(const fem::FEM &f);
|
||||
void Clear() noexcept { m_matrix.reset(); }
|
||||
[[nodiscard]] std::uint64_t GetBuildCount() const noexcept { return m_builds; }
|
||||
private:
|
||||
std::unique_ptr<mfem::HypreParMatrix> m_matrix;
|
||||
const mfem::Mesh *m_mesh{nullptr};
|
||||
const mfem::ParFiniteElementSpace *m_flux{nullptr}, *m_potential{nullptr};
|
||||
const quadrature::RuleFactory *m_policy{nullptr};
|
||||
long m_meshSequence{-1}, m_nodesSequence{-1}, m_fluxSequence{-1}, m_potentialSequence{-1};
|
||||
std::uint64_t m_builds{0};
|
||||
};
|
||||
|
||||
[[nodiscard]] std::unique_ptr<mfem::HypreParMatrix> assembleGravityPotentialSchurSurrogate(
|
||||
const fem::FEM &f,
|
||||
const mfem::Vector &trueMassDiagonal
|
||||
const mfem::Vector &trueMassDiagonal,
|
||||
PreparedGravityDivergenceCache *divergenceCache = nullptr
|
||||
);
|
||||
|
||||
struct GravityFieldBlockPreparationReport final {
|
||||
@@ -548,6 +566,7 @@ export namespace mean_field::preconditioning {
|
||||
RefreshMassInverse(geometryContext);
|
||||
report.rebuiltMassInverse = true;
|
||||
|
||||
if (report.discretizationChanged) m_fixedDivergence.Clear();
|
||||
auto potentialSchur = AssemblePotentialSchur(f, geometryContext);
|
||||
m_potentialSchurInverse.Refresh(*potentialSchur);
|
||||
m_potentialSchurSurrogate = std::move(potentialSchur);
|
||||
@@ -586,6 +605,10 @@ export namespace mean_field::preconditioning {
|
||||
return m_factorization;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::uint64_t GetDivergenceAssemblyCount() const noexcept {
|
||||
return m_fixedDivergence.GetBuildCount();
|
||||
}
|
||||
|
||||
[[nodiscard]] const PreparedGravityFieldBlockStatistics &GetStatistics() const noexcept {
|
||||
return m_statistics;
|
||||
}
|
||||
@@ -632,13 +655,13 @@ export namespace mean_field::preconditioning {
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] static std::unique_ptr<mfem::HypreParMatrix> AssemblePotentialSchur(
|
||||
[[nodiscard]] std::unique_ptr<mfem::HypreParMatrix> AssemblePotentialSchur(
|
||||
const fem::FEM &f,
|
||||
const operators::context::gravity_field::GravityFieldGeometryContext &geometryContext
|
||||
) {
|
||||
mfem::Vector trueMassDiagonal;
|
||||
geometryContext.GetMassOperator().AssembleTrueDiagonal(trueMassDiagonal);
|
||||
return assembleGravityPotentialSchurSurrogate(f, trueMassDiagonal);
|
||||
return assembleGravityPotentialSchurSurrogate(f, trueMassDiagonal, &m_fixedDivergence);
|
||||
}
|
||||
|
||||
Block m_block;
|
||||
@@ -647,6 +670,7 @@ export namespace mean_field::preconditioning {
|
||||
field::FieldDofMap m_potentialMap;
|
||||
ReducedGravityDivergenceOperator m_divergence;
|
||||
PreparedMassInverse m_massInverse;
|
||||
PreparedGravityDivergenceCache m_fixedDivergence;
|
||||
std::unique_ptr<mfem::HypreParMatrix> m_potentialSchurSurrogate;
|
||||
backend::PreparedHypreBoomerAMG<Mode> m_potentialSchurInverse;
|
||||
ReducedFieldSolverAdapter m_reducedPotentialSchurInverse;
|
||||
|
||||
@@ -784,9 +784,7 @@ export namespace mean_field::preconditioning {
|
||||
) const {
|
||||
VerifyBlock(enthalpy, 2, "enthalpy direction");
|
||||
VerifyBlock(densityAction, 0, "density action");
|
||||
m_operation->GetBarotropicClosureOperator().Mult(
|
||||
m_zeroDensity, enthalpy, ZeroVolumeDisplacement(), densityAction
|
||||
);
|
||||
m_operation->GetBarotropicClosureOperator().ApplyEnthalpyJacobianAction(enthalpy, densityAction);
|
||||
}
|
||||
|
||||
void ApplySurfaceToMaterial(
|
||||
@@ -798,8 +796,8 @@ export namespace mean_field::preconditioning {
|
||||
VerifyBlock(densityAction, 0, "density action");
|
||||
VerifyBlock(enthalpyAction, 2, "enthalpy action");
|
||||
GenerateDisplacement(surface);
|
||||
m_operation->GetBarotropicClosureOperator().Mult(
|
||||
m_zeroDensity, m_zeroEnthalpy, m_volumeDisplacement, densityAction
|
||||
m_operation->GetBarotropicClosureOperator().ApplyDisplacementJacobianAction(
|
||||
m_volumeDisplacement, densityAction
|
||||
);
|
||||
m_operation->GetHydrostaticOperator().ApplyDisplacementJacobianAction(m_volumeDisplacement, enthalpyAction);
|
||||
m_operation->GetSurfaceConstraintOperator().ApplyJacobianRows(m_zeroEnthalpy, enthalpyAction);
|
||||
|
||||
@@ -32,6 +32,9 @@ export namespace mean_field::solver::nonlinear {
|
||||
double sufficientDecrease{1.0e-4};
|
||||
double minimumStepLength{1.0e-8};
|
||||
int maximumTrials{20};
|
||||
// Skipping the predictive scan retains trial mapping validation and
|
||||
// ordinary backtracking. Useful for small, warm-start Newton steps.
|
||||
bool geometryPreflight{true};
|
||||
|
||||
void Validate() const {
|
||||
if (!std::isfinite(initialStepLength) || initialStepLength <= 0.0) {
|
||||
|
||||
@@ -143,11 +143,12 @@ namespace mean_field::solver::detail {
|
||||
};
|
||||
std::array<double, 9> minimumRealValues{};
|
||||
std::array<double, 9> maximumRealValues{};
|
||||
const std::array<int, 3> localIntegerValues{
|
||||
options.maximumIterations, options.linearSolve.maximumIterations, options.backtracking.maximumTrials
|
||||
const std::array<int, 4> localIntegerValues{
|
||||
options.maximumIterations, options.linearSolve.maximumIterations, options.backtracking.maximumTrials,
|
||||
static_cast<int>(options.backtracking.geometryPreflight)
|
||||
};
|
||||
std::array<int, 3> minimumIntegerValues{};
|
||||
std::array<int, 3> maximumIntegerValues{};
|
||||
std::array<int, 4> minimumIntegerValues{};
|
||||
std::array<int, 4> maximumIntegerValues{};
|
||||
|
||||
if (MPI_Allreduce(
|
||||
localRealValues.data(), minimumRealValues.data(), static_cast<int>(localRealValues.size()), MPI_DOUBLE,
|
||||
@@ -341,7 +342,6 @@ export namespace mean_field::solver {
|
||||
ValidateInitialState();
|
||||
InitializeWorkspaces();
|
||||
PrepareInitialOperator();
|
||||
InitializeGeometryPreflightRules();
|
||||
|
||||
physicalInverse = std::unique_ptr<PhysicalInverseType>{new PhysicalInverseType(
|
||||
PreparePhysicalInverse(std::move(preconditionerPrescription), RequireProblem(storage))
|
||||
@@ -485,6 +485,10 @@ export namespace mean_field::solver {
|
||||
const double maximumStepSize,
|
||||
const double fractionToBoundarySafety
|
||||
) {
|
||||
if (!geometryPreflightRulesInitialized) {
|
||||
InitializeGeometryPreflightRules();
|
||||
geometryPreflightRulesInitialized = true;
|
||||
}
|
||||
normalizedOperator->DenormalizeState(normalizedCorrection, physicalCorrection);
|
||||
const auto &physicalOperator = Problem().GetPhysicalOperator();
|
||||
Problem().BuildVolumeDisplacementDirection(physicalCorrection, volumeDisplacementDirection);
|
||||
@@ -513,6 +517,7 @@ export namespace mean_field::solver {
|
||||
mfem::Vector volumeDisplacementDirection;
|
||||
mfem::Vector candidatePhysicalState;
|
||||
std::vector<deformation::NewtonStepGeometryRule> geometryPreflightRules;
|
||||
bool geometryPreflightRulesInitialized{false};
|
||||
double acceptedMinimumJacobianDeterminant{std::numeric_limits<double>::quiet_NaN()};
|
||||
std::unique_ptr<NormalizedOperatorType> normalizedOperator;
|
||||
std::unique_ptr<PhysicalInverseType> physicalInverse;
|
||||
@@ -1271,31 +1276,33 @@ export namespace mean_field::solver {
|
||||
}
|
||||
|
||||
const nonlinear::MetricEvaluation previousMetric = acceptedMetric;
|
||||
const Clock::time_point geometryPreflightStart = Clock::now();
|
||||
timings.geometryPreflight = state.EstimateLargestSafeStepSize(
|
||||
nextLineSearchStepLength, options.backtracking.fractionToBoundarySafety
|
||||
);
|
||||
timings.geometryPreflightSeconds =
|
||||
std::chrono::duration<double>(Clock::now() - geometryPreflightStart).count();
|
||||
diagnostics.totalGeometryPreflightSeconds += timings.geometryPreflightSeconds;
|
||||
diagnostics.lastGeometryPreflight = timings.geometryPreflight;
|
||||
if (timings.geometryPreflight->limitedByGeometry) {
|
||||
++diagnostics.geometryLimitedIterations;
|
||||
}
|
||||
|
||||
if (timings.geometryPreflight->stepSize < options.backtracking.minimumStepLength) {
|
||||
diagnostics.finalResidualNorm = acceptedMetric.residualNorm;
|
||||
NotifyAfter(
|
||||
iteration, nonlinear::IterationDisposition::globalization_failure, false, 0.0, 0,
|
||||
diagnostics.initialResidualNorm, previousMetric, acceptedMetric, linearReport, timings, state
|
||||
if (options.backtracking.geometryPreflight) {
|
||||
const Clock::time_point geometryPreflightStart = Clock::now();
|
||||
timings.geometryPreflight = state.EstimateLargestSafeStepSize(
|
||||
nextLineSearchStepLength, options.backtracking.fractionToBoundarySafety
|
||||
);
|
||||
return Failure(
|
||||
state, std::move(diagnostics), StellarEquilibriumFailureReason::globalization_failure,
|
||||
"The geometry preflight found no orientation-preserving Newton step at or above the "
|
||||
"configured minimum step length."
|
||||
);
|
||||
}
|
||||
timings.geometryPreflightSeconds =
|
||||
std::chrono::duration<double>(Clock::now() - geometryPreflightStart).count();
|
||||
diagnostics.totalGeometryPreflightSeconds += timings.geometryPreflightSeconds;
|
||||
diagnostics.lastGeometryPreflight = timings.geometryPreflight;
|
||||
if (timings.geometryPreflight->limitedByGeometry) {
|
||||
++diagnostics.geometryLimitedIterations;
|
||||
}
|
||||
|
||||
if (timings.geometryPreflight->stepSize < options.backtracking.minimumStepLength) {
|
||||
diagnostics.finalResidualNorm = acceptedMetric.residualNorm;
|
||||
NotifyAfter(
|
||||
iteration, nonlinear::IterationDisposition::globalization_failure, false, 0.0, 0,
|
||||
diagnostics.initialResidualNorm, previousMetric, acceptedMetric, linearReport, timings, state
|
||||
);
|
||||
return Failure(
|
||||
state, std::move(diagnostics), StellarEquilibriumFailureReason::globalization_failure,
|
||||
"The geometry preflight found no orientation-preserving Newton step at or above the "
|
||||
"configured minimum step length."
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
bool accepted = false;
|
||||
double acceptedStepLength = 0.0;
|
||||
int lineSearchTrials = 0;
|
||||
@@ -1303,7 +1310,8 @@ export namespace mean_field::solver {
|
||||
StellarEquilibriumFailureReason rejectionReason =
|
||||
StellarEquilibriumFailureReason::globalization_failure;
|
||||
std::string rejectionMessage = "The backtracking line search found no acceptable Newton step.";
|
||||
double stepLength = timings.geometryPreflight->stepSize;
|
||||
double stepLength = timings.geometryPreflight ?
|
||||
timings.geometryPreflight->stepSize : nextLineSearchStepLength;
|
||||
|
||||
const Clock::time_point lineSearchStart = Clock::now();
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user