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; int get_state_width(const mfem::Array &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 make_gravity_residual_offsets(const fem::FEM &f) { mfem::Array 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 get_state_block_size( const mfem::Array &state_true_offsets, const utils::blocks::value_block ) { 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 &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
( 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_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 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::DomainMapperStateless &domain_mapper, context::gravity_field::GravityFieldLinearizationContext &linearization_context, const mfem::Array &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( 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_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 & GravityFieldOperator::GetStateTrueOffsets() const noexcept { return m_state_true_offsets; } const mfem::Array & 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( 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() == 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( 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( 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_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( 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.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(gravity_gradient_block) + 1] - state_offsets[gravity_gradient_block]; const int full_potential_size = state_offsets[static_cast(gravity_potential_block) + 1] - state_offsets[gravity_potential_block]; const int reduced_gradient_size = m_gravity_true_offsets [static_cast(gravity_gradient_residual_block) + 1] - m_gravity_true_offsets[gravity_gradient_residual_block]; const int reduced_potential_size = m_gravity_true_offsets [static_cast(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::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( 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_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 & 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( utils::blocks::displacement_field.geometry_term ); const mfem::Array &state_offsets = m_gravity_field_operator.GetStateTrueOffsets(); 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.GetStateTrueOffsets(); 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." ); } } // namespace mean_field::operators