LCOV - code coverage report
Current view: top level - colvar - ContactMap.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 119 130 91.5 %
Date: 2020-11-18 11:20:57 Functions: 10 13 76.9 %

          Line data    Source code
       1             : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
       2             :    Copyright (c) 2012-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 "Colvar.h"
      23             : #include "tools/NeighborList.h"
      24             : #include "ActionRegister.h"
      25             : #include "tools/SwitchingFunction.h"
      26             : 
      27             : #include <string>
      28             : #include <cmath>
      29             : 
      30             : using namespace std;
      31             : 
      32             : namespace PLMD {
      33             : namespace colvar {
      34             : 
      35             : //+PLUMEDOC COLVAR CONTACTMAP
      36             : /*
      37             : Calculate the distances between a number of pairs of atoms and transform each distance by a switching function.
      38             : 
      39             : The transformed distance can be compared with a reference value in order to calculate the squared distance
      40             : between two contact maps. Each distance can also be weighted for a given value. CONTACTMAP can be used together
      41             : with \ref FUNCPATHMSD to define a path in the contactmap space.
      42             : 
      43             : The individual contact map distances related to each contact can be accessed as components
      44             : named `cm.contact-1`, `cm.contact-2`, etc, assuming that the label of the CONTACTMAP is `cm`.
      45             : 
      46             : \par Examples
      47             : 
      48             : The following example calculates switching functions based on the distances between atoms
      49             : 1 and 2, 3 and 4 and 4 and 5. The values of these three switching functions are then output
      50             : to a file named colvar.
      51             : 
      52             : \plumedfile
      53             : CONTACTMAP ATOMS1=1,2 ATOMS2=3,4 ATOMS3=4,5 ATOMS4=5,6 SWITCH={RATIONAL R_0=1.5} LABEL=f1
      54             : PRINT ARG=f1.* FILE=colvar
      55             : \endplumedfile
      56             : 
      57             : The following example calculates the difference of the current contact map with respect
      58             : to a reference provided. In this case REFERENCE is the fraction of contact that is formed
      59             : (i.e. the distance between two atoms transformed with the SWITH), while R_0 is the contact
      60             : distance. WEIGHT gives the relative weight of each contact to the final distance measure.
      61             : 
      62             : \plumedfile
      63             : CONTACTMAP ...
      64             : ATOMS1=1,2 REFERENCE1=0.1 WEIGHT1=0.5
      65             : ATOMS2=3,4 REFERENCE2=0.5 WEIGHT2=1.0
      66             : ATOMS3=4,5 REFERENCE3=0.25 WEIGHT3=1.0
      67             : ATOMS4=5,6 REFERENCE4=0.0 WEIGHT4=0.5
      68             : SWITCH={RATIONAL R_0=1.5}
      69             : LABEL=cmap
      70             : CMDIST
      71             : ... CONTACTMAP
      72             : 
      73             : PRINT ARG=cmap FILE=colvar
      74             : \endplumedfile
      75             : 
      76             : The next example calculates calculates fraction of native contacts (Q)
      77             : for Trp-cage mini-protein. R_0 is the distance at which the switch function is guaranteed to
      78             : be 1.0 – it doesn't really matter for Q and  should be something very small, like 1 A.
      79             : REF is the reference distance for the contact, e.g. the distance from a crystal structure.
      80             : LAMBDA is the tolerance for the distance – if set to 1.0, the contact would have to have exactly
      81             : the reference value to be formed; instead for lambda values of 1.5–1.8 are usually used to allow some slack.
      82             : BETA is the softness of the switch function, default is 50nm.
      83             : WEIGHT is the 1/(number of contacts) giving equal weight to each contact.
      84             : 
      85             : When using native contact Q switch function, please cite \cite best2013
      86             : 
      87             : \plumedfile
      88             : # Full example available in regtest/basic/rt72/
      89             : 
      90             : CONTACTMAP ...
      91             : ATOMS1=1,67 SWITCH1={Q R_0=0.01 BETA=50.0 LAMBDA=1.5 REF=0.4059} WEIGHT1=0.003597
      92             : ATOMS2=1,68 SWITCH2={Q R_0=0.01 BETA=50.0 LAMBDA=1.5 REF=0.4039} WEIGHT2=0.003597
      93             : ATOMS3=1,69 SWITCH3={Q R_0=0.01 BETA=50.0 LAMBDA=1.5 REF=0.3215} WEIGHT3=0.003597
      94             : [snip]
      95             : ATOMS275=183,213 SWITCH275={Q R_0=0.01 BETA=50.0 LAMBDA=1.5 REF=0.355} WEIGHT275=0.003597
      96             : ATOMS276=183,234 SWITCH276={Q R_0=0.01 BETA=50.0 LAMBDA=1.5 REF=0.428} WEIGHT276=0.003597
      97             : ATOMS277=183,250 SWITCH277={Q R_0=0.01 BETA=50.0 LAMBDA=1.5 REF=0.3832} WEIGHT277=0.003597
      98             : ATOMS278=197,220 SWITCH278={Q R_0=0.01 BETA=50.0 LAMBDA=1.5 REF=0.3827} WEIGHT278=0.003597
      99             : LABEL=cmap
     100             : SUM
     101             : ... CONTACTMAP
     102             : 
     103             : PRINT ARG=cmap FILE=colvar
     104             : \endplumedfile
     105             : (See also \ref switchingfunction)
     106             : 
     107             : */
     108             : //+ENDPLUMEDOC
     109             : 
     110             : class ContactMap : public Colvar {
     111             : private:
     112             :   bool pbc, serial, docomp, dosum, docmdist;
     113             :   NeighborList *nl;
     114             :   std::vector<SwitchingFunction> sfs;
     115             :   vector<double> reference, weight;
     116             : public:
     117             :   static void registerKeywords( Keywords& keys );
     118             :   explicit ContactMap(const ActionOptions&);
     119             :   ~ContactMap();
     120             : // active methods:
     121             :   virtual void calculate();
     122           0 :   void checkFieldsAllowed() {}
     123             : };
     124             : 
     125        6456 : PLUMED_REGISTER_ACTION(ContactMap,"CONTACTMAP")
     126             : 
     127           5 : void ContactMap::registerKeywords( Keywords& keys ) {
     128           5 :   Colvar::registerKeywords( keys );
     129          20 :   keys.add("numbered","ATOMS","the atoms involved in each of the contacts you wish to calculate. "
     130             :            "Keywords like ATOMS1, ATOMS2, ATOMS3,... should be listed and one contact will be "
     131             :            "calculated for each ATOM keyword you specify.");
     132          15 :   keys.reset_style("ATOMS","atoms");
     133          20 :   keys.add("numbered","SWITCH","The switching functions to use for each of the contacts in your map. "
     134             :            "You can either specify a global switching function using SWITCH or one "
     135             :            "switching function for each contact. Details of the various switching "
     136             :            "functions you can use are provided on \\ref switchingfunction.");
     137          20 :   keys.add("numbered","REFERENCE","A reference value for a given contact, by default is 0.0 "
     138             :            "You can either specify a global reference value using REFERENCE or one "
     139             :            "reference value for each contact.");
     140          20 :   keys.add("numbered","WEIGHT","A weight value for a given contact, by default is 1.0 "
     141             :            "You can either specify a global weight value using WEIGHT or one "
     142             :            "weight value for each contact.");
     143          15 :   keys.reset_style("SWITCH","compulsory");
     144          15 :   keys.addFlag("SUM",false,"calculate the sum of all the contacts in the input");
     145          15 :   keys.addFlag("CMDIST",false,"calculate the distance with respect to the provided reference contant map");
     146          15 :   keys.addFlag("SERIAL",false,"Perform the calculation in serial - for debug purpose");
     147          20 :   keys.addOutputComponent("contact","default","By not using SUM or CMDIST each contact will be stored in a component");
     148           5 : }
     149             : 
     150           4 : ContactMap::ContactMap(const ActionOptions&ao):
     151             :   PLUMED_COLVAR_INIT(ao),
     152             :   pbc(true),
     153             :   serial(false),
     154             :   docomp(true),
     155             :   dosum(false),
     156          12 :   docmdist(false)
     157             : {
     158           8 :   parseFlag("SERIAL",serial);
     159           8 :   parseFlag("SUM",dosum);
     160           8 :   parseFlag("CMDIST",docmdist);
     161           4 :   if(docmdist==true&&dosum==true) error("You cannot use SUM and CMDIST together");
     162           4 :   bool nopbc=!pbc;
     163           8 :   parseFlag("NOPBC",nopbc);
     164           4 :   pbc=!nopbc;
     165             : 
     166             :   // Read in the atoms
     167             :   std::vector<AtomNumber> t, ga_lista, gb_lista;
     168         293 :   for(int i=1;; ++i ) {
     169         594 :     parseAtomList("ATOMS", i, t );
     170         297 :     if( t.empty() ) break;
     171             : 
     172         293 :     if( t.size()!=2 ) {
     173           0 :       std::string ss; Tools::convert(i,ss);
     174           0 :       error("ATOMS" + ss + " keyword has the wrong number of atoms");
     175             :     }
     176         586 :     ga_lista.push_back(t[0]); gb_lista.push_back(t[1]);
     177         293 :     t.resize(0);
     178             : 
     179             :     // Add a value for this contact
     180         293 :     std::string num; Tools::convert(i,num);
     181         303 :     if(!dosum&&!docmdist) {addComponentWithDerivatives("contact-"+num); componentIsNotPeriodic("contact-"+num);}
     182         293 :   }
     183             :   // Create neighbour lists
     184           8 :   nl= new NeighborList(ga_lista,gb_lista,true,pbc,getPbc());
     185             : 
     186             :   // Read in switching functions
     187           4 :   std::string errors; sfs.resize( ga_lista.size() ); unsigned nswitch=0;
     188         887 :   for(unsigned i=0; i<ga_lista.size(); ++i) {
     189         293 :     std::string num, sw1; Tools::convert(i+1, num);
     190         586 :     if( !parseNumbered( "SWITCH", i+1, sw1 ) ) break;
     191         293 :     nswitch++; sfs[i].set(sw1,errors);
     192         293 :     if( errors.length()!=0 ) error("problem reading SWITCH" + num + " keyword : " + errors );
     193             :   }
     194           4 :   if( nswitch==0 ) {
     195           0 :     std::string sw; parse("SWITCH",sw);
     196           0 :     if(sw.length()==0) error("no switching function specified use SWITCH keyword");
     197           0 :     for(unsigned i=0; i<ga_lista.size(); ++i) {
     198           0 :       sfs[i].set(sw,errors);
     199           0 :       if( errors.length()!=0 ) error("problem reading SWITCH keyword : " + errors );
     200             :     }
     201           4 :   } else if( nswitch!=sfs.size()  ) {
     202           0 :     std::string num; Tools::convert(nswitch+1, num);
     203           0 :     error("missing SWITCH" + num + " keyword");
     204             :   }
     205             : 
     206             :   // Read in reference values
     207             :   nswitch=0;
     208           8 :   reference.resize(ga_lista.size(), 0.);
     209          28 :   for(unsigned i=0; i<ga_lista.size(); ++i) {
     210          24 :     if( !parseNumbered( "REFERENCE", i+1, reference[i] ) ) break;
     211             :     nswitch++;
     212             :   }
     213           4 :   if( nswitch==0 ) {
     214           4 :     parse("REFERENCE",reference[0]);
     215         847 :     for(unsigned i=1; i<ga_lista.size(); ++i) {
     216         281 :       reference[i]=reference[0];
     217         281 :       nswitch++;
     218             :     }
     219             :   }
     220           4 :   if(nswitch == 0 && docmdist) error("with CMDIST one must use REFERENCE to setup the reference contact map");
     221             : 
     222             :   // Read in weight values
     223             :   nswitch=0;
     224           8 :   weight.resize(ga_lista.size(), 1.0);
     225         584 :   for(unsigned i=0; i<ga_lista.size(); ++i) {
     226         578 :     if( !parseNumbered( "WEIGHT", i+1, weight[i] ) ) break;
     227             :     nswitch++;
     228             :   }
     229           4 :   if( nswitch==0 ) {
     230           2 :     parse("WEIGHT",weight[0]);
     231          14 :     for(unsigned i=1; i<ga_lista.size(); ++i) {
     232           4 :       weight[i]=weight[0];
     233             :     }
     234             :     nswitch = ga_lista.size();
     235             :   }
     236             : 
     237             :   // Ouput details of all contacts
     238         887 :   for(unsigned i=0; i<sfs.size(); ++i) {
     239         879 :     log.printf("  The %uth contact is calculated from atoms : %d %d. Inflection point of switching function is at %s. Reference contact value is %f\n",
     240         586 :                i+1, ga_lista[i].serial(), gb_lista[i].serial(), ( sfs[i].description() ).c_str(), reference[i] );
     241             :   }
     242             : 
     243           4 :   if(dosum) {
     244           1 :     addValueWithDerivatives(); setNotPeriodic();
     245           1 :     log.printf("  colvar is sum of all contacts in contact map\n");
     246             :   }
     247           4 :   if(docmdist) {
     248           2 :     addValueWithDerivatives(); setNotPeriodic();
     249           2 :     log.printf("  colvar is distance between the contact map matrix and the provided reference matrix\n");
     250             :   }
     251             : 
     252           4 :   if(dosum || docmdist) {
     253           3 :     docomp=false;
     254             :   } else {
     255           1 :     serial=true;
     256           1 :     docomp=true;
     257             :   }
     258             : 
     259             :   // Set up if it is just a list of contacts
     260           4 :   requestAtoms(nl->getFullAtomList());
     261           4 :   checkRead();
     262           4 : }
     263             : 
     264          12 : ContactMap::~ContactMap() {
     265           4 :   delete nl;
     266           8 : }
     267             : 
     268         211 : void ContactMap::calculate() {
     269             : 
     270         211 :   double ncoord=0.;
     271         211 :   Tensor virial;
     272         211 :   std::vector<Vector> deriv(getNumberOfAtoms());
     273             : 
     274             :   unsigned stride;
     275             :   unsigned rank;
     276         211 :   if(serial) {
     277             :     // when using components the parallelisation do not work
     278             :     stride=1;
     279             :     rank=0;
     280             :   } else {
     281         206 :     stride=comm.Get_size();
     282         206 :     rank=comm.Get_rank();
     283             :   }
     284             : 
     285             : // sum over close pairs
     286        2867 :   for(unsigned i=rank; i<nl->size(); i+=stride) {
     287        1328 :     Vector distance;
     288        1328 :     unsigned i0=nl->getClosePair(i).first;
     289        1328 :     unsigned i1=nl->getClosePair(i).second;
     290        1328 :     if(pbc) {
     291        3984 :       distance=pbcDistance(getPosition(i0),getPosition(i1));
     292             :     } else {
     293           0 :       distance=delta(getPosition(i0),getPosition(i1));
     294             :     }
     295             : 
     296        1328 :     double dfunc=0.;
     297        5312 :     double coord = weight[i]*(sfs[i].calculate(distance.modulo(), dfunc) - reference[i]);
     298        1328 :     Vector tmpder = weight[i]*dfunc*distance;
     299        2656 :     Tensor tmpvir = weight[i]*dfunc*Tensor(distance,distance);
     300        1328 :     if(!docmdist) {
     301         606 :       deriv[i0] -= tmpder;
     302         606 :       deriv[i1] += tmpder;
     303         303 :       virial    -= tmpvir;
     304         303 :       ncoord    += coord;
     305             :     } else {
     306        1025 :       tmpder *= 2.*coord;
     307        1025 :       tmpvir *= 2.*coord;
     308        2050 :       deriv[i0] -= tmpder;
     309        2050 :       deriv[i1] += tmpder;
     310        1025 :       virial    -= tmpvir;
     311        1025 :       ncoord    += coord*coord;
     312             :     }
     313             : 
     314        1328 :     if(docomp) {
     315          25 :       Value* val=getPntrToComponent( i );
     316          50 :       setAtomsDerivatives( val, i0, deriv[i0] );
     317          50 :       setAtomsDerivatives( val, i1, deriv[i1] );
     318          25 :       setBoxDerivatives( val, -tmpvir );
     319             :       val->set(coord);
     320             :     }
     321             :   }
     322             : 
     323         211 :   if(!serial) {
     324         206 :     comm.Sum(&ncoord,1);
     325         412 :     if(!deriv.empty()) comm.Sum(&deriv[0][0],3*deriv.size());
     326         206 :     comm.Sum(&virial[0][0],9);
     327             :   }
     328             : 
     329         211 :   if( !docomp ) {
     330       10836 :     for(unsigned i=0; i<deriv.size(); ++i) setAtomsDerivatives(i,deriv[i]);
     331         206 :     setValue           (ncoord);
     332         206 :     setBoxDerivatives  (virial);
     333             :   }
     334         211 : }
     335             : 
     336             : }
     337        4839 : }

Generated by: LCOV version 1.13