perf(jacobian-action): major updates to jacobian action application by removing redudant quadrature work. ~5x increase in speed
This commit is contained in:
264
libmeanfield/interface/operators/prepared_central_density.cppm
Normal file
264
libmeanfield/interface/operators/prepared_central_density.cppm
Normal file
@@ -0,0 +1,264 @@
|
||||
module;
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <compare>
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <utility>
|
||||
|
||||
#include <mfem.hpp>
|
||||
|
||||
export module mean_field:operators.prepared_central_density;
|
||||
|
||||
export import :field.mfem;
|
||||
export import :model.compiled_fixed_central_density;
|
||||
|
||||
export namespace mean_field::operators {
|
||||
struct CentralDensityDependencyStamp final {
|
||||
std::uint64_t identity{0};
|
||||
std::uint64_t revision{0};
|
||||
|
||||
constexpr auto operator<=>(const CentralDensityDependencyStamp &) const = default;
|
||||
};
|
||||
|
||||
struct CentralDensityDependencies final {
|
||||
CentralDensityDependencyStamp enthalpy;
|
||||
|
||||
constexpr auto operator<=>(const CentralDensityDependencies &) const = default;
|
||||
};
|
||||
|
||||
struct PreparedCentralDensityReport final {
|
||||
bool refreshedCentralEnthalpy{false};
|
||||
bool refreshedBorder{false};
|
||||
bool assembledResidual{false};
|
||||
|
||||
[[nodiscard]] bool DidAnyWork() const noexcept {
|
||||
return refreshedCentralEnthalpy || refreshedBorder || assembledResidual;
|
||||
}
|
||||
|
||||
constexpr auto operator<=>(const PreparedCentralDensityReport &) const = default;
|
||||
};
|
||||
|
||||
struct CentralDensityConstraintReport final {
|
||||
double targetDensity;
|
||||
double achievedDensity;
|
||||
double targetEnthalpy;
|
||||
double achievedEnthalpy;
|
||||
double enthalpyResidual;
|
||||
double scaledResidual;
|
||||
};
|
||||
|
||||
struct CentralDensityJacobianInput final {
|
||||
const mfem::Vector &enthalpyVariation;
|
||||
double borderVariation;
|
||||
};
|
||||
|
||||
struct CentralDensityJacobianOutput final {
|
||||
mfem::Vector &enthalpyAction;
|
||||
mfem::Vector &phaseAction;
|
||||
};
|
||||
|
||||
struct CentralDensityJacobianTransposeInput final {
|
||||
const mfem::Vector &enthalpyResidualDual;
|
||||
double phaseResidualDual;
|
||||
};
|
||||
|
||||
struct CentralDensityJacobianTransposeOutput final {
|
||||
mfem::Vector &enthalpyDual;
|
||||
mfem::Vector &borderDual;
|
||||
};
|
||||
|
||||
/*
|
||||
* Bordered central-density phase condition
|
||||
*
|
||||
* R_c(h) = h(0) - h(rho_c,target),
|
||||
* R_h <- R_h + lambda_c e_c.
|
||||
*
|
||||
* The point functional e_c selects the unique scalar H1 vertex at the
|
||||
* computational origin. Its coordinate transpose supplies the border
|
||||
* column, so this contribution is algebraically symmetric before any
|
||||
* independent scaling is applied by a solver.
|
||||
*/
|
||||
class PreparedCentralDensityConstraint final {
|
||||
public:
|
||||
PreparedCentralDensityConstraint(
|
||||
field::FieldPointDofMap centerDof,
|
||||
const MPI_Comm communicator
|
||||
)
|
||||
: m_centerDof(std::move(centerDof)),
|
||||
m_communicator(communicator) {
|
||||
}
|
||||
|
||||
PreparedCentralDensityReport Prepare(
|
||||
const models::CompiledFixedCentralDensity &constraint,
|
||||
const mfem::Vector &enthalpy,
|
||||
const double border,
|
||||
const CentralDensityDependencies &dependencies
|
||||
) {
|
||||
MFEM_VERIFY(
|
||||
enthalpy.Size() == m_centerDof.field_size(),
|
||||
"The central-density phase received an enthalpy vector with the wrong size."
|
||||
);
|
||||
MFEM_VERIFY(std::isfinite(border), "The central-density phase received a non-finite border value.");
|
||||
|
||||
const bool wasPrepared = m_isPrepared;
|
||||
PreparedCentralDensityReport report;
|
||||
|
||||
if (!wasPrepared || dependencies.enthalpy != m_preparedDependencies.enthalpy) {
|
||||
double localCentralEnthalpy = 0.0;
|
||||
for (const int reducedDof : m_centerDof.reduced_dofs()) {
|
||||
const double value = enthalpy(reducedDof);
|
||||
MFEM_VERIFY(std::isfinite(value), "The central enthalpy is non-finite.");
|
||||
localCentralEnthalpy += value;
|
||||
}
|
||||
m_centralEnthalpy = GlobalSum(localCentralEnthalpy);
|
||||
report.refreshedCentralEnthalpy = true;
|
||||
}
|
||||
|
||||
if (!wasPrepared || border != m_border) {
|
||||
m_border = border;
|
||||
report.refreshedBorder = true;
|
||||
}
|
||||
|
||||
const bool targetChanged =
|
||||
!m_constraint.has_value() || constraint.targetDensity() != m_constraint->targetDensity();
|
||||
if (targetChanged) {
|
||||
m_constraint = constraint;
|
||||
}
|
||||
|
||||
if (report.refreshedCentralEnthalpy || report.refreshedBorder || targetChanged) {
|
||||
m_cachedPhaseResidual = m_centralEnthalpy - m_constraint->targetEnthalpy().value();
|
||||
report.assembledResidual = true;
|
||||
}
|
||||
|
||||
m_preparedDependencies = dependencies;
|
||||
m_isPrepared = true;
|
||||
++m_preparationCount;
|
||||
return report;
|
||||
}
|
||||
|
||||
void AddResidual(
|
||||
mfem::Vector &enthalpyResidual,
|
||||
mfem::Vector &phaseResidual
|
||||
) const {
|
||||
VerifyPrepared();
|
||||
VerifyOutputSizes(enthalpyResidual, phaseResidual);
|
||||
for (const int reducedDof : m_centerDof.reduced_dofs()) {
|
||||
enthalpyResidual(reducedDof) += m_border;
|
||||
}
|
||||
phaseResidual(0) = m_cachedPhaseResidual;
|
||||
}
|
||||
|
||||
void ApplyJacobian(
|
||||
const CentralDensityJacobianInput &input,
|
||||
CentralDensityJacobianOutput output
|
||||
) const {
|
||||
VerifyPrepared();
|
||||
MFEM_VERIFY(
|
||||
input.enthalpyVariation.Size() == m_centerDof.field_size(),
|
||||
"The central-density Jacobian received an enthalpy direction with the wrong size."
|
||||
);
|
||||
VerifyOutputSizes(output.enthalpyAction, output.phaseAction);
|
||||
|
||||
double localPhaseAction = 0.0;
|
||||
for (const int reducedDof : m_centerDof.reduced_dofs()) {
|
||||
output.enthalpyAction(reducedDof) += input.borderVariation;
|
||||
localPhaseAction += input.enthalpyVariation(reducedDof);
|
||||
}
|
||||
output.phaseAction(0) = GlobalSum(localPhaseAction);
|
||||
++m_jacobianApplicationCount;
|
||||
}
|
||||
|
||||
void ApplyJacobianTranspose(
|
||||
const CentralDensityJacobianTransposeInput &input,
|
||||
CentralDensityJacobianTransposeOutput output
|
||||
) const {
|
||||
VerifyPrepared();
|
||||
MFEM_VERIFY(
|
||||
input.enthalpyResidualDual.Size() == m_centerDof.field_size(),
|
||||
"The central-density transpose received an enthalpy residual dual with the wrong size."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
output.enthalpyDual.Size() == m_centerDof.field_size() && output.borderDual.Size() == 1,
|
||||
"The central-density transpose received output vectors with the wrong size."
|
||||
);
|
||||
|
||||
double localBorderDual = 0.0;
|
||||
for (const int reducedDof : m_centerDof.reduced_dofs()) {
|
||||
output.enthalpyDual(reducedDof) += input.phaseResidualDual;
|
||||
localBorderDual += input.enthalpyResidualDual(reducedDof);
|
||||
}
|
||||
output.borderDual(0) += GlobalSum(localBorderDual);
|
||||
++m_transposeApplicationCount;
|
||||
}
|
||||
|
||||
[[nodiscard]] CentralDensityConstraintReport GetConstraintReport() const {
|
||||
VerifyPrepared();
|
||||
const double targetEnthalpy = m_constraint->targetEnthalpy().value();
|
||||
const double scale = std::max(std::abs(targetEnthalpy), 1.0e-300);
|
||||
return {
|
||||
.targetDensity = m_constraint->targetDensity().value(),
|
||||
.achievedDensity =
|
||||
m_constraint->densityFromEnthalpy(dimensions::SpecificEnthalpyValue{m_centralEnthalpy}).value(),
|
||||
.targetEnthalpy = targetEnthalpy,
|
||||
.achievedEnthalpy = m_centralEnthalpy,
|
||||
.enthalpyResidual = m_cachedPhaseResidual,
|
||||
.scaledResidual = m_cachedPhaseResidual / scale
|
||||
};
|
||||
}
|
||||
|
||||
[[nodiscard]] bool IsPrepared() const noexcept {
|
||||
return m_isPrepared;
|
||||
}
|
||||
|
||||
[[nodiscard]] const field::FieldPointDofMap &GetCenterDof() const noexcept {
|
||||
return m_centerDof;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::uint64_t GetPreparationCount() const noexcept {
|
||||
return m_preparationCount;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::uint64_t GetJacobianApplicationCount() const noexcept {
|
||||
return m_jacobianApplicationCount;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::uint64_t GetTransposeApplicationCount() const noexcept {
|
||||
return m_transposeApplicationCount;
|
||||
}
|
||||
|
||||
private:
|
||||
[[nodiscard]] double GlobalSum(const double localValue) const {
|
||||
double globalValue = 0.0;
|
||||
MPI_Allreduce(&localValue, &globalValue, 1, MPI_DOUBLE, MPI_SUM, m_communicator);
|
||||
return globalValue;
|
||||
}
|
||||
|
||||
void VerifyOutputSizes(
|
||||
const mfem::Vector &enthalpyOutput,
|
||||
const mfem::Vector &phaseOutput
|
||||
) const {
|
||||
MFEM_VERIFY(
|
||||
enthalpyOutput.Size() == m_centerDof.field_size() && phaseOutput.Size() == 1,
|
||||
"The central-density phase received output vectors with the wrong size."
|
||||
);
|
||||
}
|
||||
|
||||
void VerifyPrepared() const {
|
||||
MFEM_VERIFY(m_isPrepared, "The central-density phase must be prepared before application.");
|
||||
}
|
||||
|
||||
field::FieldPointDofMap m_centerDof;
|
||||
MPI_Comm m_communicator;
|
||||
std::optional<models::CompiledFixedCentralDensity> m_constraint;
|
||||
CentralDensityDependencies m_preparedDependencies;
|
||||
double m_centralEnthalpy{0.0};
|
||||
double m_border{0.0};
|
||||
double m_cachedPhaseResidual{0.0};
|
||||
std::uint64_t m_preparationCount{0};
|
||||
mutable std::uint64_t m_jacobianApplicationCount{0};
|
||||
mutable std::uint64_t m_transposeApplicationCount{0};
|
||||
bool m_isPrepared{false};
|
||||
};
|
||||
} // namespace mean_field::operators
|
||||
Reference in New Issue
Block a user