LCOV - code coverage report
Current view: top level - adjmat - TopologyMatrix.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 159 159 100.0 %
Date: 2020-11-18 11:20:57 Functions: 14 15 93.3 %

          Line data    Source code
       1             : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
       2             :    Copyright (c) 2015-2019 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 "AdjacencyMatrixBase.h"
      23             : #include "multicolvar/AtomValuePack.h"
      24             : #include "core/ActionRegister.h"
      25             : #include "tools/SwitchingFunction.h"
      26             : #include "tools/HistogramBead.h"
      27             : #include "tools/Matrix.h"
      28             : 
      29             : //+PLUMEDOC MATRIX TOPOLOGY_MATRIX
      30             : /*
      31             : Adjacency matrix in which two atoms are adjacent if they are connected topologically
      32             : 
      33             : \par Examples
      34             : 
      35             : 
      36             : */
      37             : //+ENDPLUMEDOC
      38             : 
      39             : namespace PLMD {
      40             : namespace adjmat {
      41             : 
      42          18 : class TopologyMatrix : public AdjacencyMatrixBase {
      43             : private:
      44             : /// The width to use for the kernel density estimation and the
      45             : /// sizes of the bins to be used in kernel density estimation
      46             :   double sigma;
      47             :   std::string kerneltype;
      48             : /// The maximum number of bins that will be used
      49             : /// This is calculated based on the dmax of the switching functions
      50             :   unsigned maxbins;
      51             : /// The volume of the cells
      52             :   Matrix<double> cell_volume;
      53             : /// switching function
      54             :   Matrix<SwitchingFunction> switchingFunction;
      55             :   Matrix<SwitchingFunction> cylinder_sw;
      56             :   Matrix<SwitchingFunction> low_sf;
      57             :   double beadrad, lsfmax;
      58             :   Matrix<double> binw_mat;
      59             :   SwitchingFunction threshold_switch;
      60             : public:
      61             : /// Create manual
      62             :   static void registerKeywords( Keywords& keys );
      63             : /// Constructor
      64             :   explicit TopologyMatrix(const ActionOptions&);
      65             : /// Get the number of quantities that we must compute
      66             :   unsigned getNumberOfQuantities() const ;
      67             : /// Create the ith, ith switching function
      68             :   void setupConnector( const unsigned& id, const unsigned& i, const unsigned& j, const std::vector<std::string>& desc );
      69             : /// This actually calculates the value of the contact function
      70             :   double calculateWeight( const unsigned& taskCode, const double& weight, multicolvar::AtomValuePack& myatoms ) const ;
      71             : /// This does nothing
      72             :   double compute( const unsigned& tindex, multicolvar::AtomValuePack& myatoms ) const ;
      73             : /// Calculate the contribution from one of the atoms in the third element of the pack
      74             :   void calculateForThreeAtoms( const unsigned& iat, const Vector& d1, const double& d1_len,
      75             :                                HistogramBead& bead, multicolvar::AtomValuePack& myatoms ) const ;
      76             : };
      77             : 
      78        6458 : PLUMED_REGISTER_ACTION(TopologyMatrix,"TOPOLOGY_MATRIX")
      79             : 
      80           7 : void TopologyMatrix::registerKeywords( Keywords& keys ) {
      81           7 :   AdjacencyMatrixBase::registerKeywords( keys );
      82          28 :   keys.add("atoms","NODES","The list of atoms for which you would like to calculate the contact matrix.  The atoms involved must be specified "
      83             :            "as a list of labels of \\ref mcolv or labels of a \\ref multicolvarfunction actions.  If you would just like to use "
      84             :            "the atomic positions you can use a \\ref DENSITY command to specify a group of atoms.  Specifying your atomic positions using labels of "
      85             :            "other \\ref mcolv or \\ref multicolvarfunction commands is useful, however, as you can then exploit a much wider "
      86             :            "variety of functions of the contact matrix as described in \\ref contactmatrix");
      87          28 :   keys.add("atoms","ATOMS","");
      88          28 :   keys.add("numbered","SWITCH","This keyword is used if you want to employ an alternative to the continuous swiching function defined above. "
      89             :            "The following provides information on the \\ref switchingfunction that are available. "
      90             :            "When this keyword is present you no longer need the NN, MM, D_0 and R_0 keywords.");
      91          28 :   keys.add("numbered","RADIUS","");
      92          28 :   keys.add("numbered","CYLINDER_SWITCH","a switching function on ( r_ij . r_ik - 1 )/r_ij");
      93          28 :   keys.add("numbered","BIN_SIZE","the size to use for the bins");
      94          28 :   keys.add("compulsory","DENSITY_THRESHOLD","");
      95          28 :   keys.add("compulsory","SIGMA","the width of the function to be used for kernel density estimation");
      96          35 :   keys.add("compulsory","KERNEL","gaussian","the type of kernel function to be used");
      97          28 :   keys.add("hidden","FAKE","");
      98           7 : }
      99             : 
     100           6 : TopologyMatrix::TopologyMatrix( const ActionOptions& ao ):
     101             :   Action(ao),
     102             :   AdjacencyMatrixBase(ao),
     103          18 :   maxbins(0)
     104             : {
     105          30 :   readMaxThreeSpeciesMatrix("NODES", "FAKE", "FAKE", "ATOMS", true );
     106           6 :   unsigned nrows, ncols, ndonor_types; retrieveTypeDimensions( nrows, ncols, ndonor_types );
     107          18 :   switchingFunction.resize( nrows, ncols ); parseConnectionDescriptions("SWITCH",false,ndonor_types);
     108          18 :   cylinder_sw.resize( nrows, ncols ); parseConnectionDescriptions("RADIUS",false,ndonor_types);
     109          18 :   low_sf.resize( nrows, ncols ); parseConnectionDescriptions("CYLINDER_SWITCH",false,ndonor_types);
     110          12 :   binw_mat.resize( nrows, ncols ); cell_volume.resize( nrows, ncols );
     111          12 :   parseConnectionDescriptions("BIN_SIZE",false,ndonor_types);
     112             :   // Read in stuff for grid
     113          18 :   parse("SIGMA",sigma); parse("KERNEL",kerneltype);
     114             :   // Read in threshold for density cutoff
     115          12 :   std::string errors, thresh_sw_str; parse("DENSITY_THRESHOLD",thresh_sw_str);
     116           6 :   threshold_switch.set(thresh_sw_str, errors );
     117           6 :   if( errors.length()>0 ) error("errors in DENSITY_THRESHOLD switching function : " + errors );
     118          18 :   log.printf("  threshold on density of atoms in cylinder equals %s\n",threshold_switch.description().c_str() );
     119             : 
     120          18 :   for(unsigned i=0; i<getNumberOfNodeTypes(); ++i) {
     121          18 :     for(unsigned j=0; j<getNumberOfNodeTypes(); ++j) {
     122          12 :       double r=cylinder_sw(i,j).get_d0() + cylinder_sw(i,j).get_r0();
     123          12 :       cell_volume(i,j)=binw_mat(i,j)*pi*r*r;
     124             :     }
     125             :   }
     126             : 
     127             :   // Find the largest sf cutoff
     128           6 :   lsfmax=low_sf(0,0).get_dmax();
     129           6 :   double sfmax=switchingFunction(0,0).get_dmax();
     130           6 :   double rfmax=cylinder_sw(0,0).get_dmax();
     131          18 :   for(unsigned i=0; i<getNumberOfNodeTypes(); ++i) {
     132          18 :     for(unsigned j=0; j<getNumberOfNodeTypes(); ++j) {
     133           6 :       double tsf=switchingFunction(i,j).get_dmax();
     134           6 :       if( tsf>sfmax ) sfmax=tsf;
     135           6 :       double rsf=cylinder_sw(i,j).get_dmax();
     136           6 :       if( rsf>rfmax ) rfmax=rsf;
     137           6 :       double lsf=low_sf(i,j).get_dmax();
     138           6 :       if( lsf>lsfmax ) lsfmax=lsf;
     139             :     }
     140             :   }
     141             :   // Get the width of the bead
     142           6 :   HistogramBead bead; bead.isNotPeriodic();
     143           6 :   bead.setKernelType( kerneltype ); bead.set( 0.0, 1.0, sigma );
     144           6 :   beadrad = bead.getCutoff();
     145             : 
     146             :   // Set the link cell cutoff
     147           6 :   log.printf("  setting cutoff %f \n", sfmax );
     148           6 :   setLinkCellCutoff( sfmax, std::numeric_limits<double>::max() );
     149             : 
     150             :   double maxsize=0;
     151          18 :   for(unsigned i=0; i<getNumberOfNodeTypes(); ++i) {
     152          18 :     for(unsigned j=0; j<getNumberOfNodeTypes(); ++j) {
     153           6 :       if( binw_mat(i,j)>maxsize ) maxsize=binw_mat(i,j);
     154             :     }
     155             :   }
     156             :   // Set the maximum number of bins that we will need to compute
     157           6 :   maxbins = std::floor( sfmax / maxsize ) + 1;
     158             :   // Need to resize functions again here to ensure that vector sizes
     159             :   // are set correctly in AdjacencyMatrixVessel
     160           6 :   resizeFunctions();
     161           6 : }
     162             : 
     163         100 : unsigned TopologyMatrix::getNumberOfQuantities() const {
     164         100 :   return maxbins+3;
     165             : }
     166             : 
     167          24 : void TopologyMatrix::setupConnector( const unsigned& id, const unsigned& i, const unsigned& j, const std::vector<std::string>& desc ) {
     168          24 :   plumed_assert( id<4 );
     169          24 :   if( id==0 ) {
     170           6 :     std::string errors; switchingFunction(j,i).set(desc[0],errors);
     171           6 :     if( errors.length()!=0 ) error("problem reading switching function description " + errors);
     172           6 :     if( j!=i) switchingFunction(i,j).set(desc[0],errors);
     173          24 :     log.printf("  %u th and %u th multicolvar groups must be within %s\n",i+1,j+1,(switchingFunction(i,j).description()).c_str() );
     174          18 :   } else if( id==1 ) {
     175           6 :     std::string errors; cylinder_sw(j,i).set(desc[0],errors);
     176           6 :     if( errors.length()!=0 ) error("problem reading switching function description " + errors);
     177           6 :     if( j!=i) cylinder_sw(i,j).set(desc[0],errors);
     178          24 :     log.printf("  there must be not atoms within the cylinder connections atoms of multicolvar groups %u th and %u th.  This cylinder has radius %s \n",i+1,j+1,(cylinder_sw(i,j).description()).c_str() );
     179          12 :   } else if( id==2 ) {
     180           6 :     std::string errors; low_sf(j,i).set(desc[0],errors);
     181           6 :     if( errors.length()!=0 ) error("problem reading switching function description " + errors);
     182           6 :     if( j!=i ) low_sf(i,j).set(desc[0],errors);
     183          24 :     log.printf("  %u th and %u th multicolvar groups must be further apart than %s\n",i+1,j+1,(low_sf(j,i).description()).c_str() );
     184           6 :   } else if( id==3 ) {
     185           6 :     Tools::convert( desc[0], binw_mat(j,i) );
     186           6 :     if( i!=j ) binw_mat(i,j)=binw_mat(j,i);
     187          12 :     log.printf("  cylinder for %u th and %u th multicolvar groups is split into bins of length %f \n",i,j,binw_mat(i,j) );
     188             :   }
     189          24 : }
     190             : 
     191       31203 : double TopologyMatrix::calculateWeight( const unsigned& taskCode, const double& weight, multicolvar::AtomValuePack& myatoms ) const {
     192       62406 :   Vector distance = getSeparation( myatoms.getPosition(0), myatoms.getPosition(1) );
     193       62406 :   if( distance.modulo2()<switchingFunction( getBaseColvarNumber( myatoms.getIndex(0) ), getBaseColvarNumber( myatoms.getIndex(1) ) ).get_dmax2() ) return 1.0;
     194       21587 :   return 0.0;
     195             : }
     196             : 
     197        9616 : double TopologyMatrix::compute( const unsigned& tindex, multicolvar::AtomValuePack& myatoms ) const {
     198       19232 :   HistogramBead bead; bead.isNotPeriodic(); bead.setKernelType( kerneltype );
     199             : 
     200             :   // Initialise to zero density on all bins
     201       86784 :   for(unsigned bin=0; bin<maxbins; ++bin) myatoms.setValue(bin+1,0);
     202             :   // Calculate whether or not atoms 1 and 2 are within cutoff (can use delta here as pbc are done in atom setup)
     203       19232 :   Vector d1 = getSeparation( myatoms.getPosition(0), myatoms.getPosition(1) ); double d1_len = d1.modulo();
     204        9616 :   d1 = d1 / d1_len;  // Convert vector into director
     205        9616 :   AtomNumber a1 = myatoms.getAbsoluteIndex( 0 );
     206        9616 :   AtomNumber a2 = myatoms.getAbsoluteIndex( 1 );
     207   235854830 :   for(unsigned i=2; i<myatoms.getNumberOfAtoms(); ++i) {
     208   117917799 :     AtomNumber a3 = myatoms.getAbsoluteIndex( i );
     209   235835598 :     if( a3!=a1 && a3!=a2 ) calculateForThreeAtoms( i, d1, d1_len, bead, myatoms );
     210             :   }
     211             :   // std::vector<double> binvals( 1+maxbins ); for(unsigned i=1;i<maxbins;++i) binvals[i]=myatoms.getValue(i);
     212             :   // unsigned ii; double fdf;
     213             :   //std::cout<<"HELLO DENSITY "<<myatoms.getIndex(0)<<" "<<myatoms.getIndex(1)<<" "<<transformStoredValues( binvals, ii, fdf )<<std::endl;
     214             : 
     215             :   // Now find the element for which the density is maximal
     216             :   unsigned vout=2; double max=myatoms.getValue( 2 );
     217       67552 :   for(unsigned i=3; i<myatoms.getUnderlyingMultiValue().getNumberOfValues()-1; ++i) {
     218       28968 :     if( myatoms.getValue(i)>max ) { max=myatoms.getValue(i); vout=i; }
     219             :   }
     220             :   // Calculate value and derivative of switching function between atoms 1 and 2
     221        9616 :   double dfuncl, sw = switchingFunction( getBaseColvarNumber( myatoms.getIndex(0) ),
     222        9616 :                                          getBaseColvarNumber( myatoms.getIndex(1) ) ).calculate( d1_len, dfuncl );
     223             :   // Transform the density
     224        9616 :   double df, tsw = threshold_switch.calculate( max, df );
     225        9616 :   if( !doNotCalculateDerivatives() ) {
     226             :     // Factor of d1_len is required here because d1 is normalized
     227          60 :     d1 *= d1_len;
     228          60 :     addAtomDerivatives( 2+maxbins, 0, -dfuncl*d1, myatoms );
     229          60 :     addAtomDerivatives( 2+maxbins, 1, dfuncl*d1, myatoms );
     230          60 :     myatoms.addBoxDerivatives( 2+maxbins, (-dfuncl)*Tensor(d1,d1) );
     231             :     // Update active atoms so that next bit works
     232          60 :     updateActiveAtoms( myatoms );
     233             :     // Now finish caclulation of derivatives
     234             :     MultiValue& myvals=myatoms.getUnderlyingMultiValue();
     235       25998 :     for(unsigned jd=0; jd<myvals.getNumberActive(); ++jd) {
     236       12969 :       unsigned ider=myvals.getActiveIndex(jd);
     237       38907 :       myvals.addDerivative( 1, ider, sw*df*max*myvals.getDerivative( vout, ider ) + tsw*myvals.getDerivative( 2+maxbins, ider ) );
     238             :     }
     239             :   }
     240        9616 :   return sw*tsw;
     241             : }
     242             : 
     243   117917799 : void TopologyMatrix::calculateForThreeAtoms( const unsigned& iat, const Vector& d1, const double& d1_len,
     244             :     HistogramBead& bead, multicolvar::AtomValuePack& myatoms ) const {
     245             :   // Calculate if there are atoms in the cylinder (can use delta here as pbc are done in atom setup)
     246   235835598 :   Vector d2 = getSeparation( myatoms.getPosition(0), myatoms.getPosition(iat) );
     247             :   // Now calculate projection of d2 on d1
     248   117917799 :   double proj=dotProduct(d2,d1);
     249             :   // This tells us if we are outside the end of the cylinder
     250   117917799 :   double excess = proj - d1_len;
     251             :   // Return if we are outside of the cylinder as calculated based on excess
     252   212090079 :   if( excess>low_sf( getBaseColvarNumber( myatoms.getIndex(0) ), getBaseColvarNumber( myatoms.getIndex(1) ) ).get_dmax() ) return;
     253             :   // Find the length of the cylinder
     254             :   double binw = binw_mat( getBaseColvarNumber( myatoms.getIndex(0) ), getBaseColvarNumber( myatoms.getIndex(1) ) );
     255    81802351 :   double lcylinder = (std::floor( d1_len / binw ) + 1)*binw;
     256             :   // Return if the projection is outside the length of interest
     257    81802351 :   if( proj<-bead.getCutoff() || proj>(lcylinder+bead.getCutoff()) ) return;
     258             : 
     259             :   // Calculate the excess swiching function
     260    23745519 :   double edf, eval = low_sf( getBaseColvarNumber( myatoms.getIndex(0) ), getBaseColvarNumber( myatoms.getIndex(1) ) ).calculate( excess, edf );
     261             :   // Calculate the projection on the perpendicular distance from the center of the tube
     262    23745519 :   double cm = d2.modulo2() - proj*proj;
     263             : 
     264             :   // Now calculate the density in the cylinder
     265    23745519 :   if( cm<cylinder_sw( getBaseColvarNumber( myatoms.getIndex(0) ), getBaseColvarNumber( myatoms.getIndex(1) ) ).get_dmax2() ) {
     266      104467 :     double dfuncr, val = cylinder_sw( getBaseColvarNumber( myatoms.getIndex(0) ),
     267      104467 :                                       getBaseColvarNumber( myatoms.getIndex(1) ) ).calculateSqr( cm, dfuncr );
     268             :     double cellv = cell_volume( getBaseColvarNumber( myatoms.getIndex(0) ), getBaseColvarNumber( myatoms.getIndex(1) ) );
     269      104467 :     Vector dc1, dc2, dc3, dd1, dd2, dd3, de1, de2, de3;
     270      104467 :     if( !doNotCalculateDerivatives() ) {
     271        4023 :       Tensor d1_a1;
     272             :       // Derivative of director connecting atom1 - atom2 wrt the position of atom 1
     273        4023 :       d1_a1(0,0) = ( -(d1[1]*d1[1]+d1[2]*d1[2])/d1_len );   // dx/dx
     274        4023 :       d1_a1(0,1) = (  d1[0]*d1[1]/d1_len );                 // dx/dy
     275        4023 :       d1_a1(0,2) = (  d1[0]*d1[2]/d1_len );                 // dx/dz
     276        4023 :       d1_a1(1,0) = (  d1[1]*d1[0]/d1_len );                 // dy/dx
     277        4023 :       d1_a1(1,1) = ( -(d1[0]*d1[0]+d1[2]*d1[2])/d1_len );   // dy/dy
     278        4023 :       d1_a1(1,2) = (  d1[1]*d1[2]/d1_len );
     279        4023 :       d1_a1(2,0) = (  d1[2]*d1[0]/d1_len );
     280        4023 :       d1_a1(2,1) = (  d1[2]*d1[1]/d1_len );
     281        4023 :       d1_a1(2,2) = ( -(d1[1]*d1[1]+d1[0]*d1[0])/d1_len );
     282             : 
     283             :       // Calculate derivatives of dot product
     284        4023 :       dd1 = matmul(d2, d1_a1) - d1;
     285        4023 :       dd2 = matmul(d2, -d1_a1);
     286        4023 :       dd3 = d1;
     287             : 
     288             :       // Calculate derivatives of cross product
     289        4023 :       dc1 = dfuncr*( -d2 - proj*dd1 );
     290        4023 :       dc2 = dfuncr*( -proj*dd2 );
     291        4023 :       dc3 = dfuncr*( d2 - proj*dd3 );
     292             : 
     293             :       // Calculate derivatives of excess
     294        4023 :       de1 = edf*excess*( dd1 + d1 );
     295        4023 :       de2 = edf*excess*( dd2 - d1 );
     296        4023 :       de3 = edf*excess*dd3;
     297             :     }
     298             : 
     299      208934 :     Vector pos1 = myatoms.getPosition(0) + d1_len*d1;
     300      104467 :     Vector pos2 = myatoms.getPosition(0) + d2;
     301      104467 :     Vector g1derivf,g2derivf,lderivf; Tensor vir;
     302      530381 :     for(unsigned bin=0; bin<maxbins; ++bin) {
     303      425914 :       bead.set( bin*binw, (bin+1)*binw, sigma );
     304      719410 :       if( proj<(bin*binw-bead.getCutoff()) || proj>binw*(bin+1)+bead.getCutoff() ) continue;
     305      132418 :       double der, contr=bead.calculateWithCutoff( proj, der ) / cellv; der /= cellv;
     306      132418 :       myatoms.addValue( 2+bin, contr*val*eval );
     307             : 
     308      132418 :       if( !doNotCalculateDerivatives() ) {
     309        5835 :         g1derivf=contr*eval*dc1 + val*eval*der*dd1 + contr*val*de1;
     310        5835 :         addAtomDerivatives( 2+bin, 0, g1derivf, myatoms );
     311        5835 :         g2derivf=contr*eval*dc2 + val*eval*der*dd2 + contr*val*de2;
     312        5835 :         addAtomDerivatives( 2+bin, 1, g2derivf, myatoms );
     313        5835 :         lderivf=contr*eval*dc3 + val*eval*der*dd3 + contr*val*de3;
     314        5835 :         addAtomDerivatives( 2+bin, iat, lderivf, myatoms );
     315             :         // Virial
     316       11670 :         vir = -Tensor( myatoms.getPosition(0), g1derivf ) - Tensor( pos1, g2derivf ) - Tensor( pos2, lderivf );
     317        5835 :         myatoms.addBoxDerivatives( 2+bin, vir );
     318             :       }
     319             :     }
     320             :   }
     321             : }
     322             : 
     323             : }
     324        4839 : }
     325             : 

Generated by: LCOV version 1.13