466 lines
19 KiB
C++
466 lines
19 KiB
C++
module;
|
|
#include <cmath>
|
|
#include <memory>
|
|
#include <mfem.hpp>
|
|
|
|
module mean_field;
|
|
import :operators.context.gravity_field;
|
|
|
|
namespace {
|
|
using DomainSchema = mean_field::utils::domain::CoreEnvelopeVacuumDomainSchema;
|
|
|
|
[[nodiscard]] std::unique_ptr<mfem::ParMixedBilinearForm> make_divergence_operator(const mean_field::fem::FEM &f) {
|
|
auto divergence =
|
|
std::make_unique<mfem::ParMixedBilinearForm>(f.gravityFluxFes.get(), f.gravityPotentialFes.get());
|
|
|
|
divergence->SetAssemblyLevel(mfem::AssemblyLevel::PARTIAL);
|
|
|
|
auto integrator = std::make_unique<mfem::VectorFEDivergenceIntegrator>();
|
|
|
|
const mfem::FiniteElement &trialElement = *f.gravityFluxFes->GetTypicalFE();
|
|
const mfem::FiniteElement &testElement = *f.gravityPotentialFes->GetTypicalFE();
|
|
const mfem::ElementTransformation &transformation = *f.mesh->GetElementTransformation(0);
|
|
|
|
f.quadratureFactory->configure_gravity_divergence(
|
|
*integrator, mean_field::quadrature::QuadratureRole::discretization, trialElement, testElement,
|
|
transformation, mean_field::utils::DOMAINS::ALL, mean_field::quadrature::MappingKind::none
|
|
);
|
|
|
|
divergence->AddDomainIntegrator(integrator.release());
|
|
divergence->Assemble();
|
|
|
|
return divergence;
|
|
}
|
|
|
|
void validate_displacement(
|
|
const mean_field::field::FieldDofMap &displacement_map,
|
|
const mfem::Vector &displacement
|
|
) {
|
|
MFEM_VERIFY(
|
|
displacement.Size() == displacement_map.reduced_size(),
|
|
"GravityFieldGeometryContext received a displacement vector with "
|
|
"the "
|
|
"wrong size."
|
|
);
|
|
|
|
for (int i = 0; i < displacement.Size(); ++i) {
|
|
MFEM_VERIFY(
|
|
std::isfinite(displacement(i)), "GravityFieldGeometryContext received a non-finite "
|
|
"displacement "
|
|
"value."
|
|
);
|
|
}
|
|
}
|
|
|
|
void validate_linearization_state(
|
|
const mean_field::field::FieldDofMap &density_map,
|
|
const mean_field::field::FieldDofMap &displacement_map,
|
|
const mean_field::field::FieldDofMap &gravity_gradient_map,
|
|
const mean_field::field::FieldDofMap &gravity_potential_map,
|
|
const mean_field::operators::context::gravity_field::GravityFieldStateView &state
|
|
) {
|
|
MFEM_VERIFY(
|
|
state.density.Size() == density_map.reduced_size(),
|
|
"GravityFieldLinearizationContext received a density vector with "
|
|
"the "
|
|
"wrong size."
|
|
);
|
|
MFEM_VERIFY(
|
|
state.displacement.Size() == displacement_map.reduced_size(),
|
|
"GravityFieldLinearizationContext received a displacement vector "
|
|
"with "
|
|
"the wrong size."
|
|
);
|
|
MFEM_VERIFY(
|
|
state.gravity_gradient.Size() == gravity_gradient_map.reduced_size(),
|
|
"GravityFieldLinearizationContext received a gravity-gradient "
|
|
"vector "
|
|
"with the wrong size."
|
|
);
|
|
MFEM_VERIFY(
|
|
state.gravity_potential.Size() == gravity_potential_map.reduced_size(),
|
|
"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::DomainMapper &domain_mapper
|
|
)
|
|
: m_fem(f),
|
|
m_domain_mapper(domain_mapper),
|
|
m_displacement_map(
|
|
field::make_field_dof_map<
|
|
field::Displacement,
|
|
DomainSchema>(*f.displacementFes)
|
|
) {
|
|
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,
|
|
const DiscretizationRevision discretization_revision,
|
|
const DisplacementRevision displacement_revision
|
|
) {
|
|
validate_displacement(m_displacement_map, displacement);
|
|
|
|
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);
|
|
auto divergence_operator = make_divergence_operator(m_fem);
|
|
auto transpose_divergence_operator = std::make_unique<mfem::TransposeOperator>(divergence_operator.get());
|
|
|
|
mass_operator->Prepare(displacement);
|
|
source_operator->Prepare(displacement);
|
|
|
|
m_mass_operator = std::move(mass_operator);
|
|
m_source_operator = std::move(source_operator);
|
|
m_divergence_operator = std::move(divergence_operator);
|
|
m_transpose_divergence_operator = std::move(transpose_divergence_operator);
|
|
|
|
preparation.reconstructed_operators = true;
|
|
preparation.rebuilt_mass_operator = true;
|
|
preparation.rebuilt_source_operator = true;
|
|
preparation.rebuilt_divergence_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);
|
|
m_source_operator->Prepare(displacement);
|
|
|
|
preparation.rebuilt_mass_operator = true;
|
|
preparation.rebuilt_source_operator = true;
|
|
}
|
|
|
|
m_displacement_true.SetSize(m_displacement_map.full_size());
|
|
m_displacement_map.scatter(displacement, m_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::Operator &GravityFieldGeometryContext::GetDivergenceOperator() const {
|
|
MFEM_VERIFY(m_is_prepared, "GravityFieldGeometryContext must be prepared before accessing divergence.");
|
|
MFEM_VERIFY(m_divergence_operator != nullptr, "GravityFieldGeometryContext has no divergence operator.");
|
|
return *m_divergence_operator;
|
|
}
|
|
|
|
const mfem::Operator &GravityFieldGeometryContext::GetTransposeDivergenceOperator() const {
|
|
MFEM_VERIFY(
|
|
m_is_prepared, "GravityFieldGeometryContext must be prepared before accessing transpose divergence."
|
|
);
|
|
MFEM_VERIFY(
|
|
m_transpose_divergence_operator != nullptr,
|
|
"GravityFieldGeometryContext has no transpose-divergence operator."
|
|
);
|
|
return *m_transpose_divergence_operator;
|
|
}
|
|
|
|
const mfem::Vector &GravityFieldGeometryContext::GetDisplacementTrue() const {
|
|
MFEM_VERIFY(
|
|
m_is_prepared, "GravityFieldGeometryContext must be prepared before "
|
|
"accessing its displacement."
|
|
);
|
|
return m_displacement_true;
|
|
}
|
|
|
|
const field::FieldDofMap &GravityFieldGeometryContext::GetDisplacementMap() const noexcept {
|
|
return m_displacement_map;
|
|
}
|
|
|
|
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::DomainMapper &domain_mapper
|
|
)
|
|
: m_fem(f),
|
|
m_geometry_context(
|
|
f,
|
|
domain_mapper
|
|
),
|
|
m_density_map(
|
|
field::make_field_dof_map<
|
|
field::Density,
|
|
DomainSchema>(*f.densityFes)
|
|
),
|
|
m_gravity_gradient_map(
|
|
field::make_field_dof_map<
|
|
field::Gravity,
|
|
DomainSchema>(*f.gravityFluxFes)
|
|
),
|
|
m_gravity_potential_map(
|
|
field::make_field_dof_map<
|
|
field::Gravity,
|
|
DomainSchema>(*f.gravityPotentialFes)
|
|
) {
|
|
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_density_map, m_geometry_context.GetDisplacementMap(), m_gravity_gradient_map, m_gravity_potential_map,
|
|
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.SetSize(m_density_map.full_size());
|
|
m_density_map.scatter(state.density, m_density_true);
|
|
report.updated_density = true;
|
|
}
|
|
|
|
if (gravity_gradient_changed) {
|
|
m_gravity_gradient_true.SetSize(m_gravity_gradient_map.full_size());
|
|
m_gravity_gradient_map.scatter(state.gravity_gradient, m_gravity_gradient_true);
|
|
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::GetDensityTrue() const {
|
|
MFEM_VERIFY(
|
|
m_is_prepared, "GravityFieldLinearizationContext must be prepared "
|
|
"before accessing its density."
|
|
);
|
|
return m_density_true;
|
|
}
|
|
|
|
const mfem::Vector &GravityFieldLinearizationContext::GetGravityGradientTrue() const {
|
|
MFEM_VERIFY(
|
|
m_is_prepared, "GravityFieldLinearizationContext must be prepared "
|
|
"before accessing its gravity gradient."
|
|
);
|
|
return m_gravity_gradient_true;
|
|
}
|
|
|
|
const field::FieldDofMap &GravityFieldLinearizationContext::GetDensityMap() const noexcept {
|
|
return m_density_map;
|
|
}
|
|
|
|
const field::FieldDofMap &GravityFieldLinearizationContext::GetDisplacementMap() const noexcept {
|
|
return m_geometry_context.GetDisplacementMap();
|
|
}
|
|
|
|
const field::FieldDofMap &GravityFieldLinearizationContext::GetGravityGradientMap() const noexcept {
|
|
return m_gravity_gradient_map;
|
|
}
|
|
|
|
const field::FieldDofMap &GravityFieldLinearizationContext::GetGravityPotentialMap() const noexcept {
|
|
return m_gravity_potential_map;
|
|
}
|
|
|
|
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
|