SpECTRE Documentation Coverage Report
Current view: top level - ParallelAlgorithms/LinearSolver/Schwarz - ElementActions.hpp Coverage Total Hit
Commit: 0d1d717af84a81fb1284a20c4013708fe85cc2f0 Lines: 0.0 % 1 0
Test Date: 2026-10-22 17:29:42
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 "DataStructures/TaggedTuple.hpp"
      20              : #include "Domain/Structure/Direction.hpp"
      21              : #include "Domain/Structure/DirectionMap.hpp"
      22              : #include "Domain/Structure/Element.hpp"
      23              : #include "Domain/Structure/ElementId.hpp"
      24              : #include "Domain/Structure/OrientationMapHelpers.hpp"
      25              : #include "Domain/Tags.hpp"
      26              : #include "Domain/Tags/Faces.hpp"
      27              : #include "IO/Logging/Tags.hpp"
      28              : #include "IO/Logging/Verbosity.hpp"
      29              : #include "IO/Observer/Actions/RegisterWithObservers.hpp"
      30              : #include "IO/Observer/GetSectionObservationKey.hpp"
      31              : #include "IO/Observer/ObservationId.hpp"
      32              : #include "IO/Observer/Protocols/ReductionDataFormatter.hpp"
      33              : #include "IO/Observer/ReductionActions.hpp"
      34              : #include "IO/Observer/Tags.hpp"
      35              : #include "IO/Observer/TypeOfObservation.hpp"
      36              : #include "NumericalAlgorithms/Convergence/Tags.hpp"
      37              : #include "NumericalAlgorithms/DiscontinuousGalerkin/HasReceivedFromAllMortars.hpp"
      38              : #include "NumericalAlgorithms/LinearSolver/ExplicitInverse.hpp"
      39              : #include "NumericalAlgorithms/LinearSolver/Gmres.hpp"
      40              : #include "NumericalAlgorithms/Spectral/CollocationPoints.hpp"
      41              : #include "NumericalAlgorithms/Spectral/Mesh.hpp"
      42              : #include "Parallel/AlgorithmExecution.hpp"
      43              : #include "Parallel/ArrayComponentId.hpp"
      44              : #include "Parallel/GlobalCache.hpp"
      45              : #include "Parallel/InboxInserters.hpp"
      46              : #include "Parallel/Invoke.hpp"
      47              : #include "Parallel/Local.hpp"
      48              : #include "Parallel/ParallelComponentHelpers.hpp"
      49              : #include "Parallel/Printf/Printf.hpp"
      50              : #include "Parallel/Reduction.hpp"
      51              : #include "ParallelAlgorithms/Amr/Protocols/Projector.hpp"
      52              : #include "ParallelAlgorithms/Initialization/MutateAssign.hpp"
      53              : #include "ParallelAlgorithms/LinearSolver/AsynchronousSolvers/ElementActions.hpp"
      54              : #include "ParallelAlgorithms/LinearSolver/Schwarz/Actions/CommunicateOverlapFields.hpp"
      55              : #include "ParallelAlgorithms/LinearSolver/Schwarz/ElementCenteredSubdomainData.hpp"
      56              : #include "ParallelAlgorithms/LinearSolver/Schwarz/OverlapHelpers.hpp"
      57              : #include "ParallelAlgorithms/LinearSolver/Schwarz/Tags.hpp"
      58              : #include "ParallelAlgorithms/LinearSolver/Schwarz/Weighting.hpp"
      59              : #include "ParallelAlgorithms/LinearSolver/Tags.hpp"
      60              : #include "Utilities/Gsl.hpp"
      61              : #include "Utilities/PrettyType.hpp"
      62              : #include "Utilities/ProtocolHelpers.hpp"
      63              : #include "Utilities/SetNumberOfGridPoints.hpp"
      64              : #include "Utilities/TMPL.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) + ").\n";
     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<SubdomainSolver, OptionsGroup>;
     209              :   using volume_data_tag =
     210              :       Tags::VolumeDataForOutput<SubdomainData, OptionsGroup>;
     211              : 
     212              :  public:  // Iterable action
     213              :   using simple_tags_from_options = tmpl::list<subdomain_solver_tag>;
     214              : 
     215              :   using const_global_cache_tags =
     216              :       tmpl::list<Tags::MaxOverlap<OptionsGroup>,
     217              :                  LinearSolver::Tags::OutputVolumeData<OptionsGroup>>;
     218              : 
     219              :   using simple_tags = tmpl::list<
     220              :       Tags::IntrudingExtents<Dim, OptionsGroup>, Tags::Weight<OptionsGroup>,
     221              :       domain::Tags::Faces<Dim, Tags::Weight<OptionsGroup>>,
     222              :       SubdomainDataBufferTag<SubdomainData, OptionsGroup>,
     223              :       LinearSolver::Tags::ObservationId<OptionsGroup>, volume_data_tag>;
     224              :   using compute_tags = tmpl::list<>;
     225              :   template <typename DbTagsList, typename... InboxTags, typename Metavariables,
     226              :             typename ActionList, typename ParallelComponent>
     227              :   static Parallel::iterable_action_return_t apply(
     228              :       db::DataBox<DbTagsList>& box,
     229              :       const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,
     230              :       const Parallel::GlobalCache<Metavariables>& /*cache*/,
     231              :       const ElementId<Dim>& /*element_id*/, const ActionList /*meta*/,
     232              :       const ParallelComponent* const /*meta*/) {
     233              :     db::mutate_apply<InitializeElement>(make_not_null(&box));
     234              :     return {Parallel::AlgorithmExecution::Continue, std::nullopt};
     235              :   }
     236              : 
     237              :  public:  // amr::protocols::Projector
     238              :   using return_tags = tmpl::append<simple_tags, simple_tags_from_options>;
     239              :   using argument_tags =
     240              :       tmpl::list<domain::Tags::Element<Dim>, domain::Tags::Mesh<Dim>,
     241              :                  domain::Tags::Coordinates<Dim, Frame::ElementLogical>,
     242              :                  Tags::MaxOverlap<OptionsGroup>>;
     243              : 
     244              :   template <typename... AmrData>
     245              :   static void apply(
     246              :       const gsl::not_null<std::array<size_t, Dim>*> intruding_extents,
     247              :       const gsl::not_null<Scalar<DataVector>*> element_weight,
     248              :       const gsl::not_null<DirectionMap<Dim, Scalar<DataVector>>*>
     249              :           intruding_overlap_weights,
     250              :       const gsl::not_null<SubdomainData*> subdomain_data,
     251              :       const gsl::not_null<size_t*> observation_id,
     252              :       const gsl::not_null<SubdomainData*> /*volume_data_for_output*/,
     253              :       const gsl::not_null<std::unique_ptr<SubdomainSolver>*> subdomain_solver,
     254              :       const Element<Dim>& element, const Mesh<Dim>& mesh,
     255              :       const tnsr::I<DataVector, Dim, Frame::ElementLogical>& logical_coords,
     256              :       const std::optional<size_t> max_overlap, const AmrData&... amr_data) {
     257              :     const size_t num_points = mesh.number_of_grid_points();
     258              : 
     259              :     // Intruding overlaps
     260              :     std::array<double, Dim> intruding_overlap_widths{};
     261              :     for (size_t d = 0; d < Dim; ++d) {
     262              :       gsl::at(*intruding_extents, d) =
     263              :           LinearSolver::Schwarz::overlap_extent(mesh.extents(d), max_overlap);
     264              :       const auto& collocation_points =
     265              :           Spectral::collocation_points(mesh.slice_through(d));
     266              :       gsl::at(intruding_overlap_widths, d) =
     267              :           LinearSolver::Schwarz::overlap_width(gsl::at(*intruding_extents, d),
     268              :                                                collocation_points);
     269              :     }
     270              : 
     271              :     // Element weight
     272              :     // For max_overlap > 0 all overlaps will have non-zero extents on an LGL
     273              :     // mesh (because it has at least 2 points per dimension), so we don't need
     274              :     // to check their extents are non-zero individually
     275              :     if (LIKELY(max_overlap != 0)) {
     276              :       LinearSolver::Schwarz::element_weight(element_weight, logical_coords,
     277              :                                             intruding_overlap_widths,
     278              :                                             element.external_boundaries());
     279              :     } else {
     280              :       set_number_of_grid_points(element_weight, num_points);
     281              :       get(*element_weight) = 1.;
     282              :     }
     283              : 
     284              :     // Intruding overlap weights
     285              :     intruding_overlap_weights->clear();
     286              :     for (const auto& direction : element.internal_boundaries()) {
     287              :       const size_t dim = direction.dimension();
     288              :       if (gsl::at(*intruding_extents, dim) > 0) {
     289              :         const auto intruding_logical_coords =
     290              :             LinearSolver::Schwarz::data_on_overlap(
     291              :                 logical_coords, mesh.extents(),
     292              :                 gsl::at(*intruding_extents, dim), direction);
     293              :         (*intruding_overlap_weights)[direction] =
     294              :             LinearSolver::Schwarz::intruding_weight(
     295              :                 intruding_logical_coords, direction, intruding_overlap_widths,
     296              :                 element.neighbors().at(direction).size(),
     297              :                 element.external_boundaries());
     298              :       }
     299              :     }
     300              : 
     301              :     // Subdomain data buffer
     302              :     *subdomain_data = SubdomainData{num_points};
     303              : 
     304              :     // Subdomain solver
     305              :     // The subdomain solver initially gets created from options on each element.
     306              :     // Then we have to copy it around during AMR.
     307              :     if constexpr (sizeof...(AmrData) == 0) {
     308              :       *observation_id = 0;
     309              :       (void)subdomain_solver;
     310              :     } else {
     311              :       if constexpr (tt::is_a_v<tuples::TaggedTuple, AmrData...>) {
     312              :         // h-refinement: copy from the parent
     313              :         *observation_id =
     314              :             get<LinearSolver::Tags::ObservationId<OptionsGroup>>(amr_data...);
     315              :         *subdomain_solver = get<subdomain_solver_tag>(amr_data...)->get_clone();
     316              :       } else if constexpr (tt::is_a_v<std::unordered_map, AmrData...>) {
     317              :         // h-coarsening, copy from one of the children (doesn't matter which)
     318              :         *observation_id = get<LinearSolver::Tags::ObservationId<OptionsGroup>>(
     319              :             amr_data.begin()->second...);
     320              :         *subdomain_solver =
     321              :             get<subdomain_solver_tag>(amr_data.begin()->second...)->get_clone();
     322              :       } else {
     323              :         (void)observation_id;
     324              :       }
     325              :       (*subdomain_solver)->reset();
     326              :     }
     327              :   }
     328              : };
     329              : 
     330              : // Restrict the residual to neighboring subdomains that overlap with this
     331              : // element and send the data to those elements
     332              : template <typename FieldsTag, typename OptionsGroup, typename SubdomainOperator>
     333              : using SendOverlapData = LinearSolver::Schwarz::Actions::SendOverlapFields<
     334              :     tmpl::list<db::add_tag_prefix<LinearSolver::Tags::Residual, FieldsTag>>,
     335              :     OptionsGroup, true>;
     336              : 
     337              : template <size_t Dim, typename OptionsGroup, typename OverlapSolution>
     338              : struct OverlapSolutionInboxTag
     339              :     : public Parallel::InboxInserters::Map<
     340              :           OverlapSolutionInboxTag<Dim, OptionsGroup, OverlapSolution>> {
     341              :   using temporal_id = size_t;
     342              :   using type = std::map<temporal_id, OverlapMap<Dim, OverlapSolution>>;
     343              : };
     344              : 
     345              : // Wait for the residual data on regions of this element's subdomain that
     346              : // overlap with other elements. Once the residual data is available on all
     347              : // overlaps, solve the restricted problem for this element-centered subdomain.
     348              : // Apply the weighted solution on this element directly and send the solution on
     349              : // overlap regions to the neighbors that they overlap with.
     350              : template <typename FieldsTag, typename OptionsGroup, typename SubdomainOperator,
     351              :           typename SubdomainPreconditioners, typename ArraySectionIdTag>
     352              : struct SolveSubdomain {
     353              :  private:
     354              :   using fields_tag = FieldsTag;
     355              :   using residual_tag =
     356              :       db::add_tag_prefix<LinearSolver::Tags::Residual, fields_tag>;
     357              :   static constexpr size_t Dim = SubdomainOperator::volume_dim;
     358              :   using overlap_residuals_inbox_tag =
     359              :       Actions::detail::OverlapFieldsTag<Dim, tmpl::list<residual_tag>,
     360              :                                         OptionsGroup>;
     361              :   using SubdomainData =
     362              :       ElementCenteredSubdomainData<Dim, typename residual_tag::tags_list>;
     363              :   using SubdomainSolver =
     364              :       subdomain_solver<FieldsTag, SubdomainOperator, SubdomainPreconditioners>;
     365              :   using subdomain_solver_tag =
     366              :       Tags::SubdomainSolver<SubdomainSolver, OptionsGroup>;
     367              :   using OverlapData = typename SubdomainData::OverlapData;
     368              :   using overlap_solution_inbox_tag =
     369              :       OverlapSolutionInboxTag<Dim, OptionsGroup, OverlapData>;
     370              : 
     371              :  public:
     372              :   using const_global_cache_tags =
     373              :       tmpl::list<Tags::MaxOverlap<OptionsGroup>,
     374              :                  logging::Tags::Verbosity<OptionsGroup>,
     375              :                  Tags::ObservePerCoreReductions<OptionsGroup>>;
     376              :   using inbox_tags = tmpl::list<overlap_residuals_inbox_tag>;
     377              : 
     378              :   template <typename DbTagsList, typename... InboxTags, typename Metavariables,
     379              :             typename ActionList, typename ParallelComponent>
     380              :   static Parallel::iterable_action_return_t apply(
     381              :       db::DataBox<DbTagsList>& box, tuples::TaggedTuple<InboxTags...>& inboxes,
     382              :       Parallel::GlobalCache<Metavariables>& cache,
     383              :       const ElementId<Dim>& element_id, const ActionList /*meta*/,
     384              :       const ParallelComponent* const /*meta*/) {
     385              :     const size_t iteration_id =
     386              :         get<Convergence::Tags::IterationId<OptionsGroup>>(box);
     387              :     const auto& element = db::get<domain::Tags::Element<Dim>>(box);
     388              :     const auto& max_overlap = db::get<Tags::MaxOverlap<OptionsGroup>>(box);
     389              : 
     390              :     // Wait for communicated overlap data
     391              :     const bool has_overlap_data =
     392              :         max_overlap != 0 and element.number_of_neighbors() > 0;
     393              :     if (LIKELY(has_overlap_data) and
     394              :         not dg::has_received_from_all_mortars<overlap_residuals_inbox_tag>(
     395              :             iteration_id, element, inboxes)) {
     396              :       return {Parallel::AlgorithmExecution::Retry, std::nullopt};
     397              :     }
     398              : 
     399              :     // Do some logging
     400              :     if (UNLIKELY(get<logging::Tags::Verbosity<OptionsGroup>>(box) >=
     401              :                  ::Verbosity::Debug)) {
     402              :       Parallel::printf("%s %s(%zu): Solve subdomain\n", element_id,
     403              :                        pretty_type::name<OptionsGroup>(), iteration_id);
     404              :     }
     405              : 
     406              :     // Assemble the subdomain data from the data on the element and the
     407              :     // communicated overlap data
     408              :     db::mutate<SubdomainDataBufferTag<SubdomainData, OptionsGroup>>(
     409              :         [&inboxes, &iteration_id, &has_overlap_data](
     410              :             const gsl::not_null<SubdomainData*> subdomain_data,
     411              :             const auto& residual) {
     412              :           subdomain_data->element_data = residual;
     413              :           // Nothing was communicated if the overlaps are empty
     414              :           if (LIKELY(has_overlap_data)) {
     415              :             subdomain_data->overlap_data =
     416              :                 std::move(tuples::get<overlap_residuals_inbox_tag>(inboxes)
     417              :                               .extract(iteration_id)
     418              :                               .mapped());
     419              :           }
     420              :         },
     421              :         make_not_null(&box), db::get<residual_tag>(box));
     422              :     const auto& subdomain_residual =
     423              :         db::get<SubdomainDataBufferTag<SubdomainData, OptionsGroup>>(box);
     424              : 
     425              :     // Allocate workspace memory for repeatedly applying the subdomain operator
     426              :     const SubdomainOperator subdomain_operator{};
     427              : 
     428              :     // Solve the subdomain problem
     429              :     const auto& subdomain_solver = get<subdomain_solver_tag>(box);
     430              :     auto subdomain_solve_initial_guess_in_solution_out =
     431              :         make_with_value<SubdomainData>(subdomain_residual, 0.);
     432              :     const auto subdomain_solve_has_converged = subdomain_solver.solve(
     433              :         make_not_null(&subdomain_solve_initial_guess_in_solution_out),
     434              :         subdomain_operator, subdomain_residual, std::forward_as_tuple(box));
     435              :     // Re-naming the solution buffer for the code below
     436              :     auto& subdomain_solution = subdomain_solve_initial_guess_in_solution_out;
     437              : 
     438              :     // Do some logging and observing
     439              :     if (UNLIKELY(get<logging::Tags::Verbosity<OptionsGroup>>(box) >=
     440              :                  ::Verbosity::Quiet)) {
     441              :       if (not subdomain_solve_has_converged or
     442              :           subdomain_solve_has_converged.reason() ==
     443              :               Convergence::Reason::MaxIterations) {
     444              :         Parallel::printf(
     445              :             "%s %s(%zu): WARNING: Subdomain solver did not converge in %zu "
     446              :             "iterations: %e -> %e\n",
     447              :             element_id, pretty_type::name<OptionsGroup>(), iteration_id,
     448              :             subdomain_solve_has_converged.num_iterations(),
     449              :             subdomain_solve_has_converged.initial_residual_magnitude(),
     450              :             subdomain_solve_has_converged.residual_magnitude());
     451              :       } else if (UNLIKELY(get<logging::Tags::Verbosity<OptionsGroup>>(box) >=
     452              :                           ::Verbosity::Debug)) {
     453              :         Parallel::printf(
     454              :             "%s %s(%zu): Subdomain solver converged in %zu iterations (%s): %e "
     455              :             "-> %e\n",
     456              :             element_id, pretty_type::name<OptionsGroup>(), iteration_id,
     457              :             subdomain_solve_has_converged.num_iterations(),
     458              :             subdomain_solve_has_converged.reason(),
     459              :             subdomain_solve_has_converged.initial_residual_magnitude(),
     460              :             subdomain_solve_has_converged.residual_magnitude());
     461              :       }
     462              :     }
     463              :     const std::optional<std::string> section_observation_key =
     464              :         observers::get_section_observation_key<ArraySectionIdTag>(box);
     465              :     if (section_observation_key.has_value()) {
     466              :       contribute_to_subdomain_stats_observation<OptionsGroup,
     467              :                                                 ParallelComponent>(
     468              :           iteration_id + 1, subdomain_solve_has_converged.num_iterations(),
     469              :           cache, element_id, *section_observation_key,
     470              :           db::get<Tags::ObservePerCoreReductions<OptionsGroup>>(box));
     471              :     }
     472              : 
     473              :     // Record subdomain solution for volume data output
     474              :     if (db::get<LinearSolver::Tags::OutputVolumeData<OptionsGroup>>(box)) {
     475              :       db::mutate<Tags::VolumeDataForOutput<SubdomainData, OptionsGroup>>(
     476              :           [&subdomain_solution](const auto volume_data) {
     477              :             volume_data->element_data = subdomain_solution.element_data;
     478              :           },
     479              :           make_not_null(&box));
     480              :     }
     481              : 
     482              :     // Apply weighting
     483              :     if (LIKELY(max_overlap != 0)) {
     484              :       subdomain_solution.element_data *=
     485              :           get(db::get<Tags::Weight<OptionsGroup>>(box));
     486              :     }
     487              : 
     488              :     // Apply solution to central element
     489              :     db::mutate<fields_tag>(
     490              :         [&subdomain_solution](const auto fields) {
     491              :           *fields += subdomain_solution.element_data;
     492              :         },
     493              :         make_not_null(&box));
     494              : 
     495              :     // Send overlap solutions back to the neighbors that they are on
     496              :     if (LIKELY(max_overlap != 0)) {
     497              :       auto& receiver_proxy =
     498              :           Parallel::get_parallel_component<ParallelComponent>(cache);
     499              :       for (auto& [overlap_id, overlap_solution] :
     500              :            subdomain_solution.overlap_data) {
     501              :         const auto& direction = overlap_id.direction();
     502              :         const auto& neighbor_id = overlap_id.id();
     503              :         const auto& orientation =
     504              :             element.neighbors().at(direction).orientation(neighbor_id);
     505              :         const auto direction_from_neighbor = orientation(direction.opposite());
     506              :         Parallel::receive_data<overlap_solution_inbox_tag>(
     507              :             receiver_proxy[neighbor_id], iteration_id,
     508              :             std::make_pair(
     509              :                 OverlapId<Dim>{direction_from_neighbor, element.id()},
     510              :                 std::move(overlap_solution)));
     511              :       }
     512              :     }
     513              :     return {Parallel::AlgorithmExecution::Continue, std::nullopt};
     514              :   }
     515              : };
     516              : 
     517              : // Wait for the subdomain solutions on regions within this element that overlap
     518              : // with neighboring element-centered subdomains. Combine the solutions as a
     519              : // weighted sum.
     520              : template <typename FieldsTag, typename OptionsGroup, typename SubdomainOperator>
     521              : struct ReceiveOverlapSolution {
     522              :  private:
     523              :   using fields_tag = FieldsTag;
     524              :   using residual_tag =
     525              :       db::add_tag_prefix<LinearSolver::Tags::Residual, fields_tag>;
     526              :   static constexpr size_t Dim = SubdomainOperator::volume_dim;
     527              :   using SubdomainData =
     528              :       ElementCenteredSubdomainData<Dim, typename residual_tag::tags_list>;
     529              :   using OverlapSolution = typename SubdomainData::OverlapData;
     530              :   using overlap_solution_inbox_tag =
     531              :       OverlapSolutionInboxTag<Dim, OptionsGroup, OverlapSolution>;
     532              : 
     533              :  public:
     534              :   using const_global_cache_tags = tmpl::list<Tags::MaxOverlap<OptionsGroup>>;
     535              :   using inbox_tags = tmpl::list<overlap_solution_inbox_tag>;
     536              : 
     537              :   template <typename DbTagsList, typename... InboxTags, typename Metavariables,
     538              :             typename ActionList, typename ParallelComponent>
     539              :   static Parallel::iterable_action_return_t apply(
     540              :       db::DataBox<DbTagsList>& box, tuples::TaggedTuple<InboxTags...>& inboxes,
     541              :       const Parallel::GlobalCache<Metavariables>& /*cache*/,
     542              :       const ElementId<Dim>& element_id, const ActionList /*meta*/,
     543              :       const ParallelComponent* const /*meta*/) {
     544              :     const size_t iteration_id =
     545              :         get<Convergence::Tags::IterationId<OptionsGroup>>(box);
     546              :     const auto& element = db::get<domain::Tags::Element<Dim>>(box);
     547              : 
     548              :     // Nothing to do if overlap is empty
     549              :     if (UNLIKELY(db::get<Tags::MaxOverlap<OptionsGroup>>(box) == 0 or
     550              :                  element.number_of_neighbors() == 0)) {
     551              :       return {Parallel::AlgorithmExecution::Continue, std::nullopt};
     552              :     }
     553              : 
     554              :     if (not dg::has_received_from_all_mortars<overlap_solution_inbox_tag>(
     555              :             iteration_id, element, inboxes)) {
     556              :       return {Parallel::AlgorithmExecution::Retry, std::nullopt};
     557              :     }
     558              : 
     559              :     // Do some logging
     560              :     if (UNLIKELY(get<logging::Tags::Verbosity<OptionsGroup>>(box) >=
     561              :                  ::Verbosity::Debug)) {
     562              :       Parallel::printf("%s %s(%zu): Receive overlap solution\n", element_id,
     563              :                        pretty_type::name<OptionsGroup>(), iteration_id);
     564              :     }
     565              : 
     566              :     // Extract overlap solutions from inbox
     567              :     auto received_overlap_solutions =
     568              :         std::move(tuples::get<overlap_solution_inbox_tag>(inboxes)
     569              :                       .extract(iteration_id)
     570              :                       .mapped());
     571              : 
     572              :     // Record overlap solutions for volume data output
     573              :     if (db::get<LinearSolver::Tags::OutputVolumeData<OptionsGroup>>(box)) {
     574              :       db::mutate<Tags::VolumeDataForOutput<SubdomainData, OptionsGroup>>(
     575              :           [&received_overlap_solutions](const auto volume_data) {
     576              :             volume_data->overlap_data = received_overlap_solutions;
     577              :           },
     578              :           make_not_null(&box));
     579              :     }
     580              : 
     581              :     // Add overlap solutions to this element's solution in a weighted sum
     582              :     db::mutate<fields_tag>(
     583              :         [&received_overlap_solutions](
     584              :             const auto fields, const Index<Dim>& full_extents,
     585              :             const std::array<size_t, Dim>& all_intruding_extents,
     586              :             const DirectionMap<Dim, Scalar<DataVector>>&
     587              :                 all_intruding_overlap_weights) {
     588              :           for (auto& [overlap_id, overlap_solution] :
     589              :                received_overlap_solutions) {
     590              :             const auto& direction = overlap_id.direction();
     591              :             const auto& intruding_extents =
     592              :                 gsl::at(all_intruding_extents, direction.dimension());
     593              :             const auto& overlap_weight =
     594              :                 all_intruding_overlap_weights.at(direction);
     595              :             overlap_solution *= get(overlap_weight);
     596              :             LinearSolver::Schwarz::add_overlap_data(
     597              :                 fields, overlap_solution, full_extents, intruding_extents,
     598              :                 direction);
     599              :           }
     600              :         },
     601              :         make_not_null(&box), db::get<domain::Tags::Mesh<Dim>>(box).extents(),
     602              :         db::get<Tags::IntrudingExtents<Dim, OptionsGroup>>(box),
     603              :         db::get<domain::Tags::Faces<Dim, Tags::Weight<OptionsGroup>>>(box));
     604              :     return {Parallel::AlgorithmExecution::Continue, std::nullopt};
     605              :   }
     606              : };
     607              : 
     608              : }  // namespace LinearSolver::Schwarz::detail
        

Generated by: LCOV version 2.0-1