LCOV - code coverage report
Current view: top level - src/Particle - ParticleBase.hpp (source / functions) Coverage Total Hit
Test: final_report.info Lines: 61.4 % 140 86
Test Date: 2025-07-18 09:50:00 Functions: 36.0 % 734 264

            Line data    Source code
       1              : //
       2              : // Class ParticleBase
       3              : //   Base class for all user-defined particle classes.
       4              : //
       5              : //   ParticleBase is a container and manager for a set of particles.
       6              : //   The user must define a class derived from ParticleBase which describes
       7              : //   what specific data attributes the particle has (e.g., mass or charge).
       8              : //   Each attribute is an instance of a ParticleAttribute<T> class; ParticleBase
       9              : //   keeps a list of pointers to these attributes, and performs particle creation
      10              : //   and destruction.
      11              : //
      12              : //   ParticleBase is templated on the ParticleLayout mechanism for the particles.
      13              : //   This template parameter should be a class derived from ParticleLayout.
      14              : //   ParticleLayout-derived classes maintain the info on which particles are
      15              : //   located on which processor, and performs the specific communication
      16              : //   required between processors for the particles.  The ParticleLayout is
      17              : //   templated on the type and dimension of the atom position attribute, and
      18              : //   ParticleBase uses the same types for these items as the given
      19              : //   ParticleLayout.
      20              : //
      21              : //   ParticleBase and all derived classes have the following common
      22              : //   characteristics:
      23              : //       - The spatial positions of the N particles are stored in the
      24              : //         particle_position_type variable R
      25              : //       - The global index of the N particles are stored in the
      26              : //         particle_index_type variable ID
      27              : //       - A pointer to an allocated layout class.  When you construct a
      28              : //         ParticleBase, you must provide a layout instance, and ParticleBase
      29              : //         will delete this instance when it (the ParticleBase) is deleted.
      30              : //
      31              : //   To use this class, the user defines a derived class with the same
      32              : //   structure as in this example:
      33              : //
      34              : //     class UserParticles :
      35              : //              public ParticleBase< ParticleSpatialLayout<double,3> > {
      36              : //     public:
      37              : //       // attributes for this class
      38              : //       ParticleAttribute<double> rad;  // radius
      39              : //       particle_position_type    vel;  // velocity, same storage type as R
      40              : //
      41              : //       // constructor: add attributes to base class
      42              : //       UserParticles(ParticleSpatialLayout<double,2>* L) : ParticleBase(L) {
      43              : //         addAttribute(rad);
      44              : //         addAttribute(vel);
      45              : //       }
      46              : //     };
      47              : //
      48              : //   This example defines a user class with 3D position and two extra
      49              : //   attributes: a radius rad (double), and a velocity vel (a 3D Vector).
      50              : //
      51              : 
      52              : namespace ippl {
      53              : 
      54              :     template <class PLayout, typename... IP>
      55          154 :     ParticleBase<PLayout, IP...>::ParticleBase()
      56          154 :         : layout_m(nullptr)
      57          154 :         , localNum_m(0)
      58          154 :         , totalNum_m(0)
      59          154 :         , nextID_m(Comm->rank())
      60          308 :         , numNodes_m(Comm->size()) {
      61              :         if constexpr (EnableIDs) {
      62           48 :             addAttribute(ID);
      63              :         }
      64          154 :         addAttribute(R);
      65          154 :     }
      66              : 
      67              :     template <class PLayout, typename... IP>
      68          142 :     ParticleBase<PLayout, IP...>::ParticleBase(PLayout& layout)
      69          142 :         : ParticleBase() {
      70          142 :         initialize(layout);
      71          142 :     }
      72              : 
      73              :     template <class PLayout, typename... IP>
      74              :     template <typename MemorySpace>
      75          226 :     void ParticleBase<PLayout, IP...>::addAttribute(detail::ParticleAttribBase<MemorySpace>& pa) {
      76          226 :         attributes_m.template get<MemorySpace>().push_back(&pa);
      77          226 :         pa.setParticleCount(localNum_m);
      78          226 :     }
      79              : 
      80              :     template <class PLayout, typename... IP>
      81          154 :     void ParticleBase<PLayout, IP...>::initialize(PLayout& layout) {
      82              :         //         PAssert(layout_m == nullptr);
      83              : 
      84              :         // save the layout, and perform setup tasks
      85          154 :         layout_m = &layout;
      86          154 :     }
      87              : 
      88              :     template <class PLayout, typename... IP>
      89          118 :     void ParticleBase<PLayout, IP...>::create(size_type nLocal) {
      90          118 :         PAssert(layout_m != nullptr);
      91              : 
      92          118 :         if (nLocal > 0) {
      93          402 :             forAllAttributes([&]<typename Attribute>(Attribute& attribute) {
      94          120 :                 attribute->create(nLocal);
      95              :             });
      96              : 
      97              :             if constexpr (EnableIDs) {
      98              :                 // set the unique ID value for these new particles
      99              :                 using policy_type =
     100              :                     Kokkos::RangePolicy<size_type, typename particle_index_type::execution_space>;
     101           12 :                 auto pIDs     = ID.getView();
     102           12 :                 auto nextID   = this->nextID_m;
     103           12 :                 auto numNodes = this->numNodes_m;
     104           12 :                 Kokkos::parallel_for(
     105           12 :                     "ParticleBase<...>::create(size_t)", policy_type(localNum_m, nLocal),
     106        12024 :                     KOKKOS_LAMBDA(const std::int64_t i) { pIDs(i) = nextID + numNodes * i; });
     107              :                 // nextID_m += numNodes_m * (nLocal - localNum_m);
     108           12 :                 nextID_m += numNodes_m * nLocal;
     109           12 :             }
     110              : 
     111              :             // remember that we're creating these new particles
     112          118 :             localNum_m += nLocal;
     113              :         }
     114              : 
     115          118 :         Comm->allreduce(localNum_m, totalNum_m, 1, std::plus<size_type>());
     116          118 :     }
     117              : 
     118              :     template <class PLayout, typename... IP>
     119              :     void ParticleBase<PLayout, IP...>::createWithID(index_type id) {
     120              :         PAssert(layout_m != nullptr);
     121              : 
     122              :         size_type n = (id > -1) ? 1 : 0;
     123              : 
     124              :         // temporary change
     125              :         index_type tmpNextID = nextID_m;
     126              :         nextID_m             = id;
     127              :         numNodes_m           = 0;
     128              : 
     129              :         create(n);
     130              : 
     131              :         nextID_m   = tmpNextID;
     132              :         numNodes_m = Comm->size();
     133              :     }
     134              : 
     135              :     template <class PLayout, typename... IP>
     136              :     void ParticleBase<PLayout, IP...>::globalCreate(size_type nTotal) {
     137              :         PAssert(layout_m != nullptr);
     138              : 
     139              :         // Compute the number of particles local to each processor
     140              :         size_type nLocal = nTotal / numNodes_m;
     141              : 
     142              :         const size_t rank = Comm->rank();
     143              : 
     144              :         size_type rest = nTotal - nLocal * rank;
     145              :         if (rank < rest) {
     146              :             ++nLocal;
     147              :         }
     148              : 
     149              :         create(nLocal);
     150              :     }
     151              : 
     152              :     template <class PLayout, typename... IP>
     153              :     template <typename... Properties>
     154           12 :     void ParticleBase<PLayout, IP...>::destroy(const Kokkos::View<bool*, Properties...>& invalid,
     155              :                                                const size_type destroyNum) {
     156           12 :         this->internalDestroy(invalid, destroyNum);
     157              : 
     158           12 :         Comm->allreduce(localNum_m, totalNum_m, 1, std::plus<size_type>());
     159           12 :     }
     160              : 
     161              :     template <class PLayout, typename... IP>
     162              :     template <typename... Properties>
     163           12 :     void ParticleBase<PLayout, IP...>::internalDestroy(
     164              :         const Kokkos::View<bool*, Properties...>& invalid, const size_type destroyNum) {
     165           12 :         PAssert(destroyNum <= localNum_m);
     166              : 
     167              :         // If there aren't any particles to delete, do nothing
     168           12 :         if (destroyNum == 0) {
     169            0 :             return;
     170              :         }
     171              : 
     172              :         // If we're deleting all the particles, there's no point in doing
     173              :         // anything because the valid region will be empty; we only need to
     174              :         // update the particle count
     175           12 :         if (destroyNum == localNum_m) {
     176            0 :             localNum_m = 0;
     177            0 :             return;
     178              :         }
     179              : 
     180              :         using view_type       = Kokkos::View<bool*, Properties...>;
     181              :         using memory_space    = typename view_type::memory_space;
     182              :         using execution_space = typename view_type::execution_space;
     183              :         using policy_type     = Kokkos::RangePolicy<execution_space>;
     184           12 :         auto& locDeleteIndex  = deleteIndex_m.get<memory_space>();
     185           12 :         auto& locKeepIndex    = keepIndex_m.get<memory_space>();
     186              : 
     187              :         // Resize buffers, if necessary
     188           36 :         detail::runForAllSpaces([&]<typename MemorySpace>() {
     189           12 :             if (attributes_m.template get<memory_space>().size() > 0) {
     190           12 :                 int overalloc = Comm->getDefaultOverallocation();
     191           12 :                 auto& del     = deleteIndex_m.get<memory_space>();
     192           12 :                 auto& keep    = keepIndex_m.get<memory_space>();
     193           12 :                 if (del.size() < destroyNum) {
     194           12 :                     Kokkos::realloc(del, destroyNum * overalloc);
     195           12 :                     Kokkos::realloc(keep, destroyNum * overalloc);
     196              :                 }
     197              :             }
     198              :         });
     199              : 
     200              :         // Reset index buffer
     201           12 :         Kokkos::deep_copy(locDeleteIndex, -1);
     202              : 
     203              :         // Find the indices of the invalid particles in the valid region
     204           12 :         Kokkos::parallel_scan(
     205           12 :             "Scan in ParticleBase::destroy()", policy_type(0, localNum_m - destroyNum),
     206        12024 :             KOKKOS_LAMBDA(const size_t i, int& idx, const bool final) {
     207        12000 :                 if (final && invalid(i)) {
     208         6000 :                     locDeleteIndex(idx) = i;
     209              :                 }
     210        12000 :                 if (invalid(i)) {
     211         3000 :                     idx += 1;
     212              :                 }
     213              :             });
     214           12 :         Kokkos::fence();
     215              : 
     216              :         // Determine the total number of invalid particles in the valid region
     217           12 :         size_type maxDeleteIndex = 0;
     218           12 :         Kokkos::parallel_reduce(
     219           24 :             "Reduce in ParticleBase::destroy()", policy_type(0, destroyNum),
     220        12024 :             KOKKOS_LAMBDA(const size_t i, size_t& maxIdx) {
     221        12000 :                 if (locDeleteIndex(i) >= 0 && i > maxIdx) {
     222         2988 :                     maxIdx = i;
     223              :                 }
     224              :             },
     225           24 :             Kokkos::Max<size_type>(maxDeleteIndex));
     226              : 
     227              :         // Find the indices of the valid particles in the invalid region
     228           12 :         Kokkos::parallel_scan(
     229              :             "Second scan in ParticleBase::destroy()",
     230           12 :             Kokkos::RangePolicy<size_type, execution_space>(localNum_m - destroyNum, localNum_m),
     231        12024 :             KOKKOS_LAMBDA(const size_t i, int& idx, const bool final) {
     232        12000 :                 if (final && !invalid(i)) {
     233         6000 :                     locKeepIndex(idx) = i;
     234              :                 }
     235        12000 :                 if (!invalid(i)) {
     236         3000 :                     idx += 1;
     237              :                 }
     238              :             });
     239              : 
     240           12 :         Kokkos::fence();
     241              : 
     242           12 :         localNum_m -= destroyNum;
     243              : 
     244              :         // We need to delete particles in all memory spaces. If there are any attributes not stored
     245              :         // in the memory space we've already been using, we need to copy the index views to the
     246              :         // other spaces.
     247           12 :         auto filter = [&]<typename MemorySpace>() {
     248              :             return attributes_m.template get<MemorySpace>().size() > 0;
     249              :         };
     250           12 :         deleteIndex_m.copyToOtherSpaces<memory_space>(filter);
     251           12 :         keepIndex_m.copyToOtherSpaces<memory_space>(filter);
     252              : 
     253              :         // Partition the attributes into valid and invalid regions
     254              :         // NOTE: The vector elements are pointers, but we want to extract
     255              :         // the memory space from the class type, so we explicitly
     256              :         // make the lambda argument a pointer to the template parameter
     257           60 :         forAllAttributes([&]<typename Attribute>(Attribute*& attribute) {
     258              :             using att_memory_space = typename Attribute::memory_space;
     259           24 :             auto& del              = deleteIndex_m.get<att_memory_space>();
     260           24 :             auto& keep             = keepIndex_m.get<att_memory_space>();
     261           24 :             attribute->destroy(del, keep, maxDeleteIndex + 1);
     262              :         });
     263              :     }
     264              : 
     265              :     template <class PLayout, typename... IP>
     266              :     template <typename HashType>
     267            0 :     void ParticleBase<PLayout, IP...>::sendToRank(int rank, int tag,
     268              :                                                   std::vector<MPI_Request>& requests,
     269              :                                                   const HashType& hash) {
     270            0 :         size_type nSends = hash.size();
     271            0 :         requests.resize(requests.size() + 1);
     272              : 
     273            0 :         auto hashes = hash_container_type(hash, [&]<typename MemorySpace>() {
     274              :             return attributes_m.template get<MemorySpace>().size() > 0;
     275              :         });
     276            0 :         pack(hashes);
     277            0 :         detail::runForAllSpaces([&]<typename MemorySpace>() {
     278            0 :             size_type bufSize = packedSize<MemorySpace>(nSends);
     279            0 :             if (bufSize == 0) {
     280            0 :                 return;
     281              :             }
     282              : 
     283            0 :             auto buf = Comm->getBuffer<MemorySpace>(bufSize);
     284              : 
     285            0 :             Comm->isend(rank, tag++, *this, *buf, requests.back(), nSends);
     286            0 :             buf->resetWritePos();
     287            0 :         });
     288            0 :     }
     289              : 
     290              :     template <class PLayout, typename... IP>
     291            0 :     void ParticleBase<PLayout, IP...>::recvFromRank(int rank, int tag, size_type nRecvs) {
     292            0 :         detail::runForAllSpaces([&]<typename MemorySpace>() {
     293            0 :             size_type bufSize = packedSize<MemorySpace>(nRecvs);
     294            0 :             if (bufSize == 0) {
     295            0 :                 return;
     296              :             }
     297              : 
     298            0 :             auto buf = Comm->getBuffer<MemorySpace>(bufSize);
     299              : 
     300            0 :             Comm->recv(rank, tag++, *this, *buf, bufSize, nRecvs);
     301            0 :             buf->resetReadPos();
     302            0 :         });
     303            0 :         unpack(nRecvs);
     304            0 :     }
     305              : 
     306              :     template <class PLayout, typename... IP>
     307              :     template <typename Archive>
     308            0 :     void ParticleBase<PLayout, IP...>::serialize(Archive& ar, size_type nsends) {
     309              :         using memory_space = typename Archive::buffer_type::memory_space;
     310            0 :         forAllAttributes<memory_space>([&]<typename Attribute>(Attribute& att) {
     311            0 :             att->serialize(ar, nsends);
     312              :         });
     313            0 :     }
     314              : 
     315              :     template <class PLayout, typename... IP>
     316              :     template <typename Archive>
     317            0 :     void ParticleBase<PLayout, IP...>::deserialize(Archive& ar, size_type nrecvs) {
     318              :         using memory_space = typename Archive::buffer_type::memory_space;
     319            0 :         forAllAttributes<memory_space>([&]<typename Attribute>(Attribute& att) {
     320            0 :             att->deserialize(ar, nrecvs);
     321              :         });
     322            0 :     }
     323              : 
     324              :     template <class PLayout, typename... IP>
     325              :     template <typename MemorySpace>
     326            0 :     detail::size_type ParticleBase<PLayout, IP...>::packedSize(const size_type count) const {
     327            0 :         size_type total = 0;
     328            0 :         forAllAttributes<MemorySpace>([&]<typename Attribute>(const Attribute& att) {
     329            0 :             total += att->packedSize(count);
     330              :         });
     331            0 :         return total;
     332              :     }
     333              : 
     334              :     template <class PLayout, typename... IP>
     335            0 :     void ParticleBase<PLayout, IP...>::pack(const hash_container_type& hash) {
     336            0 :         detail::runForAllSpaces([&]<typename MemorySpace>() {
     337            0 :             auto& att = attributes_m.template get<MemorySpace>();
     338            0 :             for (unsigned j = 0; j < att.size(); j++) {
     339            0 :                 att[j]->pack(hash.template get<MemorySpace>());
     340              :             }
     341              :         });
     342            0 :     }
     343              : 
     344              :     template <class PLayout, typename... IP>
     345            0 :     void ParticleBase<PLayout, IP...>::unpack(size_type nrecvs) {
     346            0 :         detail::runForAllSpaces([&]<typename MemorySpace>() {
     347            0 :             auto& att = attributes_m.template get<MemorySpace>();
     348            0 :             for (unsigned j = 0; j < att.size(); j++) {
     349            0 :                 att[j]->unpack(nrecvs);
     350              :             }
     351              :         });
     352            0 :         localNum_m += nrecvs;
     353            0 :     }
     354              : }  // namespace ippl
        

Generated by: LCOV version 2.0-1