LCOV - code coverage report
Current view: top level - core - DataPassingObject.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 109 115 94.8 %
Date: 2026-06-05 17:04:24 Functions: 19 29 65.5 %

          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             : #include "DataPassingObject.h"
      23             : #include "tools/OpenMP.h"
      24             : #include "tools/Tools.h"
      25             : 
      26             : namespace PLMD {
      27             : 
      28             : template<typename T>
      29      515290 : static void getPointer(const TypesafePtr & p, const std::vector<unsigned>& shape, const unsigned& start, const unsigned& stride, T*&pp ) {
      30      515290 :   if( shape.size()==1 && stride==1 ) {
      31       37226 :     pp=p.get<T*>( {shape[0]} );
      32      478064 :   } else if( shape.size()==1 ) {
      33      367488 :     auto p_=p.get<T*>( {shape[0],stride} );
      34      367470 :     pp = p_+start;
      35      110576 :   } else if( shape.size()==2 ) {
      36      110576 :     pp=p.get<T*>( {shape[0],shape[1]} );
      37             :   }
      38      515272 : }
      39             : 
      40             : template <class T>
      41             : class DataPassingObjectTyped : public DataPassingObject {
      42             : private:
      43             : /// A pointer to the value
      44             :   TypesafePtr v;
      45             : /// A pointer to the force
      46             :   TypesafePtr f;
      47             : public:
      48             : /// This convers a number from the MD code into a double
      49             :   double MD2double(const TypesafePtr &) const override ;
      50             : /// This is used when you want to save the passed object to a double variable in PLUMED rather than the pointer
      51             : /// this can be used even when you don't pass a pointer from the MD code
      52             :   void saveValueAsDouble( const TypesafePtr & val ) override;
      53             : /// Set the pointer to the value
      54             :   void setValuePointer( const TypesafePtr & p, const std::vector<unsigned>& shape, const bool& isconst ) override;
      55             : /// Set the pointer to the force
      56             :   void setForcePointer( const TypesafePtr & p, const std::vector<unsigned>& shape ) override;
      57             : /// This gets the data in the pointer and passes it to the output value
      58             :   void share_data( std::vector<double>& values ) const override ;
      59             : /// Share the data and put it in the value from sequential data
      60             :   void share_data( const unsigned& j, const unsigned& k, Value* value ) override;
      61             : /// Share the data and put it in the value from a scattered data
      62             :   void share_data( const std::vector<AtomNumber>&index, const std::vector<unsigned>& i, Value* value ) override;
      63             : /// Pass the force from the value to the output value
      64             :   void add_force( Value* vv ) override;
      65             :   void add_force( const std::vector<int>& index, Value* value ) override;
      66             :   void add_force( const std::vector<AtomNumber>& index, const std::vector<unsigned>& i, Value* value ) override;
      67             : /// Rescale the force on the output value
      68             :   void rescale_force( const unsigned& n, const double& factor, Value* value ) override;
      69             : /// This transfers everything to the output
      70             :   void setData( Value* value ) override;
      71             : };
      72             : 
      73        8634 : std::unique_ptr<DataPassingObject> DataPassingObject::create(unsigned n) {
      74        8634 :   if(n==sizeof(double)) {
      75        8627 :     return std::make_unique<DataPassingObjectTyped<double>>();
      76           7 :   } else  if(n==sizeof(float)) {
      77           7 :     return std::make_unique<DataPassingObjectTyped<float>>();
      78             :   }
      79             :   std::string pp;
      80           0 :   Tools::convert(n,pp);
      81           0 :   plumed_merror("cannot create an MD interface with sizeof(real)=="+ pp);
      82             :   return NULL;
      83             : }
      84             : 
      85             : template <class T>
      86           0 : double DataPassingObjectTyped<T>::MD2double(const TypesafePtr & m) const {
      87           0 :   double d=double(m.template get<T>());
      88           0 :   return d;
      89             : }
      90             : 
      91             : template <class T>
      92        5102 : void DataPassingObjectTyped<T>::saveValueAsDouble( const TypesafePtr & val ) {
      93        5102 :   hasbackup=true;
      94        5102 :   bvalue=double(val.template get<T>());
      95        5102 : }
      96             : 
      97             : template <>
      98           1 : void DataPassingObjectTyped<float>::saveValueAsDouble( const TypesafePtr & val ) {
      99           1 :   hasbackup=true;
     100           1 :   bvalue=double(val.template get<float>());
     101             :   // The following is to avoid extra digits in case the MD code uses floats
     102             :   // e.g.: float f=0.002 when converted to double becomes 0.002000000094995
     103             :   // To avoid this, we keep only up to 6 significant digits after first one
     104           1 :   double magnitude=std::pow(10,std::floor(std::log10(bvalue)));
     105           1 :   bvalue = std::round(bvalue/magnitude*1e6)/1e6*magnitude;
     106           1 : }
     107             : 
     108             : template <class T>
     109      459939 : void DataPassingObjectTyped<T>::setValuePointer( const TypesafePtr & val, const std::vector<unsigned>& shape, const bool& isconst ) {
     110      459939 :   if( shape.size()==0 ) {
     111      365865 :     if( isconst ) {
     112      365737 :       val.get<const T*>();
     113             :     } else {
     114         128 :       val.get<T*>();  // just check type and discard pointer
     115             :     }
     116       94074 :   } else if( shape.size()==1 ) {
     117       23470 :     if( isconst )
     118       23456 :       val.get<const T*>({shape[0]});
     119             :     else
     120          14 :       val.get<T*>({shape[0]});  // just check type and discard pointer
     121       70604 :   } else if( shape.size()==2 ) {
     122       70604 :     if( isconst )
     123       70601 :       val.get<const T*>({shape[0],shape[1]});
     124             :     else
     125           3 :       val.get<T*>({shape[0],shape[1]});  // just check type and discard pointer
     126             :   }
     127      459938 :   v=val.copy();
     128      459938 : }
     129             : 
     130             : template <class T>
     131      291183 : void DataPassingObjectTyped<T>::setForcePointer( const TypesafePtr & val, const std::vector<unsigned>& shape ) {
     132      291183 :   if( shape.size()==0 ) {
     133      224268 :     val.get<T*>();  // just check type and discard pointer
     134       66915 :   } else if( shape.size()==1 )
     135           0 :     val.get<T*>({shape[0]});   // just check type and discard pointer
     136       66915 :   else if( shape.size()==2 )
     137       66915 :     val.get<T*>({shape[0],shape[1]});   // just check type and discard pointer
     138      291167 :   f=val.copy();
     139      291165 : }
     140             : 
     141             : template <class T>
     142       12447 : void DataPassingObjectTyped<T>::setData( Value* value ) {
     143       12447 :   if( value->getRank()==0 ) {
     144         703 :     *v.template get<T*>() = static_cast<T>(value->get()) / unit;
     145         703 :     return;
     146             :   }
     147             :   T* pp;
     148       11744 :   getPointer( v, value->getShape(), start, stride, pp );
     149       11744 :   unsigned nvals=value->getNumberOfValues();
     150      429326 :   for(unsigned i=0; i<nvals; ++i) {
     151      417582 :     pp[i] = T( value->get(i) );
     152             :   }
     153             : }
     154             : 
     155             : template <class T>
     156      248449 : void DataPassingObjectTyped<T>::share_data( const unsigned& j, const unsigned& k, Value* value ) {
     157      248449 :   if( value->getRank()==0 ) {
     158        7433 :     if( hasbackup ) {
     159        7361 :       value->set( unit*bvalue );
     160             :     } else {
     161          72 :       value->set( unit*double(v.template get<T>()) );
     162             :     }
     163        7433 :     return;
     164             :   }
     165      241016 :   std::vector<unsigned> s(value->getShape());
     166      241016 :   if( s.size()==1 ) {
     167      172409 :     s[0]=k-j;
     168             :   }
     169             :   const T* pp;
     170      241016 :   getPointer( v, s, start, stride, pp );
     171      241000 :   std::vector<double> & d=value->data;
     172      241000 :   #pragma omp parallel for num_threads(value->getGoodNumThreads(j,k))
     173             :   for(unsigned i=j; i<k; ++i) {
     174             :     d[i]=unit*pp[i*stride];
     175             :   }
     176             : }
     177             : 
     178             : template <class T>
     179        1350 : void DataPassingObjectTyped<T>::share_data( std::vector<double>& values ) const {
     180        1350 :   std::vector<unsigned> maxel(1,values.size());
     181             :   const T* pp;
     182        1350 :   getPointer( v, maxel, start, stride, pp );
     183        1350 :   #pragma omp parallel for num_threads(OpenMP::getGoodNumThreads(values))
     184             :   for(unsigned i=0; i<values.size(); ++i) {
     185             :     values[i]=unit*pp[i*stride];
     186             :   }
     187        1350 : }
     188             : 
     189             : template <class T>
     190       70700 : void DataPassingObjectTyped<T>::share_data( const std::vector<AtomNumber>&index, const std::vector<unsigned>& i, Value* value ) {
     191             :   plumed_dbg_assert( value->getRank()==1 );
     192       70700 :   std::vector<unsigned> maxel(1,index.size());
     193             : #ifndef NDEBUG
     194             : // bounds are only checked in debug mode since they require this extra step that is potentially expensive
     195             :   maxel[0]=(i.size()>0?*std::max_element(i.begin(),i.end())+1:0);
     196             : #else
     197       70700 :   maxel[0]=0;
     198             : #endif
     199             :   const T* pp;
     200       70700 :   getPointer( v, maxel, start, stride, pp );
     201             :   // cannot be parallelized with omp because access to data is not ordered
     202             :   unsigned k=0;
     203     2507486 :   for(const auto & p : index) {
     204     2436786 :     value->data[p.index()]=unit*pp[i[k]*stride];
     205     2436786 :     k++;
     206             :   }
     207       70700 : }
     208             : 
     209             : template <class T>
     210       41997 : void DataPassingObjectTyped<T>::add_force( Value* value ) {
     211       41997 :   if( value->getRank()==0 ) {
     212          40 :     *f.template get<T*>() += funit*static_cast<T>(value->getForce(0));
     213          40 :     return;
     214             :   }
     215             :   T* pp;
     216       41957 :   getPointer( f, value->getShape(), start, stride, pp );
     217       41957 :   unsigned nvals=value->getNumberOfValues();
     218       41957 :   #pragma omp parallel for num_threads(OpenMP::getGoodNumThreads(pp,nvals))
     219             :   for(unsigned i=0; i<nvals; ++i) {
     220             :     pp[i*stride] += funit*T(value->getForce(i));
     221             :   }
     222             : }
     223             : 
     224             : template <class T>
     225       85784 : void DataPassingObjectTyped<T>::add_force( const std::vector<int>& index, Value* value ) {
     226       85784 :   plumed_assert( value->getRank()==1 );
     227       85784 :   std::vector<unsigned> s(1,index.size());
     228             :   T* pp;
     229       85784 :   getPointer( f, s, start, stride, pp );
     230       85782 :   #pragma omp parallel for num_threads(OpenMP::getGoodNumThreads(pp,index.size()))
     231             :   for(unsigned i=0; i<index.size(); ++i) {
     232             :     pp[i*stride] += funit*T(value->getForce(index[i]));
     233             :   }
     234       85782 : }
     235             : 
     236             : template <class T>
     237       62589 : void DataPassingObjectTyped<T>::add_force( const std::vector<AtomNumber>& index, const std::vector<unsigned>& i, Value* value ) {
     238             :   plumed_dbg_assert( value->getRank()==1 );
     239       62589 :   std::vector<unsigned> maxel(1,index.size());
     240             : #ifndef NDEBUG
     241             : // bounds are only checked in debug mode since they require this extra step that is potentially expensive
     242             :   maxel[0]=(i.size()>0?*std::max_element(i.begin(),i.end())+1:0);
     243             : #else
     244       62589 :   maxel[0]=0;
     245             : #endif
     246             :   T* pp;
     247       62589 :   getPointer( f, maxel, start, stride, pp );
     248             :   unsigned k=0;
     249     1856013 :   for(const auto & p : index) {
     250     1793424 :     pp[stride*i[k]] += funit*T(value->getForce(p.index()));
     251     1793424 :     k++;
     252             :   }
     253       62589 : }
     254             : 
     255             : template <class T>
     256         150 : void DataPassingObjectTyped<T>::rescale_force( const unsigned& n, const double& factor, Value* value ) {
     257         150 :   plumed_assert( value->getRank()>0 );
     258         150 :   std::vector<unsigned> s( value->getShape() );
     259         150 :   if( s.size()==1 ) {
     260         150 :     s[0] = n;
     261             :   }
     262             :   T* pp;
     263         150 :   getPointer( f, s, start, stride, pp );
     264         150 :   #pragma omp parallel for num_threads(OpenMP::getGoodNumThreads(pp,n))
     265             :   for(unsigned i=0; i<n; ++i) {
     266             :     pp[i*stride] *= T(factor);
     267             :   }
     268         150 : }
     269             : 
     270             : }

Generated by: LCOV version 1.16