LCOV - code coverage report
Current view: top level - s2cm - S2ContactModel.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 153 172 89.0 %
Date: 2025-04-08 21:11:17 Functions: 6 8 75.0 %

          Line data    Source code
       1             : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
       2             :    Copyright (c) 2017-2021 Omar Valsson
       3             : 
       4             :    This file is part of S2 contact model module
       5             : 
       6             :    The S2 contact model module is free software: you can redistribute it and/or modify
       7             :    it under the terms of the GNU Lesser General Public License as published by
       8             :    the Free Software Foundation, either version 3 of the License, or
       9             :    (at your option) any later version.
      10             : 
      11             :    The S2 contact model module is distributed in the hope that it will be useful,
      12             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
      13             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      14             :    GNU Lesser General Public License for more details.
      15             : 
      16             :    You should have received a copy of the GNU Lesser General Public License
      17             :    along with the S2 contact model module.  If not, see <http://www.gnu.org/licenses/>.
      18             : +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
      19             : 
      20             : #include "colvar/Colvar.h"
      21             : #include "tools/NeighborList.h"
      22             : #include "tools/Communicator.h"
      23             : #include "core/ActionRegister.h"
      24             : #include "core/PlumedMain.h"
      25             : 
      26             : 
      27             : #include <string>
      28             : #include <cmath>
      29             : 
      30             : namespace PLMD {
      31             : namespace s2cm {
      32             : 
      33             : 
      34             : //
      35             : 
      36             : //+PLUMEDOC S2CMMOD_COLVAR S2CM
      37             : /*
      38             : S2 contact model CV.
      39             : 
      40             : This CV was used in \cite Palazzesi_s2_2017, based on NH order parameter from \cite Zhang_s2_2002 and methyl order parameter from \cite Ming_s2_2004. Input parameters can be found in the relevant papers.
      41             : 
      42             : \par Examples
      43             : 
      44             : 
      45             : */
      46             : //+ENDPLUMEDOC
      47             : 
      48             : 
      49             : 
      50             : 
      51             : 
      52             : 
      53             : 
      54             : class S2ContactModel : public Colvar {
      55             :   bool pbc_;
      56             :   bool serial_;
      57             :   std::unique_ptr<NeighborList> nl;
      58             :   bool invalidateList;
      59             :   bool firsttime;
      60             :   //
      61             :   double r_eff_;
      62             :   double inv_r_eff_;
      63             :   double prefactor_a_;
      64             :   double exp_b_;
      65             :   double offset_c_;
      66             :   double n_i_;
      67             :   double total_prefactor_;
      68             :   double r_globalshift_;
      69             : 
      70             :   enum ModelType {methyl,nh} modeltype_;
      71             : 
      72             :   //
      73             : public:
      74             :   explicit S2ContactModel(const ActionOptions&);
      75             :   ~S2ContactModel();
      76             :   virtual void calculate();
      77             :   virtual void prepare();
      78             :   static void registerKeywords( Keywords& keys );
      79             : };
      80             : 
      81             : PLUMED_REGISTER_ACTION(S2ContactModel,"S2CM")
      82             : 
      83          26 : void S2ContactModel::registerKeywords( Keywords& keys ) {
      84          26 :   Colvar::registerKeywords(keys);
      85          26 :   keys.addFlag("SERIAL",false,"Perform the calculation in serial - for debug purpose");
      86          26 :   keys.addFlag("NLIST",false,"Use a neighbour list to speed up the calculation");
      87          26 :   keys.add("optional","NL_CUTOFF","The cutoff for the neighbour list");
      88          26 :   keys.add("optional","NL_STRIDE","The frequency with which we are updating the atoms in the neighbour list");
      89          26 :   keys.add("atoms","METHYL_ATOM","the methyl carbon atom of the residue (i)");
      90          26 :   keys.add("atoms","NH_ATOMS","the hydrogen atom of the NH group of the residue (i) and carbonyl oxygen of the preceding residue (i-1)");
      91          26 :   keys.add("atoms","HEAVY_ATOMS","the heavy atoms to be included in the calculation");
      92             :   //
      93          26 :   keys.add("compulsory","R_EFF","the effective distance, r_eff in the equation, given in nm.");
      94          26 :   keys.add("compulsory","PREFACTOR_A","the prefactor, a in the equation");
      95          26 :   keys.add("compulsory","EXPONENT_B","the exponent, b in the equation");
      96          26 :   keys.add("compulsory","OFFSET_C","the offset, c in the equation");
      97          26 :   keys.add("compulsory","N_I"," n_i in the equation");
      98          26 :   keys.add("optional","R_SHIFT","shift all distances by given amount");
      99          52 :   keys.setValueDescription("scalar","the value of the CV");
     100          26 : }
     101             : 
     102          24 : S2ContactModel::S2ContactModel(const ActionOptions&ao):
     103             :   PLUMED_COLVAR_INIT(ao),
     104          24 :   pbc_(true),
     105          24 :   serial_(false),
     106          24 :   invalidateList(true),
     107          24 :   firsttime(true),
     108          24 :   r_eff_(0.0),
     109          24 :   inv_r_eff_(0.0),
     110          24 :   prefactor_a_(0.0),
     111          24 :   exp_b_(0.0),
     112          24 :   offset_c_(0.0),
     113          24 :   n_i_(0.0),
     114          24 :   total_prefactor_(0.0),
     115          24 :   r_globalshift_(0.0),
     116          24 :   modeltype_(methyl) {
     117             : 
     118          48 :   parseFlag("SERIAL",serial_);
     119             : 
     120             :   std::vector<AtomNumber> methyl_atom;
     121          48 :   parseAtomList("METHYL_ATOM",methyl_atom);
     122             :   std::vector<AtomNumber> nh_atoms;
     123          48 :   parseAtomList("NH_ATOMS",nh_atoms);
     124             : 
     125          24 :   if(methyl_atom.size()==0 && nh_atoms.size()==0) {
     126           0 :     error("you have to give either METHYL_ATOM or NH_ATOMS");
     127             :   }
     128          24 :   if(methyl_atom.size()>0 && nh_atoms.size()>0) {
     129           0 :     error("you cannot give both METHYL_ATOM or NH_ATOMS");
     130             :   }
     131          24 :   if(methyl_atom.size()>0 && methyl_atom.size()!=1) {
     132           0 :     error("you should give one atom in METHYL_ATOM, the methyl carbon atom of the residue");
     133             :   }
     134          24 :   if(nh_atoms.size()>0 && nh_atoms.size()!=2) {
     135           0 :     error("you should give two atoms in NH_ATOMS, the hydrogen atom of the NH group of the residue (i) and carbonyl oxygen of the preceding residue (i-1)");
     136             :   }
     137             : 
     138             :   std::vector<AtomNumber> heavy_atoms;
     139          48 :   parseAtomList("HEAVY_ATOMS",heavy_atoms);
     140          24 :   if(heavy_atoms.size()==0) {
     141           0 :     error("HEAVY_ATOMS is not given");
     142             :   }
     143             : 
     144             :   std::vector<AtomNumber> main_atoms;
     145          24 :   if(methyl_atom.size()>0) {
     146           0 :     modeltype_= methyl;
     147           0 :     main_atoms = methyl_atom;
     148          24 :   } else if(nh_atoms.size()>0) {
     149          24 :     modeltype_= nh;
     150          24 :     main_atoms = nh_atoms;
     151             :   }
     152             : 
     153          24 :   bool nopbc=!pbc_;
     154          24 :   parseFlag("NOPBC",nopbc);
     155          24 :   pbc_=!nopbc;
     156             : 
     157             :   // neighbor list stuff
     158          24 :   bool doneigh=false;
     159          24 :   double nl_cut=0.0;
     160          24 :   int nl_st=0;
     161          24 :   parseFlag("NLIST",doneigh);
     162          24 :   if(doneigh) {
     163          12 :     parse("NL_CUTOFF",nl_cut);
     164          12 :     if(nl_cut<=0.0) {
     165           0 :       error("NL_CUTOFF should be explicitly specified and positive");
     166             :     }
     167          12 :     parse("NL_STRIDE",nl_st);
     168          12 :     if(nl_st<=0) {
     169           0 :       error("NL_STRIDE should be explicitly specified and positive");
     170             :     }
     171             :   }
     172             : 
     173          24 :   parse("R_EFF",r_eff_);
     174          24 :   inv_r_eff_ = 1.0/r_eff_;
     175          24 :   parse("PREFACTOR_A",prefactor_a_);
     176          24 :   parse("EXPONENT_B",exp_b_);
     177          24 :   parse("OFFSET_C",offset_c_);
     178             :   unsigned int n_i_int;
     179          24 :   parse("N_I",n_i_int);
     180          24 :   n_i_ = static_cast<double>(n_i_int);
     181          24 :   total_prefactor_ = prefactor_a_/pow(n_i_,exp_b_);
     182             :   //
     183          24 :   parse("R_SHIFT",r_globalshift_);
     184             : 
     185          24 :   checkRead();
     186             : 
     187          24 :   addValueWithDerivatives();
     188          24 :   setNotPeriodic();
     189             : 
     190          24 :   bool dopair=false;
     191          24 :   if(doneigh) {
     192          24 :     nl=Tools::make_unique<NeighborList>(main_atoms,heavy_atoms,serial_,dopair,pbc_,getPbc(),comm,nl_cut,nl_st);
     193             :   } else {
     194          24 :     nl=Tools::make_unique<NeighborList>(main_atoms,heavy_atoms,serial_,dopair,pbc_,getPbc(),comm);
     195             :   }
     196             : 
     197          24 :   requestAtoms(nl->getFullAtomList());
     198             : 
     199             : 
     200          24 :   log.printf("  NMR S2 contact model CV, please read and cite ");
     201          48 :   log << plumed.cite("Palazzesi, Valsson, and Parrinello, J. Phys. Chem. Lett. 8, 4752 (2017) - DOI:10.1021/acs.jpclett.7b01770");
     202             : 
     203          24 :   if(modeltype_==methyl) {
     204           0 :     log << plumed.cite("Ming and Bruschweiler, J. Biomol. NMR, 29, 363 (2004) - DOI:10.1023/B:JNMR.0000032612.70767.35");
     205           0 :     log.printf("\n");
     206           0 :     log.printf("  calculation of methyl order parameter using atom %d \n",methyl_atom[0].serial());
     207          24 :   } else if(modeltype_==nh) {
     208          48 :     log << plumed.cite("Zhang and Bruschweiler, J. Am. Chem. Soc. 124, 12654 (2002) - DOI:10.1021/ja027847a");
     209          24 :     log.printf("\n");
     210          24 :     log.printf("  calculation of NH order parameter using atoms %d and %d\n",nh_atoms[0].serial(),nh_atoms[1].serial());
     211             :   }
     212          24 :   log.printf("  heavy atoms used in the calculation (%u in total):\n",static_cast<unsigned int>(heavy_atoms.size()));
     213        1872 :   for(unsigned int i=0; i<heavy_atoms.size(); ++i) {
     214        1848 :     if( (i+1) % 25 == 0 ) {
     215          72 :       log.printf("  \n");
     216             :     }
     217        1848 :     log.printf("  %d", heavy_atoms[i].serial());
     218             :   }
     219          24 :   log.printf("  \n");
     220          24 :   log.printf("  total number of distances: %u\n",nl->size());
     221             :   //
     222          24 :   log.printf("  using parameters");
     223          24 :   log.printf(" r_eff=%f,",r_eff_);
     224          24 :   log.printf(" a=%f,",prefactor_a_);
     225          24 :   log.printf(" b=%f,",exp_b_);
     226          24 :   log.printf(" c=%f,",offset_c_);
     227          24 :   log.printf(" n_i=%u",n_i_int);
     228          24 :   if(r_globalshift_!=0.0) {
     229           4 :     log.printf(", r_shift=%f",r_globalshift_);
     230             :   }
     231          24 :   log.printf("\n");
     232          24 :   if(pbc_) {
     233          12 :     log.printf("  using periodic boundary conditions\n");
     234             :   } else {
     235          12 :     log.printf("  without periodic boundary conditions\n");
     236             :   }
     237          24 :   if(doneigh) {
     238          12 :     log.printf("  using neighbor lists with\n");
     239          12 :     log.printf("  update every %d steps and cutoff %f\n",nl_st,nl_cut);
     240             :   }
     241          24 :   if(serial_) {
     242           0 :     log.printf("  calculation done in serial\n");
     243             :   }
     244          24 : }
     245             : 
     246          48 : S2ContactModel::~S2ContactModel() {
     247          48 : }
     248             : 
     249          48 : void S2ContactModel::prepare() {
     250          48 :   if(nl->getStride()>0) {
     251          24 :     if(firsttime || (getStep()%nl->getStride()==0)) {
     252          24 :       requestAtoms(nl->getFullAtomList());
     253          24 :       invalidateList=true;
     254          24 :       firsttime=false;
     255             :     } else {
     256           0 :       requestAtoms(nl->getReducedAtomList());
     257           0 :       invalidateList=false;
     258           0 :       if(getExchangeStep()) {
     259           0 :         error("Neighbor lists should be updated on exchange steps - choose a NL_STRIDE which divides the exchange stride!");
     260             :       }
     261             :     }
     262          24 :     if(getExchangeStep()) {
     263           0 :       firsttime=true;
     264             :     }
     265             :   }
     266          48 : }
     267             : 
     268             : // calculator
     269        5952 : void S2ContactModel::calculate() {
     270             : 
     271        5952 :   Tensor virial;
     272        5952 :   std::vector<Vector> deriv(getNumberOfAtoms());
     273             : 
     274        5952 :   if(nl->getStride()>0 && invalidateList) {
     275        2976 :     nl->update(getPositions());
     276             :   }
     277             : 
     278        5952 :   unsigned int stride=comm.Get_size();
     279        5952 :   unsigned int rank=comm.Get_rank();
     280        5952 :   if(serial_) {
     281             :     stride=1;
     282             :     rank=0;
     283             :   }
     284             : 
     285        5952 :   double contact_sum = 0.0;
     286             : 
     287        5952 :   const unsigned int nn=nl->size();
     288             : 
     289      321408 :   for(unsigned int i=rank; i<nn; i+=stride) {
     290      315456 :     Vector distance;
     291      315456 :     unsigned int i0=nl->getClosePair(i).first;
     292      315456 :     unsigned int i1=nl->getClosePair(i).second;
     293      315456 :     if(getAbsoluteIndex(i0)==getAbsoluteIndex(i1)) {
     294           0 :       continue;
     295             :     }
     296             : 
     297      315456 :     if(pbc_) {
     298       86304 :       distance=pbcDistance(getPosition(i0),getPosition(i1));
     299             :     } else {
     300      229152 :       distance=delta(getPosition(i0),getPosition(i1));
     301             :     }
     302             : 
     303      315456 :     double exp_arg = exp(-(distance.modulo()-r_globalshift_)*inv_r_eff_);
     304      315456 :     contact_sum += exp_arg;
     305             : 
     306      315456 :     exp_arg /= distance.modulo();
     307      315456 :     Vector dd(exp_arg*distance);
     308      315456 :     Tensor vv(dd,distance);
     309      315456 :     deriv[i0]-=dd;
     310      315456 :     deriv[i1]+=dd;
     311      315456 :     virial-=vv;
     312             :   }
     313             : 
     314        5952 :   if(!serial_) {
     315        5952 :     comm.Sum(contact_sum);
     316        5952 :     if(!deriv.empty()) {
     317        5952 :       comm.Sum(&deriv[0][0],3*deriv.size());
     318             :     }
     319        5952 :     comm.Sum(virial);
     320             :   }
     321             : 
     322        5952 :   double value = tanh(total_prefactor_*contact_sum);
     323             :   // using that d/dx[tanh(x)]= 1-[tanh(x)]^2
     324        5952 :   double deriv_f = -inv_r_eff_*total_prefactor_*(1.0-value*value);
     325        5952 :   value -= offset_c_;
     326             : 
     327      476160 :   for(unsigned int i=0; i<deriv.size(); ++i) {
     328      470208 :     setAtomsDerivatives(i,deriv_f*deriv[i]);
     329             :   }
     330        5952 :   setValue(value);
     331       11904 :   setBoxDerivatives(deriv_f*virial);
     332             : 
     333        5952 : }
     334             : }
     335             : }

Generated by: LCOV version 1.16