LCOV - code coverage report
Current view: top level - simplemd/services - ParallelTopologyService.h (source / functions) Coverage Total Hit
Test: coverage.info Lines: 100.0 % 4 4
Test Date: 2026-08-21 15:14:06 Functions: 100.0 % 1 1

            Line data    Source code
       1              : // Copyright (C) 2015 Technische Universitaet Muenchen
       2              : // This file is part of the Mamico project. For conditions of distribution
       3              : // and use, please see the copyright notice in Mamico's main folder, or at
       4              : // www5.in.tum.de/mamico
       5              : #ifndef _MOLECULARDYNAMICS_SERVICES_PARALLELTOPOLOGYSERVICE_H_
       6              : #define _MOLECULARDYNAMICS_SERVICES_PARALLELTOPOLOGYSERVICE_H_
       7              : 
       8              : #include "simplemd/MolecularDynamicsDefinitions.h"
       9              : #include "tarch/la/Vector.h"
      10              : #if (MD_PARALLEL == MD_YES)
      11              : #include <mpi.h>
      12              : #endif
      13              : #include "simplemd/LinkedCell.h"
      14              : #include "simplemd/Molecule.h"
      15              : #include "simplemd/services/ParallelAndLocalBufferService.h"
      16              : #include "simplemd/MoleculeContainer.h"
      17              : #include <cstdlib>
      18              : #include <iostream>
      19              : #include <vector>
      20              : 
      21              : namespace simplemd {
      22              : // forward declarations to circumvent circular includes
      23              : class MoleculeContainer;
      24              : namespace services {
      25              : class ParallelTopologyService;
      26              : } // namespace services
      27              : } // namespace simplemd
      28              : 
      29              : /** services managing the distributed memory parallelisation things.
      30              :  *  It is based on a simple domain decomposition method for the molecules.
      31              :  *  Therefore, if we have N=Nx x Ny x Nz processes available, we enumerate
      32              :  *  the processes lexicographically in their ranks and so set up an easy-to-use
      33              :  *  topology of our parallel simulation.
      34              :  *
      35              :  *  @author Philipp Neumann, Nikola Tchipev
      36              :  */
      37              : class simplemd::services::ParallelTopologyService {
      38              : public:
      39              :   /** initialise the service with global domain size and offset,
      40              :    *  predefined meshwidth for the linked cells (which is to be adopted such that
      41              :    *  it fits to the local process properties), the number of processes to be used
      42              :    *  in each spatial direction, the rank of the current process, the global
      43              :    *  description of the outer boundaries, the number of molecules per direction
      44              :    *  and initialises the ParallelAndLocalBufferService.
      45              :    *
      46              :    *  Needs to be called before the local services are initialised.
      47              :    */
      48              :   ParallelTopologyService(const tarch::la::Vector<MD_DIM, double>& domainSize, const tarch::la::Vector<MD_DIM, double>& domainOffset,
      49              :                           const tarch::la::Vector<MD_DIM, double>& meshWidth, const tarch::la::Vector<MD_DIM, unsigned int>& numberProcesses,
      50              :                           const tarch::la::Vector<MD_LINKED_CELL_NEIGHBOURS, simplemd::BoundaryType>& boundary
      51              : #if (MD_PARALLEL == MD_YES)
      52              :                           ,
      53              :                           MPI_Comm communicator
      54              : #endif
      55              :   );
      56          180 :   ~ParallelTopologyService() {}
      57              : 
      58              :   /** finish initialization by allocating buffers
      59              :    *
      60              :    *  Needs to be called after MoleculeContainer has been initialised.
      61              :    */
      62              :   void initBuffers(const unsigned int& localNumberOfMolecules);
      63              : 
      64              :   /** shuts down the buffer service and then the parallel service */
      65              :   void shutdown();
      66              : 
      67              :   /** returns the rank of the current process */
      68              :   int getRank() const;
      69              : 
      70              :   /** returns the process coordinates of the local process */
      71              :   const tarch::la::Vector<MD_DIM, unsigned int>& getProcessCoordinates() const;
      72              : 
      73              :   /** returns local information on boundary relations */
      74           12 :   const tarch::la::Vector<MD_LINKED_CELL_NEIGHBOURS, simplemd::BoundaryType>& getLocalBoundaryInformation() const { return _boundary; }
      75              : 
      76              :   /** returns the mesh width for the linked cells in the current simulation. Depending on the meshwidth
      77              :    *  handed over during initialisation, this meshwidth might be different; we try to choose it as close
      78              :    *  to the meshwidth defined by the user, but big enough so that we have the same (integer) number of
      79              :    *  cells on each process. So, _meshWidth typicall is bigger or equal the user-defined meshwidth.
      80              :    *  This may result in higher computation times! However, the physics remain the same as the cutoff-radius
      81              :    *  is not affected by these changes.
      82              :    */
      83              :   const tarch::la::Vector<MD_DIM, double>& getMeshWidth() const { return _meshWidth; }
      84              : 
      85              :   /** returns the global number of cells in each spatial direction */
      86              :   const tarch::la::Vector<MD_DIM, unsigned int>& getGlobalNumberOfCells() const { return _globalNumberOfCells; }
      87              : 
      88              :   /** returns the local number of cells in each spatial direction (i.e. only the cells of this process) */
      89              :   const tarch::la::Vector<MD_DIM, unsigned int>& getLocalNumberOfCells(bool includingGhostCells = false) const {
      90              :     return includingGhostCells ? _localNumberOfCellsWithGhostCells : _localNumberOfCells;
      91              :   }
      92              : 
      93              :   /** returns the local number of cells (i.e. only the cells of this process) */
      94              :   size_t getLocalNumberOfCellsLinear(bool includingGhostCells = false) const {
      95              :     auto numCells = getLocalNumberOfCells(includingGhostCells);
      96              :     size_t numCellsLinear = 1;
      97              :     for (int d = 0; d < MD_DIM; d++) {
      98              :       numCellsLinear *= numCells[d];
      99              :     }
     100              :     return numCellsLinear;
     101              :   }
     102              : 
     103              :   /** returns the number of processes used in each spatial direction */
     104              :   const tarch::la::Vector<MD_DIM, unsigned int>& getNumberOfProcesses() const { return _numberProcesses; }
     105              : 
     106              :   /** returns the global domain size*/
     107            4 :   const tarch::la::Vector<MD_DIM, double>& getGlobalDomainSize() const { return _domainSize; }
     108              : 
     109              :   /** returns the global domain offset */
     110            4 :   const tarch::la::Vector<MD_DIM, double>& getGlobalDomainOffset() const { return _domainOffset; }
     111              : 
     112              :   /** returns the global index of the first (non-ghost) cell */
     113              :   const tarch::la::Vector<MD_DIM, unsigned int>& getGlobalIndexOfFirstCell() const { return _globalIndexOfFirstCell; }
     114              : 
     115              :   const tarch::la::Vector<MD_DIM, unsigned int>& getLocalIndexOfFirstCell() const { return _localIndexOfFirstCell; }
     116              : 
     117              :   /** returns true, if this process does not carry any work. This can be the case, if we have more ranks available
     118              :    *  in the NodePool than specified in the xml config.
     119              :    */
     120              :   bool isIdle() const;
     121              : 
     122              :   /** broadcasts all molecules contained in the (inner) cell "cell" with index cellIndex to all
     123              :    * ghost cells that can be identified with this inner cell. Returns a vector containing the local
     124              :    * cell coordinates of all neighbored ghost cells that are a local, non-parallel boundary.
     125              :    * This is important for the case that we have 1 processor in one direction only. Then, we might
     126              :    * have a periodic boundary that needs to be handled locally!
     127              :    *
     128              :    * This method is also used for handling process-leaving particles in the optimised case when
     129              :    * they are sent together with molecules for ghost layers.
     130              :    * The local buffer of the buffer service is filled only with process-leaving particles that need
     131              :    * to be handled locally.
     132              :    * Ghost particles that need to be handled locally are handled via the indices in the returned vector.
     133              :    *
     134              :    * In the parallel case, this method makes use of the communication buffers: molecules are not
     135              :    * messaged to neighbour one by one, but rather pushed into buffers, which store all molecules to be sent to a process.
     136              :    *
     137              :    * The MPI send calls are executed later (from within BoundaryTreatment).
     138              :    */
     139              :   std::vector<tarch::la::Vector<MD_DIM, unsigned int>> broadcastInnerCellViaBuffer(LinkedCell& cell, const size_t cellIndex,
     140              :                                                                                    const simplemd::MoleculeContainer& moleculeContainer);
     141              : 
     142              :   /** sends all molecules from cell cellIndex to the respective neighbouring process. The cell
     143              :    * cellIndex needs to be a ghost cell.
     144              :    * The function returns true, if the respective cell where the molecules need to be sorted in
     145              :    * is part of another process; in this case the molecules are pushed to the send buffers and erased.
     146              :    * Otherwise, the other (non-ghost) cell is part of the same process. In this case, no send-operation is triggered and false
     147              :    * is returned.
     148              :    *
     149              :    * The MPI send calls are executed later (from within BoundaryTreatment).
     150              :    */
     151              :   bool reduceGhostCellViaBuffer(LinkedCell& cell, const size_t cellIndex, const simplemd::MoleculeContainer& moleculeContainer);
     152              : 
     153              :   /** unpack and resort local buffer
     154              :    */
     155              :   void unpackLocalBuffer(simplemd::MoleculeContainer& moleculeContainer);
     156              : 
     157              :   /** Communication schedule:
     158              :    * 1. Irecv on buffers
     159              :    * 2. Isend on buffers
     160              :    * 3. Wait for all requests to be completed
     161              :    * 4. Unpack receive buffers and sort into linked cells
     162              :    *
     163              :    * For large messages, waiting for sender requests is also a must-have.
     164              :    */
     165              :   void communicationSteps_1_2();
     166              : 
     167              :   /** See comment of communicationSteps_1_2() */
     168              :   void communicationSteps_3_4(simplemd::MoleculeContainer& MoleculeContainer);
     169              : 
     170              :   /** Compute (non-overlapping) intersection of a global region of interest (ROI) with local domain.
     171              :       For example for purposes of profile plotter. */
     172              :   bool globalToLocalRegionOfInterest(const tarch::la::Vector<MD_DIM, unsigned int>& globalStartCell, const tarch::la::Vector<MD_DIM, unsigned int>& globalRange,
     173              :                                      tarch::la::Vector<MD_DIM, unsigned int>& localStartCell, tarch::la::Vector<MD_DIM, unsigned int>& localRange) const;
     174              : 
     175              :   /** Compute global cell index from local cell index in vector form */
     176              :   tarch::la::Vector<MD_DIM, unsigned int> localToGlobalCellIndexVector(const tarch::la::Vector<MD_DIM, unsigned int>& localCellIndexVector) const;
     177              : 
     178              : #if (MD_PARALLEL == MD_YES)
     179              :   int MD_Allreduce(void* sendbuf, void* recvbuf, int count, MPI_Datatype datatype, MPI_Op op) {
     180              :     return MPI_Allreduce(sendbuf, recvbuf, count, datatype, op, _communicator);
     181              :   }
     182              : #endif
     183              : 
     184              :   tarch::la::Vector<MD_DIM, unsigned int> getGhostCellLayerThickness() const { return _ghostCellLayerThickness; }
     185              : 
     186              : private:
     187              :   /** computes all neighbour ranks and stores the results in neighbourRanks.
     188              :    * Computes _neighbourRanksUnique, _numUniqueNeighbours and cells per buffer -
     189              :    * values needed by the buffer service.
     190              :    */
     191              :   void createNeighbourRanks(const tarch::la::Vector<MD_DIM, unsigned int>& processCoordinates,
     192              :                             const tarch::la::Vector<MD_LINKED_CELL_NEIGHBOURS, simplemd::BoundaryType>& localBoundary,
     193              :                             const tarch::la::Vector<MD_DIM, unsigned int>& numberProcesses, std::vector<int>& neighbourRanks,
     194              :                             std::vector<int>& neighbourRanksUnique, unsigned int& numUniqueNeighbours, unsigned int numberOfCellsPerBuffer[]) const;
     195              : 
     196              :   /** if addedNeighbour is not already present in neighbourRanksUnique,
     197              :    * add it, also incrementing numUniqueNeighbours.
     198              :    */
     199              :   void addNeighbourToNeighbourRanksUnique(std::vector<int>& neighbourRanksUnique, unsigned int& numUniqueNeighbours, const int& addedNeighbour) const;
     200              : 
     201              :   /** computes the actual mesh size that is used in the simulation. We make the width as similar to the prescribed mesh width "prescribedWidth" as possible so
     202              :    * to have an integer number of grid cells.
     203              :    */
     204              :   tarch::la::Vector<MD_DIM, double> computeMeshwidth(const tarch::la::Vector<MD_DIM, double>& prescribedWidth,
     205              :                                                      const tarch::la::Vector<MD_DIM, unsigned int>& numberProcesses,
     206              :                                                      const tarch::la::Vector<MD_DIM, double>& domainSize) const;
     207              : 
     208              :   /** computes the number of cells in each process block.
     209              :    */
     210              :   tarch::la::Vector<MD_DIM, unsigned int> computeNumberOfCells(const tarch::la::Vector<MD_DIM, double>& meshWidth,
     211              :                                                                const tarch::la::Vector<MD_DIM, unsigned int>& numberProcesses,
     212              :                                                                const tarch::la::Vector<MD_DIM, double>& domainSize) const;
     213              : 
     214              :   /** computes the global number of grid cells */
     215              :   tarch::la::Vector<MD_DIM, unsigned int> computeGlobalNumberOfCells(const tarch::la::Vector<MD_DIM, double>& meshWidth,
     216              :                                                                      const tarch::la::Vector<MD_DIM, unsigned int>& numberProcesses,
     217              :                                                                      const tarch::la::Vector<MD_DIM, double>& domainSize) const;
     218              : 
     219              :   /** returns true if neighbourRank is present in the _neighbourRanks vector. Returns false otherwise. */
     220              :   bool isParallelNeighbour(const int& neighbourRank) const;
     221              : 
     222              :   /** returns the local information needed for periodic boundary treatment. This function
     223              :    *  is called within the init() call and initialises the _periodicBoundary field.
     224              :    */
     225              :   tarch::la::Vector<MD_LINKED_CELL_NEIGHBOURS, simplemd::BoundaryType>
     226              :   computeLocalBoundaryInformation(const tarch::la::Vector<MD_LINKED_CELL_NEIGHBOURS, simplemd::BoundaryType>& boundary,
     227              :                                   const tarch::la::Vector<MD_DIM, unsigned int>& processCoordinates,
     228              :                                   const tarch::la::Vector<MD_DIM, unsigned int>& numberProcesses);
     229              : 
     230              :   /** returns the number of transferred cells, if the neighbour is placed in distance x,y,z
     231              :    *  from the current cell.
     232              :    */
     233              :   unsigned int getNumberOfTransferredCells(const int& x
     234              : #if (MD_DIM > 1)
     235              :                                            ,
     236              :                                            const int& y
     237              : #endif
     238              : #if (MD_DIM > 2)
     239              :                                            ,
     240              :                                            const int& z
     241              : #endif
     242              :   ) const;
     243              : 
     244              :   /** corrects the position vector 'position' according to periodic boundary conditions.
     245              :    *  If the boundaryType is PERIODIC_BOUNDARY the index neighbourIndex is used to determine
     246              :    *  the location of the present boundary and to modify the position vector accordingly.
     247              :    */
     248              :   void adaptPositionForPeriodicBoundaries(tarch::la::Vector<MD_DIM, double>& position, const simplemd::BoundaryType& boundaryType, const int& x
     249              : #if (MD_DIM > 1)
     250              :                                           ,
     251              :                                           const int& y
     252              : #endif
     253              : #if (MD_DIM > 2)
     254              :                                           ,
     255              :                                           const int& z
     256              : #endif
     257              :   ) const;
     258              : 
     259              :   /** get buffer index corresponding to neighbourRank from _neighbourRanksUnique  */
     260              :   unsigned int getCurrentBufferIndexFromNeighbourRank(const int& neighbourRank) const;
     261              : 
     262              :   /** Read off all molecules from buffer and resort them in the respective linked cells.
     263              :    */
     264              :   void unpackBuffer(ParallelAndLocalBufferService::SimpleBuffer* buf, simplemd::MoleculeContainer& MoleculeContainer);
     265              : 
     266              :   /** place position, velocity, forceOld and isFixed at the end of the respective local buffer.
     267              :    */
     268              :   void pushMoleculeToLocalBuffer(const Molecule* mol, const tarch::la::Vector<MD_DIM, double>& pos);
     269              : 
     270              : #if (MD_PARALLEL == MD_YES)
     271              :   /** place position, velocity, forceOld and isFixed at the end of the respective sendBuffer.
     272              :    */
     273              :   void pushMoleculeToSendBuffer(const unsigned int& bufferIndex, const Molecule* mol, const tarch::la::Vector<MD_DIM, double>& pos);
     274              : 
     275              :   /** Issues an Isend call on buffer.
     276              :    * Does not wait for the request to be fulfilled, but uses up the respective request
     277              :    */
     278              :   void bufferIsend(ParallelAndLocalBufferService::SimpleBuffer* buffer, const int& neighbourRank, MPI_Request& request) const;
     279              : 
     280              :   /** Issues an Irecv call on buffer.
     281              :    * Does not wait for the request to be fulfilled, but uses up the respective request
     282              :    */
     283              :   void bufferIrecv(ParallelAndLocalBufferService::SimpleBuffer* buffer, const int& neighbourRank, MPI_Request& request) const;
     284              : #endif
     285              : 
     286              :   /** domain size */
     287              :   const tarch::la::Vector<MD_DIM, double> _domainSize;
     288              : 
     289              :   /** domain offset */
     290              :   const tarch::la::Vector<MD_DIM, double> _domainOffset;
     291              : 
     292              :   /** The size of a linked cell along each axis */
     293              :   const tarch::la::Vector<MD_DIM, double> _meshWidth;
     294              : 
     295              :   /** The number of ghost cells around the local domain along each axis */
     296              :   static constexpr unsigned int _ghostCellLayerThickness = 1; // NOTE: This MUST be 1 (otherwise nothing will work anymore)
     297              : 
     298              :   /** index of first cell w.r.t. global grid
     299              :    */
     300              :   tarch::la::Vector<MD_DIM, unsigned int> _globalIndexOfFirstCell;
     301              : 
     302              :   /** index of first cell w.r.t. local grid (including ghost cells )*/
     303              :   const tarch::la::Vector<MD_DIM, unsigned int> _localIndexOfFirstCell;
     304              : 
     305              :   /** definition of the process matrix, i.e. how many processes work in each
     306              :    *  spatial direction.
     307              :    */
     308              :   const tarch::la::Vector<MD_DIM, unsigned int> _numberProcesses;
     309              : 
     310              :   /** local number of cells (i.e. on each process) in all d directions */
     311              :   const tarch::la::Vector<MD_DIM, unsigned int> _localNumberOfCells;
     312              : 
     313              :   /** local number of cells (i.e. on each process) in all d directions including ghost cells */
     314              :   const tarch::la::Vector<MD_DIM, unsigned int> _localNumberOfCellsWithGhostCells;
     315              : 
     316              :   /** global number of cells in all d directions */
     317              :   const tarch::la::Vector<MD_DIM, unsigned int> _globalNumberOfCells;
     318              : 
     319              :   /** rank of this process */
     320              :   int _rank;
     321              : 
     322              :   /** boundary information on the local process */
     323              :   tarch::la::Vector<MD_LINKED_CELL_NEIGHBOURS, simplemd::BoundaryType> _boundary;
     324              : 
     325              :   /** coordinates of this process in the process matrix. This vector is the same as
     326              :    *  _rank, but written in vector form.
     327              :    */
     328              :   tarch::la::Vector<MD_DIM, unsigned int> _processCoordinates;
     329              : 
     330              :   /** vector containing all ranks of the neighbouring processes. This includes
     331              :    *  periodic neighbour relations.
     332              :    */
     333              :   std::vector<int> _neighbourRanks;
     334              : 
     335              :   /** Buffer service exported in a separate class
     336              :    * @see ParallelAndLocalBufferService
     337              :    */
     338              :   ParallelAndLocalBufferService _bufferService;
     339              : 
     340              :   /** like _neighbourRanks, but without possible repetitions
     341              :    * @see _neighbourRanks
     342              :    */
     343              :   std::vector<int> _neighbourRanksUnique;
     344              : 
     345              : #if (MD_PARALLEL == MD_YES)
     346              :   /** tag for communicating buffers */
     347              :   int _bufferTag;
     348              : #endif
     349              : 
     350              :   /** number of processes current process will communicate with
     351              :    * also equal to number of used buffers and MPI requests
     352              :    */
     353              :   unsigned int _numUniqueNeighbours;
     354              : 
     355              :   /** how many cells a buffer is responsible for */
     356              :   unsigned int _numberOfCellsPerBuffer[MD_LINKED_CELL_NEIGHBOURS];
     357              : 
     358              : #if (MD_PARALLEL == MD_YES)
     359              :   MPI_Comm _communicator;
     360              :   /** requests for sending and receiving buffers */
     361              :   MPI_Request _receiveRequests[MD_LINKED_CELL_NEIGHBOURS];
     362              :   MPI_Request _sendRequests[MD_LINKED_CELL_NEIGHBOURS];
     363              : #endif
     364              : };
     365              : #endif // _MOLECULARDYNAMICS_SERVICES_PARALLELTOPOLOGYSERVICE_H_
        

Generated by: LCOV version 2.0-1