feat(libmeanfield): centrifugal + pressure
This commit is contained in:
@@ -0,0 +1,135 @@
|
||||
module;
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include <mfem.hpp>
|
||||
|
||||
module mean_field;
|
||||
import :operators.context.barotropic_closure_linearization;
|
||||
|
||||
namespace mean_field::operators::context::barotropic {
|
||||
BarotropicClosureLinearizationContext::
|
||||
BarotropicClosureLinearizationContext(
|
||||
const fem::FEM &f,
|
||||
const mapping::DomainMapperStateless &domainMapper,
|
||||
const physics::PolytropicBarotrope &barotrope
|
||||
)
|
||||
: m_f(f),
|
||||
m_operator(
|
||||
f,
|
||||
domainMapper,
|
||||
barotrope
|
||||
) {
|
||||
MFEM_VERIFY(
|
||||
m_f.densityFes != nullptr,
|
||||
"The closure linearization context requires the "
|
||||
"density finite-element space."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
m_f.enthalpyFes != nullptr,
|
||||
"The closure linearization context requires the "
|
||||
"enthalpy finite-element space."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
m_f.displacementFes != nullptr,
|
||||
"The closure linearization context requires the "
|
||||
"displacement finite-element space."
|
||||
);
|
||||
}
|
||||
|
||||
void BarotropicClosureLinearizationContext::Prepare(
|
||||
const mfem::Vector &baseDensityTrue,
|
||||
const mfem::Vector &baseEnthalpyTrue,
|
||||
const mfem::Vector &displacementTrue,
|
||||
const BarotropicClosureRevisions &revisions
|
||||
) {
|
||||
MFEM_VERIFY(
|
||||
baseDensityTrue.Size() == m_f.densityFes->GetTrueVSize(),
|
||||
"The closure base-density vector has the wrong size."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
baseEnthalpyTrue.Size() == m_f.enthalpyFes->GetTrueVSize(),
|
||||
"The closure base-enthalpy vector has the wrong size."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
displacementTrue.Size() == m_f.displacementFes->GetTrueVSize(),
|
||||
"The closure displacement vector has the wrong size."
|
||||
);
|
||||
|
||||
if (m_isPrepared && revisions == m_revisions) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_operator.Prepare(baseDensityTrue, baseEnthalpyTrue, displacementTrue);
|
||||
|
||||
m_baseDensityTrue = baseDensityTrue;
|
||||
m_baseEnthalpyTrue = baseEnthalpyTrue;
|
||||
m_displacementTrue = displacementTrue;
|
||||
|
||||
m_revisions = revisions;
|
||||
m_isPrepared = true;
|
||||
++m_preparationCount;
|
||||
}
|
||||
|
||||
bool BarotropicClosureLinearizationContext::IsPrepared() const noexcept {
|
||||
return m_isPrepared;
|
||||
}
|
||||
|
||||
bool BarotropicClosureLinearizationContext::MatchesRevisions(
|
||||
const BarotropicClosureRevisions &revisions
|
||||
) const noexcept {
|
||||
return m_isPrepared && revisions == m_revisions;
|
||||
}
|
||||
|
||||
std::uint64_t BarotropicClosureLinearizationContext::
|
||||
GetPreparationCount() const noexcept {
|
||||
return m_preparationCount;
|
||||
}
|
||||
|
||||
const BarotropicClosureRevisions &
|
||||
BarotropicClosureLinearizationContext::GetRevisions() const {
|
||||
VerifyPrepared();
|
||||
return m_revisions;
|
||||
}
|
||||
|
||||
const mfem::Vector &
|
||||
BarotropicClosureLinearizationContext::GetBaseDensityTrue() const {
|
||||
VerifyPrepared();
|
||||
return m_baseDensityTrue;
|
||||
}
|
||||
|
||||
const mfem::Vector &
|
||||
BarotropicClosureLinearizationContext::GetBaseEnthalpyTrue() const {
|
||||
VerifyPrepared();
|
||||
return m_baseEnthalpyTrue;
|
||||
}
|
||||
|
||||
const mfem::Vector &
|
||||
BarotropicClosureLinearizationContext::GetDisplacementTrue() const {
|
||||
VerifyPrepared();
|
||||
return m_displacementTrue;
|
||||
}
|
||||
|
||||
const PreparedBarotropicClosureOperator &
|
||||
BarotropicClosureLinearizationContext::GetOperator() const noexcept {
|
||||
return m_operator;
|
||||
}
|
||||
|
||||
void BarotropicClosureLinearizationContext::BuildResidual(
|
||||
mfem::Vector &residual
|
||||
) const {
|
||||
VerifyPrepared();
|
||||
m_operator.BuildResidual(residual);
|
||||
}
|
||||
|
||||
void BarotropicClosureLinearizationContext::VerifyPrepared() const {
|
||||
MFEM_VERIFY(
|
||||
m_isPrepared, "The barotropic-closure linearization context "
|
||||
"has not been prepared."
|
||||
);
|
||||
}
|
||||
} // namespace mean_field::operators::context::barotropic
|
||||
442
libmeanfield/impl/operators/contexts/gravity_field_context.cpp
Normal file
442
libmeanfield/impl/operators/contexts/gravity_field_context.cpp
Normal file
@@ -0,0 +1,442 @@
|
||||
module;
|
||||
#include <cmath>
|
||||
#include <memory>
|
||||
#include <mfem.hpp>
|
||||
|
||||
module mean_field;
|
||||
import :operators.context.gravity_field;
|
||||
|
||||
namespace {
|
||||
void validate_displacement(
|
||||
const mean_field::fem::FEM &f,
|
||||
const mfem::Vector &displacement_true
|
||||
) {
|
||||
MFEM_VERIFY(
|
||||
f.displacementFes != nullptr,
|
||||
"GravityFieldGeometryContext requires the "
|
||||
"displacement finite-element space."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
displacement_true.Size() == f.displacementFes->GetTrueVSize(),
|
||||
"GravityFieldGeometryContext received a displacement vector with "
|
||||
"the "
|
||||
"wrong size."
|
||||
);
|
||||
|
||||
for (int i = 0; i < displacement_true.Size(); ++i) {
|
||||
MFEM_VERIFY(
|
||||
std::isfinite(displacement_true(i)),
|
||||
"GravityFieldGeometryContext received a non-finite "
|
||||
"displacement "
|
||||
"value."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void validate_linearization_state(
|
||||
const mean_field::fem::FEM &f,
|
||||
const mean_field::operators::context::gravity_field::
|
||||
GravityFieldStateView &state
|
||||
) {
|
||||
MFEM_VERIFY(
|
||||
f.densityFes != nullptr, "GravityFieldLinearizationContext "
|
||||
"requires the density finite-element "
|
||||
"space."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.gravityPotentialFes != nullptr,
|
||||
"GravityFieldLinearizationContext requires the gravity-potential "
|
||||
"finite-element space."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.gravityFluxFes != nullptr,
|
||||
"GravityFieldLinearizationContext requires the "
|
||||
"gravity-gradient finite-element space."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.displacementFes != nullptr,
|
||||
"GravityFieldLinearizationContext requires "
|
||||
"the displacement finite-element space."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
state.density.Size() == f.densityFes->GetTrueVSize(),
|
||||
"GravityFieldLinearizationContext received a density vector with "
|
||||
"the "
|
||||
"wrong size."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
state.displacement.Size() == f.displacementFes->GetTrueVSize(),
|
||||
"GravityFieldLinearizationContext received a displacement vector "
|
||||
"with "
|
||||
"the wrong size."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
state.gravity_gradient.Size() == f.gravityFluxFes->GetTrueVSize(),
|
||||
"GravityFieldLinearizationContext received a gravity-gradient "
|
||||
"vector "
|
||||
"with the wrong size."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
state.gravity_potential.Size() ==
|
||||
f.gravityPotentialFes->GetTrueVSize(),
|
||||
"GravityFieldLinearizationContext received a gravity-potential "
|
||||
"vector "
|
||||
"with the wrong size."
|
||||
);
|
||||
|
||||
for (int i = 0; i < state.density.Size(); ++i) {
|
||||
MFEM_VERIFY(
|
||||
std::isfinite(state.density(i)),
|
||||
"GravityFieldLinearizationContext received a non-finite "
|
||||
"density "
|
||||
"value."
|
||||
);
|
||||
}
|
||||
|
||||
for (int i = 0; i < state.displacement.Size(); ++i) {
|
||||
MFEM_VERIFY(
|
||||
std::isfinite(state.displacement(i)),
|
||||
"GravityFieldLinearizationContext received a non-finite "
|
||||
"displacement "
|
||||
"value."
|
||||
);
|
||||
}
|
||||
|
||||
for (int i = 0; i < state.gravity_gradient.Size(); ++i) {
|
||||
MFEM_VERIFY(
|
||||
std::isfinite(state.gravity_gradient(i)),
|
||||
"GravityFieldLinearizationContext received a non-finite "
|
||||
"gravity-gradient value."
|
||||
);
|
||||
}
|
||||
|
||||
for (int i = 0; i < state.gravity_potential.Size(); ++i) {
|
||||
MFEM_VERIFY(
|
||||
std::isfinite(state.gravity_potential(i)),
|
||||
"GravityFieldLinearizationContext received a non-finite "
|
||||
"gravity-potential value."
|
||||
);
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace mean_field::operators::context::gravity_field {
|
||||
GravityFieldGeometryContext::GravityFieldGeometryContext(
|
||||
const fem::FEM &f,
|
||||
const mapping::DomainMapperStateless &domain_mapper
|
||||
)
|
||||
: m_fem(f),
|
||||
m_domain_mapper(domain_mapper) {
|
||||
MFEM_VERIFY(
|
||||
f.mesh != nullptr, "GravityFieldGeometryContext requires a mesh."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.gravityFluxFes != nullptr,
|
||||
"GravityFieldGeometryContext requires the "
|
||||
"gravity-gradient finite-element space."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.densityFes != nullptr,
|
||||
"GravityFieldGeometryContext requires the density finite-element "
|
||||
"space."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.gravityPotentialFes != nullptr,
|
||||
"GravityFieldGeometryContext requires the gravity-potential "
|
||||
"finite-element space."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.displacementFes != nullptr,
|
||||
"GravityFieldGeometryContext requires the "
|
||||
"displacement finite-element space."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.compactificationFes != nullptr,
|
||||
"GravityFieldGeometryContext requires the compactification "
|
||||
"finite-element space."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.compactificationCoordinate != nullptr,
|
||||
"GravityFieldGeometryContext requires the compactification "
|
||||
"coordinate."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.quadratureFactory != nullptr,
|
||||
"GravityFieldGeometryContext requires the quadrature-rule factory."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
domain_mapper.GetDimension() == f.mesh->Dimension(),
|
||||
"The stateless domain-mapper dimension does not match the mesh "
|
||||
"dimension."
|
||||
);
|
||||
}
|
||||
|
||||
GravityFieldGeometryPreparation GravityFieldGeometryContext::Prepare(
|
||||
const mfem::Vector &displacement_true,
|
||||
const DiscretizationRevision discretization_revision,
|
||||
const DisplacementRevision displacement_revision
|
||||
) {
|
||||
validate_displacement(m_fem, displacement_true);
|
||||
|
||||
if (m_is_prepared) {
|
||||
MFEM_VERIFY(
|
||||
discretization_revision >= m_discretization_revision,
|
||||
"GravityFieldGeometryContext received an older discretization "
|
||||
"revision."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
displacement_revision >= m_displacement_revision,
|
||||
"GravityFieldGeometryContext received an older displacement "
|
||||
"revision."
|
||||
);
|
||||
}
|
||||
|
||||
const bool discretization_changed =
|
||||
!m_is_prepared ||
|
||||
discretization_revision != m_discretization_revision;
|
||||
const bool displacement_changed =
|
||||
!m_is_prepared || displacement_revision != m_displacement_revision;
|
||||
|
||||
GravityFieldGeometryPreparation preparation;
|
||||
|
||||
if (!discretization_changed && !displacement_changed) {
|
||||
return preparation;
|
||||
}
|
||||
|
||||
if (discretization_changed) {
|
||||
auto mass_operator =
|
||||
std::make_unique<PreparedMappedHDivMassOperator>(
|
||||
m_fem, m_domain_mapper
|
||||
);
|
||||
auto source_operator =
|
||||
std::make_unique<PreparedMappedGravitySourceOperator>(
|
||||
m_fem, m_domain_mapper
|
||||
);
|
||||
|
||||
mass_operator->Prepare(displacement_true);
|
||||
source_operator->Prepare(displacement_true);
|
||||
|
||||
m_mass_operator = std::move(mass_operator);
|
||||
m_source_operator = std::move(source_operator);
|
||||
|
||||
preparation.reconstructed_operators = true;
|
||||
preparation.rebuilt_mass_operator = true;
|
||||
preparation.rebuilt_source_operator = true;
|
||||
} else {
|
||||
MFEM_VERIFY(
|
||||
m_mass_operator != nullptr, "GravityFieldGeometryContext has "
|
||||
"no prepared H(div) mass operator."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
m_source_operator != nullptr,
|
||||
"GravityFieldGeometryContext has no prepared gravity source "
|
||||
"operator."
|
||||
);
|
||||
|
||||
m_mass_operator->Prepare(displacement_true);
|
||||
m_source_operator->Prepare(displacement_true);
|
||||
|
||||
preparation.rebuilt_mass_operator = true;
|
||||
preparation.rebuilt_source_operator = true;
|
||||
}
|
||||
|
||||
m_displacement_true = displacement_true;
|
||||
m_discretization_revision = discretization_revision;
|
||||
m_displacement_revision = displacement_revision;
|
||||
m_is_prepared = true;
|
||||
|
||||
preparation.refreshed_variation_state = true;
|
||||
|
||||
return preparation;
|
||||
}
|
||||
|
||||
const PreparedMappedHDivMassOperator &
|
||||
GravityFieldGeometryContext::GetMassOperator() const {
|
||||
MFEM_VERIFY(
|
||||
m_is_prepared,
|
||||
"GravityFieldGeometryContext must be prepared before "
|
||||
"accessing its mass operator."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
m_mass_operator != nullptr,
|
||||
"GravityFieldGeometryContext has no prepared H(div) mass operator."
|
||||
);
|
||||
return *m_mass_operator;
|
||||
}
|
||||
|
||||
const PreparedMappedGravitySourceOperator &
|
||||
GravityFieldGeometryContext::GetSourceOperator() const {
|
||||
MFEM_VERIFY(
|
||||
m_is_prepared,
|
||||
"GravityFieldGeometryContext must be prepared before "
|
||||
"accessing its source operator."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
m_source_operator != nullptr, "GravityFieldGeometryContext has no "
|
||||
"prepared gravity source operator."
|
||||
);
|
||||
return *m_source_operator;
|
||||
}
|
||||
|
||||
const mfem::Vector &GravityFieldGeometryContext::GetDisplacement() const {
|
||||
MFEM_VERIFY(
|
||||
m_is_prepared,
|
||||
"GravityFieldGeometryContext must be prepared before "
|
||||
"accessing its displacement."
|
||||
);
|
||||
return m_displacement_true;
|
||||
}
|
||||
|
||||
DiscretizationRevision
|
||||
GravityFieldGeometryContext::GetDiscretizationRevision() const noexcept {
|
||||
return m_discretization_revision;
|
||||
}
|
||||
|
||||
DisplacementRevision
|
||||
GravityFieldGeometryContext::GetDisplacementRevision() const noexcept {
|
||||
return m_displacement_revision;
|
||||
}
|
||||
|
||||
bool GravityFieldGeometryContext::IsPrepared() const noexcept {
|
||||
return m_is_prepared;
|
||||
}
|
||||
|
||||
GravityFieldLinearizationContext::GravityFieldLinearizationContext(
|
||||
const fem::FEM &f,
|
||||
const mapping::DomainMapperStateless &domain_mapper
|
||||
)
|
||||
: m_fem(f),
|
||||
m_geometry_context(
|
||||
f,
|
||||
domain_mapper
|
||||
) {
|
||||
MFEM_VERIFY(
|
||||
f.densityFes != nullptr, "GravityFieldLinearizationContext "
|
||||
"requires the density finite-element "
|
||||
"space."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.gravityPotentialFes != nullptr,
|
||||
"GravityFieldLinearizationContext requires the gravity-potential "
|
||||
"finite-element space."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.gravityFluxFes != nullptr,
|
||||
"GravityFieldLinearizationContext requires the "
|
||||
"gravity-gradient finite-element space."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.displacementFes != nullptr,
|
||||
"GravityFieldLinearizationContext requires "
|
||||
"the displacement finite-element space."
|
||||
);
|
||||
}
|
||||
|
||||
GravityFieldPreparationReport GravityFieldLinearizationContext::Prepare(
|
||||
const GravityFieldStateView &state,
|
||||
const GravityFieldRevisions &revisions
|
||||
) {
|
||||
validate_linearization_state(m_fem, state);
|
||||
|
||||
if (m_is_prepared) {
|
||||
MFEM_VERIFY(
|
||||
revisions.discretization >= m_revisions.discretization,
|
||||
"GravityFieldLinearizationContext received an older "
|
||||
"discretization "
|
||||
"revision."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
revisions.displacement >= m_revisions.displacement,
|
||||
"GravityFieldLinearizationContext received an older "
|
||||
"displacement "
|
||||
"revision."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
revisions.density >= m_revisions.density,
|
||||
"GravityFieldLinearizationContext received an older density "
|
||||
"revision."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
revisions.gravity_gradient >= m_revisions.gravity_gradient,
|
||||
"GravityFieldLinearizationContext received an older "
|
||||
"gravity-gradient "
|
||||
"revision."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
revisions.gravity_potential >= m_revisions.gravity_potential,
|
||||
"GravityFieldLinearizationContext received an older "
|
||||
"gravity-potential revision."
|
||||
);
|
||||
}
|
||||
|
||||
const bool discretization_changed =
|
||||
!m_is_prepared ||
|
||||
revisions.discretization != m_revisions.discretization;
|
||||
const bool density_changed = !m_is_prepared || discretization_changed ||
|
||||
revisions.density != m_revisions.density;
|
||||
const bool gravity_gradient_changed =
|
||||
!m_is_prepared || discretization_changed ||
|
||||
revisions.gravity_gradient != m_revisions.gravity_gradient;
|
||||
|
||||
GravityFieldPreparationReport report;
|
||||
|
||||
report.geometry = m_geometry_context.Prepare(
|
||||
state.displacement, revisions.discretization, revisions.displacement
|
||||
);
|
||||
|
||||
if (density_changed) {
|
||||
m_density_true = state.density;
|
||||
report.updated_density = true;
|
||||
}
|
||||
|
||||
if (gravity_gradient_changed) {
|
||||
m_gravity_gradient_true = state.gravity_gradient;
|
||||
report.updated_gravity_gradient = true;
|
||||
}
|
||||
|
||||
m_revisions = revisions;
|
||||
m_is_prepared = true;
|
||||
|
||||
return report;
|
||||
}
|
||||
|
||||
const GravityFieldGeometryContext &
|
||||
GravityFieldLinearizationContext::GetGeometryContext() const {
|
||||
MFEM_VERIFY(
|
||||
m_is_prepared, "GravityFieldLinearizationContext must be prepared "
|
||||
"before accessing its geometry context."
|
||||
);
|
||||
return m_geometry_context;
|
||||
}
|
||||
|
||||
const mfem::Vector &GravityFieldLinearizationContext::GetDensity() const {
|
||||
MFEM_VERIFY(
|
||||
m_is_prepared, "GravityFieldLinearizationContext must be prepared "
|
||||
"before accessing its density."
|
||||
);
|
||||
return m_density_true;
|
||||
}
|
||||
|
||||
const mfem::Vector &
|
||||
GravityFieldLinearizationContext::GetGravityGradient() const {
|
||||
MFEM_VERIFY(
|
||||
m_is_prepared, "GravityFieldLinearizationContext must be prepared "
|
||||
"before accessing its gravity gradient."
|
||||
);
|
||||
return m_gravity_gradient_true;
|
||||
}
|
||||
|
||||
const GravityFieldRevisions &
|
||||
GravityFieldLinearizationContext::GetRevisions() const {
|
||||
MFEM_VERIFY(
|
||||
m_is_prepared, "GravityFieldLinearizationContext must be prepared "
|
||||
"before accessing its revisions."
|
||||
);
|
||||
return m_revisions;
|
||||
}
|
||||
|
||||
bool GravityFieldLinearizationContext::IsPrepared() const noexcept {
|
||||
return m_is_prepared;
|
||||
}
|
||||
} // namespace mean_field::operators::context::gravity_field
|
||||
@@ -0,0 +1,309 @@
|
||||
module;
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include <mfem.hpp>
|
||||
|
||||
module mean_field;
|
||||
|
||||
import :operators.context.hydrostatic_equilibrium;
|
||||
|
||||
namespace {
|
||||
void validate_finite_vector(
|
||||
const mfem::Vector &vector,
|
||||
const char *message
|
||||
) {
|
||||
for (int i = 0; i < vector.Size(); ++i) {
|
||||
MFEM_VERIFY(std::isfinite(vector(i)), message);
|
||||
}
|
||||
}
|
||||
|
||||
void validate_state(
|
||||
const mean_field::fem::FEM &f,
|
||||
const mean_field::operators::context::hydrostatic::
|
||||
HydrostaticEquilibriumStateView &state
|
||||
) {
|
||||
MFEM_VERIFY(
|
||||
f.enthalpyFes != nullptr,
|
||||
"HydrostaticEquilibriumContext requires the "
|
||||
"enthalpy finite-element space."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
f.gravityPotentialFes != nullptr,
|
||||
"HydrostaticEquilibriumContext requires the "
|
||||
"gravity-potential finite-element space."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
f.displacementFes != nullptr,
|
||||
"HydrostaticEquilibriumContext requires the "
|
||||
"displacement finite-element space."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
state.enthalpy.Size() == f.enthalpyFes->GetTrueVSize(),
|
||||
"HydrostaticEquilibriumContext received an "
|
||||
"enthalpy vector with the wrong size."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
state.gravityPotential.Size() ==
|
||||
f.gravityPotentialFes->GetTrueVSize(),
|
||||
"HydrostaticEquilibriumContext received a "
|
||||
"gravity-potential vector with the wrong size."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
state.displacement.Size() == f.displacementFes->GetTrueVSize(),
|
||||
"HydrostaticEquilibriumContext received a "
|
||||
"displacement vector with the wrong size."
|
||||
);
|
||||
|
||||
validate_finite_vector(
|
||||
state.enthalpy, "HydrostaticEquilibriumContext received a "
|
||||
"non-finite enthalpy value."
|
||||
);
|
||||
|
||||
validate_finite_vector(
|
||||
state.gravityPotential, "HydrostaticEquilibriumContext received a "
|
||||
"non-finite gravity-potential value."
|
||||
);
|
||||
|
||||
validate_finite_vector(
|
||||
state.displacement, "HydrostaticEquilibriumContext received a "
|
||||
"non-finite displacement value."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
std::isfinite(state.bernoulliConstant),
|
||||
"HydrostaticEquilibriumContext received a "
|
||||
"non-finite Bernoulli constant."
|
||||
);
|
||||
}
|
||||
|
||||
template <typename Stamp>
|
||||
void validate_dependency_transition(
|
||||
const Stamp &prepared,
|
||||
const Stamp &requested,
|
||||
const char *message
|
||||
) {
|
||||
MFEM_VERIFY(requested.CanFollow(prepared), message);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace mean_field::operators::context::hydrostatic {
|
||||
HydrostaticEquilibriumContext::HydrostaticEquilibriumContext(
|
||||
const fem::FEM &f,
|
||||
const mapping::DomainMapperStateless &domainMapper
|
||||
)
|
||||
: m_f(f),
|
||||
m_domainMapper(domainMapper) {
|
||||
MFEM_VERIFY(
|
||||
m_f.mesh != nullptr,
|
||||
"HydrostaticEquilibriumContext requires a mesh."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
m_f.enthalpyFes != nullptr,
|
||||
"HydrostaticEquilibriumContext requires the "
|
||||
"enthalpy finite-element space."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
m_f.gravityPotentialFes != nullptr,
|
||||
"HydrostaticEquilibriumContext requires the "
|
||||
"gravity-potential finite-element space."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
m_f.displacementFes != nullptr,
|
||||
"HydrostaticEquilibriumContext requires the "
|
||||
"displacement finite-element space."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
m_domainMapper.GetDimension() == m_f.mesh->Dimension(),
|
||||
"The hydrostatic context's stateless "
|
||||
"domain-mapper dimension does not match the mesh "
|
||||
"dimension."
|
||||
);
|
||||
}
|
||||
|
||||
HydrostaticPreparationReport HydrostaticEquilibriumContext::Prepare(
|
||||
const HydrostaticEquilibriumStateView &state,
|
||||
const HydrostaticEquilibriumDependencies &dependencies
|
||||
) {
|
||||
validate_state(m_f, state);
|
||||
|
||||
if (m_isPrepared) {
|
||||
validate_dependency_transition(
|
||||
m_dependencies.discretization, dependencies.discretization,
|
||||
"HydrostaticEquilibriumContext received an older "
|
||||
"discretization revision for the same identity."
|
||||
);
|
||||
|
||||
validate_dependency_transition(
|
||||
m_dependencies.enthalpy, dependencies.enthalpy,
|
||||
"HydrostaticEquilibriumContext received an older "
|
||||
"enthalpy revision for the same identity."
|
||||
);
|
||||
|
||||
validate_dependency_transition(
|
||||
m_dependencies.gravityPotential, dependencies.gravityPotential,
|
||||
"HydrostaticEquilibriumContext received an older "
|
||||
"gravity-potential revision for the same identity."
|
||||
);
|
||||
|
||||
validate_dependency_transition(
|
||||
m_dependencies.displacement, dependencies.displacement,
|
||||
"HydrostaticEquilibriumContext received an older "
|
||||
"displacement revision for the same identity."
|
||||
);
|
||||
|
||||
validate_dependency_transition(
|
||||
m_dependencies.rotation, dependencies.rotation,
|
||||
"HydrostaticEquilibriumContext received an older "
|
||||
"rotation revision for the same identity."
|
||||
);
|
||||
|
||||
validate_dependency_transition(
|
||||
m_dependencies.bernoulliConstant,
|
||||
dependencies.bernoulliConstant,
|
||||
"HydrostaticEquilibriumContext received an older "
|
||||
"Bernoulli-constant revision for the same identity."
|
||||
);
|
||||
}
|
||||
|
||||
const bool staticChanged =
|
||||
!m_isPrepared ||
|
||||
dependencies.discretization != m_dependencies.discretization;
|
||||
|
||||
const bool enthalpyChanged =
|
||||
!m_isPrepared || dependencies.enthalpy != m_dependencies.enthalpy;
|
||||
|
||||
const bool gravityPotentialChanged =
|
||||
!m_isPrepared ||
|
||||
dependencies.gravityPotential != m_dependencies.gravityPotential;
|
||||
|
||||
const bool displacementChanged =
|
||||
!m_isPrepared ||
|
||||
dependencies.displacement != m_dependencies.displacement;
|
||||
|
||||
const bool rotationChanged =
|
||||
!m_isPrepared || dependencies.rotation != m_dependencies.rotation;
|
||||
|
||||
const bool bernoulliConstantChanged =
|
||||
!m_isPrepared ||
|
||||
dependencies.bernoulliConstant != m_dependencies.bernoulliConstant;
|
||||
|
||||
const bool geometryPreparationRequired =
|
||||
staticChanged || displacementChanged;
|
||||
|
||||
const bool rotationPreparationRequired =
|
||||
geometryPreparationRequired || rotationChanged;
|
||||
|
||||
const bool baseStatePreparationRequired =
|
||||
rotationPreparationRequired || enthalpyChanged ||
|
||||
gravityPotentialChanged || bernoulliConstantChanged;
|
||||
|
||||
HydrostaticPreparationReport report;
|
||||
|
||||
report.preparedStaticDependencies = staticChanged;
|
||||
report.preparedGeometryState = geometryPreparationRequired;
|
||||
report.preparedRotationDependencies = rotationPreparationRequired;
|
||||
report.preparedBaseState = baseStatePreparationRequired;
|
||||
|
||||
if (staticChanged || enthalpyChanged) {
|
||||
m_baseEnthalpyTrue = state.enthalpy;
|
||||
report.updatedEnthalpy = true;
|
||||
}
|
||||
|
||||
if (staticChanged || gravityPotentialChanged) {
|
||||
m_baseGravityPotentialTrue = state.gravityPotential;
|
||||
|
||||
report.updatedGravityPotential = true;
|
||||
}
|
||||
|
||||
if (geometryPreparationRequired) {
|
||||
m_displacementTrue = state.displacement;
|
||||
report.updatedDisplacement = true;
|
||||
}
|
||||
|
||||
if (staticChanged || bernoulliConstantChanged) {
|
||||
m_bernoulliConstant = state.bernoulliConstant;
|
||||
report.updatedBernoulliConstant = true;
|
||||
}
|
||||
|
||||
if (report.preparedStaticDependencies) {
|
||||
++m_statistics.staticPreparations;
|
||||
}
|
||||
|
||||
if (report.preparedGeometryState) {
|
||||
++m_statistics.geometryPreparations;
|
||||
}
|
||||
|
||||
if (report.preparedRotationDependencies) {
|
||||
++m_statistics.rotationPreparations;
|
||||
}
|
||||
|
||||
if (report.preparedBaseState) {
|
||||
++m_statistics.baseStatePreparations;
|
||||
}
|
||||
|
||||
m_dependencies = dependencies;
|
||||
m_isPrepared = true;
|
||||
|
||||
return report;
|
||||
}
|
||||
|
||||
bool HydrostaticEquilibriumContext::IsPrepared() const noexcept {
|
||||
return m_isPrepared;
|
||||
}
|
||||
|
||||
bool HydrostaticEquilibriumContext::MatchesDependencies(
|
||||
const HydrostaticEquilibriumDependencies &dependencies
|
||||
) const noexcept {
|
||||
return m_isPrepared && dependencies == m_dependencies;
|
||||
}
|
||||
|
||||
const HydrostaticEquilibriumDependencies &
|
||||
HydrostaticEquilibriumContext::GetDependencies() const {
|
||||
VerifyPrepared();
|
||||
return m_dependencies;
|
||||
}
|
||||
|
||||
const HydrostaticPreparationStatistics &
|
||||
HydrostaticEquilibriumContext::GetPreparationStatistics() const noexcept {
|
||||
return m_statistics;
|
||||
}
|
||||
|
||||
const mfem::Vector &
|
||||
HydrostaticEquilibriumContext::GetBaseEnthalpyTrue() const {
|
||||
VerifyPrepared();
|
||||
return m_baseEnthalpyTrue;
|
||||
}
|
||||
|
||||
const mfem::Vector &
|
||||
HydrostaticEquilibriumContext::GetBaseGravityPotentialTrue() const {
|
||||
VerifyPrepared();
|
||||
return m_baseGravityPotentialTrue;
|
||||
}
|
||||
|
||||
const mfem::Vector &
|
||||
HydrostaticEquilibriumContext::GetDisplacementTrue() const {
|
||||
VerifyPrepared();
|
||||
return m_displacementTrue;
|
||||
}
|
||||
|
||||
double HydrostaticEquilibriumContext::GetBernoulliConstant() const {
|
||||
VerifyPrepared();
|
||||
return m_bernoulliConstant;
|
||||
}
|
||||
|
||||
void HydrostaticEquilibriumContext::VerifyPrepared() const {
|
||||
MFEM_VERIFY(
|
||||
m_isPrepared, "HydrostaticEquilibriumContext has not been prepared."
|
||||
);
|
||||
}
|
||||
} // namespace mean_field::operators::context::hydrostatic
|
||||
790
libmeanfield/impl/operators/gravity_field.cpp
Normal file
790
libmeanfield/impl/operators/gravity_field.cpp
Normal file
@@ -0,0 +1,790 @@
|
||||
module;
|
||||
#include "profile.h"
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <mfem.hpp>
|
||||
|
||||
module mean_field;
|
||||
import :operators.gravity_field;
|
||||
import :solver.fields;
|
||||
import :operators.kernels.gravity_field;
|
||||
|
||||
namespace {
|
||||
using namespace mean_field;
|
||||
int get_state_width(const mfem::Array<int> &state_true_offsets) {
|
||||
MFEM_VERIFY(
|
||||
state_true_offsets.Size() >= 2,
|
||||
"The coupled state requires at least one block."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
state_true_offsets[0] == 0,
|
||||
"The coupled state offsets must begin at zero."
|
||||
);
|
||||
|
||||
for (int i = 0; i < state_true_offsets.Size() - 1; ++i) {
|
||||
MFEM_VERIFY(
|
||||
state_true_offsets[i + 1] >= state_true_offsets[i],
|
||||
"The coupled state offsets must be nondecreasing."
|
||||
);
|
||||
}
|
||||
|
||||
MFEM_VERIFY(
|
||||
state_true_offsets.Last() > 0, "The coupled state cannot be empty."
|
||||
);
|
||||
return state_true_offsets.Last();
|
||||
}
|
||||
|
||||
int get_gravity_residual_height(const fem::FEM &f) {
|
||||
MFEM_VERIFY(
|
||||
f.gravityFluxFes != nullptr,
|
||||
"GravityFieldOperator requires the gravity-gradient finite-element "
|
||||
"space (RT: Raviart-Thomas)."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.gravityPotentialFes != nullptr,
|
||||
"GravityFieldOperator requires the gravity-potential "
|
||||
"finite-element "
|
||||
"space (L2: Lebesgue "
|
||||
"space of square-integrable functions)."
|
||||
);
|
||||
return f.gravityFluxFes->GetTrueVSize() +
|
||||
f.gravityPotentialFes->GetTrueVSize();
|
||||
}
|
||||
|
||||
mfem::Array<int> make_gravity_residual_offsets(const fem::FEM &f) {
|
||||
mfem::Array<int> offsets(operators::gravity_residual_block_count + 1);
|
||||
offsets[0] = 0;
|
||||
offsets[1] = f.gravityFluxFes->GetTrueVSize();
|
||||
offsets[2] = offsets[1] + f.gravityPotentialFes->GetTrueVSize();
|
||||
return offsets;
|
||||
}
|
||||
|
||||
template <int index>
|
||||
int get_state_block_size(
|
||||
const mfem::Array<int> &state_true_offsets,
|
||||
const utils::blocks::value_block<index>
|
||||
) {
|
||||
MFEM_VERIFY(
|
||||
index + 1 < state_true_offsets.Size(),
|
||||
"Value block is not present in the state offsets."
|
||||
);
|
||||
return state_true_offsets[index + 1] - state_true_offsets[index];
|
||||
}
|
||||
|
||||
void validate_state_offsets(
|
||||
const fem::FEM &f,
|
||||
const mfem::Array<int> &state_true_offsets
|
||||
) {
|
||||
MFEM_VERIFY(
|
||||
f.densityFes != nullptr,
|
||||
"GravityFieldOperator requires the density finite-element space."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.displacementFes != nullptr, "GravityFieldOperator requires the "
|
||||
"displacement finite-element space."
|
||||
);
|
||||
|
||||
using form = utils::blocks::gravity_field_form;
|
||||
|
||||
constexpr auto density_block = utils::blocks::get_value_block<form>(
|
||||
utils::blocks::density_field.mass_term
|
||||
);
|
||||
constexpr auto displacement_block =
|
||||
utils::blocks::get_value_block<form>(
|
||||
utils::blocks::displacement_field.geometry_term
|
||||
);
|
||||
constexpr auto gravity_gradient_block =
|
||||
utils::blocks::get_value_block<form>(
|
||||
utils::blocks::gravity_field.gradient_term
|
||||
);
|
||||
constexpr auto gravity_potential_block =
|
||||
utils::blocks::get_value_block<form>(
|
||||
utils::blocks::gravity_field.poisson_term
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
state_true_offsets.Size() == form::value_block_count + 1,
|
||||
"The gravity state offsets do not match gravity_field_form."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
get_state_block_size(state_true_offsets, density_block) ==
|
||||
f.densityFes->GetTrueVSize(),
|
||||
"The density block does not match the density finite-element space."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
get_state_block_size(state_true_offsets, displacement_block) ==
|
||||
f.displacementFes->GetTrueVSize(),
|
||||
"The displacement block does not match the displacement "
|
||||
"finite-element "
|
||||
"space."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
get_state_block_size(state_true_offsets, gravity_gradient_block) ==
|
||||
f.gravityFluxFes->GetTrueVSize(),
|
||||
"The gravity-gradient block does not match the RT finite-element "
|
||||
"space."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
get_state_block_size(state_true_offsets, gravity_potential_block) ==
|
||||
f.gravityPotentialFes->GetTrueVSize(),
|
||||
"The gravity-potential block does not match the potential "
|
||||
"finite-element space."
|
||||
);
|
||||
}
|
||||
|
||||
void validate_gravity_context(const fem::FEM &f) {
|
||||
MFEM_VERIFY(
|
||||
f.gravityContext.b_form != nullptr,
|
||||
"GravityFieldOperator requires the divergence operator."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.gravityContext.BT != nullptr,
|
||||
"GravityFieldOperator requires the transpose divergence operator."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.quadratureFactory != nullptr,
|
||||
"GravityFieldOperator requires the quadrature-rule factory."
|
||||
);
|
||||
}
|
||||
|
||||
template <int index>
|
||||
mfem::Vector make_read_only_value_view(
|
||||
const mfem::Vector &vector,
|
||||
const mfem::Array<int> &offsets,
|
||||
const utils::blocks::value_block<index>
|
||||
) {
|
||||
MFEM_VERIFY(
|
||||
index + 1 < offsets.Size(),
|
||||
"Value block is not present in the supplied offset array."
|
||||
);
|
||||
|
||||
const int begin = offsets[index];
|
||||
const int size = offsets[index + 1] - begin;
|
||||
|
||||
MFEM_VERIFY(
|
||||
vector.Size() == offsets.Last(),
|
||||
"Vector size does not match the value-block offsets."
|
||||
);
|
||||
return mfem::Vector(
|
||||
const_cast<mfem::real_t *>(vector.GetData()) + begin, size
|
||||
);
|
||||
}
|
||||
|
||||
template <int index>
|
||||
mfem::Vector make_read_only_residual_view(
|
||||
const mfem::Vector &vector,
|
||||
const mfem::Array<int> &offsets,
|
||||
const utils::blocks::residual_block<index> block
|
||||
) {
|
||||
const int block_id = block;
|
||||
const int begin = offsets[block_id];
|
||||
const int size = offsets[block_id + 1] - begin;
|
||||
|
||||
MFEM_VERIFY(
|
||||
vector.Size() == offsets.Last(),
|
||||
"The vector does not match the residual-block layout."
|
||||
);
|
||||
|
||||
mfem::Vector view;
|
||||
view.MakeRef(const_cast<mfem::Vector &>(vector), begin, size);
|
||||
return view;
|
||||
}
|
||||
|
||||
template <int index>
|
||||
mfem::Vector make_residual_view(
|
||||
mfem::Vector &vector,
|
||||
const mfem::Array<int> &offsets,
|
||||
const utils::blocks::residual_block<index>
|
||||
) {
|
||||
MFEM_VERIFY(
|
||||
index + 1 < offsets.Size(),
|
||||
"Residual block is not present in the supplied offset array."
|
||||
);
|
||||
|
||||
const int begin = offsets[index];
|
||||
const int size = offsets[index + 1] - begin;
|
||||
|
||||
MFEM_VERIFY(
|
||||
vector.Size() == offsets.Last(),
|
||||
"Vector size does not match the residual-block offsets."
|
||||
);
|
||||
return mfem::Vector(vector.GetData() + begin, size);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace mean_field::operators {
|
||||
GravityFieldOperator::GravityFieldOperator(
|
||||
fem::FEM &f,
|
||||
const mapping::DomainMapperStateless &domain_mapper,
|
||||
context::gravity_field::GravityFieldLinearizationContext
|
||||
&linearization_context,
|
||||
const mfem::Array<int> &state_true_offsets,
|
||||
GravityFieldJacobianOperator &jacobian
|
||||
)
|
||||
: Operator(
|
||||
get_gravity_residual_height(f),
|
||||
get_state_width(state_true_offsets)
|
||||
),
|
||||
m_fem(f),
|
||||
m_domain_mapper(domain_mapper),
|
||||
m_linearization_context(linearization_context),
|
||||
m_state_true_offsets(state_true_offsets),
|
||||
m_residual_true_offsets(make_gravity_residual_offsets(f)),
|
||||
m_jacobian(jacobian) {
|
||||
MFEM_VERIFY(f.mesh != nullptr, "GravityFieldOperator requires a mesh.");
|
||||
MFEM_VERIFY(
|
||||
f.displacementFes != nullptr, "GravityFieldOperator requires the "
|
||||
"displacement finite-element space."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.smesh.exterior_coordinate != nullptr,
|
||||
"GravityFieldOperator requires the STROID exterior coordinate."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.smesh.exterior_coordinate->space != nullptr,
|
||||
"GravityFieldOperator requires the exterior-coordinate "
|
||||
"finite-element "
|
||||
"space."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.smesh.exterior_coordinate->values != nullptr,
|
||||
"GravityFieldOperator requires the exterior-coordinate values."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
domain_mapper.GetDimension() == f.mesh->Dimension(),
|
||||
"GravityFieldOperator received a domain mapper with the wrong "
|
||||
"dimension."
|
||||
);
|
||||
|
||||
validate_state_offsets(f, m_state_true_offsets);
|
||||
validate_gravity_context(f);
|
||||
|
||||
bool has_vacuum_domain = false;
|
||||
|
||||
for (int i = 0; i < f.mesh->attributes.Size(); ++i) {
|
||||
if (f.mesh->attributes[i] ==
|
||||
domain_mapper.GetVacuumElementAttribute()) {
|
||||
has_vacuum_domain = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
MFEM_VERIFY(
|
||||
has_vacuum_domain,
|
||||
"GravityFieldOperator requires a compactified vacuum domain."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
m_residual_true_offsets.Last() == Height(),
|
||||
"The gravity residual offsets do not match the operator height."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
m_state_true_offsets.Last() == Width(),
|
||||
"The coupled state offsets do not match the operator width."
|
||||
);
|
||||
}
|
||||
|
||||
context::gravity_field::GravityFieldPreparationReport
|
||||
GravityFieldOperator::Prepare(
|
||||
const mfem::Vector &state,
|
||||
const context::gravity_field::GravityFieldRevisions &revisions
|
||||
) {
|
||||
using form = utils::blocks::gravity_field_form;
|
||||
|
||||
constexpr auto density_block = utils::blocks::get_value_block<form>(
|
||||
utils::blocks::density_field.mass_term
|
||||
);
|
||||
constexpr auto displacement_block =
|
||||
utils::blocks::get_value_block<form>(
|
||||
utils::blocks::displacement_field.geometry_term
|
||||
);
|
||||
constexpr auto gravity_gradient_block =
|
||||
utils::blocks::get_value_block<form>(
|
||||
utils::blocks::gravity_field.gradient_term
|
||||
);
|
||||
constexpr auto gravity_potential_block =
|
||||
utils::blocks::get_value_block<form>(
|
||||
utils::blocks::gravity_field.poisson_term
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
state.Size() == Width(), "GravityFieldOperator received a "
|
||||
"preparation state with the wrong size."
|
||||
);
|
||||
|
||||
const mfem::Vector density = make_read_only_value_view(
|
||||
state, m_state_true_offsets, density_block
|
||||
);
|
||||
const mfem::Vector displacement = make_read_only_value_view(
|
||||
state, m_state_true_offsets, displacement_block
|
||||
);
|
||||
const mfem::Vector gravity_gradient = make_read_only_value_view(
|
||||
state, m_state_true_offsets, gravity_gradient_block
|
||||
);
|
||||
const mfem::Vector gravity_potential = make_read_only_value_view(
|
||||
state, m_state_true_offsets, gravity_potential_block
|
||||
);
|
||||
|
||||
return m_linearization_context.Prepare(
|
||||
{.density = density,
|
||||
.displacement = displacement,
|
||||
.gravity_gradient = gravity_gradient,
|
||||
.gravity_potential = gravity_potential},
|
||||
revisions
|
||||
);
|
||||
}
|
||||
|
||||
const mfem::Array<int> &
|
||||
GravityFieldOperator::GetStateTrueOffsets() const noexcept {
|
||||
return m_state_true_offsets;
|
||||
}
|
||||
|
||||
const mfem::Array<int> &
|
||||
GravityFieldOperator::GetResidualTrueOffsets() const noexcept {
|
||||
return m_residual_true_offsets;
|
||||
}
|
||||
|
||||
void GravityFieldOperator::ApplyGravityUnknowns(
|
||||
const mfem::Vector &gravity_gradient,
|
||||
const mfem::Vector &gravity_potential,
|
||||
const context::gravity_field::GravityFieldGeometryContext
|
||||
&geometry_context,
|
||||
mfem::Vector &action
|
||||
) const {
|
||||
using form = utils::blocks::gravity_field_form;
|
||||
|
||||
constexpr auto gravity_gradient_residual_block =
|
||||
utils::blocks::get_residual_block<form>(
|
||||
utils::blocks::gravity_field.gradient_term
|
||||
);
|
||||
constexpr auto gravity_poisson_residual_block =
|
||||
utils::blocks::get_residual_block<form>(
|
||||
utils::blocks::gravity_field.poisson_term
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
geometry_context.IsPrepared(),
|
||||
"GravityFieldOperator received an unprepared geometry context."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
gravity_gradient.Size() == m_fem.gravityFluxFes->GetTrueVSize(),
|
||||
"GravityFieldOperator received a gravity-gradient vector with the "
|
||||
"wrong size."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
gravity_potential.Size() ==
|
||||
m_fem.gravityPotentialFes->GetTrueVSize(),
|
||||
"GravityFieldOperator received a gravity-potential vector with the "
|
||||
"wrong size."
|
||||
);
|
||||
|
||||
action.SetSize(Height());
|
||||
action = 0.0;
|
||||
|
||||
mfem::Vector gravity_gradient_action = make_residual_view(
|
||||
action, m_residual_true_offsets, gravity_gradient_residual_block
|
||||
);
|
||||
mfem::Vector gravity_poisson_action = make_residual_view(
|
||||
action, m_residual_true_offsets, gravity_poisson_residual_block
|
||||
);
|
||||
mfem::Vector transpose_divergence_action(
|
||||
gravity_gradient_action.Size()
|
||||
);
|
||||
|
||||
geometry_context.GetMassOperator().Mult(
|
||||
gravity_gradient, gravity_gradient_action
|
||||
);
|
||||
m_fem.gravityContext.BT->Mult(
|
||||
gravity_potential, transpose_divergence_action
|
||||
);
|
||||
gravity_gradient_action += transpose_divergence_action;
|
||||
m_fem.gravityContext.b_form->Mult(
|
||||
gravity_gradient, gravity_poisson_action
|
||||
);
|
||||
}
|
||||
|
||||
void GravityFieldOperator::ApplyDensitySource(
|
||||
const mfem::Vector &density,
|
||||
const context::gravity_field::GravityFieldGeometryContext
|
||||
&geometry_context,
|
||||
mfem::Vector &action
|
||||
) const {
|
||||
using form = utils::blocks::gravity_field_form;
|
||||
|
||||
constexpr auto gravity_poisson_residual_block =
|
||||
utils::blocks::get_residual_block<form>(
|
||||
utils::blocks::gravity_field.poisson_term
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
geometry_context.IsPrepared(),
|
||||
"GravityFieldOperator received an unprepared geometry context."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
density.Size() == m_fem.densityFes->GetTrueVSize(),
|
||||
"GravityFieldOperator received a density vector with the wrong "
|
||||
"size."
|
||||
);
|
||||
|
||||
action.SetSize(Height());
|
||||
action = 0.0;
|
||||
|
||||
mfem::Vector gravity_poisson_action = make_residual_view(
|
||||
action, m_residual_true_offsets, gravity_poisson_residual_block
|
||||
);
|
||||
geometry_context.GetSourceOperator().Mult(
|
||||
density, gravity_poisson_action
|
||||
);
|
||||
}
|
||||
|
||||
void GravityFieldOperator::Mult(
|
||||
const mfem::Vector &state,
|
||||
mfem::Vector &residual
|
||||
) const {
|
||||
MEAN_FIELD_PROFILE_SCOPE("GravityFieldOperator::Mult");
|
||||
|
||||
using form = utils::blocks::gravity_field_form;
|
||||
|
||||
constexpr auto density_block = utils::blocks::get_value_block<form>(
|
||||
utils::blocks::density_field.mass_term
|
||||
);
|
||||
constexpr auto gravity_gradient_block =
|
||||
utils::blocks::get_value_block<form>(
|
||||
utils::blocks::gravity_field.gradient_term
|
||||
);
|
||||
constexpr auto gravity_potential_block =
|
||||
utils::blocks::get_value_block<form>(
|
||||
utils::blocks::gravity_field.poisson_term
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
state.Size() == Width(),
|
||||
"GravityFieldOperator received a state with the wrong size."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
m_linearization_context.IsPrepared(),
|
||||
"GravityFieldOperator must be prepared before Mult is called."
|
||||
);
|
||||
|
||||
const mfem::Vector density = make_read_only_value_view(
|
||||
state, m_state_true_offsets, density_block
|
||||
);
|
||||
const mfem::Vector gravity_gradient = make_read_only_value_view(
|
||||
state, m_state_true_offsets, gravity_gradient_block
|
||||
);
|
||||
const mfem::Vector gravity_potential = make_read_only_value_view(
|
||||
state, m_state_true_offsets, gravity_potential_block
|
||||
);
|
||||
const context::gravity_field::GravityFieldGeometryContext
|
||||
&geometry_context = m_linearization_context.GetGeometryContext();
|
||||
|
||||
mfem::Vector source;
|
||||
|
||||
ApplyGravityUnknowns(
|
||||
gravity_gradient, gravity_potential, geometry_context, residual
|
||||
);
|
||||
ApplyDensitySource(density, geometry_context, source);
|
||||
|
||||
residual -= source;
|
||||
}
|
||||
|
||||
context::gravity_field::GravityFieldLinearizationContext &
|
||||
GravityFieldOperator::GetLinearizationContext() noexcept {
|
||||
return m_linearization_context;
|
||||
}
|
||||
|
||||
const context::gravity_field::GravityFieldLinearizationContext &
|
||||
GravityFieldOperator::GetLinearizationContext() const noexcept {
|
||||
return m_linearization_context;
|
||||
}
|
||||
|
||||
mfem::Operator &
|
||||
GravityFieldOperator::GetGradient(const mfem::Vector &state) const {
|
||||
MFEM_VERIFY(
|
||||
state.Size() == Width(), "GravityFieldOperator received a "
|
||||
"linearization state with the wrong size."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
m_linearization_context.IsPrepared(),
|
||||
"GravityFieldOperator must be prepared before GetGradient is "
|
||||
"called."
|
||||
);
|
||||
|
||||
return m_jacobian;
|
||||
}
|
||||
ReducedGravityFieldOperator::ReducedGravityFieldOperator(
|
||||
GravityFieldOperator &gravity_field_operator,
|
||||
context::gravity_field::GravityFieldGeometryContext
|
||||
&gravity_field_geometry_context,
|
||||
const mfem::Vector &displacement
|
||||
)
|
||||
: Operator(
|
||||
gravity_field_operator.Height(),
|
||||
gravity_field_operator.Height()
|
||||
),
|
||||
m_gravity_field_operator(gravity_field_operator),
|
||||
m_gravity_true_offsets(
|
||||
gravity_field_operator.GetResidualTrueOffsets()
|
||||
),
|
||||
m_gravity_field_geometry_context(gravity_field_geometry_context) {
|
||||
using form = utils::blocks::gravity_field_form;
|
||||
|
||||
constexpr auto gravity_gradient_block =
|
||||
utils::blocks::get_value_block<form>(
|
||||
utils::blocks::gravity_field.gradient_term
|
||||
);
|
||||
constexpr auto gravity_potential_block =
|
||||
utils::blocks::get_value_block<form>(
|
||||
utils::blocks::gravity_field.poisson_term
|
||||
);
|
||||
constexpr auto gravity_gradient_residual_block =
|
||||
utils::blocks::get_residual_block<form>(
|
||||
utils::blocks::gravity_field.gradient_term
|
||||
);
|
||||
constexpr auto gravity_poisson_residual_block =
|
||||
utils::blocks::get_residual_block<form>(
|
||||
utils::blocks::gravity_field.poisson_term
|
||||
);
|
||||
|
||||
const mfem::Array<int> &state_offsets =
|
||||
m_gravity_field_operator.GetStateTrueOffsets();
|
||||
|
||||
MFEM_VERIFY(
|
||||
state_offsets.Size() == form::value_block_count + 1,
|
||||
"ReducedGravityFieldOperator received an invalid full-state layout."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
m_gravity_true_offsets.Size() == form::residual_block_count + 1,
|
||||
"ReducedGravityFieldOperator received an invalid gravity-residual "
|
||||
"layout."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
state_offsets[0] == 0, "The full-state offsets must begin at zero."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
m_gravity_true_offsets[0] == 0,
|
||||
"The reduced gravity offsets must begin at zero."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
state_offsets.Last() == m_gravity_field_operator.Width(),
|
||||
"The full-state offsets do not match the gravity-field operator "
|
||||
"width."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
m_gravity_true_offsets.Last() == m_gravity_field_operator.Height(),
|
||||
"The reduced gravity offsets do not match the gravity-field "
|
||||
"operator "
|
||||
"height."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
Width() == Height(), "ReducedGravityFieldOperator must be square."
|
||||
);
|
||||
|
||||
const int full_gradient_size =
|
||||
state_offsets[static_cast<int>(gravity_gradient_block) + 1] -
|
||||
state_offsets[gravity_gradient_block];
|
||||
const int full_potential_size =
|
||||
state_offsets[static_cast<int>(gravity_potential_block) + 1] -
|
||||
state_offsets[gravity_potential_block];
|
||||
const int reduced_gradient_size =
|
||||
m_gravity_true_offsets
|
||||
[static_cast<int>(gravity_gradient_residual_block) + 1] -
|
||||
m_gravity_true_offsets[gravity_gradient_residual_block];
|
||||
const int reduced_potential_size =
|
||||
m_gravity_true_offsets
|
||||
[static_cast<int>(gravity_poisson_residual_block) + 1] -
|
||||
m_gravity_true_offsets[gravity_poisson_residual_block];
|
||||
|
||||
MFEM_VERIFY(
|
||||
full_gradient_size == reduced_gradient_size,
|
||||
"The reduced gravity-gradient block does not match the full-state "
|
||||
"gravity-gradient block."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
full_potential_size == reduced_potential_size,
|
||||
"The reduced gravity-potential block does not match the Poisson "
|
||||
"residual block."
|
||||
);
|
||||
|
||||
SetDisplacement(displacement);
|
||||
}
|
||||
|
||||
void ReducedGravityFieldOperator::SetDisplacement(
|
||||
const mfem::Vector &displacement
|
||||
) {
|
||||
ValidateDisplacement(displacement);
|
||||
|
||||
context::gravity_field::DiscretizationRevision discretization_revision;
|
||||
context::gravity_field::DisplacementRevision displacement_revision;
|
||||
|
||||
if (m_gravity_field_geometry_context.IsPrepared()) {
|
||||
discretization_revision =
|
||||
m_gravity_field_geometry_context.GetDiscretizationRevision();
|
||||
displacement_revision =
|
||||
m_gravity_field_geometry_context.GetDisplacementRevision();
|
||||
|
||||
MFEM_VERIFY(
|
||||
displacement_revision.value <
|
||||
std::numeric_limits<std::uint64_t>::max(),
|
||||
"The reduced gravity displacement revision has overflowed."
|
||||
);
|
||||
++displacement_revision.value;
|
||||
}
|
||||
|
||||
m_gravity_field_geometry_context.Prepare(
|
||||
displacement, discretization_revision, displacement_revision
|
||||
);
|
||||
}
|
||||
|
||||
const mfem::Vector &ReducedGravityFieldOperator::GetDisplacement() const {
|
||||
return m_gravity_field_geometry_context.GetDisplacement();
|
||||
}
|
||||
|
||||
void ReducedGravityFieldOperator::BuildRightHandSide(
|
||||
const mfem::Vector &density,
|
||||
mfem::Vector &right_hand_side
|
||||
) const {
|
||||
ValidateDensity(density);
|
||||
|
||||
m_gravity_field_operator.ApplyDensitySource(
|
||||
density, m_gravity_field_geometry_context, right_hand_side
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
right_hand_side.Size() == Height(),
|
||||
"ReducedGravityFieldOperator produced a right-hand side with the "
|
||||
"wrong "
|
||||
"size."
|
||||
);
|
||||
}
|
||||
|
||||
void ReducedGravityFieldOperator::Mult(
|
||||
const mfem::Vector &gravity_state,
|
||||
mfem::Vector &action
|
||||
) const {
|
||||
MEAN_FIELD_PROFILE_SCOPE("ReducedGravityFieldOperator::Mult");
|
||||
|
||||
using form = utils::blocks::gravity_field_form;
|
||||
|
||||
constexpr auto gravity_gradient_residual_block =
|
||||
utils::blocks::get_residual_block<form>(
|
||||
utils::blocks::gravity_field.gradient_term
|
||||
);
|
||||
constexpr auto gravity_poisson_residual_block =
|
||||
utils::blocks::get_residual_block<form>(
|
||||
utils::blocks::gravity_field.poisson_term
|
||||
);
|
||||
|
||||
ValidateGravityState(gravity_state);
|
||||
|
||||
const mfem::Vector gravity_gradient_true = make_read_only_residual_view(
|
||||
gravity_state, m_gravity_true_offsets,
|
||||
gravity_gradient_residual_block
|
||||
);
|
||||
const mfem::Vector gravity_potential_true =
|
||||
make_read_only_residual_view(
|
||||
gravity_state, m_gravity_true_offsets,
|
||||
gravity_poisson_residual_block
|
||||
);
|
||||
|
||||
m_gravity_field_operator.ApplyGravityUnknowns(
|
||||
gravity_gradient_true, gravity_potential_true,
|
||||
m_gravity_field_geometry_context, action
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
action.Size() == Height(), "ReducedGravityFieldOperator produced "
|
||||
"an action with the wrong size."
|
||||
);
|
||||
}
|
||||
|
||||
GravityFieldOperator &
|
||||
ReducedGravityFieldOperator::GetGravityFieldOperator() noexcept {
|
||||
return m_gravity_field_operator;
|
||||
}
|
||||
|
||||
const GravityFieldOperator &
|
||||
ReducedGravityFieldOperator::GetGravityFieldOperator() const noexcept {
|
||||
return m_gravity_field_operator;
|
||||
}
|
||||
|
||||
context::gravity_field::GravityFieldGeometryContext &
|
||||
ReducedGravityFieldOperator::GetGeometryContext() noexcept {
|
||||
return m_gravity_field_geometry_context;
|
||||
}
|
||||
|
||||
const context::gravity_field::GravityFieldGeometryContext &
|
||||
ReducedGravityFieldOperator::GetGeometryContext() const noexcept {
|
||||
return m_gravity_field_geometry_context;
|
||||
}
|
||||
|
||||
const mfem::Array<int> &
|
||||
ReducedGravityFieldOperator::GetGravityTrueOffsets() const noexcept {
|
||||
return m_gravity_true_offsets;
|
||||
}
|
||||
|
||||
void ReducedGravityFieldOperator::ValidateDisplacement(
|
||||
const mfem::Vector &displacement
|
||||
) const {
|
||||
using form = utils::blocks::gravity_field_form;
|
||||
|
||||
constexpr auto displacement_block =
|
||||
utils::blocks::get_value_block<form>(
|
||||
utils::blocks::displacement_field.geometry_term
|
||||
);
|
||||
|
||||
const mfem::Array<int> &state_offsets =
|
||||
m_gravity_field_operator.GetStateTrueOffsets();
|
||||
const int expected_size =
|
||||
state_offsets[static_cast<int>(displacement_block) + 1] -
|
||||
state_offsets[displacement_block];
|
||||
|
||||
MFEM_VERIFY(
|
||||
displacement.Size() == expected_size,
|
||||
"ReducedGravityFieldOperator received a displacement with the "
|
||||
"wrong "
|
||||
"size."
|
||||
);
|
||||
|
||||
for (int i = 0; i < displacement.Size(); ++i) {
|
||||
MFEM_VERIFY(
|
||||
std::isfinite(displacement(i)),
|
||||
"ReducedGravityFieldOperator received a non-finite "
|
||||
"displacement "
|
||||
"value."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void ReducedGravityFieldOperator::ValidateDensity(
|
||||
const mfem::Vector &density
|
||||
) const {
|
||||
using form = utils::blocks::gravity_field_form;
|
||||
|
||||
constexpr auto density_block = utils::blocks::get_value_block<form>(
|
||||
utils::blocks::density_field.mass_term
|
||||
);
|
||||
|
||||
const mfem::Array<int> &state_offsets =
|
||||
m_gravity_field_operator.GetStateTrueOffsets();
|
||||
const int expected_size =
|
||||
state_offsets[static_cast<int>(density_block) + 1] -
|
||||
state_offsets[density_block];
|
||||
|
||||
MFEM_VERIFY(
|
||||
density.Size() == expected_size,
|
||||
"ReducedGravityFieldOperator received a density with the wrong "
|
||||
"size."
|
||||
);
|
||||
}
|
||||
|
||||
void ReducedGravityFieldOperator::ValidateGravityState(
|
||||
const mfem::Vector &gravity_state
|
||||
) const {
|
||||
MFEM_VERIFY(
|
||||
gravity_state.Size() == Width(),
|
||||
"ReducedGravityFieldOperator received "
|
||||
"a gravity state with the wrong size."
|
||||
);
|
||||
}
|
||||
} // namespace mean_field::operators
|
||||
317
libmeanfield/impl/operators/gravity_field_jacobian.cpp
Normal file
317
libmeanfield/impl/operators/gravity_field_jacobian.cpp
Normal file
@@ -0,0 +1,317 @@
|
||||
module;
|
||||
#include <mfem.hpp>
|
||||
|
||||
module mean_field;
|
||||
import :operators.gravity_field_jacobian;
|
||||
import :operators.kernels.gravity_field;
|
||||
import :utils.blocks;
|
||||
|
||||
namespace {
|
||||
template <int index>
|
||||
mfem::Vector make_read_only_value_view(
|
||||
const mfem::Vector &vector,
|
||||
const mfem::Array<int> &offsets,
|
||||
const mean_field::utils::blocks::value_block<index>
|
||||
) {
|
||||
const int offset = offsets[index];
|
||||
const int size = offsets[index + 1] - offset;
|
||||
return mfem::Vector(
|
||||
const_cast<mfem::real_t *>(vector.GetData()) + offset, size
|
||||
);
|
||||
}
|
||||
|
||||
template <int index>
|
||||
mfem::Vector make_residual_view(
|
||||
mfem::Vector &vector,
|
||||
const mfem::Array<int> &offsets,
|
||||
const mean_field::utils::blocks::residual_block<index>
|
||||
) {
|
||||
const int offset = offsets[index];
|
||||
const int size = offsets[index + 1] - offset;
|
||||
return mfem::Vector(vector.GetData() + offset, size);
|
||||
}
|
||||
|
||||
template <int index>
|
||||
int get_block_size(
|
||||
const mfem::Array<int> &offsets,
|
||||
const mean_field::utils::blocks::value_block<index>
|
||||
) {
|
||||
return offsets[index + 1] - offsets[index];
|
||||
}
|
||||
|
||||
template <int index>
|
||||
int get_block_size(
|
||||
const mfem::Array<int> &offsets,
|
||||
const mean_field::utils::blocks::residual_block<index>
|
||||
) {
|
||||
return offsets[index + 1] - offsets[index];
|
||||
}
|
||||
|
||||
void validate_offsets(
|
||||
const mfem::Array<int> &offsets,
|
||||
const int block_count,
|
||||
const char *message
|
||||
) {
|
||||
MFEM_VERIFY(offsets.Size() == block_count + 1, message);
|
||||
MFEM_VERIFY(offsets[0] == 0, "Block offsets must begin at zero.");
|
||||
|
||||
for (int i = 0; i < block_count; ++i)
|
||||
MFEM_VERIFY(
|
||||
offsets[i + 1] >= offsets[i],
|
||||
"Block offsets must be nondecreasing."
|
||||
);
|
||||
}
|
||||
|
||||
void validate_layout(
|
||||
const mean_field::fem::FEM &f,
|
||||
const mfem::Array<int> &state_offsets,
|
||||
const mfem::Array<int> &residual_offsets
|
||||
) {
|
||||
using form = mean_field::utils::blocks::gravity_field_form;
|
||||
|
||||
constexpr auto density_block =
|
||||
mean_field::utils::blocks::get_value_block<form>(
|
||||
mean_field::utils::blocks::density_field.mass_term
|
||||
);
|
||||
constexpr auto displacement_block =
|
||||
mean_field::utils::blocks::get_value_block<form>(
|
||||
mean_field::utils::blocks::displacement_field.geometry_term
|
||||
);
|
||||
constexpr auto gravity_gradient_block =
|
||||
mean_field::utils::blocks::get_value_block<form>(
|
||||
mean_field::utils::blocks::gravity_field.gradient_term
|
||||
);
|
||||
constexpr auto gravity_potential_block =
|
||||
mean_field::utils::blocks::get_value_block<form>(
|
||||
mean_field::utils::blocks::gravity_field.poisson_term
|
||||
);
|
||||
constexpr auto gravity_gradient_residual_block =
|
||||
mean_field::utils::blocks::get_residual_block<form>(
|
||||
mean_field::utils::blocks::gravity_field.gradient_term
|
||||
);
|
||||
constexpr auto gravity_poisson_residual_block =
|
||||
mean_field::utils::blocks::get_residual_block<form>(
|
||||
mean_field::utils::blocks::gravity_field.poisson_term
|
||||
);
|
||||
|
||||
validate_offsets(
|
||||
state_offsets, form::value_block_count,
|
||||
"Gravity Jacobian state offsets do not match the gravity field "
|
||||
"form."
|
||||
);
|
||||
validate_offsets(
|
||||
residual_offsets, form::residual_block_count,
|
||||
"Gravity Jacobian residual offsets do not match the gravity field "
|
||||
"form."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
get_block_size(state_offsets, density_block) ==
|
||||
f.densityFes->GetTrueVSize(),
|
||||
"The Jacobian density block has the wrong size."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
get_block_size(state_offsets, displacement_block) ==
|
||||
f.displacementFes->GetTrueVSize(),
|
||||
"The Jacobian displacement block has the wrong size."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
get_block_size(state_offsets, gravity_gradient_block) ==
|
||||
f.gravityFluxFes->GetTrueVSize(),
|
||||
"The Jacobian gravity-gradient block has the wrong size."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
get_block_size(state_offsets, gravity_potential_block) ==
|
||||
f.gravityPotentialFes->GetTrueVSize(),
|
||||
"The Jacobian gravity-potential block has the wrong size."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
get_block_size(residual_offsets, gravity_gradient_residual_block) ==
|
||||
f.gravityFluxFes->GetTrueVSize(),
|
||||
"The Jacobian gradient-residual block has the wrong size."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
get_block_size(residual_offsets, gravity_poisson_residual_block) ==
|
||||
f.gravityPotentialFes->GetTrueVSize(),
|
||||
"The Jacobian Poisson-residual block has the wrong size."
|
||||
);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace mean_field::operators {
|
||||
GravityFieldJacobianOperator::GravityFieldJacobianOperator(
|
||||
fem::FEM &f,
|
||||
const mapping::DomainMapperStateless &domain_mapper,
|
||||
const context::gravity_field::GravityFieldLinearizationContext
|
||||
&linearization_context,
|
||||
const mfem::Array<int> &state_true_offsets,
|
||||
const mfem::Array<int> &residual_true_offsets
|
||||
)
|
||||
: Operator(
|
||||
residual_true_offsets.Last(),
|
||||
state_true_offsets.Last()
|
||||
),
|
||||
m_fem(f),
|
||||
m_domain_mapper(domain_mapper),
|
||||
m_linearization_context(linearization_context),
|
||||
m_state_true_offsets(state_true_offsets),
|
||||
m_residual_true_offsets(residual_true_offsets) {
|
||||
MFEM_VERIFY(
|
||||
f.densityFes != nullptr,
|
||||
"GravityFieldJacobianOperator requires the density finite-element "
|
||||
"space."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.gravityPotentialFes != nullptr,
|
||||
"GravityFieldJacobianOperator requires the gravity-potential "
|
||||
"finite-element space."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.gravityFluxFes != nullptr,
|
||||
"GravityFieldJacobianOperator requires the "
|
||||
"gravity-gradient finite-element space."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.displacementFes != nullptr,
|
||||
"GravityFieldJacobianOperator requires the "
|
||||
"displacement finite-element space."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.gravityContext.b_form != nullptr,
|
||||
"GravityFieldJacobianOperator requires the divergence operator."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.gravityContext.BT != nullptr,
|
||||
"GravityFieldJacobianOperator requires the transpose divergence "
|
||||
"operator."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.quadratureFactory != nullptr,
|
||||
"GravityFieldJacobianOperator requires the quadrature-rule factory."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
domain_mapper.GetDimension() == f.mesh->Dimension(),
|
||||
"GravityFieldJacobianOperator received a domain mapper with the "
|
||||
"wrong "
|
||||
"dimension."
|
||||
);
|
||||
|
||||
validate_layout(f, m_state_true_offsets, m_residual_true_offsets);
|
||||
}
|
||||
|
||||
void GravityFieldJacobianOperator::Mult(
|
||||
const mfem::Vector &direction,
|
||||
mfem::Vector &action
|
||||
) const {
|
||||
MFEM_VERIFY(
|
||||
m_linearization_context.IsPrepared(),
|
||||
"GravityFieldJacobianOperator requires a prepared linearization "
|
||||
"context."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
direction.Size() == Width(),
|
||||
"GravityFieldJacobianOperator received a direction with the wrong "
|
||||
"size."
|
||||
);
|
||||
|
||||
using form = utils::blocks::gravity_field_form;
|
||||
|
||||
constexpr auto density_block = utils::blocks::get_value_block<form>(
|
||||
utils::blocks::density_field.mass_term
|
||||
);
|
||||
constexpr auto displacement_block =
|
||||
utils::blocks::get_value_block<form>(
|
||||
utils::blocks::displacement_field.geometry_term
|
||||
);
|
||||
constexpr auto gravity_gradient_block =
|
||||
utils::blocks::get_value_block<form>(
|
||||
utils::blocks::gravity_field.gradient_term
|
||||
);
|
||||
constexpr auto gravity_potential_block =
|
||||
utils::blocks::get_value_block<form>(
|
||||
utils::blocks::gravity_field.poisson_term
|
||||
);
|
||||
constexpr auto gravity_gradient_residual_block =
|
||||
utils::blocks::get_residual_block<form>(
|
||||
utils::blocks::gravity_field.gradient_term
|
||||
);
|
||||
constexpr auto gravity_poisson_residual_block =
|
||||
utils::blocks::get_residual_block<form>(
|
||||
utils::blocks::gravity_field.poisson_term
|
||||
);
|
||||
|
||||
const context::gravity_field::GravityFieldGeometryContext
|
||||
&geometry_context = m_linearization_context.GetGeometryContext();
|
||||
const mfem::Vector &density = m_linearization_context.GetDensity();
|
||||
const mfem::Vector &displacement = geometry_context.GetDisplacement();
|
||||
const mfem::Vector &gravity_gradient =
|
||||
m_linearization_context.GetGravityGradient();
|
||||
|
||||
const mfem::Vector density_direction = make_read_only_value_view(
|
||||
direction, m_state_true_offsets, density_block
|
||||
);
|
||||
const mfem::Vector displacement_direction = make_read_only_value_view(
|
||||
direction, m_state_true_offsets, displacement_block
|
||||
);
|
||||
const mfem::Vector gravity_gradient_direction =
|
||||
make_read_only_value_view(
|
||||
direction, m_state_true_offsets, gravity_gradient_block
|
||||
);
|
||||
const mfem::Vector gravity_potential_direction =
|
||||
make_read_only_value_view(
|
||||
direction, m_state_true_offsets, gravity_potential_block
|
||||
);
|
||||
|
||||
action.SetSize(Height());
|
||||
action = 0.0;
|
||||
|
||||
mfem::Vector gravity_gradient_action = make_residual_view(
|
||||
action, m_residual_true_offsets, gravity_gradient_residual_block
|
||||
);
|
||||
mfem::Vector gravity_poisson_action = make_residual_view(
|
||||
action, m_residual_true_offsets, gravity_poisson_residual_block
|
||||
);
|
||||
|
||||
mfem::Vector transpose_divergence_action;
|
||||
mfem::Vector source_action;
|
||||
mfem::Vector mass_variation_action;
|
||||
mfem::Vector source_variation_action;
|
||||
|
||||
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, mass_variation_action
|
||||
);
|
||||
|
||||
kernels::apply_mapped_source_variation(
|
||||
m_fem, m_domain_mapper, density, displacement,
|
||||
displacement_direction, source_variation_action
|
||||
);
|
||||
|
||||
transpose_divergence_action.SetSize(gravity_gradient_action.Size());
|
||||
m_fem.gravityContext.BT->Mult(
|
||||
gravity_potential_direction, transpose_divergence_action
|
||||
);
|
||||
|
||||
gravity_gradient_action += transpose_divergence_action;
|
||||
gravity_gradient_action += mass_variation_action;
|
||||
|
||||
m_fem.gravityContext.b_form->Mult(
|
||||
gravity_gradient_direction, gravity_poisson_action
|
||||
);
|
||||
|
||||
gravity_poisson_action -= source_action;
|
||||
gravity_poisson_action -= source_variation_action;
|
||||
}
|
||||
|
||||
const context::gravity_field::GravityFieldLinearizationContext &
|
||||
GravityFieldJacobianOperator::GetLinearizationContext() const noexcept {
|
||||
return m_linearization_context;
|
||||
}
|
||||
} // namespace mean_field::operators
|
||||
@@ -0,0 +1,811 @@
|
||||
module;
|
||||
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
#include <mfem.hpp>
|
||||
|
||||
module mean_field;
|
||||
|
||||
import :operators.kernels.barotropic_closure;
|
||||
|
||||
namespace {
|
||||
enum class ClosureAction { residual, density, enthalpy };
|
||||
|
||||
void true_to_local(
|
||||
const mfem::ParFiniteElementSpace &finiteElementSpace,
|
||||
const mfem::Vector &trueVector,
|
||||
mfem::Vector &localVector
|
||||
) {
|
||||
MFEM_VERIFY(
|
||||
trueVector.Size() == finiteElementSpace.GetTrueVSize(),
|
||||
"True vector has the wrong size."
|
||||
);
|
||||
|
||||
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
|
||||
) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
int get_eos_extra_order(
|
||||
const mean_field::physics::PolytropicBarotrope &barotrope
|
||||
) {
|
||||
const double extraOrder =
|
||||
(barotrope.polytropic_index() - 1.0) *
|
||||
static_cast<double>(
|
||||
mean_field::field::Enthalpy::Scalar::familyOrder
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
std::isfinite(extraOrder) && extraOrder >= 0.0 &&
|
||||
extraOrder <=
|
||||
static_cast<double>(std::numeric_limits<int>::max()),
|
||||
"The EOS effective polynomial order is invalid."
|
||||
);
|
||||
|
||||
return static_cast<int>(std::ceil(extraOrder));
|
||||
}
|
||||
|
||||
const mfem::IntegrationRule &get_eos_rule(
|
||||
const mean_field::fem::FEM &f,
|
||||
const mean_field::physics::PolytropicBarotrope &barotrope,
|
||||
const mfem::FiniteElement &densityElement,
|
||||
const mfem::FiniteElement &enthalpyElement,
|
||||
const mfem::ElementTransformation &transformation
|
||||
) {
|
||||
using EnthalpyField =
|
||||
mean_field::field::Field<mean_field::field::Enthalpy>;
|
||||
|
||||
MFEM_VERIFY(
|
||||
densityElement.GetOrder() ==
|
||||
mean_field::field::Density::Scalar::familyOrder,
|
||||
"The EOS test element does not match the "
|
||||
"registered density field."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
enthalpyElement.GetOrder() ==
|
||||
mean_field::field::Enthalpy::Scalar::familyOrder,
|
||||
"The EOS trial element does not match the "
|
||||
"registered enthalpy field."
|
||||
);
|
||||
|
||||
const mean_field::quadrature::Query query = EnthalpyField::make_query<
|
||||
mean_field::field::Enthalpy::Form::EosClosureSource>(
|
||||
mean_field::quadrature::QuadratureRole::discretization,
|
||||
transformation.OrderW(),
|
||||
std::array<int, 1>{get_eos_extra_order(barotrope)},
|
||||
mean_field::utils::DOMAINS::STELLAR,
|
||||
mean_field::quadrature::MappingKind::general
|
||||
);
|
||||
|
||||
const auto resolution =
|
||||
f.quadratureFactory->get(query, transformation.GetGeometryType());
|
||||
|
||||
MFEM_VERIFY(
|
||||
resolution.integration_rule != nullptr,
|
||||
"The quadrature policy did not return an "
|
||||
"EOS-closure integration rule."
|
||||
);
|
||||
|
||||
return *resolution.integration_rule;
|
||||
}
|
||||
|
||||
void validate_common_inputs(
|
||||
const mean_field::fem::FEM &f,
|
||||
const mean_field::mapping::DomainMapperStateless &domainMapper,
|
||||
const mfem::Vector &displacementTrue
|
||||
) {
|
||||
MFEM_VERIFY(
|
||||
f.mesh != nullptr, "The EOS closure kernel requires a mesh."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.densityFes != nullptr,
|
||||
"The EOS closure kernel requires the density "
|
||||
"finite-element space."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.enthalpyFes != nullptr,
|
||||
"The EOS closure kernel requires the enthalpy "
|
||||
"finite-element space."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.displacementFes != nullptr,
|
||||
"The EOS closure kernel requires the displacement "
|
||||
"finite-element space."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.compactificationFes != nullptr,
|
||||
"The EOS closure kernel requires the "
|
||||
"compactification finite-element space."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.compactificationCoordinate != nullptr,
|
||||
"The EOS closure kernel requires the "
|
||||
"compactification coordinate."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.quadratureFactory != nullptr,
|
||||
"The EOS closure kernel requires the quadrature "
|
||||
"rule factory."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
displacementTrue.Size() == f.displacementFes->GetTrueVSize(),
|
||||
"The displacement vector has the wrong size."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
domainMapper.GetDimension() == f.mesh->Dimension(),
|
||||
"The domain-mapper dimension does not match "
|
||||
"the mesh dimension."
|
||||
);
|
||||
}
|
||||
|
||||
void apply_closure_action(
|
||||
const mean_field::fem::FEM &f,
|
||||
const mean_field::mapping::DomainMapperStateless &domainMapper,
|
||||
const mean_field::physics::PolytropicBarotrope &barotrope,
|
||||
const ClosureAction closureAction,
|
||||
const mfem::Vector *densityInputTrue,
|
||||
const mfem::Vector *baseEnthalpyTrue,
|
||||
const mfem::Vector *enthalpyVariationTrue,
|
||||
const mfem::Vector &displacementTrue,
|
||||
mfem::Vector &action
|
||||
) {
|
||||
validate_common_inputs(f, domainMapper, displacementTrue);
|
||||
|
||||
if (closureAction == ClosureAction::residual ||
|
||||
closureAction == ClosureAction::density) {
|
||||
MFEM_VERIFY(
|
||||
densityInputTrue != nullptr &&
|
||||
densityInputTrue->Size() == f.densityFes->GetTrueVSize(),
|
||||
"The density input has the wrong size."
|
||||
);
|
||||
}
|
||||
|
||||
if (closureAction == ClosureAction::residual ||
|
||||
closureAction == ClosureAction::enthalpy) {
|
||||
MFEM_VERIFY(
|
||||
baseEnthalpyTrue != nullptr &&
|
||||
baseEnthalpyTrue->Size() == f.enthalpyFes->GetTrueVSize(),
|
||||
"The base enthalpy has the wrong size."
|
||||
);
|
||||
}
|
||||
|
||||
if (closureAction == ClosureAction::enthalpy) {
|
||||
MFEM_VERIFY(
|
||||
enthalpyVariationTrue != nullptr &&
|
||||
enthalpyVariationTrue->Size() ==
|
||||
f.enthalpyFes->GetTrueVSize(),
|
||||
"The enthalpy variation has the wrong size."
|
||||
);
|
||||
}
|
||||
|
||||
mfem::Vector densityInputLocal;
|
||||
mfem::Vector baseEnthalpyLocal;
|
||||
mfem::Vector enthalpyVariationLocal;
|
||||
mfem::Vector displacementLocal;
|
||||
|
||||
if (densityInputTrue != nullptr) {
|
||||
true_to_local(*f.densityFes, *densityInputTrue, densityInputLocal);
|
||||
}
|
||||
|
||||
if (baseEnthalpyTrue != nullptr) {
|
||||
true_to_local(*f.enthalpyFes, *baseEnthalpyTrue, baseEnthalpyLocal);
|
||||
}
|
||||
|
||||
if (enthalpyVariationTrue != nullptr) {
|
||||
true_to_local(
|
||||
*f.enthalpyFes, *enthalpyVariationTrue, enthalpyVariationLocal
|
||||
);
|
||||
}
|
||||
|
||||
true_to_local(*f.displacementFes, displacementTrue, displacementLocal);
|
||||
|
||||
mfem::Vector localAction(f.densityFes->GetVSize());
|
||||
localAction = 0.0;
|
||||
|
||||
mean_field::mapping::DomainMapperStateless::Workspace workspace(
|
||||
f.mesh->Dimension()
|
||||
);
|
||||
|
||||
mfem::Array<int> densityDofs;
|
||||
mfem::Array<int> enthalpyDofs;
|
||||
mfem::Array<int> displacementDofs;
|
||||
mfem::Array<int> compactificationDofs;
|
||||
|
||||
mfem::Vector elementDensityInput;
|
||||
mfem::Vector elementBaseEnthalpy;
|
||||
mfem::Vector elementEnthalpyVariation;
|
||||
mfem::Vector elementDisplacement;
|
||||
mfem::Vector elementCompactification;
|
||||
mfem::Vector elementAction;
|
||||
|
||||
mfem::Vector densityShape;
|
||||
mfem::Vector enthalpyShape;
|
||||
|
||||
const int vacuumAttribute = domainMapper.GetVacuumElementAttribute();
|
||||
|
||||
for (int elementId = 0; elementId < f.mesh->GetNE(); ++elementId) {
|
||||
mfem::ElementTransformation *transformation =
|
||||
f.mesh->GetElementTransformation(elementId);
|
||||
|
||||
MFEM_VERIFY(
|
||||
transformation != nullptr,
|
||||
"The EOS closure kernel received a null "
|
||||
"element transformation."
|
||||
);
|
||||
|
||||
if (transformation->Attribute == vacuumAttribute) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const mfem::FiniteElement &densityElement =
|
||||
*f.densityFes->GetFE(elementId);
|
||||
const mfem::FiniteElement &enthalpyElement =
|
||||
*f.enthalpyFes->GetFE(elementId);
|
||||
const mfem::FiniteElement &displacementElement =
|
||||
*f.displacementFes->GetFE(elementId);
|
||||
const mfem::FiniteElement &compactificationElement =
|
||||
*f.compactificationFes->GetFE(elementId);
|
||||
|
||||
mfem::DofTransformation *densityDofTransformation =
|
||||
f.densityFes->GetElementDofs(elementId, densityDofs);
|
||||
|
||||
mfem::DofTransformation *enthalpyDofTransformation =
|
||||
f.enthalpyFes->GetElementDofs(elementId, enthalpyDofs);
|
||||
|
||||
mfem::DofTransformation *displacementDofTransformation =
|
||||
f.displacementFes->GetElementVDofs(elementId, displacementDofs);
|
||||
|
||||
mfem::DofTransformation *compactificationDofTransformation =
|
||||
f.compactificationFes->GetElementDofs(
|
||||
elementId, compactificationDofs
|
||||
);
|
||||
|
||||
if (densityInputTrue != nullptr) {
|
||||
densityInputLocal.GetSubVector(
|
||||
densityDofs, elementDensityInput
|
||||
);
|
||||
|
||||
if (densityDofTransformation != nullptr) {
|
||||
densityDofTransformation->InvTransformPrimal(
|
||||
elementDensityInput
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (baseEnthalpyTrue != nullptr) {
|
||||
baseEnthalpyLocal.GetSubVector(
|
||||
enthalpyDofs, elementBaseEnthalpy
|
||||
);
|
||||
|
||||
if (enthalpyDofTransformation != nullptr) {
|
||||
enthalpyDofTransformation->InvTransformPrimal(
|
||||
elementBaseEnthalpy
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (enthalpyVariationTrue != nullptr) {
|
||||
enthalpyVariationLocal.GetSubVector(
|
||||
enthalpyDofs, elementEnthalpyVariation
|
||||
);
|
||||
|
||||
if (enthalpyDofTransformation != nullptr) {
|
||||
enthalpyDofTransformation->InvTransformPrimal(
|
||||
elementEnthalpyVariation
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
displacementLocal.GetSubVector(
|
||||
displacementDofs, elementDisplacement
|
||||
);
|
||||
|
||||
f.compactificationCoordinate->GetSubVector(
|
||||
compactificationDofs, elementCompactification
|
||||
);
|
||||
|
||||
if (displacementDofTransformation != nullptr) {
|
||||
displacementDofTransformation->InvTransformPrimal(
|
||||
elementDisplacement
|
||||
);
|
||||
}
|
||||
|
||||
if (compactificationDofTransformation != nullptr) {
|
||||
compactificationDofTransformation->InvTransformPrimal(
|
||||
elementCompactification
|
||||
);
|
||||
}
|
||||
|
||||
const mean_field::mapping::ElementDisplacementData
|
||||
displacementData = mean_field::mapping::
|
||||
ElementDisplacementDataFromElementVDofs(
|
||||
displacementElement, elementDisplacement
|
||||
);
|
||||
|
||||
const mean_field::mapping::ElementCompactificationData
|
||||
compactificationData(
|
||||
compactificationElement, elementCompactification
|
||||
);
|
||||
|
||||
const mean_field::mapping::ElementMappingData mappingData{
|
||||
.displacement = displacementData,
|
||||
.compactification = compactificationData
|
||||
};
|
||||
|
||||
densityShape.SetSize(densityElement.GetDof());
|
||||
enthalpyShape.SetSize(enthalpyElement.GetDof());
|
||||
elementAction.SetSize(densityElement.GetDof());
|
||||
elementAction = 0.0;
|
||||
|
||||
const mfem::IntegrationRule &integrationRule = get_eos_rule(
|
||||
f, barotrope, densityElement, enthalpyElement, *transformation
|
||||
);
|
||||
|
||||
for (int quadratureIndex = 0;
|
||||
quadratureIndex < integrationRule.GetNPoints();
|
||||
++quadratureIndex) {
|
||||
const mfem::IntegrationPoint &integrationPoint =
|
||||
integrationRule.IntPoint(quadratureIndex);
|
||||
|
||||
transformation->SetIntPoint(&integrationPoint);
|
||||
|
||||
mean_field::mapping::VolumeMappingContext mappingContext;
|
||||
|
||||
const mean_field::mapping::MappingStatus mappingStatus =
|
||||
domainMapper.EvaluateVolume(
|
||||
mappingData, *transformation, integrationPoint,
|
||||
workspace, mappingContext
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
mappingStatus == mean_field::mapping::MappingStatus::valid,
|
||||
"Stateless mapping failed in the EOS "
|
||||
"closure kernel. Element: "
|
||||
<< elementId
|
||||
<< ", attribute: " << transformation->Attribute
|
||||
<< ", quadrature point: " << quadratureIndex
|
||||
<< ", status: " << static_cast<int>(mappingStatus)
|
||||
);
|
||||
|
||||
densityElement.CalcShape(integrationPoint, densityShape);
|
||||
|
||||
double integrand = 0.0;
|
||||
|
||||
if (closureAction == ClosureAction::density) {
|
||||
integrand = elementDensityInput * densityShape;
|
||||
} else {
|
||||
enthalpyElement.CalcShape(integrationPoint, enthalpyShape);
|
||||
|
||||
const double baseEnthalpy =
|
||||
elementBaseEnthalpy * enthalpyShape;
|
||||
|
||||
if (closureAction == ClosureAction::residual) {
|
||||
const double density =
|
||||
elementDensityInput * densityShape;
|
||||
|
||||
integrand =
|
||||
density -
|
||||
barotrope.density_from_enthalpy(baseEnthalpy);
|
||||
} else {
|
||||
const double enthalpyVariation =
|
||||
elementEnthalpyVariation * enthalpyShape;
|
||||
|
||||
integrand = -barotrope.density_derivative_from_enthalpy(
|
||||
baseEnthalpy
|
||||
) *
|
||||
enthalpyVariation;
|
||||
}
|
||||
}
|
||||
|
||||
const double weightedIntegrand =
|
||||
mappingContext.quadrature.weight * integrand;
|
||||
|
||||
for (int densityDof = 0; densityDof < densityElement.GetDof();
|
||||
++densityDof) {
|
||||
elementAction(densityDof) +=
|
||||
weightedIntegrand * densityShape(densityDof);
|
||||
}
|
||||
}
|
||||
|
||||
if (densityDofTransformation != nullptr) {
|
||||
densityDofTransformation->TransformDual(elementAction);
|
||||
}
|
||||
|
||||
localAction.AddElementVector(densityDofs, elementAction);
|
||||
}
|
||||
|
||||
local_to_true(*f.densityFes, localAction, action);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace mean_field::operators::kernels {
|
||||
void apply_barotropic_closure(
|
||||
const fem::FEM &f,
|
||||
const mapping::DomainMapperStateless &domainMapper,
|
||||
const physics::PolytropicBarotrope &barotrope,
|
||||
const mfem::Vector &densityTrue,
|
||||
const mfem::Vector &enthalpyTrue,
|
||||
const mfem::Vector &displacementTrue,
|
||||
mfem::Vector &residual
|
||||
) {
|
||||
apply_closure_action(
|
||||
f, domainMapper, barotrope, ClosureAction::residual, &densityTrue,
|
||||
&enthalpyTrue, nullptr, displacementTrue, residual
|
||||
);
|
||||
}
|
||||
|
||||
void apply_barotropic_closure_density_action(
|
||||
const fem::FEM &f,
|
||||
const mapping::DomainMapperStateless &domainMapper,
|
||||
const physics::PolytropicBarotrope &barotrope,
|
||||
const mfem::Vector &densityVariationTrue,
|
||||
const mfem::Vector &displacementTrue,
|
||||
mfem::Vector &action
|
||||
) {
|
||||
apply_closure_action(
|
||||
f, domainMapper, barotrope, ClosureAction::density,
|
||||
&densityVariationTrue, nullptr, nullptr, displacementTrue, action
|
||||
);
|
||||
}
|
||||
|
||||
void apply_barotropic_closure_enthalpy_action(
|
||||
const fem::FEM &f,
|
||||
const mapping::DomainMapperStateless &domainMapper,
|
||||
const physics::PolytropicBarotrope &barotrope,
|
||||
const mfem::Vector &baseEnthalpyTrue,
|
||||
const mfem::Vector &enthalpyVariationTrue,
|
||||
const mfem::Vector &displacementTrue,
|
||||
mfem::Vector &action
|
||||
) {
|
||||
apply_closure_action(
|
||||
f, domainMapper, barotrope, ClosureAction::enthalpy, nullptr,
|
||||
&baseEnthalpyTrue, &enthalpyVariationTrue, displacementTrue, action
|
||||
);
|
||||
}
|
||||
|
||||
void apply_barotropic_closure_displacement_action(
|
||||
const fem::FEM &f,
|
||||
const mapping::DomainMapperStateless &domainMapper,
|
||||
const physics::PolytropicBarotrope &barotrope,
|
||||
const mfem::Vector &baseDensityTrue,
|
||||
const mfem::Vector &baseEnthalpyTrue,
|
||||
const mfem::Vector &displacementTrue,
|
||||
const mfem::Vector &displacementVariationTrue,
|
||||
mfem::Vector &action
|
||||
) {
|
||||
MFEM_VERIFY(
|
||||
f.mesh != nullptr, "The barotropic-closure displacement action "
|
||||
"requires a mesh."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
f.densityFes != nullptr,
|
||||
"The barotropic-closure displacement action "
|
||||
"requires the density finite-element space."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
f.enthalpyFes != nullptr,
|
||||
"The barotropic-closure displacement action "
|
||||
"requires the enthalpy finite-element space."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
f.displacementFes != nullptr,
|
||||
"The barotropic-closure displacement action "
|
||||
"requires the displacement finite-element space."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
f.compactificationFes != nullptr,
|
||||
"The barotropic-closure displacement action "
|
||||
"requires the compactification finite-element space."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
f.compactificationCoordinate != nullptr,
|
||||
"The barotropic-closure displacement action "
|
||||
"requires the compactification coordinate."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
f.quadratureFactory != nullptr,
|
||||
"The barotropic-closure displacement action "
|
||||
"requires the quadrature-rule factory."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
baseDensityTrue.Size() == f.densityFes->GetTrueVSize(),
|
||||
"The base-density vector has the wrong size."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
baseEnthalpyTrue.Size() == f.enthalpyFes->GetTrueVSize(),
|
||||
"The base-enthalpy vector has the wrong size."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
displacementTrue.Size() == f.displacementFes->GetTrueVSize(),
|
||||
"The displacement vector has the wrong size."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
displacementVariationTrue.Size() ==
|
||||
f.displacementFes->GetTrueVSize(),
|
||||
"The displacement-variation vector has the wrong size."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
domainMapper.GetDimension() == f.mesh->Dimension(),
|
||||
"The domain-mapper dimension does not match the "
|
||||
"mesh dimension."
|
||||
);
|
||||
|
||||
mfem::Vector baseDensityLocal;
|
||||
mfem::Vector baseEnthalpyLocal;
|
||||
mfem::Vector displacementLocal;
|
||||
mfem::Vector displacementVariationLocal;
|
||||
|
||||
true_to_local(*f.densityFes, baseDensityTrue, baseDensityLocal);
|
||||
|
||||
true_to_local(*f.enthalpyFes, baseEnthalpyTrue, baseEnthalpyLocal);
|
||||
|
||||
true_to_local(*f.displacementFes, displacementTrue, displacementLocal);
|
||||
|
||||
true_to_local(
|
||||
*f.displacementFes, displacementVariationTrue,
|
||||
displacementVariationLocal
|
||||
);
|
||||
|
||||
mfem::Vector localAction(f.densityFes->GetVSize());
|
||||
localAction = 0.0;
|
||||
|
||||
mapping::DomainMapperStateless::Workspace workspace(
|
||||
f.mesh->Dimension()
|
||||
);
|
||||
|
||||
mfem::Array<int> densityDofs;
|
||||
mfem::Array<int> enthalpyDofs;
|
||||
mfem::Array<int> displacementDofs;
|
||||
mfem::Array<int> compactificationDofs;
|
||||
|
||||
mfem::Vector elementBaseDensity;
|
||||
mfem::Vector elementBaseEnthalpy;
|
||||
mfem::Vector elementDisplacement;
|
||||
mfem::Vector elementDisplacementVariation;
|
||||
mfem::Vector elementCompactification;
|
||||
|
||||
mfem::Vector densityShape;
|
||||
mfem::Vector enthalpyShape;
|
||||
mfem::Vector elementAction;
|
||||
|
||||
mapping::VolumeMappingContext mappingContext;
|
||||
mapping::VolumeMappingVariation mappingVariation;
|
||||
|
||||
const int vacuumAttribute = domainMapper.GetVacuumElementAttribute();
|
||||
|
||||
for (int elementId = 0; elementId < f.mesh->GetNE(); ++elementId) {
|
||||
mfem::ElementTransformation *transformation =
|
||||
f.mesh->GetElementTransformation(elementId);
|
||||
|
||||
MFEM_VERIFY(
|
||||
transformation != nullptr,
|
||||
"The barotropic-closure displacement action "
|
||||
"received a null element transformation."
|
||||
);
|
||||
|
||||
if (transformation->Attribute == vacuumAttribute) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const mfem::FiniteElement &densityElement =
|
||||
*f.densityFes->GetFE(elementId);
|
||||
|
||||
const mfem::FiniteElement &enthalpyElement =
|
||||
*f.enthalpyFes->GetFE(elementId);
|
||||
|
||||
const mfem::FiniteElement &displacementElement =
|
||||
*f.displacementFes->GetFE(elementId);
|
||||
|
||||
const mfem::FiniteElement &compactificationElement =
|
||||
*f.compactificationFes->GetFE(elementId);
|
||||
|
||||
mfem::DofTransformation *densityDofTransformation =
|
||||
f.densityFes->GetElementDofs(elementId, densityDofs);
|
||||
|
||||
mfem::DofTransformation *enthalpyDofTransformation =
|
||||
f.enthalpyFes->GetElementDofs(elementId, enthalpyDofs);
|
||||
|
||||
mfem::DofTransformation *displacementDofTransformation =
|
||||
f.displacementFes->GetElementVDofs(elementId, displacementDofs);
|
||||
|
||||
mfem::DofTransformation *compactificationDofTransformation =
|
||||
f.compactificationFes->GetElementDofs(
|
||||
elementId, compactificationDofs
|
||||
);
|
||||
|
||||
baseDensityLocal.GetSubVector(densityDofs, elementBaseDensity);
|
||||
|
||||
baseEnthalpyLocal.GetSubVector(enthalpyDofs, elementBaseEnthalpy);
|
||||
|
||||
displacementLocal.GetSubVector(
|
||||
displacementDofs, elementDisplacement
|
||||
);
|
||||
|
||||
displacementVariationLocal.GetSubVector(
|
||||
displacementDofs, elementDisplacementVariation
|
||||
);
|
||||
|
||||
f.compactificationCoordinate->GetSubVector(
|
||||
compactificationDofs, elementCompactification
|
||||
);
|
||||
|
||||
if (densityDofTransformation != nullptr) {
|
||||
densityDofTransformation->InvTransformPrimal(
|
||||
elementBaseDensity
|
||||
);
|
||||
}
|
||||
|
||||
if (enthalpyDofTransformation != nullptr) {
|
||||
enthalpyDofTransformation->InvTransformPrimal(
|
||||
elementBaseEnthalpy
|
||||
);
|
||||
}
|
||||
|
||||
if (displacementDofTransformation != nullptr) {
|
||||
displacementDofTransformation->InvTransformPrimal(
|
||||
elementDisplacement
|
||||
);
|
||||
|
||||
displacementDofTransformation->InvTransformPrimal(
|
||||
elementDisplacementVariation
|
||||
);
|
||||
}
|
||||
|
||||
if (compactificationDofTransformation != nullptr) {
|
||||
compactificationDofTransformation->InvTransformPrimal(
|
||||
elementCompactification
|
||||
);
|
||||
}
|
||||
|
||||
const mapping::ElementDisplacementData displacementData =
|
||||
mapping::ElementDisplacementDataFromElementVDofs(
|
||||
displacementElement, elementDisplacement
|
||||
);
|
||||
|
||||
const mapping::ElementDisplacementData displacementVariationData =
|
||||
mapping::ElementDisplacementDataFromElementVDofs(
|
||||
displacementElement, elementDisplacementVariation
|
||||
);
|
||||
|
||||
const mapping::ElementCompactificationData compactificationData(
|
||||
compactificationElement, elementCompactification
|
||||
);
|
||||
|
||||
const mapping::ElementMappingData mappingData{
|
||||
.displacement = displacementData,
|
||||
.compactification = compactificationData
|
||||
};
|
||||
|
||||
densityShape.SetSize(densityElement.GetDof());
|
||||
|
||||
enthalpyShape.SetSize(enthalpyElement.GetDof());
|
||||
|
||||
elementAction.SetSize(densityElement.GetDof());
|
||||
elementAction = 0.0;
|
||||
|
||||
const mfem::IntegrationRule &integrationRule = get_eos_rule(
|
||||
f, barotrope, densityElement, enthalpyElement, *transformation
|
||||
);
|
||||
|
||||
for (int quadraturePoint = 0;
|
||||
quadraturePoint < integrationRule.GetNPoints();
|
||||
++quadraturePoint) {
|
||||
const mfem::IntegrationPoint &integrationPoint =
|
||||
integrationRule.IntPoint(quadraturePoint);
|
||||
|
||||
transformation->SetIntPoint(&integrationPoint);
|
||||
|
||||
const mapping::MappingStatus mappingStatus =
|
||||
domainMapper.EvaluateVolume(
|
||||
mappingData, *transformation, integrationPoint,
|
||||
workspace, mappingContext
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
mappingStatus == mapping::MappingStatus::valid,
|
||||
"The base mapping is invalid while applying "
|
||||
"the barotropic-closure displacement action. "
|
||||
"Element: "
|
||||
<< elementId
|
||||
<< ", attribute: " << transformation->Attribute
|
||||
<< ", quadrature point: " << quadraturePoint
|
||||
<< ", status: " << static_cast<int>(mappingStatus)
|
||||
);
|
||||
|
||||
const mapping::MappingStatus variationStatus =
|
||||
domainMapper.EvaluateVolumeVariation(
|
||||
mappingData, displacementVariationData, *transformation,
|
||||
integrationPoint, mappingContext, workspace,
|
||||
mappingVariation
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
variationStatus == mapping::MappingStatus::valid,
|
||||
"The mapping variation is invalid while "
|
||||
"applying the barotropic-closure "
|
||||
"displacement action. Element: "
|
||||
<< elementId
|
||||
<< ", attribute: " << transformation->Attribute
|
||||
<< ", quadrature point: " << quadraturePoint
|
||||
<< ", status: " << static_cast<int>(variationStatus)
|
||||
);
|
||||
|
||||
densityElement.CalcShape(integrationPoint, densityShape);
|
||||
|
||||
enthalpyElement.CalcShape(integrationPoint, enthalpyShape);
|
||||
|
||||
const double densityValue = elementBaseDensity * densityShape;
|
||||
|
||||
const double enthalpyValue =
|
||||
elementBaseEnthalpy * enthalpyShape;
|
||||
|
||||
const double closureValue =
|
||||
densityValue -
|
||||
barotrope.density_from_enthalpy(enthalpyValue);
|
||||
|
||||
const double geometryActionValue =
|
||||
closureValue * mappingVariation.weight_variation;
|
||||
|
||||
MFEM_VERIFY(
|
||||
std::isfinite(closureValue) &&
|
||||
std::isfinite(geometryActionValue),
|
||||
"The barotropic-closure displacement action "
|
||||
"encountered a non-finite quadrature value."
|
||||
);
|
||||
|
||||
elementAction.Add(geometryActionValue, densityShape);
|
||||
}
|
||||
|
||||
if (densityDofTransformation != nullptr) {
|
||||
densityDofTransformation->TransformDual(elementAction);
|
||||
}
|
||||
|
||||
localAction.AddElementVector(densityDofs, elementAction);
|
||||
}
|
||||
|
||||
local_to_true(*f.densityFes, localAction, action);
|
||||
}
|
||||
} // namespace mean_field::operators::kernels
|
||||
1030
libmeanfield/impl/operators/kernels/gravity_kernels.cpp
Normal file
1030
libmeanfield/impl/operators/kernels/gravity_kernels.cpp
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,775 @@
|
||||
module;
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <mfem.hpp>
|
||||
#include <optional>
|
||||
|
||||
module mean_field;
|
||||
|
||||
import :operators.kernels.hydrostatic_equilibrium;
|
||||
|
||||
namespace {
|
||||
void true_to_local(
|
||||
const mfem::ParFiniteElementSpace &finiteElementSpace,
|
||||
const mfem::Vector &trueVector,
|
||||
mfem::Vector &localVector
|
||||
) {
|
||||
MFEM_VERIFY(
|
||||
trueVector.Size() == finiteElementSpace.GetTrueVSize(),
|
||||
"True vector has the wrong size."
|
||||
);
|
||||
|
||||
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
|
||||
) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
void validate_fem(
|
||||
const mean_field::fem::FEM &f,
|
||||
const mean_field::mapping::DomainMapperStateless &domainMapper
|
||||
) {
|
||||
MFEM_VERIFY(
|
||||
f.mesh != nullptr, "The hydrostatic kernel requires a mesh."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
f.enthalpyFes != nullptr, "The hydrostatic kernel requires the "
|
||||
"enthalpy finite-element space."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
f.gravityPotentialFes != nullptr,
|
||||
"The hydrostatic kernel requires the "
|
||||
"gravity-potential finite-element space."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
f.displacementFes != nullptr, "The hydrostatic kernel requires the "
|
||||
"displacement finite-element space."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
f.compactificationFes != nullptr,
|
||||
"The hydrostatic kernel requires the "
|
||||
"compactification finite-element space."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
f.compactificationCoordinate != nullptr,
|
||||
"The hydrostatic kernel requires the "
|
||||
"compactification coordinate."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
f.quadratureFactory != nullptr,
|
||||
"The hydrostatic kernel requires the "
|
||||
"quadrature-rule factory."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
f.mesh->Dimension() == 3,
|
||||
"The rigid-rotation hydrostatic kernel "
|
||||
"currently requires a three-dimensional mesh."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
domainMapper.GetDimension() == f.mesh->Dimension(),
|
||||
"The domain-mapper dimension does not match "
|
||||
"the mesh dimension."
|
||||
);
|
||||
}
|
||||
|
||||
const mfem::IntegrationRule &get_hydrostatic_rule(
|
||||
const mean_field::fem::FEM &f,
|
||||
const mfem::FiniteElement &enthalpyElement,
|
||||
const mfem::FiniteElement &potentialElement,
|
||||
const mfem::ElementTransformation &transformation
|
||||
) {
|
||||
using EnthalpyField =
|
||||
mean_field::field::Field<mean_field::field::Enthalpy>;
|
||||
|
||||
MFEM_VERIFY(
|
||||
enthalpyElement.GetOrder() ==
|
||||
mean_field::field::Enthalpy::Scalar::familyOrder,
|
||||
"The hydrostatic test element does not match "
|
||||
"the registered enthalpy field."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
potentialElement.GetOrder() ==
|
||||
mean_field::field::Gravity::Potential::familyOrder,
|
||||
"The hydrostatic potential element does not "
|
||||
"match the registered gravity-potential field."
|
||||
);
|
||||
|
||||
const auto enthalpyQuery = EnthalpyField::make_query<
|
||||
mean_field::field::Enthalpy::Form::EquilibriumEnthalpy>(
|
||||
mean_field::quadrature::QuadratureRole::discretization,
|
||||
transformation.OrderW(), {}, mean_field::utils::DOMAINS::STELLAR,
|
||||
mean_field::quadrature::MappingKind::general
|
||||
);
|
||||
|
||||
const auto gravityQuery = EnthalpyField::make_query<
|
||||
mean_field::field::Enthalpy::Form::EquilibriumGravity>(
|
||||
mean_field::quadrature::QuadratureRole::discretization,
|
||||
transformation.OrderW(), {}, mean_field::utils::DOMAINS::STELLAR,
|
||||
mean_field::quadrature::MappingKind::general
|
||||
);
|
||||
|
||||
const auto rotationQuery = EnthalpyField::make_query<
|
||||
mean_field::field::Enthalpy::Form::EquilibriumRotation>(
|
||||
mean_field::quadrature::QuadratureRole::discretization,
|
||||
transformation.OrderW(), std::array<int, 1>{2},
|
||||
mean_field::utils::DOMAINS::STELLAR,
|
||||
mean_field::quadrature::MappingKind::general
|
||||
);
|
||||
|
||||
const auto constantQuery = EnthalpyField::make_query<
|
||||
mean_field::field::Enthalpy::Form::EquilibriumConstant>(
|
||||
mean_field::quadrature::QuadratureRole::discretization,
|
||||
transformation.OrderW(), {}, mean_field::utils::DOMAINS::STELLAR,
|
||||
mean_field::quadrature::MappingKind::general
|
||||
);
|
||||
|
||||
int integrationOrder = 0;
|
||||
|
||||
const auto update_order = [&f, &transformation, &integrationOrder](
|
||||
const mean_field::quadrature::Query &query
|
||||
) {
|
||||
const auto rule = f.quadratureFactory->get(
|
||||
query, transformation.GetGeometryType()
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
rule.integration_rule != nullptr,
|
||||
"The quadrature policy did not return "
|
||||
"a hydrostatic-equilibrium rule."
|
||||
);
|
||||
|
||||
integrationOrder =
|
||||
std::max(integrationOrder, rule.resolution.order);
|
||||
};
|
||||
|
||||
update_order(enthalpyQuery);
|
||||
update_order(gravityQuery);
|
||||
update_order(rotationQuery);
|
||||
update_order(constantQuery);
|
||||
|
||||
return mfem::IntRules.Get(
|
||||
transformation.GetGeometryType(), integrationOrder
|
||||
);
|
||||
}
|
||||
|
||||
struct HydrostaticAssemblyRequest {
|
||||
const mean_field::physics::RigidRotation *rotation{nullptr};
|
||||
|
||||
const mfem::Vector *baseEnthalpyTrue{nullptr};
|
||||
const mfem::Vector *basePotentialTrue{nullptr};
|
||||
|
||||
const mfem::Vector *enthalpyVariationTrue{nullptr};
|
||||
const mfem::Vector *potentialVariationTrue{nullptr};
|
||||
const mfem::Vector *displacementVariationTrue{nullptr};
|
||||
|
||||
double bernoulliConstant{0.0};
|
||||
double constantVariation{0.0};
|
||||
|
||||
bool buildResidual{false};
|
||||
};
|
||||
|
||||
void assemble_hydrostatic_form(
|
||||
const mean_field::fem::FEM &f,
|
||||
const mean_field::mapping::DomainMapperStateless &domainMapper,
|
||||
const mfem::Vector &displacementTrue,
|
||||
const HydrostaticAssemblyRequest &request,
|
||||
mfem::Vector &result
|
||||
) {
|
||||
validate_fem(f, domainMapper);
|
||||
|
||||
MFEM_VERIFY(
|
||||
displacementTrue.Size() == f.displacementFes->GetTrueVSize(),
|
||||
"The hydrostatic displacement vector has "
|
||||
"the wrong size."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
std::isfinite(request.bernoulliConstant),
|
||||
"The Bernoulli constant is non-finite."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
std::isfinite(request.constantVariation),
|
||||
"The Bernoulli-constant variation is non-finite."
|
||||
);
|
||||
|
||||
const bool requiresBaseState =
|
||||
request.buildResidual ||
|
||||
request.displacementVariationTrue != nullptr;
|
||||
|
||||
if (requiresBaseState) {
|
||||
MFEM_VERIFY(
|
||||
request.rotation != nullptr,
|
||||
"The hydrostatic residual or geometry "
|
||||
"action requires the rotation model."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
request.baseEnthalpyTrue != nullptr,
|
||||
"The hydrostatic residual or geometry "
|
||||
"action requires the base enthalpy."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
request.basePotentialTrue != nullptr,
|
||||
"The hydrostatic residual or geometry "
|
||||
"action requires the base potential."
|
||||
);
|
||||
}
|
||||
|
||||
if (request.baseEnthalpyTrue != nullptr) {
|
||||
MFEM_VERIFY(
|
||||
request.baseEnthalpyTrue->Size() ==
|
||||
f.enthalpyFes->GetTrueVSize(),
|
||||
"The base enthalpy vector has the wrong size."
|
||||
);
|
||||
}
|
||||
|
||||
if (request.basePotentialTrue != nullptr) {
|
||||
MFEM_VERIFY(
|
||||
request.basePotentialTrue->Size() ==
|
||||
f.gravityPotentialFes->GetTrueVSize(),
|
||||
"The base potential vector has the wrong size."
|
||||
);
|
||||
}
|
||||
|
||||
if (request.enthalpyVariationTrue != nullptr) {
|
||||
MFEM_VERIFY(
|
||||
request.enthalpyVariationTrue->Size() ==
|
||||
f.enthalpyFes->GetTrueVSize(),
|
||||
"The enthalpy variation has the wrong size."
|
||||
);
|
||||
}
|
||||
|
||||
if (request.potentialVariationTrue != nullptr) {
|
||||
MFEM_VERIFY(
|
||||
request.potentialVariationTrue->Size() ==
|
||||
f.gravityPotentialFes->GetTrueVSize(),
|
||||
"The potential variation has the wrong size."
|
||||
);
|
||||
}
|
||||
|
||||
if (request.displacementVariationTrue != nullptr) {
|
||||
MFEM_VERIFY(
|
||||
request.displacementVariationTrue->Size() ==
|
||||
f.displacementFes->GetTrueVSize(),
|
||||
"The displacement variation has the wrong size."
|
||||
);
|
||||
}
|
||||
|
||||
mfem::Vector displacementLocal;
|
||||
true_to_local(*f.displacementFes, displacementTrue, displacementLocal);
|
||||
|
||||
mfem::Vector baseEnthalpyLocal;
|
||||
mfem::Vector basePotentialLocal;
|
||||
mfem::Vector enthalpyVariationLocal;
|
||||
mfem::Vector potentialVariationLocal;
|
||||
mfem::Vector displacementVariationLocal;
|
||||
|
||||
if (request.baseEnthalpyTrue != nullptr) {
|
||||
true_to_local(
|
||||
*f.enthalpyFes, *request.baseEnthalpyTrue, baseEnthalpyLocal
|
||||
);
|
||||
}
|
||||
|
||||
if (request.basePotentialTrue != nullptr) {
|
||||
true_to_local(
|
||||
*f.gravityPotentialFes, *request.basePotentialTrue,
|
||||
basePotentialLocal
|
||||
);
|
||||
}
|
||||
|
||||
if (request.enthalpyVariationTrue != nullptr) {
|
||||
true_to_local(
|
||||
*f.enthalpyFes, *request.enthalpyVariationTrue,
|
||||
enthalpyVariationLocal
|
||||
);
|
||||
}
|
||||
|
||||
if (request.potentialVariationTrue != nullptr) {
|
||||
true_to_local(
|
||||
*f.gravityPotentialFes, *request.potentialVariationTrue,
|
||||
potentialVariationLocal
|
||||
);
|
||||
}
|
||||
|
||||
if (request.displacementVariationTrue != nullptr) {
|
||||
true_to_local(
|
||||
*f.displacementFes, *request.displacementVariationTrue,
|
||||
displacementVariationLocal
|
||||
);
|
||||
}
|
||||
|
||||
mfem::Vector localResult(f.enthalpyFes->GetVSize());
|
||||
|
||||
localResult = 0.0;
|
||||
|
||||
mean_field::mapping::DomainMapperStateless::Workspace workspace(
|
||||
f.mesh->Dimension()
|
||||
);
|
||||
|
||||
mfem::Array<int> enthalpyDofs;
|
||||
mfem::Array<int> potentialDofs;
|
||||
mfem::Array<int> displacementDofs;
|
||||
mfem::Array<int> compactificationDofs;
|
||||
|
||||
mfem::Vector elementBaseEnthalpy;
|
||||
mfem::Vector elementBasePotential;
|
||||
mfem::Vector elementEnthalpyVariation;
|
||||
mfem::Vector elementPotentialVariation;
|
||||
mfem::Vector elementDisplacement;
|
||||
mfem::Vector elementDisplacementVariation;
|
||||
mfem::Vector elementCompactification;
|
||||
mfem::Vector elementResult;
|
||||
|
||||
mfem::Vector enthalpyShape;
|
||||
mfem::Vector potentialShape;
|
||||
|
||||
const int vacuumAttribute = domainMapper.GetVacuumElementAttribute();
|
||||
|
||||
for (int elementId = 0; elementId < f.mesh->GetNE(); ++elementId) {
|
||||
mfem::ElementTransformation *transformation =
|
||||
f.mesh->GetElementTransformation(elementId);
|
||||
|
||||
MFEM_VERIFY(
|
||||
transformation != nullptr,
|
||||
"The hydrostatic kernel received a null "
|
||||
"element transformation."
|
||||
);
|
||||
|
||||
if (transformation->Attribute == vacuumAttribute) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const mfem::FiniteElement &enthalpyElement =
|
||||
*f.enthalpyFes->GetFE(elementId);
|
||||
|
||||
const mfem::FiniteElement &potentialElement =
|
||||
*f.gravityPotentialFes->GetFE(elementId);
|
||||
|
||||
const mfem::FiniteElement &displacementElement =
|
||||
*f.displacementFes->GetFE(elementId);
|
||||
|
||||
const mfem::FiniteElement &compactificationElement =
|
||||
*f.compactificationFes->GetFE(elementId);
|
||||
|
||||
mfem::DofTransformation *enthalpyDofTransformation =
|
||||
f.enthalpyFes->GetElementDofs(elementId, enthalpyDofs);
|
||||
|
||||
mfem::DofTransformation *potentialDofTransformation =
|
||||
f.gravityPotentialFes->GetElementDofs(elementId, potentialDofs);
|
||||
|
||||
mfem::DofTransformation *displacementDofTransformation =
|
||||
f.displacementFes->GetElementVDofs(elementId, displacementDofs);
|
||||
|
||||
mfem::DofTransformation *compactificationDofTransformation =
|
||||
f.compactificationFes->GetElementDofs(
|
||||
elementId, compactificationDofs
|
||||
);
|
||||
|
||||
displacementLocal.GetSubVector(
|
||||
displacementDofs, elementDisplacement
|
||||
);
|
||||
|
||||
f.compactificationCoordinate->GetSubVector(
|
||||
compactificationDofs, elementCompactification
|
||||
);
|
||||
|
||||
if (request.baseEnthalpyTrue != nullptr) {
|
||||
baseEnthalpyLocal.GetSubVector(
|
||||
enthalpyDofs, elementBaseEnthalpy
|
||||
);
|
||||
}
|
||||
|
||||
if (request.basePotentialTrue != nullptr) {
|
||||
basePotentialLocal.GetSubVector(
|
||||
potentialDofs, elementBasePotential
|
||||
);
|
||||
}
|
||||
|
||||
if (request.enthalpyVariationTrue != nullptr) {
|
||||
enthalpyVariationLocal.GetSubVector(
|
||||
enthalpyDofs, elementEnthalpyVariation
|
||||
);
|
||||
}
|
||||
|
||||
if (request.potentialVariationTrue != nullptr) {
|
||||
potentialVariationLocal.GetSubVector(
|
||||
potentialDofs, elementPotentialVariation
|
||||
);
|
||||
}
|
||||
|
||||
if (request.displacementVariationTrue != nullptr) {
|
||||
displacementVariationLocal.GetSubVector(
|
||||
displacementDofs, elementDisplacementVariation
|
||||
);
|
||||
}
|
||||
|
||||
if (enthalpyDofTransformation != nullptr) {
|
||||
if (request.baseEnthalpyTrue != nullptr) {
|
||||
enthalpyDofTransformation->InvTransformPrimal(
|
||||
elementBaseEnthalpy
|
||||
);
|
||||
}
|
||||
|
||||
if (request.enthalpyVariationTrue != nullptr) {
|
||||
enthalpyDofTransformation->InvTransformPrimal(
|
||||
elementEnthalpyVariation
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (potentialDofTransformation != nullptr) {
|
||||
if (request.basePotentialTrue != nullptr) {
|
||||
potentialDofTransformation->InvTransformPrimal(
|
||||
elementBasePotential
|
||||
);
|
||||
}
|
||||
|
||||
if (request.potentialVariationTrue != nullptr) {
|
||||
potentialDofTransformation->InvTransformPrimal(
|
||||
elementPotentialVariation
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (displacementDofTransformation != nullptr) {
|
||||
displacementDofTransformation->InvTransformPrimal(
|
||||
elementDisplacement
|
||||
);
|
||||
|
||||
if (request.displacementVariationTrue != nullptr) {
|
||||
displacementDofTransformation->InvTransformPrimal(
|
||||
elementDisplacementVariation
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (compactificationDofTransformation != nullptr) {
|
||||
compactificationDofTransformation->InvTransformPrimal(
|
||||
elementCompactification
|
||||
);
|
||||
}
|
||||
|
||||
const mean_field::mapping::ElementDisplacementData
|
||||
displacementData = mean_field::mapping::
|
||||
ElementDisplacementDataFromElementVDofs(
|
||||
displacementElement, elementDisplacement
|
||||
);
|
||||
|
||||
const mean_field::mapping::ElementCompactificationData
|
||||
compactificationData(
|
||||
compactificationElement, elementCompactification
|
||||
);
|
||||
|
||||
const mean_field::mapping::ElementMappingData mappingData{
|
||||
.displacement = displacementData,
|
||||
.compactification = compactificationData
|
||||
};
|
||||
|
||||
std::optional<mean_field::mapping::ElementDisplacementData>
|
||||
displacementVariationData;
|
||||
|
||||
if (request.displacementVariationTrue != nullptr) {
|
||||
displacementVariationData.emplace(
|
||||
mean_field::mapping::
|
||||
ElementDisplacementDataFromElementVDofs(
|
||||
displacementElement, elementDisplacementVariation
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
elementResult.SetSize(enthalpyElement.GetDof());
|
||||
|
||||
elementResult = 0.0;
|
||||
|
||||
enthalpyShape.SetSize(enthalpyElement.GetDof());
|
||||
|
||||
potentialShape.SetSize(potentialElement.GetDof());
|
||||
|
||||
const mfem::IntegrationRule &integrationRule = get_hydrostatic_rule(
|
||||
f, enthalpyElement, potentialElement, *transformation
|
||||
);
|
||||
|
||||
for (int quadraturePoint = 0;
|
||||
quadraturePoint < integrationRule.GetNPoints();
|
||||
++quadraturePoint) {
|
||||
const mfem::IntegrationPoint &integrationPoint =
|
||||
integrationRule.IntPoint(quadraturePoint);
|
||||
|
||||
transformation->SetIntPoint(&integrationPoint);
|
||||
|
||||
mean_field::mapping::VolumeMappingContext mappingContext;
|
||||
|
||||
const mean_field::mapping::MappingStatus mappingStatus =
|
||||
domainMapper.EvaluateVolume(
|
||||
mappingData, *transformation, integrationPoint,
|
||||
workspace, mappingContext
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
mappingStatus == mean_field::mapping::MappingStatus::valid,
|
||||
"The base mapping is invalid in the "
|
||||
"hydrostatic kernel. Element: "
|
||||
<< elementId
|
||||
<< ", quadrature point: " << quadraturePoint
|
||||
<< ", status: " << static_cast<int>(mappingStatus)
|
||||
);
|
||||
|
||||
enthalpyElement.CalcShape(integrationPoint, enthalpyShape);
|
||||
|
||||
potentialElement.CalcShape(integrationPoint, potentialShape);
|
||||
|
||||
double baseIntegrand = 0.0;
|
||||
|
||||
if (requiresBaseState) {
|
||||
const double enthalpyValue =
|
||||
elementBaseEnthalpy * enthalpyShape;
|
||||
|
||||
const double potentialValue =
|
||||
elementBasePotential * potentialShape;
|
||||
|
||||
const double rotationPotential =
|
||||
request.rotation->potential(
|
||||
mappingContext.mapping.physical_position
|
||||
);
|
||||
|
||||
baseIntegrand = enthalpyValue + potentialValue -
|
||||
rotationPotential -
|
||||
request.bernoulliConstant;
|
||||
}
|
||||
|
||||
if (request.buildResidual) {
|
||||
elementResult.Add(
|
||||
mappingContext.quadrature.weight * baseIntegrand,
|
||||
enthalpyShape
|
||||
);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
double materialVariation = -request.constantVariation;
|
||||
|
||||
if (request.enthalpyVariationTrue != nullptr) {
|
||||
materialVariation +=
|
||||
elementEnthalpyVariation * enthalpyShape;
|
||||
}
|
||||
|
||||
if (request.potentialVariationTrue != nullptr) {
|
||||
materialVariation +=
|
||||
elementPotentialVariation * potentialShape;
|
||||
}
|
||||
|
||||
double weightedVariation =
|
||||
mappingContext.quadrature.weight * materialVariation;
|
||||
|
||||
if (request.displacementVariationTrue != nullptr) {
|
||||
mean_field::mapping::VolumeMappingVariation
|
||||
mappingVariation;
|
||||
|
||||
const mean_field::mapping::MappingStatus variationStatus =
|
||||
domainMapper.EvaluateVolumeVariation(
|
||||
mappingData, *displacementVariationData,
|
||||
*transformation, integrationPoint, mappingContext,
|
||||
workspace, mappingVariation
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
variationStatus ==
|
||||
mean_field::mapping::MappingStatus::valid,
|
||||
"The mapping variation is invalid "
|
||||
"in the hydrostatic kernel."
|
||||
);
|
||||
|
||||
const double rotationVariation =
|
||||
request.rotation->potential_directional_derivative(
|
||||
mappingContext.mapping.physical_position,
|
||||
mappingVariation.mapping.physical_position_variation
|
||||
);
|
||||
|
||||
weightedVariation +=
|
||||
baseIntegrand * mappingVariation.weight_variation -
|
||||
rotationVariation * mappingContext.quadrature.weight;
|
||||
}
|
||||
|
||||
elementResult.Add(weightedVariation, enthalpyShape);
|
||||
}
|
||||
|
||||
if (enthalpyDofTransformation != nullptr) {
|
||||
enthalpyDofTransformation->TransformDual(elementResult);
|
||||
}
|
||||
|
||||
localResult.AddElementVector(enthalpyDofs, elementResult);
|
||||
}
|
||||
|
||||
local_to_true(*f.enthalpyFes, localResult, result);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace mean_field::operators::kernels {
|
||||
void apply_hydrostatic_equilibrium(
|
||||
const fem::FEM &f,
|
||||
const mapping::DomainMapperStateless &domainMapper,
|
||||
const physics::RigidRotation &rotation,
|
||||
const mfem::Vector &enthalpyTrue,
|
||||
const mfem::Vector &potentialTrue,
|
||||
const mfem::Vector &displacementTrue,
|
||||
const double bernoulliConstant,
|
||||
mfem::Vector &residual
|
||||
) {
|
||||
HydrostaticAssemblyRequest request;
|
||||
|
||||
request.rotation = &rotation;
|
||||
request.baseEnthalpyTrue = &enthalpyTrue;
|
||||
request.basePotentialTrue = &potentialTrue;
|
||||
request.bernoulliConstant = bernoulliConstant;
|
||||
request.buildResidual = true;
|
||||
|
||||
assemble_hydrostatic_form(
|
||||
f, domainMapper, displacementTrue, request, residual
|
||||
);
|
||||
}
|
||||
|
||||
void apply_hydrostatic_equilibrium_enthalpy_action(
|
||||
const fem::FEM &f,
|
||||
const mapping::DomainMapperStateless &domainMapper,
|
||||
const mfem::Vector &enthalpyVariationTrue,
|
||||
const mfem::Vector &displacementTrue,
|
||||
mfem::Vector &action
|
||||
) {
|
||||
HydrostaticAssemblyRequest request;
|
||||
|
||||
request.enthalpyVariationTrue = &enthalpyVariationTrue;
|
||||
|
||||
assemble_hydrostatic_form(
|
||||
f, domainMapper, displacementTrue, request, action
|
||||
);
|
||||
}
|
||||
|
||||
void apply_hydrostatic_equilibrium_potential_action(
|
||||
const fem::FEM &f,
|
||||
const mapping::DomainMapperStateless &domainMapper,
|
||||
const mfem::Vector &potentialVariationTrue,
|
||||
const mfem::Vector &displacementTrue,
|
||||
mfem::Vector &action
|
||||
) {
|
||||
HydrostaticAssemblyRequest request;
|
||||
|
||||
request.potentialVariationTrue = &potentialVariationTrue;
|
||||
|
||||
assemble_hydrostatic_form(
|
||||
f, domainMapper, displacementTrue, request, action
|
||||
);
|
||||
}
|
||||
|
||||
void apply_hydrostatic_equilibrium_constant_action(
|
||||
const fem::FEM &f,
|
||||
const mapping::DomainMapperStateless &domainMapper,
|
||||
const double constantVariation,
|
||||
const mfem::Vector &displacementTrue,
|
||||
mfem::Vector &action
|
||||
) {
|
||||
HydrostaticAssemblyRequest request;
|
||||
|
||||
request.constantVariation = constantVariation;
|
||||
|
||||
assemble_hydrostatic_form(
|
||||
f, domainMapper, displacementTrue, request, action
|
||||
);
|
||||
}
|
||||
|
||||
void apply_hydrostatic_equilibrium_displacement_action(
|
||||
const fem::FEM &f,
|
||||
const mapping::DomainMapperStateless &domainMapper,
|
||||
const physics::RigidRotation &rotation,
|
||||
const mfem::Vector &baseEnthalpyTrue,
|
||||
const mfem::Vector &basePotentialTrue,
|
||||
const mfem::Vector &baseDisplacementTrue,
|
||||
const double baseBernoulliConstant,
|
||||
const mfem::Vector &displacementVariationTrue,
|
||||
mfem::Vector &action
|
||||
) {
|
||||
HydrostaticAssemblyRequest request;
|
||||
|
||||
request.rotation = &rotation;
|
||||
request.baseEnthalpyTrue = &baseEnthalpyTrue;
|
||||
request.basePotentialTrue = &basePotentialTrue;
|
||||
request.displacementVariationTrue = &displacementVariationTrue;
|
||||
request.bernoulliConstant = baseBernoulliConstant;
|
||||
|
||||
assemble_hydrostatic_form(
|
||||
f, domainMapper, baseDisplacementTrue, request, action
|
||||
);
|
||||
}
|
||||
|
||||
void apply_hydrostatic_equilibrium_action(
|
||||
const fem::FEM &f,
|
||||
const mapping::DomainMapperStateless &domainMapper,
|
||||
const physics::RigidRotation &rotation,
|
||||
const mfem::Vector &baseEnthalpyTrue,
|
||||
const mfem::Vector &basePotentialTrue,
|
||||
const mfem::Vector &baseDisplacementTrue,
|
||||
const double baseBernoulliConstant,
|
||||
const mfem::Vector &enthalpyVariationTrue,
|
||||
const mfem::Vector &potentialVariationTrue,
|
||||
const double constantVariation,
|
||||
const mfem::Vector &displacementVariationTrue,
|
||||
mfem::Vector &action
|
||||
) {
|
||||
HydrostaticAssemblyRequest request;
|
||||
|
||||
request.rotation = &rotation;
|
||||
request.baseEnthalpyTrue = &baseEnthalpyTrue;
|
||||
request.basePotentialTrue = &basePotentialTrue;
|
||||
request.enthalpyVariationTrue = &enthalpyVariationTrue;
|
||||
request.potentialVariationTrue = &potentialVariationTrue;
|
||||
request.displacementVariationTrue = &displacementVariationTrue;
|
||||
request.bernoulliConstant = baseBernoulliConstant;
|
||||
request.constantVariation = constantVariation;
|
||||
|
||||
assemble_hydrostatic_form(
|
||||
f, domainMapper, baseDisplacementTrue, request, action
|
||||
);
|
||||
}
|
||||
} // namespace mean_field::operators::kernels
|
||||
464
libmeanfield/impl/operators/kernels/pressure_force_kernels.cpp
Normal file
464
libmeanfield/impl/operators/kernels/pressure_force_kernels.cpp
Normal file
@@ -0,0 +1,464 @@
|
||||
module;
|
||||
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
|
||||
#include <mfem.hpp>
|
||||
|
||||
module mean_field;
|
||||
|
||||
import :operators.kernels.pressure_force;
|
||||
|
||||
namespace {
|
||||
void true_to_local(
|
||||
const mfem::ParFiniteElementSpace &finiteElementSpace,
|
||||
const mfem::Vector &trueVector,
|
||||
mfem::Vector &localVector
|
||||
) {
|
||||
MFEM_VERIFY(
|
||||
trueVector.Size() == finiteElementSpace.GetTrueVSize(),
|
||||
"The pressure-force true vector has the wrong size."
|
||||
);
|
||||
|
||||
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
|
||||
) {
|
||||
MFEM_VERIFY(
|
||||
localVector.Size() == finiteElementSpace.GetVSize(),
|
||||
"The pressure-force 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;
|
||||
}
|
||||
}
|
||||
|
||||
[[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;
|
||||
}
|
||||
|
||||
if (ordering == mfem::Ordering::byVDIM) {
|
||||
return component + scalarDof * dimension;
|
||||
}
|
||||
MFEM_ABORT("The displacement space uses an unsupported ordering.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
[[nodiscard]] int get_pressure_extra_order(
|
||||
const mean_field::physics::PolytropicBarotrope &barotrope
|
||||
) {
|
||||
/*
|
||||
* Pressure has the enthalpy dependence
|
||||
*
|
||||
* P(h) proportional to h^(n + 1).
|
||||
*
|
||||
* The registered enthalpy operand already contributes one factor
|
||||
* of the enthalpy polynomial order. The remaining dynamic
|
||||
* contribution is therefore n times that order.
|
||||
*/
|
||||
const double extraOrder =
|
||||
barotrope.polytropic_index() *
|
||||
static_cast<double>(
|
||||
mean_field::field::Enthalpy::Scalar::familyOrder
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
std::isfinite(extraOrder) && extraOrder >= 0.0 &&
|
||||
extraOrder <=
|
||||
static_cast<double>(std::numeric_limits<int>::max()),
|
||||
"The pressure EOS effective polynomial order is invalid."
|
||||
);
|
||||
|
||||
return static_cast<int>(std::ceil(extraOrder));
|
||||
}
|
||||
|
||||
[[nodiscard]] const mfem::IntegrationRule &get_pressure_force_rule(
|
||||
const mean_field::fem::FEM &f,
|
||||
const mean_field::physics::PolytropicBarotrope &barotrope,
|
||||
const mfem::FiniteElement &enthalpyElement,
|
||||
const mfem::FiniteElement &displacementElement,
|
||||
const mfem::ElementTransformation &transformation
|
||||
) {
|
||||
using EnthalpyField =
|
||||
mean_field::field::Field<mean_field::field::Enthalpy>;
|
||||
|
||||
MFEM_VERIFY(
|
||||
enthalpyElement.GetOrder() ==
|
||||
mean_field::field::Enthalpy::Scalar::familyOrder,
|
||||
"The pressure-force enthalpy element does not match the "
|
||||
"registered enthalpy field."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
displacementElement.GetOrder() ==
|
||||
mean_field::field::Displacement::Vector::familyOrder,
|
||||
"The pressure-force test element does not match the "
|
||||
"registered displacement field."
|
||||
);
|
||||
|
||||
const mean_field::quadrature::Query query = EnthalpyField::make_query<
|
||||
mean_field::field::Enthalpy::Form::PressureForce>(
|
||||
mean_field::quadrature::QuadratureRole::discretization,
|
||||
transformation.OrderW(),
|
||||
std::array<int, 1>{get_pressure_extra_order(barotrope)},
|
||||
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 pressure-force "
|
||||
"integration rule."
|
||||
);
|
||||
|
||||
return *rule.integration_rule;
|
||||
}
|
||||
|
||||
void validate_inputs(
|
||||
const mean_field::fem::FEM &f,
|
||||
const mean_field::mapping::DomainMapperStateless &domainMapper,
|
||||
const mfem::Vector &enthalpyTrue,
|
||||
const mfem::Vector &displacementTrue
|
||||
) {
|
||||
MFEM_VERIFY(
|
||||
f.mesh != nullptr, "The pressure-force kernel requires a mesh."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
f.enthalpyFes != nullptr,
|
||||
"The pressure-force kernel requires the enthalpy "
|
||||
"finite-element space."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
f.displacementFes != nullptr,
|
||||
"The pressure-force kernel requires the displacement "
|
||||
"finite-element space."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
f.compactificationFes != nullptr,
|
||||
"The pressure-force kernel requires the compactification "
|
||||
"finite-element space."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
f.compactificationCoordinate != nullptr,
|
||||
"The pressure-force kernel requires the compactification "
|
||||
"coordinate."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
f.quadratureFactory != nullptr,
|
||||
"The pressure-force kernel requires the quadrature "
|
||||
"rule factory."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
enthalpyTrue.Size() == f.enthalpyFes->GetTrueVSize(),
|
||||
"The pressure-force enthalpy vector has the wrong size."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
displacementTrue.Size() == f.displacementFes->GetTrueVSize(),
|
||||
"The pressure-force displacement vector has the wrong size."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
domainMapper.GetDimension() == f.mesh->Dimension(),
|
||||
"The pressure-force domain-mapper dimension does not match "
|
||||
"the mesh dimension."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
f.displacementFes->GetVDim() == f.mesh->Dimension(),
|
||||
"The displacement vector dimension does not match the "
|
||||
"mesh dimension."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
f.displacementFes->GetOrdering() == mfem::Ordering::byNODES,
|
||||
"The pressure-force kernel requires the registered byNODES "
|
||||
"displacement ordering."
|
||||
);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace mean_field::operators::kernels {
|
||||
void apply_pressure_force_residual(
|
||||
const fem::FEM &f,
|
||||
const mapping::DomainMapperStateless &domainMapper,
|
||||
const physics::PolytropicBarotrope &barotrope,
|
||||
const mfem::Vector &enthalpyTrue,
|
||||
const mfem::Vector &displacementTrue,
|
||||
mfem::Vector &residualTrue
|
||||
) {
|
||||
validate_inputs(f, domainMapper, enthalpyTrue, displacementTrue);
|
||||
|
||||
mfem::Vector enthalpyLocal;
|
||||
mfem::Vector displacementLocal;
|
||||
|
||||
true_to_local(*f.enthalpyFes, enthalpyTrue, enthalpyLocal);
|
||||
|
||||
true_to_local(*f.displacementFes, displacementTrue, displacementLocal);
|
||||
|
||||
mfem::Vector localResidual(f.displacementFes->GetVSize());
|
||||
localResidual = 0.0;
|
||||
|
||||
mapping::DomainMapperStateless::Workspace workspace(
|
||||
f.mesh->Dimension()
|
||||
);
|
||||
|
||||
mfem::Array<int> enthalpyDofs;
|
||||
mfem::Array<int> displacementDofs;
|
||||
mfem::Array<int> compactificationDofs;
|
||||
|
||||
mfem::Vector elementEnthalpy;
|
||||
mfem::Vector elementDisplacement;
|
||||
mfem::Vector elementCompactification;
|
||||
mfem::Vector elementResidual;
|
||||
mfem::Vector enthalpyShape;
|
||||
|
||||
mfem::DenseMatrix displacementDShapeReference;
|
||||
mfem::DenseMatrix displacementDShapePhysical;
|
||||
|
||||
mapping::VolumeMappingContext mappingContext;
|
||||
|
||||
const int dimension = f.mesh->Dimension();
|
||||
const int vacuumAttribute = domainMapper.GetVacuumElementAttribute();
|
||||
|
||||
const mfem::Ordering::Type displacementOrdering =
|
||||
f.displacementFes->GetOrdering();
|
||||
|
||||
for (int elementId = 0; elementId < f.mesh->GetNE(); ++elementId) {
|
||||
mfem::ElementTransformation *transformation =
|
||||
f.mesh->GetElementTransformation(elementId);
|
||||
|
||||
MFEM_VERIFY(
|
||||
transformation != nullptr,
|
||||
"The pressure-force kernel received a null element "
|
||||
"transformation."
|
||||
);
|
||||
|
||||
/*
|
||||
* Skip vacuum before constructing or evaluating any mapping
|
||||
* data for the element.
|
||||
*/
|
||||
if (transformation->Attribute == vacuumAttribute) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const mfem::FiniteElement &enthalpyElement =
|
||||
*f.enthalpyFes->GetFE(elementId);
|
||||
|
||||
const mfem::FiniteElement &displacementElement =
|
||||
*f.displacementFes->GetFE(elementId);
|
||||
|
||||
const mfem::FiniteElement &compactificationElement =
|
||||
*f.compactificationFes->GetFE(elementId);
|
||||
|
||||
mfem::DofTransformation *enthalpyDofTransformation =
|
||||
f.enthalpyFes->GetElementDofs(elementId, enthalpyDofs);
|
||||
|
||||
mfem::DofTransformation *displacementDofTransformation =
|
||||
f.displacementFes->GetElementVDofs(elementId, displacementDofs);
|
||||
|
||||
mfem::DofTransformation *compactificationDofTransformation =
|
||||
f.compactificationFes->GetElementDofs(
|
||||
elementId, compactificationDofs
|
||||
);
|
||||
|
||||
enthalpyLocal.GetSubVector(enthalpyDofs, elementEnthalpy);
|
||||
|
||||
displacementLocal.GetSubVector(
|
||||
displacementDofs, elementDisplacement
|
||||
);
|
||||
|
||||
f.compactificationCoordinate->GetSubVector(
|
||||
compactificationDofs, elementCompactification
|
||||
);
|
||||
|
||||
if (enthalpyDofTransformation != nullptr) {
|
||||
enthalpyDofTransformation->InvTransformPrimal(elementEnthalpy);
|
||||
}
|
||||
|
||||
if (displacementDofTransformation != nullptr) {
|
||||
displacementDofTransformation->InvTransformPrimal(
|
||||
elementDisplacement
|
||||
);
|
||||
}
|
||||
|
||||
if (compactificationDofTransformation != nullptr) {
|
||||
compactificationDofTransformation->InvTransformPrimal(
|
||||
elementCompactification
|
||||
);
|
||||
}
|
||||
|
||||
const mapping::ElementDisplacementData displacementData =
|
||||
mapping::ElementDisplacementDataFromElementVDofs(
|
||||
displacementElement, elementDisplacement
|
||||
);
|
||||
|
||||
const mapping::ElementCompactificationData compactificationData(
|
||||
compactificationElement, elementCompactification
|
||||
);
|
||||
|
||||
const mapping::ElementMappingData mappingData{
|
||||
.displacement = displacementData,
|
||||
.compactification = compactificationData
|
||||
};
|
||||
|
||||
const int scalarDisplacementDofCount = displacementElement.GetDof();
|
||||
|
||||
MFEM_VERIFY(
|
||||
displacementDofs.Size() ==
|
||||
scalarDisplacementDofCount * dimension,
|
||||
"The pressure-force element displacement vector has "
|
||||
"the wrong size."
|
||||
);
|
||||
|
||||
enthalpyShape.SetSize(enthalpyElement.GetDof());
|
||||
|
||||
displacementDShapeReference.SetSize(
|
||||
scalarDisplacementDofCount, dimension
|
||||
);
|
||||
|
||||
displacementDShapePhysical.SetSize(
|
||||
scalarDisplacementDofCount, dimension
|
||||
);
|
||||
|
||||
elementResidual.SetSize(displacementDofs.Size());
|
||||
elementResidual = 0.0;
|
||||
|
||||
const mfem::IntegrationRule &integrationRule =
|
||||
get_pressure_force_rule(
|
||||
f, barotrope, enthalpyElement, displacementElement,
|
||||
*transformation
|
||||
);
|
||||
|
||||
for (int quadratureIndex = 0;
|
||||
quadratureIndex < integrationRule.GetNPoints();
|
||||
++quadratureIndex) {
|
||||
const mfem::IntegrationPoint &integrationPoint =
|
||||
integrationRule.IntPoint(quadratureIndex);
|
||||
|
||||
transformation->SetIntPoint(&integrationPoint);
|
||||
|
||||
const mapping::MappingStatus mappingStatus =
|
||||
domainMapper.EvaluateVolume(
|
||||
mappingData, *transformation, integrationPoint,
|
||||
workspace, mappingContext
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
mappingStatus == mapping::MappingStatus::valid,
|
||||
"Stateless mapping failed in the pressure-force "
|
||||
"kernel. Element: "
|
||||
<< elementId
|
||||
<< ", attribute: " << transformation->Attribute
|
||||
<< ", quadrature point: " << quadratureIndex
|
||||
<< ", status: " << static_cast<int>(mappingStatus)
|
||||
);
|
||||
|
||||
enthalpyElement.CalcShape(integrationPoint, enthalpyShape);
|
||||
|
||||
const double enthalpyValue = elementEnthalpy * enthalpyShape;
|
||||
|
||||
const double pressureValue =
|
||||
barotrope.pressure_from_enthalpy(enthalpyValue);
|
||||
|
||||
displacementElement.CalcDShape(
|
||||
integrationPoint, displacementDShapeReference
|
||||
);
|
||||
|
||||
/*
|
||||
* Row i of DShape is grad_reference(N_i). Multiplication
|
||||
* by the complete inverse element Jacobian gives
|
||||
*
|
||||
* grad_physical(N_i)
|
||||
* = grad_reference(N_i) J^{-1}.
|
||||
*/
|
||||
mfem::Mult(
|
||||
displacementDShapeReference,
|
||||
mappingContext.quadrature.J_inv, displacementDShapePhysical
|
||||
);
|
||||
|
||||
const double weightedPressure =
|
||||
pressureValue * mappingContext.quadrature.weight;
|
||||
|
||||
MFEM_VERIFY(
|
||||
std::isfinite(pressureValue) &&
|
||||
std::isfinite(weightedPressure),
|
||||
"The pressure-force kernel encountered a non-finite "
|
||||
"quadrature value."
|
||||
);
|
||||
|
||||
/*
|
||||
* For the vector basis N_i e_c,
|
||||
*
|
||||
* div(N_i e_c) = partial_c N_i.
|
||||
*
|
||||
* Therefore
|
||||
*
|
||||
* R_(i,c)
|
||||
* = -integral P partial_c N_i dV.
|
||||
*/
|
||||
for (int scalarDof = 0; scalarDof < scalarDisplacementDofCount;
|
||||
++scalarDof) {
|
||||
for (int component = 0; component < dimension;
|
||||
++component) {
|
||||
const int vectorDof = vector_dof_index(
|
||||
displacementOrdering, scalarDof, component,
|
||||
scalarDisplacementDofCount, dimension
|
||||
);
|
||||
|
||||
elementResidual(vectorDof) -=
|
||||
weightedPressure *
|
||||
displacementDShapePhysical(scalarDof, component);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (displacementDofTransformation != nullptr) {
|
||||
displacementDofTransformation->TransformDual(elementResidual);
|
||||
}
|
||||
|
||||
localResidual.AddElementVector(displacementDofs, elementResidual);
|
||||
}
|
||||
|
||||
local_to_true(*f.displacementFes, localResidual, residualTrue);
|
||||
}
|
||||
} // namespace mean_field::operators::kernels
|
||||
718
libmeanfield/impl/operators/prepared_barotropic_closure.cpp
Normal file
718
libmeanfield/impl/operators/prepared_barotropic_closure.cpp
Normal file
@@ -0,0 +1,718 @@
|
||||
module;
|
||||
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <mfem.hpp>
|
||||
|
||||
module mean_field;
|
||||
|
||||
import :operators.prepared_barotropic_closure;
|
||||
|
||||
namespace {
|
||||
int get_density_size(const mean_field::fem::FEM &f) {
|
||||
MFEM_VERIFY(
|
||||
f.densityFes != nullptr,
|
||||
"PreparedBarotropicClosureOperator requires the "
|
||||
"density finite-element space."
|
||||
);
|
||||
|
||||
return f.densityFes->GetTrueVSize();
|
||||
}
|
||||
|
||||
int get_enthalpy_size(const mean_field::fem::FEM &f) {
|
||||
MFEM_VERIFY(
|
||||
f.enthalpyFes != nullptr,
|
||||
"PreparedBarotropicClosureOperator requires the "
|
||||
"enthalpy finite-element space."
|
||||
);
|
||||
|
||||
return f.enthalpyFes->GetTrueVSize();
|
||||
}
|
||||
|
||||
void validate_finite_vector(
|
||||
const mfem::Vector &vector,
|
||||
const char *message
|
||||
) {
|
||||
for (int i = 0; i < vector.Size(); ++i) {
|
||||
MFEM_VERIFY(std::isfinite(vector(i)), message);
|
||||
}
|
||||
}
|
||||
|
||||
void true_to_local(
|
||||
const mfem::ParFiniteElementSpace &finiteElementSpace,
|
||||
const mfem::Vector &trueVector,
|
||||
mfem::Vector &localVector
|
||||
) {
|
||||
MFEM_VERIFY(
|
||||
trueVector.Size() == finiteElementSpace.GetTrueVSize(),
|
||||
"True vector has the wrong size."
|
||||
);
|
||||
|
||||
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
|
||||
) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
int get_eos_extra_order(
|
||||
const mean_field::physics::PolytropicBarotrope &barotrope
|
||||
) {
|
||||
const double extraOrder =
|
||||
(barotrope.polytropic_index() - 1.0) *
|
||||
static_cast<double>(
|
||||
mean_field::field::Enthalpy::Scalar::familyOrder
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
std::isfinite(extraOrder) && extraOrder >= 0.0 &&
|
||||
extraOrder <=
|
||||
static_cast<double>(std::numeric_limits<int>::max()),
|
||||
"The EOS effective polynomial order is invalid."
|
||||
);
|
||||
|
||||
return static_cast<int>(std::ceil(extraOrder));
|
||||
}
|
||||
|
||||
const mfem::IntegrationRule &get_eos_rule(
|
||||
const mean_field::fem::FEM &f,
|
||||
const mean_field::physics::PolytropicBarotrope &barotrope,
|
||||
const mfem::FiniteElement &densityElement,
|
||||
const mfem::FiniteElement &enthalpyElement,
|
||||
const mfem::ElementTransformation &transformation
|
||||
) {
|
||||
using EnthalpyField =
|
||||
mean_field::field::Field<mean_field::field::Enthalpy>;
|
||||
|
||||
MFEM_VERIFY(
|
||||
densityElement.GetOrder() ==
|
||||
mean_field::field::Density::Scalar::familyOrder,
|
||||
"The prepared EOS test element does not match "
|
||||
"the registered density field."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
enthalpyElement.GetOrder() ==
|
||||
mean_field::field::Enthalpy::Scalar::familyOrder,
|
||||
"The prepared EOS trial element does not match "
|
||||
"the registered enthalpy field."
|
||||
);
|
||||
|
||||
const mean_field::quadrature::Query query = EnthalpyField::make_query<
|
||||
mean_field::field::Enthalpy::Form::EosClosureSource>(
|
||||
mean_field::quadrature::QuadratureRole::discretization,
|
||||
transformation.OrderW(),
|
||||
std::array<int, 1>{get_eos_extra_order(barotrope)},
|
||||
mean_field::utils::DOMAINS::STELLAR,
|
||||
mean_field::quadrature::MappingKind::general
|
||||
);
|
||||
|
||||
const auto resolution =
|
||||
f.quadratureFactory->get(query, transformation.GetGeometryType());
|
||||
|
||||
MFEM_VERIFY(
|
||||
resolution.integration_rule != nullptr,
|
||||
"The quadrature policy did not return a prepared "
|
||||
"EOS-closure integration rule."
|
||||
);
|
||||
|
||||
return *resolution.integration_rule;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace mean_field::operators {
|
||||
PreparedBarotropicClosureOperator::PreparedBarotropicClosureOperator(
|
||||
const fem::FEM &f,
|
||||
const mapping::DomainMapperStateless &domainMapper,
|
||||
const physics::PolytropicBarotrope &barotrope
|
||||
)
|
||||
: mfem::Operator(
|
||||
f.densityFes->GetTrueVSize(),
|
||||
f.densityFes->GetTrueVSize() + f.enthalpyFes->GetTrueVSize() +
|
||||
f.displacementFes->GetTrueVSize()
|
||||
),
|
||||
m_fem(f),
|
||||
m_domainMapper(domainMapper),
|
||||
m_barotrope(barotrope),
|
||||
m_densitySize(f.densityFes->GetTrueVSize()),
|
||||
m_enthalpySize(f.enthalpyFes->GetTrueVSize()) {
|
||||
MFEM_VERIFY(
|
||||
m_fem.densityFes != nullptr,
|
||||
"PreparedBarotropicClosureOperator requires "
|
||||
"a density finite-element space."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
m_fem.enthalpyFes != nullptr,
|
||||
"PreparedBarotropicClosureOperator requires "
|
||||
"an enthalpy finite-element space."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
m_fem.displacementFes != nullptr,
|
||||
"PreparedBarotropicClosureOperator requires "
|
||||
"a displacement finite-element space."
|
||||
);
|
||||
}
|
||||
|
||||
void PreparedBarotropicClosureOperator::Prepare(
|
||||
const mfem::Vector &baseDensityTrue,
|
||||
const mfem::Vector &baseEnthalpyTrue,
|
||||
const mfem::Vector &displacementTrue
|
||||
) {
|
||||
MFEM_VERIFY(
|
||||
baseDensityTrue.Size() == m_densitySize,
|
||||
"PreparedBarotropicClosureOperator received a "
|
||||
"base-density vector with the wrong size."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
baseEnthalpyTrue.Size() == m_enthalpySize,
|
||||
"PreparedBarotropicClosureOperator received a "
|
||||
"base-enthalpy vector with the wrong size."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
displacementTrue.Size() == m_fem.displacementFes->GetTrueVSize(),
|
||||
"PreparedBarotropicClosureOperator received a "
|
||||
"displacement vector with the wrong size."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
baseDensityTrue.Size() == m_fem.densityFes->GetTrueVSize(),
|
||||
"The base density true vector has the wrong size."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
baseEnthalpyTrue.Size() == m_fem.enthalpyFes->GetTrueVSize(),
|
||||
"The base enthalpy true vector has the wrong size."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
displacementTrue.Size() == m_fem.displacementFes->GetTrueVSize(),
|
||||
"The base displacement true vector has the wrong size."
|
||||
);
|
||||
|
||||
validate_finite_vector(
|
||||
baseDensityTrue, "PreparedBarotropicClosureOperator received a "
|
||||
"non-finite base-density value."
|
||||
);
|
||||
|
||||
validate_finite_vector(
|
||||
baseEnthalpyTrue, "PreparedBarotropicClosureOperator received a "
|
||||
"non-finite base-enthalpy value."
|
||||
);
|
||||
|
||||
validate_finite_vector(
|
||||
displacementTrue, "PreparedBarotropicClosureOperator received a "
|
||||
"non-finite displacement value."
|
||||
);
|
||||
|
||||
m_isPrepared = false;
|
||||
m_elements.clear();
|
||||
m_elements.reserve(m_fem.mesh->GetNE());
|
||||
|
||||
mfem::Vector baseDensityLocal;
|
||||
mfem::Vector baseEnthalpyLocal;
|
||||
mfem::Vector displacementLocal;
|
||||
|
||||
true_to_local(*m_fem.densityFes, baseDensityTrue, baseDensityLocal);
|
||||
|
||||
true_to_local(*m_fem.enthalpyFes, baseEnthalpyTrue, baseEnthalpyLocal);
|
||||
|
||||
true_to_local(
|
||||
*m_fem.displacementFes, displacementTrue, displacementLocal
|
||||
);
|
||||
|
||||
mapping::DomainMapperStateless::Workspace workspace(
|
||||
m_fem.mesh->Dimension()
|
||||
);
|
||||
|
||||
mfem::Array<int> displacementDofs;
|
||||
mfem::Array<int> compactificationDofs;
|
||||
|
||||
mfem::Vector elementBaseDensity;
|
||||
mfem::Vector elementBaseEnthalpy;
|
||||
mfem::Vector elementDisplacement;
|
||||
mfem::Vector elementCompactification;
|
||||
|
||||
mfem::Vector densityShape;
|
||||
mfem::Vector enthalpyShape;
|
||||
|
||||
const int vacuumAttribute = m_domainMapper.GetVacuumElementAttribute();
|
||||
|
||||
for (int elementId = 0; elementId < m_fem.mesh->GetNE(); ++elementId) {
|
||||
mfem::ElementTransformation *transformation =
|
||||
m_fem.mesh->GetElementTransformation(elementId);
|
||||
|
||||
MFEM_VERIFY(
|
||||
transformation != nullptr,
|
||||
"PreparedBarotropicClosureOperator received "
|
||||
"a null element transformation."
|
||||
);
|
||||
|
||||
if (transformation->Attribute == vacuumAttribute) {
|
||||
continue;
|
||||
}
|
||||
|
||||
m_elements.emplace_back();
|
||||
ElementPAData &data = m_elements.back();
|
||||
|
||||
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
|
||||
);
|
||||
|
||||
mfem::DofTransformation *compactificationDofTransformation =
|
||||
m_fem.compactificationFes->GetElementDofs(
|
||||
elementId, compactificationDofs
|
||||
);
|
||||
|
||||
baseDensityLocal.GetSubVector(data.densityDofs, elementBaseDensity);
|
||||
|
||||
baseEnthalpyLocal.GetSubVector(
|
||||
data.enthalpyDofs, elementBaseEnthalpy
|
||||
);
|
||||
|
||||
displacementLocal.GetSubVector(
|
||||
displacementDofs, elementDisplacement
|
||||
);
|
||||
|
||||
m_fem.compactificationCoordinate->GetSubVector(
|
||||
compactificationDofs, elementCompactification
|
||||
);
|
||||
|
||||
if (data.densityDofTransformation != nullptr) {
|
||||
data.densityDofTransformation->InvTransformPrimal(
|
||||
elementBaseDensity
|
||||
);
|
||||
}
|
||||
|
||||
if (data.enthalpyDofTransformation != nullptr) {
|
||||
data.enthalpyDofTransformation->InvTransformPrimal(
|
||||
elementBaseEnthalpy
|
||||
);
|
||||
}
|
||||
|
||||
if (displacementDofTransformation != nullptr) {
|
||||
displacementDofTransformation->InvTransformPrimal(
|
||||
elementDisplacement
|
||||
);
|
||||
}
|
||||
|
||||
if (compactificationDofTransformation != nullptr) {
|
||||
compactificationDofTransformation->InvTransformPrimal(
|
||||
elementCompactification
|
||||
);
|
||||
}
|
||||
|
||||
const mfem::FiniteElement &densityElement =
|
||||
*m_fem.densityFes->GetFE(elementId);
|
||||
|
||||
const mfem::FiniteElement &enthalpyElement =
|
||||
*m_fem.enthalpyFes->GetFE(elementId);
|
||||
|
||||
const mfem::FiniteElement &displacementElement =
|
||||
*m_fem.displacementFes->GetFE(elementId);
|
||||
|
||||
const mfem::FiniteElement &compactificationElement =
|
||||
*m_fem.compactificationFes->GetFE(elementId);
|
||||
|
||||
const mapping::ElementDisplacementData displacementData =
|
||||
mapping::ElementDisplacementDataFromElementVDofs(
|
||||
displacementElement, elementDisplacement
|
||||
);
|
||||
|
||||
const mapping::ElementCompactificationData compactificationData(
|
||||
compactificationElement, elementCompactification
|
||||
);
|
||||
|
||||
const mapping::ElementMappingData mappingData{
|
||||
.displacement = displacementData,
|
||||
.compactification = compactificationData
|
||||
};
|
||||
|
||||
const mfem::IntegrationRule &integrationRule = get_eos_rule(
|
||||
m_fem, m_barotrope, densityElement, enthalpyElement,
|
||||
*transformation
|
||||
);
|
||||
|
||||
const int quadraturePointCount = integrationRule.GetNPoints();
|
||||
|
||||
const int densityDofCount = densityElement.GetDof();
|
||||
|
||||
const int enthalpyDofCount = enthalpyElement.GetDof();
|
||||
|
||||
data.densityBasis.SetSize(quadraturePointCount, densityDofCount);
|
||||
|
||||
data.enthalpyBasis.SetSize(quadraturePointCount, enthalpyDofCount);
|
||||
|
||||
data.weightedResidual.SetSize(quadraturePointCount);
|
||||
|
||||
data.quadratureWeights.SetSize(quadraturePointCount);
|
||||
|
||||
data.weightedEnthalpyDerivative.SetSize(quadraturePointCount);
|
||||
|
||||
densityShape.SetSize(densityDofCount);
|
||||
|
||||
enthalpyShape.SetSize(enthalpyDofCount);
|
||||
|
||||
for (int quadraturePoint = 0;
|
||||
quadraturePoint < quadraturePointCount; ++quadraturePoint) {
|
||||
const mfem::IntegrationPoint &integrationPoint =
|
||||
integrationRule.IntPoint(quadraturePoint);
|
||||
|
||||
transformation->SetIntPoint(&integrationPoint);
|
||||
|
||||
mapping::VolumeMappingContext mappingContext;
|
||||
|
||||
const mapping::MappingStatus mappingStatus =
|
||||
m_domainMapper.EvaluateVolume(
|
||||
mappingData, *transformation, integrationPoint,
|
||||
workspace, mappingContext
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
mappingStatus == mapping::MappingStatus::valid,
|
||||
"Stateless mapping failed while preparing "
|
||||
"the barotropic closure operator. Element: "
|
||||
<< elementId
|
||||
<< ", attribute: " << transformation->Attribute
|
||||
<< ", quadrature point: " << quadraturePoint
|
||||
<< ", status: " << static_cast<int>(mappingStatus)
|
||||
);
|
||||
|
||||
densityElement.CalcShape(integrationPoint, densityShape);
|
||||
|
||||
enthalpyElement.CalcShape(integrationPoint, enthalpyShape);
|
||||
|
||||
for (int densityDof = 0; densityDof < densityDofCount;
|
||||
++densityDof) {
|
||||
data.densityBasis(quadraturePoint, densityDof) =
|
||||
densityShape(densityDof);
|
||||
}
|
||||
|
||||
for (int enthalpyDof = 0; enthalpyDof < enthalpyDofCount;
|
||||
++enthalpyDof) {
|
||||
data.enthalpyBasis(quadraturePoint, enthalpyDof) =
|
||||
enthalpyShape(enthalpyDof);
|
||||
}
|
||||
|
||||
const double density = elementBaseDensity * densityShape;
|
||||
|
||||
const double enthalpy = elementBaseEnthalpy * enthalpyShape;
|
||||
|
||||
const double quadratureWeight =
|
||||
mappingContext.quadrature.weight;
|
||||
|
||||
const double eosDensity =
|
||||
m_barotrope.density_from_enthalpy(enthalpy);
|
||||
|
||||
const double enthalpyDerivative =
|
||||
m_barotrope.density_derivative_from_enthalpy(enthalpy);
|
||||
|
||||
MFEM_VERIFY(
|
||||
std::isfinite(quadratureWeight) && quadratureWeight > 0.0 &&
|
||||
std::isfinite(eosDensity) &&
|
||||
std::isfinite(enthalpyDerivative),
|
||||
"PreparedBarotropicClosureOperator "
|
||||
"encountered invalid quadrature data."
|
||||
);
|
||||
|
||||
data.quadratureWeights(quadraturePoint) = quadratureWeight;
|
||||
|
||||
data.weightedResidual(quadraturePoint) =
|
||||
quadratureWeight * (density - eosDensity);
|
||||
|
||||
data.weightedEnthalpyDerivative(quadraturePoint) =
|
||||
quadratureWeight * enthalpyDerivative;
|
||||
}
|
||||
}
|
||||
|
||||
MFEM_VERIFY(
|
||||
!m_elements.empty(), "PreparedBarotropicClosureOperator found no "
|
||||
"stellar elements."
|
||||
);
|
||||
|
||||
m_baseDensityTrue = baseDensityTrue;
|
||||
m_baseEnthalpyTrue = baseEnthalpyTrue;
|
||||
m_baseDisplacementTrue = displacementTrue;
|
||||
|
||||
m_isPrepared = true;
|
||||
++m_preparationCount;
|
||||
}
|
||||
|
||||
void PreparedBarotropicClosureOperator::BuildResidual(
|
||||
mfem::Vector &residual
|
||||
) const {
|
||||
MFEM_VERIFY(
|
||||
m_isPrepared, "PreparedBarotropicClosureOperator must be "
|
||||
"prepared before BuildResidual is called."
|
||||
);
|
||||
|
||||
mfem::Vector localResidual(m_fem.densityFes->GetVSize());
|
||||
localResidual = 0.0;
|
||||
|
||||
mfem::Vector elementResidual;
|
||||
|
||||
for (const ElementPAData &data : m_elements) {
|
||||
elementResidual.SetSize(data.densityDofs.Size());
|
||||
|
||||
data.densityBasis.MultTranspose(
|
||||
data.weightedResidual, elementResidual
|
||||
);
|
||||
|
||||
if (data.densityDofTransformation != nullptr) {
|
||||
data.densityDofTransformation->TransformDual(elementResidual);
|
||||
}
|
||||
|
||||
localResidual.AddElementVector(data.densityDofs, elementResidual);
|
||||
}
|
||||
|
||||
local_to_true(*m_fem.densityFes, localResidual, residual);
|
||||
}
|
||||
|
||||
void PreparedBarotropicClosureOperator::Mult(
|
||||
const mfem::Vector &densityVariationTrue,
|
||||
const mfem::Vector &enthalpyVariationTrue,
|
||||
const mfem::Vector &displacementVariationTrue,
|
||||
mfem::Vector &action
|
||||
) const {
|
||||
VerifyPrepared();
|
||||
|
||||
MFEM_VERIFY(
|
||||
densityVariationTrue.Size() == m_densitySize,
|
||||
"The density-variation true vector has "
|
||||
"the wrong size."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
enthalpyVariationTrue.Size() == m_enthalpySize,
|
||||
"The enthalpy-variation true vector has "
|
||||
"the wrong size."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
displacementVariationTrue.Size() ==
|
||||
m_fem.displacementFes->GetTrueVSize(),
|
||||
"The displacement-variation true vector has "
|
||||
"the wrong size."
|
||||
);
|
||||
|
||||
Mult(densityVariationTrue, enthalpyVariationTrue, action);
|
||||
|
||||
mfem::Vector displacementAction;
|
||||
|
||||
kernels::apply_barotropic_closure_displacement_action(
|
||||
m_fem, m_domainMapper, m_barotrope, m_baseDensityTrue,
|
||||
m_baseEnthalpyTrue, m_baseDisplacementTrue,
|
||||
displacementVariationTrue, displacementAction
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
displacementAction.Size() == m_densitySize,
|
||||
"The barotropic-closure displacement action "
|
||||
"returned a vector with the wrong size."
|
||||
);
|
||||
|
||||
action += displacementAction;
|
||||
}
|
||||
|
||||
void PreparedBarotropicClosureOperator::Mult(
|
||||
const mfem::Vector &combinedVariation,
|
||||
mfem::Vector &action
|
||||
) const {
|
||||
VerifyPrepared();
|
||||
|
||||
const int displacementSize = m_fem.displacementFes->GetTrueVSize();
|
||||
|
||||
const int combinedSize =
|
||||
m_densitySize + m_enthalpySize + displacementSize;
|
||||
|
||||
MFEM_VERIFY(
|
||||
combinedVariation.Size() == combinedSize,
|
||||
"The combined barotropic-closure variation "
|
||||
"vector has the wrong size. Expected "
|
||||
<< combinedSize << " entries but received "
|
||||
<< combinedVariation.Size() << "."
|
||||
);
|
||||
|
||||
mfem::real_t *combinedData =
|
||||
const_cast<mfem::real_t *>(combinedVariation.HostRead());
|
||||
|
||||
const mfem::Vector densityVariationTrue(combinedData, m_densitySize);
|
||||
|
||||
const mfem::Vector enthalpyVariationTrue(
|
||||
combinedData + m_densitySize, m_enthalpySize
|
||||
);
|
||||
|
||||
const mfem::Vector displacementVariationTrue(
|
||||
combinedData + m_densitySize + m_enthalpySize, displacementSize
|
||||
);
|
||||
|
||||
Mult(
|
||||
densityVariationTrue, enthalpyVariationTrue,
|
||||
displacementVariationTrue, action
|
||||
);
|
||||
}
|
||||
|
||||
void PreparedBarotropicClosureOperator::Mult(
|
||||
const mfem::Vector &densityVariationTrue,
|
||||
const mfem::Vector &enthalpyVariationTrue,
|
||||
mfem::Vector &action
|
||||
) const {
|
||||
MFEM_VERIFY(
|
||||
m_isPrepared, "PreparedBarotropicClosureOperator must be "
|
||||
"prepared before Mult is called."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
densityVariationTrue.Size() == m_densitySize,
|
||||
"PreparedBarotropicClosureOperator received a "
|
||||
"density variation with the wrong size."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
enthalpyVariationTrue.Size() == m_enthalpySize,
|
||||
"PreparedBarotropicClosureOperator received an "
|
||||
"enthalpy variation with the wrong size."
|
||||
);
|
||||
|
||||
mfem::Vector densityVariationLocal;
|
||||
mfem::Vector enthalpyVariationLocal;
|
||||
|
||||
true_to_local(
|
||||
*m_fem.densityFes, densityVariationTrue, densityVariationLocal
|
||||
);
|
||||
|
||||
true_to_local(
|
||||
*m_fem.enthalpyFes, enthalpyVariationTrue, enthalpyVariationLocal
|
||||
);
|
||||
|
||||
mfem::Vector localAction(m_fem.densityFes->GetVSize());
|
||||
localAction = 0.0;
|
||||
|
||||
mfem::Vector elementDensityVariation;
|
||||
mfem::Vector elementEnthalpyVariation;
|
||||
mfem::Vector quadratureDensityVariation;
|
||||
mfem::Vector quadratureEnthalpyVariation;
|
||||
mfem::Vector quadratureAction;
|
||||
mfem::Vector elementAction;
|
||||
|
||||
for (const ElementPAData &data : m_elements) {
|
||||
densityVariationLocal.GetSubVector(
|
||||
data.densityDofs, elementDensityVariation
|
||||
);
|
||||
|
||||
enthalpyVariationLocal.GetSubVector(
|
||||
data.enthalpyDofs, elementEnthalpyVariation
|
||||
);
|
||||
|
||||
if (data.densityDofTransformation != nullptr) {
|
||||
data.densityDofTransformation->InvTransformPrimal(
|
||||
elementDensityVariation
|
||||
);
|
||||
}
|
||||
|
||||
if (data.enthalpyDofTransformation != nullptr) {
|
||||
data.enthalpyDofTransformation->InvTransformPrimal(
|
||||
elementEnthalpyVariation
|
||||
);
|
||||
}
|
||||
|
||||
quadratureDensityVariation.SetSize(data.quadratureWeights.Size());
|
||||
|
||||
quadratureEnthalpyVariation.SetSize(data.quadratureWeights.Size());
|
||||
|
||||
quadratureAction.SetSize(data.quadratureWeights.Size());
|
||||
|
||||
data.densityBasis.Mult(
|
||||
elementDensityVariation, quadratureDensityVariation
|
||||
);
|
||||
|
||||
data.enthalpyBasis.Mult(
|
||||
elementEnthalpyVariation, quadratureEnthalpyVariation
|
||||
);
|
||||
|
||||
for (int quadraturePoint = 0;
|
||||
quadraturePoint < quadratureAction.Size(); ++quadraturePoint) {
|
||||
quadratureAction(quadraturePoint) =
|
||||
data.quadratureWeights(quadraturePoint) *
|
||||
quadratureDensityVariation(quadraturePoint) -
|
||||
data.weightedEnthalpyDerivative(quadraturePoint) *
|
||||
quadratureEnthalpyVariation(quadraturePoint);
|
||||
}
|
||||
|
||||
elementAction.SetSize(data.densityDofs.Size());
|
||||
|
||||
data.densityBasis.MultTranspose(quadratureAction, elementAction);
|
||||
|
||||
if (data.densityDofTransformation != nullptr) {
|
||||
data.densityDofTransformation->TransformDual(elementAction);
|
||||
}
|
||||
|
||||
localAction.AddElementVector(data.densityDofs, elementAction);
|
||||
}
|
||||
|
||||
local_to_true(*m_fem.densityFes, localAction, action);
|
||||
}
|
||||
|
||||
bool PreparedBarotropicClosureOperator::IsPrepared() const noexcept {
|
||||
return m_isPrepared;
|
||||
}
|
||||
|
||||
std::uint64_t
|
||||
PreparedBarotropicClosureOperator::GetPreparationCount() const noexcept {
|
||||
return m_preparationCount;
|
||||
}
|
||||
|
||||
int PreparedBarotropicClosureOperator::GetDensitySize() const noexcept {
|
||||
return m_densitySize;
|
||||
}
|
||||
|
||||
int PreparedBarotropicClosureOperator::GetEnthalpySize() const noexcept {
|
||||
return m_enthalpySize;
|
||||
}
|
||||
|
||||
void PreparedBarotropicClosureOperator::VerifyPrepared() const {
|
||||
MFEM_VERIFY(
|
||||
m_isPrepared, "PreparedBarotropicClosureOperator must be "
|
||||
"prepared before this operation is called."
|
||||
);
|
||||
}
|
||||
} // namespace mean_field::operators
|
||||
617
libmeanfield/impl/operators/prepared_gravity_source.cpp
Normal file
617
libmeanfield/impl/operators/prepared_gravity_source.cpp
Normal file
@@ -0,0 +1,617 @@
|
||||
module;
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <mfem.hpp>
|
||||
#include <numbers>
|
||||
|
||||
module mean_field;
|
||||
import :operators.prepared_gravity_source;
|
||||
|
||||
namespace {
|
||||
int get_operator_height(const mean_field::fem::FEM &f) {
|
||||
MFEM_VERIFY(
|
||||
f.gravityPotentialFes != nullptr,
|
||||
"PreparedMappedGravitySourceOperator requires the "
|
||||
"gravity-potential "
|
||||
"finite-element space."
|
||||
);
|
||||
return f.gravityPotentialFes->GetTrueVSize();
|
||||
}
|
||||
|
||||
int get_operator_width(const mean_field::fem::FEM &f) {
|
||||
MFEM_VERIFY(
|
||||
f.densityFes != nullptr,
|
||||
"PreparedMappedGravitySourceOperator requires the density "
|
||||
"finite-element space."
|
||||
);
|
||||
return f.densityFes->GetTrueVSize();
|
||||
}
|
||||
|
||||
void true_to_local(
|
||||
const mfem::ParFiniteElementSpace &finite_element_space,
|
||||
const mfem::Vector &true_vector,
|
||||
mfem::Vector &local_vector
|
||||
) {
|
||||
local_vector.SetSize(finite_element_space.GetVSize());
|
||||
|
||||
const mfem::Operator *prolongation =
|
||||
finite_element_space.GetProlongationMatrix();
|
||||
|
||||
if (prolongation != nullptr) {
|
||||
prolongation->Mult(true_vector, local_vector);
|
||||
} else {
|
||||
local_vector = true_vector;
|
||||
}
|
||||
}
|
||||
|
||||
void local_to_true(
|
||||
const mfem::ParFiniteElementSpace &finite_element_space,
|
||||
const mfem::Vector &local_vector,
|
||||
mfem::Vector &true_vector
|
||||
) {
|
||||
MFEM_VERIFY(
|
||||
local_vector.Size() == finite_element_space.GetVSize(),
|
||||
"Local vector has the wrong size."
|
||||
);
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
const mfem::IntegrationRule &get_source_rule(
|
||||
const mean_field::fem::FEM &f,
|
||||
const mfem::FiniteElement &density_element,
|
||||
const mfem::FiniteElement &potential_element,
|
||||
const mfem::ElementTransformation &transformation
|
||||
) {
|
||||
using GravityField =
|
||||
mean_field::field::Field<mean_field::field::Gravity>;
|
||||
MFEM_VERIFY(
|
||||
density_element.GetOrder() ==
|
||||
mean_field::field::Density::Scalar::familyOrder,
|
||||
"The prepared source trial element does not match the registered "
|
||||
"density field."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
potential_element.GetOrder() ==
|
||||
mean_field::field::Gravity::Potential::familyOrder,
|
||||
"The prepared source test element does not match the registered "
|
||||
"gravity potential."
|
||||
);
|
||||
const mean_field::quadrature::Query query = GravityField::make_query<
|
||||
mean_field::field::Gravity::Form::SourceProjection>(
|
||||
mean_field::quadrature::QuadratureRole::discretization,
|
||||
transformation.OrderW(), {}, mean_field::utils::DOMAINS::STELLAR,
|
||||
mean_field::quadrature::MappingKind::general
|
||||
);
|
||||
|
||||
return *f.quadratureFactory
|
||||
->get(query, transformation.GetGeometryType())
|
||||
.integration_rule;
|
||||
}
|
||||
|
||||
class FrozenMappedGravitySourceCoefficient final
|
||||
: public mfem::Coefficient {
|
||||
public:
|
||||
FrozenMappedGravitySourceCoefficient(
|
||||
const mean_field::fem::FEM &f,
|
||||
const mean_field::mapping::DomainMapperStateless &domain_mapper,
|
||||
const mfem::Vector &displacement_true
|
||||
)
|
||||
: m_fem(f),
|
||||
m_domain_mapper(domain_mapper),
|
||||
m_workspace(domain_mapper.GetDimension()) {
|
||||
true_to_local(
|
||||
*m_fem.displacementFes, displacement_true, m_displacement_local
|
||||
);
|
||||
}
|
||||
|
||||
double Eval(
|
||||
mfem::ElementTransformation &transformation,
|
||||
const mfem::IntegrationPoint &integration_point
|
||||
) override {
|
||||
transformation.SetIntPoint(&integration_point);
|
||||
|
||||
const int element_id = transformation.ElementNo;
|
||||
MFEM_VERIFY(
|
||||
element_id >= 0 && element_id < m_fem.mesh->GetNE(),
|
||||
"Mapped gravity source coefficient received an invalid element "
|
||||
"ID."
|
||||
);
|
||||
if (transformation.Attribute ==
|
||||
m_domain_mapper.GetVacuumElementAttribute()) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
LoadElement(element_id);
|
||||
const mean_field::mapping::ElementMappingData mapping_data{
|
||||
.displacement = *m_displacement_data,
|
||||
.compactification = *m_compactification_data
|
||||
};
|
||||
|
||||
mean_field::mapping::VolumeMappingContext mapping_context;
|
||||
|
||||
const mean_field::mapping::MappingStatus status =
|
||||
m_domain_mapper.EvaluateVolume(
|
||||
mapping_data, transformation, integration_point,
|
||||
m_workspace, mapping_context
|
||||
);
|
||||
|
||||
if (status != mean_field::mapping::MappingStatus::valid) {
|
||||
const mfem::FiniteElement &displacement_element =
|
||||
*m_fem.displacementFes->GetFE(element_id);
|
||||
const mfem::FiniteElement &compactification_element =
|
||||
*m_fem.compactificationFes->GetFE(element_id);
|
||||
|
||||
mfem::Vector displacement_shape(displacement_element.GetDof());
|
||||
mfem::Vector compactification_shape(
|
||||
compactification_element.GetDof()
|
||||
);
|
||||
mfem::Vector reference_position(m_domain_mapper.GetDimension());
|
||||
mfem::Vector displacement_value(m_domain_mapper.GetDimension());
|
||||
|
||||
displacement_element.CalcShape(
|
||||
integration_point, displacement_shape
|
||||
);
|
||||
compactification_element.CalcShape(
|
||||
integration_point, compactification_shape
|
||||
);
|
||||
transformation.Transform(integration_point, reference_position);
|
||||
m_displacement_data->GetDofMatrix().MultTranspose(
|
||||
displacement_shape, displacement_value
|
||||
);
|
||||
|
||||
const double compactification_coordinate =
|
||||
m_compactification_data->GetDofs() * compactification_shape;
|
||||
|
||||
MFEM_ABORT(
|
||||
"Stateless domain mapping failed while preparing the "
|
||||
"gravity "
|
||||
"source operator."
|
||||
<< "\nMapping status = " << static_cast<int>(status)
|
||||
<< "\nElement ID = " << element_id
|
||||
<< "\nElement attribute = " << transformation.Attribute
|
||||
<< "\nIntegration-point index = " << integration_point.index
|
||||
<< "\nIntegration point = <" << integration_point.x << ", "
|
||||
<< integration_point.y << ", " << integration_point.z << ">"
|
||||
<< "\nReference position = <" << reference_position(0)
|
||||
<< ", " << reference_position(1) << ", "
|
||||
<< reference_position(2) << ">"
|
||||
<< "\nReference radius = " << reference_position.Norml2()
|
||||
<< "\nDisplacement value = <" << displacement_value(0)
|
||||
<< ", " << displacement_value(1) << ", "
|
||||
<< displacement_value(2) << ">"
|
||||
<< "\nDisplacement magnitude = "
|
||||
<< displacement_value.Norml2()
|
||||
<< "\nCompactification coordinate = "
|
||||
<< compactification_coordinate
|
||||
<< "\nDisplacement ordering = "
|
||||
<< static_cast<int>(m_fem.displacementFes->GetOrdering())
|
||||
);
|
||||
}
|
||||
const double mapping_determinant =
|
||||
mapping_context.mapping.mapping_determinant;
|
||||
MFEM_VERIFY(
|
||||
std::isfinite(mapping_determinant) && mapping_determinant > 0.0,
|
||||
"Prepared gravity source operator encountered a non-positive "
|
||||
"or "
|
||||
"non-finite mapping determinant."
|
||||
);
|
||||
|
||||
return 4.0 * std::numbers::pi * mean_field::utils::G *
|
||||
mapping_determinant;
|
||||
}
|
||||
|
||||
private:
|
||||
void LoadElement(const int element_id) {
|
||||
if (element_id == m_cached_element_id) {
|
||||
return;
|
||||
}
|
||||
|
||||
const mfem::FiniteElement &displacement_element =
|
||||
*m_fem.displacementFes->GetFE(element_id);
|
||||
const mfem::FiniteElement &compactification_element =
|
||||
*m_fem.compactificationFes->GetFE(element_id);
|
||||
|
||||
mfem::DofTransformation *displacement_dof_transformation =
|
||||
m_fem.displacementFes->GetElementVDofs(
|
||||
element_id, m_displacement_dofs
|
||||
);
|
||||
mfem::DofTransformation *compactification_dof_transformation =
|
||||
m_fem.compactificationFes->GetElementDofs(
|
||||
element_id, m_compactification_dofs
|
||||
);
|
||||
|
||||
m_displacement_local.GetSubVector(
|
||||
m_displacement_dofs, m_element_displacement
|
||||
);
|
||||
m_fem.compactificationCoordinate->GetSubVector(
|
||||
m_compactification_dofs, m_element_compactification
|
||||
);
|
||||
|
||||
if (displacement_dof_transformation != nullptr) {
|
||||
displacement_dof_transformation->InvTransformPrimal(
|
||||
m_element_displacement
|
||||
);
|
||||
}
|
||||
|
||||
if (compactification_dof_transformation != nullptr) {
|
||||
compactification_dof_transformation->InvTransformPrimal(
|
||||
m_element_compactification
|
||||
);
|
||||
}
|
||||
|
||||
m_displacement_data = std::make_unique<
|
||||
mean_field::mapping::ElementDisplacementData>(
|
||||
mean_field::mapping::ElementDisplacementDataFromElementVDofs(
|
||||
displacement_element, m_element_displacement
|
||||
)
|
||||
);
|
||||
|
||||
m_compactification_data = std::make_unique<
|
||||
mean_field::mapping::ElementCompactificationData>(
|
||||
compactification_element, m_element_compactification
|
||||
);
|
||||
|
||||
m_cached_element_id = element_id;
|
||||
}
|
||||
|
||||
const mean_field::fem::FEM &m_fem;
|
||||
const mean_field::mapping::DomainMapperStateless &m_domain_mapper;
|
||||
|
||||
mfem::Vector m_displacement_local;
|
||||
|
||||
mfem::Array<int> m_displacement_dofs;
|
||||
mfem::Array<int> m_compactification_dofs;
|
||||
|
||||
mfem::Vector m_element_displacement;
|
||||
mfem::Vector m_element_compactification;
|
||||
|
||||
std::unique_ptr<mean_field::mapping::ElementDisplacementData>
|
||||
m_displacement_data;
|
||||
std::unique_ptr<mean_field::mapping::ElementCompactificationData>
|
||||
m_compactification_data;
|
||||
|
||||
mean_field::mapping::DomainMapperStateless::Workspace m_workspace;
|
||||
int m_cached_element_id{-1};
|
||||
};
|
||||
} // namespace
|
||||
|
||||
namespace mean_field::operators {
|
||||
PreparedMappedGravitySourceOperator::PreparedMappedGravitySourceOperator(
|
||||
const fem::FEM &f,
|
||||
const mapping::DomainMapperStateless &domain_mapper
|
||||
)
|
||||
: Operator(
|
||||
get_operator_height(f),
|
||||
get_operator_width(f)
|
||||
),
|
||||
m_fem(f),
|
||||
m_domain_mapper(domain_mapper) {
|
||||
MFEM_VERIFY(
|
||||
f.mesh != nullptr,
|
||||
"PreparedMappedGravitySourceOperator requires a mesh."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.densityFes != nullptr,
|
||||
"PreparedMappedGravitySourceOperator requires the density "
|
||||
"finite-element space."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.gravityPotentialFes != nullptr,
|
||||
"PreparedMappedGravitySourceOperator requires the "
|
||||
"gravity-potential "
|
||||
"finite-element space."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.displacementFes != nullptr,
|
||||
"PreparedMappedGravitySourceOperator requires "
|
||||
"the displacement finite-element space."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.compactificationFes != nullptr,
|
||||
"PreparedMappedGravitySourceOperator requires the compactification "
|
||||
"finite-element space."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.compactificationCoordinate != nullptr,
|
||||
"PreparedMappedGravitySourceOperator requires the compactification "
|
||||
"coordinate."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.quadratureFactory != nullptr,
|
||||
"PreparedMappedGravitySourceOperator "
|
||||
"requires the quadrature-rule factory."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
domain_mapper.GetDimension() == f.mesh->Dimension(),
|
||||
"The stateless domain-mapper dimension does not match the mesh "
|
||||
"dimension."
|
||||
);
|
||||
|
||||
utils::populate_element_mask(
|
||||
f.mesh.get(), utils::DOMAINS::STELLAR, m_stellar_marker
|
||||
);
|
||||
}
|
||||
|
||||
void PreparedMappedGravitySourceOperator::Prepare(
|
||||
const mfem::Vector &displacement_true
|
||||
) {
|
||||
MFEM_VERIFY(
|
||||
displacement_true.Size() == m_fem.displacementFes->GetTrueVSize(),
|
||||
"PreparedMappedGravitySourceOperator received a displacement "
|
||||
"vector "
|
||||
"with the wrong size."
|
||||
);
|
||||
|
||||
for (int i = 0; i < displacement_true.Size(); ++i) {
|
||||
MFEM_VERIFY(
|
||||
std::isfinite(displacement_true(i)),
|
||||
"PreparedMappedGravitySourceOperator received a non-finite "
|
||||
"displacement value."
|
||||
);
|
||||
}
|
||||
|
||||
m_is_prepared = false;
|
||||
m_elements.clear();
|
||||
m_elements.reserve(m_fem.mesh->GetNE());
|
||||
|
||||
FrozenMappedGravitySourceCoefficient source_coefficient(
|
||||
m_fem, m_domain_mapper, displacement_true
|
||||
);
|
||||
|
||||
for (int element_id = 0; element_id < m_fem.mesh->GetNE();
|
||||
++element_id) {
|
||||
const int attribute = m_fem.mesh->GetAttribute(element_id);
|
||||
|
||||
if (attribute <= 0 || attribute > m_stellar_marker.Size() ||
|
||||
m_stellar_marker[attribute - 1] == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
m_elements.emplace_back();
|
||||
ElementPAData &data = m_elements.back();
|
||||
|
||||
data.element_id = element_id;
|
||||
|
||||
data.density_dof_transformation =
|
||||
m_fem.densityFes->GetElementDofs(element_id, data.density_dofs);
|
||||
|
||||
data.potential_dof_transformation =
|
||||
m_fem.gravityPotentialFes->GetElementDofs(
|
||||
element_id, data.potential_dofs
|
||||
);
|
||||
|
||||
const mfem::FiniteElement &density_element =
|
||||
*m_fem.densityFes->GetFE(element_id);
|
||||
|
||||
const mfem::FiniteElement &potential_element =
|
||||
*m_fem.gravityPotentialFes->GetFE(element_id);
|
||||
|
||||
mfem::ElementTransformation &transformation =
|
||||
*m_fem.mesh->GetElementTransformation(element_id);
|
||||
|
||||
const mfem::IntegrationRule &integration_rule = get_source_rule(
|
||||
m_fem, density_element, potential_element, transformation
|
||||
);
|
||||
|
||||
const int quadrature_point_count = integration_rule.GetNPoints();
|
||||
|
||||
const int density_dof_count = density_element.GetDof();
|
||||
|
||||
const int potential_dof_count = potential_element.GetDof();
|
||||
|
||||
data.density_basis.SetSize(
|
||||
quadrature_point_count, density_dof_count
|
||||
);
|
||||
|
||||
data.potential_basis.SetSize(
|
||||
quadrature_point_count, potential_dof_count
|
||||
);
|
||||
|
||||
data.quadrature_data.SetSize(quadrature_point_count);
|
||||
|
||||
mfem::Vector density_shape(density_dof_count);
|
||||
mfem::Vector potential_shape(potential_dof_count);
|
||||
|
||||
for (int quadrature_point = 0;
|
||||
quadrature_point < quadrature_point_count;
|
||||
++quadrature_point) {
|
||||
const mfem::IntegrationPoint &integration_point =
|
||||
integration_rule.IntPoint(quadrature_point);
|
||||
|
||||
transformation.SetIntPoint(&integration_point);
|
||||
|
||||
// CalcPhysShape matches the scalar mixed-mass discretization,
|
||||
// including the finite-element map type.
|
||||
density_element.CalcPhysShape(transformation, density_shape);
|
||||
|
||||
potential_element.CalcPhysShape(
|
||||
transformation, potential_shape
|
||||
);
|
||||
|
||||
for (int i = 0; i < density_dof_count; ++i) {
|
||||
data.density_basis(quadrature_point, i) = density_shape(i);
|
||||
}
|
||||
|
||||
for (int i = 0; i < potential_dof_count; ++i) {
|
||||
data.potential_basis(quadrature_point, i) =
|
||||
potential_shape(i);
|
||||
}
|
||||
|
||||
const double coefficient_value =
|
||||
source_coefficient.Eval(transformation, integration_point);
|
||||
|
||||
transformation.SetIntPoint(&integration_point);
|
||||
|
||||
const double quadrature_value = integration_point.weight *
|
||||
transformation.Weight() *
|
||||
coefficient_value;
|
||||
|
||||
MFEM_VERIFY(
|
||||
std::isfinite(quadrature_value) && quadrature_value > 0.0,
|
||||
"Prepared gravity source operator encountered invalid "
|
||||
"quadrature data on element "
|
||||
<< element_id << ", quadrature point "
|
||||
<< quadrature_point << "."
|
||||
);
|
||||
|
||||
data.quadrature_data(quadrature_point) = quadrature_value;
|
||||
}
|
||||
}
|
||||
|
||||
MFEM_VERIFY(
|
||||
!m_elements.empty(),
|
||||
"PreparedMappedGravitySourceOperator found no stellar elements."
|
||||
);
|
||||
|
||||
m_is_prepared = true;
|
||||
++m_preparation_count;
|
||||
}
|
||||
void PreparedMappedGravitySourceOperator::Mult(
|
||||
const mfem::Vector &density_true,
|
||||
mfem::Vector &action
|
||||
) const {
|
||||
MFEM_VERIFY(
|
||||
m_is_prepared,
|
||||
"PreparedMappedGravitySourceOperator must be prepared before "
|
||||
"Mult is called."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
density_true.Size() == Width(),
|
||||
"PreparedMappedGravitySourceOperator received a density vector "
|
||||
"with the wrong size."
|
||||
);
|
||||
|
||||
mfem::Vector density_local;
|
||||
|
||||
true_to_local(*m_fem.densityFes, density_true, density_local);
|
||||
|
||||
mfem::Vector local_action(m_fem.gravityPotentialFes->GetVSize());
|
||||
local_action = 0.0;
|
||||
|
||||
mfem::Vector element_density;
|
||||
mfem::Vector quadrature_density;
|
||||
mfem::Vector element_action;
|
||||
|
||||
for (const ElementPAData &data : m_elements) {
|
||||
density_local.GetSubVector(data.density_dofs, element_density);
|
||||
|
||||
if (data.density_dof_transformation != nullptr) {
|
||||
data.density_dof_transformation->InvTransformPrimal(
|
||||
element_density
|
||||
);
|
||||
}
|
||||
|
||||
quadrature_density.SetSize(data.quadrature_data.Size());
|
||||
|
||||
// B_density * x_e
|
||||
data.density_basis.Mult(element_density, quadrature_density);
|
||||
|
||||
// D * B_density * x_e
|
||||
for (int q = 0; q < quadrature_density.Size(); ++q) {
|
||||
quadrature_density(q) *= data.quadrature_data(q);
|
||||
}
|
||||
|
||||
element_action.SetSize(data.potential_dofs.Size());
|
||||
|
||||
// B_potential^T * D * B_density * x_e
|
||||
data.potential_basis.MultTranspose(
|
||||
quadrature_density, element_action
|
||||
);
|
||||
|
||||
if (data.potential_dof_transformation != nullptr) {
|
||||
data.potential_dof_transformation->TransformDual(
|
||||
element_action
|
||||
);
|
||||
}
|
||||
|
||||
local_action.AddElementVector(data.potential_dofs, element_action);
|
||||
}
|
||||
|
||||
local_to_true(*m_fem.gravityPotentialFes, local_action, action);
|
||||
}
|
||||
|
||||
void PreparedMappedGravitySourceOperator::MultTranspose(
|
||||
const mfem::Vector &potential_true,
|
||||
mfem::Vector &action
|
||||
) const {
|
||||
MFEM_VERIFY(
|
||||
m_is_prepared,
|
||||
"PreparedMappedGravitySourceOperator must be prepared before "
|
||||
"MultTranspose is called."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
potential_true.Size() == Height(),
|
||||
"PreparedMappedGravitySourceOperator received a potential vector "
|
||||
"with the wrong size."
|
||||
);
|
||||
|
||||
mfem::Vector potential_local;
|
||||
|
||||
true_to_local(
|
||||
*m_fem.gravityPotentialFes, potential_true, potential_local
|
||||
);
|
||||
|
||||
mfem::Vector local_action(m_fem.densityFes->GetVSize());
|
||||
local_action = 0.0;
|
||||
|
||||
mfem::Vector element_potential;
|
||||
mfem::Vector quadrature_potential;
|
||||
mfem::Vector element_action;
|
||||
|
||||
for (const ElementPAData &data : m_elements) {
|
||||
potential_local.GetSubVector(
|
||||
data.potential_dofs, element_potential
|
||||
);
|
||||
|
||||
if (data.potential_dof_transformation != nullptr) {
|
||||
data.potential_dof_transformation->InvTransformPrimal(
|
||||
element_potential
|
||||
);
|
||||
}
|
||||
|
||||
quadrature_potential.SetSize(data.quadrature_data.Size());
|
||||
|
||||
data.potential_basis.Mult(element_potential, quadrature_potential);
|
||||
|
||||
for (int q = 0; q < quadrature_potential.Size(); ++q) {
|
||||
quadrature_potential(q) *= data.quadrature_data(q);
|
||||
}
|
||||
|
||||
element_action.SetSize(data.density_dofs.Size());
|
||||
|
||||
data.density_basis.MultTranspose(
|
||||
quadrature_potential, element_action
|
||||
);
|
||||
|
||||
if (data.density_dof_transformation != nullptr) {
|
||||
data.density_dof_transformation->TransformDual(element_action);
|
||||
}
|
||||
|
||||
local_action.AddElementVector(data.density_dofs, element_action);
|
||||
}
|
||||
|
||||
local_to_true(*m_fem.densityFes, local_action, action);
|
||||
}
|
||||
bool PreparedMappedGravitySourceOperator::IsPrepared() const noexcept {
|
||||
return m_is_prepared;
|
||||
}
|
||||
|
||||
std::uint64_t
|
||||
PreparedMappedGravitySourceOperator::GetPreparationCount() const noexcept {
|
||||
return m_preparation_count;
|
||||
}
|
||||
} // namespace mean_field::operators
|
||||
441
libmeanfield/impl/operators/prepared_hdiv_mass.cpp
Normal file
441
libmeanfield/impl/operators/prepared_hdiv_mass.cpp
Normal file
@@ -0,0 +1,441 @@
|
||||
module;
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <mfem.hpp>
|
||||
|
||||
module mean_field;
|
||||
import :operators.prepared_hdiv_mass;
|
||||
|
||||
namespace {
|
||||
int get_operator_size(const mean_field::fem::FEM &f) {
|
||||
MFEM_VERIFY(
|
||||
f.gravityFluxFes != nullptr,
|
||||
"PreparedMappedHDivMassOperator requires the "
|
||||
"gravity-gradient finite-element space."
|
||||
);
|
||||
return f.gravityFluxFes->GetTrueVSize();
|
||||
}
|
||||
|
||||
void true_to_local(
|
||||
const mfem::ParFiniteElementSpace &finite_element_space,
|
||||
const mfem::Vector &true_vector,
|
||||
mfem::Vector &local_vector
|
||||
) {
|
||||
local_vector.SetSize(finite_element_space.GetVSize());
|
||||
|
||||
const mfem::Operator *prolongation =
|
||||
finite_element_space.GetProlongationMatrix();
|
||||
|
||||
if (prolongation != nullptr) {
|
||||
prolongation->Mult(true_vector, local_vector);
|
||||
} else {
|
||||
local_vector = true_vector;
|
||||
}
|
||||
}
|
||||
|
||||
int find_representative_element(
|
||||
const mean_field::fem::FEM &f,
|
||||
const mfem::Array<int> &marker
|
||||
) {
|
||||
for (int element_id = 0; element_id < f.mesh->GetNE(); ++element_id) {
|
||||
const int attribute = f.mesh->GetAttribute(element_id);
|
||||
|
||||
if (attribute > 0 && attribute <= marker.Size() &&
|
||||
marker[attribute - 1] != 0) {
|
||||
return element_id;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
void validate_uniform_domain_discretization(
|
||||
const mean_field::fem::FEM &f,
|
||||
const mfem::Array<int> &marker,
|
||||
const int representative_element_id
|
||||
) {
|
||||
const mfem::FiniteElement &representative_element =
|
||||
*f.gravityFluxFes->GetFE(representative_element_id);
|
||||
const mfem::ElementTransformation &representative_transformation =
|
||||
*f.mesh->GetElementTransformation(representative_element_id);
|
||||
|
||||
for (int element_id = 0; element_id < f.mesh->GetNE(); ++element_id) {
|
||||
const int attribute = f.mesh->GetAttribute(element_id);
|
||||
|
||||
if (attribute <= 0 || attribute > marker.Size() ||
|
||||
marker[attribute - 1] == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const mfem::FiniteElement &element =
|
||||
*f.gravityFluxFes->GetFE(element_id);
|
||||
const mfem::ElementTransformation &transformation =
|
||||
*f.mesh->GetElementTransformation(element_id);
|
||||
|
||||
MFEM_VERIFY(
|
||||
element.GetGeomType() == representative_element.GetGeomType(),
|
||||
"Prepared H(div) mass domains currently require a uniform "
|
||||
"element "
|
||||
"geometry."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
element.GetOrder() == representative_element.GetOrder(),
|
||||
"Prepared H(div) mass domains currently require a uniform "
|
||||
"finite-element order."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
transformation.OrderW() ==
|
||||
representative_transformation.OrderW(),
|
||||
"Prepared H(div) mass domains currently require a uniform "
|
||||
"geometry-weight order."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class FrozenMappedHDivMassCoefficient final
|
||||
: public mfem::MatrixCoefficient {
|
||||
public:
|
||||
FrozenMappedHDivMassCoefficient(
|
||||
const mean_field::fem::FEM &f,
|
||||
const mean_field::mapping::DomainMapperStateless &domain_mapper,
|
||||
const mfem::Vector &displacement_true,
|
||||
bool elevates_vacuum
|
||||
)
|
||||
: MatrixCoefficient(domain_mapper.GetDimension()),
|
||||
m_fem(f),
|
||||
m_domain_mapper(domain_mapper),
|
||||
m_workspace(domain_mapper.GetDimension()),
|
||||
m_elevates_vacuum(elevates_vacuum) {
|
||||
true_to_local(
|
||||
*m_fem.displacementFes, displacement_true, m_displacement_local
|
||||
);
|
||||
}
|
||||
|
||||
void Eval(
|
||||
mfem::DenseMatrix &mass_tensor,
|
||||
mfem::ElementTransformation &transformation,
|
||||
const mfem::IntegrationPoint &integration_point
|
||||
) override {
|
||||
transformation.SetIntPoint(&integration_point);
|
||||
|
||||
const int element_id = transformation.ElementNo;
|
||||
MFEM_VERIFY(
|
||||
element_id >= 0 && element_id < m_fem.mesh->GetNE(),
|
||||
"Mapped H(div) mass coefficient received an invalid element ID."
|
||||
);
|
||||
|
||||
const bool element_is_vacuum =
|
||||
transformation.Attribute ==
|
||||
m_domain_mapper.GetVacuumElementAttribute();
|
||||
|
||||
if (element_is_vacuum != m_elevates_vacuum) {
|
||||
mass_tensor.SetSize(m_domain_mapper.GetDimension());
|
||||
mass_tensor = 0.0;
|
||||
return;
|
||||
}
|
||||
|
||||
LoadElement(element_id);
|
||||
|
||||
const mean_field::mapping::ElementMappingData mapping_data{
|
||||
.displacement = *m_displacement_data,
|
||||
.compactification = *m_compactification_data
|
||||
};
|
||||
|
||||
mean_field::mapping::VolumeMappingContext mapping_context;
|
||||
|
||||
const mean_field::mapping::MappingStatus status =
|
||||
m_domain_mapper.EvaluateVolume(
|
||||
mapping_data, transformation, integration_point,
|
||||
m_workspace, mapping_context
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
status == mean_field::mapping::MappingStatus::valid,
|
||||
"Stateless domain mapping failed while preparing the H(div) "
|
||||
"mass "
|
||||
"operator. Mapping status = "
|
||||
<< static_cast<int>(status)
|
||||
<< ", element ID = " << element_id
|
||||
<< ", element attribute = " << transformation.Attribute
|
||||
<< ", coefficient domain = "
|
||||
<< (m_elevates_vacuum ? "vacuum" : "stellar")
|
||||
);
|
||||
|
||||
const mfem::DenseMatrix &mapping_jacobian =
|
||||
mapping_context.mapping.mapping_jacobian;
|
||||
const double mapping_determinant =
|
||||
mapping_context.mapping.mapping_determinant;
|
||||
|
||||
MFEM_VERIFY(
|
||||
std::isfinite(mapping_determinant) && mapping_determinant > 0.0,
|
||||
"Prepared H(div) mass operator encountered a non-positive or "
|
||||
"non-finite mapping determinant."
|
||||
);
|
||||
|
||||
mfem::MultAtB(mapping_jacobian, mapping_jacobian, mass_tensor);
|
||||
mass_tensor *= 1.0 / mapping_determinant;
|
||||
}
|
||||
|
||||
private:
|
||||
void LoadElement(const int element_id) {
|
||||
if (element_id == m_cached_element_id) {
|
||||
return;
|
||||
}
|
||||
|
||||
const mfem::FiniteElement &displacement_element =
|
||||
*m_fem.displacementFes->GetFE(element_id);
|
||||
const mfem::FiniteElement &compactification_element =
|
||||
*m_fem.compactificationFes->GetFE(element_id);
|
||||
|
||||
mfem::DofTransformation *displacement_dof_transformation =
|
||||
m_fem.displacementFes->GetElementVDofs(
|
||||
element_id, m_displacement_dofs
|
||||
);
|
||||
mfem::DofTransformation *compactification_dof_transformation =
|
||||
m_fem.compactificationFes->GetElementDofs(
|
||||
element_id, m_compactification_dofs
|
||||
);
|
||||
|
||||
m_displacement_local.GetSubVector(
|
||||
m_displacement_dofs, m_element_displacement
|
||||
);
|
||||
m_fem.compactificationCoordinate->GetSubVector(
|
||||
m_compactification_dofs, m_element_compactification
|
||||
);
|
||||
|
||||
if (displacement_dof_transformation != nullptr) {
|
||||
displacement_dof_transformation->InvTransformPrimal(
|
||||
m_element_displacement
|
||||
);
|
||||
}
|
||||
|
||||
if (compactification_dof_transformation != nullptr) {
|
||||
compactification_dof_transformation->InvTransformPrimal(
|
||||
m_element_compactification
|
||||
);
|
||||
}
|
||||
|
||||
m_displacement_data = std::make_unique<
|
||||
mean_field::mapping::ElementDisplacementData>(
|
||||
mean_field::mapping::ElementDisplacementDataFromElementVDofs(
|
||||
displacement_element, m_element_displacement
|
||||
)
|
||||
);
|
||||
|
||||
m_compactification_data = std::make_unique<
|
||||
mean_field::mapping::ElementCompactificationData>(
|
||||
compactification_element, m_element_compactification
|
||||
);
|
||||
|
||||
m_cached_element_id = element_id;
|
||||
}
|
||||
|
||||
const mean_field::fem::FEM &m_fem;
|
||||
const mean_field::mapping::DomainMapperStateless &m_domain_mapper;
|
||||
|
||||
mfem::Vector m_displacement_local;
|
||||
|
||||
mfem::Array<int> m_displacement_dofs;
|
||||
mfem::Array<int> m_compactification_dofs;
|
||||
|
||||
mfem::Vector m_element_displacement;
|
||||
mfem::Vector m_element_compactification;
|
||||
|
||||
std::unique_ptr<mean_field::mapping::ElementDisplacementData>
|
||||
m_displacement_data;
|
||||
std::unique_ptr<mean_field::mapping::ElementCompactificationData>
|
||||
m_compactification_data;
|
||||
|
||||
mean_field::mapping::DomainMapperStateless::Workspace m_workspace;
|
||||
int m_cached_element_id{-1};
|
||||
bool m_elevates_vacuum;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
namespace mean_field::operators {
|
||||
PreparedMappedHDivMassOperator::PreparedMappedHDivMassOperator(
|
||||
const fem::FEM &f,
|
||||
const mapping::DomainMapperStateless &domain_mapper
|
||||
)
|
||||
: Operator(get_operator_size(f)),
|
||||
m_fem(f),
|
||||
m_domain_mapper(domain_mapper) {
|
||||
MFEM_VERIFY(
|
||||
f.mesh != nullptr, "PreparedMappedHDivMassOperator requires a mesh."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.gravityFluxFes != nullptr,
|
||||
"PreparedMappedHDivMassOperator requires the "
|
||||
"gravity-gradient finite-element space."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.displacementFes != nullptr,
|
||||
"PreparedMappedHDivMassOperator requires the "
|
||||
"displacement finite-element space."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.compactificationFes != nullptr,
|
||||
"PreparedMappedHDivMassOperator requires the compactification "
|
||||
"finite-element space."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.compactificationCoordinate != nullptr,
|
||||
"PreparedMappedHDivMassOperator requires the compactification "
|
||||
"coordinate."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.quadratureFactory != nullptr,
|
||||
"PreparedMappedHDivMassOperator requires the quadrature-rule "
|
||||
"factory."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
domain_mapper.GetDimension() == f.mesh->Dimension(),
|
||||
"The stateless domain-mapper dimension does not match the mesh "
|
||||
"dimension."
|
||||
);
|
||||
|
||||
utils::populate_element_mask(
|
||||
f.mesh.get(), utils::DOMAINS::STELLAR, m_stellar_marker
|
||||
);
|
||||
utils::populate_element_mask(
|
||||
f.mesh.get(), utils::DOMAINS::VACUUM, m_vacuum_marker
|
||||
);
|
||||
|
||||
const int stellar_element_id =
|
||||
find_representative_element(f, m_stellar_marker);
|
||||
const int vacuum_element_id =
|
||||
find_representative_element(f, m_vacuum_marker);
|
||||
|
||||
MFEM_VERIFY(
|
||||
stellar_element_id >= 0, "PreparedMappedHDivMassOperator requires "
|
||||
"at least one stellar element."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
vacuum_element_id >= 0,
|
||||
"PreparedMappedHDivMassOperator requires at "
|
||||
"least one compactified vacuum element."
|
||||
);
|
||||
|
||||
validate_uniform_domain_discretization(
|
||||
f, m_stellar_marker, stellar_element_id
|
||||
);
|
||||
validate_uniform_domain_discretization(
|
||||
f, m_vacuum_marker, vacuum_element_id
|
||||
);
|
||||
}
|
||||
|
||||
void PreparedMappedHDivMassOperator::Prepare(
|
||||
const mfem::Vector &displacement_true
|
||||
) {
|
||||
MFEM_VERIFY(
|
||||
displacement_true.Size() == m_fem.displacementFes->GetTrueVSize(),
|
||||
"PreparedMappedHDivMassOperator received a displacement vector "
|
||||
"with "
|
||||
"the wrong size."
|
||||
);
|
||||
|
||||
for (int i = 0; i < displacement_true.Size(); ++i) {
|
||||
MFEM_VERIFY(
|
||||
std::isfinite(displacement_true(i)),
|
||||
"PreparedMappedHDivMassOperator received a non-finite "
|
||||
"displacement "
|
||||
"value."
|
||||
);
|
||||
}
|
||||
|
||||
const int stellar_element_id =
|
||||
find_representative_element(m_fem, m_stellar_marker);
|
||||
const int vacuum_element_id =
|
||||
find_representative_element(m_fem, m_vacuum_marker);
|
||||
|
||||
const mfem::FiniteElement &stellar_element =
|
||||
*m_fem.gravityFluxFes->GetFE(stellar_element_id);
|
||||
const mfem::FiniteElement &vacuum_element =
|
||||
*m_fem.gravityFluxFes->GetFE(vacuum_element_id);
|
||||
|
||||
mfem::ElementTransformation &stellar_transformation =
|
||||
*m_fem.mesh->GetElementTransformation(stellar_element_id);
|
||||
mfem::ElementTransformation &vacuum_transformation =
|
||||
*m_fem.mesh->GetElementTransformation(vacuum_element_id);
|
||||
|
||||
m_mass_form.reset();
|
||||
m_stellar_mass_coefficient.reset();
|
||||
m_vacuum_mass_coefficient.reset();
|
||||
|
||||
m_stellar_mass_coefficient =
|
||||
std::make_unique<FrozenMappedHDivMassCoefficient>(
|
||||
m_fem, m_domain_mapper, displacement_true, false
|
||||
);
|
||||
m_vacuum_mass_coefficient =
|
||||
std::make_unique<FrozenMappedHDivMassCoefficient>(
|
||||
m_fem, m_domain_mapper, displacement_true, true
|
||||
);
|
||||
|
||||
m_mass_form =
|
||||
std::make_unique<mfem::ParBilinearForm>(m_fem.gravityFluxFes.get());
|
||||
m_mass_form->SetAssemblyLevel(mfem::AssemblyLevel::PARTIAL);
|
||||
|
||||
auto stellar_integrator =
|
||||
std::make_unique<mfem::VectorFEMassIntegrator>(
|
||||
*m_stellar_mass_coefficient
|
||||
);
|
||||
auto vacuum_integrator = std::make_unique<mfem::VectorFEMassIntegrator>(
|
||||
*m_vacuum_mass_coefficient
|
||||
);
|
||||
|
||||
m_fem.quadratureFactory->configure_gravity_hdiv_mass(
|
||||
*stellar_integrator, quadrature::QuadratureRole::discretization,
|
||||
stellar_element, stellar_transformation, utils::DOMAINS::STELLAR,
|
||||
quadrature::MappingKind::general
|
||||
);
|
||||
|
||||
m_fem.quadratureFactory->configure_gravity_hdiv_mass(
|
||||
*vacuum_integrator, quadrature::QuadratureRole::discretization,
|
||||
vacuum_element, vacuum_transformation, utils::DOMAINS::VACUUM,
|
||||
quadrature::MappingKind::kelvin
|
||||
);
|
||||
|
||||
m_mass_form->AddDomainIntegrator(
|
||||
stellar_integrator.release(), m_stellar_marker
|
||||
);
|
||||
m_mass_form->AddDomainIntegrator(
|
||||
vacuum_integrator.release(), m_vacuum_marker
|
||||
);
|
||||
m_mass_form->Assemble();
|
||||
|
||||
m_is_prepared = true;
|
||||
++m_preparation_count;
|
||||
}
|
||||
|
||||
void PreparedMappedHDivMassOperator::Mult(
|
||||
const mfem::Vector &gravity_gradient_true,
|
||||
mfem::Vector &action
|
||||
) const {
|
||||
MFEM_VERIFY(
|
||||
m_is_prepared, "PreparedMappedHDivMassOperator must be prepared "
|
||||
"before Mult is called."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
m_mass_form != nullptr, "PreparedMappedHDivMassOperator has no "
|
||||
"assembled partial-assembly form."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
gravity_gradient_true.Size() == Width(),
|
||||
"PreparedMappedHDivMassOperator received a gravity-gradient vector "
|
||||
"with the wrong size."
|
||||
);
|
||||
|
||||
action.SetSize(Height());
|
||||
m_mass_form->Mult(gravity_gradient_true, action);
|
||||
}
|
||||
|
||||
bool PreparedMappedHDivMassOperator::IsPrepared() const noexcept {
|
||||
return m_is_prepared;
|
||||
}
|
||||
|
||||
std::uint64_t
|
||||
PreparedMappedHDivMassOperator::GetPreparationCount() const noexcept {
|
||||
return m_preparation_count;
|
||||
}
|
||||
} // namespace mean_field::operators
|
||||
1432
libmeanfield/impl/operators/prepared_hydrostatic_equilibrium.cpp
Normal file
1432
libmeanfield/impl/operators/prepared_hydrostatic_equilibrium.cpp
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user