LCOV - code coverage report
Current view: top level - core - ParallelTaskManager.h (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 165 169 97.6 %
Date: 2026-11-05 10:19:04 Functions: 640 724 88.4 %

          Line data    Source code
       1             : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
       2             :    Copyright (c) 2017-2023 The plumed team
       3             :    (see the PEOPLE file at the root of the distribution for a list of names)
       4             : 
       5             :    See http://www.plumed.org for more information.
       6             : 
       7             :    This file is part of plumed, version 2.
       8             : 
       9             :    plumed is free software: you can redistribute it and/or modify
      10             :    it under the terms of the GNU Lesser General Public License as published by
      11             :    the Free Software Foundation, either version 3 of the License, or
      12             :    (at your option) any later version.
      13             : 
      14             :    plumed is distributed in the hope that it will be useful,
      15             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
      16             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      17             :    GNU Lesser General Public License for more details.
      18             : 
      19             :    You should have received a copy of the GNU Lesser General Public License
      20             :    along with plumed.  If not, see <http://www.gnu.org/licenses/>.
      21             : +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
      22             : #ifndef __PLUMED_core_ParallelTaskManager_h
      23             : #define __PLUMED_core_ParallelTaskManager_h
      24             : 
      25             : #include "ActionWithVector.h"
      26             : #include "tools/Communicator.h"
      27             : #include "tools/OpenMP.h"
      28             : #include "tools/View.h"
      29             : #include "tools/View2D.h"
      30             : 
      31             : #include "tools/ColvarOutput.h"
      32             : #include "tools/OpenACC.h"
      33             : 
      34             : namespace PLMD {
      35             : 
      36             : template<typename precision>
      37             : struct ParActionsInput {
      38             :   /// Do we need to calculate the derivatives
      39             :   bool noderiv{false};
      40             :   /// Periodic boundary conditions
      41             :   const Pbc* pbc;
      42             :   /// The number of components the underlying action is computing
      43             :   std::size_t ncomponents{0};
      44             :   /// The number of scalars we are calculating for each task
      45             :   unsigned nscalars{0};
      46             :   /// The number of force scalars for each task
      47             :   unsigned nforcescalars{0};
      48             :   /// Number of derivatives for each scalar being calculated
      49             :   std::size_t nderivatives_per_scalar{0};
      50             :   /// The start of the thread unsafe forces
      51             :   unsigned threadunsafe_forces_start{0};
      52             :   /// This holds all the input data that is required to calculate all values for all tasks
      53             :   unsigned dataSize{0};
      54             :   precision *inputdata{nullptr};
      55             :   /// Bookeeping stuff for arguments
      56             :   std::size_t nargs{0};
      57             :   std::size_t ranks_size{0};
      58             :   const std::size_t* ranks{nullptr};
      59             :   std::size_t shapestarts_size{0};
      60             :   const std::size_t* shapestarts{nullptr};
      61             :   std::size_t shapedata_size{0};
      62             :   const std::size_t* shapedata{nullptr};
      63             :   std::size_t ncols_size{0};
      64             :   const std::size_t* ncols{nullptr};
      65             :   std::size_t bookstarts_size{0};
      66             :   const std::size_t* bookstarts{nullptr};
      67             :   std::size_t booksizes_size{0};
      68             :   const std::size_t* booksizes{nullptr};
      69             :   std::size_t bookeeping_size{0};
      70             :   const std::size_t* bookeeping{nullptr};
      71             :   std::size_t argstarts_size{0};
      72             :   const std::size_t* argstarts{nullptr};
      73             :   static ParActionsInput create( const Pbc& box ) {
      74        4153 :     auto toret=ParActionsInput();
      75        4086 :     toret.pbc=&box;
      76             :     return toret;
      77             :   }
      78             : 
      79             :   //helper function to bring data to the device in a controlled way
      80             :   void toACCDevice()const {
      81             : #pragma acc enter data copyin(this[0:1], noderiv, pbc[0:1],ncomponents, \
      82             :     nscalars, nderivatives_per_scalar, threadunsafe_forces_start, \
      83             :      dataSize, inputdata[0:dataSize])
      84             :     if (nargs>0) {
      85             : #pragma acc enter data copyin( nargs, \
      86             :      ranks_size, ranks[0:ranks_size], \
      87             :      shapestarts_size, shapestarts[0:shapestarts_size], \
      88             :      shapedata_size, shapedata[0:shapedata_size], \
      89             :      ncols_size, ncols[0:ncols_size], \
      90             :      bookstarts_size, bookstarts[0:bookstarts_size], \
      91             :      booksizes_size, booksizes[0:booksizes_size], \
      92             :      bookeeping_size, bookeeping[0:bookeeping_size], \
      93             :      argstarts_size, argstarts[0:argstarts_size] \
      94             :     )
      95             :     }
      96             :     pbc->toACCDevice();
      97             :   }
      98             :   //helper function to remove data from the device in a controlled way
      99             :   void removeFromACCDevice() const  {
     100             :     pbc->removeFromACCDevice();
     101             :     // assuming dataSize is not changed
     102             :     if (nargs>0) {
     103             : #pragma acc exit data delete( \
     104             :     shapestarts[0:shapestarts_size], shapestarts_size, \
     105             :     shapedata[0:shapedata_size], shapedata_size, \
     106             :     ncols[0:ncols_size], ncols_size, \
     107             :     bookstarts[0:bookstarts_size], bookstarts_size, \
     108             :     booksizes[0:booksizes_size], booksizes_size, \
     109             :     bookeeping[0:bookeeping_size], bookeeping_size, \
     110             :     argstarts[0:argstarts_size], argstarts_size, \
     111             :     ranks[0:ranks_size], ranks_size, \
     112             :     nargs )
     113             :     }
     114             : 
     115             : #pragma acc exit data delete( \
     116             :     inputdata[0:dataSize], dataSize, \
     117             :     threadunsafe_forces_start, nderivatives_per_scalar, \
     118             :     nscalars, ncomponents, pbc[0:1],noderiv,this[0:1])
     119             :   }
     120             :   /// Setup the arguments
     121             :   void setupArguments( const ArgumentsBookkeeping& ab );
     122             :   unsigned sizeOfFakeVals() const {
     123             :     return nscalars;
     124             :   }
     125             : };
     126             : 
     127             : template<typename precision>
     128      183432 : inline void ParActionsInput<precision>::setupArguments( const ArgumentsBookkeeping& ab ) {
     129      183432 :   nargs = ab.nargs;
     130      183432 :   ranks = ab.ranks.data();
     131      183432 :   ranks_size = ab.ranks.size();
     132      183432 :   shapestarts = ab.shapestarts.data();
     133      183432 :   shapestarts_size = ab.shapestarts.size();
     134      183432 :   shapedata = ab.shapedata.data();
     135      183432 :   shapedata_size = ab.shapedata.size();
     136      183432 :   ncols = ab.ncols.data();
     137      183432 :   ncols_size = ab.ncols.size();
     138      183432 :   bookstarts = ab.bookstarts.data();
     139      183432 :   bookstarts_size = ab.bookstarts.size();
     140      183432 :   booksizes = ab.booksizes.data();
     141      183432 :   booksizes_size = ab.booksizes.size();
     142      183432 :   bookeeping = ab.bookeeping.data();
     143      183432 :   bookeeping_size = ab.bookeeping.size();
     144      183432 :   argstarts = ab.argstarts.data();
     145      183432 :   argstarts_size = ab.argstarts.size();
     146      183432 : }
     147             : 
     148             : struct ArgumentBookeepingHolder {
     149             :   std::size_t rank;
     150             :   std::size_t ncols;
     151             :   std::size_t start;
     152             :   View<const std::size_t> shape;
     153             :   View<const std::size_t> bookeeping;
     154             : 
     155             :   template<typename precision>
     156      813152 :   static ArgumentBookeepingHolder create ( std::size_t argno, const ParActionsInput<precision>& inp ) {
     157             :     return ArgumentBookeepingHolder{
     158      813152 :       inp.ranks[argno], // rank
     159      813152 :       inp.ncols[argno], // ncols
     160      813152 :       inp.argstarts[argno], // start
     161      813152 :       View<const std::size_t>(inp.shapedata + inp.shapestarts[argno], inp.ranks[argno] ), // shape
     162      813152 :       View<const std::size_t>(inp.bookeeping + inp.bookstarts[argno], inp.booksizes[argno] )  // bookeeping
     163      813152 :     };
     164             :   }
     165             : };
     166             : 
     167             : template<typename precision>
     168             : struct ParActionsOutput {
     169             :   View<precision> values;
     170             :   View<precision> derivatives;
     171             :   View<precision> buffer;
     172             : 
     173             :   static ParActionsOutput create( std::size_t ncomp,
     174             :                                   precision* v,
     175             :                                   std::size_t ndev,
     176             :                                   precision* d,
     177             :                                   std::size_t nb,
     178             :                                   precision* b ) {
     179             :     return ParActionsOutput{
     180             :       View{v,ncomp}, //values
     181             :       View{d,ndev},  // derivatives
     182             :       View{b,nb}     // buffer
     183             :     };
     184             :   }
     185             : };
     186             : 
     187             : struct ForceIndexHolder {
     188             :   View<std::size_t> threadsafe_derivatives_end;
     189             :   View<std::size_t> tot_indices;
     190             :   View2D<std::size_t> indices;
     191             : 
     192             : /// Constructs a ForceIndexHolder object.
     193             : /// \param nc Definition here (number of components?)
     194             : /// \param nd Definition here (number of derivatives?)
     195             : /// \param ind Pointer to an array storing index data. It should have a size of at least 2*nc + nc*nd.
     196             :   static ForceIndexHolder create(const std::size_t nc,
     197             :                                  const std::size_t nd,
     198             :                                  std::size_t* ind ) {
     199             :     return ForceIndexHolder{
     200             :       View{ind,nc},        // threadsafe_derivatives_end
     201             :       View{ind+nc,nc},     // tot_indices
     202             :       View2D{ind+2*nc,nc,nd} // indices
     203             :     };
     204             :   }
     205             : 
     206             :   template<typename precision>
     207             :   static ForceIndexHolder create(const ParActionsInput<precision>& inp,
     208             :                                  std::size_t* ind ) {
     209             :     return create(inp.ncomponents,
     210             :                   inp.nderivatives_per_scalar,ind);
     211             :   }
     212             : 
     213             : /// \brief Returns the number of indexes needed by a ForceIndexHolder
     214             : ///        for each scalar.
     215             : ///
     216             : /// \param nc Definition here (number of components?)
     217             : /// \param nd Definition here (number of derivatives?)
     218             : ///
     219             : /// \return the number of indexes needed by a ForceIndexHolder
     220             : ///         for each scalar.
     221             :   static size_t indexesPerScalar(const std::size_t nc, const std::size_t nd) {
     222             :     return nc       // threadsafe_derivatives_end
     223             :            + nc     // tot_indices
     224             :            + nc*nd; // indices
     225             :   }
     226             :   template<typename precision>
     227             :   static size_t indexesPerScalar(const ParActionsInput<precision>& inp) {
     228             :     return indexesPerScalar(inp.ncomponents,
     229             :                             inp.nderivatives_per_scalar);
     230             :   }
     231             : };
     232             : 
     233             : template <typename precision>
     234             : class ForcesInput {
     235             : public:
     236             :   View<precision> force;
     237             :   View2D<precision> deriv;
     238             :   static ForcesInput create( std::size_t nv, precision* f, std::size_t nd, precision* d ) {
     239             :     return ForcesInput{
     240             :       View{f,nv},     //force
     241             :       View2D{d,nv,nd} //deriv
     242             :     };
     243             :   }
     244             : };
     245             : 
     246             : //There is no need to pass this as reference:
     247             : template <typename precision>
     248             : struct ForcesOutput {
     249             :   //I would suggest to invert the name or to be clearer
     250             : // like something that recalls that "thread_safe" will be need to be reducted (hence it is NOT thread safe)
     251             :   View<precision> thread_safe;
     252             :   //these are the forces that we promise will not provoke races
     253             :   View<precision> thread_unsafe;
     254             :   //const T* is a ptr to const T
     255             :   //T* const is a conts ptr to a modifiable T
     256             :   static ForcesOutput create(std::vector<precision>& reduced, std::vector<precision>& notReduced) {
     257             :     return ForcesOutput{
     258             :       View{reduced.data(),reduced.size()},      // thread_safe
     259             :       View{notReduced.data(),notReduced.size()} // thread_unsafe
     260             :     };
     261             :   }
     262             :   static ForcesOutput create(precision* reduced, size_t rs, precision* notReduce, size_t nrsz) {
     263             :     return ForcesOutput{
     264             :       View{reduced,rs},    // thread_safe
     265             :       View{notReduce,nrsz} // thread_unsafe
     266             :     };
     267             :   }
     268             : };
     269             : 
     270             : // namespace PTMUtils {
     271             : // template<class, class = void>
     272             : // constexpr bool has_gatherForces_custom = false;
     273             : //
     274             : // //this verifies that T has a method gatherForces_custom that can be called with this signature
     275             : // template<class T>
     276             : // constexpr bool has_gatherForces_custom <
     277             : // T,
     278             : // std::void_t<
     279             : // decltype(T::gatherForces_custom(
     280             : //            std::declval<unsigned >(),
     281             : //            std::declval<size_t >(),
     282             : //            std::declval<size_t >(),
     283             : //            std::declval<const typename T::input_type & >(),
     284             : //            std::declval<const ParActionsInput& >(),
     285             : //            std::declval<View<unsigned> >(),
     286             : //            std::declval<double *>(),
     287             : //            std::declval<double *>(),
     288             : //            std::declval<View<double> >()
     289             : //          ))
     290             : // >
     291             : // > = true;
     292             : //
     293             : // template<class, class = void>
     294             : // constexpr bool has_gatherForces_GPU = false;
     295             : //
     296             : // //this verifies that T has a method gatherForces_custom that can be called with this signature
     297             : // template<class T>
     298             : // constexpr bool has_gatherForces_GPU <
     299             : // T,
     300             : // std::void_t<
     301             : // decltype(T::gatherForcesGPU(
     302             : //            std::declval<unsigned >(),
     303             : //            std::declval<const typename T::input_type & >(),
     304             : //            std::declval<const ParActionsInput& >(),
     305             : //            std::declval<const ForceInput& >(),
     306             : //            std::declval<ForceOutput >()
     307             : //          ))
     308             : // >
     309             : // > = true;
     310             : //
     311             : // /// If the template class has virialSize, otherwise is 9
     312             : // template<class, class=void>
     313             : // constexpr size_t virialSize = 9;
     314             : //
     315             : // template<class T>
     316             : // constexpr size_t virialSize<T, std::void_t<decltype(T::virialSize),
     317             : // //this ensures that T::virialSize is a static member
     318             : //           std::enable_if_t<!std::is_member_pointer_v<decltype(&T::virialSize)>>
     319             : //           >>
     320             : //           = T::virialSize;
     321             : // } //namespace PTMUtils
     322             : 
     323             : template <typename CV, typename=void>
     324             : struct cvprecision {
     325             :   typedef double type;
     326             : };
     327             : 
     328             : template <typename CV>
     329             : struct cvprecision<CV,std::void_t<typename CV::precision>> {
     330             :   typedef typename CV::precision type;
     331             : };
     332             : 
     333             : template <typename CV>
     334             : using cvprecision_t = typename cvprecision<CV>::type;
     335             : 
     336             : template <class T>
     337             : class ParallelTaskManager {
     338             : public:
     339             :   using input_type= typename T::input_type;
     340             :   using precision = cvprecision_t<T>;
     341             :   typedef ParActionsInput<precision> ParallelActionsInput;
     342             :   typedef ParActionsOutput<precision> ParallelActionsOutput;
     343             :   typedef ForcesInput<precision> ForceInput;
     344             :   typedef ForcesOutput<precision> ForceOutput;
     345             : protected:
     346             : /// The underlying action for which we are managing parallel tasks
     347             :   ActionWithVector* action;
     348             : /// The MPI communicator
     349             :   Communicator& comm;
     350             : /// True if not using MPI for parllisation
     351             :   bool serial;
     352             : /// Are we using acc for parallisation
     353             :   bool useacc;
     354             : /// Check if the value of the derivative is known to be zero if the value
     355             : /// is zero.  Used to optimise force calculation
     356             :   bool derivativesZeroWhenValueZero;
     357             : /// Number of derivatives calculated for each task
     358             :   std::size_t nderivatives_per_task;
     359             : /// The number of forces on each thread
     360             : /// The number of forces on each thread
     361             :   std::size_t nthreaded_forces;
     362             : /// This holds the values before we pass them to the value
     363             :   std::vector<precision> value_stash;
     364             : /// A temporary set of vectors for holding forces over threads
     365             :   std::vector<std::vector<precision> > omp_forces;
     366             : /// This structs is used to pass data between the parallel interface and the function caller
     367             :   ParallelActionsInput myinput;
     368             :   ArgumentsBookkeeping argumentsMap;
     369             : //this holds the data for myinput that will be passed though myinput
     370             :   std::vector<precision> input_buffer;
     371             : /// This holds temporary data that we use in performTask
     372             :   std::size_t workspace_size;
     373             : /// This holds data for that the underlying action needs to do the calculation
     374             :   input_type actiondata;
     375             : //// This is used internally to get the number of elements in the value stash
     376             :   std::size_t getValueStashSize() const ;
     377             : /// This is used internally to gather the forces on the threads
     378             :   void gatherThreads( ForceOutput forces );
     379             : public:
     380             :   static void registerKeywords( Keywords& keys );
     381             :   ParallelTaskManager(ActionWithVector* av);
     382             : /// Setup the parallel task manager the three arguments are
     383             : /// nder = number of derivatives per scalar
     384             : /// nforce_ts = number of forces that are modified by multiple tasks
     385             :   void setupParallelTaskManager( std::size_t nder, std::size_t nforce_ts );
     386             : /// Copy the data from the underlying colvar into this parallel action
     387             :   void setActionInput( const input_type& adata );
     388             : /// Creating the size of the workspace
     389             :   void setWorkspaceSize( std::size_t size );
     390             : //// Set the number of force scalars per row
     391             :   void setNForceScalars( const unsigned& n );
     392             : /// Get the action input so we can use it
     393             :   input_type& getActionInput();
     394             :   const input_type& getActionInput() const ;
     395             : /// Is the calculation running in serial
     396             :   bool runInSerial() const {
     397        9836 :     return serial;
     398             :   }
     399             : /// This runs all the tasks
     400             :   void runAllTasks();
     401             : /// Apply the forces on the parallel object
     402             :   void applyForces( std::vector<double>& forcesForApply, bool reset_forces=true );
     403             : /// This is used to gather forces that are thread safe
     404             :   static void gatherThreadSafeForces( const ParallelActionsInput& input,
     405             :                                       const ForceIndexHolder& force_indices,
     406             :                                       const ForceInput& fdata,
     407             :                                       View<precision> forces );
     408             : /// This is used to gather forces that are not thread safe
     409             :   static void gatherThreadUnsafeForces( const ParallelActionsInput& input,
     410             :                                         const ForceIndexHolder& force_indices,
     411             :                                         const ForceInput& fdata,
     412             :                                         View<precision> forces );
     413             : };
     414             : 
     415             : struct defaultPTM {
     416             :   template <typename ACC>
     417             :   using PTM=ParallelTaskManager<ACC>;
     418             : };
     419             : 
     420             : template <class T>
     421       10037 : void ParallelTaskManager<T>::registerKeywords( Keywords& keys ) {
     422       10037 :   keys.addFlag("SERIAL",false,"do the calculation in serial.  Do not parallelize");
     423       10037 :   keys.addFlag("USEGPU",false,"run this calculation on the GPU");
     424       20074 :   keys.addLinkInDocForFlag("USEGPU","plugin_openaccPTM.md");
     425       20074 :   keys.addLinkInDocForFlag("SERIAL", "actions.md");
     426       10037 : }
     427             : 
     428             : template <class T>
     429        3920 : ParallelTaskManager<T>::ParallelTaskManager(ActionWithVector* av):
     430        3920 :   action(av),
     431        3920 :   comm(av->comm),
     432        3920 :   useacc(false),
     433        3920 :   derivativesZeroWhenValueZero(false),
     434        3920 :   nderivatives_per_task(0),
     435        3920 :   nthreaded_forces(0),
     436             :   myinput(ParallelActionsInput::create(av->getPbc())),
     437        3920 :   workspace_size(0) {
     438        3920 :   action->parseFlag("USEGPU",useacc);
     439             : #ifdef __PLUMED_USE_OPENACC
     440             :   if( useacc ) {
     441             :     action->log.printf("  using GPU to calculate this action\n");
     442             :   }
     443             : #else
     444        3920 :   if( useacc ) {
     445           0 :     action->error("cannot use USEGPU flag as PLUMED has not been compiled with openacc");
     446             :   }
     447             : #endif
     448        3920 :   action->parseFlag("SERIAL",serial);
     449        3920 :   if( serial ) {
     450           0 :     action->log.printf("  not using MPI to parallelise this action\n");
     451             :   }
     452        3920 : }
     453             : 
     454             : template <class T>
     455      219329 : std::size_t ParallelTaskManager<T>::getValueStashSize() const {
     456             :   std::size_t valuesize=0;
     457      481196 :   for(unsigned i=0; i<action->getNumberOfComponents(); ++i) {
     458      261867 :     valuesize += action->getSizeOfValueInStash( action->getConstPntrToComponent(i) );
     459             :   }
     460      219329 :   return valuesize;
     461             : }
     462             : 
     463             : 
     464             : template <class T>
     465       36130 : void ParallelTaskManager<T>::setupParallelTaskManager( std::size_t nder,
     466             :     std::size_t nforce_ts ) {
     467       36130 :   plumed_massert( action->getNumberOfComponents()>0, "there should be some components wen you setup the index list" );
     468       36130 :   myinput.ncomponents = action->getNumberOfComponents();
     469       36130 :   unsigned ntasks=0;
     470       36130 :   action->getNumberOfTasks( ntasks );
     471       36130 :   myinput.nscalars = 0;
     472       36130 :   myinput.nforcescalars = 0;
     473       79585 :   for(unsigned i=0; i<myinput.ncomponents; ++i) {
     474       43455 :     if( (action->copyOutput(i))->hasDerivatives() ) {
     475        2974 :       myinput.nscalars += 1 + action->getNumberOfDerivatives();
     476        2974 :       myinput.nforcescalars += 1;
     477       40481 :     } else if( (action->copyOutput(i))->getRank()==1 ) {
     478       19296 :       myinput.nscalars += 1;
     479       19296 :       myinput.nforcescalars += 1;
     480       21185 :     } else if( (action->copyOutput(i))->getRank()==2 ) {
     481       21185 :       if( ntasks==(action->copyOutput(i))->getShape()[0] ) {
     482       14011 :         myinput.nscalars += (action->copyOutput(i))->getNumberOfColumns();
     483       14011 :         myinput.nforcescalars += (action->copyOutput(i))->getNumberOfColumns();
     484             :       } else {
     485        7174 :         myinput.nscalars += 1;
     486        7174 :         myinput.nforcescalars += 1;
     487             :       }
     488             :     }
     489             :   }
     490       36130 :   myinput.nderivatives_per_scalar = nder;
     491       36130 :   nderivatives_per_task = nder*myinput.nforcescalars;
     492       36130 :   value_stash.resize( getValueStashSize() );
     493       36130 :   myinput.threadunsafe_forces_start = action->getNumberOfForceDerivatives() - nforce_ts;
     494       36130 :   unsigned t=OpenMP::getNumThreads();
     495       36130 :   if( useacc ) {
     496             :     t = 1;
     497             :   }
     498       36130 :   omp_forces.resize(t);
     499      108390 :   for(unsigned i=0; i<t; ++i) {
     500       72260 :     omp_forces[i].resize(nforce_ts);
     501             :   }
     502             :   // Check if we know the derivative of the component is zero if the value is zero
     503       36130 :   derivativesZeroWhenValueZero = true;
     504       70838 :   for(unsigned i=0; i<action->getNumberOfComponents(); ++i) {
     505       42475 :     if( !(action->getConstPntrToComponent(i))->isDerivativeZeroWhenValueIsZero() ) {
     506        7767 :       derivativesZeroWhenValueZero = false;
     507        7767 :       break;
     508             :     }
     509             :   }
     510       36130 : }
     511             : 
     512             : template <class T>
     513         122 : void ParallelTaskManager<T>::setNForceScalars( const unsigned& n ) {
     514         122 :   myinput.nscalars = n;
     515         122 :   myinput.nforcescalars = n;
     516         122 :   nderivatives_per_task = myinput.nderivatives_per_scalar*myinput.nforcescalars;
     517         122 : }
     518             : 
     519             : template <class T>
     520        1474 : void ParallelTaskManager<T>::setActionInput( const input_type& adata ) {
     521        1060 :   actiondata=adata;
     522        1474 : }
     523             : 
     524             : template <class T>
     525      294401 : typename ParallelTaskManager<T>::input_type& ParallelTaskManager<T>::getActionInput() {
     526      294401 :   return actiondata;
     527             : }
     528             : 
     529             : template <class T>
     530      820787 : const typename ParallelTaskManager<T>::input_type& ParallelTaskManager<T>::getActionInput() const {
     531      820787 :   return actiondata;
     532             : }
     533             : 
     534             : template <class T>
     535       13003 : void ParallelTaskManager<T>::setWorkspaceSize( std::size_t size ) {
     536       13003 :   workspace_size = size;
     537       13003 : }
     538             : 
     539             : template <class T>
     540      183199 : void ParallelTaskManager<T>::runAllTasks() {
     541             :   // Get the list of active tasks
     542      183199 :   std::vector<unsigned> & partialTaskList( action->getListOfActiveTasks() );
     543      183199 :   unsigned nactive_tasks=partialTaskList.size();
     544             :   // Get all the input data so we can broadcast it to the GPU
     545      183199 :   myinput.noderiv = true;
     546      394476 :   for(unsigned i=0; i<action->getNumberOfComponents(); ++i) {
     547      218412 :     if( (action->getConstPntrToComponent(i))->hasDerivatives() ) {
     548        7135 :       myinput.noderiv=false;
     549        7135 :       break;
     550             :     }
     551             :   }
     552      183199 :   action->getInputData( input_buffer, argumentsMap );
     553      183199 :   myinput.dataSize = input_buffer.size();
     554      183199 :   myinput.inputdata = input_buffer.data();
     555             :   // Transfer all the bookeeping information about the arguments
     556      183199 :   myinput.setupArguments( argumentsMap );
     557             :   // Reset the values at the start of the task loop
     558      183199 :   std::size_t totalvals=getValueStashSize();
     559      183199 :   if( value_stash.size()!=totalvals ) {
     560          62 :     value_stash.resize(totalvals);
     561             :   }
     562             :   std::fill (value_stash.begin(),value_stash.end(), precision(0.0));
     563      183199 :   if( useacc ) {
     564             :     // there should not be any available path to get here
     565           0 :     plumed_merror("to use the USEGPU flag you need to LOAD a plugin like \"openaccPTM\"");
     566             :   } else {
     567             :     // Get the MPI details
     568      183199 :     unsigned stride=comm.Get_size();
     569      183199 :     unsigned rank=comm.Get_rank();
     570      183199 :     if(serial) {
     571             :       stride=1;
     572             :       rank=0;
     573             :     }
     574             : 
     575             :     // Get number of threads for OpenMP
     576      183199 :     unsigned nt=OpenMP::getNumThreads();
     577      183199 :     if( nt*stride*10>nactive_tasks ) {
     578      101146 :       nt=nactive_tasks/stride/10;
     579             :     }
     580             :     if( nt==0 ) {
     581             :       nt=1;
     582             :     }
     583             : 
     584      183199 :     #pragma omp parallel num_threads(nt)
     585             :     {
     586             :       std::vector<precision> buffer( workspace_size );
     587             :       std::vector<precision> derivatives( nderivatives_per_task );
     588             :       #pragma omp for nowait
     589             :       for(unsigned i=rank; i<nactive_tasks; i+=stride) {
     590             :         std::size_t task_index = partialTaskList[i];
     591             :         std::size_t val_pos = task_index*myinput.nscalars;
     592             :         auto myout = ParallelActionsOutput::create ( myinput.nscalars,
     593             :                      value_stash.data()+val_pos,
     594             :                      nderivatives_per_task,
     595             :                      derivatives.data(),
     596             :                      workspace_size,
     597             :                      buffer.data() );
     598             :         // Calculate the stuff in the loop for this action
     599             :         T::performTask( task_index, actiondata, myinput, myout );
     600             :       }
     601             :     }
     602             :     // MPI Gather everything
     603      183199 :     if( !serial ) {
     604      183199 :       comm.Sum( value_stash );
     605             :     }
     606             :   }
     607             : // And transfer the value to the output values
     608      183199 :   action->transferStashToValues( partialTaskList, value_stash );
     609      183199 : }
     610             : 
     611             : template<typename prec>
     612             : struct forceData {
     613             :   std::vector<prec> ffa;
     614             :   // the forces already in forcesForApply are kept: the caller may be
     615             :   // accumulating over several applyForces calls (reset_forces=false)
     616             :   forceData( std::vector<double>& forcesForApply ):
     617             :     ffa(forcesForApply.begin(),forcesForApply.end()),destination(forcesForApply) {}
     618             :   void update() {
     619             :     std::copy(ffa.begin(),ffa.end(),destination.begin());
     620             :   }
     621             : private:
     622             :   std::vector<double>& destination;
     623             : };
     624             : 
     625             : template<>
     626             : struct forceData<double> {
     627             :   PLMD::View<double> ffa;
     628             :   forceData( std::vector<double>& forcesForApply ):
     629             :     ffa(forcesForApply.data(),forcesForApply.size()) {}
     630             :   void update() {
     631             :   }
     632             : };
     633             : template <class T>
     634      142436 : void ParallelTaskManager<T>::applyForces( std::vector<double>& forcesForApply, bool reset_forces ) {
     635             :   // Get the list of active tasks
     636      142436 :   std::vector<unsigned> & partialTaskList= action->getListOfActiveTasks() ;
     637      142436 :   unsigned nactive_tasks=partialTaskList.size();
     638             :   forceData<precision> forces(forcesForApply);
     639             :   // Only set to false if we are doing a loop over the columns of a matrix to avoid
     640             :   // having to gather forces across multiple nodes
     641      142436 :   if( reset_forces ) {
     642             :     // Clear force buffer
     643             :     std::fill (forces.ffa.begin(),forces.ffa.end(), precision(0.0));
     644             :   }
     645             :   // Get all the input data so we can broadcast it to the GPU
     646      142436 :   myinput.noderiv = false;
     647             :   // Retrieve the forces from the values
     648      142436 :   action->transferForcesToStash( partialTaskList, value_stash );
     649             : 
     650      142436 :   if( useacc ) {
     651             :     // there should not be any available path to get here
     652           0 :     plumed_merror("to use the USEGPU flag you need to LOAD a plugin like \"openaccPTM\"");
     653             :   } else {
     654             :     // Get the MPI details
     655      142436 :     unsigned stride=comm.Get_size();
     656      142436 :     unsigned rank=comm.Get_rank();
     657      142436 :     if(serial) {
     658             :       stride=1;
     659             :       rank=0;
     660             :     }
     661             : 
     662             :     // Get number of threads for OpenMP
     663      142436 :     unsigned nt=OpenMP::getNumThreads();
     664      142436 :     if( nt*stride*10>nactive_tasks ) {
     665       77520 :       nt=nactive_tasks/stride/10;
     666             :     }
     667             :     if( nt==0 ) {
     668             :       nt=1;
     669             :     }
     670      142436 :     #pragma omp parallel num_threads(nt)
     671             :     {
     672             :       const unsigned t=OpenMP::getThreadNum();
     673             :       omp_forces[t].assign( omp_forces[t].size(), 0.0 );
     674             :       std::vector<precision> buffer( workspace_size );
     675             :       std::vector<precision> fake_vals( myinput.sizeOfFakeVals() );
     676             :       std::vector<precision> derivatives( nderivatives_per_task );
     677             :       std::vector<std::size_t> indices(ForceIndexHolder::indexesPerScalar(myinput));
     678             : 
     679             :       auto force_indices = ForceIndexHolder::create( myinput,indices.data() );
     680             :       #pragma omp for nowait
     681             :       for(unsigned i=rank; i<nactive_tasks; i+=stride) {
     682             :         std::size_t task_index = partialTaskList[i];
     683             :         auto myout = ParallelActionsOutput::create( myinput.nscalars,
     684             :                      fake_vals.data(),
     685             :                      derivatives.size(),
     686             :                      derivatives.data(),
     687             :                      workspace_size,
     688             :                      buffer.data() );
     689             :         // Calculate the stuff in the loop for this action
     690             :         T::performTask( task_index, actiondata, myinput, myout );
     691             : 
     692             :         // If this is a matrix this returns a number that isn't one as we have to loop over the columns
     693             :         const std::size_t nvpt = T::getNumberOfValuesPerTask( task_index, actiondata );
     694             :         for(unsigned j=0; j<nvpt; ++j) {
     695             :           bool noforces = false;
     696             :           // Skip the force calculation if we have been told that we can do this when the value is zero
     697             :           if( derivativesZeroWhenValueZero ) {
     698             :             noforces = true;
     699             :             for(unsigned k=0; k<myinput.ncomponents; ++k) {
     700             :               if( fabs(fake_vals[j*myinput.ncomponents+k])>epsilon ) {
     701             :                 noforces = false;
     702             :               }
     703             :             }
     704             :           }
     705             :           if( !noforces ) {
     706             :             // Get the force indices
     707             :             T::getForceIndices( task_index,
     708             :                                 j,
     709             :                                 forces.ffa.size(),
     710             :                                 actiondata,
     711             :                                 myinput,
     712             :                                 force_indices );
     713             :             // Create a force input object
     714             :             auto finput=ForceInput::create( myinput.nforcescalars,
     715             :                                             value_stash.data()
     716             :                                             + myinput.nforcescalars*task_index
     717             :                                             + j*myinput.ncomponents,
     718             :                                             myinput.nderivatives_per_scalar,
     719             :                                             derivatives.data()
     720             :                                             + j*myinput.ncomponents*myinput.nderivatives_per_scalar );
     721             : 
     722             :             // Gather forces that are thread safe
     723             :             gatherThreadSafeForces( myinput,
     724             :                                     force_indices,
     725             :                                     finput,
     726             :                                     View<double>(forces.ffa.data(),
     727             :                                                  forces.ffa.size()) );
     728             : 
     729             :             // Gather forces that are not thread safe
     730             :             gatherThreadUnsafeForces( myinput,
     731             :                                       force_indices,
     732             :                                       finput,
     733             :                                       View<precision>(omp_forces[t].data(),
     734             :                                           omp_forces[t].size()) );
     735             :           }
     736             :         }
     737             :       }
     738             : 
     739             :       #pragma omp critical
     740             :       gatherThreads( ForceOutput::create(omp_forces[t], forcesForApply ) );
     741             :     }
     742             :     forces.update();
     743             :     // MPI Gather everything (this must be extended to the gpu thing, after makning it mpi-aware)
     744      142436 :     if( !serial ) {
     745      142436 :       comm.Sum( forcesForApply );
     746             :     }
     747             :   }
     748      142436 : }
     749             : 
     750             : template <class T>
     751    16827294 : void ParallelTaskManager<T>::gatherThreadSafeForces( const ParallelActionsInput& input,
     752             :     const ForceIndexHolder& force_indices,
     753             :     const ForceInput& fdata,
     754             :     View<precision> forces ) {
     755    50115034 :   for(unsigned i=0; i<input.ncomponents; ++i) {
     756    33287740 :     double ff = fdata.force[i];
     757   117423093 :     for(unsigned j=0; j<force_indices.threadsafe_derivatives_end[i]; ++j) {
     758    84135353 :       forces[ force_indices.indices[i][j] ] += ff*fdata.deriv[i][j];
     759             :     }
     760             :   }
     761    16827294 : }
     762             : 
     763             : template <class T>
     764    16827294 : void ParallelTaskManager<T>::gatherThreadUnsafeForces(const ParallelActionsInput& input,
     765             :     const ForceIndexHolder& force_indices,
     766             :     const ForceInput& fdata,
     767             :     View<precision> forces ) {
     768    50115034 :   for(unsigned i=0; i<input.ncomponents; ++i) {
     769    33287740 :     const double ff = fdata.force[i];
     770   195692138 :     for(unsigned d=force_indices.threadsafe_derivatives_end[i];
     771   195692138 :         d<force_indices.tot_indices[i]; ++d) {
     772   162404398 :       forces[ force_indices.indices[i][d] - input.threadunsafe_forces_start ]
     773   162404398 :       += ff*fdata.deriv[i][d];
     774             :     }
     775             :   }
     776    16827294 : }
     777             : 
     778             : template <class T>
     779      207352 : void ParallelTaskManager<T>::gatherThreads( ForceOutput forces ) {
     780             :   //Forceoutput is basically two spans, so it is ok to pass it by value
     781             :   unsigned k=0;
     782     8692140 :   for(unsigned n=forces.thread_unsafe.size()-forces.thread_safe.size(); n<forces.thread_unsafe.size(); ++n) {
     783     8484788 :     forces.thread_unsafe[n] += forces.thread_safe[k];
     784     8484788 :     ++k;
     785             :   }
     786      207352 : }
     787             : 
     788             : } // namespace PLMD
     789             : #endif

Generated by: LCOV version 1.16