perf(jacobian-action): major updates to jacobian action application by removing redudant quadrature work. ~5x increase in speed

This commit is contained in:
2026-09-02 17:01:50 -04:00
parent 85500fef3b
commit 25510008dd
74 changed files with 8967 additions and 814 deletions

View File

@@ -3,7 +3,6 @@ module;
module mean_field;
import :operators.gravity_field_jacobian;
import :operators.kernels.gravity_field;
import :utils.blocks;
namespace {
@@ -202,7 +201,6 @@ namespace mean_field::operators {
const context::gravity_field::GravityFieldGeometryContext &geometry_context =
m_linearization_context.GetGeometryContext();
const mfem::Vector &density = m_linearization_context.GetDensityTrue();
const mfem::Vector &displacement = geometry_context.GetDisplacementTrue();
const mfem::Vector &gravity_gradient = m_linearization_context.GetGravityGradientTrue();
const mfem::Vector density_direction = make_read_only_value_view(direction, m_state_offsets, density_block);
@@ -244,14 +242,13 @@ namespace mean_field::operators {
geometry_context.GetMassOperator().Mult(gravity_gradient_direction, gravity_gradient_action);
geometry_context.GetSourceOperator().Mult(density_direction, source_action);
kernels::apply_mapped_hdiv_mass_variation(
m_fem, m_domain_mapper, gravity_gradient, displacement, displacement_direction_true,
mass_variation_action_true
geometry_context.GetMassOperator().MultDisplacementVariationTrue(
gravity_gradient, displacement_direction_true, mass_variation_action_true
);
flux_map.gather(mass_variation_action_true, mass_variation_action);
kernels::apply_mapped_source_variation(
m_fem, m_domain_mapper, density, displacement, displacement_direction_true, source_variation_action_true
geometry_context.GetSourceOperator().MultDisplacementVariationTrue(
density, displacement_direction_true, source_variation_action_true
);
potential_map.gather(source_variation_action_true, source_variation_action);

View File

@@ -12,10 +12,11 @@ import :field.registry;
import :utils.domain;
namespace {
namespace eos = mean_field::eos;
namespace dimensions = mean_field::dimensions;
namespace eos = mean_field::eos;
using DomainSchema = mean_field::utils::domain::CoreEnvelopeVacuumDomainSchema;
using ClosureDomain = mean_field::field::FieldDomainT<mean_field::field::Density>;
using DomainSchema = mean_field::utils::domain::CoreEnvelopeVacuumDomainSchema;
using ClosureDomain = mean_field::field::FieldDomainT<mean_field::field::Density>;
enum class ClosureAction { residual, density, enthalpy };
@@ -339,7 +340,9 @@ namespace {
const double density = elementDensityInput * densityShape;
const double equationOfStateDensity =
eos::evaluate<eos::quantity::Density>(barotrope, eos::SpecificEnthalpyValue{baseEnthalpy})
eos::evaluate<dimensions::quantity::Density>(
barotrope, dimensions::SpecificEnthalpyValue{baseEnthalpy}
)
.value();
integrand = density - equationOfStateDensity;
@@ -348,7 +351,7 @@ namespace {
const double densityDerivative =
eos::partialDerivative<eos::quantity::Density, eos::quantity::SpecificEnthalpy>(
barotrope, eos::SpecificEnthalpyValue{baseEnthalpy}
barotrope, dimensions::SpecificEnthalpyValue{baseEnthalpy}
)
.value();
@@ -640,16 +643,18 @@ namespace mean_field::operators::kernels {
enthalpyElement.CalcShape(integrationPoint, enthalpyShape);
const double densityValue = elementBaseDensity * densityShape;
const double densityValue = elementBaseDensity * densityShape;
const double enthalpyValue = elementBaseEnthalpy * enthalpyShape;
const double enthalpyValue = elementBaseEnthalpy * enthalpyShape;
const double equationOfStateDensity =
eos::evaluate<eos::quantity::Density>(barotrope, eos::SpecificEnthalpyValue{enthalpyValue}).value();
const double equationOfStateDensity = eos::evaluate<dimensions::quantity::Density>(
barotrope, dimensions::SpecificEnthalpyValue{enthalpyValue}
)
.value();
const double closureValue = densityValue - equationOfStateDensity;
const double closureValue = densityValue - equationOfStateDensity;
const double geometryActionValue = closureValue * mappingVariation.weight_variation;
const double geometryActionValue = closureValue * mappingVariation.weight_variation;
MFEM_VERIFY(
std::isfinite(closureValue) && std::isfinite(geometryActionValue),

View File

@@ -490,6 +490,8 @@ namespace mean_field::operators::kernels {
mfem::DenseMatrix gravity_gradient_shape;
mfem::DenseMatrix mass_tensor_variation;
mapping::VolumeMappingContext mapping_context;
mapping::VolumeMappingVariation mapping_variation;
for (int element_id = 0; element_id < f.mesh->GetNE(); ++element_id) {
const mfem::FiniteElement &gravity_gradient_element = *f.gravityFluxFes->GetFE(element_id);
@@ -555,7 +557,6 @@ namespace mean_field::operators::kernels {
const mfem::IntegrationPoint &integration_point = integration_rule.IntPoint(q);
transformation->SetIntPoint(&integration_point);
mapping::VolumeMappingContext mapping_context;
const mapping::MappingStatus status = domain_mapper.EvaluateVolume(
mapping_data, *transformation, integration_point, workspace, mapping_context
);
@@ -568,7 +569,6 @@ namespace mean_field::operators::kernels {
<< ", status: " << static_cast<int>(status)
);
mapping::VolumeMappingVariation mapping_variation;
const mapping::MappingStatus variation_status = domain_mapper.EvaluateVolumeVariation(
mapping_data, displacement_variation_data, *transformation, integration_point, mapping_context,
workspace, mapping_variation

View File

@@ -12,9 +12,10 @@ module mean_field;
import :operators.kernels.pressure_force;
namespace {
namespace eos = mean_field::eos;
namespace dimensions = mean_field::dimensions;
namespace eos = mean_field::eos;
using DomainSchema = mean_field::utils::domain::CoreEnvelopeVacuumDomainSchema;
using DomainSchema = mean_field::utils::domain::CoreEnvelopeVacuumDomainSchema;
[[nodiscard]] bool is_vacuum_attribute(const int attribute) {
return DomainSchema::template attribute_belongs_to<mean_field::utils::domain::Vacuum>(attribute);
@@ -416,14 +417,15 @@ namespace {
if (pressureForceAction == PressureForceAction::residual ||
pressureForceAction == PressureForceAction::displacement) {
pressureFactor =
eos::evaluate<eos::quantity::Pressure>(barotrope, eos::SpecificEnthalpyValue{enthalpyValue})
.value();
pressureFactor = eos::evaluate<dimensions::quantity::Pressure>(
barotrope, dimensions::SpecificEnthalpyValue{enthalpyValue}
)
.value();
} else {
const double enthalpyVariationValue = elementEnthalpyVariation * enthalpyShape;
pressureFactor = eos::partialDerivative<eos::quantity::Pressure, eos::quantity::SpecificEnthalpy>(
barotrope, eos::SpecificEnthalpyValue{enthalpyValue}
barotrope, dimensions::SpecificEnthalpyValue{enthalpyValue}
)
.value() *
enthalpyVariationValue;

View File

@@ -287,7 +287,6 @@ namespace mean_field::operators {
mapping::DomainMapper::Workspace workspace(m_fem.mesh->Dimension());
mfem::Array<int> displacementDofs;
mfem::Array<int> compactificationDofs;
mfem::Vector elementBaseDensity;
@@ -311,18 +310,19 @@ namespace mean_field::operators {
m_elements.emplace_back();
ElementPAData &data = m_elements.back();
data.elementId = elementId;
data.densityDofTransformation = m_fem.densityFes->GetElementDofs(elementId, data.densityDofs);
data.enthalpyDofTransformation = m_fem.enthalpyFes->GetElementDofs(elementId, data.enthalpyDofs);
mfem::DofTransformation *displacementDofTransformation =
m_fem.displacementFes->GetElementVDofs(elementId, displacementDofs);
data.displacementDofTransformation =
m_fem.displacementFes->GetElementVDofs(elementId, data.displacementDofs);
mfem::DofTransformation *compactificationDofTransformation =
m_fem.compactificationFes->GetElementDofs(elementId, compactificationDofs);
baseDensityLocal.GetSubVector(data.densityDofs, elementBaseDensity);
baseEnthalpyLocal.GetSubVector(data.enthalpyDofs, elementBaseEnthalpy);
displacementLocal.GetSubVector(displacementDofs, elementDisplacement);
displacementLocal.GetSubVector(data.displacementDofs, elementDisplacement);
m_fem.compactificationCoordinate->GetSubVector(compactificationDofs, elementCompactification);
if (data.densityDofTransformation != nullptr) {
@@ -331,8 +331,8 @@ namespace mean_field::operators {
if (data.enthalpyDofTransformation != nullptr) {
data.enthalpyDofTransformation->InvTransformPrimal(elementBaseEnthalpy);
}
if (displacementDofTransformation != nullptr) {
displacementDofTransformation->InvTransformPrimal(elementDisplacement);
if (data.displacementDofTransformation != nullptr) {
data.displacementDofTransformation->InvTransformPrimal(elementDisplacement);
}
if (compactificationDofTransformation != nullptr) {
compactificationDofTransformation->InvTransformPrimal(elementCompactification);
@@ -363,6 +363,9 @@ namespace mean_field::operators {
data.densityBasis.SetSize(quadraturePointCount, densityDofCount);
data.enthalpyBasis.SetSize(quadraturePointCount, enthalpyDofCount);
data.inverseElementJacobians.SetSize(
quadraturePointCount, m_fem.mesh->Dimension() * m_fem.mesh->Dimension()
);
data.weightedResidual.SetSize(quadraturePointCount);
data.quadratureWeights.SetSize(quadraturePointCount);
data.weightedEnthalpyDerivative.SetSize(quadraturePointCount);
@@ -388,6 +391,18 @@ namespace mean_field::operators {
<< ", quadrature point: " << quadraturePoint << ", status: " << static_cast<int>(mappingStatus)
);
MFEM_VERIFY(
!mappingContext.mapping.compactified,
"Prepared barotropic closure support unexpectedly includes a compactified element."
);
for (int row = 0; row < m_fem.mesh->Dimension(); ++row) {
for (int column = 0; column < m_fem.mesh->Dimension(); ++column) {
data.inverseElementJacobians(quadraturePoint, row * m_fem.mesh->Dimension() + column) =
mappingContext.quadrature.J_inv(row, column);
}
}
densityElement.CalcShape(integrationPoint, densityShape);
enthalpyElement.CalcShape(integrationPoint, enthalpyShape);
@@ -401,7 +416,7 @@ namespace mean_field::operators {
const double density = elementBaseDensity * densityShape;
const double enthalpy = elementBaseEnthalpy * enthalpyShape;
const double quadratureWeight = mappingContext.quadrature.weight;
const eos::SpecificEnthalpyValue specificEnthalpy{enthalpy};
const dimensions::SpecificEnthalpyValue specificEnthalpy{enthalpy};
const double eosDensity =
eos::evaluate<eos::quantity::Density>(m_equationOfState, specificEnthalpy).value();
const double enthalpyDerivative =
@@ -485,10 +500,7 @@ namespace mean_field::operators {
ApplyThermodynamicActionFull(m_densityVariationTrue, m_enthalpyVariationTrue, m_fullThermodynamicAction);
kernels::apply_barotropic_closure_displacement_action(
m_fem, m_domainMapper, m_equationOfState, m_baseDensityTrue, m_baseEnthalpyTrue, m_baseDisplacementTrue,
m_displacementVariationTrue, m_fullDisplacementAction
);
ApplyDisplacementActionFull(m_displacementVariationTrue, m_fullDisplacementAction);
MFEM_VERIFY(
m_fullThermodynamicAction.Size() == m_densityMap.full_size() &&
@@ -589,6 +601,86 @@ namespace mean_field::operators {
local_to_true(*m_fem.densityFes, localAction, actionTrue);
}
void PreparedBarotropicClosureOperator::ApplyDisplacementActionFull(
const mfem::Vector &displacementVariationTrue,
mfem::Vector &actionTrue
) const {
MFEM_VERIFY(
displacementVariationTrue.Size() == m_displacementMap.full_size(),
"The full displacement variation has the wrong size."
);
true_to_local(*m_fem.displacementFes, displacementVariationTrue, m_displacementVariationLocal);
m_localDisplacementAction.SetSize(m_fem.densityFes->GetVSize());
m_localDisplacementAction = 0.0;
const int dimension = m_fem.mesh->Dimension();
for (const ElementPAData &data : m_elements) {
m_displacementVariationLocal.GetSubVector(data.displacementDofs, m_elementDisplacementVariation);
if (data.displacementDofTransformation != nullptr) {
data.displacementDofTransformation->InvTransformPrimal(m_elementDisplacementVariation);
}
const mfem::FiniteElement &displacementElement = *m_fem.displacementFes->GetFE(data.elementId);
const mapping::ElementDisplacementData directionData =
mapping::ElementDisplacementDataFromElementVDofs(displacementElement, m_elementDisplacementVariation);
const mfem::DenseMatrix &directionDofs = directionData.GetDofMatrix();
mfem::ElementTransformation *transformation = m_fem.mesh->GetElementTransformation(data.elementId);
MFEM_VERIFY(
transformation != nullptr,
"Prepared barotropic closure displacement action received a null element transformation."
);
const mfem::FiniteElement &densityElement = *m_fem.densityFes->GetFE(data.elementId);
const mfem::FiniteElement &enthalpyElement = *m_fem.enthalpyFes->GetFE(data.elementId);
const mfem::IntegrationRule &integrationRule =
get_eos_rule(m_fem, m_equationOfState, densityElement, enthalpyElement, *transformation);
MFEM_VERIFY(
data.inverseElementJacobians.Height() == integrationRule.GetNPoints() &&
data.inverseElementJacobians.Width() == dimension * dimension,
"Prepared barotropic closure inverse-Jacobian data has an incompatible size."
);
m_referenceDShape.SetSize(displacementElement.GetDof(), dimension);
m_referenceDisplacementJacobian.SetSize(dimension, dimension);
m_quadratureDisplacementAction.SetSize(integrationRule.GetNPoints());
for (int quadraturePoint = 0; quadraturePoint < integrationRule.GetNPoints(); ++quadraturePoint) {
const mfem::IntegrationPoint &integrationPoint = integrationRule.IntPoint(quadraturePoint);
displacementElement.CalcDShape(integrationPoint, m_referenceDShape);
mfem::MultAtB(directionDofs, m_referenceDShape, m_referenceDisplacementJacobian);
double logarithmicJacobianVariation{0.0};
for (int row = 0; row < dimension; ++row) {
for (int column = 0; column < dimension; ++column) {
logarithmicJacobianVariation +=
data.inverseElementJacobians(quadraturePoint, row * dimension + column) *
m_referenceDisplacementJacobian(column, row);
}
}
m_quadratureDisplacementAction(quadraturePoint) =
data.weightedResidual(quadraturePoint) * logarithmicJacobianVariation;
MFEM_VERIFY(
std::isfinite(m_quadratureDisplacementAction(quadraturePoint)),
"Prepared barotropic closure displacement action encountered a non-finite quadrature value."
);
}
m_elementDisplacementAction.SetSize(data.densityDofs.Size());
data.densityBasis.MultTranspose(m_quadratureDisplacementAction, m_elementDisplacementAction);
if (data.densityDofTransformation != nullptr) {
data.densityDofTransformation->TransformDual(m_elementDisplacementAction);
}
m_localDisplacementAction.AddElementVector(data.densityDofs, m_elementDisplacementAction);
}
local_to_true(*m_fem.densityFes, m_localDisplacementAction, actionTrue);
}
bool PreparedBarotropicClosureOperator::IsPrepared() const noexcept {
return m_isPrepared && m_context.IsPrepared();
}

View File

@@ -0,0 +1,223 @@
module;
#include <array>
#include <cmath>
#include <cstdint>
#include <memory>
#include <utility>
#include <mfem.hpp>
module mean_field;
import :operators.prepared_central_density_stellar_equilibrium;
namespace {
using DomainSchema = mean_field::utils::domain::CoreEnvelopeVacuumDomainSchema;
using PhysicalForm = mean_field::utils::blocks::surface_deformed_stellar_equilibrium_form;
using BorderedForm = mean_field::operators::CentralDensityStellarEquilibriumForm;
[[nodiscard]] std::array<
int,
BorderedForm::value_block_count>
make_value_sizes(const mean_field::operators::StellarEquilibriumLayout &physicalLayout) {
std::array<int, BorderedForm::value_block_count> sizes{};
for (int block = 0; block < PhysicalForm::value_block_count; ++block) {
sizes[block] = physicalLayout.value_offsets()[block + 1] - physicalLayout.value_offsets()[block];
}
sizes[PhysicalForm::value_block_count] = 1;
return sizes;
}
[[nodiscard]] std::array<
int,
BorderedForm::residual_block_count>
make_residual_sizes(const mean_field::operators::StellarEquilibriumLayout &physicalLayout) {
std::array<int, BorderedForm::residual_block_count> sizes{};
for (int block = 0; block < PhysicalForm::residual_block_count; ++block) {
sizes[block] = physicalLayout.residual_offsets()[block + 1] - physicalLayout.residual_offsets()[block];
}
sizes[PhysicalForm::residual_block_count] = 1;
return sizes;
}
[[nodiscard]] mean_field::operators::CentralDensityDependencies
make_phase_dependencies(const mean_field::operators::StellarEquilibriumDependencies &dependencies) {
return {.enthalpy = {.identity = dependencies.enthalpy.identity, .revision = dependencies.enthalpy.revision}};
}
void validate_finite_scalar(
const double value,
const char *message
) {
MFEM_VERIFY(std::isfinite(value), message);
}
} // namespace
namespace mean_field::operators {
field::FieldPointDofMap PreparedCentralDensityStellarEquilibriumOperator::MakeCenterDofMap(const fem::FEM &f) {
MFEM_VERIFY(
f.mesh != nullptr && f.enthalpyFes != nullptr,
"The central-density phase requires the mesh and enthalpy finite-element space."
);
const field::FieldDofMap enthalpyMap = field::make_field_dof_map<field::Enthalpy, DomainSchema>(*f.enthalpyFes);
mfem::Vector origin(f.mesh->SpaceDimension());
origin = 0.0;
return field::make_field_point_dof_map<field::Enthalpy>(*f.enthalpyFes, enthalpyMap, origin, 1.0e-12);
}
PreparedCentralDensityStellarEquilibriumOperator::PreparedCentralDensityStellarEquilibriumOperator(
fem::FEM &f,
std::unique_ptr<PreparedStellarEquilibriumOperator> physicalOperator,
models::CompiledFixedCentralDensity centralDensity,
field::FieldPointDofMap centerDof
)
: mfem::Operator(
physicalOperator->Height() + 1,
physicalOperator->Width() + 1
),
m_physicalOperator(std::move(physicalOperator)),
m_centralDensity(std::move(centralDensity)),
m_phaseConstraint(
std::move(centerDof),
f.mesh->GetComm()
),
m_rootManifest(
make_value_sizes(m_physicalOperator->GetLayout()),
make_residual_sizes(m_physicalOperator->GetLayout()),
m_physicalOperator->GetTargetMass(),
m_physicalOperator->GetSurfaceConstraintOperator().GetPhysicalCondition().targetPressure,
m_physicalOperator->GetSurfaceConstraintOperator().GetSurfaceRows().size(),
CentralDensityManifestInput{
.targetDensity = m_centralDensity.targetDensity().value(),
.targetEnthalpy = m_centralDensity.targetEnthalpy().value(),
.centerDofCount = 1
}
) {
MFEM_VERIFY(
Width() == m_rootManifest.layout().value_offsets().Last() &&
Height() == m_rootManifest.layout().residual_offsets().Last(),
"The central-density bordered root has inconsistent dimensions."
);
}
PreparedCentralDensityStellarEquilibriumReport PreparedCentralDensityStellarEquilibriumOperator::Prepare(
const mfem::Vector &state,
const StellarEquilibriumDependencies &dependencies,
const physics::RigidRotation &rotation
) {
MFEM_VERIFY(state.Size() == Width(), "The central-density bordered root received a state with the wrong size.");
const auto stateView = m_rootManifest.stateView(state);
const mfem::Vector enthalpy = stateView.block(utils::blocks::enthalpy_field.specific_term);
const mfem::Vector border = stateView.block(utils::blocks::fixed_central_density_phase.central_value_term);
validate_finite_scalar(border(0), "The central-density bordered root received a non-finite border value.");
mfem::Vector physicalState(const_cast<mfem::real_t *>(state.GetData()), m_physicalOperator->Width());
m_isPrepared = false;
PreparedCentralDensityStellarEquilibriumReport report;
report.physical = m_physicalOperator->Prepare(physicalState, dependencies, rotation);
report.phase =
m_phaseConstraint.Prepare(m_centralDensity, enthalpy, border(0), make_phase_dependencies(dependencies));
if (report.physical.assembledResidual || report.phase.DidAnyWork() || m_cachedResidual.Size() != Height()) {
AssembleResidual();
report.assembledResidual = true;
}
m_isPrepared = true;
return report;
}
void PreparedCentralDensityStellarEquilibriumOperator::AssembleResidual() {
mfem::Vector physicalResidual;
m_physicalOperator->BuildResidual(physicalResidual);
m_cachedResidual.SetSize(Height());
m_cachedResidual = 0.0;
mfem::Vector physicalDestination(m_cachedResidual.GetData(), physicalResidual.Size());
physicalDestination = physicalResidual;
const auto residualView = m_rootManifest.residualView(m_cachedResidual);
mfem::Vector enthalpyResidual = residualView.block(utils::blocks::enthalpy_field.specific_term);
mfem::Vector phaseResidual = residualView.block(utils::blocks::fixed_central_density_phase.central_value_term);
m_phaseConstraint.AddResidual(enthalpyResidual, phaseResidual);
}
void PreparedCentralDensityStellarEquilibriumOperator::BuildResidual(mfem::Vector &residual) const {
VerifyPrepared();
residual = m_cachedResidual;
}
void PreparedCentralDensityStellarEquilibriumOperator::Mult(
const mfem::Vector &direction,
mfem::Vector &action
) const {
VerifyPrepared();
MFEM_VERIFY(
direction.Size() == Width(), "The central-density bordered root received a direction with the wrong size."
);
const auto directionView = m_rootManifest.directionView(direction);
const mfem::Vector enthalpyDirection = directionView.block(utils::blocks::enthalpy_field.specific_term);
const mfem::Vector borderDirection =
directionView.block(utils::blocks::fixed_central_density_phase.central_value_term);
validate_finite_scalar(
borderDirection(0), "The central-density bordered root received a non-finite border direction."
);
mfem::Vector physicalDirection(const_cast<mfem::real_t *>(direction.GetData()), m_physicalOperator->Width());
mfem::Vector physicalAction;
m_physicalOperator->Mult(physicalDirection, physicalAction);
action.SetSize(Height());
action = 0.0;
mfem::Vector physicalDestination(action.GetData(), physicalAction.Size());
physicalDestination = physicalAction;
const auto actionView = m_rootManifest.residualView(action);
mfem::Vector enthalpyAction = actionView.block(utils::blocks::enthalpy_field.specific_term);
mfem::Vector phaseAction = actionView.block(utils::blocks::fixed_central_density_phase.central_value_term);
m_phaseConstraint.ApplyJacobian(
{.enthalpyVariation = enthalpyDirection, .borderVariation = borderDirection(0)},
{.enthalpyAction = enthalpyAction, .phaseAction = phaseAction}
);
}
bool PreparedCentralDensityStellarEquilibriumOperator::IsPrepared() const noexcept {
return m_isPrepared && m_physicalOperator->IsPrepared() && m_phaseConstraint.IsPrepared();
}
const CentralDensityStellarEquilibriumLayout &
PreparedCentralDensityStellarEquilibriumOperator::GetLayout() const noexcept {
return m_rootManifest.layout();
}
const CentralDensityStellarEquilibriumRootManifest &
PreparedCentralDensityStellarEquilibriumOperator::GetRootManifest() const noexcept {
return m_rootManifest;
}
const PreparedStellarEquilibriumOperator &
PreparedCentralDensityStellarEquilibriumOperator::GetPhysicalOperator() const noexcept {
return *m_physicalOperator;
}
const PreparedCentralDensityConstraint &
PreparedCentralDensityStellarEquilibriumOperator::GetCentralDensityConstraint() const noexcept {
return m_phaseConstraint;
}
RootConstraintReport PreparedCentralDensityStellarEquilibriumOperator::GetFixedMassReport() const {
VerifyPrepared();
return m_physicalOperator->GetFixedMassReport();
}
CentralDensityConstraintReport PreparedCentralDensityStellarEquilibriumOperator::GetCentralDensityReport() const {
VerifyPrepared();
return m_phaseConstraint.GetConstraintReport();
}
void PreparedCentralDensityStellarEquilibriumOperator::VerifyPrepared() const {
MFEM_VERIFY(IsPrepared(), "The central-density bordered root must be prepared before application.");
}
} // namespace mean_field::operators

View File

@@ -8,6 +8,8 @@ import :operators.kernels.gravity_displacement_force;
import :operators.prepared_gravity_displacement_force;
namespace {
using DomainSchema = mean_field::utils::domain::CoreEnvelopeVacuumDomainSchema;
[[nodiscard]] bool relevant_revisions_match(
const mean_field::operators::context::gravity_field::GravityFieldRevisions &left,
const mean_field::operators::context::gravity_field::GravityFieldRevisions &right
@@ -15,6 +17,71 @@ namespace {
return left.discretization == right.discretization && left.displacement == right.displacement &&
left.density == right.density && left.gravity_gradient == right.gravity_gradient;
}
[[nodiscard]] bool is_vacuum_attribute(const int attribute) {
return DomainSchema::template attribute_belongs_to<mean_field::utils::domain::Vacuum>(attribute);
}
void true_to_local(
const mfem::ParFiniteElementSpace &finiteElementSpace,
const mfem::Vector &trueVector,
mfem::Vector &localVector
) {
localVector.SetSize(finiteElementSpace.GetVSize());
const mfem::Operator *prolongation = finiteElementSpace.GetProlongationMatrix();
if (prolongation != nullptr) {
prolongation->Mult(trueVector, localVector);
} else {
localVector = trueVector;
}
}
void local_to_true(
const mfem::ParFiniteElementSpace &finiteElementSpace,
const mfem::Vector &localVector,
mfem::Vector &trueVector
) {
trueVector.SetSize(finiteElementSpace.GetTrueVSize());
trueVector = 0.0;
const mfem::Operator *prolongation = finiteElementSpace.GetProlongationMatrix();
if (prolongation != nullptr) {
prolongation->MultTranspose(localVector, trueVector);
} else {
trueVector = localVector;
}
}
[[nodiscard]] int vector_dof_index(
const mfem::Ordering::Type ordering,
const int scalarDof,
const int component,
const int scalarDofCount,
const int dimension
) {
if (ordering == mfem::Ordering::byNODES) {
return scalarDof + component * scalarDofCount;
}
MFEM_VERIFY(ordering == mfem::Ordering::byVDIM, "Unsupported displacement ordering.");
return scalarDof * dimension + component;
}
[[nodiscard]] const mfem::IntegrationRule &get_gravity_force_rule(
const mean_field::fem::FEM &f,
const mfem::ElementTransformation &transformation
) {
using DisplacementField = mean_field::field::Field<mean_field::field::Displacement>;
const mean_field::quadrature::Query query =
DisplacementField::make_query<mean_field::field::Displacement::Form::GravityForce>(
mean_field::quadrature::QuadratureRole::discretization, transformation.OrderW(), {},
mean_field::utils::DOMAINS::STELLAR, mean_field::quadrature::MappingKind::general
);
const mean_field::quadrature::MfemRule rule = f.quadratureFactory->get(query, transformation.GetGeometryType());
MFEM_VERIFY(
rule.integration_rule != nullptr,
"The quadrature policy did not return a gravity-displacement-force integration rule."
);
return *rule.integration_rule;
}
} // namespace
namespace mean_field::operators {
@@ -41,6 +108,122 @@ namespace mean_field::operators {
);
}
void PreparedGravityDisplacementForceOperator::PrepareElementData() {
m_elements.clear();
m_elements.reserve(m_fem.mesh->GetNE());
mfem::Vector baseDensityLocal;
mfem::Vector baseGravityGradientLocal;
mfem::Vector baseDisplacementLocal;
true_to_local(*m_fem.densityFes, m_gravityContext.GetDensityTrue(), baseDensityLocal);
true_to_local(*m_fem.gravityFluxFes, m_gravityContext.GetGravityGradientTrue(), baseGravityGradientLocal);
true_to_local(
*m_fem.displacementFes, m_gravityContext.GetGeometryContext().GetDisplacementTrue(), baseDisplacementLocal
);
mapping::DomainMapper::Workspace workspace(m_domainMapper.GetDimension());
mapping::VolumeMappingContext mappingContext;
mfem::Array<int> compactificationDofs;
mfem::Vector elementBaseDensity;
mfem::Vector elementBaseGravityGradient;
mfem::Vector elementBaseDisplacement;
mfem::Vector elementCompactification;
mfem::Vector densityShape;
mfem::Vector baseGravityReferenceValue;
mfem::DenseMatrix gravityGradientShape;
const int dimension = m_domainMapper.GetDimension();
for (int elementId = 0; elementId < m_fem.mesh->GetNE(); ++elementId) {
mfem::ElementTransformation *transformation = m_fem.mesh->GetElementTransformation(elementId);
MFEM_VERIFY(transformation != nullptr, "Prepared gravity force received a null transformation.");
if (is_vacuum_attribute(transformation->Attribute)) {
continue;
}
m_elements.emplace_back();
ElementPAData &data = m_elements.back();
data.elementId = elementId;
data.densityDofTransformation = m_fem.densityFes->GetElementDofs(elementId, data.densityDofs);
data.gravityGradientDofTransformation =
m_fem.gravityFluxFes->GetElementVDofs(elementId, data.gravityGradientDofs);
data.displacementDofTransformation =
m_fem.displacementFes->GetElementVDofs(elementId, data.displacementDofs);
mfem::DofTransformation *compactificationDofTransformation =
m_fem.compactificationFes->GetElementDofs(elementId, compactificationDofs);
baseDensityLocal.GetSubVector(data.densityDofs, elementBaseDensity);
baseGravityGradientLocal.GetSubVector(data.gravityGradientDofs, elementBaseGravityGradient);
baseDisplacementLocal.GetSubVector(data.displacementDofs, elementBaseDisplacement);
m_fem.compactificationCoordinate->GetSubVector(compactificationDofs, elementCompactification);
if (data.densityDofTransformation != nullptr) {
data.densityDofTransformation->InvTransformPrimal(elementBaseDensity);
}
if (data.gravityGradientDofTransformation != nullptr) {
data.gravityGradientDofTransformation->InvTransformPrimal(elementBaseGravityGradient);
}
if (data.displacementDofTransformation != nullptr) {
data.displacementDofTransformation->InvTransformPrimal(elementBaseDisplacement);
}
if (compactificationDofTransformation != nullptr) {
compactificationDofTransformation->InvTransformPrimal(elementCompactification);
}
const mfem::FiniteElement &densityElement = *m_fem.densityFes->GetFE(elementId);
const mfem::FiniteElement &gravityGradientElement = *m_fem.gravityFluxFes->GetFE(elementId);
const mfem::FiniteElement &displacementElement = *m_fem.displacementFes->GetFE(elementId);
const mfem::FiniteElement &compactificationElement = *m_fem.compactificationFes->GetFE(elementId);
data.integrationRule = &get_gravity_force_rule(m_fem, *transformation);
const mapping::ElementDisplacementData displacementData =
mapping::ElementDisplacementDataFromElementVDofs(displacementElement, elementBaseDisplacement);
const mapping::ElementCompactificationData compactificationData(
compactificationElement, elementCompactification
);
const mapping::ElementMappingData mappingData{
.displacement = displacementData, .compactification = compactificationData
};
const int quadraturePointCount = data.integrationRule->GetNPoints();
data.mappingJacobians.SetSize(quadraturePointCount, dimension * dimension);
data.inverseMeshJacobians.SetSize(quadraturePointCount, dimension * dimension);
data.baseGravityReferenceValues.SetSize(quadraturePointCount, dimension);
data.baseDensityValues.SetSize(quadraturePointCount);
data.referenceWeights.SetSize(quadraturePointCount);
densityShape.SetSize(densityElement.GetDof());
gravityGradientShape.SetSize(gravityGradientElement.GetDof(), dimension);
baseGravityReferenceValue.SetSize(dimension);
for (int quadraturePoint = 0; quadraturePoint < quadraturePointCount; ++quadraturePoint) {
const mfem::IntegrationPoint &integrationPoint = data.integrationRule->IntPoint(quadraturePoint);
const mapping::MappingStatus status = m_domainMapper.EvaluateVolume(
mappingData, *transformation, integrationPoint, workspace, mappingContext
);
MFEM_VERIFY(
status == mapping::MappingStatus::valid && !mappingContext.mapping.compactified,
"Prepared gravity force encountered an invalid stellar mapping."
);
densityElement.CalcShape(integrationPoint, densityShape);
gravityGradientElement.CalcVShape(*transformation, gravityGradientShape);
gravityGradientShape.MultTranspose(elementBaseGravityGradient, baseGravityReferenceValue);
data.baseDensityValues(quadraturePoint) = elementBaseDensity * densityShape;
data.referenceWeights(quadraturePoint) = integrationPoint.weight * transformation->Weight();
const mfem::DenseMatrix &inverseMeshJacobian = transformation->InverseJacobian();
for (int row = 0; row < dimension; ++row) {
data.baseGravityReferenceValues(quadraturePoint, row) = baseGravityReferenceValue(row);
for (int column = 0; column < dimension; ++column) {
const int entry = row * dimension + column;
data.mappingJacobians(quadraturePoint, entry) =
mappingContext.mapping.mapping_jacobian(row, column);
data.inverseMeshJacobians(quadraturePoint, entry) = inverseMeshJacobian(row, column);
}
}
}
}
}
PreparedGravityDisplacementForceReport PreparedGravityDisplacementForceOperator::Prepare() {
MFEM_VERIFY(
m_gravityContext.IsPrepared(), "PreparedGravityDisplacementForceOperator requires the shared "
@@ -59,6 +242,7 @@ namespace mean_field::operators {
);
m_cachedResidual.SetSize(m_gravityContext.GetDisplacementMap().reduced_size());
m_gravityContext.GetDisplacementMap().gather(m_actionTrue, m_cachedResidual);
PrepareElementData();
m_preparedRevisions = requestedRevisions;
++m_residualPreparationCount;
@@ -130,6 +314,117 @@ namespace mean_field::operators {
++m_displacementJacobianStatistics.applications;
}
void PreparedGravityDisplacementForceOperator::ApplyPreparedCompleteJacobianActionTrue(
const mfem::Vector &densityVariationTrue,
const mfem::Vector &displacementVariationTrue,
const mfem::Vector &gravityGradientVariationTrue,
mfem::Vector &actionTrue
) const {
true_to_local(*m_fem.densityFes, densityVariationTrue, m_densityVariationLocal);
true_to_local(*m_fem.gravityFluxFes, gravityGradientVariationTrue, m_gravityGradientVariationLocal);
true_to_local(*m_fem.displacementFes, displacementVariationTrue, m_displacementVariationLocal);
m_localAction.SetSize(m_fem.displacementFes->GetVSize());
m_localAction = 0.0;
const int dimension = m_domainMapper.GetDimension();
const mfem::Ordering::Type ordering = m_fem.displacementFes->GetOrdering();
for (const ElementPAData &data : m_elements) {
MFEM_VERIFY(data.integrationRule != nullptr, "Prepared gravity force has no integration rule.");
m_densityVariationLocal.GetSubVector(data.densityDofs, m_elementDensityVariation);
m_gravityGradientVariationLocal.GetSubVector(data.gravityGradientDofs, m_elementGravityGradientVariation);
m_displacementVariationLocal.GetSubVector(data.displacementDofs, m_elementDisplacementVariation);
if (data.densityDofTransformation != nullptr) {
data.densityDofTransformation->InvTransformPrimal(m_elementDensityVariation);
}
if (data.gravityGradientDofTransformation != nullptr) {
data.gravityGradientDofTransformation->InvTransformPrimal(m_elementGravityGradientVariation);
}
if (data.displacementDofTransformation != nullptr) {
data.displacementDofTransformation->InvTransformPrimal(m_elementDisplacementVariation);
}
const mfem::FiniteElement &densityElement = *m_fem.densityFes->GetFE(data.elementId);
const mfem::FiniteElement &gravityGradientElement = *m_fem.gravityFluxFes->GetFE(data.elementId);
const mfem::FiniteElement &displacementElement = *m_fem.displacementFes->GetFE(data.elementId);
mfem::ElementTransformation *transformation = m_fem.mesh->GetElementTransformation(data.elementId);
MFEM_VERIFY(transformation != nullptr, "Prepared gravity force received a null transformation.");
const mapping::ElementDisplacementData directionData =
mapping::ElementDisplacementDataFromElementVDofs(displacementElement, m_elementDisplacementVariation);
const mfem::DenseMatrix &directionDofs = directionData.GetDofMatrix();
const int scalarDisplacementDofCount = displacementElement.GetDof();
m_densityShape.SetSize(densityElement.GetDof());
m_displacementShape.SetSize(scalarDisplacementDofCount);
m_gravityGradientShape.SetSize(gravityGradientElement.GetDof(), dimension);
m_referenceDisplacementDShape.SetSize(scalarDisplacementDofCount, dimension);
m_referenceDisplacementJacobian.SetSize(dimension, dimension);
m_displacementJacobianVariation.SetSize(dimension, dimension);
m_mappingJacobian.SetSize(dimension, dimension);
m_inverseMeshJacobian.SetSize(dimension, dimension);
m_baseGravityReferenceValue.SetSize(dimension);
m_gravityVariationReferenceValue.SetSize(dimension);
m_mappedBaseGravity.SetSize(dimension);
m_mappedGravityVariation.SetSize(dimension);
m_mappedGeometryVariation.SetSize(dimension);
m_forceValue.SetSize(dimension);
m_elementAction.SetSize(data.displacementDofs.Size());
m_elementAction = 0.0;
for (int quadraturePoint = 0; quadraturePoint < data.integrationRule->GetNPoints(); ++quadraturePoint) {
const mfem::IntegrationPoint &integrationPoint = data.integrationRule->IntPoint(quadraturePoint);
densityElement.CalcShape(integrationPoint, m_densityShape);
displacementElement.CalcShape(integrationPoint, m_displacementShape);
displacementElement.CalcDShape(integrationPoint, m_referenceDisplacementDShape);
mfem::MultAtB(directionDofs, m_referenceDisplacementDShape, m_referenceDisplacementJacobian);
transformation->SetIntPoint(&integrationPoint);
gravityGradientElement.CalcVShape(*transformation, m_gravityGradientShape);
m_gravityGradientShape.MultTranspose(
m_elementGravityGradientVariation, m_gravityVariationReferenceValue
);
for (int row = 0; row < dimension; ++row) {
m_baseGravityReferenceValue(row) = data.baseGravityReferenceValues(quadraturePoint, row);
for (int column = 0; column < dimension; ++column) {
const int entry = row * dimension + column;
m_mappingJacobian(row, column) = data.mappingJacobians(quadraturePoint, entry);
m_inverseMeshJacobian(row, column) = data.inverseMeshJacobians(quadraturePoint, entry);
}
}
mfem::Mult(m_referenceDisplacementJacobian, m_inverseMeshJacobian, m_displacementJacobianVariation);
m_mappingJacobian.Mult(m_baseGravityReferenceValue, m_mappedBaseGravity);
m_mappingJacobian.Mult(m_gravityVariationReferenceValue, m_mappedGravityVariation);
m_displacementJacobianVariation.Mult(m_baseGravityReferenceValue, m_mappedGeometryVariation);
const double densityVariationValue = m_elementDensityVariation * m_densityShape;
const double baseDensityValue = data.baseDensityValues(quadraturePoint);
m_forceValue = 0.0;
m_forceValue.Add(densityVariationValue, m_mappedBaseGravity);
m_forceValue.Add(baseDensityValue, m_mappedGravityVariation);
m_forceValue.Add(baseDensityValue, m_mappedGeometryVariation);
m_forceValue *= data.referenceWeights(quadraturePoint);
for (int scalarDof = 0; scalarDof < scalarDisplacementDofCount; ++scalarDof) {
for (int component = 0; component < dimension; ++component) {
const int vectorDof =
vector_dof_index(ordering, scalarDof, component, scalarDisplacementDofCount, dimension);
m_elementAction(vectorDof) += m_displacementShape(scalarDof) * m_forceValue(component);
}
}
}
if (data.displacementDofTransformation != nullptr) {
data.displacementDofTransformation->TransformDual(m_elementAction);
}
m_localAction.AddElementVector(data.displacementDofs, m_elementAction);
}
local_to_true(*m_fem.displacementFes, m_localAction, actionTrue);
}
void PreparedGravityDisplacementForceOperator::ApplyCompleteJacobianAction(
const mfem::Vector &densityVariation,
const mfem::Vector &displacementVariation,
@@ -145,10 +440,8 @@ namespace mean_field::operators {
m_gravityContext.GetGravityGradientMap().scatter(gravityGradientVariation, m_gravityGradientVariationTrue);
m_gravityContext.GetDisplacementMap().scatter(displacementVariation, m_displacementVariationTrue);
kernels::apply_gravity_displacement_force_complete_action(
m_fem, m_domainMapper, m_gravityContext.GetDensityTrue(), m_densityVariationTrue,
m_gravityContext.GetGravityGradientTrue(), m_gravityGradientVariationTrue, m_displacementVariationTrue,
m_gravityContext.GetGeometryContext().GetDisplacementTrue(), m_actionTrue
ApplyPreparedCompleteJacobianActionTrue(
m_densityVariationTrue, m_displacementVariationTrue, m_gravityGradientVariationTrue, m_actionTrue
);
action.SetSize(m_gravityContext.GetDisplacementMap().reduced_size());
m_gravityContext.GetDisplacementMap().gather(m_actionTrue, action);

View File

@@ -174,9 +174,15 @@ namespace {
"non-finite mapping determinant."
);
m_inverse_element_jacobian = mapping_context.quadrature.J_inv;
return 4.0 * std::numbers::pi * mean_field::utils::G * mapping_determinant;
}
[[nodiscard]] const mfem::DenseMatrix &GetInverseElementJacobian() const noexcept {
return m_inverse_element_jacobian;
}
private:
void LoadElement(const int element_id) {
if (element_id == m_cached_element_id) {
@@ -230,6 +236,7 @@ namespace {
std::unique_ptr<mean_field::mapping::ElementCompactificationData> m_compactification_data;
mean_field::mapping::DomainMapper::Workspace m_workspace;
mfem::DenseMatrix m_inverse_element_jacobian;
int m_cached_element_id{-1};
};
} // namespace
@@ -336,6 +343,9 @@ namespace mean_field::operators {
data.potential_dof_transformation =
m_fem.gravityPotentialFes->GetElementDofs(element_id, data.potential_dofs);
data.displacement_dof_transformation =
m_fem.displacementFes->GetElementVDofs(element_id, data.displacement_dofs);
const mfem::FiniteElement &density_element = *m_fem.densityFes->GetFE(element_id);
const mfem::FiniteElement &potential_element = *m_fem.gravityPotentialFes->GetFE(element_id);
@@ -344,6 +354,7 @@ namespace mean_field::operators {
const mfem::IntegrationRule &integration_rule =
get_source_rule(m_fem, density_element, potential_element, transformation);
data.integration_rule = &integration_rule;
const int quadrature_point_count = integration_rule.GetNPoints();
@@ -355,6 +366,9 @@ namespace mean_field::operators {
data.potential_basis.SetSize(quadrature_point_count, potential_dof_count);
const int dimension = m_fem.mesh->Dimension();
data.inverse_element_jacobians.SetSize(quadrature_point_count, dimension * dimension);
data.quadrature_data.SetSize(quadrature_point_count);
mfem::Vector density_shape(density_dof_count);
@@ -381,6 +395,14 @@ namespace mean_field::operators {
const double coefficient_value = source_coefficient.Eval(transformation, integration_point);
const mfem::DenseMatrix &inverse_element_jacobian = source_coefficient.GetInverseElementJacobian();
for (int row = 0; row < dimension; ++row) {
for (int column = 0; column < dimension; ++column) {
data.inverse_element_jacobians(quadrature_point, row * dimension + column) =
inverse_element_jacobian(row, column);
}
}
transformation.SetIntPoint(&integration_point);
const double quadrature_value = integration_point.weight * transformation.Weight() * coefficient_value;
@@ -463,6 +485,98 @@ namespace mean_field::operators {
m_potential_map.gather(m_action_true, action);
}
void PreparedMappedGravitySourceOperator::MultDisplacementVariationTrue(
const mfem::Vector &densityTrue,
const mfem::Vector &displacementVariationTrue,
mfem::Vector &actionVariationTrue
) const {
MFEM_VERIFY(
m_is_prepared,
"PreparedMappedGravitySourceOperator must be prepared before applying a displacement variation."
);
MFEM_VERIFY(
densityTrue.Size() == m_fem.densityFes->GetTrueVSize(), "The full density vector has the wrong size."
);
MFEM_VERIFY(
displacementVariationTrue.Size() == m_fem.displacementFes->GetTrueVSize(),
"The full displacement variation has the wrong size."
);
true_to_local(*m_fem.densityFes, densityTrue, m_density_local);
true_to_local(*m_fem.displacementFes, displacementVariationTrue, m_displacement_variation_local);
m_local_variation_action.SetSize(m_fem.gravityPotentialFes->GetVSize());
m_local_variation_action = 0.0;
const int dimension = m_fem.mesh->Dimension();
for (const ElementPAData &data : m_elements) {
MFEM_VERIFY(
data.integration_rule != nullptr,
"Prepared gravity source displacement variation has no integration rule."
);
m_density_local.GetSubVector(data.density_dofs, m_element_density);
m_displacement_variation_local.GetSubVector(data.displacement_dofs, m_element_displacement_variation);
if (data.density_dof_transformation != nullptr) {
data.density_dof_transformation->InvTransformPrimal(m_element_density);
}
if (data.displacement_dof_transformation != nullptr) {
data.displacement_dof_transformation->InvTransformPrimal(m_element_displacement_variation);
}
const mfem::FiniteElement &displacement_element = *m_fem.displacementFes->GetFE(data.element_id);
const mapping::ElementDisplacementData direction_data = mapping::ElementDisplacementDataFromElementVDofs(
displacement_element, m_element_displacement_variation
);
const mfem::DenseMatrix &direction_dofs = direction_data.GetDofMatrix();
MFEM_VERIFY(
data.inverse_element_jacobians.Height() == data.integration_rule->GetNPoints() &&
data.inverse_element_jacobians.Width() == dimension * dimension,
"Prepared gravity source inverse-Jacobian data has an incompatible size."
);
m_reference_displacement_dshape.SetSize(displacement_element.GetDof(), dimension);
m_reference_displacement_jacobian.SetSize(dimension, dimension);
m_quadrature_variation_action.SetSize(data.integration_rule->GetNPoints());
data.density_basis.Mult(m_element_density, m_quadrature_variation_action);
for (int quadrature_point = 0; quadrature_point < data.integration_rule->GetNPoints(); ++quadrature_point) {
const mfem::IntegrationPoint &integration_point = data.integration_rule->IntPoint(quadrature_point);
displacement_element.CalcDShape(integration_point, m_reference_displacement_dshape);
mfem::MultAtB(direction_dofs, m_reference_displacement_dshape, m_reference_displacement_jacobian);
double logarithmic_jacobian_variation{0.0};
for (int row = 0; row < dimension; ++row) {
for (int column = 0; column < dimension; ++column) {
logarithmic_jacobian_variation +=
data.inverse_element_jacobians(quadrature_point, row * dimension + column) *
m_reference_displacement_jacobian(column, row);
}
}
m_quadrature_variation_action(quadrature_point) *=
data.quadrature_data(quadrature_point) * logarithmic_jacobian_variation;
MFEM_VERIFY(
std::isfinite(m_quadrature_variation_action(quadrature_point)),
"Prepared gravity source displacement variation encountered a non-finite quadrature value."
);
}
m_element_variation_action.SetSize(data.potential_dofs.Size());
data.potential_basis.MultTranspose(m_quadrature_variation_action, m_element_variation_action);
if (data.potential_dof_transformation != nullptr) {
data.potential_dof_transformation->TransformDual(m_element_variation_action);
}
m_local_variation_action.AddElementVector(data.potential_dofs, m_element_variation_action);
}
local_to_true(*m_fem.gravityPotentialFes, m_local_variation_action, actionVariationTrue);
}
void PreparedMappedGravitySourceOperator::MultTranspose(
const mfem::Vector &potential,
mfem::Vector &action

View File

@@ -35,6 +35,128 @@ namespace {
}
}
void local_to_true(
const mfem::ParFiniteElementSpace &finite_element_space,
const mfem::Vector &local_vector,
mfem::Vector &true_vector
) {
true_vector.SetSize(finite_element_space.GetTrueVSize());
true_vector = 0.0;
const mfem::Operator *prolongation = finite_element_space.GetProlongationMatrix();
if (prolongation != nullptr) {
prolongation->MultTranspose(local_vector, true_vector);
} else {
true_vector = local_vector;
}
}
mean_field::quadrature::MappingKind get_mapping_kind(
const mean_field::mapping::DomainMapper &domain_mapper,
const mfem::ElementTransformation &transformation
) {
return domain_mapper.IsCompactifiedElement(transformation) ? mean_field::quadrature::MappingKind::kelvin
: mean_field::quadrature::MappingKind::general;
}
const mfem::IntegrationRule &get_hdiv_mass_rule(
const mean_field::fem::FEM &f,
const mean_field::mapping::DomainMapper &domain_mapper,
const mfem::FiniteElement &element,
const mfem::ElementTransformation &transformation
) {
using GravityField = mean_field::field::Field<mean_field::field::Gravity>;
const mean_field::quadrature::Query query =
GravityField::make_query<mean_field::field::Gravity::Form::HDivMass>(
mean_field::quadrature::QuadratureRole::discretization, transformation.OrderW(), {},
mean_field::utils::DOMAINS::ALL, get_mapping_kind(domain_mapper, transformation)
);
const auto resolution = f.quadratureFactory->get(query, transformation.GetGeometryType());
MFEM_VERIFY(
resolution.integration_rule != nullptr,
"The quadrature policy did not return an H(div) mass integration rule."
);
return *resolution.integration_rule;
}
int frozen_mapping_width(const int dimension) {
return 3 * dimension + 4 * dimension * dimension + 3;
}
void freeze_mapping_context(
const mean_field::mapping::VolumeMappingContext &context,
const int quadrature_point,
mfem::DenseMatrix &data
) {
const int dimension = context.mapping.reference_position.Size();
const int displacement_jacobian_start = 3 * dimension;
const int mapping_jacobian_start = displacement_jacobian_start + dimension * dimension;
const int inverse_mapping_start = mapping_jacobian_start + dimension * dimension;
const int inverse_element_start = inverse_mapping_start + dimension * dimension;
const int scalar_start = inverse_element_start + dimension * dimension;
for (int component = 0; component < dimension; ++component) {
data(quadrature_point, component) = context.mapping.reference_position(component);
data(quadrature_point, dimension + component) = context.mapping.displaced_position(component);
data(quadrature_point, 2 * dimension + component) = context.mapping.physical_position(component);
}
for (int row = 0; row < dimension; ++row) {
for (int column = 0; column < dimension; ++column) {
const int entry = row * dimension + column;
data(quadrature_point, displacement_jacobian_start + entry) =
context.mapping.displacement_jacobian(row, column);
data(quadrature_point, mapping_jacobian_start + entry) = context.mapping.mapping_jacobian(row, column);
data(quadrature_point, inverse_mapping_start + entry) =
context.mapping.inverse_mapping_jacobian(row, column);
data(quadrature_point, inverse_element_start + entry) = context.quadrature.J_inv(row, column);
}
}
data(quadrature_point, scalar_start) = context.mapping.mapping_determinant;
data(quadrature_point, scalar_start + 1) = context.quadrature.weight;
data(quadrature_point, scalar_start + 2) = context.mapping.compactified ? 1.0 : 0.0;
}
void thaw_mapping_context(
const mfem::DenseMatrix &data,
const int quadrature_point,
const int dimension,
mean_field::mapping::VolumeMappingContext &context
) {
const int displacement_jacobian_start = 3 * dimension;
const int mapping_jacobian_start = displacement_jacobian_start + dimension * dimension;
const int inverse_mapping_start = mapping_jacobian_start + dimension * dimension;
const int inverse_element_start = inverse_mapping_start + dimension * dimension;
const int scalar_start = inverse_element_start + dimension * dimension;
context.mapping.reference_position.SetSize(dimension);
context.mapping.displaced_position.SetSize(dimension);
context.mapping.physical_position.SetSize(dimension);
context.mapping.displacement_jacobian.SetSize(dimension, dimension);
context.mapping.mapping_jacobian.SetSize(dimension, dimension);
context.mapping.inverse_mapping_jacobian.SetSize(dimension, dimension);
context.quadrature.J_inv.SetSize(dimension, dimension);
for (int component = 0; component < dimension; ++component) {
context.mapping.reference_position(component) = data(quadrature_point, component);
context.mapping.displaced_position(component) = data(quadrature_point, dimension + component);
context.mapping.physical_position(component) = data(quadrature_point, 2 * dimension + component);
}
for (int row = 0; row < dimension; ++row) {
for (int column = 0; column < dimension; ++column) {
const int entry = row * dimension + column;
context.mapping.displacement_jacobian(row, column) =
data(quadrature_point, displacement_jacobian_start + entry);
context.mapping.mapping_jacobian(row, column) = data(quadrature_point, mapping_jacobian_start + entry);
context.mapping.inverse_mapping_jacobian(row, column) =
data(quadrature_point, inverse_mapping_start + entry);
context.quadrature.J_inv(row, column) = data(quadrature_point, inverse_element_start + entry);
}
}
context.mapping.mapping_determinant = data(quadrature_point, scalar_start);
context.mapping.compactified = data(quadrature_point, scalar_start + 2) != 0.0;
context.quadrature.detJ = context.mapping.mapping_determinant;
context.quadrature.weight = data(quadrature_point, scalar_start + 1);
}
int find_representative_element(
const mean_field::fem::FEM &f,
const mfem::Array<int> &marker
@@ -238,7 +360,8 @@ namespace mean_field::operators {
field::make_field_dof_map<
field::Displacement,
DomainSchema>(*f.displacementFes)
) {
),
m_variationWorkspace(domain_mapper.GetDimension()) {
MFEM_VERIFY(f.mesh != nullptr, "PreparedMappedHDivMassOperator requires a mesh.");
MFEM_VERIFY(
f.gravityFluxFes != nullptr, "PreparedMappedHDivMassOperator requires the "
@@ -285,6 +408,78 @@ namespace mean_field::operators {
validate_uniform_domain_discretization(f, m_vacuum_marker, vacuum_element_id);
}
void PreparedMappedHDivMassOperator::PrepareVariationData() {
m_variationElements.clear();
m_variationElements.reserve(m_fem.mesh->GetNE());
mfem::Vector displacementLocal;
true_to_local(*m_fem.displacementFes, m_displacement_true, displacementLocal);
mfem::Vector elementDisplacement;
mfem::Vector elementCompactification;
mapping::VolumeMappingContext mappingContext;
for (int elementId = 0; elementId < m_fem.mesh->GetNE(); ++elementId) {
m_variationElements.emplace_back();
ElementVariationData &data = m_variationElements.back();
data.elementId = elementId;
data.gravityGradientDofTransformation =
m_fem.gravityFluxFes->GetElementVDofs(elementId, data.gravityGradientDofs);
data.displacementDofTransformation =
m_fem.displacementFes->GetElementVDofs(elementId, data.displacementDofs);
mfem::DofTransformation *compactificationDofTransformation =
m_fem.compactificationFes->GetElementDofs(elementId, data.compactificationDofs);
displacementLocal.GetSubVector(data.displacementDofs, elementDisplacement);
m_fem.compactificationCoordinate->GetSubVector(data.compactificationDofs, elementCompactification);
if (data.displacementDofTransformation != nullptr) {
data.displacementDofTransformation->InvTransformPrimal(elementDisplacement);
}
if (compactificationDofTransformation != nullptr) {
compactificationDofTransformation->InvTransformPrimal(elementCompactification);
}
data.baseDisplacement = elementDisplacement;
data.compactification = elementCompactification;
const mfem::FiniteElement &gravityGradientElement = *m_fem.gravityFluxFes->GetFE(elementId);
const mfem::FiniteElement &displacementElement = *m_fem.displacementFes->GetFE(elementId);
const mfem::FiniteElement &compactificationElement = *m_fem.compactificationFes->GetFE(elementId);
mfem::ElementTransformation *transformation = m_fem.mesh->GetElementTransformation(elementId);
MFEM_VERIFY(
transformation != nullptr, "Prepared H(div) variation data received a null element transformation."
);
data.integrationRule = &get_hdiv_mass_rule(m_fem, m_domain_mapper, gravityGradientElement, *transformation);
data.frozenMappingData.SetSize(
data.integrationRule->GetNPoints(), frozen_mapping_width(m_domain_mapper.GetDimension())
);
const mapping::ElementDisplacementData displacementData =
mapping::ElementDisplacementDataFromElementVDofs(displacementElement, data.baseDisplacement);
const mapping::ElementCompactificationData compactificationData(
compactificationElement, data.compactification
);
const mapping::ElementMappingData mappingData{
.displacement = displacementData, .compactification = compactificationData
};
for (int quadraturePoint = 0; quadraturePoint < data.integrationRule->GetNPoints(); ++quadraturePoint) {
const mfem::IntegrationPoint &integrationPoint = data.integrationRule->IntPoint(quadraturePoint);
const mapping::MappingStatus status = m_domain_mapper.EvaluateVolume(
mappingData, *transformation, integrationPoint, m_variationWorkspace, mappingContext
);
MFEM_VERIFY(
status == mapping::MappingStatus::valid,
"Prepared H(div) variation data encountered an invalid mapping. Element: "
<< elementId << ", quadrature point: " << quadraturePoint
<< ", status: " << static_cast<int>(status)
);
freeze_mapping_context(mappingContext, quadraturePoint, data.frozenMappingData);
}
}
}
void PreparedMappedHDivMassOperator::Prepare(const mfem::Vector &displacement) {
MFEM_VERIFY(
displacement.Size() == m_displacement_map.reduced_size(),
@@ -346,6 +541,8 @@ namespace mean_field::operators {
m_stellar_mass_form->Assemble();
m_vacuum_mass_form->Assemble();
PrepareVariationData();
m_is_prepared = true;
++m_preparation_count;
}
@@ -378,6 +575,110 @@ namespace mean_field::operators {
m_flux_map.gather(m_action_true, action);
}
void PreparedMappedHDivMassOperator::MultDisplacementVariationTrue(
const mfem::Vector &gravityGradientTrue,
const mfem::Vector &displacementVariationTrue,
mfem::Vector &actionVariationTrue
) const {
MFEM_VERIFY(
m_is_prepared, "PreparedMappedHDivMassOperator must be prepared before applying a displacement variation."
);
MFEM_VERIFY(
gravityGradientTrue.Size() == m_fem.gravityFluxFes->GetTrueVSize(),
"The full gravity-gradient vector has the wrong size."
);
MFEM_VERIFY(
displacementVariationTrue.Size() == m_fem.displacementFes->GetTrueVSize(),
"The full displacement variation has the wrong size."
);
true_to_local(*m_fem.gravityFluxFes, gravityGradientTrue, m_gravityGradientLocal);
true_to_local(*m_fem.displacementFes, displacementVariationTrue, m_displacementVariationLocal);
m_localVariationAction.SetSize(m_fem.gravityFluxFes->GetVSize());
m_localVariationAction = 0.0;
const int dimension = m_domain_mapper.GetDimension();
for (const ElementVariationData &data : m_variationElements) {
MFEM_VERIFY(
data.integrationRule != nullptr &&
data.frozenMappingData.Height() == data.integrationRule->GetNPoints() &&
data.frozenMappingData.Width() == frozen_mapping_width(dimension),
"Prepared H(div) variation data is incomplete."
);
m_gravityGradientLocal.GetSubVector(data.gravityGradientDofs, m_elementGravityGradient);
m_displacementVariationLocal.GetSubVector(data.displacementDofs, m_elementDisplacementVariation);
if (data.gravityGradientDofTransformation != nullptr) {
data.gravityGradientDofTransformation->InvTransformPrimal(m_elementGravityGradient);
}
if (data.displacementDofTransformation != nullptr) {
data.displacementDofTransformation->InvTransformPrimal(m_elementDisplacementVariation);
}
const mfem::FiniteElement &gravityGradientElement = *m_fem.gravityFluxFes->GetFE(data.elementId);
const mfem::FiniteElement &displacementElement = *m_fem.displacementFes->GetFE(data.elementId);
const mfem::FiniteElement &compactificationElement = *m_fem.compactificationFes->GetFE(data.elementId);
mfem::ElementTransformation *transformation = m_fem.mesh->GetElementTransformation(data.elementId);
MFEM_VERIFY(
transformation != nullptr,
"Prepared H(div) displacement variation received a null element transformation."
);
const mapping::ElementDisplacementData baseDisplacementData =
mapping::ElementDisplacementDataFromElementVDofs(displacementElement, data.baseDisplacement);
const mapping::ElementDisplacementData directionData =
mapping::ElementDisplacementDataFromElementVDofs(displacementElement, m_elementDisplacementVariation);
const mapping::ElementCompactificationData compactificationData(
compactificationElement, data.compactification
);
const mapping::ElementMappingData mappingData{
.displacement = baseDisplacementData, .compactification = compactificationData
};
m_elementVariationAction.SetSize(gravityGradientElement.GetDof());
m_elementVariationAction = 0.0;
m_gravityGradientValue.SetSize(dimension);
m_massTensorVariationAction.SetSize(dimension);
m_gravityGradientShape.SetSize(gravityGradientElement.GetDof(), dimension);
m_massTensorVariation.SetSize(dimension, dimension);
for (int quadraturePoint = 0; quadraturePoint < data.integrationRule->GetNPoints(); ++quadraturePoint) {
const mfem::IntegrationPoint &integrationPoint = data.integrationRule->IntPoint(quadraturePoint);
thaw_mapping_context(data.frozenMappingData, quadraturePoint, dimension, m_baseMappingContext);
const mapping::MappingStatus status = m_domain_mapper.EvaluateVolumeVariation(
mappingData, directionData, *transformation, integrationPoint, m_baseMappingContext,
m_variationWorkspace, m_mappingVariation
);
MFEM_VERIFY(
status == mapping::MappingStatus::valid,
"Prepared H(div) displacement variation encountered an invalid mapping variation. Element: "
<< data.elementId << ", quadrature point: " << quadraturePoint
<< ", status: " << static_cast<int>(status)
);
mapping::ComputeHDivMassTensorVariation(
m_baseMappingContext.mapping, m_mappingVariation.mapping, m_massTensorVariation
);
transformation->SetIntPoint(&integrationPoint);
gravityGradientElement.CalcVShape(*transformation, m_gravityGradientShape);
m_gravityGradientShape.MultTranspose(m_elementGravityGradient, m_gravityGradientValue);
m_massTensorVariation.Mult(m_gravityGradientValue, m_massTensorVariationAction);
const double referenceWeight = integrationPoint.weight * transformation->Weight();
m_gravityGradientShape.AddMult(m_massTensorVariationAction, m_elementVariationAction, referenceWeight);
}
if (data.gravityGradientDofTransformation != nullptr) {
data.gravityGradientDofTransformation->TransformDual(m_elementVariationAction);
}
m_localVariationAction.AddElementVector(data.gravityGradientDofs, m_elementVariationAction);
}
local_to_true(*m_fem.gravityFluxFes, m_localVariationAction, actionVariationTrue);
}
void PreparedMappedHDivMassOperator::AssembleDiagonal(mfem::Vector &diagonal) const {
mfem::Vector true_diagonal;
AssembleTrueDiagonal(true_diagonal);

View File

@@ -992,6 +992,7 @@ namespace mean_field::operators {
mfem::Vector elementDisplacementVariation;
mfem::Vector weightedQuadratureVariation;
mfem::Vector elementAction;
mapping::VolumeMappingVariation variation;
for (const ElementPAData &data : m_elements) {
MFEM_VERIFY(
@@ -1040,11 +1041,7 @@ namespace mean_field::operators {
for (int quadraturePoint = 0; quadraturePoint < quadraturePointCount; ++quadraturePoint) {
const mfem::IntegrationPoint &integrationPoint = data.integrationRule->IntPoint(quadraturePoint);
transformation->SetIntPoint(&integrationPoint);
mapping::VolumeMappingVariation variation;
const mapping::MappingStatus mappingStatus = m_domainMapper.EvaluateVolumeVariation(
const mapping::MappingStatus mappingStatus = m_domainMapper.EvaluateVolumeVariation(
mappingData, directionData, *transformation, integrationPoint,
data.baseMappingContexts[quadraturePoint], workspace, variation
);

View File

@@ -42,6 +42,25 @@ namespace {
}
}
void local_to_true(
const mfem::ParFiniteElementSpace &finiteElementSpace,
const mfem::Vector &localVector,
mfem::Vector &trueVector
) {
MFEM_VERIFY(localVector.Size() == finiteElementSpace.GetVSize(), "Local vector has the wrong size.");
trueVector.SetSize(finiteElementSpace.GetTrueVSize());
trueVector = 0.0;
const mfem::Operator *prolongation = finiteElementSpace.GetProlongationMatrix();
if (prolongation != nullptr) {
prolongation->MultTranspose(localVector, trueVector);
} else {
trueVector = localVector;
}
}
const mfem::IntegrationRule &get_mass_normalization_rule(
const mean_field::fem::FEM &f,
const mfem::FiniteElement &densityElement,
@@ -212,6 +231,13 @@ namespace mean_field::operators {
return report;
}
PreparedMassNormalizationReport PreparedMassNormalizationOperator::Prepare(
const models::CompiledFixedMass &constraint,
const MassNormalizationDependencies &dependencies
) {
return Prepare({.targetMass = constraint.targetMass().value()}, dependencies);
}
void PreparedMassNormalizationOperator::BuildStaticPlan() {
m_elements.clear();
m_elements.reserve(m_fem.mesh->GetNE());
@@ -432,6 +458,7 @@ namespace mean_field::operators {
true_to_local(*m_fem.displacementFes, displacementVariation, displacementVariationLocal);
mapping::DomainMapper::Workspace workspace(m_fem.mesh->Dimension());
mapping::VolumeMappingVariation variation;
mfem::Vector elementDisplacementVariation;
double localAction = 0.0;
@@ -464,8 +491,6 @@ namespace mean_field::operators {
mfem::ElementTransformation *transformation = m_fem.mesh->GetElementTransformation(data.elementId);
for (const QuadraturePointData &point : data.quadraturePoints) {
mapping::VolumeMappingVariation variation;
const mapping::MappingStatus status = m_domainMapper.EvaluateVolumeVariation(
mappingData, directionData, *transformation, point.integrationPoint, point.mappingContext,
workspace, variation
@@ -558,6 +583,137 @@ namespace mean_field::operators {
++m_actionStatistics.completeApplications;
}
void PreparedMassNormalizationOperator::ApplyJacobian(
const FixedMassJacobianInput &input,
mfem::Vector &action
) const {
ApplyCompleteJacobianAction(input.densityVariation, input.displacementVariation, action);
}
void PreparedMassNormalizationOperator::AssembleDensityTransposeAction(
const double residualDual,
mfem::Vector &densityDual
) const {
mfem::Vector localDual(m_fem.densityFes->GetVSize());
localDual = 0.0;
mfem::Vector elementDual;
for (const ElementPAData &data : m_elements) {
elementDual.SetSize(data.densityDofs.Size());
elementDual = 0.0;
for (const QuadraturePointData &point : data.quadraturePoints) {
elementDual.Add(residualDual * point.mappingContext.quadrature.weight, point.densityShape);
}
if (data.densityDofTransformation != nullptr) {
data.densityDofTransformation->TransformDual(elementDual);
}
localDual.AddElementVector(data.densityDofs, elementDual);
}
mfem::Vector trueDual;
local_to_true(*m_fem.densityFes, localDual, trueDual);
densityDual.SetSize(m_gravityContext.GetDensityMap().reduced_size());
m_gravityContext.GetDensityMap().gather(trueDual, densityDual);
}
void PreparedMassNormalizationOperator::AssembleDisplacementTransposeAction(
const double residualDual,
mfem::Vector &displacementDual
) const {
mfem::Vector localDual(m_fem.displacementFes->GetVSize());
localDual = 0.0;
mapping::DomainMapper::Workspace workspace(m_fem.mesh->Dimension());
mapping::VolumeMappingVariation variation;
mfem::Vector elementDirection;
mfem::Vector elementDual;
for (const ElementPAData &data : m_elements) {
const mfem::FiniteElement &displacementElement = *m_fem.displacementFes->GetFE(data.elementId);
const mfem::FiniteElement &compactificationElement = *m_fem.compactificationFes->GetFE(data.elementId);
const mapping::ElementDisplacementData baseDisplacementData =
mapping::ElementDisplacementDataFromElementVDofs(displacementElement, data.baseDisplacement);
const mapping::ElementCompactificationData compactificationData(
compactificationElement, data.compactification
);
const mapping::ElementMappingData mappingData{
.displacement = baseDisplacementData, .compactification = compactificationData
};
mfem::ElementTransformation *transformation = m_fem.mesh->GetElementTransformation(data.elementId);
elementDirection.SetSize(data.displacementDofs.Size());
elementDual.SetSize(data.displacementDofs.Size());
elementDual = 0.0;
for (int elementDof = 0; elementDof < elementDirection.Size(); ++elementDof) {
elementDirection = 0.0;
elementDirection(elementDof) = 1.0;
const mapping::ElementDisplacementData directionData =
mapping::ElementDisplacementDataFromElementVDofs(displacementElement, elementDirection);
double elementDofAction = 0.0;
for (const QuadraturePointData &point : data.quadraturePoints) {
const mapping::MappingStatus status = m_domainMapper.EvaluateVolumeVariation(
mappingData, directionData, *transformation, point.integrationPoint, point.mappingContext,
workspace, variation
);
MFEM_VERIFY(
status == mapping::MappingStatus::valid,
"Stateless mapping variation failed in the mass-normalization transpose action. Element: "
<< data.elementId << ", status: " << static_cast<int>(status)
);
elementDofAction += point.density * variation.weight_variation;
}
elementDual(elementDof) = residualDual * elementDofAction;
}
if (data.displacementDofTransformation != nullptr) {
data.displacementDofTransformation->TransformDual(elementDual);
}
localDual.AddElementVector(data.displacementDofs, elementDual);
}
mfem::Vector trueDual;
local_to_true(*m_fem.displacementFes, localDual, trueDual);
displacementDual.SetSize(m_gravityContext.GetDisplacementMap().reduced_size());
m_gravityContext.GetDisplacementMap().gather(trueDual, displacementDual);
}
void PreparedMassNormalizationOperator::ApplyCompleteJacobianTransposeAction(
const double residualDual,
mfem::Vector &densityDual,
mfem::Vector &displacementDual
) const {
VerifyPrepared();
MFEM_VERIFY(std::isfinite(residualDual), "Mass-normalization transpose action received a non-finite dual.");
AssembleDensityTransposeAction(residualDual, densityDual);
AssembleDisplacementTransposeAction(residualDual, displacementDual);
++m_actionStatistics.transposeApplications;
}
void PreparedMassNormalizationOperator::ApplyJacobianTranspose(
const mfem::Vector &residualDual,
FixedMassJacobianTransposeOutput output
) const {
MFEM_VERIFY(residualDual.Size() == 1, "Fixed-mass transpose action requires one residual dual value.");
ApplyCompleteJacobianTransposeAction(residualDual(0), output.densityDual, output.displacementDual);
}
double PreparedMassNormalizationOperator::GlobalSum(const double localValue) const {
double globalValue = 0.0;
MPI_Allreduce(&localValue, &globalValue, 1, MPI_DOUBLE, MPI_SUM, m_fem.mesh->GetComm());
@@ -720,6 +876,44 @@ namespace mean_field::operators {
action(m_layout.offset(massResidual)) = massAction(0);
}
void PreparedMassNormalizationJacobianOperator::MultTranspose(
const mfem::Vector &residualDual,
mfem::Vector &stateDual
) const {
MFEM_VERIFY(
m_preparedOperator.IsPrepared(),
"Prepared mass-normalization MFEM adapter requires a prepared row operator."
);
MFEM_VERIFY(
residualDual.Size() == Height(),
"Prepared mass-normalization MFEM adapter received a residual dual with the wrong size."
);
using Form = utils::blocks::barotropic_equilibrium_form;
constexpr auto densityValue = utils::blocks::get_value_block<Form>(utils::blocks::density_field.mass_term);
constexpr auto displacementValue =
utils::blocks::get_value_block<Form>(utils::blocks::displacement_field.geometry_term);
constexpr auto massResidual =
utils::blocks::get_residual_block<Form>(utils::blocks::fixed_total_mass_constraint.mass_normalization_term);
mfem::Vector densityDual;
mfem::Vector displacementDual;
m_preparedOperator.ApplyCompleteJacobianTransposeAction(
residualDual(m_layout.offset(massResidual)), densityDual, displacementDual
);
stateDual.SetSize(Width());
stateDual = 0.0;
mfem::Vector densityBlock(stateDual.GetData() + m_layout.offset(densityValue), m_layout.size(densityValue));
densityBlock = densityDual;
mfem::Vector displacementBlock(
stateDual.GetData() + m_layout.offset(displacementValue), m_layout.size(displacementValue)
);
displacementBlock = displacementDual;
}
const MassNormalizationLayout &PreparedMassNormalizationJacobianOperator::GetLayout() const noexcept {
return m_layout;
}

View File

@@ -619,7 +619,7 @@ namespace mean_field::operators {
for (int quadraturePoint = 0; quadraturePoint < quadraturePointCount; ++quadraturePoint) {
const double enthalpy = quadratureEnthalpy(quadraturePoint);
const eos::SpecificEnthalpyValue specificEnthalpy{enthalpy};
const dimensions::SpecificEnthalpyValue specificEnthalpy{enthalpy};
const double pressure =
eos::evaluate<eos::quantity::Pressure>(m_equationOfState, specificEnthalpy).value();
@@ -813,11 +813,13 @@ namespace mean_field::operators {
localAction = 0.0;
mapping::DomainMapper::Workspace workspace(m_fem.mesh->Dimension());
mfem::Vector elementDisplacementVariation;
mfem::Vector elementAction;
mfem::DenseMatrix referenceDisplacementDShape;
mfem::DenseMatrix referenceDisplacementJacobian;
mfem::DenseMatrix inverseElementJacobianVariation;
mfem::DenseMatrix matrixTemporary;
mfem::DenseMatrix physicalTestGradientVariation;
const int dimension = m_fem.mesh->Dimension();
@@ -850,13 +852,11 @@ namespace mean_field::operators {
const mapping::ElementDisplacementData directionData =
mapping::ElementDisplacementDataFromElementVDofs(displacementElement, elementDisplacementVariation);
const mapping::ElementMappingData mappingData{
.displacement = *data.baseDisplacementData, .compactification = *data.compactificationData
};
const int quadraturePointCount = data.integrationRule->GetNPoints();
const int quadraturePointCount = data.integrationRule->GetNPoints();
const int scalarDisplacementDofCount = displacementElement.GetDof();
const int scalarDisplacementDofCount = displacementElement.GetDof();
const mfem::DenseMatrix &directionDofs = directionData.GetDofMatrix();
MFEM_VERIFY(
static_cast<int>(data.baseMappingContexts.size()) == quadraturePointCount &&
@@ -869,31 +869,32 @@ namespace mean_field::operators {
elementAction = 0.0;
referenceDisplacementDShape.SetSize(scalarDisplacementDofCount, dimension);
referenceDisplacementJacobian.SetSize(dimension, dimension);
inverseElementJacobianVariation.SetSize(dimension, dimension);
matrixTemporary.SetSize(dimension, dimension);
physicalTestGradientVariation.SetSize(scalarDisplacementDofCount, dimension);
for (int quadraturePoint = 0; quadraturePoint < quadraturePointCount; ++quadraturePoint) {
const mfem::IntegrationPoint &integrationPoint = data.integrationRule->IntPoint(quadraturePoint);
transformation->SetIntPoint(&integrationPoint);
displacementElement.CalcDShape(integrationPoint, referenceDisplacementDShape);
mfem::MultAtB(directionDofs, referenceDisplacementDShape, referenceDisplacementJacobian);
mapping::VolumeMappingVariation variation;
const mfem::DenseMatrix &inverseElementJacobian =
data.baseMappingContexts[quadraturePoint].quadrature.J_inv;
mfem::Mult(inverseElementJacobian, referenceDisplacementJacobian, matrixTemporary);
const mapping::MappingStatus mappingStatus = m_domainMapper.EvaluateVolumeVariation(
mappingData, directionData, *transformation, integrationPoint,
data.baseMappingContexts[quadraturePoint], workspace, variation
);
double logarithmicJacobianVariation{0.0};
for (int component = 0; component < dimension; ++component) {
logarithmicJacobianVariation += matrixTemporary(component, component);
}
MFEM_VERIFY(
mappingStatus == mapping::MappingStatus::valid,
"Stateless mapping variation failed while applying "
"the prepared pressure-force displacement Jacobian. "
"Element: "
<< data.elementId << ", attribute: " << transformation->Attribute
<< ", quadrature point: " << quadraturePoint << ", status: " << static_cast<int>(mappingStatus)
);
mfem::Mult(matrixTemporary, inverseElementJacobian, inverseElementJacobianVariation);
inverseElementJacobianVariation *= -1.0;
mfem::Mult(
data.referenceTestGradients[quadraturePoint], variation.inverse_element_jacobian_variation,
data.referenceTestGradients[quadraturePoint], inverseElementJacobianVariation,
physicalTestGradientVariation
);
@@ -905,12 +906,13 @@ namespace mean_field::operators {
displacementOrdering, scalarDof, component, scalarDisplacementDofCount, dimension
);
const double gradientWeightVariation =
data.quadratureWeights(quadraturePoint) *
physicalTestGradientVariation(scalarDof, component) +
variation.weight_variation * physicalTestGradient(scalarDof, component);
const double gradientWeightVariation = data.quadratureWeights(quadraturePoint) *
physicalTestGradientVariation(scalarDof, component) +
data.quadratureWeights(quadraturePoint) *
logarithmicJacobianVariation *
physicalTestGradient(scalarDof, component);
const double contribution = data.pressure(quadraturePoint) * gradientWeightVariation;
const double contribution = data.pressure(quadraturePoint) * gradientWeightVariation;
MFEM_VERIFY(
std::isfinite(gradientWeightVariation) && std::isfinite(contribution),

View File

@@ -1,5 +1,6 @@
module;
#include <array>
#include <mfem.hpp>
module mean_field;
@@ -7,6 +8,75 @@ module mean_field;
import :operators.kernels.rotational_displacement_force;
import :operators.prepared_rotational_displacement_force;
namespace {
using DomainSchema = mean_field::utils::domain::CoreEnvelopeVacuumDomainSchema;
[[nodiscard]] bool is_vacuum_attribute(const int attribute) {
return DomainSchema::template attribute_belongs_to<mean_field::utils::domain::Vacuum>(attribute);
}
void true_to_local(
const mfem::ParFiniteElementSpace &finiteElementSpace,
const mfem::Vector &trueVector,
mfem::Vector &localVector
) {
localVector.SetSize(finiteElementSpace.GetVSize());
const mfem::Operator *prolongation = finiteElementSpace.GetProlongationMatrix();
if (prolongation != nullptr) {
prolongation->Mult(trueVector, localVector);
} else {
localVector = trueVector;
}
}
void local_to_true(
const mfem::ParFiniteElementSpace &finiteElementSpace,
const mfem::Vector &localVector,
mfem::Vector &trueVector
) {
trueVector.SetSize(finiteElementSpace.GetTrueVSize());
trueVector = 0.0;
const mfem::Operator *prolongation = finiteElementSpace.GetProlongationMatrix();
if (prolongation != nullptr) {
prolongation->MultTranspose(localVector, trueVector);
} else {
trueVector = localVector;
}
}
[[nodiscard]] int vector_dof_index(
const mfem::Ordering::Type ordering,
const int scalarDof,
const int component,
const int scalarDofCount,
const int dimension
) {
if (ordering == mfem::Ordering::byNODES) {
return scalarDof + component * scalarDofCount;
}
MFEM_VERIFY(ordering == mfem::Ordering::byVDIM, "Unsupported displacement ordering.");
return scalarDof * dimension + component;
}
[[nodiscard]] const mfem::IntegrationRule &get_rotation_force_rule(
const mean_field::fem::FEM &f,
const mfem::ElementTransformation &transformation
) {
using DisplacementField = mean_field::field::Field<mean_field::field::Displacement>;
const mean_field::quadrature::Query query =
DisplacementField::make_query<mean_field::field::Displacement::Form::CentrifugalForce>(
mean_field::quadrature::QuadratureRole::discretization, transformation.OrderW(), std::array<int, 1>{1},
mean_field::utils::DOMAINS::STELLAR, mean_field::quadrature::MappingKind::general
);
const mean_field::quadrature::MfemRule rule = f.quadratureFactory->get(query, transformation.GetGeometryType());
MFEM_VERIFY(
rule.integration_rule != nullptr,
"The quadrature policy did not return a rotational-displacement-force integration rule."
);
return *rule.integration_rule;
}
} // namespace
namespace mean_field::operators {
PreparedRotationalDisplacementForceOperator::PreparedRotationalDisplacementForceOperator(
const fem::FEM &f,
@@ -49,6 +119,105 @@ namespace mean_field::operators {
);
}
void PreparedRotationalDisplacementForceOperator::PrepareElementData() {
MFEM_VERIFY(m_rotation.has_value(), "Prepared rotational force has no frozen rotation state.");
m_elements.clear();
m_elements.reserve(m_fem.mesh->GetNE());
mfem::Vector baseDensityLocal;
mfem::Vector baseDisplacementLocal;
true_to_local(*m_fem.densityFes, m_context.GetBaseDensityTrue(), baseDensityLocal);
true_to_local(*m_fem.displacementFes, m_context.GetDisplacementTrue(), baseDisplacementLocal);
mapping::DomainMapper::Workspace workspace(m_domainMapper.GetDimension());
mapping::VolumeMappingContext mappingContext;
mfem::Array<int> compactificationDofs;
mfem::Vector elementBaseDensity;
mfem::Vector elementBaseDisplacement;
mfem::Vector elementCompactification;
mfem::Vector densityShape;
mfem::Vector potentialGradient;
const int dimension = m_domainMapper.GetDimension();
for (int elementId = 0; elementId < m_fem.mesh->GetNE(); ++elementId) {
mfem::ElementTransformation *transformation = m_fem.mesh->GetElementTransformation(elementId);
MFEM_VERIFY(transformation != nullptr, "Prepared rotational force received a null transformation.");
if (is_vacuum_attribute(transformation->Attribute)) {
continue;
}
m_elements.emplace_back();
ElementPAData &data = m_elements.back();
data.elementId = elementId;
data.densityDofTransformation = m_fem.densityFes->GetElementDofs(elementId, data.densityDofs);
data.displacementDofTransformation =
m_fem.displacementFes->GetElementVDofs(elementId, data.displacementDofs);
mfem::DofTransformation *compactificationDofTransformation =
m_fem.compactificationFes->GetElementDofs(elementId, compactificationDofs);
baseDensityLocal.GetSubVector(data.densityDofs, elementBaseDensity);
baseDisplacementLocal.GetSubVector(data.displacementDofs, elementBaseDisplacement);
m_fem.compactificationCoordinate->GetSubVector(compactificationDofs, elementCompactification);
if (data.densityDofTransformation != nullptr) {
data.densityDofTransformation->InvTransformPrimal(elementBaseDensity);
}
if (data.displacementDofTransformation != nullptr) {
data.displacementDofTransformation->InvTransformPrimal(elementBaseDisplacement);
}
if (compactificationDofTransformation != nullptr) {
compactificationDofTransformation->InvTransformPrimal(elementCompactification);
}
const mfem::FiniteElement &densityElement = *m_fem.densityFes->GetFE(elementId);
const mfem::FiniteElement &displacementElement = *m_fem.displacementFes->GetFE(elementId);
const mfem::FiniteElement &compactificationElement = *m_fem.compactificationFes->GetFE(elementId);
data.integrationRule = &get_rotation_force_rule(m_fem, *transformation);
const mapping::ElementDisplacementData displacementData =
mapping::ElementDisplacementDataFromElementVDofs(displacementElement, elementBaseDisplacement);
const mapping::ElementCompactificationData compactificationData(
compactificationElement, elementCompactification
);
const mapping::ElementMappingData mappingData{
.displacement = displacementData, .compactification = compactificationData
};
const int quadraturePointCount = data.integrationRule->GetNPoints();
data.inverseElementJacobians.SetSize(quadraturePointCount, dimension * dimension);
data.centrifugalAccelerations.SetSize(quadraturePointCount, dimension);
data.baseDensityValues.SetSize(quadraturePointCount);
data.quadratureWeights.SetSize(quadraturePointCount);
densityShape.SetSize(densityElement.GetDof());
potentialGradient.SetSize(dimension);
for (int quadraturePoint = 0; quadraturePoint < quadraturePointCount; ++quadraturePoint) {
const mfem::IntegrationPoint &integrationPoint = data.integrationRule->IntPoint(quadraturePoint);
const mapping::MappingStatus status = m_domainMapper.EvaluateVolume(
mappingData, *transformation, integrationPoint, workspace, mappingContext
);
MFEM_VERIFY(
status == mapping::MappingStatus::valid && !mappingContext.mapping.compactified,
"Prepared rotational force encountered an invalid stellar mapping."
);
densityElement.CalcShape(integrationPoint, densityShape);
m_rotation->potential_gradient(mappingContext.mapping.physical_position, potentialGradient);
data.baseDensityValues(quadraturePoint) = elementBaseDensity * densityShape;
data.quadratureWeights(quadraturePoint) = mappingContext.quadrature.weight;
for (int row = 0; row < dimension; ++row) {
data.centrifugalAccelerations(quadraturePoint, row) = -potentialGradient(row);
for (int column = 0; column < dimension; ++column) {
data.inverseElementJacobians(quadraturePoint, row * dimension + column) =
mappingContext.quadrature.J_inv(row, column);
}
}
}
}
}
PreparedRotationalDisplacementForceReport PreparedRotationalDisplacementForceOperator::Prepare(
const context::rotational_displacement_force::RotationalDisplacementForceStateView &state,
const context::rotational_displacement_force::RotationalDisplacementForceDependencies &dependencies,
@@ -83,6 +252,7 @@ namespace mean_field::operators {
);
m_cachedResidual.SetSize(m_context.GetDisplacementMap().reduced_size());
m_context.GetDisplacementMap().gather(m_actionTrue, m_cachedResidual);
PrepareElementData();
++m_residualPreparationCount;
report.preparedResidual = true;
@@ -143,6 +313,97 @@ namespace mean_field::operators {
++m_displacementJacobianStatistics.applications;
}
void PreparedRotationalDisplacementForceOperator::ApplyPreparedCompleteJacobianActionTrue(
const mfem::Vector &densityVariationTrue,
const mfem::Vector &displacementVariationTrue,
mfem::Vector &actionTrue
) const {
true_to_local(*m_fem.densityFes, densityVariationTrue, m_densityVariationLocal);
true_to_local(*m_fem.displacementFes, displacementVariationTrue, m_displacementVariationLocal);
m_localAction.SetSize(m_fem.displacementFes->GetVSize());
m_localAction = 0.0;
const int dimension = m_domainMapper.GetDimension();
const mfem::Ordering::Type ordering = m_fem.displacementFes->GetOrdering();
for (const ElementPAData &data : m_elements) {
MFEM_VERIFY(data.integrationRule != nullptr, "Prepared rotational force has no integration rule.");
m_densityVariationLocal.GetSubVector(data.densityDofs, m_elementDensityVariation);
m_displacementVariationLocal.GetSubVector(data.displacementDofs, m_elementDisplacementVariation);
if (data.densityDofTransformation != nullptr) {
data.densityDofTransformation->InvTransformPrimal(m_elementDensityVariation);
}
if (data.displacementDofTransformation != nullptr) {
data.displacementDofTransformation->InvTransformPrimal(m_elementDisplacementVariation);
}
const mfem::FiniteElement &densityElement = *m_fem.densityFes->GetFE(data.elementId);
const mfem::FiniteElement &displacementElement = *m_fem.displacementFes->GetFE(data.elementId);
const mapping::ElementDisplacementData directionData =
mapping::ElementDisplacementDataFromElementVDofs(displacementElement, m_elementDisplacementVariation);
const mfem::DenseMatrix &directionDofs = directionData.GetDofMatrix();
const int scalarDisplacementDofCount = displacementElement.GetDof();
m_densityShape.SetSize(densityElement.GetDof());
m_displacementShape.SetSize(scalarDisplacementDofCount);
m_referenceDisplacementDShape.SetSize(scalarDisplacementDofCount, dimension);
m_referenceDisplacementJacobian.SetSize(dimension, dimension);
m_physicalPositionVariation.SetSize(dimension);
m_centrifugalAcceleration.SetSize(dimension);
m_centrifugalAccelerationVariation.SetSize(dimension);
m_weightedForce.SetSize(dimension);
m_elementAction.SetSize(data.displacementDofs.Size());
m_elementAction = 0.0;
for (int quadraturePoint = 0; quadraturePoint < data.integrationRule->GetNPoints(); ++quadraturePoint) {
const mfem::IntegrationPoint &integrationPoint = data.integrationRule->IntPoint(quadraturePoint);
densityElement.CalcShape(integrationPoint, m_densityShape);
displacementElement.CalcShape(integrationPoint, m_displacementShape);
displacementElement.CalcDShape(integrationPoint, m_referenceDisplacementDShape);
mfem::MultAtB(directionDofs, m_referenceDisplacementDShape, m_referenceDisplacementJacobian);
directionDofs.MultTranspose(m_displacementShape, m_physicalPositionVariation);
m_rotation->potential_gradient_directional_derivative(
m_physicalPositionVariation, m_centrifugalAccelerationVariation
);
m_centrifugalAccelerationVariation *= -1.0;
double logarithmicJacobianVariation{0.0};
for (int row = 0; row < dimension; ++row) {
m_centrifugalAcceleration(row) = data.centrifugalAccelerations(quadraturePoint, row);
for (int column = 0; column < dimension; ++column) {
logarithmicJacobianVariation +=
data.inverseElementJacobians(quadraturePoint, row * dimension + column) *
m_referenceDisplacementJacobian(column, row);
}
}
const double densityVariationValue = m_elementDensityVariation * m_densityShape;
const double baseDensityValue = data.baseDensityValues(quadraturePoint);
m_weightedForce = 0.0;
m_weightedForce.Add(densityVariationValue, m_centrifugalAcceleration);
m_weightedForce.Add(baseDensityValue, m_centrifugalAccelerationVariation);
m_weightedForce.Add(baseDensityValue * logarithmicJacobianVariation, m_centrifugalAcceleration);
m_weightedForce *= data.quadratureWeights(quadraturePoint);
for (int scalarDof = 0; scalarDof < scalarDisplacementDofCount; ++scalarDof) {
for (int component = 0; component < dimension; ++component) {
const int vectorDof =
vector_dof_index(ordering, scalarDof, component, scalarDisplacementDofCount, dimension);
m_elementAction(vectorDof) += m_displacementShape(scalarDof) * m_weightedForce(component);
}
}
}
if (data.displacementDofTransformation != nullptr) {
data.displacementDofTransformation->TransformDual(m_elementAction);
}
m_localAction.AddElementVector(data.displacementDofs, m_elementAction);
}
local_to_true(*m_fem.displacementFes, m_localAction, actionTrue);
}
void PreparedRotationalDisplacementForceOperator::ApplyCompleteJacobianAction(
const mfem::Vector &densityVariation,
const mfem::Vector &displacementVariation,
@@ -155,10 +416,7 @@ namespace mean_field::operators {
m_context.GetDensityMap().scatter(densityVariation, m_densityVariationTrue);
m_context.GetDisplacementMap().scatter(displacementVariation, m_displacementVariationTrue);
kernels::apply_rotational_displacement_force_complete_action(
m_fem, m_domainMapper, *m_rotation, m_context.GetBaseDensityTrue(), m_densityVariationTrue,
m_displacementVariationTrue, m_context.GetDisplacementTrue(), m_actionTrue
);
ApplyPreparedCompleteJacobianActionTrue(m_densityVariationTrue, m_displacementVariationTrue, m_actionTrue);
action.SetSize(m_context.GetDisplacementMap().reduced_size());
m_context.GetDisplacementMap().gather(m_actionTrue, action);

View File

@@ -22,26 +22,34 @@ namespace {
);
}
[[nodiscard]] mean_field::operators::StellarEquilibriumLayout make_layout(
using StellarRootForm = mean_field::utils::blocks::surface_deformed_stellar_equilibrium_form;
[[nodiscard]] std::array<
int,
StellarRootForm::value_block_count>
make_value_sizes(
const mean_field::field::FieldDofMap &densityMap,
const int surfaceDeformationParameterCount,
const mean_field::field::FieldDofMap &gravityFluxMap,
const mean_field::field::FieldDofMap &gravityPotentialMap,
const mean_field::field::FieldDofMap &enthalpyMap
) {
using Form = mean_field::utils::blocks::surface_deformed_stellar_equilibrium_form;
return {densityMap.reduced_size(), surfaceDeformationParameterCount, gravityFluxMap.reduced_size(),
gravityPotentialMap.reduced_size(), enthalpyMap.reduced_size(), 1};
}
const std::array<int, Form::value_block_count> valueSizes{
densityMap.reduced_size(), surfaceDeformationParameterCount, gravityFluxMap.reduced_size(),
gravityPotentialMap.reduced_size(), enthalpyMap.reduced_size(), 1
};
const std::array<int, Form::residual_block_count> residualSizes{
gravityFluxMap.reduced_size(), gravityPotentialMap.reduced_size(), densityMap.reduced_size(),
surfaceDeformationParameterCount, enthalpyMap.reduced_size(), 1
};
return {valueSizes, residualSizes};
[[nodiscard]] std::array<
int,
StellarRootForm::residual_block_count>
make_residual_sizes(
const mean_field::field::FieldDofMap &densityMap,
const int surfaceDeformationParameterCount,
const mean_field::field::FieldDofMap &gravityFluxMap,
const mean_field::field::FieldDofMap &gravityPotentialMap,
const mean_field::field::FieldDofMap &enthalpyMap
) {
return {gravityFluxMap.reduced_size(), gravityPotentialMap.reduced_size(), densityMap.reduced_size(),
surfaceDeformationParameterCount, enthalpyMap.reduced_size(), 1};
}
[[nodiscard]] mfem::Array<int> make_gravity_state_offsets(
@@ -70,47 +78,6 @@ namespace {
return offsets;
}
template <int index>
[[nodiscard]] mfem::Vector make_value_view(
const mfem::Vector &vector,
const mean_field::operators::StellarEquilibriumLayout &layout,
const mean_field::utils::blocks::value_block<index> block
) {
MFEM_VERIFY(
vector.Size() == layout.value_offsets().Last(),
"The coupled vector does not match the stellar-equilibrium value layout."
);
return mfem::Vector(const_cast<mfem::real_t *>(vector.GetData()) + layout.offset(block), layout.size(block));
}
template <int index>
[[nodiscard]] mfem::Vector make_residual_view(
mfem::Vector &vector,
const mean_field::operators::StellarEquilibriumLayout &layout,
const mean_field::utils::blocks::residual_block<index> block
) {
MFEM_VERIFY(
vector.Size() == layout.residual_offsets().Last(),
"The coupled vector does not match the stellar-equilibrium residual layout."
);
return mfem::Vector(vector.GetData() + layout.offset(block), layout.size(block));
}
template <int index>
void assign_residual_block(
mfem::Vector &coupledResidual,
const mean_field::operators::StellarEquilibriumLayout &layout,
const mean_field::utils::blocks::residual_block<index> block,
const mfem::Vector &blockResidual,
const char *message
) {
MFEM_VERIFY(layout.size(block) == blockResidual.Size(), message);
mfem::Vector destination = make_residual_view(coupledResidual, layout, block);
destination = blockResidual;
}
void assign_gravity_block(
mfem::Vector &gravityState,
const mfem::Array<int> &offsets,
@@ -264,7 +231,8 @@ namespace mean_field::operators {
field::FieldDofMap enthalpyMap;
field::FieldBoundaryDofMap pressureSurfaceRows;
StellarEquilibriumLayout layout;
std::array<int, StellarRootForm::value_block_count> valueSizes;
std::array<int, StellarRootForm::residual_block_count> residualSizes;
mfem::Array<int> gravityStateOffsets;
mfem::Array<int> gravityResidualOffsets;
@@ -307,7 +275,14 @@ namespace mean_field::operators {
enthalpyMap
)
),
layout(make_layout(
valueSizes(make_value_sizes(
densityMap,
domainDeformation.parameterCount(),
gravityFluxMap,
gravityPotentialMap,
enthalpyMap
)),
residualSizes(make_residual_sizes(
densityMap,
domainDeformation.parameterCount(),
gravityFluxMap,
@@ -339,7 +314,7 @@ namespace mean_field::operators {
fem::FEM &f,
const mapping::DomainMapper &domainMapper,
const eos::Polytrope &equationOfState,
const double targetMass,
models::CompiledFixedMass fixedMassConstraint,
const PressureSurfaceConstraintView surfaceConstraint,
deformation::PreparedDomainDeformationRuntime domainDeformation
)
@@ -347,7 +322,7 @@ namespace mean_field::operators {
f,
domainMapper,
equationOfState,
targetMass,
std::move(fixedMassConstraint),
surfaceConstraint,
MakeConstructionData(
f,
@@ -360,15 +335,31 @@ namespace mean_field::operators {
fem::FEM &f,
const mapping::DomainMapper &domainMapper,
const eos::Polytrope &equationOfState,
const double targetMass,
models::CompiledFixedMass fixedMassConstraint,
const PressureSurfaceConstraintView surfaceConstraint,
ConstructionData constructionData
)
: mfem::Operator(
constructionData.layout.residual_offsets().Last(),
constructionData.layout.value_offsets().Last()
StellarEquilibriumLayout(
constructionData.valueSizes,
constructionData.residualSizes
)
.residual_offsets()
.Last(),
StellarEquilibriumLayout(
constructionData.valueSizes,
constructionData.residualSizes
)
.value_offsets()
.Last()
),
m_rootManifest(
constructionData.valueSizes,
constructionData.residualSizes,
fixedMassConstraint.targetMass().value(),
surfaceConstraint.descriptor().targetPressure,
constructionData.pressureSurfaceRows.size()
),
m_layout(constructionData.layout),
m_gravityStateOffsets(constructionData.gravityStateOffsets),
m_gravityContext(
f,
@@ -413,14 +404,11 @@ namespace mean_field::operators {
surfaceConstraint
),
m_domainDeformation(std::move(constructionData.domainDeformation)),
m_targetMass(targetMass) {
MFEM_VERIFY(
std::isfinite(m_targetMass) && m_targetMass > 0.0,
"PreparedStellarEquilibriumOperator requires a finite, positive target mass."
);
m_fixedMassConstraint(std::move(fixedMassConstraint)) {
MFEM_VERIFY(
Width() == m_layout.value_offsets().Last() && Height() == m_layout.residual_offsets().Last(),
Width() == m_rootManifest.layout().value_offsets().Last() &&
Height() == m_rootManifest.layout().residual_offsets().Last(),
"PreparedStellarEquilibriumOperator has inconsistent block dimensions."
);
@@ -502,27 +490,18 @@ namespace mean_field::operators {
);
}
m_isPrepared = false;
m_isPrepared = false;
using Form = utils::blocks::surface_deformed_stellar_equilibrium_form;
constexpr auto densityValue = utils::blocks::get_value_block<Form>(utils::blocks::density_field.mass_term);
constexpr auto surfaceDeformationValue =
utils::blocks::get_value_block<Form>(utils::blocks::surface_deformation_field.parameters_term);
constexpr auto gravityGradientValue =
utils::blocks::get_value_block<Form>(utils::blocks::gravity_field.gradient_term);
constexpr auto gravityPotentialValue =
utils::blocks::get_value_block<Form>(utils::blocks::gravity_field.poisson_term);
constexpr auto enthalpyValue =
utils::blocks::get_value_block<Form>(utils::blocks::enthalpy_field.specific_term);
constexpr auto bernoulliValue =
utils::blocks::get_value_block<Form>(utils::blocks::barotropic_constant_field.mass_normalization_term);
const auto rootState = m_rootManifest.stateView(state);
const mfem::Vector reducedDensity = make_value_view(state, m_layout, densityValue);
const mfem::Vector surfaceDeformationParameters = make_value_view(state, m_layout, surfaceDeformationValue);
const mfem::Vector gravityGradient = make_value_view(state, m_layout, gravityGradientValue);
const mfem::Vector gravityPotential = make_value_view(state, m_layout, gravityPotentialValue);
const mfem::Vector reducedEnthalpy = make_value_view(state, m_layout, enthalpyValue);
const mfem::Vector bernoulli = make_value_view(state, m_layout, bernoulliValue);
const mfem::Vector reducedDensity = rootState.block(utils::blocks::density_field.mass_term);
const mfem::Vector surfaceDeformationParameters =
rootState.block(utils::blocks::surface_deformation_field.parameters_term);
const mfem::Vector gravityGradient = rootState.block(utils::blocks::gravity_field.gradient_term);
const mfem::Vector gravityPotential = rootState.block(utils::blocks::gravity_field.poisson_term);
const mfem::Vector reducedEnthalpy = rootState.block(utils::blocks::enthalpy_field.specific_term);
const mfem::Vector bernoulli =
rootState.block(utils::blocks::fixed_total_mass_constraint.mass_normalization_term);
const bool generatedGeometryChanged =
!wasPrepared || dependencies.discretization != m_preparedDependencies.discretization ||
@@ -569,7 +548,7 @@ namespace mean_field::operators {
);
report.massNormalization = m_massNormalizationOperator.Prepare(
{.targetMass = m_targetMass}, make_mass_dependencies(dependencies, m_generatedDisplacementDependency)
m_fixedMassConstraint, make_mass_dependencies(dependencies, m_generatedDisplacementDependency)
);
report.surfaceConstraint = m_surfaceConstraintOperator.Prepare(
@@ -588,21 +567,6 @@ namespace mean_field::operators {
}
void PreparedStellarEquilibriumOperator::AssembleResidual() {
using Form = utils::blocks::surface_deformed_stellar_equilibrium_form;
constexpr auto gravityGradientResidual =
utils::blocks::get_residual_block<Form>(utils::blocks::gravity_field.gradient_term);
constexpr auto gravityPotentialResidual =
utils::blocks::get_residual_block<Form>(utils::blocks::gravity_field.poisson_term);
constexpr auto densityResidual =
utils::blocks::get_residual_block<Form>(utils::blocks::density_field.mass_term);
constexpr auto surfaceShapeResidual =
utils::blocks::get_residual_block<Form>(utils::blocks::surface_deformation_field.shape_equilibrium_term);
constexpr auto enthalpyResidual =
utils::blocks::get_residual_block<Form>(utils::blocks::enthalpy_field.specific_term);
constexpr auto massResidual =
utils::blocks::get_residual_block<Form>(utils::blocks::barotropic_constant_field.mass_normalization_term);
mfem::Vector gravity;
mfem::Vector closure;
mfem::Vector surfaceShape;
@@ -621,41 +585,29 @@ namespace mean_field::operators {
m_massNormalizationOperator.BuildResidual(mass);
m_cachedResidual.SetSize(Height());
m_cachedResidual = 0.0;
m_cachedResidual = 0.0;
const auto residualView = m_rootManifest.residualView(m_cachedResidual);
MFEM_VERIFY(
gravity.Size() == m_layout.size(gravityGradientResidual) + m_layout.size(gravityPotentialResidual),
gravity.Size() == residualView.block(utils::blocks::gravity_field.gradient_term).Size() +
residualView.block(utils::blocks::gravity_field.poisson_term).Size(),
"The gravity residual has the wrong size."
);
mfem::Vector gravityGradient(gravity.GetData(), m_layout.size(gravityGradientResidual));
mfem::Vector gravityGradient(
gravity.GetData(), residualView.block(utils::blocks::gravity_field.gradient_term).Size()
);
mfem::Vector gravityPotential(
gravity.GetData() + m_layout.size(gravityGradientResidual), m_layout.size(gravityPotentialResidual)
gravity.GetData() + gravityGradient.Size(),
residualView.block(utils::blocks::gravity_field.poisson_term).Size()
);
assign_residual_block(
m_cachedResidual, m_layout, gravityGradientResidual, gravityGradient,
"The gravity-gradient residual has the wrong size."
);
assign_residual_block(
m_cachedResidual, m_layout, gravityPotentialResidual, gravityPotential,
"The gravity-potential residual has the wrong size."
);
assign_residual_block(
m_cachedResidual, m_layout, densityResidual, closure, "The closure residual has the wrong size."
);
assign_residual_block(
m_cachedResidual, m_layout, surfaceShapeResidual, surfaceShape,
"The surface-shape residual has the wrong size."
);
assign_residual_block(
m_cachedResidual, m_layout, enthalpyResidual, hydrostatic, "The hydrostatic residual has the wrong size."
);
assign_residual_block(
m_cachedResidual, m_layout, massResidual, mass, "The mass-normalization residual has the wrong size."
);
residualView.assign(utils::blocks::gravity_field.gradient_term, gravityGradient);
residualView.assign(utils::blocks::gravity_field.poisson_term, gravityPotential);
residualView.assign(utils::blocks::density_field.mass_term, closure);
residualView.assign(utils::blocks::surface_deformation_field.shape_equilibrium_term, surfaceShape);
residualView.assign(utils::blocks::enthalpy_field.specific_term, hydrostatic);
residualView.assign(utils::blocks::fixed_total_mass_constraint.mass_normalization_term, mass);
++m_statistics.residualAssemblies;
}
@@ -679,39 +631,16 @@ namespace mean_field::operators {
direction, "PreparedStellarEquilibriumOperator received a non-finite Jacobian direction."
);
using Form = utils::blocks::surface_deformed_stellar_equilibrium_form;
const auto rootDirection = m_rootManifest.directionView(direction);
constexpr auto densityValue = utils::blocks::get_value_block<Form>(utils::blocks::density_field.mass_term);
constexpr auto surfaceDeformationValue =
utils::blocks::get_value_block<Form>(utils::blocks::surface_deformation_field.parameters_term);
constexpr auto gravityGradientValue =
utils::blocks::get_value_block<Form>(utils::blocks::gravity_field.gradient_term);
constexpr auto gravityPotentialValue =
utils::blocks::get_value_block<Form>(utils::blocks::gravity_field.poisson_term);
constexpr auto enthalpyValue =
utils::blocks::get_value_block<Form>(utils::blocks::enthalpy_field.specific_term);
constexpr auto bernoulliValue =
utils::blocks::get_value_block<Form>(utils::blocks::barotropic_constant_field.mass_normalization_term);
constexpr auto gravityGradientResidual =
utils::blocks::get_residual_block<Form>(utils::blocks::gravity_field.gradient_term);
constexpr auto gravityPotentialResidual =
utils::blocks::get_residual_block<Form>(utils::blocks::gravity_field.poisson_term);
constexpr auto densityResidual =
utils::blocks::get_residual_block<Form>(utils::blocks::density_field.mass_term);
constexpr auto surfaceShapeResidual =
utils::blocks::get_residual_block<Form>(utils::blocks::surface_deformation_field.shape_equilibrium_term);
constexpr auto enthalpyResidual =
utils::blocks::get_residual_block<Form>(utils::blocks::enthalpy_field.specific_term);
constexpr auto massResidual =
utils::blocks::get_residual_block<Form>(utils::blocks::barotropic_constant_field.mass_normalization_term);
const mfem::Vector reducedDensityDirection = make_value_view(direction, m_layout, densityValue);
const mfem::Vector surfaceDeformationDirection = make_value_view(direction, m_layout, surfaceDeformationValue);
const mfem::Vector gravityGradientDirection = make_value_view(direction, m_layout, gravityGradientValue);
const mfem::Vector gravityPotentialDirection = make_value_view(direction, m_layout, gravityPotentialValue);
const mfem::Vector reducedEnthalpyDirection = make_value_view(direction, m_layout, enthalpyValue);
const mfem::Vector bernoulliDirection = make_value_view(direction, m_layout, bernoulliValue);
const mfem::Vector reducedDensityDirection = rootDirection.block(utils::blocks::density_field.mass_term);
const mfem::Vector surfaceDeformationDirection =
rootDirection.block(utils::blocks::surface_deformation_field.parameters_term);
const mfem::Vector gravityGradientDirection = rootDirection.block(utils::blocks::gravity_field.gradient_term);
const mfem::Vector gravityPotentialDirection = rootDirection.block(utils::blocks::gravity_field.poisson_term);
const mfem::Vector reducedEnthalpyDirection = rootDirection.block(utils::blocks::enthalpy_field.specific_term);
const mfem::Vector bernoulliDirection =
rootDirection.block(utils::blocks::fixed_total_mass_constraint.mass_normalization_term);
m_domainDeformation.applyJacobian(
m_surfaceDeformationParameters, surfaceDeformationDirection, m_volumeDisplacementDirection
@@ -757,41 +686,29 @@ namespace mean_field::operators {
);
action.SetSize(Height());
action = 0.0;
action = 0.0;
const auto actionView = m_rootManifest.residualView(action);
MFEM_VERIFY(
gravityAction.Size() == m_layout.size(gravityGradientResidual) + m_layout.size(gravityPotentialResidual),
gravityAction.Size() == actionView.block(utils::blocks::gravity_field.gradient_term).Size() +
actionView.block(utils::blocks::gravity_field.poisson_term).Size(),
"The gravity Jacobian action has the wrong size."
);
mfem::Vector gravityGradientAction(gravityAction.GetData(), m_layout.size(gravityGradientResidual));
mfem::Vector gravityGradientAction(
gravityAction.GetData(), actionView.block(utils::blocks::gravity_field.gradient_term).Size()
);
mfem::Vector gravityPotentialAction(
gravityAction.GetData() + m_layout.size(gravityGradientResidual), m_layout.size(gravityPotentialResidual)
gravityAction.GetData() + gravityGradientAction.Size(),
actionView.block(utils::blocks::gravity_field.poisson_term).Size()
);
assign_residual_block(
action, m_layout, gravityGradientResidual, gravityGradientAction,
"The gravity-gradient Jacobian action has the wrong size."
);
assign_residual_block(
action, m_layout, gravityPotentialResidual, gravityPotentialAction,
"The gravity-potential Jacobian action has the wrong size."
);
assign_residual_block(
action, m_layout, densityResidual, closureAction, "The closure Jacobian action has the wrong size."
);
assign_residual_block(
action, m_layout, surfaceShapeResidual, m_surfaceShapeAction,
"The surface-shape Jacobian action has the wrong size."
);
assign_residual_block(
action, m_layout, enthalpyResidual, hydrostaticAction, "The hydrostatic Jacobian action has the wrong size."
);
assign_residual_block(
action, m_layout, massResidual, massAction, "The mass-normalization Jacobian action has the wrong size."
);
actionView.assign(utils::blocks::gravity_field.gradient_term, gravityGradientAction);
actionView.assign(utils::blocks::gravity_field.poisson_term, gravityPotentialAction);
actionView.assign(utils::blocks::density_field.mass_term, closureAction);
actionView.assign(utils::blocks::surface_deformation_field.shape_equilibrium_term, m_surfaceShapeAction);
actionView.assign(utils::blocks::enthalpy_field.specific_term, hydrostaticAction);
actionView.assign(utils::blocks::fixed_total_mass_constraint.mass_normalization_term, massAction);
++m_statistics.jacobianApplications;
}
@@ -803,11 +720,30 @@ namespace mean_field::operators {
}
double PreparedStellarEquilibriumOperator::GetTargetMass() const noexcept {
return m_targetMass;
return m_fixedMassConstraint.targetMass().value();
}
const StellarEquilibriumLayout &PreparedStellarEquilibriumOperator::GetLayout() const noexcept {
return m_layout;
return m_rootManifest.layout();
}
const StellarEquilibriumRootManifest &PreparedStellarEquilibriumOperator::GetRootManifest() const noexcept {
return m_rootManifest;
}
RootStateView<utils::blocks::surface_deformed_stellar_equilibrium_form>
PreparedStellarEquilibriumOperator::GetRootStateView(const mfem::Vector &state) const {
return m_rootManifest.stateView(state);
}
ResidualView<utils::blocks::surface_deformed_stellar_equilibrium_form>
PreparedStellarEquilibriumOperator::GetResidualView(mfem::Vector &residual) const {
return m_rootManifest.residualView(residual);
}
RootConstraintReport PreparedStellarEquilibriumOperator::GetFixedMassReport() const {
VerifyPrepared();
return m_rootManifest.fixedMassReport(m_massNormalizationOperator.GetCurrentMass());
}
const StellarEquilibriumDependencies &PreparedStellarEquilibriumOperator::GetDependencies() const {