LCOV - code coverage report
Current view: top level - core - ActionWithMatrix.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 136 153 88.9 %
Date: 2026-11-05 10:19:04 Functions: 11 16 68.8 %

          Line data    Source code
       1             : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
       2             :    Copyright (c) 2011-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             : #include "ActionWithMatrix.h"
      23             : #include "tools/Communicator.h"
      24             : #include "tools/OpenMP.h"
      25             : 
      26             : namespace PLMD {
      27             : 
      28        1711 : void ActionWithMatrix::registerKeywords( Keywords& keys ) {
      29        1711 :   ActionWithVector::registerKeywords( keys );
      30        1711 :   keys.addFlag("AVOID_THREAD_GATHER",false,"avoid race conditions in shared memory by doing more calculations rather than using more memory");
      31        1711 :   keys.addFlag("DO_THREAD_GATHER",false,"do fewer calculations and use more memory");
      32        1711 : }
      33             : 
      34         544 : ActionWithMatrix::ActionWithMatrix(const ActionOptions&ao):
      35             :   Action(ao),
      36             :   ActionWithVector(ao),
      37         544 :   maxcolsize(0),
      38         544 :   diagzero(false),
      39         544 :   gatherForceOnColumns(false) {
      40             : 
      41         544 :   if( keywords.exists("ELEMENTS_ON_DIAGONAL_ARE_ZERO") ) {
      42         176 :     parseFlag("ELEMENTS_ON_DIAGONAL_ARE_ZERO",diagzero);
      43         176 :     if( diagzero ) {
      44          28 :       log.printf("  setting diagonal elements equal to zero\n");
      45             :     }
      46             :   }
      47         544 :   if( getName().find("ACC")!=std::string::npos ) {
      48             :     bool ignore;
      49           0 :     parseFlag("AVOID_THREAD_GATHER",ignore);
      50           0 :     if( ignore ) {
      51           0 :       warning("AVOID_THREAD_GATHER flag is always activated when the calculation is run on the GPU as the calculation is much faster with this option activated");
      52             :     }
      53             :     bool dothreads;
      54           0 :     parseFlag("DO_THREAD_GATHER",dothreads);
      55           0 :     if( !dothreads ) {
      56           0 :       no_thread_gather=true;
      57             :     } else {
      58           0 :       no_thread_gather=false;
      59             :     }
      60             :   } else {
      61         544 :     parseFlag("AVOID_THREAD_GATHER",no_thread_gather);
      62         544 :     if( OpenMP::getNumThreads()==1 ) {
      63           0 :       no_thread_gather = false;
      64             :     }
      65             :     bool ignore;
      66         544 :     parseFlag("DO_THREAD_GATHER",ignore);
      67         544 :     if( ignore ) {
      68           0 :       warning("NO_THREAD_GATHER flag is always activated when the calculation is run on the CPU as the calculation is faster with this option activated");
      69             :     }
      70             :   }
      71         544 :   if( no_thread_gather && comm.Get_size()>1 ) {
      72           0 :     error("AVOID_THREAD_GATHER keyword is incompatible with MPI - it should be possible to fix this. Email: gareth.tribello@gmail.com if you are interested");
      73             :   }
      74         544 :   if( no_thread_gather ) {
      75          15 :     log.printf("  turning on low memory implementation for force gathering\n");
      76             :   }
      77         544 : }
      78             : 
      79             : 
      80             : class RequiredMatrixElementsUpdater {
      81             :   RequiredMatrixElements& outmat;
      82             : public:
      83             :   RequiredMatrixElementsUpdater( RequiredMatrixElements& mat ) : outmat(mat) {}
      84             :   ~RequiredMatrixElementsUpdater() {
      85             :     outmat.update();
      86             :   }
      87             : };
      88             : 
      89       13058 : void ActionWithMatrix::copyMatrixBookeepingFromFirstComponent( RequiredMatrixElements& outmat ) {
      90             :   RequiredMatrixElementsUpdater updater(outmat);
      91       13058 :   Value* mycomp = getPntrToComponent(0);
      92       13058 :   outmat.ncols = mycomp->getNumberOfColumns();
      93             :   outmat.resize( mycomp->matrix_bookeeping.size() );
      94    48633647 :   for(unsigned i=0; i<outmat.size(); ++i) {
      95    48620589 :     outmat[i] = mycomp->matrix_bookeeping[i];
      96             :   }
      97       13058 :   if( !no_thread_gather || doNotCalculateDerivatives() ) {
      98             :     return;
      99             :   }
     100          62 :   std::vector<unsigned> column_totals( getConstPntrToComponent(0)->getShape()[1], 0 );
     101         698 :   for(unsigned i=0; i<mycomp->getShape()[0]; ++i) {
     102       17280 :     for(unsigned j=0; j<mycomp->getRowLength(i); ++j) {
     103       16644 :       column_totals[ mycomp->getRowIndex(i,j) ]++;
     104             :     }
     105             :   }
     106             :   unsigned n_nonzerocols = 0;
     107          62 :   maxcolsize = column_totals[0];
     108          62 :   if( maxcolsize>0 ) {
     109             :     n_nonzerocols = 1;
     110             :   }
     111        2232 :   for(unsigned i=1; i<column_totals.size(); ++i) {
     112        2170 :     if( column_totals[i]>maxcolsize ) {
     113           5 :       maxcolsize = column_totals[i];
     114             :     }
     115        2170 :     if( column_totals[i]>0 ) {
     116         873 :       n_nonzerocols++;
     117             :     }
     118             :   }
     119          62 :   column_list.resize( n_nonzerocols );
     120             :   n_nonzerocols = 0;
     121        2294 :   for(unsigned i=0; i<column_totals.size(); ++i) {
     122        2232 :     if( column_totals[i]>0 ) {
     123         935 :       column_list[n_nonzerocols]=i;
     124         935 :       n_nonzerocols++;
     125             :     }
     126             :   }
     127             : }
     128             : 
     129         465 : void ActionWithMatrix::updateBookeepingArrays( RequiredMatrixElements& outmat ) {
     130         465 :   Value* myval = getPntrToComponent(0);
     131         465 :   unsigned lstart = myval->getShape()[0];
     132         465 :   if( getNumberOfMasks()>0 ) {
     133         184 :     Value* maskarg = getPntrToArgument(getNumberOfArguments()-getNumberOfMasks());
     134         449 :     for(unsigned i=0; i<getNumberOfComponents(); ++i) {
     135         265 :       getPntrToComponent(i)->reshapeMatrixStore( maskarg->getNumberOfColumns() );
     136             :     }
     137      262759 :     for(unsigned i=0; i<lstart; ++i) {
     138      262575 :       unsigned rstart = i*(1+myval->getNumberOfColumns());
     139      262575 :       myval->setMatrixBookeepingElement( rstart, maskarg->getRowLength(i) );
     140    10879714 :       for(unsigned j=0; j<maskarg->getRowLength(i); ++j) {
     141    10354564 :         myval->setMatrixBookeepingElement( rstart + 1 + j, maskarg->getRowIndex(i, j) );
     142             :       }
     143             :     }
     144         281 :   } else if ( diagzero ) {
     145         420 :     for(unsigned i=0; i<getNumberOfArguments(); ++i) {
     146         280 :       if( getPntrToArgument(i)->getRank()==2 && getPntrToArgument(i)->getShape()[1]!=getPntrToArgument(i)->getNumberOfColumns() ) {
     147           0 :         error("DIAGZERO flag is incompatible with sparse matrices");
     148             :       }
     149             :     }
     150         280 :     for(unsigned i=0; i<getNumberOfComponents(); ++i) {
     151         140 :       getPntrToComponent(i)->reshapeMatrixStore( myval->getShape()[1]-1 );
     152             :     }
     153        2900 :     for(unsigned i=0; i<lstart; ++i) {
     154        2760 :       unsigned k=0, rstart = i*(1+myval->getNumberOfColumns());
     155        2760 :       myval->setMatrixBookeepingElement( rstart, myval->getShape()[1]-1 );
     156      204120 :       for(unsigned j=0; j<myval->getShape()[1]; ++j) {
     157      201360 :         if( i!=j ) {
     158      198600 :           myval->setMatrixBookeepingElement( rstart + 1 + k, j );
     159      198600 :           k++;
     160             :         }
     161             :       }
     162             :     }
     163             :   } else {
     164         300 :     for(unsigned i=0; i<getNumberOfComponents(); ++i) {
     165         159 :       getPntrToComponent(i)->reshapeMatrixStore( myval->getShape()[1] );
     166             :     }
     167             :   }
     168         465 :   copyMatrixBookeepingFromFirstComponent( outmat );
     169         564 :   for(unsigned i=1; i<getNumberOfComponents(); ++i) {
     170          99 :     getPntrToComponent(i)->copyBookeepingArrayFromArgument( myval );
     171             :   }
     172         465 : }
     173             : 
     174          61 : void ActionWithMatrix::getColumnBookeepingArrays( RequiredMatrixElements& outmat ) {
     175          61 :   outmat.ncols = maxcolsize;
     176          61 :   Value* mycomp = getPntrToComponent(0);
     177          61 :   outmat.resize( mycomp->getShape()[1]*(1+outmat.ncols) );
     178        2267 :   for(unsigned i=0; i<mycomp->getShape()[1]; ++i) {
     179        2206 :     outmat[i*(1+outmat.ncols)] = 0;
     180             :   }
     181         633 :   for(unsigned i=0; i<mycomp->getShape()[0]; ++i) {
     182       15552 :     for(unsigned j=0; j<mycomp->getRowLength(i); ++j) {
     183       14980 :       unsigned cstart = mycomp->getRowIndex(i,j)*( 1 + outmat.ncols );
     184       14980 :       outmat[cstart + 1 + outmat[cstart]] = i;
     185       14980 :       outmat[cstart]++;
     186             :     }
     187             :   }
     188          61 : }
     189             : 
     190       36905 : std::vector<unsigned>& ActionWithMatrix::getListOfActiveTasks() {
     191       36905 :   if( gatherForceOnColumns ) {
     192          61 :     return column_list;
     193             :   } else {
     194       36844 :     return ActionWithVector::getListOfActiveTasks();
     195             :   }
     196             : }
     197             : 
     198       27898 : unsigned ActionWithMatrix::getSizeOfValueInStash( const Value* myval ) {
     199       27898 :   unsigned nstored = myval->getNumberOfStoredValues();
     200       27898 :   if( no_thread_gather ) {
     201         184 :     unsigned ncolstore = maxcolsize*myval->getShape()[1];
     202         184 :     if( ncolstore>nstored ) {
     203             :       return ncolstore;
     204             :     }
     205             :   }
     206             :   return nstored;
     207             : }
     208             : 
     209             : // The stash holds the elements of a row contiguously, with the components of
     210             : // an element interleaved ([row][column][component]); each component Value
     211             : // stores its rows contiguously ([row][column]). The copies write the storage
     212             : // of the Values directly (ActionWithMatrix is a friend of Value), the rows
     213             : // are independent so the loop over them is parallel.
     214             : template <typename T>
     215       13058 : void ActionWithMatrix::matrixStashToValuesImpl( const std::vector<unsigned>& partialTaskList, const std::vector<T>& stash ) {
     216             :   const std::size_t ncomp = getNumberOfComponents();
     217       13058 :   const std::size_t ncols = getPntrToComponent(0)->getNumberOfColumns();
     218       13058 :   const unsigned nrows = partialTaskList.size();
     219       13058 :   std::vector<double*> data( ncomp );
     220       13058 :   std::vector<Value*> periodic;
     221       27007 :   for(std::size_t k=0; k<ncomp; ++k) {
     222       13949 :     Value* myval = getPntrToComponent(k);
     223             :     plumed_dbg_assert( !myval->hasDeriv );
     224       13949 :     myval->value_set=true;
     225       13949 :     data[k] = myval->data.data();
     226       13949 :     if( myval->isPeriodic() ) {
     227          55 :       periodic.push_back( myval );
     228             :     }
     229             :   }
     230       13058 :   const Value* first = getPntrToComponent(0);
     231             : #ifdef _OPENMP
     232             :   // without OpenMP nt would be unused (an error with -Werror)
     233       13058 :   const unsigned nt = OpenMP::getGoodNumThreads( stash );
     234             : #endif
     235       13058 :   #pragma omp parallel for num_threads(nt) schedule(static)
     236             :   for(unsigned i=0; i<nrows; ++i) {
     237             :     const std::size_t row = partialTaskList[i];
     238             :     const unsigned ncr = first->getRowLength(row);
     239             : #ifndef NDEBUG
     240             :     for(std::size_t k=1; k<ncomp; ++k) {
     241             :       plumed_assert( ncr == getPntrToComponent(k)->getRowLength(row) );
     242             :     }
     243             : #endif
     244             :     const T* src = stash.data() + ncomp*ncols*row;
     245             :     const std::size_t dst = row*ncols;
     246             :     for(std::size_t j=0; j<ncr; ++j) {
     247             :       for(std::size_t k=0; k<ncomp; ++k) {
     248             :         data[k][dst+j] = src[j*ncomp+k];
     249             :       }
     250             :     }
     251             :     // e.g. the matrix of torsions is periodic
     252             :     for(Value* myval : periodic) {
     253             :       for(std::size_t j=0; j<ncr; ++j) {
     254             :         myval->applyPeriodicity( dst+j );
     255             :       }
     256             :     }
     257             :   }
     258       13058 : }
     259             : 
     260             : template <typename T>
     261       12019 : void ActionWithMatrix::matrixForcesToStashImpl( const std::vector<unsigned>& partialTaskList, std::vector<T>& stash ) const {
     262             :   const std::size_t ncomp = getNumberOfComponents();
     263       12019 :   const std::size_t ncols = getConstPntrToComponent(0)->getNumberOfColumns();
     264       12019 :   const Value* first = getConstPntrToComponent(0);
     265       12019 :   std::vector<const double*> force( ncomp );
     266       24539 :   for(std::size_t k=0; k<ncomp; ++k) {
     267       12520 :     force[k] = getConstPntrToComponent(k)->inputForce.data();
     268             :   }
     269       12019 :   if( gatherForceOnColumns ) {
     270             :     // forces on the columns: element (i,j) of the matrix goes to the slot of
     271             :     // column j; the slots of a column are filled in the order of the rows
     272             :     const std::vector<unsigned>& rowTasks( getConstListOfActiveTasks() );
     273          61 :     const unsigned nrows = rowTasks.size();
     274          61 :     std::vector<unsigned> column_totals( first->getShape()[1], 0 );
     275         633 :     for(unsigned i=0; i<nrows; ++i) {
     276         572 :       const std::size_t row = rowTasks[i];
     277             :       const unsigned ncr = first->getRowLength(row);
     278       15552 :       for(std::size_t j=0; j<ncr; ++j) {
     279       14980 :         const std::size_t colno = first->getRowIndex(row,j);
     280       14980 :         T* dst = stash.data() + ncomp*maxcolsize*colno + column_totals[colno]*ncomp;
     281       42218 :         for(std::size_t k=0; k<ncomp; ++k) {
     282             :           plumed_dbg_assert( colno==getConstPntrToComponent(k)->getRowIndex(row,j) );
     283       27238 :           dst[k] = force[k][row*ncols+j];
     284             :         }
     285       14980 :         column_totals[colno]++;
     286             :       }
     287             :     }
     288             :     return;
     289             :   }
     290       11958 :   const unsigned nrows = partialTaskList.size();
     291             : #ifdef _OPENMP
     292             :   // without OpenMP nt would be unused (an error with -Werror)
     293       11958 :   const unsigned nt = OpenMP::getGoodNumThreads( stash );
     294             : #endif
     295       11958 :   #pragma omp parallel for num_threads(nt) schedule(static)
     296             :   for(unsigned i=0; i<nrows; ++i) {
     297             :     const std::size_t row = partialTaskList[i];
     298             :     const unsigned ncr = first->getRowLength(row);
     299             : #ifndef NDEBUG
     300             :     for(std::size_t k=1; k<ncomp; ++k) {
     301             :       plumed_assert( ncr == getConstPntrToComponent(k)->getRowLength(row) );
     302             :     }
     303             : #endif
     304             :     T* dst = stash.data() + ncomp*ncols*row;
     305             :     const std::size_t src = row*ncols;
     306             :     for(std::size_t j=0; j<ncr; ++j) {
     307             :       for(std::size_t k=0; k<ncomp; ++k) {
     308             :         dst[j*ncomp+k] = force[k][src+j];
     309             :       }
     310             :     }
     311             :   }
     312             : }
     313             : 
     314       13058 : void ActionWithMatrix::transferStashToValues( const std::vector<unsigned>& partialTaskList, const std::vector<double>& stash ) {
     315       13058 :   matrixStashToValuesImpl( partialTaskList, stash );
     316       13058 : }
     317             : 
     318           0 : void ActionWithMatrix::transferStashToValues( const std::vector<unsigned>& partialTaskList, const std::vector<float>& stash ) {
     319           0 :   matrixStashToValuesImpl( partialTaskList, stash );
     320           0 : }
     321             : 
     322       12019 : void ActionWithMatrix::transferForcesToStash( const std::vector<unsigned>& partialTaskList, std::vector<double>& stash ) const {
     323       12019 :   matrixForcesToStashImpl( partialTaskList, stash );
     324       12019 : }
     325             : 
     326           0 : void ActionWithMatrix::transferForcesToStash( const std::vector<unsigned>& partialTaskList, std::vector<float>& stash ) const {
     327           0 :   matrixForcesToStashImpl( partialTaskList, stash );
     328           0 : }
     329             : 
     330             : } //namespace PLMD

Generated by: LCOV version 1.16