LCOV - code coverage report
Current view: top level - multicolvar - LocalAverage.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 107 135 79.3 %
Date: 2024-10-11 08:09:47 Functions: 8 10 80.0 %

          Line data    Source code
       1             : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
       2             :    Copyright (c) 2013-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 "MultiColvarBase.h"
      23             : #include "AtomValuePack.h"
      24             : #include "core/ActionRegister.h"
      25             : #include "tools/SwitchingFunction.h"
      26             : 
      27             : //+PLUMEDOC MCOLVARF LOCAL_AVERAGE
      28             : /*
      29             : Calculate averages over spherical regions centered on atoms
      30             : 
      31             : As is explained in <a href="http://www.youtube.com/watch?v=iDvZmbWE5ps"> this video </a> certain multicolvars
      32             : calculate one scalar quantity or one vector for each of the atoms in the system.  For example
      33             : \ref COORDINATIONNUMBER measures the coordination number of each of the atoms in the system and \ref Q4 measures
      34             : the fourth order Steinhardt parameter for each of the atoms in the system.  These quantities provide tell us something about
      35             : the disposition of the atoms in the first coordination sphere of each of the atoms of interest.  Lechner and Dellago \cite dellago-q6
      36             : have suggested that one can probe local order in a system by taking the average value of such symmetry functions over
      37             : the atoms within a spherical cutoff of each of these atoms in the systems.  When this is done with Steinhardt parameters
      38             : they claim this gives a coordinate that is better able to distinguish solid and liquid configurations of Lennard-Jones atoms.
      39             : 
      40             : You can calculate such locally averaged quantities within plumed by using the LOCAL_AVERAGE command.  This command calculates
      41             : the following atom-centered quantities:
      42             : 
      43             : \f[
      44             : s_i = \frac{ c_i + \sum_j \sigma(r_{ij})c_j }{ 1 + \sum_j \sigma(r_{ij}) }
      45             : \f]
      46             : 
      47             : where the \f$c_i\f$ and \f$c_j\f$ values can be for any one of the symmetry functions that can be calculated using plumed
      48             : multicolvars.  The function \f$\sigma( r_{ij} )\f$ is a \ref switchingfunction that acts on the distance between
      49             : atoms \f$i\f$ and \f$j\f$.  Lechner and Dellago suggest that the parameters of this function should be set so that it the function is equal to one
      50             : when atom \f$j\f$ is in the first coordination sphere of atom \f$i\f$ and is zero otherwise.
      51             : 
      52             : The \f$s_i\f$ quantities calculated using the above command can be again thought of as atom-centered symmetry functions.  They
      53             : thus operate much like multicolvars.  You can thus calculate properties of the distribution of \f$s_i\f$ values using MEAN, LESS_THAN, HISTOGRAM
      54             : and so on.  You can also probe the value of these averaged variables in regions of the box by using the command in tandem with the
      55             : \ref AROUND command.
      56             : 
      57             : \par Examples
      58             : 
      59             : This example input calculates the coordination numbers for all the atoms in the system.  These coordination numbers are then averaged over
      60             : spherical regions.  The number of averaged coordination numbers that are greater than 4 is then output to a file.
      61             : 
      62             : \plumedfile
      63             : COORDINATIONNUMBER SPECIES=1-64 D_0=1.3 R_0=0.2 LABEL=d1
      64             : LOCAL_AVERAGE SPECIES=d1 SWITCH={RATIONAL D_0=1.3 R_0=0.2} MORE_THAN={RATIONAL R_0=4} LABEL=la
      65             : PRINT ARG=la.* FILE=colvar
      66             : \endplumedfile
      67             : 
      68             : This example input calculates the \f$q_4\f$ (see \ref Q4) vectors for each of the atoms in the system.  These vectors are then averaged
      69             : component by component over a spherical region.  The average value for this quantity is then output to a file.  This calculates the
      70             : quantities that were used in the paper by Lechner and Dellago \cite dellago-q6
      71             : 
      72             : \plumedfile
      73             : Q4 SPECIES=1-64 SWITCH={RATIONAL D_0=1.3 R_0=0.2} LABEL=q4
      74             : LOCAL_AVERAGE SPECIES=q4 SWITCH={RATIONAL D_0=1.3 R_0=0.2} MEAN LABEL=la
      75             : PRINT ARG=la.* FILE=colvar
      76             : \endplumedfile
      77             : 
      78             : */
      79             : //+ENDPLUMEDOC
      80             : 
      81             : namespace PLMD {
      82             : namespace multicolvar {
      83             : 
      84             : class LocalAverage : public MultiColvarBase {
      85             : private:
      86             : /// Cutoff
      87             :   double rcut2;
      88             : /// The switching function that tells us if atoms are close enough together
      89             :   SwitchingFunction switchingFunction;
      90             : public:
      91             :   static void registerKeywords( Keywords& keys );
      92             :   explicit LocalAverage(const ActionOptions&);
      93             : /// We have to overwrite this here
      94             :   unsigned getNumberOfQuantities() const override;
      95             : /// Actually do the calculation
      96             :   double compute( const unsigned& tindex, AtomValuePack& myatoms ) const override;
      97             : /// We overwrite this in order to have dumpmulticolvar working for local average
      98           0 :   void normalizeVector( std::vector<double>& vals ) const override {}
      99             : /// Is the variable periodic
     100           3 :   bool isPeriodic() override { return false; }
     101             : };
     102             : 
     103       10427 : PLUMED_REGISTER_ACTION(LocalAverage,"LOCAL_AVERAGE")
     104             : 
     105           5 : void LocalAverage::registerKeywords( Keywords& keys ) {
     106           5 :   MultiColvarBase::registerKeywords( keys );
     107          10 :   keys.add("compulsory","NN","6","The n parameter of the switching function ");
     108          10 :   keys.add("compulsory","MM","0","The m parameter of the switching function; 0 implies 2*NN");
     109          10 :   keys.add("compulsory","D_0","0.0","The d_0 parameter of the switching function");
     110          10 :   keys.add("compulsory","R_0","The r_0 parameter of the switching function");
     111          10 :   keys.add("optional","SWITCH","This keyword is used if you want to employ an alternative to the continuous switching function defined above. "
     112             :            "The following provides information on the \\ref switchingfunction that are available. "
     113             :            "When this keyword is present you no longer need the NN, MM, D_0 and R_0 keywords.");
     114             :   // Use actionWithDistributionKeywords
     115          15 :   keys.use("SPECIES"); keys.use("SPECIESA"); keys.use("SPECIESB");
     116          20 :   keys.remove("LOWMEM"); keys.use("MEAN"); keys.use("MORE_THAN"); keys.use("LESS_THAN");
     117          15 :   keys.use("BETWEEN"); keys.use("HISTOGRAM"); keys.use("MOMENTS");
     118          10 :   keys.addFlag("LOWMEM",false,"lower the memory requirements");
     119          15 :   if( keys.reserved("VMEAN") ) keys.use("VMEAN");
     120          15 :   if( keys.reserved("VSUM") ) keys.use("VSUM");
     121           5 : }
     122             : 
     123           4 : LocalAverage::LocalAverage(const ActionOptions& ao):
     124             :   Action(ao),
     125           4 :   MultiColvarBase(ao)
     126             : {
     127           4 :   if( getNumberOfBaseMultiColvars()>1 ) error("local average with more than one base colvar makes no sense");
     128             :   // Read in the switching function
     129           8 :   std::string sw, errors; parse("SWITCH",sw);
     130           4 :   if(sw.length()>0) {
     131           4 :     switchingFunction.set(sw,errors);
     132             :   } else {
     133           0 :     double r_0=-1.0, d_0; int nn, mm;
     134           0 :     parse("NN",nn); parse("MM",mm);
     135           0 :     parse("R_0",r_0); parse("D_0",d_0);
     136           0 :     if( r_0<0.0 ) error("you must set a value for R_0");
     137           0 :     switchingFunction.set(nn,mm,r_0,d_0);
     138             :   }
     139           4 :   log.printf("  averaging over central molecule and those within %s\n",( switchingFunction.description() ).c_str() );
     140           4 :   rcut2 = switchingFunction.get_dmax()*switchingFunction.get_dmax();
     141           4 :   setLinkCellCutoff( switchingFunction.get_dmax() );
     142           4 :   std::vector<AtomNumber> all_atoms; setupMultiColvarBase( all_atoms );
     143           4 : }
     144             : 
     145          71 : unsigned LocalAverage::getNumberOfQuantities() const {
     146          71 :   return getBaseMultiColvar(0)->getNumberOfQuantities();
     147             : }
     148             : 
     149        1004 : double LocalAverage::compute( const unsigned& tindex, AtomValuePack& myatoms ) const {
     150             :   double sw, dfunc; MultiValue& myvals = myatoms.getUnderlyingMultiValue();
     151        1004 :   std::vector<double> values( getBaseMultiColvar(0)->getNumberOfQuantities() );
     152             : 
     153        1004 :   getInputData( 0, false, myatoms, values );
     154             :   myvals.addTemporyValue( values[0] );
     155        1004 :   if( values.size()>2 ) {
     156       27108 :     for(unsigned j=2; j<values.size(); ++j) myatoms.addValue( j, values[0]*values[j] );
     157             :   } else {
     158           0 :     myatoms.addValue( 1, values[0]*values[1] );
     159             :   }
     160             : 
     161        1004 :   if( !doNotCalculateDerivatives() ) {
     162        1004 :     MultiValue& myder=getInputDerivatives( 0, false, myatoms );
     163             : 
     164             :     // Convert input atom to local index
     165             :     unsigned katom = myatoms.getIndex( 0 ); plumed_dbg_assert( katom<atom_lab.size() ); plumed_dbg_assert( atom_lab[katom].first>0 );
     166             :     // Find base colvar
     167        1004 :     unsigned mmc=atom_lab[katom].first - 1; plumed_dbg_assert( mybasemulticolvars[mmc]->taskIsCurrentlyActive( atom_lab[katom].second ) );
     168             :     // Get start of indices for this atom
     169        1004 :     unsigned basen=0; for(unsigned i=0; i<mmc; ++i) basen+=mybasemulticolvars[i]->getNumberOfDerivatives() - 9;
     170             :     plumed_dbg_assert( basen%3==0 ); // Check the number of atoms is consistent with input derivatives
     171             : 
     172        1004 :     unsigned virbas = myvals.getNumberOfDerivatives()-9;
     173        1004 :     if( values.size()>2 ) {
     174       49505 :       for(unsigned j=0; j<myder.getNumberActive(); ++j) {
     175             :         unsigned jder=myder.getActiveIndex(j);
     176       48501 :         if( jder<mybasemulticolvars[mmc]->getNumberOfDerivatives()-9 ) {
     177       39465 :           unsigned kder=basen+jder;
     178     1065555 :           for(unsigned k=2; k<values.size(); ++k) {
     179     1026090 :             myatoms.addDerivative( k, kder, values[0]*myder.getDerivative(k,jder) );
     180     1026090 :             myatoms.addDerivative( k, kder, values[k]*myder.getDerivative(0,jder) );
     181             :           }
     182             :         } else {
     183        9036 :           unsigned kder=virbas + (jder - mybasemulticolvars[mmc]->getNumberOfDerivatives() + 9);
     184      243972 :           for(unsigned k=2; k<values.size(); ++k) {
     185      234936 :             myatoms.addDerivative( k, kder, values[0]*myder.getDerivative(k,jder) );
     186      234936 :             myatoms.addDerivative( k, kder, values[k]*myder.getDerivative(0,jder) );
     187             :           }
     188             :         }
     189             :       }
     190             :     } else {
     191           0 :       for(unsigned j=0; j<myder.getNumberActive(); ++j) {
     192             :         unsigned jder=myder.getActiveIndex(j);
     193           0 :         if( jder<mybasemulticolvars[mmc]->getNumberOfDerivatives()-9 ) {
     194           0 :           unsigned kder=basen+jder;
     195           0 :           myatoms.addDerivative( 1, kder, values[0]*myder.getDerivative(1,jder) );
     196           0 :           myatoms.addDerivative( 1, kder, values[1]*myder.getDerivative(0,jder) );
     197             :         } else {
     198           0 :           unsigned kder=virbas + (jder - mybasemulticolvars[mmc]->getNumberOfDerivatives() + 9);
     199           0 :           myatoms.addDerivative( 1, kder, values[0]*myder.getDerivative(1,jder) );
     200           0 :           myatoms.addDerivative( 1, kder, values[1]*myder.getDerivative(0,jder) );
     201             :         }
     202             :       }
     203             :     }
     204       49505 :     for(unsigned j=0; j<myder.getNumberActive(); ++j) {
     205             :       unsigned jder=myder.getActiveIndex(j);
     206       48501 :       if( jder<mybasemulticolvars[mmc]->getNumberOfDerivatives()-9 ) {
     207       39465 :         unsigned kder=basen+jder;
     208       39465 :         myvals.addTemporyDerivative( kder, myder.getDerivative(0, jder) );
     209             :       } else {
     210        9036 :         unsigned kder=virbas + (jder - mybasemulticolvars[mmc]->getNumberOfDerivatives() + 9);
     211        9036 :         myvals.addTemporyDerivative( kder, myder.getDerivative(0, jder) );
     212             :       }
     213             :     }
     214        1004 :     myder.clearAll();
     215             :   }
     216             : 
     217      274074 :   for(unsigned i=1; i<myatoms.getNumberOfAtoms(); ++i) {
     218             :     Vector& distance=myatoms.getPosition(i);  // getSeparation( myatoms.getPosition(0), myatoms.getPosition(i) );
     219             :     double d2;
     220      363595 :     if ( (d2=distance[0]*distance[0])<rcut2 &&
     221       90525 :          (d2+=distance[1]*distance[1])<rcut2 &&
     222      305390 :          (d2+=distance[2]*distance[2])<rcut2 &&
     223             :          d2>epsilon) {
     224             : 
     225       14639 :       sw = switchingFunction.calculateSqr( d2, dfunc );
     226             : 
     227       14639 :       getInputData( i, false, myatoms, values );
     228       14639 :       if( values.size()>2 ) {
     229      395253 :         for(unsigned j=2; j<values.size(); ++j) myatoms.addValue( j, sw*values[0]*values[j] );
     230             :       } else {
     231           0 :         myatoms.addValue( 1, sw*values[0]*values[1] );
     232             :       }
     233             :       myvals.addTemporyValue(sw);
     234             : 
     235       14639 :       if( !doNotCalculateDerivatives() ) {
     236       14639 :         Tensor vir(distance,distance);
     237       14639 :         MultiValue& myder=getInputDerivatives( i, false, myatoms );
     238             : 
     239             :         // Convert input atom to local index
     240             :         unsigned katom = myatoms.getIndex( i ); plumed_dbg_assert( katom<atom_lab.size() ); plumed_dbg_assert( atom_lab[katom].first>0 );
     241             :         // Find base colvar
     242       14639 :         unsigned mmc=atom_lab[katom].first - 1; plumed_dbg_assert( mybasemulticolvars[mmc]->taskIsCurrentlyActive( atom_lab[katom].second ) );
     243             :         // Get start of indices for this atom
     244       19559 :         unsigned basen=0; for(unsigned j=0; j<mmc; ++j) basen+=mybasemulticolvars[j]->getNumberOfDerivatives() - 9;
     245             :         plumed_dbg_assert( basen%3==0 ); // Check the number of atoms is consistent with input derivatives
     246             : 
     247       14639 :         unsigned virbas = myvals.getNumberOfDerivatives()-9;
     248       14639 :         if( values.size()>2 ) {
     249     1424993 :           for(unsigned j=0; j<myder.getNumberActive(); ++j) {
     250             :             unsigned jder=myder.getActiveIndex(j);
     251     1410354 :             if( jder<mybasemulticolvars[mmc]->getNumberOfDerivatives()-9 ) {
     252     1278603 :               unsigned kder=basen+jder;
     253    34522281 :               for(unsigned k=2; k<values.size(); ++k) {
     254    33243678 :                 myatoms.addDerivative( k, kder, sw*values[0]*myder.getDerivative(k,jder) );
     255    33243678 :                 myatoms.addDerivative( k, kder, sw*values[k]*myder.getDerivative(0,jder) );
     256             :               }
     257             :             } else {
     258      131751 :               unsigned kder=virbas + (jder - mybasemulticolvars[mmc]->getNumberOfDerivatives() + 9);
     259     3557277 :               for(unsigned k=2; k<values.size(); ++k) {
     260     3425526 :                 myatoms.addDerivative( k, kder, sw*values[0]*myder.getDerivative(k,jder) );
     261     3425526 :                 myatoms.addDerivative( k, kder, sw*values[k]*myder.getDerivative(0,jder) );
     262             :               }
     263             :             }
     264             :           }
     265      395253 :           for(unsigned k=2; k<values.size(); ++k) {
     266      380614 :             addAtomDerivatives( k, 0, (-dfunc)*values[0]*values[k]*distance, myatoms );
     267      380614 :             addAtomDerivatives( k, i, (+dfunc)*values[0]*values[k]*distance, myatoms );
     268      380614 :             myatoms.addBoxDerivatives( k, (-dfunc)*values[0]*values[k]*vir );
     269             :           }
     270             :         } else {
     271           0 :           for(unsigned j=0; j<myder.getNumberActive(); ++j) {
     272             :             unsigned jder=myder.getActiveIndex(j);
     273           0 :             if( jder<mybasemulticolvars[mmc]->getNumberOfDerivatives()-9 ) {
     274           0 :               unsigned kder=basen+jder;
     275           0 :               myatoms.addDerivative( 1, kder, sw*values[0]*myder.getDerivative(1,jder) );
     276           0 :               myatoms.addDerivative( 1, kder, sw*values[1]*myder.getDerivative(0,jder) );
     277             :             } else {
     278           0 :               unsigned kder=virbas + (jder - mybasemulticolvars[mmc]->getNumberOfDerivatives() + 9);
     279           0 :               myatoms.addDerivative( 1, kder, sw*values[0]*myder.getDerivative(1,jder) );
     280           0 :               myatoms.addDerivative( 1, kder, sw*values[1]*myder.getDerivative(0,jder) );
     281             :             }
     282             :           }
     283           0 :           addAtomDerivatives( 1, 0, (-dfunc)*values[0]*values[1]*distance, myatoms );
     284           0 :           addAtomDerivatives( 1, i, (+dfunc)*values[0]*values[1]*distance, myatoms );
     285           0 :           myatoms.addBoxDerivatives( 1, (-dfunc)*values[0]*values[1]*vir );
     286             :         }
     287             :         // And the bit we use to average the vector
     288       14639 :         addAtomDerivatives( -1, 0, (-dfunc)*values[0]*distance, myatoms );
     289       14639 :         addAtomDerivatives( -1, i, (+dfunc)*values[0]*distance, myatoms );
     290     1424993 :         for(unsigned j=0; j<myder.getNumberActive(); ++j) {
     291             :           unsigned jder=myder.getActiveIndex(j);
     292     1410354 :           if( jder<mybasemulticolvars[mmc]->getNumberOfDerivatives()-9 ) {
     293     1278603 :             unsigned kder=basen+jder;
     294     1278603 :             myvals.addTemporyDerivative( kder, sw*myder.getDerivative(0, jder) );
     295             :           } else {
     296      131751 :             unsigned kder=virbas + (jder - mybasemulticolvars[mmc]->getNumberOfDerivatives() + 9);
     297      131751 :             myvals.addTemporyDerivative( kder, sw*myder.getDerivative(0, jder) );
     298             :           }
     299             :         }
     300       14639 :         myatoms.addTemporyBoxDerivatives( (-dfunc)*values[0]*vir );
     301       14639 :         myder.clearAll();
     302             :       }
     303             :     }
     304             :   }
     305             : 
     306             :   // Set the tempory weight
     307        1004 :   updateActiveAtoms( myatoms );
     308        1004 :   if( values.size()>2) {
     309             :     double norm=0;
     310       27108 :     for(unsigned i=2; i<values.size(); ++i) {
     311       26104 :       myvals.quotientRule( i, i );
     312             :       // Calculate length of vector
     313       26104 :       norm+=myvals.get(i)*myvals.get(i);
     314             :     }
     315        1004 :     norm=sqrt(norm); myatoms.setValue(1, norm); double inorm = 1.0 / norm;
     316      132755 :     for(unsigned j=0; j<myvals.getNumberActive(); ++j) {
     317      131751 :       unsigned jder=myvals.getActiveIndex(j);
     318     3557277 :       for(unsigned i=2; i<values.size(); ++i) {
     319     3425526 :         myvals.addDerivative( 1, jder, myvals.get(i)*inorm*myvals.getDerivative(i,jder) );
     320             :       }
     321             :     }
     322             :   } else {
     323           0 :     myvals.quotientRule( 1, 1 );
     324             :   }
     325             : 
     326        1004 :   return myatoms.getValue(1);
     327             : }
     328             : 
     329             : }
     330             : }

Generated by: LCOV version 1.15