SpECTRE Documentation Coverage Report
Current view: top level - Evolution/Particles/MonteCarlo - GhostZoneCommunication.hpp Hit Total Coverage
Commit: 1f2210958b4f38fdc0400907ee7c6d5af5111418 Lines: 5 24 20.8 %
Date: 2025-12-05 05:03:31
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 <optional>
       7             : #include <tuple>
       8             : #include <utility>
       9             : 
      10             : #include "DataStructures/DataBox/DataBox.hpp"
      11             : #include "DataStructures/DataVector.hpp"
      12             : #include "DataStructures/Index.hpp"
      13             : #include "DataStructures/Tensor/Tensor.hpp"
      14             : #include "Domain/Structure/Direction.hpp"
      15             : #include "Domain/Structure/DirectionalId.hpp"
      16             : #include "Domain/Structure/DirectionalIdMap.hpp"
      17             : #include "Domain/Structure/Element.hpp"
      18             : #include "Domain/Structure/ElementId.hpp"
      19             : #include "Domain/Structure/OrientationMapHelpers.hpp"
      20             : #include "Domain/Tags.hpp"
      21             : #include "Domain/Tags/NeighborMesh.hpp"
      22             : #include "Evolution/DgSubcell/ActiveGrid.hpp"
      23             : #include "Evolution/DgSubcell/GhostData.hpp"
      24             : #include "Evolution/DgSubcell/SliceData.hpp"
      25             : #include "Evolution/DgSubcell/Tags/ActiveGrid.hpp"
      26             : #include "Evolution/DgSubcell/Tags/Coordinates.hpp"
      27             : #include "Evolution/DgSubcell/Tags/Interpolators.hpp"
      28             : #include "Evolution/DgSubcell/Tags/Mesh.hpp"
      29             : #include "Evolution/Particles/MonteCarlo/GhostZoneCommunicationStep.hpp"
      30             : #include "Evolution/Particles/MonteCarlo/GhostZoneCommunicationTags.hpp"
      31             : #include "Evolution/Particles/MonteCarlo/MortarData.hpp"
      32             : #include "Evolution/Particles/MonteCarlo/Tags.hpp"
      33             : #include "Parallel/AlgorithmExecution.hpp"
      34             : #include "Parallel/GlobalCache.hpp"
      35             : #include "PointwiseFunctions/Hydro/TagsDeclarations.hpp"
      36             : #include "Time/TimeStepId.hpp"
      37             : #include "Utilities/ErrorHandling/Assert.hpp"
      38             : #include "Utilities/Gsl.hpp"
      39             : 
      40             : /// \cond
      41             : namespace Tags {
      42             : struct TimeStepId;
      43             : }  // namespace Tags
      44             : /// \endcond
      45             : 
      46             : namespace Particles::MonteCarlo::Actions {
      47             : 
      48             : /// Mutator to get required volume data for communication
      49             : /// before a MC step; i.e. data sent from live points
      50             : /// to ghost points in neighbors.
      51           1 : struct GhostDataMutatorPreStep {
      52           0 :   using return_tags = tmpl::list<>;
      53           0 :   using argument_tags = tmpl::list<
      54             :       hydro::Tags::RestMassDensity<DataVector>,
      55             :       hydro::Tags::ElectronFraction<DataVector>,
      56             :       hydro::Tags::Temperature<DataVector>,
      57             :       Particles::MonteCarlo::Tags::CellLightCrossingTime<DataVector>>;
      58           0 :   static const size_t number_of_vars = 4;
      59             : 
      60           0 :   static DataVector apply(const Scalar<DataVector>& rest_mass_density,
      61             :                           const Scalar<DataVector>& electron_fraction,
      62             :                           const Scalar<DataVector>& temperature,
      63             :                           const Scalar<DataVector>& cell_light_crossing_time) {
      64             :     const size_t dv_size = get(rest_mass_density).size();
      65             :     DataVector buffer{dv_size * number_of_vars};
      66             :     std::copy(
      67             :         get(rest_mass_density).data(),
      68             :         std::next(get(rest_mass_density).data(), static_cast<int>(dv_size)),
      69             :         buffer.data());
      70             :     std::copy(
      71             :         get(electron_fraction).data(),
      72             :         std::next(get(electron_fraction).data(), static_cast<int>(dv_size)),
      73             :         std::next(buffer.data(), static_cast<int>(dv_size)));
      74             :     std::copy(get(temperature).data(),
      75             :               std::next(get(temperature).data(), static_cast<int>(dv_size)),
      76             :               std::next(buffer.data(), static_cast<int>(dv_size * 2)));
      77             :     std::copy(get(cell_light_crossing_time).data(),
      78             :               std::next(get(cell_light_crossing_time).data(),
      79             :                         static_cast<int>(dv_size)),
      80             :               std::next(buffer.data(), static_cast<int>(dv_size * 3)));
      81             :     return buffer;
      82             :   }
      83             : };
      84             : 
      85             : /// Mutator to get required volume data for communication
      86             : /// after a MC step; i.e. data sent from ghost points
      87             : /// in neighbors to live points evolving the fluid. The
      88             : /// data contains information about the back reaction of
      89             : /// neutrinos on the fluid
      90             : template <size_t Dim>
      91           1 : struct GhostDataMutatorPostStep {
      92           0 :   using return_tags = tmpl::list<>;
      93           0 :   using argument_tags =
      94             :       tmpl::list<Particles::MonteCarlo::Tags::CouplingTildeTau<DataVector>,
      95             :                  Particles::MonteCarlo::Tags::CouplingTildeRhoYe<DataVector>,
      96             :                  Particles::MonteCarlo::Tags::CouplingTildeS<DataVector, Dim>>;
      97           0 :   static const size_t number_of_components = 2 + Dim;
      98             : 
      99           0 :   static DataVector apply(const Scalar<DataVector>& coupling_tilde_tau,
     100             :                           const Scalar<DataVector>& coupling_tilde_rho_ye,
     101             :                           const tnsr::i<DataVector, Dim>& coupling_tilde_s) {
     102             :     const size_t dv_size = get(coupling_tilde_tau).size();
     103             :     DataVector buffer{dv_size * number_of_components};
     104             :     std::copy(
     105             :         get(coupling_tilde_tau).data(),
     106             :         std::next(get(coupling_tilde_tau).data(), static_cast<int>(dv_size)),
     107             :         buffer.data());
     108             :     std::copy(
     109             :         get(coupling_tilde_rho_ye).data(),
     110             :         std::next(get(coupling_tilde_rho_ye).data(), static_cast<int>(dv_size)),
     111             :         std::next(buffer.data(), static_cast<int>(dv_size)));
     112             :     for (size_t d = 0; d < Dim; d++) {
     113             :       std::copy(
     114             :           coupling_tilde_s.get(d).data(),
     115             :           std::next(coupling_tilde_s.get(d).data(), static_cast<int>(dv_size)),
     116             :           std::next(buffer.data(), static_cast<int>(dv_size * (2 + d))));
     117             :     }
     118             :     return buffer;
     119             :   }
     120             : };
     121             : 
     122             : /// Mutator that returns packets currently in ghost zones in a
     123             : /// DirectionMap<Dim,std::vector<Particles::MonteCarlo::Packet>>
     124             : /// and remove them from the list of packets of the current
     125             : /// element
     126             : template <size_t Dim>
     127           1 : struct GhostDataMcPackets {
     128           0 :   using return_tags = tmpl::list<Particles::MonteCarlo::Tags::PacketsOnElement>;
     129           0 :   using argument_tags = tmpl::list<::domain::Tags::Element<Dim>>;
     130             : 
     131           0 :   static DirectionMap<Dim, std::vector<Particles::MonteCarlo::Packet>> apply(
     132             :       const gsl::not_null<std::vector<Particles::MonteCarlo::Packet>*> packets,
     133             :       const Element<Dim>& element) {
     134             :     DirectionMap<Dim, std::vector<Particles::MonteCarlo::Packet>> output{};
     135             :     for (const auto& [direction, neighbors_in_direction] :
     136             :          element.neighbors()) {
     137             :       output[direction] = std::vector<Particles::MonteCarlo::Packet>{};
     138             :     }
     139             :     // Loop over packets. If a packet is out of the current element,
     140             :     // remove it from the element and copy it to the appropriate
     141             :     // neighbor list if it exists.
     142             :     // Note: At this point, we only handle simple boundaries
     143             :     // (one neighbor per direction, with the logical coordinates
     144             :     // being transformed by adding/removing 2). We assume that
     145             :     // packets that go out of the element but do not match any
     146             :     // neighbor have reached the domain boundary and can be removed.
     147             :     size_t n_packets = packets->size();
     148             :     for (size_t p = 0; p < n_packets; p++) {
     149             :       Packet& packet = (*packets)[p];
     150             :       std::optional<Direction<Dim>> max_distance_direction = std::nullopt;
     151             :       double max_distance = 0.0;
     152             :       // TO DO: Deal with dimensions higher than the grid dimension
     153             :       // e.g. for axisymmetric evolution
     154             :       for (size_t d = 0; d < Dim; d++) {
     155             :         if (packet.coordinates.get(d) < -1.0) {
     156             :           const double distance = -1.0 - packet.coordinates.get(d);
     157             :           if (max_distance < distance) {
     158             :             max_distance = distance;
     159             :             max_distance_direction = Direction<Dim>(d, Side::Lower);
     160             :           }
     161             :         }
     162             :         if (packet.coordinates.get(d) > 1.0) {
     163             :           const double distance = packet.coordinates.get(d) - 1.0;
     164             :           if (max_distance < distance) {
     165             :             max_distance = distance;
     166             :             max_distance_direction = Direction<Dim>(d, Side::Upper);
     167             :           }
     168             :         }
     169             :       }
     170             :       // If a packet should be moved along an edge / corner, we move
     171             :       // it into the direct neighbor it is effectively closest to
     172             :       // (in topological coordinates). This may need improvements.
     173             :       if (max_distance_direction != std::nullopt) {
     174             :         const size_t d = max_distance_direction.value().dimension();
     175             :         const Side side = max_distance_direction.value().side();
     176             :         packet.coordinates.get(d) += (side == Side::Lower) ? (2.0) : (-2.0);
     177             :         // Corner/edge treatment; move packet in a live point for now
     178             :         // Definitely needs improvement...
     179             :         for (size_t dd = 0; dd < Dim; dd++) {
     180             :           if (dd != d && packet.coordinates.get(dd) < -1.0) {
     181             :             packet.coordinates.get(dd) = -2.0 - packet.coordinates.get(dd);
     182             :           }
     183             :           if (dd != d && packet.coordinates.get(dd) > 1.0) {
     184             :             packet.coordinates.get(dd) = 2.0 - packet.coordinates.get(dd);
     185             :           }
     186             :         }
     187             :         if (output.contains(max_distance_direction.value())) {
     188             :           output[max_distance_direction.value()].push_back(packet);
     189             :         }
     190             :         std::swap((*packets)[p], (*packets)[n_packets - 1]);
     191             :         packets->pop_back();
     192             :         p--;
     193             :         n_packets--;
     194             :       }
     195             :     }
     196             :     return output;
     197             :   }
     198             : };
     199             : 
     200             : // Take the data for coupling MC to hydro gathered from neighboring
     201             : // ghost zones (stored after communication in GhostZoneCouplingDataTag)
     202             : // and add them to the coupling terms in live zones. After calling this
     203             : // action, the coupling terms CouplingTildeTau, CouplingTildeRhoYe,
     204             : // CouplingTildeS should contain the changes to the energy, RhoYe, and
     205             : // momentum of the fluid due to MC packets evolved locally and those
     206             : // that evolved within ghost zones on neighboring elements.
     207             : // THIS FUNCTION IS CURRENTLY ONLY IMPLENTED IN 3D!
     208             : // As other parts of the MC communication, it is also incompatible with
     209             : // mesh refinement.
     210             : template <size_t Dim>
     211           0 : struct CombineCouplingDataPostStep {
     212           0 :   using return_tags =
     213             :       tmpl::list<Particles::MonteCarlo::Tags::CouplingTildeTau<DataVector>,
     214             :                  Particles::MonteCarlo::Tags::CouplingTildeRhoYe<DataVector>,
     215             :                  Particles::MonteCarlo::Tags::CouplingTildeS<DataVector, Dim>>;
     216           0 :   using argument_tags =
     217             :       tmpl::list<Particles::MonteCarlo::Tags::GhostZoneCouplingDataTag<Dim>,
     218             :                  ::domain::Tags::Element<Dim>,
     219             :                  evolution::dg::subcell::Tags::Mesh<Dim>>;
     220           0 :   static void apply(
     221             :       gsl::not_null<Scalar<DataVector>*> coupling_tilde_tau,
     222             :       gsl::not_null<Scalar<DataVector>*> coupling_tilde_rho_ye,
     223             :       gsl::not_null<tnsr::i<DataVector, Dim, Frame::Inertial>*>
     224             :           coupling_tilde_s,
     225             :       const Particles::MonteCarlo::GhostZoneCouplingData<Dim>& coupling_data,
     226             :       const Element<Dim>& element, const Mesh<Dim>& subcell_mesh) {
     227             :     // Check that we do not use this function for 1D/2D runs
     228             :     ASSERT(Dim == 3, "CombineCouplingDataPostStep only coded in 3D so far");
     229             : 
     230             :     const size_t num_ghost_zones = 1;
     231             :     const Index<Dim> local_extents = subcell_mesh.extents();
     232             :     Index<Dim> ghost_extents = local_extents;
     233             :     for (size_t d = 0; d < 3; d++) {
     234             :       ghost_extents[d] += 2 * num_ghost_zones;
     235             :     }
     236             :     // Loop over neighboring elements
     237             :     for (const auto& [direction, neighbors_in_direction] :
     238             :          element.neighbors()) {
     239             :       for (const auto& neighbor : neighbors_in_direction) {
     240             :         DirectionalId<Dim> directional_element_id{direction, neighbor};
     241             :         if (coupling_data.coupling_tilde_tau.at(directional_element_id) ==
     242             :             std::nullopt) {
     243             :           continue;
     244             :         }
     245             :         const size_t dimension = directional_element_id.direction().dimension();
     246             :         const Side side = directional_element_id.direction().side();
     247             :         // Extents of coupling data: mesh with ghost points, sliced
     248             :         // in direction 'dimension'
     249             :         Index<Dim> ghost_zone_extents = ghost_extents;
     250             :         ghost_zone_extents[dimension] = num_ghost_zones;
     251             :         // These loops only work in 3D; needs fixing for other dimensions
     252             :         for (size_t i = 0; i < ghost_zone_extents[0]; ++i) {
     253             :           for (size_t j = 0; j < ghost_zone_extents[1]; ++j) {
     254             :             for (size_t k = 0; k < ghost_zone_extents[2]; ++k) {
     255             :               Index<Dim> index_3d{i, j, k};
     256             :               // Index of point in ghost zone data
     257             :               const size_t ghost_index =
     258             :                   collapsed_index(index_3d, ghost_zone_extents);
     259             :               // Index of corresponding live point within the full
     260             :               // mesh, with ghost zones.
     261             :               // For two ghost zones e.g., the 1d mesh looks like
     262             :               //        x x o o o o o o x x
     263             :               // with neighbors
     264             :               //  o o o o o x x     x x o o o o o
     265             :               // with x = GZ, o = live points. On the lower face,
     266             :               // we thus add data from the first ghost point to
     267             :               // index num_ghost_zones. On the upper face, the first
     268             :               // ghost point goes to index local_extents (the size of
     269             :               // the mesh without ghost zone).
     270             :               index_3d[dimension] =
     271             :                   (side == Side::Lower)
     272             :                       ? index_3d[dimension] + num_ghost_zones
     273             :                       : local_extents[dimension] + index_3d[dimension];
     274             :               const size_t extended_index =
     275             :                   collapsed_index(index_3d, ghost_extents);
     276             :               // Add gathered data to existing coupling terms
     277             :               coupling_tilde_tau->get()[extended_index] +=
     278             :                   coupling_data.coupling_tilde_tau.at(directional_element_id)
     279             :                       .value()[ghost_index];
     280             :               coupling_tilde_rho_ye->get()[extended_index] +=
     281             :                   coupling_data.coupling_tilde_rho_ye.at(directional_element_id)
     282             :                       .value()[ghost_index];
     283             :               for (size_t d = 0; d < Dim; d++) {
     284             :                 coupling_tilde_s->get(d)[extended_index] +=
     285             :                     coupling_data.coupling_tilde_s.at(directional_element_id)
     286             :                         .value()
     287             :                         .get(d)[ghost_index];
     288             :               }
     289             :             }
     290             :           }
     291             :         }
     292             :       }
     293             :     }
     294             :   }
     295             : };
     296             : 
     297             : /// Action responsible for the Send operation of ghost
     298             : /// zone communication for Monte-Carlo transport.
     299             : /// If CommStep == PreStep, this sends the fluid and
     300             : /// metric variables needed for evolution of MC packets.
     301             : /// If CommStep == PostStep, this sends packets that
     302             : /// have moved from one element to another as well
     303             : /// as coupling information from the ghost zone to the
     304             : /// live points.
     305             : template <size_t Dim, bool LocalTimeStepping,
     306             :           Particles::MonteCarlo::CommunicationStep CommStep>
     307           1 : struct SendDataForMcCommunication {
     308           0 :   using inbox_tags =
     309             :       tmpl::list<Particles::MonteCarlo::McGhostZoneDataInboxTag<Dim, CommStep>>;
     310             : 
     311             :   template <typename DbTags, typename... InboxTags, typename ArrayIndex,
     312             :             typename ActionList, typename ParallelComponent,
     313             :             typename Metavariables>
     314           0 :   static Parallel::iterable_action_return_t apply(
     315             :       db::DataBox<DbTags>& box, tuples::TaggedTuple<InboxTags...>& /*inboxes*/,
     316             :       Parallel::GlobalCache<Metavariables>& cache,
     317             :       const ArrayIndex& /*array_index*/, const ActionList /*meta*/,
     318             :       const ParallelComponent* const /*meta*/) {
     319             :     static_assert(not LocalTimeStepping,
     320             :                   "Monte Carlo is not tested with local time stepping.");
     321             : 
     322             :     ASSERT(db::get<evolution::dg::subcell::Tags::ActiveGrid>(box) ==
     323             :                evolution::dg::subcell::ActiveGrid::Subcell,
     324             :            "The SendDataForReconstructionPreStep action in MC "
     325             :            "can only be called when Subcell is the active scheme.");
     326             :     db::mutate<Particles::MonteCarlo::Tags::McGhostZoneDataTag<Dim>>(
     327             :         [](const auto ghost_data_ptr) {
     328             :           // Clear the previous neighbor data and add current local data
     329             :           ghost_data_ptr->clear();
     330             :         },
     331             :         make_not_null(&box));
     332             : 
     333             :     auto& receiver_proxy =
     334             :         Parallel::get_parallel_component<ParallelComponent>(cache);
     335             :     const size_t ghost_zone_size = 1;
     336             : 
     337             :     const Element<Dim>& element = db::get<::domain::Tags::Element<Dim>>(box);
     338             :     const Mesh<Dim>& subcell_mesh =
     339             :         db::get<evolution::dg::subcell::Tags::Mesh<Dim>>(box);
     340             :     const TimeStepId& time_step_id = db::get<::Tags::TimeStepId>(box);
     341             :     Index<Dim> extents_with_ghost_zone = subcell_mesh.extents();
     342             :     for (size_t d = 0; d < Dim; d++) {
     343             :       extents_with_ghost_zone[d] += 2 * ghost_zone_size;
     344             :     }
     345             : 
     346             :     // Fill volume data that should be fed to neighbor ghost zones
     347             :     // PreStep, we send fluid data from the live points to the ghost points.
     348             :     // PostStep, we send coupling data from the ghost points to the live
     349             :     // points (note the different DataVector sizes; the PostStep data has
     350             :     // different dimensions because it is taken from DataVectors including
     351             :     // ghost points.
     352             :     DataVector volume_data_to_slice =
     353             :         CommStep == Particles::MonteCarlo::CommunicationStep::PreStep
     354             :             ? db::mutate_apply(GhostDataMutatorPreStep{}, make_not_null(&box))
     355             :             : db::mutate_apply(GhostDataMutatorPostStep<Dim>{},
     356             :                                make_not_null(&box));
     357             :     const DirectionMap<Dim, DataVector> all_sliced_data =
     358             :         CommStep == Particles::MonteCarlo::CommunicationStep::PreStep
     359             :             ? evolution::dg::subcell::slice_data(
     360             :                   volume_data_to_slice, subcell_mesh.extents(), ghost_zone_size,
     361             :                   element.internal_boundaries(), 0,
     362             :                   db::get<evolution::dg::subcell::Tags::
     363             :                               InterpolatorsFromFdToNeighborFd<Dim>>(box))
     364             :             : evolution::dg::subcell::slice_data(
     365             :                   volume_data_to_slice, extents_with_ghost_zone,
     366             :                   ghost_zone_size, element.internal_boundaries(), 0,
     367             :                   db::get<evolution::dg::subcell::Tags::
     368             :                               InterpolatorsFromFdToNeighborFd<Dim>>(box));
     369             :     const DirectionMap<Dim, std::vector<Particles::MonteCarlo::Packet>>
     370             :         all_packets_ghost_zone =
     371             :             CommStep == Particles::MonteCarlo::CommunicationStep::PostStep
     372             :                 ? db::mutate_apply(GhostDataMcPackets<Dim>{},
     373             :                                    make_not_null(&box))
     374             :                 : DirectionMap<Dim,
     375             :                                std::vector<Particles::MonteCarlo::Packet>>{};
     376             : 
     377             :     for (const auto& [direction, neighbors_in_direction] :
     378             :          element.neighbors()) {
     379             :       for (const ElementId<Dim>& neighbor : neighbors_in_direction) {
     380             :         const auto& orientation = neighbors_in_direction.orientation(neighbor);
     381             :         const auto direction_from_neighbor = orientation(direction.opposite());
     382             :         std::optional<std::vector<Particles::MonteCarlo::Packet>>
     383             :             packets_to_send = std::nullopt;
     384             :         DataVector subcell_data_to_send{};
     385             : 
     386             :         // PreStep and PostStep both send slice data to neighboring domains
     387             :         const auto& sliced_data_in_direction = all_sliced_data.at(direction);
     388             :         subcell_data_to_send = DataVector{sliced_data_in_direction.size()};
     389             :         std::copy(sliced_data_in_direction.begin(),
     390             :                   sliced_data_in_direction.end(), subcell_data_to_send.begin());
     391             :         if (CommStep == Particles::MonteCarlo::CommunicationStep::PostStep) {
     392             :           packets_to_send = all_packets_ghost_zone.at(direction);
     393             :           if (packets_to_send.value().empty()) {
     394             :             packets_to_send = std::nullopt;
     395             :           }
     396             :         }
     397             : 
     398             :         McGhostZoneData<Dim> data{subcell_data_to_send, packets_to_send};
     399             : 
     400             :         Parallel::receive_data<
     401             :             Particles::MonteCarlo::McGhostZoneDataInboxTag<Dim, CommStep>>(
     402             :             receiver_proxy[neighbor], time_step_id,
     403             :             std::pair{DirectionalId<Dim>{direction_from_neighbor, element.id()},
     404             :                       std::move(data)});
     405             :       }
     406             :     }
     407             :     return {Parallel::AlgorithmExecution::Continue, std::nullopt};
     408             :   }
     409             : };
     410             : 
     411             : /// Action responsible for the Receive operation of ghost
     412             : /// zone communication for Monte-Carlo transport.
     413             : /// If CommStep == PreStep, this gets the fluid and
     414             : /// metric variables needed for evolution of MC packets.
     415             : /// If CommStep == PostStep, this gets packets that
     416             : /// have moved into the current element.
     417             : /// The current code does not yet deal with data required
     418             : /// to calculate the backreaction on the fluid.
     419             : template <size_t Dim, CommunicationStep CommStep>
     420           1 : struct ReceiveDataForMcCommunication {
     421             :   template <typename DbTags, typename... InboxTags, typename ArrayIndex,
     422             :             typename ActionList, typename ParallelComponent,
     423             :             typename Metavariables>
     424           0 :   static Parallel::iterable_action_return_t apply(
     425             :       db::DataBox<DbTags>& box, tuples::TaggedTuple<InboxTags...>& inboxes,
     426             :       const Parallel::GlobalCache<Metavariables>& /*cache*/,
     427             :       const ArrayIndex& /*array_index*/, const ActionList /*meta*/,
     428             :       const ParallelComponent* const /*meta*/) {
     429             :     const Element<Dim>& element = db::get<::domain::Tags::Element<Dim>>(box);
     430             :     const auto number_of_expected_messages = element.neighbors().size();
     431             :     if (UNLIKELY(number_of_expected_messages == 0)) {
     432             :       // We have no neighbors, so just continue without doing any work
     433             :       return {Parallel::AlgorithmExecution::Continue, std::nullopt};
     434             :     }
     435             : 
     436             :     const auto& current_time_step_id = db::get<::Tags::TimeStepId>(box);
     437             :     std::map<TimeStepId, DirectionalIdMap<
     438             :                              Dim, Particles::MonteCarlo::McGhostZoneData<Dim>>>&
     439             :         inbox = tuples::get<Particles::MonteCarlo::McGhostZoneDataInboxTag<
     440             :             Metavariables::volume_dim, CommStep>>(inboxes);
     441             :     const auto& received = inbox.find(current_time_step_id);
     442             :     // Check we have at least some data from correct time, and then check that
     443             :     // we have received all data
     444             :     if (received == inbox.end() or
     445             :         received->second.size() != number_of_expected_messages) {
     446             :       return {Parallel::AlgorithmExecution::Retry, std::nullopt};
     447             :     }
     448             : 
     449             :     // Now that we have received all the data, copy it over as needed.
     450             :     DirectionalIdMap<Dim, Particles::MonteCarlo::McGhostZoneData<Dim>>
     451             :         received_data = std::move(inbox[current_time_step_id]);
     452             :     inbox.erase(current_time_step_id);
     453             : 
     454             :     // Copy the received PreStep data in the MortarData container
     455             :     if (CommStep == Particles::MonteCarlo::CommunicationStep::PreStep) {
     456             :       db::mutate<Particles::MonteCarlo::Tags::MortarDataTag<Dim>>(
     457             :           [&element,
     458             :            &received_data](const gsl::not_null<MortarData<Dim>*> mortar_data) {
     459             :             for (const auto& [direction, neighbors_in_direction] :
     460             :                  element.neighbors()) {
     461             :               for (const auto& neighbor : neighbors_in_direction) {
     462             :                 DirectionalId<Dim> directional_element_id{direction, neighbor};
     463             :                 const DataVector& received_data_direction =
     464             :                     received_data[directional_element_id]
     465             :                         .ghost_zone_hydro_variables;
     466             :                 if (mortar_data->rest_mass_density[directional_element_id] ==
     467             :                     std::nullopt) {
     468             :                   continue;
     469             :                 }
     470             :                 const size_t mortar_data_size =
     471             :                     mortar_data->rest_mass_density[directional_element_id]
     472             :                         .value()
     473             :                         .size();
     474             :                 ASSERT(received_data_direction.size() == mortar_data_size * 4,
     475             :                        "Inconsistent sizes between inbox and mortar data");
     476             :                 std::copy(received_data_direction.data(),
     477             :                           std::next(received_data_direction.data(),
     478             :                                     static_cast<int>(mortar_data_size)),
     479             :                           mortar_data->rest_mass_density[directional_element_id]
     480             :                               .value()
     481             :                               .data());
     482             :                 std::copy(std::next(received_data_direction.data(),
     483             :                                     static_cast<int>(mortar_data_size)),
     484             :                           std::next(received_data_direction.data(),
     485             :                                     static_cast<int>(mortar_data_size * 2)),
     486             :                           mortar_data->electron_fraction[directional_element_id]
     487             :                               .value()
     488             :                               .data());
     489             :                 std::copy(std::next(received_data_direction.data(),
     490             :                                     static_cast<int>(mortar_data_size * 2)),
     491             :                           std::next(received_data_direction.data(),
     492             :                                     static_cast<int>(mortar_data_size * 3)),
     493             :                           mortar_data->temperature[directional_element_id]
     494             :                               .value()
     495             :                               .data());
     496             :                 std::copy(std::next(received_data_direction.data(),
     497             :                                     static_cast<int>(mortar_data_size * 3)),
     498             :                           std::next(received_data_direction.data(),
     499             :                                     static_cast<int>(mortar_data_size * 4)),
     500             :                           mortar_data
     501             :                               ->cell_light_crossing_time[directional_element_id]
     502             :                               .value()
     503             :                               .data());
     504             :               }
     505             :             }
     506             :           },
     507             :           make_not_null(&box));
     508             :     } else {
     509             :       const Mesh<Dim>& subcell_mesh =
     510             :           db::get<evolution::dg::subcell::Tags::Mesh<Dim>>(box);
     511             :       db::mutate<Particles::MonteCarlo::Tags::GhostZoneCouplingDataTag<Dim>,
     512             :                  Particles::MonteCarlo::Tags::PacketsOnElement>(
     513             :           [&element, &received_data, &subcell_mesh](
     514             :               const gsl::not_null<GhostZoneCouplingData<Dim>*> coupling_data,
     515             :               const gsl::not_null<std::vector<Particles::MonteCarlo::Packet>*>
     516             :                   packet_list) {
     517             :             // Two parts here: first deal with coupling data brought back
     518             :             // from ghost zones to live points; then handle packet
     519             :             // communication.
     520             :             const Index<Dim>& extents = subcell_mesh.extents();
     521             :             for (const auto& [direction, neighbors_in_direction] :
     522             :                  element.neighbors()) {
     523             :               for (const auto& neighbor : neighbors_in_direction) {
     524             :                 DirectionalId<Dim> directional_element_id{direction, neighbor};
     525             :                 // Move boundary data to the coupling_data structure for later
     526             :                 // use
     527             :                 const DataVector& received_data_direction =
     528             :                     received_data[directional_element_id]
     529             :                         .ghost_zone_hydro_variables;
     530             :                 if (coupling_data->coupling_tilde_tau[directional_element_id] !=
     531             :                     std::nullopt) {
     532             :                   const size_t coupling_data_size =
     533             :                       coupling_data->coupling_tilde_tau[directional_element_id]
     534             :                           .value()
     535             :                           .size();
     536             :                   ASSERT(received_data_direction.size() ==
     537             :                              coupling_data_size * (2 + Dim),
     538             :                          "Inconsistent sizes between inbox and coupling data");
     539             :                   std::copy(
     540             :                       received_data_direction.data(),
     541             :                       std::next(received_data_direction.data(),
     542             :                                 static_cast<int>(coupling_data_size)),
     543             :                       coupling_data->coupling_tilde_tau[directional_element_id]
     544             :                           .value()
     545             :                           .data());
     546             :                   std::copy(std::next(received_data_direction.data(),
     547             :                                       static_cast<int>(coupling_data_size)),
     548             :                             std::next(received_data_direction.data(),
     549             :                                       static_cast<int>(coupling_data_size * 2)),
     550             :                             coupling_data
     551             :                                 ->coupling_tilde_rho_ye[directional_element_id]
     552             :                                 .value()
     553             :                                 .data());
     554             :                   for (size_t d = 0; d < Dim; d++) {
     555             :                     std::copy(
     556             :                         std::next(
     557             :                             received_data_direction.data(),
     558             :                             static_cast<int>(coupling_data_size * (2 + d))),
     559             :                         std::next(
     560             :                             received_data_direction.data(),
     561             :                             static_cast<int>(coupling_data_size * (3 + d))),
     562             :                         coupling_data->coupling_tilde_s[directional_element_id]
     563             :                             .value()
     564             :                             .get(d)
     565             :                             .data());
     566             :                   }
     567             :                 }
     568             :                 // Get packet data
     569             :                 std::optional<std::vector<Particles::MonteCarlo::Packet>>&
     570             :                     received_data_packets =
     571             :                         received_data[directional_element_id]
     572             :                             .packets_entering_this_element;
     573             :                 if (received_data_packets == std::nullopt) {
     574             :                   continue;
     575             :                 } else {
     576             :                   const size_t n_packets = received_data_packets.value().size();
     577             :                   for (size_t p = 0; p < n_packets; p++) {
     578             :                     // Find closest grid point to received packet at current
     579             :                     // time, using extents for live points only.
     580             :                     Packet& packet = received_data_packets.value()[p];
     581             :                     Index<Dim> closest_point_index_3d;
     582             :                     for (size_t d = 0; d < Dim; d++) {
     583             :                       closest_point_index_3d[d] =
     584             :                           std::floor((packet.coordinates[d] + 1.0) *
     585             :                                      static_cast<double>(extents[d]) / 2.0);
     586             :                       if (UNLIKELY(closest_point_index_3d[d] == extents[d])) {
     587             :                         closest_point_index_3d[d]--;
     588             :                       }
     589             :                     }
     590             :                     packet.index_of_closest_grid_point =
     591             :                         collapsed_index(closest_point_index_3d, extents);
     592             :                     // Now add packets to list in current element
     593             :                     packet_list->push_back(received_data_packets.value()[p]);
     594             :                   }
     595             :                 }
     596             :               }
     597             :             }
     598             :           },
     599             :           make_not_null(&box));
     600             :       // Combine ghost zone data with live points data. Currently only done in
     601             :       // 3D
     602             :       if constexpr (Dim == 3) {
     603             :         db::mutate_apply(CombineCouplingDataPostStep<Dim>{},
     604             :                          make_not_null(&box));
     605             :       }
     606             :     }
     607             :     return {Parallel::AlgorithmExecution::Continue, std::nullopt};
     608             :   }
     609             : };
     610             : 
     611             : }  // namespace Particles::MonteCarlo::Actions

Generated by: LCOV version 1.14