SpECTRE Documentation Coverage Report
Current view: top level - ParallelAlgorithms/LinearSolver/Schwarz - ElementActions.hpp Hit Total Coverage
Commit: a6a8ee404306bec9d92da8ab89f636b037aefc25 Lines: 0 1 0.0 %
Date: 2024-07-26 22:35:59
Legend: Lines: hit not hit

          Line data    Source code
       1           0 : // Distributed under the MIT License.
       2             : // See LICENSE.txt for details.
       3             : 
       4             : #pragma once
       5             : 
       6             : #include <cstddef>
       7             : #include <map>
       8             : #include <optional>
       9             : #include <string>
      10             : #include <tuple>
      11             : #include <unordered_map>
      12             : #include <utility>
      13             : #include <vector>
      14             : 
      15             : #include "DataStructures/DataBox/DataBox.hpp"
      16             : #include "DataStructures/DataBox/PrefixHelpers.hpp"
      17             : #include "DataStructures/DataBox/Tag.hpp"
      18             : #include "DataStructures/Index.hpp"
      19             : #include "Domain/Structure/Direction.hpp"
      20             : #include "Domain/Structure/DirectionMap.hpp"
      21             : #include "Domain/Structure/Element.hpp"
      22             : #include "Domain/Structure/ElementId.hpp"
      23             : #include "Domain/Structure/OrientationMapHelpers.hpp"
      24             : #include "Domain/Tags.hpp"
      25             : #include "Domain/Tags/Faces.hpp"
      26             : #include "IO/Logging/Tags.hpp"
      27             : #include "IO/Logging/Verbosity.hpp"
      28             : #include "IO/Observer/Actions/RegisterWithObservers.hpp"
      29             : #include "IO/Observer/GetSectionObservationKey.hpp"
      30             : #include "IO/Observer/ObservationId.hpp"
      31             : #include "IO/Observer/Protocols/ReductionDataFormatter.hpp"
      32             : #include "IO/Observer/ReductionActions.hpp"
      33             : #include "IO/Observer/Tags.hpp"
      34             : #include "IO/Observer/TypeOfObservation.hpp"
      35             : #include "NumericalAlgorithms/Convergence/Tags.hpp"
      36             : #include "NumericalAlgorithms/DiscontinuousGalerkin/HasReceivedFromAllMortars.hpp"
      37             : #include "NumericalAlgorithms/LinearSolver/ExplicitInverse.hpp"
      38             : #include "NumericalAlgorithms/LinearSolver/Gmres.hpp"
      39             : #include "NumericalAlgorithms/Spectral/Mesh.hpp"
      40             : #include "NumericalAlgorithms/Spectral/Spectral.hpp"
      41             : #include "Parallel/AlgorithmExecution.hpp"
      42             : #include "Parallel/ArrayComponentId.hpp"
      43             : #include "Parallel/GlobalCache.hpp"
      44             : #include "Parallel/InboxInserters.hpp"
      45             : #include "Parallel/Invoke.hpp"
      46             : #include "Parallel/Local.hpp"
      47             : #include "Parallel/ParallelComponentHelpers.hpp"
      48             : #include "Parallel/Printf/Printf.hpp"
      49             : #include "Parallel/Reduction.hpp"
      50             : #include "ParallelAlgorithms/Amr/Protocols/Projector.hpp"
      51             : #include "ParallelAlgorithms/Initialization/MutateAssign.hpp"
      52             : #include "ParallelAlgorithms/LinearSolver/AsynchronousSolvers/ElementActions.hpp"
      53             : #include "ParallelAlgorithms/LinearSolver/Schwarz/Actions/CommunicateOverlapFields.hpp"
      54             : #include "ParallelAlgorithms/LinearSolver/Schwarz/ElementCenteredSubdomainData.hpp"
      55             : #include "ParallelAlgorithms/LinearSolver/Schwarz/OverlapHelpers.hpp"
      56             : #include "ParallelAlgorithms/LinearSolver/Schwarz/Tags.hpp"
      57             : #include "ParallelAlgorithms/LinearSolver/Schwarz/Weighting.hpp"
      58             : #include "ParallelAlgorithms/LinearSolver/Tags.hpp"
      59             : #include "Utilities/Gsl.hpp"
      60             : #include "Utilities/PrettyType.hpp"
      61             : #include "Utilities/ProtocolHelpers.hpp"
      62             : #include "Utilities/SetNumberOfGridPoints.hpp"
      63             : #include "Utilities/TMPL.hpp"
      64             : #include "Utilities/TaggedTuple.hpp"
      65             : #include "Utilities/TypeTraits/IsA.hpp"
      66             : 
      67             : namespace LinearSolver::Schwarz::detail {
      68             : 
      69             : using reduction_data = Parallel::ReductionData<
      70             :     // Iteration
      71             :     Parallel::ReductionDatum<size_t, funcl::AssertEqual<>>,
      72             :     // Number of subdomains (= number of elements)
      73             :     Parallel::ReductionDatum<size_t, funcl::Plus<>>,
      74             :     // Average number of subdomain solver iterations
      75             :     Parallel::ReductionDatum<double, funcl::Plus<>, funcl::Divides<>,
      76             :                              std::index_sequence<1>>,
      77             :     // Minimum number of subdomain solver iterations
      78             :     Parallel::ReductionDatum<size_t, funcl::Min<>>,
      79             :     // Maximum number of subdomain solver iterations
      80             :     Parallel::ReductionDatum<size_t, funcl::Max<>>,
      81             :     // Total number of subdomain solver iterations
      82             :     Parallel::ReductionDatum<size_t, funcl::Plus<>>>;
      83             : 
      84             : template <typename OptionsGroup>
      85             : struct SubdomainStatsFormatter
      86             :     : tt::ConformsTo<observers::protocols::ReductionDataFormatter> {
      87             :   using reduction_data = Schwarz::detail::reduction_data;
      88             :   SubdomainStatsFormatter() = default;
      89             :   SubdomainStatsFormatter(std::string local_section_observation_key)
      90             :       : section_observation_key(std::move(local_section_observation_key)) {}
      91             :   std::string operator()(const size_t iteration_id, const size_t num_subdomains,
      92             :                          const double avg_subdomain_its,
      93             :                          const size_t min_subdomain_its,
      94             :                          const size_t max_subdomain_its,
      95             :                          const size_t total_subdomain_its) const {
      96             :     return pretty_type::name<OptionsGroup>() + section_observation_key + "(" +
      97             :            get_output(iteration_id) + ") completed all " +
      98             :            get_output(num_subdomains) +
      99             :            " subdomain solves. Average of number of iterations: " +
     100             :            get_output(avg_subdomain_its) + " (min " +
     101             :            get_output(min_subdomain_its) + ", max " +
     102             :            get_output(max_subdomain_its) + ", total " +
     103             :            get_output(total_subdomain_its) + ").";
     104             :   }
     105             :   // NOLINTNEXTLINE(google-runtime-references)
     106             :   void pup(PUP::er& p) { p | section_observation_key; }
     107             :   std::string section_observation_key{};
     108             : };
     109             : 
     110             : template <typename OptionsGroup, typename ArraySectionIdTag>
     111             : struct RegisterObservers {
     112             :   template <typename ParallelComponent, typename DbTagsList,
     113             :             typename ArrayIndex>
     114             :   static std::pair<observers::TypeOfObservation, observers::ObservationKey>
     115             :   register_info(const db::DataBox<DbTagsList>& box,
     116             :                 const ArrayIndex& /*array_index*/) {
     117             :     // Get the observation key, or "Unused" if the element does not belong
     118             :     // to a section with this tag. In the latter case, no observations will
     119             :     // ever be contributed.
     120             :     const std::optional<std::string> section_observation_key =
     121             :         observers::get_section_observation_key<ArraySectionIdTag>(box);
     122             :     ASSERT(section_observation_key != "Unused",
     123             :            "The identifier 'Unused' is reserved to indicate that no "
     124             :            "observations with this key will be contributed. Use a different "
     125             :            "key, or change the identifier 'Unused' to something else.");
     126             :     return {
     127             :         observers::TypeOfObservation::Reduction,
     128             :         observers::ObservationKey{pretty_type::get_name<OptionsGroup>() +
     129             :                                   section_observation_key.value_or("Unused") +
     130             :                                   "SubdomainSolves"}};
     131             :   }
     132             : };
     133             : 
     134             : template <typename FieldsTag, typename OptionsGroup, typename SourceTag,
     135             :           typename ArraySectionIdTag>
     136             : using RegisterElement = observers::Actions::RegisterWithObservers<
     137             :     RegisterObservers<OptionsGroup, ArraySectionIdTag>>;
     138             : 
     139             : template <typename OptionsGroup, typename ParallelComponent,
     140             :           typename Metavariables, typename ArrayIndex>
     141             : void contribute_to_subdomain_stats_observation(
     142             :     const size_t iteration_id, const size_t subdomain_solve_num_iterations,
     143             :     Parallel::GlobalCache<Metavariables>& cache, const ArrayIndex& array_index,
     144             :     const std::string& section_observation_key, const bool observe_per_core) {
     145             :   auto& local_observer = *Parallel::local_branch(
     146             :       Parallel::get_parallel_component<observers::Observer<Metavariables>>(
     147             :           cache));
     148             : 
     149             :   auto formatter =
     150             :       UNLIKELY(get<logging::Tags::Verbosity<OptionsGroup>>(cache) >=
     151             :                ::Verbosity::Verbose)
     152             :           ? std::make_optional(
     153             :                 SubdomainStatsFormatter<OptionsGroup>{section_observation_key})
     154             :           : std::nullopt;
     155             :   Parallel::simple_action<observers::Actions::ContributeReductionData>(
     156             :       local_observer,
     157             :       observers::ObservationId(iteration_id,
     158             :                                pretty_type::get_name<OptionsGroup>() +
     159             :                                    section_observation_key + "SubdomainSolves"),
     160             :       Parallel::make_array_component_id<ParallelComponent>(array_index),
     161             :       std::string{"/" + pretty_type::name<OptionsGroup>() +
     162             :                   section_observation_key + "SubdomainSolves"},
     163             :       std::vector<std::string>{"Iteration", "NumSubdomains", "AvgNumIterations",
     164             :                                "MinNumIterations", "MaxNumIterations",
     165             :                                "TotalNumIterations"},
     166             :       reduction_data{
     167             :           iteration_id, 1, static_cast<double>(subdomain_solve_num_iterations),
     168             :           subdomain_solve_num_iterations, subdomain_solve_num_iterations,
     169             :           subdomain_solve_num_iterations},
     170             :       std::move(formatter), observe_per_core);
     171             : }
     172             : 
     173             : template <typename SubdomainDataType, typename OptionsGroup>
     174             : struct SubdomainDataBufferTag : db::SimpleTag {
     175             :   static std::string name() {
     176             :     return "SubdomainData(" + pretty_type::name<OptionsGroup>() + ")";
     177             :   }
     178             :   using type = SubdomainDataType;
     179             : };
     180             : 
     181             : // Allow factory-creating any of these serial linear solvers for use as
     182             : // subdomain solver
     183             : template <typename FieldsTag, typename SubdomainOperator,
     184             :           typename SubdomainPreconditioners,
     185             :           typename SubdomainData = ElementCenteredSubdomainData<
     186             :               SubdomainOperator::volume_dim,
     187             :               typename db::add_tag_prefix<LinearSolver::Tags::Residual,
     188             :                                           FieldsTag>::tags_list>>
     189             : using subdomain_solver = LinearSolver::Serial::LinearSolver<tmpl::append<
     190             :     tmpl::list<::LinearSolver::Serial::Registrars::Gmres<SubdomainData>,
     191             :                ::LinearSolver::Serial::Registrars::ExplicitInverse<
     192             :                    typename SubdomainData::value_type>>,
     193             :     SubdomainPreconditioners>>;
     194             : 
     195             : template <typename FieldsTag, typename OptionsGroup, typename SubdomainOperator,
     196             :           typename SubdomainPreconditioners>
     197             : struct InitializeElement : tt::ConformsTo<amr::protocols::Projector> {
     198             :  private:
     199             :   using fields_tag = FieldsTag;
     200             :   using residual_tag =
     201             :       db::add_tag_prefix<LinearSolver::Tags::Residual, fields_tag>;
     202             :   static constexpr size_t Dim = SubdomainOperator::volume_dim;
     203             :   using SubdomainData =
     204             :       ElementCenteredSubdomainData<Dim, typename residual_tag::tags_list>;
     205             :   using SubdomainSolver =
     206             :       subdomain_solver<FieldsTag, SubdomainOperator, SubdomainPreconditioners>;
     207             :   using subdomain_solver_tag =
     208             :       Tags::SubdomainSolver<std::unique_ptr<SubdomainSolver>, OptionsGroup>;
     209             : 
     210             :  public:  // Iterable action
     211             :   using simple_tags_from_options = tmpl::list<subdomain_solver_tag>;
     212             : 
     213             :   using const_global_cache_tags = tmpl::list<Tags::MaxOverlap<OptionsGroup>>;
     214             : 
     215             :   using simple_tags =
     216             :       tmpl::list<Tags::IntrudingExtents<Dim, OptionsGroup>,
     217             :                  Tags::Weight<OptionsGroup>,
     218             :                  domain::Tags::Faces<Dim, Tags::Weight<OptionsGroup>>,
     219             :                  SubdomainDataBufferTag<SubdomainData, OptionsGroup>>;
     220             :   using compute_tags = tmpl::list<>;
     221             :   template <typename DbTagsList, typename... InboxTags, typename Metavariables,
     222             :             typename ActionList, typename ParallelComponent>
     223             :   static Parallel::iterable_action_return_t apply(
     224             :       db::DataBox<DbTagsList>& box,
     225             :       const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,
     226             :       const Parallel::GlobalCache<Metavariables>& /*cache*/,
     227             :       const ElementId<Dim>& /*element_id*/, const ActionList /*meta*/,
     228             :       const ParallelComponent* const /*meta*/) {
     229             :     db::mutate_apply<InitializeElement>(make_not_null(&box));
     230             :     return {Parallel::AlgorithmExecution::Continue, std::nullopt};
     231             :   }
     232             : 
     233             :  public:  // amr::protocols::Projector
     234             :   using return_tags = tmpl::append<simple_tags, simple_tags_from_options>;
     235             :   using argument_tags =
     236             :       tmpl::list<domain::Tags::Element<Dim>, domain::Tags::Mesh<Dim>,
     237             :                  domain::Tags::Coordinates<Dim, Frame::ElementLogical>,
     238             :                  Tags::MaxOverlap<OptionsGroup>>;
     239             : 
     240             :   template <typename... AmrData>
     241             :   static void apply(
     242             :       const gsl::not_null<std::array<size_t, Dim>*> intruding_extents,
     243             :       const gsl::not_null<Scalar<DataVector>*> element_weight,
     244             :       const gsl::not_null<DirectionMap<Dim, Scalar<DataVector>>*>
     245             :           intruding_overlap_weights,
     246             :       const gsl::not_null<SubdomainData*> subdomain_data,
     247             :       [[maybe_unused]] const gsl::not_null<std::unique_ptr<SubdomainSolver>*>
     248             :           subdomain_solver,
     249             :       const Element<Dim>& element, const Mesh<Dim>& mesh,
     250             :       const tnsr::I<DataVector, Dim, Frame::ElementLogical>& logical_coords,
     251             :       const size_t max_overlap, const AmrData&... amr_data) {
     252             :     const size_t num_points = mesh.number_of_grid_points();
     253             : 
     254             :     // Intruding overlaps
     255             :     std::array<double, Dim> intruding_overlap_widths{};
     256             :     for (size_t d = 0; d < Dim; ++d) {
     257             :       gsl::at(*intruding_extents, d) =
     258             :           LinearSolver::Schwarz::overlap_extent(mesh.extents(d), max_overlap);
     259             :       const auto& collocation_points =
     260             :           Spectral::collocation_points(mesh.slice_through(d));
     261             :       gsl::at(intruding_overlap_widths, d) =
     262             :           LinearSolver::Schwarz::overlap_width(gsl::at(*intruding_extents, d),
     263             :                                                collocation_points);
     264             :     }
     265             : 
     266             :     // Element weight
     267             :     // For max_overlap > 0 all overlaps will have non-zero extents on an LGL
     268             :     // mesh (because it has at least 2 points per dimension), so we don't need
     269             :     // to check their extents are non-zero individually
     270             :     if (LIKELY(max_overlap > 0)) {
     271             :       LinearSolver::Schwarz::element_weight(element_weight, logical_coords,
     272             :                                             intruding_overlap_widths,
     273             :                                             element.external_boundaries());
     274             :     } else {
     275             :       set_number_of_grid_points(element_weight, num_points);
     276             :       get(*element_weight) = 1.;
     277             :     }
     278             : 
     279             :     // Intruding overlap weights
     280             :     intruding_overlap_weights->clear();
     281             :     for (const auto& direction : element.internal_boundaries()) {
     282             :       const size_t dim = direction.dimension();
     283             :       if (gsl::at(*intruding_extents, dim) > 0) {
     284             :         const auto intruding_logical_coords =
     285             :             LinearSolver::Schwarz::data_on_overlap(
     286             :                 logical_coords, mesh.extents(),
     287             :                 gsl::at(*intruding_extents, dim), direction);
     288             :         (*intruding_overlap_weights)[direction] =
     289             :             LinearSolver::Schwarz::intruding_weight(
     290             :                 intruding_logical_coords, direction, intruding_overlap_widths,
     291             :                 element.neighbors().at(direction).size(),
     292             :                 element.external_boundaries());
     293             :       }
     294             :     }
     295             : 
     296             :     // Subdomain data buffer
     297             :     *subdomain_data = SubdomainData{num_points};
     298             : 
     299             :     // Subdomain solver
     300             :     // The subdomain solver initially gets created from options on each element.
     301             :     // Then we have to copy it around during AMR.
     302             :     if constexpr (sizeof...(AmrData) == 1) {
     303             :       if constexpr (tt::is_a_v<tuples::TaggedTuple, AmrData...>) {
     304             :         // h-refinement: copy from the parent
     305             :         *subdomain_solver = get<subdomain_solver_tag>(amr_data...)->get_clone();
     306             :       } else if constexpr (tt::is_a_v<std::unordered_map, AmrData...>) {
     307             :         // h-coarsening, copy from one of the children (doesn't matter which)
     308             :         *subdomain_solver =
     309             :             get<subdomain_solver_tag>(amr_data.begin()->second...)->get_clone();
     310             :       }
     311             :       (*subdomain_solver)->reset();
     312             :     }
     313             :   }
     314             : };
     315             : 
     316             : // Restrict the residual to neighboring subdomains that overlap with this
     317             : // element and send the data to those elements
     318             : template <typename FieldsTag, typename OptionsGroup, typename SubdomainOperator>
     319             : using SendOverlapData = LinearSolver::Schwarz::Actions::SendOverlapFields<
     320             :     tmpl::list<db::add_tag_prefix<LinearSolver::Tags::Residual, FieldsTag>>,
     321             :     OptionsGroup, true>;
     322             : 
     323             : template <size_t Dim, typename OptionsGroup, typename OverlapSolution>
     324             : struct OverlapSolutionInboxTag
     325             :     : public Parallel::InboxInserters::Map<
     326             :           OverlapSolutionInboxTag<Dim, OptionsGroup, OverlapSolution>> {
     327             :   using temporal_id = size_t;
     328             :   using type = std::map<temporal_id, OverlapMap<Dim, OverlapSolution>>;
     329             : };
     330             : 
     331             : // Wait for the residual data on regions of this element's subdomain that
     332             : // overlap with other elements. Once the residual data is available on all
     333             : // overlaps, solve the restricted problem for this element-centered subdomain.
     334             : // Apply the weighted solution on this element directly and send the solution on
     335             : // overlap regions to the neighbors that they overlap with.
     336             : template <typename FieldsTag, typename OptionsGroup, typename SubdomainOperator,
     337             :           typename ArraySectionIdTag>
     338             : struct SolveSubdomain {
     339             :  private:
     340             :   using fields_tag = FieldsTag;
     341             :   using residual_tag =
     342             :       db::add_tag_prefix<LinearSolver::Tags::Residual, fields_tag>;
     343             :   static constexpr size_t Dim = SubdomainOperator::volume_dim;
     344             :   using overlap_residuals_inbox_tag =
     345             :       Actions::detail::OverlapFieldsTag<Dim, tmpl::list<residual_tag>,
     346             :                                         OptionsGroup>;
     347             :   using SubdomainData =
     348             :       ElementCenteredSubdomainData<Dim, typename residual_tag::tags_list>;
     349             :   using OverlapData = typename SubdomainData::OverlapData;
     350             :   using overlap_solution_inbox_tag =
     351             :       OverlapSolutionInboxTag<Dim, OptionsGroup, OverlapData>;
     352             : 
     353             :  public:
     354             :   using const_global_cache_tags =
     355             :       tmpl::list<Tags::MaxOverlap<OptionsGroup>,
     356             :                  logging::Tags::Verbosity<OptionsGroup>,
     357             :                  Tags::ObservePerCoreReductions<OptionsGroup>>;
     358             :   using inbox_tags = tmpl::list<overlap_residuals_inbox_tag>;
     359             : 
     360             :   template <typename DbTagsList, typename... InboxTags, typename Metavariables,
     361             :             typename ActionList, typename ParallelComponent>
     362             :   static Parallel::iterable_action_return_t apply(
     363             :       db::DataBox<DbTagsList>& box, tuples::TaggedTuple<InboxTags...>& inboxes,
     364             :       Parallel::GlobalCache<Metavariables>& cache,
     365             :       const ElementId<Dim>& element_id, const ActionList /*meta*/,
     366             :       const ParallelComponent* const /*meta*/) {
     367             :     const size_t iteration_id =
     368             :         get<Convergence::Tags::IterationId<OptionsGroup>>(box);
     369             :     const auto& element = db::get<domain::Tags::Element<Dim>>(box);
     370             :     const size_t max_overlap = db::get<Tags::MaxOverlap<OptionsGroup>>(box);
     371             : 
     372             :     // Wait for communicated overlap data
     373             :     const bool has_overlap_data =
     374             :         max_overlap > 0 and element.number_of_neighbors() > 0;
     375             :     if (LIKELY(has_overlap_data) and
     376             :         not dg::has_received_from_all_mortars<overlap_residuals_inbox_tag>(
     377             :             iteration_id, element, inboxes)) {
     378             :       return {Parallel::AlgorithmExecution::Retry, std::nullopt};
     379             :     }
     380             : 
     381             :     // Do some logging
     382             :     if (UNLIKELY(get<logging::Tags::Verbosity<OptionsGroup>>(box) >=
     383             :                  ::Verbosity::Debug)) {
     384             :       Parallel::printf("%s %s(%zu): Solve subdomain\n", element_id,
     385             :                        pretty_type::name<OptionsGroup>(), iteration_id);
     386             :     }
     387             : 
     388             :     // Assemble the subdomain data from the data on the element and the
     389             :     // communicated overlap data
     390             :     db::mutate<SubdomainDataBufferTag<SubdomainData, OptionsGroup>>(
     391             :         [&inboxes, &iteration_id, &has_overlap_data](
     392             :             const gsl::not_null<SubdomainData*> subdomain_data,
     393             :             const auto& residual) {
     394             :           subdomain_data->element_data = residual;
     395             :           // Nothing was communicated if the overlaps are empty
     396             :           if (LIKELY(has_overlap_data)) {
     397             :             subdomain_data->overlap_data =
     398             :                 std::move(tuples::get<overlap_residuals_inbox_tag>(inboxes)
     399             :                               .extract(iteration_id)
     400             :                               .mapped());
     401             :           }
     402             :         },
     403             :         make_not_null(&box), db::get<residual_tag>(box));
     404             :     const auto& subdomain_residual =
     405             :         db::get<SubdomainDataBufferTag<SubdomainData, OptionsGroup>>(box);
     406             : 
     407             :     // Allocate workspace memory for repeatedly applying the subdomain operator
     408             :     const SubdomainOperator subdomain_operator{};
     409             : 
     410             :     // Solve the subdomain problem
     411             :     const auto& subdomain_solver =
     412             :         get<Tags::SubdomainSolverBase<OptionsGroup>>(box);
     413             :     auto subdomain_solve_initial_guess_in_solution_out =
     414             :         make_with_value<SubdomainData>(subdomain_residual, 0.);
     415             :     const auto subdomain_solve_has_converged = subdomain_solver.solve(
     416             :         make_not_null(&subdomain_solve_initial_guess_in_solution_out),
     417             :         subdomain_operator, subdomain_residual, std::forward_as_tuple(box));
     418             :     // Re-naming the solution buffer for the code below
     419             :     auto& subdomain_solution = subdomain_solve_initial_guess_in_solution_out;
     420             : 
     421             :     // Do some logging and observing
     422             :     if (UNLIKELY(get<logging::Tags::Verbosity<OptionsGroup>>(box) >=
     423             :                  ::Verbosity::Quiet)) {
     424             :       if (not subdomain_solve_has_converged or
     425             :           subdomain_solve_has_converged.reason() ==
     426             :               Convergence::Reason::MaxIterations) {
     427             :         Parallel::printf(
     428             :             "%s %s(%zu): WARNING: Subdomain solver did not converge in %zu "
     429             :             "iterations: %e -> %e\n",
     430             :             element_id, pretty_type::name<OptionsGroup>(), iteration_id,
     431             :             subdomain_solve_has_converged.num_iterations(),
     432             :             subdomain_solve_has_converged.initial_residual_magnitude(),
     433             :             subdomain_solve_has_converged.residual_magnitude());
     434             :       } else if (UNLIKELY(get<logging::Tags::Verbosity<OptionsGroup>>(box) >=
     435             :                           ::Verbosity::Debug)) {
     436             :         Parallel::printf(
     437             :             "%s %s(%zu): Subdomain solver converged in %zu iterations (%s): %e "
     438             :             "-> %e\n",
     439             :             element_id, pretty_type::name<OptionsGroup>(), iteration_id,
     440             :             subdomain_solve_has_converged.num_iterations(),
     441             :             subdomain_solve_has_converged.reason(),
     442             :             subdomain_solve_has_converged.initial_residual_magnitude(),
     443             :             subdomain_solve_has_converged.residual_magnitude());
     444             :       }
     445             :     }
     446             :     const std::optional<std::string> section_observation_key =
     447             :         observers::get_section_observation_key<ArraySectionIdTag>(box);
     448             :     if (section_observation_key.has_value()) {
     449             :       contribute_to_subdomain_stats_observation<OptionsGroup,
     450             :                                                 ParallelComponent>(
     451             :           iteration_id + 1, subdomain_solve_has_converged.num_iterations(),
     452             :           cache, element_id, *section_observation_key,
     453             :           db::get<Tags::ObservePerCoreReductions<OptionsGroup>>(box));
     454             :     }
     455             : 
     456             :     // Apply weighting
     457             :     if (LIKELY(max_overlap > 0)) {
     458             :       subdomain_solution.element_data *=
     459             :           get(db::get<Tags::Weight<OptionsGroup>>(box));
     460             :     }
     461             : 
     462             :     // Apply solution to central element
     463             :     db::mutate<fields_tag>(
     464             :         [&subdomain_solution](const auto fields) {
     465             :           *fields += subdomain_solution.element_data;
     466             :         },
     467             :         make_not_null(&box));
     468             : 
     469             :     // Send overlap solutions back to the neighbors that they are on
     470             :     if (LIKELY(max_overlap > 0)) {
     471             :       auto& receiver_proxy =
     472             :           Parallel::get_parallel_component<ParallelComponent>(cache);
     473             :       for (auto& [overlap_id, overlap_solution] :
     474             :            subdomain_solution.overlap_data) {
     475             :         const auto& direction = overlap_id.direction();
     476             :         const auto& neighbor_id = overlap_id.id();
     477             :         const auto& orientation =
     478             :             element.neighbors().at(direction).orientation();
     479             :         const auto direction_from_neighbor = orientation(direction.opposite());
     480             :         Parallel::receive_data<overlap_solution_inbox_tag>(
     481             :             receiver_proxy[neighbor_id], iteration_id,
     482             :             std::make_pair(
     483             :                 OverlapId<Dim>{direction_from_neighbor, element.id()},
     484             :                 std::move(overlap_solution)));
     485             :       }
     486             :     }
     487             :     return {Parallel::AlgorithmExecution::Continue, std::nullopt};
     488             :   }
     489             : };
     490             : 
     491             : // Wait for the subdomain solutions on regions within this element that overlap
     492             : // with neighboring element-centered subdomains. Combine the solutions as a
     493             : // weighted sum.
     494             : template <typename FieldsTag, typename OptionsGroup, typename SubdomainOperator>
     495             : struct ReceiveOverlapSolution {
     496             :  private:
     497             :   using fields_tag = FieldsTag;
     498             :   using residual_tag =
     499             :       db::add_tag_prefix<LinearSolver::Tags::Residual, fields_tag>;
     500             :   static constexpr size_t Dim = SubdomainOperator::volume_dim;
     501             :   using SubdomainData =
     502             :       ElementCenteredSubdomainData<Dim, typename residual_tag::tags_list>;
     503             :   using OverlapSolution = typename SubdomainData::OverlapData;
     504             :   using overlap_solution_inbox_tag =
     505             :       OverlapSolutionInboxTag<Dim, OptionsGroup, OverlapSolution>;
     506             : 
     507             :  public:
     508             :   using const_global_cache_tags = tmpl::list<Tags::MaxOverlap<OptionsGroup>>;
     509             :   using inbox_tags = tmpl::list<overlap_solution_inbox_tag>;
     510             : 
     511             :   template <typename DbTagsList, typename... InboxTags, typename Metavariables,
     512             :             typename ActionList, typename ParallelComponent>
     513             :   static Parallel::iterable_action_return_t apply(
     514             :       db::DataBox<DbTagsList>& box, tuples::TaggedTuple<InboxTags...>& inboxes,
     515             :       const Parallel::GlobalCache<Metavariables>& /*cache*/,
     516             :       const ElementId<Dim>& element_id, const ActionList /*meta*/,
     517             :       const ParallelComponent* const /*meta*/) {
     518             :     const size_t iteration_id =
     519             :         get<Convergence::Tags::IterationId<OptionsGroup>>(box);
     520             :     const auto& element = db::get<domain::Tags::Element<Dim>>(box);
     521             : 
     522             :     // Nothing to do if overlap is empty
     523             :     if (UNLIKELY(db::get<Tags::MaxOverlap<OptionsGroup>>(box) == 0 or
     524             :                  element.number_of_neighbors() == 0)) {
     525             :       return {Parallel::AlgorithmExecution::Continue, std::nullopt};
     526             :     }
     527             : 
     528             :     if (not dg::has_received_from_all_mortars<overlap_solution_inbox_tag>(
     529             :             iteration_id, element, inboxes)) {
     530             :       return {Parallel::AlgorithmExecution::Retry, std::nullopt};
     531             :     }
     532             : 
     533             :     // Do some logging
     534             :     if (UNLIKELY(get<logging::Tags::Verbosity<OptionsGroup>>(box) >=
     535             :                  ::Verbosity::Debug)) {
     536             :       Parallel::printf("%s %s(%zu): Receive overlap solution\n", element_id,
     537             :                        pretty_type::name<OptionsGroup>(), iteration_id);
     538             :     }
     539             : 
     540             :     // Add solutions on overlaps to this element's solution in a weighted sum
     541             :     const auto received_overlap_solutions =
     542             :         std::move(tuples::get<overlap_solution_inbox_tag>(inboxes)
     543             :                       .extract(iteration_id)
     544             :                       .mapped());
     545             :     db::mutate<fields_tag>(
     546             :         [&received_overlap_solutions](
     547             :             const auto fields, const Index<Dim>& full_extents,
     548             :             const std::array<size_t, Dim>& all_intruding_extents,
     549             :             const DirectionMap<Dim, Scalar<DataVector>>&
     550             :                 all_intruding_overlap_weights) {
     551             :           for (const auto& [overlap_id, overlap_solution] :
     552             :                received_overlap_solutions) {
     553             :             const auto& direction = overlap_id.direction();
     554             :             const auto& intruding_extents =
     555             :                 gsl::at(all_intruding_extents, direction.dimension());
     556             :             const auto& overlap_weight =
     557             :                 all_intruding_overlap_weights.at(direction);
     558             :             LinearSolver::Schwarz::add_overlap_data(
     559             :                 fields, overlap_solution * get(overlap_weight), full_extents,
     560             :                 intruding_extents, direction);
     561             :           }
     562             :         },
     563             :         make_not_null(&box), db::get<domain::Tags::Mesh<Dim>>(box).extents(),
     564             :         db::get<Tags::IntrudingExtents<Dim, OptionsGroup>>(box),
     565             :         db::get<domain::Tags::Faces<Dim, Tags::Weight<OptionsGroup>>>(box));
     566             :     return {Parallel::AlgorithmExecution::Continue, std::nullopt};
     567             :   }
     568             : };
     569             : 
     570             : }  // namespace LinearSolver::Schwarz::detail

Generated by: LCOV version 1.14