LCOV - code coverage report
Current view: top level - adjmat - Sprint.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 61 74 82.4 %
Date: 2024-10-11 08:09:47 Functions: 7 8 87.5 %

          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 "ActionWithInputMatrix.h"
      23             : #include "AdjacencyMatrixVessel.h"
      24             : #include "core/ActionRegister.h"
      25             : 
      26             : //+PLUMEDOC MATRIXF SPRINT
      27             : /*
      28             : Calculate SPRINT topological variables from an adjacency matrix.
      29             : 
      30             : The SPRINT topological variables are calculated from the largest eigenvalue, \f$\lambda\f$ of
      31             : an \f$n\times n\f$ adjacency matrix and its corresponding eigenvector, \f$\mathbf{V}\f$, using:
      32             : 
      33             : \f[
      34             : s_i = \sqrt{n} \lambda v_i
      35             : \f]
      36             : 
      37             : You can use different quantities to measure whether or not two given atoms/molecules are
      38             : adjacent or not in the adjacency matrix.  The simplest measure of adjacency is is whether
      39             : two atoms/molecules are within some cutoff of each other.  Further complexity can be added by
      40             : insisting that two molecules are adjacent if they are within a certain distance of each
      41             : other and if they have similar orientations.
      42             : 
      43             : \par Examples
      44             : 
      45             : This example input calculates the 7 SPRINT coordinates for a 7 atom cluster of Lennard-Jones
      46             : atoms and prints their values to a file.  In this input the SPRINT coordinates are calculated
      47             : in the manner described in ?? so two atoms are adjacent if they are within a cutoff:
      48             : 
      49             : \plumedfile
      50             : DENSITY SPECIES=1-7 LABEL=d1
      51             : CONTACT_MATRIX ATOMS=d1 SWITCH={RATIONAL R_0=0.1} LABEL=mat
      52             : SPRINT MATRIX=mat LABEL=ss
      53             : PRINT ARG=ss.* FILE=colvar
      54             : \endplumedfile
      55             : 
      56             : This example input calculates the 14 SPRINT coordinates for a molecule composed of 7 hydrogen and
      57             : 7 carbon atoms.  Once again two atoms are adjacent if they are within a cutoff:
      58             : 
      59             : \plumedfile
      60             : DENSITY SPECIES=1-7 LABEL=c
      61             : DENSITY SPECIES=8-14 LABEL=h
      62             : 
      63             : CONTACT_MATRIX ...
      64             :   ATOMS=c,h
      65             :   SWITCH11={RATIONAL R_0=2.6 NN=6 MM=12}
      66             :   SWITCH12={RATIONAL R_0=2.2 NN=6 MM=12}
      67             :   SWITCH22={RATIONAL R_0=2.2 NN=6 MM=12}
      68             :   LABEL=mat
      69             : ... CONTACT_MATRIX
      70             : 
      71             : SPRINT MATRIX=mat LABEL=ss
      72             : 
      73             : PRINT ARG=ss.* FILE=colvar
      74             : \endplumedfile
      75             : 
      76             : */
      77             : //+ENDPLUMEDOC
      78             : 
      79             : 
      80             : namespace PLMD {
      81             : namespace adjmat {
      82             : 
      83             : class Sprint : public ActionWithInputMatrix {
      84             : private:
      85             : /// Square root of number of atoms
      86             :   double sqrtn;
      87             : /// Vector that stores eigenvalues
      88             :   std::vector<double> eigvals;
      89             : /// This is used to speed up the calculation of derivatives
      90             :   DynamicList<unsigned> active_elements;
      91             : /// Vector that stores max eigenvector
      92             :   std::vector< std::pair<double,int> > maxeig;
      93             : /// Adjacency matrix
      94             :   Matrix<double> thematrix;
      95             : /// Matrix that stores eigenvectors
      96             :   Matrix<double> eigenvecs;
      97             : public:
      98             : /// Create manual
      99             :   static void registerKeywords( Keywords& keys );
     100             : /// Constructor
     101             :   explicit Sprint(const ActionOptions&);
     102             : /// Do the matrix calculation
     103             :   void calculate() override;
     104             : /// Sprint needs its only apply routine as it creates values
     105             :   void apply() override;
     106             : };
     107             : 
     108       10421 : PLUMED_REGISTER_ACTION(Sprint,"SPRINT")
     109             : 
     110           2 : void Sprint::registerKeywords( Keywords& keys ) {
     111           2 :   ActionWithInputMatrix::registerKeywords( keys );
     112           2 :   componentsAreNotOptional(keys);
     113           4 :   keys.addOutputComponent("coord","default","all \\f$n\\f$ sprint coordinates are calculated and then stored in increasing order. "
     114             :                           "the smallest sprint coordinate will be labeled <em>label</em>.coord-1, "
     115             :                           "the second smallest will be labelled <em>label</em>.coord-1 and so on");
     116           2 : }
     117             : 
     118           1 : Sprint::Sprint(const ActionOptions&ao):
     119             :   Action(ao),
     120             :   ActionWithInputMatrix(ao),
     121           1 :   eigvals( getNumberOfNodes() ),
     122           1 :   maxeig( getNumberOfNodes() ),
     123           1 :   thematrix( getNumberOfNodes(), getNumberOfNodes() ),
     124           3 :   eigenvecs( getNumberOfNodes(), getNumberOfNodes() )
     125             : {
     126             :   // Check on setup
     127             :   // if( getNumberOfVessels()!=1 ) error("there should be no vessel keywords");
     128             :   // Check for bad colvar input ( we  are going to get rid of this because we are going to have input adjacency matrix in future )
     129             :   // for(unsigned i=0;i<getNumberOfAtomGroups();++i){
     130             :   //    /// Check me GAT
     131             :   //    // if( !getBaseMultiColvar(i)->hasDifferentiableOrientation() ) error("cannot use multicolvar of type " + getBaseMultiColvar(i)->getName() );
     132             :   // }
     133             : 
     134           1 :   if( !getAdjacencyVessel()->isSymmetric() ) error("input contact matrix is not symmetric");
     135           1 :   std::vector<AtomNumber> fake_atoms; setupMultiColvarBase( fake_atoms );
     136             : 
     137             :   // Create all the values
     138           1 :   sqrtn = std::sqrt( static_cast<double>( getNumberOfNodes() ) );
     139          15 :   for(unsigned i=0; i<getNumberOfNodes(); ++i) {
     140          14 :     std::string num; Tools::convert(i,num);
     141          14 :     addComponentWithDerivatives("coord-"+num);
     142          14 :     componentIsNotPeriodic("coord-"+num);
     143          14 :     getPntrToComponent(i)->resizeDerivatives( getNumberOfDerivatives() );
     144             :   }
     145             : 
     146             :   // Setup the dynamic list to hold all the tasks
     147           1 :   unsigned ntriangle = 0.5*getNumberOfNodes()*(getNumberOfNodes()-1);
     148          92 :   for(unsigned i=0; i<ntriangle; ++i) active_elements.addIndexToList( i );
     149           1 : }
     150             : 
     151          17 : void Sprint::calculate() {
     152             :   // Get the adjacency matrix
     153          17 :   getAdjacencyVessel()->retrieveMatrix( active_elements, thematrix );
     154             :   // Diagonalize it
     155          17 :   diagMat( thematrix, eigvals, eigenvecs );
     156             :   // Get the maximum eigevalue
     157          17 :   double lambda = eigvals[ getNumberOfNodes()-1 ];
     158             :   // Get the corresponding eigenvector
     159         255 :   for(unsigned j=0; j<maxeig.size(); ++j) {
     160         238 :     maxeig[j].first = std::fabs( eigenvecs( getNumberOfNodes()-1, j ) );
     161         238 :     maxeig[j].second = j;
     162             :     // Must make all components of principle eigenvector +ve
     163         238 :     eigenvecs( getNumberOfNodes()-1, j ) = maxeig[j].first;
     164             :   }
     165             : 
     166             :   // Reorder each block of eigevectors
     167             :   unsigned startnum=0;
     168          51 :   for(unsigned j=0; j<getNumberOfNodeTypes(); ++j) {
     169          34 :     unsigned nthis = getNumberOfAtomsInGroup(j);
     170             :     // Sort into ascending order
     171          34 :     std::sort( maxeig.begin() + startnum, maxeig.begin() + startnum + nthis );
     172             :     // Used so we can do sorting in blocks
     173          34 :     startnum += nthis;
     174             :   }
     175             :   // Set the sprint coordinates
     176         255 :   for(int icomp=0; icomp<getNumberOfComponents(); ++icomp) {
     177         238 :     getPntrToComponent(icomp)->set( sqrtn*lambda*maxeig[icomp].first );
     178             :   }
     179             : 
     180             :   // Parallelism
     181             :   unsigned rank, stride;
     182          17 :   if( serialCalculation() ) { stride=1; rank=0; }
     183          17 :   else { rank=comm.Get_rank(); stride=comm.Get_size(); }
     184             : 
     185             :   // Derivatives
     186          17 :   MultiValue myvals( 2, getNumberOfDerivatives() );
     187          17 :   Matrix<double> mymat_ders( getNumberOfComponents(), getNumberOfDerivatives() );
     188             :   // std::vector<unsigned> catoms(2);
     189          17 :   unsigned nval = getNumberOfNodes(); mymat_ders=0;
     190        1564 :   for(unsigned i=rank; i<active_elements.getNumberActive(); i+=stride) {
     191        1547 :     unsigned j, k; getAdjacencyVessel()->getMatrixIndices( active_elements[i], j, k );
     192        1547 :     double tmp1 = 2 * eigenvecs(nval-1,j)*eigenvecs(nval-1,k);
     193       23205 :     for(int icomp=0; icomp<getNumberOfComponents(); ++icomp) {
     194             :       double tmp2 = 0.;
     195      303212 :       for(unsigned n=0; n<nval-1; ++n) { // Need care on following line
     196      281554 :         tmp2 += eigenvecs(n,maxeig[icomp].second) * ( eigenvecs(n,j)*eigenvecs(nval-1,k) + eigenvecs(n,k)*eigenvecs(nval-1,j) ) / ( lambda - eigvals[n] );
     197             :       }
     198       21658 :       double prefactor=sqrtn*( tmp1*maxeig[icomp].first + tmp2*lambda );
     199       21658 :       getAdjacencyVessel()->retrieveDerivatives( active_elements[i], false, myvals );
     200      346528 :       for(unsigned jd=0; jd<myvals.getNumberActive(); ++jd) {
     201             :         unsigned ider=myvals.getActiveIndex(jd);
     202      324870 :         mymat_ders( icomp, ider ) += prefactor*myvals.getDerivative( 1, ider );
     203             :       }
     204             :     }
     205             :   }
     206          17 :   if( !serialCalculation() ) comm.Sum( mymat_ders );
     207             : 
     208         255 :   for(int j=0; j<getNumberOfComponents(); ++j) {
     209         238 :     Value* val=getPntrToComponent(j);
     210       12376 :     for(unsigned i=0; i<getNumberOfDerivatives(); ++i) val->addDerivative( i, mymat_ders(j,i) );
     211             :   }
     212          17 : }
     213             : 
     214          17 : void Sprint::apply() {
     215             :   std::vector<Vector>&   f(modifyForces());
     216             :   Tensor&           v(modifyVirial());
     217             :   unsigned          nat=getNumberOfAtoms();
     218             : 
     219          17 :   std::vector<double> forces( 3*getNumberOfAtoms() + 9 );
     220         255 :   for(int i=0; i<getNumberOfComponents(); ++i) {
     221         238 :     if( getPntrToComponent(i)->applyForce( forces ) ) {
     222           0 :       for(unsigned j=0; j<nat; ++j) {
     223           0 :         f[j][0]+=forces[3*j+0];
     224           0 :         f[j][1]+=forces[3*j+1];
     225           0 :         f[j][2]+=forces[3*j+2];
     226             :       }
     227           0 :       v(0,0)+=forces[3*nat+0];
     228           0 :       v(0,1)+=forces[3*nat+1];
     229           0 :       v(0,2)+=forces[3*nat+2];
     230           0 :       v(1,0)+=forces[3*nat+3];
     231           0 :       v(1,1)+=forces[3*nat+4];
     232           0 :       v(1,2)+=forces[3*nat+5];
     233           0 :       v(2,0)+=forces[3*nat+6];
     234           0 :       v(2,1)+=forces[3*nat+7];
     235           0 :       v(2,2)+=forces[3*nat+8];
     236             :     }
     237             :   }
     238          17 : }
     239             : 
     240             : }
     241             : }

Generated by: LCOV version 1.15