761 lines
31 KiB
C++
761 lines
31 KiB
C++
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;
|
|
using DomainSchema = utils::domain::CoreEnvelopeVacuumDomainSchema;
|
|
int get_state_width(const mfem::Array<int> &state_offsets) {
|
|
MFEM_VERIFY(state_offsets.Size() >= 2,
|
|
"The coupled state requires at least one block.");
|
|
MFEM_VERIFY(state_offsets[0] == 0,
|
|
"The coupled state offsets must begin at zero.");
|
|
|
|
for (int i = 0; i < state_offsets.Size() - 1; ++i) {
|
|
MFEM_VERIFY(state_offsets[i + 1] >= state_offsets[i],
|
|
"The coupled state offsets must be nondecreasing.");
|
|
}
|
|
|
|
MFEM_VERIFY(state_offsets.Last() > 0, "The coupled state cannot be empty.");
|
|
return state_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).");
|
|
using DomainSchema = utils::domain::CoreEnvelopeVacuumDomainSchema;
|
|
return field::make_field_dof_map<field::Gravity, DomainSchema>(
|
|
*f.gravityFluxFes)
|
|
.reduced_size() +
|
|
field::make_field_dof_map<field::Gravity, DomainSchema>(
|
|
*f.gravityPotentialFes)
|
|
.reduced_size();
|
|
}
|
|
|
|
mfem::Array<int> make_gravity_residual_offsets(const fem::FEM &f) {
|
|
mfem::Array<int> offsets(
|
|
utils::blocks::gravity_field_form::residual_block_count + 1);
|
|
offsets[0] = 0;
|
|
using DomainSchema = utils::domain::CoreEnvelopeVacuumDomainSchema;
|
|
offsets[1] =
|
|
field::make_field_dof_map<field::Gravity, DomainSchema>(*f.gravityFluxFes)
|
|
.reduced_size();
|
|
offsets[2] =
|
|
offsets[1] + field::make_field_dof_map<field::Gravity, DomainSchema>(
|
|
*f.gravityPotentialFes)
|
|
.reduced_size();
|
|
return offsets;
|
|
}
|
|
|
|
template <int index>
|
|
int get_state_block_size(const mfem::Array<int> &state_offsets,
|
|
const utils::blocks::value_block<index>) {
|
|
MFEM_VERIFY(index + 1 < state_offsets.Size(),
|
|
"Value block is not present in the state offsets.");
|
|
return state_offsets[index + 1] - state_offsets[index];
|
|
}
|
|
|
|
void validate_state_offsets(const fem::FEM &f,
|
|
const mfem::Array<int> &state_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_offsets.Size() == form::value_block_count + 1,
|
|
"The gravity state offsets do not match gravity_field_form.");
|
|
using DomainSchema = utils::domain::CoreEnvelopeVacuumDomainSchema;
|
|
const auto density_map =
|
|
field::make_field_dof_map<field::Density, DomainSchema>(*f.densityFes);
|
|
const auto displacement_map =
|
|
field::make_field_dof_map<field::Displacement, DomainSchema>(
|
|
*f.displacementFes);
|
|
const auto flux_map = field::make_field_dof_map<field::Gravity, DomainSchema>(
|
|
*f.gravityFluxFes);
|
|
const auto potential_map =
|
|
field::make_field_dof_map<field::Gravity, DomainSchema>(
|
|
*f.gravityPotentialFes);
|
|
|
|
MFEM_VERIFY(
|
|
get_state_block_size(state_offsets, density_block) ==
|
|
density_map.reduced_size(),
|
|
"The density block does not match the density finite-element space.");
|
|
MFEM_VERIFY(get_state_block_size(state_offsets, displacement_block) ==
|
|
displacement_map.reduced_size(),
|
|
"The displacement block does not match the displacement "
|
|
"finite-element "
|
|
"space.");
|
|
MFEM_VERIFY(get_state_block_size(state_offsets, gravity_gradient_block) ==
|
|
flux_map.reduced_size(),
|
|
"The gravity-gradient block does not match the RT finite-element "
|
|
"space.");
|
|
MFEM_VERIFY(get_state_block_size(state_offsets, gravity_potential_block) ==
|
|
potential_map.reduced_size(),
|
|
"The gravity-potential block does not match the potential "
|
|
"finite-element space.");
|
|
}
|
|
|
|
void validate_gravity_context(const fem::FEM &f) {
|
|
MFEM_VERIFY(f.quadratureFactory != nullptr,
|
|
"GravityFieldOperator requires the quadrature-rule factory.");
|
|
}
|
|
|
|
[[nodiscard]] std::unique_ptr<mfem::HypreParMatrix>
|
|
make_gravity_schur_preconditioner(const fem::FEM &f,
|
|
const mfem::Vector &mass_diagonal) {
|
|
MFEM_VERIFY(mass_diagonal.Size() == f.gravityFluxFes->GetTrueVSize(),
|
|
"The gravity Schur preconditioner requires a full "
|
|
"gravity-gradient mass diagonal.");
|
|
|
|
mfem::Vector inverse_mass_diagonal(mass_diagonal);
|
|
|
|
for (int i = 0; i < inverse_mass_diagonal.Size(); ++i) {
|
|
MFEM_VERIFY(std::isfinite(inverse_mass_diagonal(i)) &&
|
|
inverse_mass_diagonal(i) > 0.0,
|
|
"The gravity Schur preconditioner encountered a non-positive "
|
|
"or non-finite mass diagonal.");
|
|
inverse_mass_diagonal(i) = 1.0 / inverse_mass_diagonal(i);
|
|
}
|
|
|
|
mfem::ParMixedBilinearForm divergence(f.gravityFluxFes.get(),
|
|
f.gravityPotentialFes.get());
|
|
auto integrator = std::make_unique<mfem::VectorFEDivergenceIntegrator>();
|
|
|
|
const mfem::FiniteElement &trial_element = *f.gravityFluxFes->GetTypicalFE();
|
|
const mfem::FiniteElement &test_element =
|
|
*f.gravityPotentialFes->GetTypicalFE();
|
|
const mfem::ElementTransformation &transformation =
|
|
*f.mesh->GetElementTransformation(0);
|
|
|
|
f.quadratureFactory->configure_gravity_divergence(
|
|
*integrator, quadrature::QuadratureRole::preconditioner, trial_element,
|
|
test_element, transformation, utils::DOMAINS::ALL,
|
|
quadrature::MappingKind::none);
|
|
|
|
divergence.AddDomainIntegrator(integrator.release());
|
|
divergence.Assemble();
|
|
divergence.Finalize();
|
|
|
|
std::unique_ptr<mfem::HypreParMatrix> divergence_matrix(
|
|
divergence.ParallelAssemble());
|
|
std::unique_ptr<mfem::HypreParMatrix> inverse_mass_divergence_transpose(
|
|
divergence_matrix->Transpose());
|
|
inverse_mass_divergence_transpose->ScaleRows(inverse_mass_diagonal);
|
|
|
|
return std::unique_ptr<mfem::HypreParMatrix>(mfem::ParMult(
|
|
divergence_matrix.get(), inverse_mass_divergence_transpose.get()));
|
|
}
|
|
|
|
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::DomainMapper &domain_mapper,
|
|
context::gravity_field::GravityFieldLinearizationContext
|
|
&linearization_context,
|
|
const mfem::Array<int> &state_offsets,
|
|
GravityFieldJacobianOperator &jacobian)
|
|
: Operator(get_gravity_residual_height(f), get_state_width(state_offsets)),
|
|
m_fem(f), m_domain_mapper(domain_mapper),
|
|
m_linearization_context(linearization_context),
|
|
m_state_offsets(state_offsets),
|
|
m_residual_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_offsets);
|
|
validate_gravity_context(f);
|
|
|
|
bool has_vacuum_domain = false;
|
|
|
|
for (int i = 0; i < f.mesh->attributes.Size(); ++i) {
|
|
if (DomainSchema::template attribute_belongs_to<utils::domain::Vacuum>(
|
|
f.mesh->attributes[i])) {
|
|
has_vacuum_domain = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
MFEM_VERIFY(has_vacuum_domain,
|
|
"GravityFieldOperator requires a compactified vacuum domain.");
|
|
MFEM_VERIFY(m_residual_offsets.Last() == Height(),
|
|
"The gravity residual offsets do not match the operator height.");
|
|
MFEM_VERIFY(m_state_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_offsets, density_block);
|
|
const mfem::Vector displacement =
|
|
make_read_only_value_view(state, m_state_offsets, displacement_block);
|
|
const mfem::Vector gravity_gradient =
|
|
make_read_only_value_view(state, m_state_offsets, gravity_gradient_block);
|
|
const mfem::Vector gravity_potential = make_read_only_value_view(
|
|
state, m_state_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::GetStateOffsets() const noexcept {
|
|
return m_state_offsets;
|
|
}
|
|
|
|
const mfem::Array<int> &
|
|
GravityFieldOperator::GetResidualOffsets() const noexcept {
|
|
return m_residual_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() ==
|
|
geometry_context.GetMassOperator().GetFluxMap().reduced_size(),
|
|
"GravityFieldOperator received a gravity-gradient vector with the "
|
|
"wrong size.");
|
|
MFEM_VERIFY(
|
|
gravity_potential.Size() ==
|
|
geometry_context.GetSourceOperator().GetPotentialMap().reduced_size(),
|
|
"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_offsets, gravity_gradient_residual_block);
|
|
mfem::Vector gravity_poisson_action = make_residual_view(
|
|
action, m_residual_offsets, gravity_poisson_residual_block);
|
|
const field::FieldDofMap &flux_map =
|
|
geometry_context.GetMassOperator().GetFluxMap();
|
|
const field::FieldDofMap &potential_map =
|
|
geometry_context.GetSourceOperator().GetPotentialMap();
|
|
mfem::Vector potential_true(potential_map.full_size());
|
|
mfem::Vector transpose_divergence_action_true(flux_map.full_size());
|
|
mfem::Vector transpose_divergence_action(flux_map.reduced_size());
|
|
mfem::Vector gradient_true(flux_map.full_size());
|
|
mfem::Vector divergence_action_true(potential_map.full_size());
|
|
|
|
geometry_context.GetMassOperator().Mult(gravity_gradient,
|
|
gravity_gradient_action);
|
|
potential_map.scatter(gravity_potential, potential_true);
|
|
geometry_context.GetTransposeDivergenceOperator().Mult(
|
|
potential_true, transpose_divergence_action_true);
|
|
flux_map.gather(transpose_divergence_action_true,
|
|
transpose_divergence_action);
|
|
gravity_gradient_action += transpose_divergence_action;
|
|
flux_map.scatter(gravity_gradient, gradient_true);
|
|
geometry_context.GetDivergenceOperator().Mult(gradient_true,
|
|
divergence_action_true);
|
|
potential_map.gather(divergence_action_true, 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() ==
|
|
geometry_context.GetSourceOperator().GetDensityMap().reduced_size(),
|
|
"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_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_offsets, density_block);
|
|
const mfem::Vector gravity_gradient =
|
|
make_read_only_value_view(state, m_state_offsets, gravity_gradient_block);
|
|
const mfem::Vector gravity_potential = make_read_only_value_view(
|
|
state, m_state_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_offsets(gravity_field_operator.GetResidualOffsets()),
|
|
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.GetStateOffsets();
|
|
|
|
MFEM_VERIFY(
|
|
state_offsets.Size() == form::value_block_count + 1,
|
|
"ReducedGravityFieldOperator received an invalid coupled-state layout.");
|
|
MFEM_VERIFY(
|
|
m_gravity_offsets.Size() == form::residual_block_count + 1,
|
|
"ReducedGravityFieldOperator received an invalid gravity-residual "
|
|
"layout.");
|
|
MFEM_VERIFY(state_offsets[0] == 0,
|
|
"The coupled-state offsets must begin at zero.");
|
|
MFEM_VERIFY(m_gravity_offsets[0] == 0,
|
|
"The reduced gravity offsets must begin at zero.");
|
|
MFEM_VERIFY(
|
|
state_offsets.Last() == m_gravity_field_operator.Width(),
|
|
"The coupled-state offsets do not match the gravity-field operator "
|
|
"width.");
|
|
MFEM_VERIFY(m_gravity_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 state_gradient_size =
|
|
state_offsets[static_cast<int>(gravity_gradient_block) + 1] -
|
|
state_offsets[gravity_gradient_block];
|
|
const int state_potential_size =
|
|
state_offsets[static_cast<int>(gravity_potential_block) + 1] -
|
|
state_offsets[gravity_potential_block];
|
|
const int gravity_gradient_size =
|
|
m_gravity_offsets[static_cast<int>(gravity_gradient_residual_block) + 1] -
|
|
m_gravity_offsets[gravity_gradient_residual_block];
|
|
const int gravity_potential_size =
|
|
m_gravity_offsets[static_cast<int>(gravity_poisson_residual_block) + 1] -
|
|
m_gravity_offsets[gravity_poisson_residual_block];
|
|
|
|
MFEM_VERIFY(state_gradient_size == gravity_gradient_size,
|
|
"The gravity-gradient block does not match the coupled-state "
|
|
"gravity-gradient block.");
|
|
MFEM_VERIFY(state_potential_size == gravity_potential_size,
|
|
"The 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);
|
|
m_displacement = displacement;
|
|
}
|
|
|
|
const mfem::Vector &ReducedGravityFieldOperator::GetDisplacement() const {
|
|
return m_displacement;
|
|
}
|
|
|
|
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 = make_read_only_residual_view(
|
|
gravity_state, m_gravity_offsets, gravity_gradient_residual_block);
|
|
const mfem::Vector gravity_potential = make_read_only_residual_view(
|
|
gravity_state, m_gravity_offsets, gravity_poisson_residual_block);
|
|
|
|
m_gravity_field_operator.ApplyGravityUnknowns(
|
|
gravity_gradient, gravity_potential, 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::GetGravityOffsets() const noexcept {
|
|
return m_gravity_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.GetStateOffsets();
|
|
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.GetStateOffsets();
|
|
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.");
|
|
}
|
|
|
|
ReducedGravityFieldPreconditioner::ReducedGravityFieldPreconditioner(
|
|
const fem::FEM &f,
|
|
const context::gravity_field::GravityFieldGeometryContext &geometry_context)
|
|
: Solver(geometry_context.GetMassOperator().GetFluxMap().reduced_size() +
|
|
geometry_context.GetSourceOperator()
|
|
.GetPotentialMap()
|
|
.reduced_size()),
|
|
m_flux_map(geometry_context.GetMassOperator().GetFluxMap()),
|
|
m_potential_map(geometry_context.GetSourceOperator().GetPotentialMap()),
|
|
m_offsets(3) {
|
|
MFEM_VERIFY(geometry_context.IsPrepared(),
|
|
"The reduced gravity preconditioner requires prepared geometry.");
|
|
MFEM_VERIFY(f.mesh != nullptr,
|
|
"The reduced gravity preconditioner requires a parallel mesh.");
|
|
MFEM_VERIFY(f.gravityFluxFes != nullptr && f.gravityPotentialFes != nullptr,
|
|
"The reduced gravity preconditioner requires both gravity "
|
|
"finite-element spaces.");
|
|
MFEM_VERIFY(f.quadratureFactory != nullptr,
|
|
"The reduced gravity preconditioner requires the quadrature-rule "
|
|
"factory.");
|
|
|
|
m_offsets[0] = 0;
|
|
m_offsets[1] = m_flux_map.reduced_size();
|
|
m_offsets[2] = m_offsets[1] + m_potential_map.reduced_size();
|
|
|
|
MFEM_VERIFY(m_offsets.Last() == Height(),
|
|
"The reduced gravity preconditioner has inconsistent offsets.");
|
|
|
|
mfem::Vector reduced_mass_diagonal;
|
|
geometry_context.GetMassOperator().AssembleDiagonal(reduced_mass_diagonal);
|
|
m_mass_preconditioner = std::make_unique<mfem::OperatorJacobiSmoother>(
|
|
reduced_mass_diagonal, m_empty_tdofs);
|
|
|
|
mfem::Vector true_mass_diagonal;
|
|
geometry_context.GetMassOperator().AssembleTrueDiagonal(true_mass_diagonal);
|
|
m_schur = make_gravity_schur_preconditioner(f, true_mass_diagonal);
|
|
|
|
m_potential_preconditioner = std::make_unique<mfem::HypreBoomerAMG>();
|
|
m_potential_preconditioner->SetPrintLevel(0);
|
|
m_potential_preconditioner->SetOperator(*m_schur);
|
|
}
|
|
|
|
void ReducedGravityFieldPreconditioner::SetOperator(
|
|
const mfem::Operator &gravity_operator) {
|
|
MFEM_VERIFY(gravity_operator.Width() == Width() &&
|
|
gravity_operator.Height() == Height(),
|
|
"The reduced gravity preconditioner received an operator with "
|
|
"incompatible dimensions.");
|
|
}
|
|
|
|
void ReducedGravityFieldPreconditioner::Mult(
|
|
const mfem::Vector &right_hand_side, mfem::Vector &action) const {
|
|
MFEM_VERIFY(right_hand_side.Size() == Width(),
|
|
"The reduced gravity preconditioner received a right-hand side "
|
|
"with the wrong size.");
|
|
|
|
mfem::Vector gradient_rhs;
|
|
gradient_rhs.MakeRef(const_cast<mfem::Vector &>(right_hand_side),
|
|
m_offsets[0], m_offsets[1] - m_offsets[0]);
|
|
mfem::Vector potential_rhs;
|
|
potential_rhs.MakeRef(const_cast<mfem::Vector &>(right_hand_side),
|
|
m_offsets[1], m_offsets[2] - m_offsets[1]);
|
|
|
|
action.SetSize(Height());
|
|
mfem::Vector gradient_action;
|
|
gradient_action.MakeRef(action, m_offsets[0], m_offsets[1] - m_offsets[0]);
|
|
mfem::Vector potential_action;
|
|
potential_action.MakeRef(action, m_offsets[1], m_offsets[2] - m_offsets[1]);
|
|
|
|
m_mass_preconditioner->Mult(gradient_rhs, gradient_action);
|
|
|
|
m_potential_rhs_true.SetSize(m_potential_map.full_size());
|
|
m_potential_action_true.SetSize(m_potential_map.full_size());
|
|
m_potential_map.scatter(potential_rhs, m_potential_rhs_true);
|
|
m_potential_preconditioner->Mult(m_potential_rhs_true,
|
|
m_potential_action_true);
|
|
m_potential_map.gather(m_potential_action_true, potential_action);
|
|
}
|
|
|
|
const mfem::Array<int> &
|
|
ReducedGravityFieldPreconditioner::GetOffsets() const noexcept {
|
|
return m_offsets;
|
|
}
|
|
} // namespace mean_field::operators
|