LCOV - code coverage report
Current view: top level - src/Particle - ParticleBase.hpp (source / functions) Coverage Total Hit
Test: final_report.info Lines: 88.6 % 140 124
Test Date: 2025-07-19 00:25:23 Functions: 80.1 % 734 588

            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          408 :     ParticleBase<PLayout, IP...>::ParticleBase()
      56          408 :         : layout_m(nullptr)
      57          408 :         , localNum_m(0)
      58          408 :         , totalNum_m(0)
      59          408 :         , nextID_m(Comm->rank())
      60          816 :         , numNodes_m(Comm->size()) {
      61              :         if constexpr (EnableIDs) {
      62           96 :             addAttribute(ID);
      63              :         }
      64          408 :         addAttribute(R);
      65          408 :     }
      66              : 
      67              :     template <class PLayout, typename... IP>
      68          384 :     ParticleBase<PLayout, IP...>::ParticleBase(PLayout& layout)
      69          384 :         : ParticleBase() {
      70          384 :         initialize(layout);
      71          384 :     }
      72              : 
      73              :     template <class PLayout, typename... IP>
      74              :     template <typename MemorySpace>
      75          672 :     void ParticleBase<PLayout, IP...>::addAttribute(detail::ParticleAttribBase<MemorySpace>& pa) {
      76          672 :         attributes_m.template get<MemorySpace>().push_back(&pa);
      77          672 :         pa.setParticleCount(localNum_m);
      78          672 :     }
      79              : 
      80              :     template <class PLayout, typename... IP>
      81          408 :     void ParticleBase<PLayout, IP...>::initialize(PLayout& layout) {
      82              :         //         PAssert(layout_m == nullptr);
      83              : 
      84              :         // save the layout, and perform setup tasks
      85          408 :         layout_m = &layout;
      86          408 :     }
      87              : 
      88              :     template <class PLayout, typename... IP>
      89          312 :     void ParticleBase<PLayout, IP...>::create(size_type nLocal) {
      90          312 :         PAssert(layout_m != nullptr);
      91              : 
      92          312 :         if (nLocal > 0) {
      93         1224 :             forAllAttributes([&]<typename Attribute>(Attribute& attribute) {
      94          456 :                 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            0 :                 auto pIDs     = ID.getView();
     102            0 :                 auto nextID   = this->nextID_m;
     103            0 :                 auto numNodes = this->numNodes_m;
     104            0 :                 Kokkos::parallel_for(
     105            0 :                     "ParticleBase<...>::create(size_t)", policy_type(localNum_m, nLocal),
     106            0 :                     KOKKOS_LAMBDA(const std::int64_t i) { pIDs(i) = nextID + numNodes * i; });
     107              :                 // nextID_m += numNodes_m * (nLocal - localNum_m);
     108            0 :                 nextID_m += numNodes_m * nLocal;
     109            0 :             }
     110              : 
     111              :             // remember that we're creating these new particles
     112          312 :             localNum_m += nLocal;
     113              :         }
     114              : 
     115          312 :         Comm->allreduce(localNum_m, totalNum_m, 1, std::plus<size_type>());
     116          312 :     }
     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            0 :     void ParticleBase<PLayout, IP...>::destroy(const Kokkos::View<bool*, Properties...>& invalid,
     155              :                                                const size_type destroyNum) {
     156            0 :         this->internalDestroy(invalid, destroyNum);
     157              : 
     158            0 :         Comm->allreduce(localNum_m, totalNum_m, 1, std::plus<size_type>());
     159            0 :     }
     160              : 
     161              :     template <class PLayout, typename... IP>
     162              :     template <typename... Properties>
     163          168 :     void ParticleBase<PLayout, IP...>::internalDestroy(
     164              :         const Kokkos::View<bool*, Properties...>& invalid, const size_type destroyNum) {
     165          168 :         PAssert(destroyNum <= localNum_m);
     166              : 
     167              :         // If there aren't any particles to delete, do nothing
     168          168 :         if (destroyNum == 0) {
     169           36 :             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          132 :         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          132 :         auto& locDeleteIndex  = deleteIndex_m.get<memory_space>();
     185          132 :         auto& locKeepIndex    = keepIndex_m.get<memory_space>();
     186              : 
     187              :         // Resize buffers, if necessary
     188          396 :         detail::runForAllSpaces([&]<typename MemorySpace>() {
     189          132 :             if (attributes_m.template get<memory_space>().size() > 0) {
     190          132 :                 int overalloc = Comm->getDefaultOverallocation();
     191          132 :                 auto& del     = deleteIndex_m.get<memory_space>();
     192          132 :                 auto& keep    = keepIndex_m.get<memory_space>();
     193          132 :                 if (del.size() < destroyNum) {
     194          120 :                     Kokkos::realloc(del, destroyNum * overalloc);
     195          120 :                     Kokkos::realloc(keep, destroyNum * overalloc);
     196              :                 }
     197              :             }
     198              :         });
     199              : 
     200              :         // Reset index buffer
     201          132 :         Kokkos::deep_copy(locDeleteIndex, -1);
     202              : 
     203              :         // Find the indices of the invalid particles in the valid region
     204          132 :         Kokkos::parallel_scan(
     205          132 :             "Scan in ParticleBase::destroy()", policy_type(0, localNum_m - destroyNum),
     206         7020 :             KOKKOS_LAMBDA(const size_t i, int& idx, const bool final) {
     207         6756 :                 if (final && invalid(i)) {
     208         2788 :                     locDeleteIndex(idx) = i;
     209              :                 }
     210         6756 :                 if (invalid(i)) {
     211         1394 :                     idx += 1;
     212              :                 }
     213              :             });
     214          132 :         Kokkos::fence();
     215              : 
     216              :         // Determine the total number of invalid particles in the valid region
     217          132 :         size_type maxDeleteIndex = 0;
     218          132 :         Kokkos::parallel_reduce(
     219          264 :             "Reduce in ParticleBase::destroy()", policy_type(0, destroyNum),
     220         6028 :             KOKKOS_LAMBDA(const size_t i, size_t& maxIdx) {
     221         5764 :                 if (locDeleteIndex(i) >= 0 && i > maxIdx) {
     222         1262 :                     maxIdx = i;
     223              :                 }
     224              :             },
     225          264 :             Kokkos::Max<size_type>(maxDeleteIndex));
     226              : 
     227              :         // Find the indices of the valid particles in the invalid region
     228          132 :         Kokkos::parallel_scan(
     229              :             "Second scan in ParticleBase::destroy()",
     230          132 :             Kokkos::RangePolicy<size_type, execution_space>(localNum_m - destroyNum, localNum_m),
     231         6028 :             KOKKOS_LAMBDA(const size_t i, int& idx, const bool final) {
     232         5764 :                 if (final && !invalid(i)) {
     233         2788 :                     locKeepIndex(idx) = i;
     234              :                 }
     235         5764 :                 if (!invalid(i)) {
     236         1394 :                     idx += 1;
     237              :                 }
     238              :             });
     239              : 
     240          132 :         Kokkos::fence();
     241              : 
     242          132 :         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          132 :         auto filter = [&]<typename MemorySpace>() {
     248              :             return attributes_m.template get<MemorySpace>().size() > 0;
     249              :         };
     250          132 :         deleteIndex_m.copyToOtherSpaces<memory_space>(filter);
     251          132 :         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          708 :         forAllAttributes([&]<typename Attribute>(Attribute*& attribute) {
     258              :             using att_memory_space = typename Attribute::memory_space;
     259          288 :             auto& del              = deleteIndex_m.get<att_memory_space>();
     260          288 :             auto& keep             = keepIndex_m.get<att_memory_space>();
     261          288 :             attribute->destroy(del, keep, maxDeleteIndex + 1);
     262              :         });
     263              :     }
     264              : 
     265              :     template <class PLayout, typename... IP>
     266              :     template <typename HashType>
     267          132 :     void ParticleBase<PLayout, IP...>::sendToRank(int rank, int tag,
     268              :                                                   std::vector<MPI_Request>& requests,
     269              :                                                   const HashType& hash) {
     270          132 :         size_type nSends = hash.size();
     271          132 :         requests.resize(requests.size() + 1);
     272              : 
     273          132 :         auto hashes = hash_container_type(hash, [&]<typename MemorySpace>() {
     274              :             return attributes_m.template get<MemorySpace>().size() > 0;
     275              :         });
     276          132 :         pack(hashes);
     277          264 :         detail::runForAllSpaces([&]<typename MemorySpace>() {
     278          132 :             size_type bufSize = packedSize<MemorySpace>(nSends);
     279          132 :             if (bufSize == 0) {
     280            0 :                 return;
     281              :             }
     282              : 
     283          132 :             auto buf = Comm->getBuffer<MemorySpace>(bufSize);
     284              : 
     285          132 :             Comm->isend(rank, tag++, *this, *buf, requests.back(), nSends);
     286          132 :             buf->resetWritePos();
     287          132 :         });
     288          132 :     }
     289              : 
     290              :     template <class PLayout, typename... IP>
     291          132 :     void ParticleBase<PLayout, IP...>::recvFromRank(int rank, int tag, size_type nRecvs) {
     292          264 :         detail::runForAllSpaces([&]<typename MemorySpace>() {
     293          132 :             size_type bufSize = packedSize<MemorySpace>(nRecvs);
     294          132 :             if (bufSize == 0) {
     295            0 :                 return;
     296              :             }
     297              : 
     298          132 :             auto buf = Comm->getBuffer<MemorySpace>(bufSize);
     299              : 
     300          132 :             Comm->recv(rank, tag++, *this, *buf, bufSize, nRecvs);
     301          132 :             buf->resetReadPos();
     302          132 :         });
     303          132 :         unpack(nRecvs);
     304          132 :     }
     305              : 
     306              :     template <class PLayout, typename... IP>
     307              :     template <typename Archive>
     308          132 :     void ParticleBase<PLayout, IP...>::serialize(Archive& ar, size_type nsends) {
     309              :         using memory_space = typename Archive::buffer_type::memory_space;
     310          708 :         forAllAttributes<memory_space>([&]<typename Attribute>(Attribute& att) {
     311          288 :             att->serialize(ar, nsends);
     312              :         });
     313          132 :     }
     314              : 
     315              :     template <class PLayout, typename... IP>
     316              :     template <typename Archive>
     317          132 :     void ParticleBase<PLayout, IP...>::deserialize(Archive& ar, size_type nrecvs) {
     318              :         using memory_space = typename Archive::buffer_type::memory_space;
     319          708 :         forAllAttributes<memory_space>([&]<typename Attribute>(Attribute& att) {
     320          288 :             att->deserialize(ar, nrecvs);
     321              :         });
     322          132 :     }
     323              : 
     324              :     template <class PLayout, typename... IP>
     325              :     template <typename MemorySpace>
     326          264 :     detail::size_type ParticleBase<PLayout, IP...>::packedSize(const size_type count) const {
     327          264 :         size_type total = 0;
     328         1416 :         forAllAttributes<MemorySpace>([&]<typename Attribute>(const Attribute& att) {
     329          576 :             total += att->packedSize(count);
     330              :         });
     331          264 :         return total;
     332              :     }
     333              : 
     334              :     template <class PLayout, typename... IP>
     335          132 :     void ParticleBase<PLayout, IP...>::pack(const hash_container_type& hash) {
     336          396 :         detail::runForAllSpaces([&]<typename MemorySpace>() {
     337          132 :             auto& att = attributes_m.template get<MemorySpace>();
     338          420 :             for (unsigned j = 0; j < att.size(); j++) {
     339          288 :                 att[j]->pack(hash.template get<MemorySpace>());
     340              :             }
     341              :         });
     342          132 :     }
     343              : 
     344              :     template <class PLayout, typename... IP>
     345          132 :     void ParticleBase<PLayout, IP...>::unpack(size_type nrecvs) {
     346          396 :         detail::runForAllSpaces([&]<typename MemorySpace>() {
     347          132 :             auto& att = attributes_m.template get<MemorySpace>();
     348          420 :             for (unsigned j = 0; j < att.size(); j++) {
     349          288 :                 att[j]->unpack(nrecvs);
     350              :             }
     351              :         });
     352          132 :         localNum_m += nrecvs;
     353          132 :     }
     354              : }  // namespace ippl
        

Generated by: LCOV version 2.0-1