module; #include "profile.h" #include #include #include #include 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 &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(*f.gravityFluxFes).reduced_size() + field::make_field_dof_map(*f.gravityPotentialFes).reduced_size(); } mfem::Array make_gravity_residual_offsets(const fem::FEM &f) { mfem::Array 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(*f.gravityFluxFes).reduced_size(); offsets[2] = offsets[1] + field::make_field_dof_map(*f.gravityPotentialFes).reduced_size(); return offsets; } template int get_state_block_size( const mfem::Array &state_offsets, const utils::blocks::value_block ) { 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 &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
(utils::blocks::density_field.mass_term); constexpr auto displacement_block = utils::blocks::get_value_block(utils::blocks::displacement_field.geometry_term); constexpr auto gravity_gradient_block = utils::blocks::get_value_block(utils::blocks::gravity_field.gradient_term); constexpr auto gravity_potential_block = utils::blocks::get_value_block(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(*f.densityFes); const auto displacement_map = field::make_field_dof_map(*f.displacementFes); const auto flux_map = field::make_field_dof_map(*f.gravityFluxFes); const auto potential_map = field::make_field_dof_map(*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 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(); 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 divergence_matrix(divergence.ParallelAssemble()); std::unique_ptr inverse_mass_divergence_transpose(divergence_matrix->Transpose()); inverse_mass_divergence_transpose->ScaleRows(inverse_mass_diagonal); return std::unique_ptr( mfem::ParMult(divergence_matrix.get(), inverse_mass_divergence_transpose.get()) ); } template mfem::Vector make_read_only_value_view( const mfem::Vector &vector, const mfem::Array &offsets, const utils::blocks::value_block ) { 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(vector.GetData()) + begin, size); } template mfem::Vector make_read_only_residual_view( const mfem::Vector &vector, const mfem::Array &offsets, const utils::blocks::residual_block 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(vector), begin, size); return view; } template mfem::Vector make_residual_view( mfem::Vector &vector, const mfem::Array &offsets, const utils::blocks::residual_block ) { 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 &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(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(utils::blocks::density_field.mass_term); constexpr auto displacement_block = utils::blocks::get_value_block(utils::blocks::displacement_field.geometry_term); constexpr auto gravity_gradient_block = utils::blocks::get_value_block(utils::blocks::gravity_field.gradient_term); constexpr auto gravity_potential_block = utils::blocks::get_value_block(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 &GravityFieldOperator::GetStateOffsets() const noexcept { return m_state_offsets; } const mfem::Array &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(utils::blocks::gravity_field.gradient_term); constexpr auto gravity_poisson_residual_block = utils::blocks::get_residual_block(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(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(utils::blocks::density_field.mass_term); constexpr auto gravity_gradient_block = utils::blocks::get_value_block(utils::blocks::gravity_field.gradient_term); constexpr auto gravity_potential_block = utils::blocks::get_value_block(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(utils::blocks::gravity_field.gradient_term); constexpr auto gravity_potential_block = utils::blocks::get_value_block(utils::blocks::gravity_field.poisson_term); constexpr auto gravity_gradient_residual_block = utils::blocks::get_residual_block(utils::blocks::gravity_field.gradient_term); constexpr auto gravity_poisson_residual_block = utils::blocks::get_residual_block(utils::blocks::gravity_field.poisson_term); const mfem::Array &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(gravity_gradient_block) + 1] - state_offsets[gravity_gradient_block]; const int state_potential_size = state_offsets[static_cast(gravity_potential_block) + 1] - state_offsets[gravity_potential_block]; const int gravity_gradient_size = m_gravity_offsets[static_cast(gravity_gradient_residual_block) + 1] - m_gravity_offsets[gravity_gradient_residual_block]; const int gravity_potential_size = m_gravity_offsets[static_cast(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::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(utils::blocks::gravity_field.gradient_term); constexpr auto gravity_poisson_residual_block = utils::blocks::get_residual_block(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 &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(utils::blocks::displacement_field.geometry_term); const mfem::Array &state_offsets = m_gravity_field_operator.GetStateOffsets(); const int expected_size = state_offsets[static_cast(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(utils::blocks::density_field.mass_term); const mfem::Array &state_offsets = m_gravity_field_operator.GetStateOffsets(); const int expected_size = state_offsets[static_cast(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(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(); 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(right_hand_side), m_offsets[0], m_offsets[1] - m_offsets[0]); mfem::Vector potential_rhs; potential_rhs.MakeRef(const_cast(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 &ReducedGravityFieldPreconditioner::GetOffsets() const noexcept { return m_offsets; } } // namespace mean_field::operators