265 lines
9.7 KiB
C++
265 lines
9.7 KiB
C++
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
|