LCOV - code coverage report
Current view: top level - src/Particle - ParticleAttrib.hpp (source / functions) Coverage Total Hit
Test: final_report.info Lines: 70.6 % 119 84
Test Date: 2025-07-17 08:40:11 Functions: 51.9 % 237 123

            Line data    Source code
       1              : //
       2              : // Class ParticleAttrib
       3              : //   Templated class for all particle attribute classes.
       4              : //
       5              : //   This templated class is used to represent a single particle attribute.
       6              : //   An attribute is one data element within a particle object, and is
       7              : //   stored as a Kokkos::View. This class stores the type information for the
       8              : //   attribute, and provides methods to create and destroy new items, and
       9              : //   to perform operations involving this attribute with others.
      10              : //
      11              : //   ParticleAttrib is the primary element involved in expressions for
      12              : //   particles (just as BareField is the primary element there).  This file
      13              : //   defines the necessary templated classes and functions to make
      14              : //   ParticleAttrib a capable expression-template participant.
      15              : //
      16              : #include "Ippl.h"
      17              : 
      18              : #include "Communicate/DataTypes.h"
      19              : 
      20              : #include "Utility/IpplTimings.h"
      21              : 
      22              : namespace ippl {
      23              : 
      24              :     template <typename T, class... Properties>
      25          252 :     void ParticleAttrib<T, Properties...>::create(size_type n) {
      26          252 :         size_type required = *(this->localNum_mp) + n;
      27          252 :         if (this->size() < required) {
      28          252 :             int overalloc = Comm->getDefaultOverallocation();
      29          252 :             this->realloc(required * overalloc);
      30              :         }
      31          252 :     }
      32              : 
      33              :     template <typename T, class... Properties>
      34           24 :     void ParticleAttrib<T, Properties...>::destroy(const hash_type& deleteIndex,
      35              :                                                    const hash_type& keepIndex,
      36              :                                                    size_type invalidCount) {
      37              :         // Replace all invalid particles in the valid region with valid
      38              :         // particles in the invalid region
      39              :         using policy_type = Kokkos::RangePolicy<execution_space>;
      40           24 :         Kokkos::parallel_for(
      41           24 :             "ParticleAttrib::destroy()", policy_type(0, invalidCount),
      42        12048 :             KOKKOS_CLASS_LAMBDA(const size_t i) {
      43            0 :                 dview_m(deleteIndex(i)) = dview_m(keepIndex(i));
      44              :             });
      45           24 :     }
      46              : 
      47              :     template <typename T, class... Properties>
      48            0 :     void ParticleAttrib<T, Properties...>::pack(const hash_type& hash) {
      49            0 :         auto size = hash.extent(0);
      50            0 :         if (buf_m.extent(0) < size) {
      51            0 :             int overalloc = Comm->getDefaultOverallocation();
      52            0 :             Kokkos::realloc(buf_m, size * overalloc);
      53              :         }
      54              : 
      55              :         using policy_type = Kokkos::RangePolicy<execution_space>;
      56            0 :         Kokkos::parallel_for(
      57            0 :             "ParticleAttrib::pack()", policy_type(0, size),
      58            0 :             KOKKOS_CLASS_LAMBDA(const size_t i) { buf_m(i) = dview_m(hash(i)); });
      59            0 :         Kokkos::fence();
      60            0 :     }
      61              : 
      62              :     template <typename T, class... Properties>
      63            0 :     void ParticleAttrib<T, Properties...>::unpack(size_type nrecvs) {
      64            0 :         auto size          = dview_m.extent(0);
      65            0 :         size_type required = *(this->localNum_mp) + nrecvs;
      66            0 :         if (size < required) {
      67            0 :             int overalloc = Comm->getDefaultOverallocation();
      68            0 :             this->resize(required * overalloc);
      69              :         }
      70              : 
      71            0 :         size_type count   = *(this->localNum_mp);
      72              :         using policy_type = Kokkos::RangePolicy<execution_space>;
      73            0 :         Kokkos::parallel_for(
      74            0 :             "ParticleAttrib::unpack()", policy_type(0, nrecvs),
      75            0 :             KOKKOS_CLASS_LAMBDA(const size_t i) { dview_m(count + i) = buf_m(i); });
      76            0 :         Kokkos::fence();
      77            0 :     }
      78              : 
      79              :     template <typename T, class... Properties>
      80              :     // KOKKOS_INLINE_FUNCTION
      81           48 :     ParticleAttrib<T, Properties...>& ParticleAttrib<T, Properties...>::operator=(T x) {
      82              :         using policy_type = Kokkos::RangePolicy<execution_space>;
      83           48 :         Kokkos::parallel_for(
      84           48 :             "ParticleAttrib::operator=()", policy_type(0, *(this->localNum_mp)),
      85         3936 :             KOKKOS_CLASS_LAMBDA(const size_t i) { dview_m(i) = x; });
      86           48 :         return *this;
      87              :     }
      88              : 
      89              :     template <typename T, class... Properties>
      90              :     template <typename E, size_t N>
      91              :     // KOKKOS_INLINE_FUNCTION
      92            0 :     ParticleAttrib<T, Properties...>& ParticleAttrib<T, Properties...>::operator=(
      93              :         detail::Expression<E, N> const& expr) {
      94              :         using capture_type = detail::CapturedExpression<E, N>;
      95            0 :         capture_type expr_ = reinterpret_cast<const capture_type&>(expr);
      96              : 
      97              :         using policy_type = Kokkos::RangePolicy<execution_space>;
      98            0 :         Kokkos::parallel_for(
      99            0 :             "ParticleAttrib::operator=()", policy_type(0, *(this->localNum_mp)),
     100            0 :             KOKKOS_CLASS_LAMBDA(const size_t i) { dview_m(i) = expr_(i); });
     101            0 :         return *this;
     102              :     }
     103              : 
     104              :     template <typename T, class... Properties>
     105              :     template <typename Field, class PT, typename policy_type>
     106           24 :     void ParticleAttrib<T, Properties...>::scatter(
     107              :         Field& f, const ParticleAttrib<Vector<PT, Field::dim>, Properties...>& pp,
     108              :         policy_type iteration_policy, hash_type hash_array) const {
     109           24 :         constexpr unsigned Dim = Field::dim;
     110              :         using PositionType     = typename Field::Mesh_t::value_type;
     111              : 
     112           24 :         static IpplTimings::TimerRef scatterTimer = IpplTimings::getTimer("scatter");
     113           24 :         IpplTimings::startTimer(scatterTimer);
     114              :         using view_type = typename Field::view_type;
     115           24 :         view_type view  = f.getView();
     116              : 
     117              :         using mesh_type       = typename Field::Mesh_t;
     118           24 :         const mesh_type& mesh = f.get_mesh();
     119              : 
     120              :         using vector_type = typename mesh_type::vector_type;
     121              :         using value_type  = typename ParticleAttrib<T, Properties...>::value_type;
     122              : 
     123           24 :         const vector_type& dx     = mesh.getMeshSpacing();
     124           24 :         const vector_type& origin = mesh.getOrigin();
     125           24 :         const vector_type invdx   = 1.0 / dx;
     126              : 
     127           24 :         const FieldLayout<Dim>& layout = f.getLayout();
     128           24 :         const NDIndex<Dim>& lDom       = layout.getLocalNDIndex();
     129           24 :         const int nghost               = f.getNghost();
     130              : 
     131              :         //using policy_type = Kokkos::RangePolicy<execution_space>;
     132           24 :         const bool useHashView = hash_array.extent(0) > 0;
     133           24 :         if (useHashView && (iteration_policy.end() > hash_array.extent(0))) {
     134            0 :             Inform m("scatter");
     135            0 :             m << "Hash array was passed to scatter, but size does not match iteration policy." << endl;
     136            0 :             ippl::Comm->abort();
     137            0 :         }
     138           24 :         Kokkos::parallel_for(
     139              :             "ParticleAttrib::scatter", iteration_policy,
     140         1968 :             KOKKOS_CLASS_LAMBDA(const size_t idx) {
     141              :                 // map index to possible hash_map
     142         1920 :                 size_t mapped_idx = useHashView ? hash_array(idx) : idx;
     143              : 
     144              :                 // find nearest grid point
     145         1920 :                 vector_type l                        = (pp(mapped_idx) - origin) * invdx + 0.5;
     146         1920 :                 Vector<int, Field::dim> index        = l;
     147         1920 :                 Vector<PositionType, Field::dim> whi = l - index;
     148         1920 :                 Vector<PositionType, Field::dim> wlo = 1.0 - whi;
     149              : 
     150         1920 :                 Vector<size_t, Field::dim> args = index - lDom.first() + nghost;
     151              : 
     152              :                 // scatter
     153         1920 :                 const value_type& val = dview_m(mapped_idx);
     154         1920 :                 detail::scatterToField(std::make_index_sequence<1 << Field::dim>{}, view, wlo, whi,
     155              :                                        args, val);
     156         1920 :             });
     157           24 :         IpplTimings::stopTimer(scatterTimer);
     158              : 
     159           24 :         static IpplTimings::TimerRef accumulateHaloTimer = IpplTimings::getTimer("accumulateHalo");
     160           24 :         IpplTimings::startTimer(accumulateHaloTimer);
     161           24 :         f.accumulateHalo();
     162           24 :         IpplTimings::stopTimer(accumulateHaloTimer);
     163           24 :     }
     164              : 
     165              :     template <typename T, class... Properties>
     166              :     template <typename Field, typename P2>
     167           12 :     void ParticleAttrib<T, Properties...>::gather(
     168              :         Field& f, const ParticleAttrib<Vector<P2, Field::dim>, Properties...>& pp,
     169              :         const bool addToAttribute) {
     170           12 :         constexpr unsigned Dim = Field::dim;
     171              :         using PositionType     = typename Field::Mesh_t::value_type;
     172              : 
     173           12 :         static IpplTimings::TimerRef fillHaloTimer = IpplTimings::getTimer("fillHalo");
     174           12 :         IpplTimings::startTimer(fillHaloTimer);
     175           12 :         f.fillHalo();
     176           12 :         IpplTimings::stopTimer(fillHaloTimer);
     177              : 
     178           12 :         static IpplTimings::TimerRef gatherTimer = IpplTimings::getTimer("gather");
     179           12 :         IpplTimings::startTimer(gatherTimer);
     180           12 :         const typename Field::view_type view = f.getView();
     181              : 
     182              :         using mesh_type       = typename Field::Mesh_t;
     183           12 :         const mesh_type& mesh = f.get_mesh();
     184              : 
     185              :         using vector_type = typename mesh_type::vector_type;
     186              : 
     187           12 :         const vector_type& dx     = mesh.getMeshSpacing();
     188           12 :         const vector_type& origin = mesh.getOrigin();
     189           12 :         const vector_type invdx   = 1.0 / dx;
     190              : 
     191           12 :         const FieldLayout<Dim>& layout = f.getLayout();
     192           12 :         const NDIndex<Dim>& lDom       = layout.getLocalNDIndex();
     193           12 :         const int nghost               = f.getNghost();
     194              : 
     195              :         using policy_type = Kokkos::RangePolicy<execution_space>;
     196           12 :         Kokkos::parallel_for(
     197           12 :             "ParticleAttrib::gather", policy_type(0, *(this->localNum_mp)),
     198          408 :             KOKKOS_CLASS_LAMBDA(const size_t idx) {
     199              :                 // find nearest grid point
     200          384 :                 vector_type l                        = (pp(idx) - origin) * invdx + 0.5;
     201          384 :                 Vector<int, Field::dim> index        = l;
     202          384 :                 Vector<PositionType, Field::dim> whi = l - index;
     203          384 :                 Vector<PositionType, Field::dim> wlo = 1.0 - whi;
     204              : 
     205          384 :                 Vector<size_t, Field::dim> args = index - lDom.first() + nghost;
     206              : 
     207              :                 // gather
     208            0 :                 value_type gathered = detail::gatherFromField(std::make_index_sequence<1 << Field::dim>{},
     209          384 :                                                               view, wlo, whi, args);
     210          384 :                 if (addToAttribute) {
     211            0 :                     dview_m(idx) += gathered;
     212              :                 } else {
     213          768 :                     dview_m(idx)  = gathered;
     214              :                 }
     215          384 :             });
     216           12 :         IpplTimings::stopTimer(gatherTimer);
     217           12 :     }
     218              : 
     219              :     /*
     220              :      * Non-class function
     221              :      *
     222              :      */
     223              : 
     224              :     /**
     225              :      * @brief Non-class interface for scattering particle attribute data onto a field.
     226              :      *
     227              :      * This overload preserves legacy functionality by providing a default iteration policy.
     228              :      * It calls the member scatter() with a default Kokkos::RangePolicy.
     229              :      * 
     230              :      * @note The default behaviour is to scatter all particles without any custom index mapping.
     231              :      *
     232              :      * @tparam Attrib1 The type of the particle attribute.
     233              :      * @tparam Field The type of the field.
     234              :      * @tparam Attrib2 The type of the particle position attribute.
     235              :      * @tparam policy_type (Default: `Kokkos::RangePolicy<typename Field::execution_space>`)
     236              :      * @param attrib The particle attribute to scatter.
     237              :      * @param f The field onto which the data is scattered.
     238              :      * @param pp The ParticleAttrib representing particle positions.
     239              :      */
     240              :     template <typename Attrib1, typename Field, typename Attrib2, 
     241              :                 typename policy_type = Kokkos::RangePolicy<typename Field::execution_space>>
     242           24 :     inline void scatter(const Attrib1& attrib, Field& f, const Attrib2& pp) {
     243           24 :         attrib.scatter(f, pp, policy_type(0, attrib.getParticleCount())); 
     244           24 :     }
     245              : 
     246              :     /**
     247              :      * @brief Non-class interface for scattering with a custom iteration policy and optional index array.
     248              :      *
     249              :      * This overload allows the caller to specify a custom `Kokkos::range_policy` and an optional
     250              :      * `ippl::hash_type` array. It forwards the parameters to the member scatter() function.
     251              :      * 
     252              :      * @note See ParticleAttrib::scatter() for more information on the custom iteration functionality.
     253              :      *
     254              :      * @tparam Attrib1 The type of the particle attribute.
     255              :      * @tparam Field The type of the field.
     256              :      * @tparam Attrib2 The type of the particle position attribute.
     257              :      * @tparam policy_type (Default: `Kokkos::RangePolicy<typename Field::execution_space>`)
     258              :      * @param attrib The particle attribute to scatter.
     259              :      * @param f The field onto which the data is scattered.
     260              :      * @param pp The ParticleAttrib representing particle positions.
     261              :      * @param iteration_policy A custom `Kokkos::range_policy` defining the iteration range.
     262              :      * @param hash_array An optional `ippl::hash_type` array for index mapping.
     263              :      */
     264              :     template <typename Attrib1, typename Field, typename Attrib2, 
     265              :                 typename policy_type = Kokkos::RangePolicy<typename Field::execution_space>>
     266              :     inline void scatter(const Attrib1& attrib, Field& f, const Attrib2& pp, 
     267              :                         policy_type iteration_policy, typename Attrib1::hash_type hash_array = {}) {
     268              :         attrib.scatter(f, pp, iteration_policy, hash_array);
     269              :     }
     270              : 
     271              :     /**
     272              :      * @brief Non-class interface for gathering field data into a particle attribute.
     273              :      * 
     274              :      * This interface calls the member ParticleAttrib::gather() function with the provided
     275              :      * parameters and preserving legacy behavior by assigning `addToAttribute` a default value.
     276              :      * 
     277              :      * @note See ParticleAttrib::gather() for more information on the behavior of `addToAttribute`.
     278              :      * 
     279              :      * @tparam Attrib1 The type of the particle attribute.
     280              :      * @tparam Field The type of the field.
     281              :      * @tparam Attrib2 The type of the particle position attribute.
     282              :      * @param attrib The particle attribute to gather data into.
     283              :      * @param f The field from which data is gathered.
     284              :      * @param pp The ParticleAttrib representing particle positions.
     285              :      * @param addToAttribute If true, the gathered field value is added to the current attribute value;
     286              :      *                       otherwise, the attribute value is overwritten.
     287              :      */
     288              :     template <typename Attrib1, typename Field, typename Attrib2>
     289           12 :     inline void gather(Attrib1& attrib, Field& f, const Attrib2& pp, 
     290              :                         const bool addToAttribute = false) {
     291           12 :         attrib.gather(f, pp, addToAttribute);
     292           12 :     }
     293              : 
     294              : #define DefineParticleReduction(fun, name, op, MPI_Op)            \
     295              :     template <typename T, class... Properties>                    \
     296              :     T ParticleAttrib<T, Properties...>::name() {                  \
     297              :         T temp            = 0.0;                                  \
     298              :         using policy_type = Kokkos::RangePolicy<execution_space>; \
     299              :         Kokkos::parallel_reduce(                                  \
     300              :             "fun", policy_type(0, *(this->localNum_mp)),          \
     301              :             KOKKOS_CLASS_LAMBDA(const size_t i, T& valL) {        \
     302              :                 T myVal = dview_m(i);                             \
     303              :                 op;                                               \
     304              :             },                                                    \
     305              :             Kokkos::fun<T>(temp));                                \
     306              :         T globaltemp = 0.0;                                       \
     307              :         Comm->allreduce(temp, globaltemp, 1, MPI_Op<T>());        \
     308              :         return globaltemp;                                        \
     309              :     }
     310              : 
     311          432 :     DefineParticleReduction(Sum, sum, valL += myVal, std::plus)
     312              :     DefineParticleReduction(Max, max, if (myVal > valL) valL = myVal, std::greater)
     313              :     DefineParticleReduction(Min, min, if (myVal < valL) valL = myVal, std::less)
     314              :     DefineParticleReduction(Prod, prod, valL *= myVal, std::multiplies)
     315              : }  // namespace ippl
        

Generated by: LCOV version 2.0-1