perf(jacobian-action): major updates to jacobian action application by removing redudant quadrature work. ~5x increase in speed
This commit is contained in:
@@ -84,15 +84,24 @@ export namespace mean_field::operators {
|
||||
mfem::Vector &actionTrue
|
||||
) const;
|
||||
|
||||
void ApplyDisplacementActionFull(
|
||||
const mfem::Vector &displacementVariationTrue,
|
||||
mfem::Vector &actionTrue
|
||||
) const;
|
||||
|
||||
struct ElementPAData {
|
||||
int elementId{-1};
|
||||
mfem::Array<int> densityDofs;
|
||||
mfem::Array<int> enthalpyDofs;
|
||||
mfem::Array<int> displacementDofs;
|
||||
|
||||
mfem::DofTransformation *densityDofTransformation{nullptr};
|
||||
mfem::DofTransformation *enthalpyDofTransformation{nullptr};
|
||||
mfem::DofTransformation *displacementDofTransformation{nullptr};
|
||||
|
||||
mfem::DenseMatrix densityBasis;
|
||||
mfem::DenseMatrix enthalpyBasis;
|
||||
mfem::DenseMatrix inverseElementJacobians;
|
||||
|
||||
mfem::Vector weightedResidual;
|
||||
mfem::Vector quadratureWeights;
|
||||
@@ -122,6 +131,14 @@ export namespace mean_field::operators {
|
||||
mutable mfem::Vector m_fullDisplacementAction;
|
||||
mutable mfem::Vector m_fullResidual;
|
||||
|
||||
mutable mfem::Vector m_displacementVariationLocal;
|
||||
mutable mfem::Vector m_localDisplacementAction;
|
||||
mutable mfem::Vector m_elementDisplacementVariation;
|
||||
mutable mfem::Vector m_quadratureDisplacementAction;
|
||||
mutable mfem::Vector m_elementDisplacementAction;
|
||||
mutable mfem::DenseMatrix m_referenceDShape;
|
||||
mutable mfem::DenseMatrix m_referenceDisplacementJacobian;
|
||||
|
||||
std::uint64_t m_preparationCount{0};
|
||||
bool m_isPrepared{false};
|
||||
};
|
||||
|
||||
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
|
||||
@@ -0,0 +1,117 @@
|
||||
module;
|
||||
|
||||
#include <concepts>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include <mfem.hpp>
|
||||
|
||||
export module mean_field:operators.prepared_central_density_stellar_equilibrium;
|
||||
|
||||
export import :model.compiled_fixed_central_density;
|
||||
export import :operators.prepared_central_density;
|
||||
export import :operators.prepared_stellar_equilibrium;
|
||||
|
||||
export namespace mean_field::operators {
|
||||
using CentralDensityStellarEquilibriumSpecificationModel = model::StellarModel<
|
||||
models::
|
||||
SpecificationSet<eos::Polytrope, models::FixedTotalMass, surface::Isobaric, models::FixedCentralDensity>>;
|
||||
|
||||
using CentralDensityStellarEquilibriumForm = utils::blocks::central_density_bordered_stellar_equilibrium_form;
|
||||
using CentralDensityStellarEquilibriumJacobianForm =
|
||||
utils::blocks::central_density_bordered_stellar_equilibrium_jacobian_form;
|
||||
using CentralDensityStellarEquilibriumLayout = utils::blocks::form_layout<CentralDensityStellarEquilibriumForm>;
|
||||
using CentralDensityStellarEquilibriumSystemManifest = EquilibriumSystemManifest<
|
||||
CentralDensityStellarEquilibriumSpecificationModel,
|
||||
CentralDensityStellarEquilibriumForm,
|
||||
CentralDensityStellarEquilibriumJacobianForm>;
|
||||
|
||||
using CentralDensityStellarEquilibriumRootManifest = CentralDensityStellarEquilibriumSystemManifest;
|
||||
|
||||
struct PreparedCentralDensityStellarEquilibriumReport final {
|
||||
PreparedStellarEquilibriumReport physical;
|
||||
PreparedCentralDensityReport phase;
|
||||
bool assembledResidual{false};
|
||||
|
||||
[[nodiscard]] bool DidAnyWork() const noexcept {
|
||||
return physical.DidAnyWork() || phase.DidAnyWork() || assembledResidual;
|
||||
}
|
||||
};
|
||||
|
||||
class PreparedCentralDensityStellarEquilibriumOperator final : public mfem::Operator {
|
||||
public:
|
||||
PreparedCentralDensityStellarEquilibriumOperator(
|
||||
fem::FEM &f,
|
||||
const mapping::DomainMapper &domainMapper,
|
||||
const eos::Polytrope &equationOfState,
|
||||
models::CompiledFixedMass fixedMassConstraint,
|
||||
PressureSurfaceConstraintView surfaceConstraint,
|
||||
deformation::PreparedDomainDeformationRuntime domainDeformation,
|
||||
models::CompiledFixedCentralDensity centralDensity
|
||||
)
|
||||
: PreparedCentralDensityStellarEquilibriumOperator(
|
||||
f,
|
||||
std::make_unique<PreparedStellarEquilibriumOperator>(
|
||||
f,
|
||||
domainMapper,
|
||||
equationOfState,
|
||||
std::move(fixedMassConstraint),
|
||||
surfaceConstraint,
|
||||
std::move(domainDeformation)
|
||||
),
|
||||
std::move(centralDensity),
|
||||
MakeCenterDofMap(f)
|
||||
) {
|
||||
}
|
||||
|
||||
PreparedCentralDensityStellarEquilibriumOperator(const PreparedCentralDensityStellarEquilibriumOperator &) =
|
||||
delete;
|
||||
PreparedCentralDensityStellarEquilibriumOperator &
|
||||
operator=(const PreparedCentralDensityStellarEquilibriumOperator &) = delete;
|
||||
PreparedCentralDensityStellarEquilibriumOperator(PreparedCentralDensityStellarEquilibriumOperator &&) = delete;
|
||||
PreparedCentralDensityStellarEquilibriumOperator &
|
||||
operator=(PreparedCentralDensityStellarEquilibriumOperator &&) = delete;
|
||||
|
||||
PreparedCentralDensityStellarEquilibriumReport Prepare(
|
||||
const mfem::Vector &state,
|
||||
const StellarEquilibriumDependencies &dependencies,
|
||||
const physics::RigidRotation &rotation
|
||||
);
|
||||
|
||||
void BuildResidual(mfem::Vector &residual) const;
|
||||
|
||||
void Mult(
|
||||
const mfem::Vector &direction,
|
||||
mfem::Vector &action
|
||||
) const override;
|
||||
|
||||
[[nodiscard]] bool IsPrepared() const noexcept;
|
||||
[[nodiscard]] const CentralDensityStellarEquilibriumLayout &GetLayout() const noexcept;
|
||||
[[nodiscard]] const CentralDensityStellarEquilibriumRootManifest &GetRootManifest() const noexcept;
|
||||
[[nodiscard]] const PreparedStellarEquilibriumOperator &GetPhysicalOperator() const noexcept;
|
||||
[[nodiscard]] const PreparedCentralDensityConstraint &GetCentralDensityConstraint() const noexcept;
|
||||
[[nodiscard]] RootConstraintReport GetFixedMassReport() const;
|
||||
[[nodiscard]] CentralDensityConstraintReport GetCentralDensityReport() const;
|
||||
|
||||
private:
|
||||
static field::FieldPointDofMap MakeCenterDofMap(const fem::FEM &f);
|
||||
|
||||
PreparedCentralDensityStellarEquilibriumOperator(
|
||||
fem::FEM &f,
|
||||
std::unique_ptr<PreparedStellarEquilibriumOperator> physicalOperator,
|
||||
models::CompiledFixedCentralDensity centralDensity,
|
||||
field::FieldPointDofMap centerDof
|
||||
);
|
||||
|
||||
void AssembleResidual();
|
||||
void VerifyPrepared() const;
|
||||
|
||||
std::unique_ptr<PreparedStellarEquilibriumOperator> m_physicalOperator;
|
||||
models::CompiledFixedCentralDensity m_centralDensity;
|
||||
PreparedCentralDensityConstraint m_phaseConstraint;
|
||||
CentralDensityStellarEquilibriumRootManifest m_rootManifest;
|
||||
mfem::Vector m_cachedResidual;
|
||||
bool m_isPrepared{false};
|
||||
};
|
||||
} // namespace mean_field::operators
|
||||
37
libmeanfield/interface/operators/prepared_constraint.cppm
Normal file
37
libmeanfield/interface/operators/prepared_constraint.cppm
Normal file
@@ -0,0 +1,37 @@
|
||||
module;
|
||||
|
||||
#include <concepts>
|
||||
#include <type_traits>
|
||||
|
||||
#include <mfem.hpp>
|
||||
|
||||
export module mean_field:operators.prepared_constraint;
|
||||
|
||||
export import :model.compiled_fixed_mass;
|
||||
|
||||
export namespace mean_field::operators {
|
||||
template <typename Candidate>
|
||||
concept PreparedConstraint = requires(
|
||||
std::remove_cvref_t<Candidate> &prepared,
|
||||
const std::remove_cvref_t<Candidate> &constPrepared,
|
||||
const typename std::remove_cvref_t<Candidate>::CompiledConstraintType &constraint,
|
||||
const typename std::remove_cvref_t<Candidate>::Dependencies &dependencies,
|
||||
const typename std::remove_cvref_t<Candidate>::JacobianInput &jacobianInput,
|
||||
typename std::remove_cvref_t<Candidate>::JacobianTransposeOutput transposeOutput,
|
||||
const mfem::Vector &residualDual,
|
||||
mfem::Vector &result
|
||||
) {
|
||||
typename std::remove_cvref_t<Candidate>::SpecificationType;
|
||||
typename std::remove_cvref_t<Candidate>::CompiledConstraintType;
|
||||
typename std::remove_cvref_t<Candidate>::Dependencies;
|
||||
typename std::remove_cvref_t<Candidate>::Report;
|
||||
typename std::remove_cvref_t<Candidate>::JacobianInput;
|
||||
typename std::remove_cvref_t<Candidate>::JacobianTransposeOutput;
|
||||
requires models::CompiledConstraint<typename std::remove_cvref_t<Candidate>::CompiledConstraintType>;
|
||||
{ prepared.Prepare(constraint, dependencies) } -> std::same_as<typename std::remove_cvref_t<Candidate>::Report>;
|
||||
{ constPrepared.BuildResidual(result) } -> std::same_as<void>;
|
||||
{ constPrepared.ApplyJacobian(jacobianInput, result) } -> std::same_as<void>;
|
||||
{ constPrepared.ApplyJacobianTranspose(residualDual, transposeOutput) } -> std::same_as<void>;
|
||||
{ constPrepared.IsPrepared() } noexcept -> std::same_as<bool>;
|
||||
};
|
||||
} // namespace mean_field::operators
|
||||
@@ -2,6 +2,7 @@ module;
|
||||
|
||||
#include <compare>
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
#include <mfem.hpp>
|
||||
|
||||
@@ -105,6 +106,29 @@ export namespace mean_field::operators {
|
||||
|
||||
private:
|
||||
void VerifyPrepared() const;
|
||||
void PrepareElementData();
|
||||
void ApplyPreparedCompleteJacobianActionTrue(
|
||||
const mfem::Vector &densityVariationTrue,
|
||||
const mfem::Vector &displacementVariationTrue,
|
||||
const mfem::Vector &gravityGradientVariationTrue,
|
||||
mfem::Vector &actionTrue
|
||||
) const;
|
||||
|
||||
struct ElementPAData {
|
||||
int elementId{-1};
|
||||
mfem::Array<int> densityDofs;
|
||||
mfem::Array<int> gravityGradientDofs;
|
||||
mfem::Array<int> displacementDofs;
|
||||
mfem::DofTransformation *densityDofTransformation{nullptr};
|
||||
mfem::DofTransformation *gravityGradientDofTransformation{nullptr};
|
||||
mfem::DofTransformation *displacementDofTransformation{nullptr};
|
||||
const mfem::IntegrationRule *integrationRule{nullptr};
|
||||
mfem::DenseMatrix mappingJacobians;
|
||||
mfem::DenseMatrix inverseMeshJacobians;
|
||||
mfem::DenseMatrix baseGravityReferenceValues;
|
||||
mfem::Vector baseDensityValues;
|
||||
mfem::Vector referenceWeights;
|
||||
};
|
||||
|
||||
const fem::FEM &m_fem;
|
||||
const mapping::DomainMapper &m_domainMapper;
|
||||
@@ -112,11 +136,34 @@ export namespace mean_field::operators {
|
||||
|
||||
context::gravity_field::GravityFieldRevisions m_preparedRevisions;
|
||||
mfem::Vector m_cachedResidual;
|
||||
std::vector<ElementPAData> m_elements;
|
||||
|
||||
mutable mfem::Vector m_densityVariationTrue;
|
||||
mutable mfem::Vector m_gravityGradientVariationTrue;
|
||||
mutable mfem::Vector m_displacementVariationTrue;
|
||||
mutable mfem::Vector m_actionTrue;
|
||||
mutable mfem::Vector m_densityVariationLocal;
|
||||
mutable mfem::Vector m_gravityGradientVariationLocal;
|
||||
mutable mfem::Vector m_displacementVariationLocal;
|
||||
mutable mfem::Vector m_localAction;
|
||||
mutable mfem::Vector m_elementDensityVariation;
|
||||
mutable mfem::Vector m_elementGravityGradientVariation;
|
||||
mutable mfem::Vector m_elementDisplacementVariation;
|
||||
mutable mfem::Vector m_elementAction;
|
||||
mutable mfem::Vector m_densityShape;
|
||||
mutable mfem::Vector m_displacementShape;
|
||||
mutable mfem::Vector m_baseGravityReferenceValue;
|
||||
mutable mfem::Vector m_gravityVariationReferenceValue;
|
||||
mutable mfem::Vector m_mappedBaseGravity;
|
||||
mutable mfem::Vector m_mappedGravityVariation;
|
||||
mutable mfem::Vector m_mappedGeometryVariation;
|
||||
mutable mfem::Vector m_forceValue;
|
||||
mutable mfem::DenseMatrix m_gravityGradientShape;
|
||||
mutable mfem::DenseMatrix m_referenceDisplacementDShape;
|
||||
mutable mfem::DenseMatrix m_referenceDisplacementJacobian;
|
||||
mutable mfem::DenseMatrix m_displacementJacobianVariation;
|
||||
mutable mfem::DenseMatrix m_mappingJacobian;
|
||||
mutable mfem::DenseMatrix m_inverseMeshJacobian;
|
||||
|
||||
std::uint64_t m_residualPreparationCount{0};
|
||||
mutable std::uint64_t m_residualApplicationCount{0};
|
||||
|
||||
@@ -22,6 +22,11 @@ export namespace mean_field::operators {
|
||||
const mfem::Vector &density,
|
||||
mfem::Vector &action
|
||||
) const override;
|
||||
void MultDisplacementVariationTrue(
|
||||
const mfem::Vector &densityTrue,
|
||||
const mfem::Vector &displacementVariationTrue,
|
||||
mfem::Vector &actionVariationTrue
|
||||
) const;
|
||||
|
||||
[[nodiscard]] bool IsPrepared() const noexcept;
|
||||
[[nodiscard]] std::uint64_t GetPreparationCount() const noexcept;
|
||||
@@ -41,13 +46,18 @@ export namespace mean_field::operators {
|
||||
|
||||
mfem::Array<int> density_dofs;
|
||||
mfem::Array<int> potential_dofs;
|
||||
mfem::Array<int> displacement_dofs;
|
||||
|
||||
mfem::DofTransformation *density_dof_transformation{nullptr};
|
||||
mfem::DofTransformation *potential_dof_transformation{nullptr};
|
||||
mfem::DofTransformation *displacement_dof_transformation{nullptr};
|
||||
|
||||
const mfem::IntegrationRule *integration_rule{nullptr};
|
||||
|
||||
// Rows are quadrature points; columns are element DOFs.
|
||||
mfem::DenseMatrix density_basis;
|
||||
mfem::DenseMatrix potential_basis;
|
||||
mfem::DenseMatrix inverse_element_jacobians;
|
||||
|
||||
// Contains quadrature weight, mesh Jacobian, mapped Jacobian,
|
||||
// and 4*pi*G.
|
||||
@@ -67,6 +77,15 @@ export namespace mean_field::operators {
|
||||
mutable mfem::Vector m_density_true;
|
||||
mutable mfem::Vector m_potential_true;
|
||||
mutable mfem::Vector m_action_true;
|
||||
mutable mfem::Vector m_density_local;
|
||||
mutable mfem::Vector m_displacement_variation_local;
|
||||
mutable mfem::Vector m_local_variation_action;
|
||||
mutable mfem::Vector m_element_density;
|
||||
mutable mfem::Vector m_element_displacement_variation;
|
||||
mutable mfem::Vector m_quadrature_variation_action;
|
||||
mutable mfem::Vector m_element_variation_action;
|
||||
mutable mfem::DenseMatrix m_reference_displacement_dshape;
|
||||
mutable mfem::DenseMatrix m_reference_displacement_jacobian;
|
||||
mfem::Vector m_displacement_true;
|
||||
|
||||
std::uint64_t m_preparation_count{0};
|
||||
|
||||
@@ -2,6 +2,7 @@ module;
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <mfem.hpp>
|
||||
#include <vector>
|
||||
|
||||
export module mean_field:operators.prepared_hdiv_mass;
|
||||
export import :fem;
|
||||
@@ -21,6 +22,11 @@ export namespace mean_field::operators {
|
||||
const mfem::Vector &gravity_gradient,
|
||||
mfem::Vector &action
|
||||
) const override;
|
||||
void MultDisplacementVariationTrue(
|
||||
const mfem::Vector &gravityGradientTrue,
|
||||
const mfem::Vector &displacementVariationTrue,
|
||||
mfem::Vector &actionVariationTrue
|
||||
) const;
|
||||
void AssembleDiagonal(mfem::Vector &diagonal) const override;
|
||||
void AssembleTrueDiagonal(mfem::Vector &diagonal) const;
|
||||
|
||||
@@ -31,6 +37,21 @@ export namespace mean_field::operators {
|
||||
[[nodiscard]] const field::FieldDofMap &GetDisplacementMap() const noexcept;
|
||||
|
||||
private:
|
||||
struct ElementVariationData {
|
||||
int elementId{-1};
|
||||
mfem::Array<int> gravityGradientDofs;
|
||||
mfem::Array<int> displacementDofs;
|
||||
mfem::Array<int> compactificationDofs;
|
||||
mfem::DofTransformation *gravityGradientDofTransformation{nullptr};
|
||||
mfem::DofTransformation *displacementDofTransformation{nullptr};
|
||||
mfem::Vector baseDisplacement;
|
||||
mfem::Vector compactification;
|
||||
const mfem::IntegrationRule *integrationRule{nullptr};
|
||||
mfem::DenseMatrix frozenMappingData;
|
||||
};
|
||||
|
||||
void PrepareVariationData();
|
||||
|
||||
const fem::FEM &m_fem;
|
||||
const mapping::DomainMapper &m_domain_mapper;
|
||||
|
||||
@@ -48,6 +69,21 @@ export namespace mean_field::operators {
|
||||
mutable mfem::Vector m_action_true;
|
||||
mutable mfem::Vector m_domain_action_true;
|
||||
mfem::Vector m_displacement_true;
|
||||
std::vector<ElementVariationData> m_variationElements;
|
||||
|
||||
mutable mapping::DomainMapper::Workspace m_variationWorkspace;
|
||||
mutable mapping::VolumeMappingContext m_baseMappingContext;
|
||||
mutable mapping::VolumeMappingVariation m_mappingVariation;
|
||||
mutable mfem::Vector m_gravityGradientLocal;
|
||||
mutable mfem::Vector m_displacementVariationLocal;
|
||||
mutable mfem::Vector m_localVariationAction;
|
||||
mutable mfem::Vector m_elementGravityGradient;
|
||||
mutable mfem::Vector m_elementDisplacementVariation;
|
||||
mutable mfem::Vector m_elementVariationAction;
|
||||
mutable mfem::Vector m_gravityGradientValue;
|
||||
mutable mfem::Vector m_massTensorVariationAction;
|
||||
mutable mfem::DenseMatrix m_gravityGradientShape;
|
||||
mutable mfem::DenseMatrix m_massTensorVariation;
|
||||
std::uint64_t m_preparation_count{0};
|
||||
bool m_is_prepared{false};
|
||||
};
|
||||
|
||||
@@ -9,7 +9,9 @@ export module mean_field:operators.prepared_mass_normalization;
|
||||
|
||||
export import :fem;
|
||||
export import :mapping.domain_mapper;
|
||||
export import :model.compiled_fixed_mass;
|
||||
export import :operators.context.gravity_field;
|
||||
export import :operators.prepared_constraint;
|
||||
export import :utils.blocks;
|
||||
|
||||
export namespace mean_field::operators {
|
||||
@@ -33,6 +35,16 @@ export namespace mean_field::operators {
|
||||
double targetMass{0.0};
|
||||
};
|
||||
|
||||
struct FixedMassJacobianInput final {
|
||||
const mfem::Vector &densityVariation;
|
||||
const mfem::Vector &displacementVariation;
|
||||
};
|
||||
|
||||
struct FixedMassJacobianTransposeOutput final {
|
||||
mfem::Vector &densityDual;
|
||||
mfem::Vector &displacementDual;
|
||||
};
|
||||
|
||||
struct PreparedMassNormalizationReport final {
|
||||
bool rebuiltStaticPlan{false};
|
||||
bool refreshedGeometry{false};
|
||||
@@ -43,12 +55,15 @@ export namespace mean_field::operators {
|
||||
[[nodiscard]] bool DidAnyWork() const noexcept {
|
||||
return rebuiltStaticPlan || refreshedGeometry || refreshedDensity || updatedTargetMass || assembledResidual;
|
||||
}
|
||||
|
||||
constexpr auto operator<=>(const PreparedMassNormalizationReport &) const = default;
|
||||
};
|
||||
|
||||
struct PreparedMassNormalizationActionStatistics final {
|
||||
std::uint64_t densityApplications{0};
|
||||
std::uint64_t displacementApplications{0};
|
||||
std::uint64_t completeApplications{0};
|
||||
std::uint64_t transposeApplications{0};
|
||||
|
||||
constexpr auto operator<=>(const PreparedMassNormalizationActionStatistics &) const = default;
|
||||
};
|
||||
@@ -66,6 +81,13 @@ export namespace mean_field::operators {
|
||||
*/
|
||||
class PreparedMassNormalizationOperator final {
|
||||
public:
|
||||
using SpecificationType = models::FixedTotalMass;
|
||||
using CompiledConstraintType = models::CompiledFixedMass;
|
||||
using Dependencies = MassNormalizationDependencies;
|
||||
using Report = PreparedMassNormalizationReport;
|
||||
using JacobianInput = FixedMassJacobianInput;
|
||||
using JacobianTransposeOutput = FixedMassJacobianTransposeOutput;
|
||||
|
||||
PreparedMassNormalizationOperator(
|
||||
const fem::FEM &f,
|
||||
const mapping::DomainMapper &domainMapper,
|
||||
@@ -82,6 +104,11 @@ export namespace mean_field::operators {
|
||||
const MassNormalizationDependencies &dependencies
|
||||
);
|
||||
|
||||
PreparedMassNormalizationReport Prepare(
|
||||
const models::CompiledFixedMass &constraint,
|
||||
const MassNormalizationDependencies &dependencies
|
||||
);
|
||||
|
||||
void BuildResidual(mfem::Vector &residual) const;
|
||||
|
||||
void ApplyDensityJacobianAction(
|
||||
@@ -100,6 +127,22 @@ export namespace mean_field::operators {
|
||||
mfem::Vector &action
|
||||
) const;
|
||||
|
||||
void ApplyJacobian(
|
||||
const FixedMassJacobianInput &input,
|
||||
mfem::Vector &action
|
||||
) const;
|
||||
|
||||
void ApplyCompleteJacobianTransposeAction(
|
||||
double residualDual,
|
||||
mfem::Vector &densityDual,
|
||||
mfem::Vector &displacementDual
|
||||
) const;
|
||||
|
||||
void ApplyJacobianTranspose(
|
||||
const mfem::Vector &residualDual,
|
||||
FixedMassJacobianTransposeOutput output
|
||||
) const;
|
||||
|
||||
[[nodiscard]] bool IsPrepared() const noexcept;
|
||||
[[nodiscard]] double GetCurrentMass() const;
|
||||
[[nodiscard]] double GetTargetMass() const;
|
||||
@@ -145,6 +188,16 @@ export namespace mean_field::operators {
|
||||
|
||||
[[nodiscard]] double EvaluateDisplacementActionLocal(const mfem::Vector &displacementVariation) const;
|
||||
|
||||
void AssembleDensityTransposeAction(
|
||||
double residualDual,
|
||||
mfem::Vector &densityDual
|
||||
) const;
|
||||
|
||||
void AssembleDisplacementTransposeAction(
|
||||
double residualDual,
|
||||
mfem::Vector &displacementDual
|
||||
) const;
|
||||
|
||||
[[nodiscard]] double GlobalSum(double localValue) const;
|
||||
|
||||
const fem::FEM &m_fem;
|
||||
@@ -167,6 +220,10 @@ export namespace mean_field::operators {
|
||||
bool m_isPrepared{false};
|
||||
};
|
||||
|
||||
using PreparedFixedMass = PreparedMassNormalizationOperator;
|
||||
|
||||
static_assert(PreparedConstraint<PreparedFixedMass>);
|
||||
|
||||
using MassNormalizationLayout = utils::blocks::form_layout<utils::blocks::barotropic_equilibrium_form>;
|
||||
|
||||
class PreparedMassNormalizationJacobianOperator final : public mfem::Operator {
|
||||
@@ -181,6 +238,11 @@ export namespace mean_field::operators {
|
||||
mfem::Vector &action
|
||||
) const override;
|
||||
|
||||
void MultTranspose(
|
||||
const mfem::Vector &residualDual,
|
||||
mfem::Vector &stateDual
|
||||
) const override;
|
||||
|
||||
[[nodiscard]] const MassNormalizationLayout &GetLayout() const noexcept;
|
||||
|
||||
private:
|
||||
|
||||
@@ -3,6 +3,7 @@ module;
|
||||
#include <compare>
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
#include <mfem.hpp>
|
||||
|
||||
@@ -103,6 +104,25 @@ export namespace mean_field::operators {
|
||||
|
||||
private:
|
||||
void VerifyPrepared() const;
|
||||
void PrepareElementData();
|
||||
void ApplyPreparedCompleteJacobianActionTrue(
|
||||
const mfem::Vector &densityVariationTrue,
|
||||
const mfem::Vector &displacementVariationTrue,
|
||||
mfem::Vector &actionTrue
|
||||
) const;
|
||||
|
||||
struct ElementPAData {
|
||||
int elementId{-1};
|
||||
mfem::Array<int> densityDofs;
|
||||
mfem::Array<int> displacementDofs;
|
||||
mfem::DofTransformation *densityDofTransformation{nullptr};
|
||||
mfem::DofTransformation *displacementDofTransformation{nullptr};
|
||||
const mfem::IntegrationRule *integrationRule{nullptr};
|
||||
mfem::DenseMatrix inverseElementJacobians;
|
||||
mfem::DenseMatrix centrifugalAccelerations;
|
||||
mfem::Vector baseDensityValues;
|
||||
mfem::Vector quadratureWeights;
|
||||
};
|
||||
|
||||
const fem::FEM &m_fem;
|
||||
const mapping::DomainMapper &m_domainMapper;
|
||||
@@ -111,9 +131,24 @@ export namespace mean_field::operators {
|
||||
|
||||
std::optional<physics::RigidRotation> m_rotation;
|
||||
mfem::Vector m_cachedResidual;
|
||||
std::vector<ElementPAData> m_elements;
|
||||
mutable mfem::Vector m_densityVariationTrue;
|
||||
mutable mfem::Vector m_displacementVariationTrue;
|
||||
mutable mfem::Vector m_actionTrue;
|
||||
mutable mfem::Vector m_densityVariationLocal;
|
||||
mutable mfem::Vector m_displacementVariationLocal;
|
||||
mutable mfem::Vector m_localAction;
|
||||
mutable mfem::Vector m_elementDensityVariation;
|
||||
mutable mfem::Vector m_elementDisplacementVariation;
|
||||
mutable mfem::Vector m_elementAction;
|
||||
mutable mfem::Vector m_densityShape;
|
||||
mutable mfem::Vector m_displacementShape;
|
||||
mutable mfem::Vector m_physicalPositionVariation;
|
||||
mutable mfem::Vector m_centrifugalAcceleration;
|
||||
mutable mfem::Vector m_centrifugalAccelerationVariation;
|
||||
mutable mfem::Vector m_weightedForce;
|
||||
mutable mfem::DenseMatrix m_referenceDisplacementDShape;
|
||||
mutable mfem::DenseMatrix m_referenceDisplacementJacobian;
|
||||
|
||||
context::rotational_displacement_force::RotationalDisplacementForceDependencies m_preparedDependencies;
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ export import :fem;
|
||||
export import :field.mfem;
|
||||
export import :mapping.domain_mapper;
|
||||
export import :model.stellar;
|
||||
export import :model.typed_stellar;
|
||||
export import :operators.context.gravity_field;
|
||||
export import :operators.gravity_field;
|
||||
export import :operators.gravity_field_jacobian;
|
||||
@@ -23,6 +24,7 @@ export import :operators.prepared_displacement_residual;
|
||||
export import :operators.prepared_hydrostatic_equilibrium;
|
||||
export import :operators.prepared_mass_normalization;
|
||||
export import :operators.prepared_surface_constraint;
|
||||
export import :operators.root_manifest;
|
||||
export import :physics.rigid_rotation;
|
||||
export import :utils.blocks;
|
||||
|
||||
@@ -82,6 +84,16 @@ export namespace mean_field::operators {
|
||||
using StellarEquilibriumLayout =
|
||||
utils::blocks::form_layout<utils::blocks::surface_deformed_stellar_equilibrium_form>;
|
||||
|
||||
using StellarEquilibriumSpecificationModel =
|
||||
model::StellarModel<models::SpecificationSet<eos::Polytrope, models::FixedTotalMass, surface::Isobaric>>;
|
||||
|
||||
using StellarEquilibriumSystemManifest = EquilibriumSystemManifest<
|
||||
StellarEquilibriumSpecificationModel,
|
||||
utils::blocks::surface_deformed_stellar_equilibrium_form,
|
||||
utils::blocks::surface_deformed_stellar_equilibrium_jacobian_form>;
|
||||
|
||||
using StellarEquilibriumRootManifest = StellarEquilibriumSystemManifest;
|
||||
|
||||
class PreparedStellarEquilibriumOperator final : public mfem::Operator {
|
||||
public:
|
||||
template <models::StellarModelType Model>
|
||||
@@ -101,12 +113,27 @@ export namespace mean_field::operators {
|
||||
f,
|
||||
domainMapper,
|
||||
stellarModel.equationOfState(),
|
||||
stellarModel.targetMass(),
|
||||
models::compileConstraint(models::FixedTotalMass{dimensions::MassValue{stellarModel.targetMass()}}),
|
||||
PressureSurfaceConstraintView{stellarModel.compiledSurfaceConstraint()},
|
||||
deformation::PreparedDomainDeformationRuntime{stellarModel.compileDomainDeformation(f)}
|
||||
) {
|
||||
}
|
||||
|
||||
/*
|
||||
* Authoritative construction path for a compiled equilibrium system.
|
||||
* The caller owns the EOS and compiled surface constraint for this
|
||||
* operator's lifetime; the remaining compiled contributions are
|
||||
* transferred into the operator.
|
||||
*/
|
||||
PreparedStellarEquilibriumOperator(
|
||||
fem::FEM &f,
|
||||
const mapping::DomainMapper &domainMapper,
|
||||
const eos::Polytrope &equationOfState,
|
||||
models::CompiledFixedMass fixedMassConstraint,
|
||||
PressureSurfaceConstraintView surfaceConstraint,
|
||||
deformation::PreparedDomainDeformationRuntime domainDeformation
|
||||
);
|
||||
|
||||
PreparedStellarEquilibriumOperator(const PreparedStellarEquilibriumOperator &) = delete;
|
||||
PreparedStellarEquilibriumOperator &operator=(const PreparedStellarEquilibriumOperator &) = delete;
|
||||
PreparedStellarEquilibriumOperator(PreparedStellarEquilibriumOperator &&) = delete;
|
||||
@@ -128,6 +155,12 @@ export namespace mean_field::operators {
|
||||
[[nodiscard]] bool IsPrepared() const noexcept;
|
||||
[[nodiscard]] double GetTargetMass() const noexcept;
|
||||
[[nodiscard]] const StellarEquilibriumLayout &GetLayout() const noexcept;
|
||||
[[nodiscard]] const StellarEquilibriumRootManifest &GetRootManifest() const noexcept;
|
||||
[[nodiscard]] RootStateView<utils::blocks::surface_deformed_stellar_equilibrium_form>
|
||||
GetRootStateView(const mfem::Vector &state) const;
|
||||
[[nodiscard]] ResidualView<utils::blocks::surface_deformed_stellar_equilibrium_form>
|
||||
GetResidualView(mfem::Vector &residual) const;
|
||||
[[nodiscard]] RootConstraintReport GetFixedMassReport() const;
|
||||
[[nodiscard]] const StellarEquilibriumDependencies &GetDependencies() const;
|
||||
[[nodiscard]] const PreparedStellarEquilibriumStatistics &GetStatistics() const noexcept;
|
||||
|
||||
@@ -160,16 +193,7 @@ export namespace mean_field::operators {
|
||||
fem::FEM &f,
|
||||
const mapping::DomainMapper &domainMapper,
|
||||
const eos::Polytrope &equationOfState,
|
||||
double targetMass,
|
||||
PressureSurfaceConstraintView surfaceConstraint,
|
||||
deformation::PreparedDomainDeformationRuntime domainDeformation
|
||||
);
|
||||
|
||||
PreparedStellarEquilibriumOperator(
|
||||
fem::FEM &f,
|
||||
const mapping::DomainMapper &domainMapper,
|
||||
const eos::Polytrope &equationOfState,
|
||||
double targetMass,
|
||||
models::CompiledFixedMass fixedMassConstraint,
|
||||
PressureSurfaceConstraintView surfaceConstraint,
|
||||
ConstructionData constructionData
|
||||
);
|
||||
@@ -177,7 +201,7 @@ export namespace mean_field::operators {
|
||||
void AssembleResidual();
|
||||
void VerifyPrepared() const;
|
||||
|
||||
StellarEquilibriumLayout m_layout;
|
||||
StellarEquilibriumRootManifest m_rootManifest;
|
||||
mfem::Array<int> m_gravityStateOffsets;
|
||||
|
||||
context::gravity_field::GravityFieldLinearizationContext m_gravityContext;
|
||||
@@ -198,7 +222,7 @@ export namespace mean_field::operators {
|
||||
mfem::Vector m_generatedVolumeDisplacement;
|
||||
mfem::Vector m_fullMechanicalResidual;
|
||||
mfem::Vector m_cachedResidual;
|
||||
double m_targetMass{0.0};
|
||||
models::CompiledFixedMass m_fixedMassConstraint;
|
||||
|
||||
mutable PreparedStellarEquilibriumStatistics m_statistics;
|
||||
bool m_isPrepared{false};
|
||||
|
||||
@@ -14,10 +14,10 @@ export import :field.mfem;
|
||||
export import :surface.compiled;
|
||||
|
||||
namespace mean_field::operators::detail {
|
||||
template <eos::ThermodynamicQuantityType Quantity> struct SingleQuantitySurfaceState final {
|
||||
eos::QuantityValue<Quantity> quantityValue;
|
||||
template <dimensions::ThermodynamicQuantityType Quantity> struct SingleQuantitySurfaceState final {
|
||||
dimensions::QuantityValue<Quantity> quantityValue;
|
||||
|
||||
[[nodiscard]] eos::QuantityValue<Quantity> value(Quantity) const noexcept {
|
||||
[[nodiscard]] dimensions::QuantityValue<Quantity> value(Quantity) const noexcept {
|
||||
return quantityValue;
|
||||
}
|
||||
};
|
||||
@@ -37,7 +37,7 @@ export namespace mean_field::operators {
|
||||
typename std::remove_cvref_t<Candidate>::CarrierQuantity;
|
||||
typename std::remove_cvref_t<Candidate>::CarrierField;
|
||||
typename std::remove_cvref_t<Candidate>::SurfaceDependencies;
|
||||
} && std::same_as<typename std::remove_cvref_t<Candidate>::PhysicalQuantity, eos::quantity::Pressure> &&
|
||||
} && std::same_as<typename std::remove_cvref_t<Candidate>::PhysicalQuantity, dimensions::quantity::Pressure> &&
|
||||
std::same_as<
|
||||
typename std::remove_cvref_t<Candidate>::SurfaceDependencies::RowField,
|
||||
typename std::remove_cvref_t<Candidate>::CarrierField> &&
|
||||
@@ -112,7 +112,7 @@ export namespace mean_field::operators {
|
||||
|
||||
for (int surfaceIndex = 0; surfaceIndex < surfaceRows.size(); ++surfaceIndex) {
|
||||
const detail::SingleQuantitySurfaceState<CarrierQuantity> state{
|
||||
eos::QuantityValue<CarrierQuantity>{surfaceState(surfaceIndex)}
|
||||
dimensions::QuantityValue<CarrierQuantity>{surfaceState(surfaceIndex)}
|
||||
};
|
||||
rowResidual(surfaceRows.reduced_dofs()[surfaceIndex]) =
|
||||
static_cast<const Constraint *>(constraint)->residual(state);
|
||||
@@ -132,10 +132,10 @@ export namespace mean_field::operators {
|
||||
for (int surfaceIndex = 0; surfaceIndex < surfaceRows.size(); ++surfaceIndex) {
|
||||
const int reducedDof = surfaceRows.reduced_dofs()[surfaceIndex];
|
||||
const detail::SingleQuantitySurfaceState<CarrierQuantity> state{
|
||||
eos::QuantityValue<CarrierQuantity>{surfaceState(surfaceIndex)}
|
||||
dimensions::QuantityValue<CarrierQuantity>{surfaceState(surfaceIndex)}
|
||||
};
|
||||
const detail::SingleQuantitySurfaceState<CarrierQuantity> variation{
|
||||
eos::QuantityValue<CarrierQuantity>{stateVariation(reducedDof)}
|
||||
dimensions::QuantityValue<CarrierQuantity>{stateVariation(reducedDof)}
|
||||
};
|
||||
rowAction(reducedDof) = static_cast<const Constraint *>(constraint)->jacobianAction(state, variation);
|
||||
}
|
||||
|
||||
601
libmeanfield/interface/operators/root_manifest.cppm
Normal file
601
libmeanfield/interface/operators/root_manifest.cppm
Normal file
@@ -0,0 +1,601 @@
|
||||
module;
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <concepts>
|
||||
#include <cstddef>
|
||||
#include <optional>
|
||||
#include <span>
|
||||
#include <stdexcept>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
|
||||
#include <mfem.hpp>
|
||||
|
||||
export module mean_field:operators.root_manifest;
|
||||
|
||||
export import :model.compiled_fixed_mass;
|
||||
export import :model.compiled_fixed_central_density;
|
||||
export import :model.specifications;
|
||||
export import :utils.blocks;
|
||||
|
||||
export namespace mean_field::operators {
|
||||
enum class RootBlockKind { value, residual };
|
||||
enum class RootBlockProvenance { physical_operator, model_specification };
|
||||
enum class RootRowInjection { physical_equation, append_global, replace_carrier_rows };
|
||||
enum class RootColumnPolicy { physical_state, existing_physical_multiplier, solver_border, no_column };
|
||||
enum class RootScalePolicy { unscaled, target_relative };
|
||||
|
||||
struct RootBlockDescriptor final {
|
||||
std::string_view stableId;
|
||||
std::string_view symbol;
|
||||
RootBlockKind kind;
|
||||
RootBlockProvenance provenance;
|
||||
std::string_view source;
|
||||
RootRowInjection rowInjection;
|
||||
RootColumnPolicy columnPolicy;
|
||||
RootScalePolicy scalePolicy;
|
||||
int canonicalIndex;
|
||||
int offset;
|
||||
int size;
|
||||
double scale;
|
||||
};
|
||||
|
||||
struct RootRowReplacementDescriptor final {
|
||||
std::string_view stableId;
|
||||
std::string_view sourceSpecification;
|
||||
models::SpecificationRole role;
|
||||
int carrierResidualBlock;
|
||||
int replacedRowCount;
|
||||
};
|
||||
|
||||
struct RootConstraintDescriptor final {
|
||||
std::string_view stableId;
|
||||
models::SpecificationRole role;
|
||||
RootRowInjection rowInjection;
|
||||
RootColumnPolicy columnPolicy;
|
||||
int valueBlock;
|
||||
int residualBlock;
|
||||
int rowArity;
|
||||
int columnArity;
|
||||
double target;
|
||||
std::optional<double> carrierTarget;
|
||||
std::string_view targetUnits;
|
||||
std::string_view residualUnits;
|
||||
double residualScale;
|
||||
};
|
||||
|
||||
struct CentralDensityManifestInput final {
|
||||
double targetDensity;
|
||||
double targetEnthalpy;
|
||||
int centerDofCount;
|
||||
};
|
||||
|
||||
struct RootConstraintReport final {
|
||||
RootConstraintDescriptor descriptor;
|
||||
double achieved;
|
||||
double dimensionalResidual;
|
||||
double scaledResidual;
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
struct StaticRootBlockDescriptor final {
|
||||
std::string_view stableId;
|
||||
std::string_view symbol;
|
||||
RootBlockProvenance provenance;
|
||||
std::string_view source;
|
||||
RootRowInjection rowInjection;
|
||||
RootColumnPolicy columnPolicy;
|
||||
RootScalePolicy scalePolicy;
|
||||
};
|
||||
|
||||
template <typename Block> struct RootBlockTraits;
|
||||
|
||||
#define MEAN_FIELD_PHYSICAL_VALUE_BLOCK(BlockType, StableId, Symbol) \
|
||||
template <> struct RootBlockTraits<BlockType> { \
|
||||
static constexpr StaticRootBlockDescriptor descriptor{ \
|
||||
StableId, \
|
||||
Symbol, \
|
||||
RootBlockProvenance::physical_operator, \
|
||||
"stellar_equilibrium", \
|
||||
RootRowInjection::physical_equation, \
|
||||
RootColumnPolicy::physical_state, \
|
||||
RootScalePolicy::unscaled \
|
||||
}; \
|
||||
}
|
||||
|
||||
#define MEAN_FIELD_PHYSICAL_RESIDUAL_BLOCK(BlockType, StableId, Symbol) \
|
||||
template <> struct RootBlockTraits<BlockType> { \
|
||||
static constexpr StaticRootBlockDescriptor descriptor{ \
|
||||
StableId, \
|
||||
Symbol, \
|
||||
RootBlockProvenance::physical_operator, \
|
||||
"stellar_equilibrium", \
|
||||
RootRowInjection::physical_equation, \
|
||||
RootColumnPolicy::no_column, \
|
||||
RootScalePolicy::unscaled \
|
||||
}; \
|
||||
}
|
||||
|
||||
MEAN_FIELD_PHYSICAL_VALUE_BLOCK(
|
||||
utils::blocks::density::mass::value,
|
||||
"density",
|
||||
"rho"
|
||||
);
|
||||
MEAN_FIELD_PHYSICAL_VALUE_BLOCK(
|
||||
utils::blocks::displacement::geometry::value,
|
||||
"volume_displacement",
|
||||
"d"
|
||||
);
|
||||
MEAN_FIELD_PHYSICAL_VALUE_BLOCK(
|
||||
utils::blocks::surface_deformation::parameters::value,
|
||||
"surface_deformation",
|
||||
"q"
|
||||
);
|
||||
MEAN_FIELD_PHYSICAL_VALUE_BLOCK(
|
||||
utils::blocks::gravity::gradient::value,
|
||||
"gravity_gradient",
|
||||
"g"
|
||||
);
|
||||
MEAN_FIELD_PHYSICAL_VALUE_BLOCK(
|
||||
utils::blocks::gravity::poisson::value,
|
||||
"gravity_potential",
|
||||
"Phi"
|
||||
);
|
||||
MEAN_FIELD_PHYSICAL_VALUE_BLOCK(
|
||||
utils::blocks::enthalpy::specific::value,
|
||||
"specific_enthalpy",
|
||||
"h"
|
||||
);
|
||||
|
||||
MEAN_FIELD_PHYSICAL_RESIDUAL_BLOCK(
|
||||
utils::blocks::gravity::gradient::residual,
|
||||
"gravity_gradient_relation",
|
||||
"R_g"
|
||||
);
|
||||
MEAN_FIELD_PHYSICAL_RESIDUAL_BLOCK(
|
||||
utils::blocks::gravity::poisson::residual,
|
||||
"poisson_balance",
|
||||
"R_Phi"
|
||||
);
|
||||
MEAN_FIELD_PHYSICAL_RESIDUAL_BLOCK(
|
||||
utils::blocks::density::mass::residual,
|
||||
"barotropic_closure",
|
||||
"R_rho"
|
||||
);
|
||||
MEAN_FIELD_PHYSICAL_RESIDUAL_BLOCK(
|
||||
utils::blocks::displacement::geometry::residual,
|
||||
"mechanical_balance",
|
||||
"R_d"
|
||||
);
|
||||
MEAN_FIELD_PHYSICAL_RESIDUAL_BLOCK(
|
||||
utils::blocks::surface_deformation::shape_equilibrium::residual,
|
||||
"surface_shape_balance",
|
||||
"R_q"
|
||||
);
|
||||
MEAN_FIELD_PHYSICAL_RESIDUAL_BLOCK(
|
||||
utils::blocks::enthalpy::specific::residual,
|
||||
"hydrostatic_balance",
|
||||
"R_h"
|
||||
);
|
||||
|
||||
#undef MEAN_FIELD_PHYSICAL_VALUE_BLOCK
|
||||
#undef MEAN_FIELD_PHYSICAL_RESIDUAL_BLOCK
|
||||
|
||||
template <> struct RootBlockTraits<utils::blocks::fixed_total_mass::mass_normalization::value> {
|
||||
static constexpr StaticRootBlockDescriptor descriptor{
|
||||
"fixed_total_mass.multiplier",
|
||||
"C",
|
||||
RootBlockProvenance::model_specification,
|
||||
"FixedTotalMass",
|
||||
RootRowInjection::physical_equation,
|
||||
RootColumnPolicy::existing_physical_multiplier,
|
||||
RootScalePolicy::unscaled
|
||||
};
|
||||
};
|
||||
|
||||
template <> struct RootBlockTraits<utils::blocks::fixed_total_mass::mass_normalization::residual> {
|
||||
static constexpr StaticRootBlockDescriptor descriptor{
|
||||
"fixed_total_mass.residual",
|
||||
"R_M",
|
||||
RootBlockProvenance::model_specification,
|
||||
"FixedTotalMass",
|
||||
RootRowInjection::append_global,
|
||||
RootColumnPolicy::no_column,
|
||||
RootScalePolicy::target_relative
|
||||
};
|
||||
};
|
||||
|
||||
template <> struct RootBlockTraits<utils::blocks::fixed_central_density::central_value::value> {
|
||||
static constexpr StaticRootBlockDescriptor descriptor{
|
||||
"fixed_central_density.border",
|
||||
"lambda_rho_c",
|
||||
RootBlockProvenance::model_specification,
|
||||
"FixedCentralDensity",
|
||||
RootRowInjection::physical_equation,
|
||||
RootColumnPolicy::solver_border,
|
||||
RootScalePolicy::unscaled
|
||||
};
|
||||
};
|
||||
|
||||
template <> struct RootBlockTraits<utils::blocks::fixed_central_density::central_value::residual> {
|
||||
static constexpr StaticRootBlockDescriptor descriptor{
|
||||
"fixed_central_density.residual", "R_rho_c",
|
||||
RootBlockProvenance::model_specification, "FixedCentralDensity",
|
||||
RootRowInjection::append_global, RootColumnPolicy::no_column,
|
||||
RootScalePolicy::target_relative
|
||||
};
|
||||
};
|
||||
|
||||
template <typename Block>
|
||||
[[nodiscard]] constexpr double blockScale(
|
||||
const double fixedMassScale,
|
||||
const double centralDensityScale
|
||||
) noexcept {
|
||||
if constexpr (std::same_as<Block, utils::blocks::fixed_total_mass::mass_normalization::residual>) {
|
||||
return fixedMassScale;
|
||||
} else if constexpr (std::same_as<Block, utils::blocks::fixed_central_density::central_value::residual>) {
|
||||
return centralDensityScale;
|
||||
} else {
|
||||
return 1.0;
|
||||
}
|
||||
}
|
||||
|
||||
template <
|
||||
RootBlockKind Kind,
|
||||
typename... Blocks>
|
||||
[[nodiscard]] std::array<
|
||||
RootBlockDescriptor,
|
||||
sizeof...(Blocks)>
|
||||
makeBlockDescriptors(
|
||||
const mfem::Array<int> &offsets,
|
||||
const double fixedMassScale,
|
||||
const double centralDensityScale,
|
||||
utils::blocks::type_list<Blocks...>
|
||||
) {
|
||||
std::array<RootBlockDescriptor, sizeof...(Blocks)> descriptors{};
|
||||
int index = 0;
|
||||
((descriptors[index] =
|
||||
{.stableId = RootBlockTraits<Blocks>::descriptor.stableId,
|
||||
.symbol = RootBlockTraits<Blocks>::descriptor.symbol,
|
||||
.kind = Kind,
|
||||
.provenance = RootBlockTraits<Blocks>::descriptor.provenance,
|
||||
.source = RootBlockTraits<Blocks>::descriptor.source,
|
||||
.rowInjection = RootBlockTraits<Blocks>::descriptor.rowInjection,
|
||||
.columnPolicy = RootBlockTraits<Blocks>::descriptor.columnPolicy,
|
||||
.scalePolicy = RootBlockTraits<Blocks>::descriptor.scalePolicy,
|
||||
.canonicalIndex = index,
|
||||
.offset = offsets[index],
|
||||
.size = offsets[index + 1] - offsets[index],
|
||||
.scale = blockScale<Blocks>(fixedMassScale, centralDensityScale)},
|
||||
++index),
|
||||
...);
|
||||
return descriptors;
|
||||
}
|
||||
|
||||
template <models::SpecifiedModelType Model>
|
||||
inline constexpr bool hasCentralDensity = Model::template containsSpecification<models::FixedCentralDensity>;
|
||||
|
||||
template <models::SpecifiedModelType Model>
|
||||
inline constexpr std::size_t rootConstraintCount = 2 + (hasCentralDensity<Model> ? 1 : 0);
|
||||
|
||||
template <
|
||||
models::SpecifiedModelType Model,
|
||||
typename Form>
|
||||
[[nodiscard]] std::array<
|
||||
RootConstraintDescriptor,
|
||||
rootConstraintCount<Model>>
|
||||
makeConstraintDescriptors(
|
||||
const double targetMass,
|
||||
const double targetSurfacePressure,
|
||||
const double fixedMassScale,
|
||||
const std::optional<CentralDensityManifestInput> centralDensity
|
||||
) {
|
||||
std::array<RootConstraintDescriptor, rootConstraintCount<Model>> descriptors{};
|
||||
descriptors[0] = {
|
||||
.stableId = "FixedTotalMass",
|
||||
.role = models::SpecificationRole::invariant,
|
||||
.rowInjection = RootRowInjection::append_global,
|
||||
.columnPolicy = RootColumnPolicy::existing_physical_multiplier,
|
||||
.valueBlock = models::FixedMassLayoutRequest::valueBlock<Form>().index,
|
||||
.residualBlock = models::FixedMassLayoutRequest::residualBlock<Form>().index,
|
||||
.rowArity = 1,
|
||||
.columnArity = 1,
|
||||
.target = targetMass,
|
||||
.carrierTarget = targetMass,
|
||||
.targetUnits = "mass",
|
||||
.residualUnits = "mass",
|
||||
.residualScale = fixedMassScale
|
||||
};
|
||||
descriptors[1] = {
|
||||
.stableId = "IsobaricSurface",
|
||||
.role = models::SpecificationRole::boundary_condition,
|
||||
.rowInjection = RootRowInjection::replace_carrier_rows,
|
||||
.columnPolicy = RootColumnPolicy::no_column,
|
||||
.valueBlock = -1,
|
||||
.residualBlock =
|
||||
utils::blocks::get_residual_block<Form>(utils::blocks::enthalpy_field.specific_term).index,
|
||||
.rowArity = 0,
|
||||
.columnArity = 0,
|
||||
.target = targetSurfacePressure,
|
||||
.carrierTarget = std::nullopt,
|
||||
.targetUnits = "pressure",
|
||||
.residualUnits = "specific_enthalpy",
|
||||
.residualScale = 1.0
|
||||
};
|
||||
|
||||
if constexpr (hasCentralDensity<Model>) {
|
||||
if (!centralDensity.has_value()) {
|
||||
throw std::invalid_argument(
|
||||
"A model containing FixedCentralDensity requires central-density manifest metadata."
|
||||
);
|
||||
}
|
||||
descriptors[2] = {
|
||||
.stableId = "FixedCentralDensity",
|
||||
.role = models::SpecificationRole::phase_condition,
|
||||
.rowInjection = RootRowInjection::append_global,
|
||||
.columnPolicy = RootColumnPolicy::solver_border,
|
||||
.valueBlock = models::CentralDensityLayoutRequest::valueBlock<Form>().index,
|
||||
.residualBlock = models::CentralDensityLayoutRequest::residualBlock<Form>().index,
|
||||
.rowArity = 1,
|
||||
.columnArity = 1,
|
||||
.target = centralDensity->targetDensity,
|
||||
.carrierTarget = centralDensity->targetEnthalpy,
|
||||
.targetUnits = "density",
|
||||
.residualUnits = "specific_enthalpy",
|
||||
.residualScale = std::max(std::abs(centralDensity->targetEnthalpy), 1.0e-300)
|
||||
};
|
||||
} else if (centralDensity.has_value()) {
|
||||
throw std::invalid_argument(
|
||||
"Central-density manifest metadata was provided to a model without FixedCentralDensity."
|
||||
);
|
||||
}
|
||||
return descriptors;
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
template <typename Form> class RootStateView final {
|
||||
public:
|
||||
RootStateView(
|
||||
const mfem::Vector &state,
|
||||
const utils::blocks::form_layout<Form> &layout
|
||||
)
|
||||
: m_state(state),
|
||||
m_layout(layout) {
|
||||
if (state.Size() != layout.value_offsets().Last()) {
|
||||
throw std::invalid_argument("RootStateView received a vector with the wrong size.");
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Term> [[nodiscard]] mfem::Vector block(const Term &term) const {
|
||||
constexpr auto valueBlock = utils::blocks::get_value_block<Form>(term);
|
||||
return mfem::Vector(
|
||||
const_cast<mfem::real_t *>(m_state.GetData()) + m_layout.offset(valueBlock), m_layout.size(valueBlock)
|
||||
);
|
||||
}
|
||||
|
||||
[[nodiscard]] const mfem::Vector &vector() const noexcept {
|
||||
return m_state;
|
||||
}
|
||||
|
||||
private:
|
||||
const mfem::Vector &m_state;
|
||||
const utils::blocks::form_layout<Form> &m_layout;
|
||||
};
|
||||
|
||||
template <typename Form> class ResidualView final {
|
||||
public:
|
||||
ResidualView(
|
||||
mfem::Vector &residual,
|
||||
const utils::blocks::form_layout<Form> &layout
|
||||
)
|
||||
: m_residual(residual),
|
||||
m_layout(layout) {
|
||||
if (residual.Size() != layout.residual_offsets().Last()) {
|
||||
throw std::invalid_argument("ResidualView received a vector with the wrong size.");
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Term> [[nodiscard]] mfem::Vector block(const Term &term) const {
|
||||
constexpr auto residualBlock = utils::blocks::get_residual_block<Form>(term);
|
||||
return mfem::Vector(m_residual.GetData() + m_layout.offset(residualBlock), m_layout.size(residualBlock));
|
||||
}
|
||||
|
||||
template <typename Term>
|
||||
void assign(
|
||||
const Term &term,
|
||||
const mfem::Vector &source
|
||||
) const {
|
||||
mfem::Vector destination = block(term);
|
||||
if (destination.Size() != source.Size()) {
|
||||
throw std::invalid_argument("ResidualView block assignment has the wrong size.");
|
||||
}
|
||||
destination = source;
|
||||
}
|
||||
|
||||
[[nodiscard]] mfem::Vector &vector() const noexcept {
|
||||
return m_residual;
|
||||
}
|
||||
|
||||
private:
|
||||
mfem::Vector &m_residual;
|
||||
const utils::blocks::form_layout<Form> &m_layout;
|
||||
};
|
||||
|
||||
template <models::SpecifiedModelType Model, typename Form, typename JacobianForm>
|
||||
requires utils::blocks::valid_jacobian_form<Form, JacobianForm>
|
||||
class CompiledRootManifest final {
|
||||
public:
|
||||
using ModelType = Model;
|
||||
using FormType = Form;
|
||||
using JacobianType = JacobianForm;
|
||||
using Layout = utils::blocks::form_layout<Form>;
|
||||
using StateView = RootStateView<Form>;
|
||||
using DirectionView = RootStateView<Form>;
|
||||
using RootResidualView = ResidualView<Form>;
|
||||
|
||||
static constexpr models::ModelCompilationClass compilationClass = Model::compilationClass;
|
||||
static constexpr bool symbolicallySquare = Model::symbolicallySquare;
|
||||
|
||||
CompiledRootManifest(
|
||||
const std::array<
|
||||
int,
|
||||
Form::value_block_count> &valueSizes,
|
||||
const std::array<
|
||||
int,
|
||||
Form::residual_block_count> &residualSizes,
|
||||
const double targetMass,
|
||||
const double targetSurfacePressure,
|
||||
const int replacedSurfaceRowCount,
|
||||
const std::optional<CentralDensityManifestInput> centralDensity = std::nullopt
|
||||
)
|
||||
: m_layout(
|
||||
valueSizes,
|
||||
residualSizes
|
||||
),
|
||||
m_fixedMassScale(
|
||||
std::max(
|
||||
std::abs(targetMass),
|
||||
1.0e-300
|
||||
)
|
||||
),
|
||||
m_centralDensityScale(
|
||||
centralDensity.has_value() ? std::max(
|
||||
std::abs(centralDensity->targetEnthalpy),
|
||||
1.0e-300
|
||||
)
|
||||
: 1.0
|
||||
),
|
||||
m_valueBlocks(
|
||||
detail::makeBlockDescriptors<RootBlockKind::value>(
|
||||
m_layout.value_offsets(),
|
||||
m_fixedMassScale,
|
||||
m_centralDensityScale,
|
||||
typename Form::value_blocks{}
|
||||
)
|
||||
),
|
||||
m_residualBlocks(
|
||||
detail::makeBlockDescriptors<RootBlockKind::residual>(
|
||||
m_layout.residual_offsets(),
|
||||
m_fixedMassScale,
|
||||
m_centralDensityScale,
|
||||
typename Form::residual_blocks{}
|
||||
)
|
||||
),
|
||||
m_replacements{RootRowReplacementDescriptor{
|
||||
.stableId = "isobaric_surface.replacement",
|
||||
.sourceSpecification = "IsobaricSurface",
|
||||
.role = models::SpecificationRole::boundary_condition,
|
||||
.carrierResidualBlock =
|
||||
utils::blocks::get_residual_block<Form>(utils::blocks::enthalpy_field.specific_term).index,
|
||||
.replacedRowCount = replacedSurfaceRowCount
|
||||
}},
|
||||
m_constraints(
|
||||
detail::makeConstraintDescriptors<
|
||||
Model,
|
||||
Form>(
|
||||
targetMass,
|
||||
targetSurfacePressure,
|
||||
m_fixedMassScale,
|
||||
centralDensity
|
||||
)
|
||||
) {
|
||||
if (replacedSurfaceRowCount < 0) {
|
||||
throw std::invalid_argument(
|
||||
"An equilibrium-system manifest cannot contain a negative replacement-row count."
|
||||
);
|
||||
}
|
||||
if (centralDensity.has_value() && centralDensity->centerDofCount < 0) {
|
||||
throw std::invalid_argument(
|
||||
"An equilibrium-system manifest cannot contain a negative central-DOF count."
|
||||
);
|
||||
}
|
||||
if constexpr (compilationClass == models::EquilibriumSystemCompilation::complete_equilibrium_system) {
|
||||
if (m_layout.value_offsets().Last() != m_layout.residual_offsets().Last()) {
|
||||
throw std::invalid_argument(
|
||||
"A complete equilibrium system must have equal state and equation dimensions."
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] const Layout &layout() const noexcept {
|
||||
return m_layout;
|
||||
}
|
||||
|
||||
[[nodiscard]] StateView stateView(const mfem::Vector &state) const {
|
||||
return {state, m_layout};
|
||||
}
|
||||
|
||||
[[nodiscard]] DirectionView directionView(const mfem::Vector &direction) const {
|
||||
return {direction, m_layout};
|
||||
}
|
||||
|
||||
[[nodiscard]] RootResidualView residualView(mfem::Vector &residual) const {
|
||||
return {residual, m_layout};
|
||||
}
|
||||
|
||||
[[nodiscard]] std::span<const RootBlockDescriptor> valueBlocks() const noexcept {
|
||||
return m_valueBlocks;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::span<const RootBlockDescriptor> residualBlocks() const noexcept {
|
||||
return m_residualBlocks;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::span<const RootRowReplacementDescriptor> rowReplacements() const noexcept {
|
||||
return m_replacements;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::span<const RootConstraintDescriptor> constraints() const noexcept {
|
||||
return m_constraints;
|
||||
}
|
||||
|
||||
[[nodiscard]] static constexpr std::span<const models::RuntimeSpecificationDescriptor>
|
||||
specificationDescriptors() noexcept {
|
||||
return Model::runtimeSpecificationDescriptors();
|
||||
}
|
||||
|
||||
[[nodiscard]] RootConstraintReport fixedMassReport(const double achievedMass) const {
|
||||
const RootConstraintDescriptor &descriptor = m_constraints[0];
|
||||
const double residual = achievedMass - descriptor.target;
|
||||
return {
|
||||
.descriptor = descriptor,
|
||||
.achieved = achievedMass,
|
||||
.dimensionalResidual = residual,
|
||||
.scaledResidual = residual / descriptor.residualScale
|
||||
};
|
||||
}
|
||||
|
||||
private:
|
||||
Layout m_layout;
|
||||
double m_fixedMassScale;
|
||||
double m_centralDensityScale;
|
||||
std::array<RootBlockDescriptor, Form::value_block_count> m_valueBlocks;
|
||||
std::array<RootBlockDescriptor, Form::residual_block_count> m_residualBlocks;
|
||||
std::array<RootRowReplacementDescriptor, 1> m_replacements;
|
||||
std::array<RootConstraintDescriptor, detail::rootConstraintCount<Model>> m_constraints;
|
||||
};
|
||||
|
||||
// Physics-facing names for the public equilibrium-system boundary. The
|
||||
// root-oriented names remain available while existing solver consumers
|
||||
// migrate, but new APIs should expose these aliases.
|
||||
using EquilibriumBlockKind = RootBlockKind;
|
||||
using EquilibriumBlockProvenance = RootBlockProvenance;
|
||||
using EquilibriumEquationInjection = RootRowInjection;
|
||||
using EquilibriumGeneratedVariablePolicy = RootColumnPolicy;
|
||||
using EquilibriumScalePolicy = RootScalePolicy;
|
||||
using EquilibriumBlockDescriptor = RootBlockDescriptor;
|
||||
using EquilibriumEquationReplacementDescriptor = RootRowReplacementDescriptor;
|
||||
using EquilibriumSpecificationDescriptor = RootConstraintDescriptor;
|
||||
using EquilibriumSpecificationReport = RootConstraintReport;
|
||||
|
||||
template <typename Form> using EquilibriumStateView = RootStateView<Form>;
|
||||
|
||||
template <typename Form> using EquilibriumResidualView = ResidualView<Form>;
|
||||
|
||||
template <models::SpecifiedModelType Model, typename Form, typename JacobianForm>
|
||||
requires utils::blocks::valid_jacobian_form<Form, JacobianForm>
|
||||
using EquilibriumSystemManifest = CompiledRootManifest<Model, Form, JacobianForm>;
|
||||
} // namespace mean_field::operators
|
||||
@@ -0,0 +1,210 @@
|
||||
module;
|
||||
|
||||
#include <concepts>
|
||||
#include <cstddef>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include <mfem.hpp>
|
||||
|
||||
export module mean_field:operators.stellar_equilibrium_problem;
|
||||
|
||||
export import :deformation.domain_deformation;
|
||||
export import :equilibrium.stellar_discretization;
|
||||
export import :model.typed_stellar;
|
||||
export import :operators.prepared_central_density_stellar_equilibrium;
|
||||
export import :surface.compiler;
|
||||
|
||||
export namespace mean_field::equilibrium {
|
||||
template <typename Candidate>
|
||||
concept StellarEquilibriumModel = model::StellarModelType<Candidate> && requires {
|
||||
requires std::remove_cvref_t<Candidate>::template containsSpecification<eos::Polytrope>;
|
||||
requires std::remove_cvref_t<Candidate>::template containsSpecification<surface::Isobaric>;
|
||||
requires std::remove_cvref_t<Candidate>::template containsSpecification<models::FixedTotalMass>;
|
||||
requires std::remove_cvref_t<Candidate>::specificationCount ==
|
||||
3 + static_cast<std::size_t>(
|
||||
std::remove_cvref_t<Candidate>::template containsSpecification<models::FixedCentralDensity>
|
||||
);
|
||||
};
|
||||
|
||||
template <StellarEquilibriumModel Model> class StellarEquilibriumProblem final {
|
||||
public:
|
||||
using ModelType = std::remove_cvref_t<Model>;
|
||||
|
||||
static constexpr bool hasFixedCentralDensity =
|
||||
ModelType::template containsSpecification<models::FixedCentralDensity>;
|
||||
static constexpr bool symbolicallySquare = ModelType::symbolicallySquare;
|
||||
|
||||
using PreparedOperatorType = std::conditional_t<
|
||||
hasFixedCentralDensity,
|
||||
operators::PreparedCentralDensityStellarEquilibriumOperator,
|
||||
operators::PreparedStellarEquilibriumOperator>;
|
||||
using CompiledSurfaceConstraintType =
|
||||
surface::CompiledPressureSurfaceConstraintT<surface::BarotropicSurfaceFormulation, eos::Polytrope>;
|
||||
|
||||
StellarEquilibriumProblem(
|
||||
ModelType stellarModel,
|
||||
const StellarDiscretization discretization
|
||||
)
|
||||
requires(!hasFixedCentralDensity)
|
||||
: m_stellarModel(std::move(stellarModel)),
|
||||
m_discretization(discretization),
|
||||
m_compiledSurfaceConstraint(CompileSurfaceConstraint(m_stellarModel)),
|
||||
m_preparedOperator(
|
||||
m_discretization.finiteElementModel(),
|
||||
m_discretization.domainMapper(),
|
||||
m_stellarModel.template specification<eos::Polytrope>(),
|
||||
models::compileConstraint(m_stellarModel.template specification<models::FixedTotalMass>()),
|
||||
operators::PressureSurfaceConstraintView{m_compiledSurfaceConstraint},
|
||||
CompileDefaultDomainDeformation(m_discretization.finiteElementModel())
|
||||
) {
|
||||
VerifyProblem();
|
||||
}
|
||||
|
||||
StellarEquilibriumProblem(
|
||||
ModelType stellarModel,
|
||||
const StellarDiscretization discretization
|
||||
)
|
||||
requires hasFixedCentralDensity
|
||||
: m_stellarModel(std::move(stellarModel)),
|
||||
m_discretization(discretization),
|
||||
m_compiledSurfaceConstraint(CompileSurfaceConstraint(m_stellarModel)),
|
||||
m_preparedOperator(
|
||||
m_discretization.finiteElementModel(),
|
||||
m_discretization.domainMapper(),
|
||||
m_stellarModel.template specification<eos::Polytrope>(),
|
||||
models::compileConstraint(m_stellarModel.template specification<models::FixedTotalMass>()),
|
||||
operators::PressureSurfaceConstraintView{m_compiledSurfaceConstraint},
|
||||
CompileDefaultDomainDeformation(m_discretization.finiteElementModel()),
|
||||
models::compileConstraint(
|
||||
m_stellarModel.template specification<models::FixedCentralDensity>(),
|
||||
m_stellarModel.template specification<eos::Polytrope>()
|
||||
)
|
||||
) {
|
||||
VerifyProblem();
|
||||
}
|
||||
|
||||
StellarEquilibriumProblem(const StellarEquilibriumProblem &) = delete;
|
||||
StellarEquilibriumProblem &operator=(const StellarEquilibriumProblem &) = delete;
|
||||
StellarEquilibriumProblem(StellarEquilibriumProblem &&) = delete;
|
||||
StellarEquilibriumProblem &operator=(StellarEquilibriumProblem &&) = delete;
|
||||
|
||||
[[nodiscard]] const ModelType &GetStellarModel() const noexcept {
|
||||
return m_stellarModel;
|
||||
}
|
||||
|
||||
[[nodiscard]] const StellarDiscretization &GetDiscretization() const noexcept {
|
||||
return m_discretization;
|
||||
}
|
||||
|
||||
[[nodiscard]] const CompiledSurfaceConstraintType &GetCompiledSurfaceConstraint() const noexcept {
|
||||
return m_compiledSurfaceConstraint;
|
||||
}
|
||||
|
||||
[[nodiscard]] PreparedOperatorType &GetPreparedOperator() noexcept {
|
||||
return m_preparedOperator;
|
||||
}
|
||||
|
||||
[[nodiscard]] const PreparedOperatorType &GetPreparedOperator() const noexcept {
|
||||
return m_preparedOperator;
|
||||
}
|
||||
|
||||
[[nodiscard]] const auto &GetManifest() const noexcept {
|
||||
return m_preparedOperator.GetRootManifest();
|
||||
}
|
||||
|
||||
[[nodiscard]] const field::FieldBoundaryDofMap &GetPressureSurfaceRows() const noexcept {
|
||||
if constexpr (hasFixedCentralDensity) {
|
||||
return m_preparedOperator.GetPhysicalOperator().GetSurfaceConstraintOperator().GetSurfaceRows();
|
||||
} else {
|
||||
return m_preparedOperator.GetSurfaceConstraintOperator().GetSurfaceRows();
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] int StateSize() const noexcept {
|
||||
return m_preparedOperator.Width();
|
||||
}
|
||||
|
||||
[[nodiscard]] int EquationSize() const noexcept {
|
||||
return m_preparedOperator.Height();
|
||||
}
|
||||
|
||||
[[nodiscard]] const mfem::Operator &GetLinearizationOperator() const noexcept {
|
||||
return m_preparedOperator;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto Prepare(
|
||||
const mfem::Vector &state,
|
||||
const operators::StellarEquilibriumDependencies &dependencies,
|
||||
const physics::RigidRotation &rotation
|
||||
) {
|
||||
return m_preparedOperator.Prepare(state, dependencies, rotation);
|
||||
}
|
||||
|
||||
void BuildResidual(mfem::Vector &residual) const {
|
||||
m_preparedOperator.BuildResidual(residual);
|
||||
}
|
||||
|
||||
void ApplyLinearization(
|
||||
const mfem::Vector &direction,
|
||||
mfem::Vector &action
|
||||
) const {
|
||||
m_preparedOperator.Mult(direction, action);
|
||||
}
|
||||
|
||||
private:
|
||||
[[nodiscard]] static CompiledSurfaceConstraintType CompileSurfaceConstraint(const ModelType &stellarModel) {
|
||||
return surface::compilePressureSurfaceConstraint<surface::BarotropicSurfaceFormulation>(
|
||||
stellarModel.template specification<surface::Isobaric>(),
|
||||
stellarModel.template specification<eos::Polytrope>()
|
||||
);
|
||||
}
|
||||
|
||||
[[nodiscard]] static deformation::PreparedDomainDeformationRuntime
|
||||
CompileDefaultDomainDeformation(fem::FEM &finiteElementModel) {
|
||||
MFEM_VERIFY(
|
||||
finiteElementModel.mesh != nullptr,
|
||||
"Default stellar domain-deformation compilation requires a physical mesh."
|
||||
);
|
||||
mfem::Vector referenceCenter(finiteElementModel.mesh->SpaceDimension());
|
||||
referenceCenter = 0.0;
|
||||
|
||||
return deformation::PreparedDomainDeformationRuntime{deformation::compileDomainDeformation(
|
||||
deformation::NodalRadialSurface{std::move(referenceCenter)},
|
||||
deformation::PowerLawRadialInteriorExtension{}, deformation::FixedInfinityRadialVacuumExtension{},
|
||||
finiteElementModel
|
||||
)};
|
||||
}
|
||||
|
||||
void VerifyProblem() const {
|
||||
MFEM_VERIFY(symbolicallySquare, "A stellar equilibrium problem must be symbolically square.");
|
||||
MFEM_VERIFY(
|
||||
StateSize() == EquationSize(),
|
||||
"The discretized stellar equilibrium problem has unequal state and equation dimensions."
|
||||
);
|
||||
MFEM_VERIFY(m_discretization.isCurrent(), "The stellar equilibrium problem has a stale discretization.");
|
||||
}
|
||||
|
||||
ModelType m_stellarModel;
|
||||
StellarDiscretization m_discretization;
|
||||
CompiledSurfaceConstraintType m_compiledSurfaceConstraint;
|
||||
PreparedOperatorType m_preparedOperator;
|
||||
};
|
||||
|
||||
template <StellarEquilibriumModel Model>
|
||||
[[nodiscard]] auto discretize(
|
||||
Model &&stellarModel,
|
||||
const StellarDiscretization discretization
|
||||
) {
|
||||
using ModelType = std::remove_cvref_t<Model>;
|
||||
return StellarEquilibriumProblem<ModelType>{std::forward<Model>(stellarModel), discretization};
|
||||
}
|
||||
|
||||
template <StellarEquilibriumModel Model>
|
||||
[[nodiscard]] auto discretize(
|
||||
Model &&stellarModel,
|
||||
fem::FEM &finiteElementModel
|
||||
) {
|
||||
return discretize(std::forward<Model>(stellarModel), StellarDiscretization{finiteElementModel});
|
||||
}
|
||||
} // namespace mean_field::equilibrium
|
||||
@@ -0,0 +1,26 @@
|
||||
module;
|
||||
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
export module mean_field:operators.stellar_equilibrium_system;
|
||||
|
||||
export import :operators.stellar_equilibrium_problem;
|
||||
|
||||
export namespace mean_field::equilibrium {
|
||||
// Transitional source-compatible names. New code should use
|
||||
// StellarEquilibriumProblem and equilibrium::discretize.
|
||||
template <typename Candidate>
|
||||
concept CurrentlySupportedStellarModel = StellarEquilibriumModel<Candidate>;
|
||||
|
||||
template <StellarEquilibriumModel Model> using StellarEquilibriumSystem = StellarEquilibriumProblem<Model>;
|
||||
|
||||
template <StellarEquilibriumModel Model>
|
||||
[[nodiscard]] auto makeStellarEquilibriumSystem(
|
||||
fem::FEM &finiteElementModel,
|
||||
const mapping::DomainMapper &domainMapper,
|
||||
Model &&stellarModel
|
||||
) {
|
||||
return discretize(std::forward<Model>(stellarModel), StellarDiscretization{finiteElementModel, domainMapper});
|
||||
}
|
||||
} // namespace mean_field::equilibrium
|
||||
Reference in New Issue
Block a user