LCOV - code coverage report
Current view: top level - isdb - Jcoupling.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 160 172 93.0 %
Date: 2024-10-11 08:09:47 Functions: 7 8 87.5 %

          Line data    Source code
       1             : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
       2             :    Copyright (c) 2016-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 "MetainferenceBase.h"
      23             : #include "core/ActionRegister.h"
      24             : #include "tools/Pbc.h"
      25             : #include "tools/Torsion.h"
      26             : 
      27             : namespace PLMD {
      28             : namespace isdb {
      29             : 
      30             : //+PLUMEDOC ISDB_COLVAR JCOUPLING
      31             : /*
      32             : Calculates \f$^3J\f$ coupling constants for a dihedral angle.
      33             : 
      34             : The J-coupling between two atoms is given by the Karplus relation:
      35             : 
      36             : \f[
      37             : ^3J(\theta)=A\cos^2(\theta+\Delta\theta)+B\cos(\theta+\Delta\theta)+C
      38             : \f]
      39             : 
      40             : where \f$A\f$, \f$B\f$ and \f$C\f$ are the Karplus parameters and \f$\Delta\theta\f$ is an additional constant
      41             : added on to the dihedral angle \f$\theta\f$. The Karplus parameters are determined empirically and are dependent
      42             : on the type of J-coupling.
      43             : 
      44             : This collective variable computes the J-couplings for a set of atoms defining a dihedral angle. You can specify
      45             : the atoms involved using the \ref MOLINFO notation. You can also specify the experimental couplings using the
      46             :  COUPLING keywords. These will be included in the output. You must choose the type of
      47             : coupling using the type keyword, you can also supply custom Karplus parameters using TYPE=CUSTOM and the A, B, C
      48             : and SHIFT keywords. You will need to make sure you are using the correct dihedral angle:
      49             : 
      50             : - Ha-N: \f$\psi\f$
      51             : - Ha-HN: \f$\phi\f$
      52             : - N-C\f$\gamma\f$: \f$\chi_1\f$
      53             : - CO-C\f$\gamma\f$: \f$\chi_1\f$
      54             : 
      55             : J-couplings can be used to calculate a Metainference score using the internal keyword DOSCORE and all the options
      56             : of \ref METAINFERENCE .
      57             : 
      58             : \par Examples
      59             : 
      60             : In the following example we calculate the Ha-N J-coupling from a set of atoms involved in
      61             : dihedral \f$\psi\f$ angles in the peptide backbone. We also add the experimental data points and compute
      62             : the correlation and other measures and finally print the results.
      63             : 
      64             : \plumedfile
      65             : #SETTINGS MOLFILE=regtest/basic/rt32/helix.pdb
      66             : MOLINFO MOLTYPE=protein STRUCTURE=peptide.pdb
      67             : WHOLEMOLECULES ENTITY0=1-111
      68             : 
      69             : JCOUPLING ...
      70             :     TYPE=HAN
      71             :     ATOMS1=@psi-2 COUPLING1=-0.49
      72             :     ATOMS2=@psi-4 COUPLING2=-0.54
      73             :     ATOMS3=@psi-5 COUPLING3=-0.53
      74             :     ATOMS4=@psi-7 COUPLING4=-0.39
      75             :     ATOMS5=@psi-8 COUPLING5=-0.39
      76             :     LABEL=jhan
      77             : ... JCOUPLING
      78             : 
      79             : jhanst: STATS ARG=(jhan\.j-.*) PARARG=(jhan\.exp-.*)
      80             : 
      81             : PRINT ARG=jhanst.*,jhan.* FILE=COLVAR STRIDE=100
      82             : \endplumedfile
      83             : 
      84             : */
      85             : //+ENDPLUMEDOC
      86             : 
      87             : class JCoupling :
      88             :   public MetainferenceBase
      89             : {
      90             : private:
      91             :   bool pbc;
      92             :   enum { HAN, HAHN, CCG, NCG, CUSTOM };
      93             :   unsigned ncoupl_;
      94             :   double ka_;
      95             :   double kb_;
      96             :   double kc_;
      97             :   double kshift_;
      98             : 
      99             : public:
     100             :   static void registerKeywords(Keywords& keys);
     101             :   explicit JCoupling(const ActionOptions&);
     102             :   void calculate() override;
     103             :   void update() override;
     104             : };
     105             : 
     106       10431 : PLUMED_REGISTER_ACTION(JCoupling, "JCOUPLING")
     107             : 
     108           7 : void JCoupling::registerKeywords(Keywords& keys) {
     109           7 :   componentsAreNotOptional(keys);
     110           7 :   MetainferenceBase::registerKeywords(keys);
     111          14 :   keys.addFlag("NOPBC",false,"ignore the periodic boundary conditions when calculating distances");
     112          14 :   keys.add("numbered", "ATOMS", "the 4 atoms involved in each of the bonds for which you wish to calculate the J-coupling. "
     113             :            "Keywords like ATOMS1, ATOMS2, ATOMS3,... should be listed and one J-coupling will be "
     114             :            "calculated for each ATOMS keyword you specify.");
     115          14 :   keys.reset_style("ATOMS", "atoms");
     116          14 :   keys.add("compulsory", "TYPE", "Type of J-coupling to compute (HAN,HAHN,CCG,NCG,CUSTOM)");
     117          14 :   keys.add("optional", "A", "Karplus parameter A");
     118          14 :   keys.add("optional", "B", "Karplus parameter B");
     119          14 :   keys.add("optional", "C", "Karplus parameter C");
     120          14 :   keys.add("optional", "SHIFT", "Angle shift in radians");
     121          14 :   keys.add("numbered", "COUPLING", "Add an experimental value for each coupling");
     122          14 :   keys.addOutputComponent("j", "default", "the calculated J-coupling");
     123          14 :   keys.addOutputComponent("exp", "COUPLING", "the experimental J-coupling");
     124           7 : }
     125             : 
     126           6 : JCoupling::JCoupling(const ActionOptions&ao):
     127             :   PLUMED_METAINF_INIT(ao),
     128           6 :   pbc(true)
     129             : {
     130           6 :   bool nopbc = !pbc;
     131           6 :   parseFlag("NOPBC", nopbc);
     132           6 :   pbc =! nopbc;
     133             : 
     134             :   // Read in the atoms
     135             :   std::vector<AtomNumber> t, atoms;
     136           6 :   for (int i = 1; ; ++i) {
     137          68 :     parseAtomList("ATOMS", i, t );
     138          34 :     if (t.empty()) {
     139             :       break;
     140             :     }
     141             : 
     142          28 :     if (t.size() != 4) {
     143             :       std::string ss;
     144           0 :       Tools::convert(i, ss);
     145           0 :       error("ATOMS" + ss + " keyword has the wrong number of atoms");
     146             :     }
     147             : 
     148             :     // This makes the distance calculation easier later on (see Torsion implementation)
     149          28 :     atoms.push_back(t[0]);
     150          28 :     atoms.push_back(t[1]);
     151          28 :     atoms.push_back(t[1]);
     152          28 :     atoms.push_back(t[2]);
     153          28 :     atoms.push_back(t[2]);
     154          28 :     atoms.push_back(t[3]);
     155          28 :     t.resize(0);
     156          28 :   }
     157             : 
     158             :   // We now have 6 atoms per datapoint
     159           6 :   ncoupl_ = atoms.size()/6;
     160             : 
     161             :   // Parse J-Coupling type, this will determine the Karplus parameters
     162             :   unsigned jtype_ = CUSTOM;
     163             :   std::string string_type;
     164          12 :   parse("TYPE", string_type);
     165           6 :   if(string_type == "HAN") {
     166             :     jtype_ = HAN;
     167           5 :   } else if(string_type == "HAHN") {
     168             :     jtype_ = HAHN;
     169           2 :   } else if(string_type == "CCG") {
     170             :     jtype_ = CCG;
     171           1 :   } else if(string_type == "NCG") {
     172             :     jtype_ = NCG;
     173           0 :   } else if(string_type == "CUSTOM") {
     174             :     jtype_ = CUSTOM;
     175             :   } else {
     176           0 :     error("Unknown J-coupling type!");
     177             :   }
     178             : 
     179             :   // Optionally add an experimental value (like with RDCs)
     180             :   std::vector<double> coupl;
     181           6 :   coupl.resize( ncoupl_ );
     182             :   unsigned ntarget=0;
     183          13 :   for(unsigned i=0; i<ncoupl_; ++i) {
     184          24 :     if( !parseNumbered( "COUPLING", i+1, coupl[i] ) ) break;
     185           7 :     ntarget++;
     186             :   }
     187             :   bool addcoupling=false;
     188           6 :   if(ntarget!=ncoupl_ && ntarget!=0) error("found wrong number of COUPLING values");
     189           6 :   if(ntarget==ncoupl_) addcoupling=true;
     190           6 :   if(getDoScore()&&!addcoupling) error("with DOSCORE you need to set the COUPLING values");
     191             : 
     192             :   // For custom types we allow use of custom Karplus parameters
     193           6 :   if (jtype_ == CUSTOM) {
     194           0 :     parse("A", ka_);
     195           0 :     parse("B", kb_);
     196           0 :     parse("C", kc_);
     197           0 :     parse("SHIFT", kshift_);
     198             :   }
     199             : 
     200           6 :   log << "  Bibliography ";
     201          12 :   log<<plumed.cite("Bonomi, Camilloni, Bioinformatics, 33, 3999 (2017)");
     202             : 
     203             :   // Set Karplus parameters
     204           6 :   switch (jtype_) {
     205           1 :   case HAN:
     206           1 :     ka_ = -0.88;
     207           1 :     kb_ = -0.61;
     208           1 :     kc_ = -0.27;
     209           1 :     kshift_ = pi / 3.0;
     210           2 :     log << plumed.cite("Wang A C, Bax A, J. Am. Chem. Soc. 117, 1810 (1995)");
     211           1 :     log<<"\n";
     212           1 :     log.printf("  J-coupling type is HAN, with A: %f, B: %f, C: %f, angle shift: %f\n", ka_, kb_, kc_, kshift_);
     213             :     break;
     214           3 :   case HAHN:
     215           3 :     ka_ = 7.09;
     216           3 :     kb_ = -1.42;
     217           3 :     kc_ = 1.55;
     218           3 :     kshift_ = -pi / 3.0;
     219           6 :     log << plumed.cite("Hu J-S, Bax A, J. Am. Chem. Soc. 119, 6360 (1997)");
     220           3 :     log<<"\n";
     221           3 :     log.printf("  J-coupling type is HAHN, with A: %f, B: %f, C: %f, angle shift: %f\n", ka_, kb_, kc_, kshift_);
     222             :     break;
     223           1 :   case CCG:
     224           1 :     ka_ = 2.31;
     225           1 :     kb_ = -0.87;
     226           1 :     kc_ = 0.55;
     227           1 :     kshift_ = (2.0 * pi) / 3.0;
     228           2 :     log << plumed.cite("Perez C, Löhr F, Rüterjans H, Schmidt J, J. Am. Chem. Soc. 123, 7081 (2001)");
     229           1 :     log<<"\n";
     230           1 :     log.printf("  J-coupling type is CCG, with A: %f, B: %f, C: %f, angle shift: %f\n", ka_, kb_, kc_, kshift_);
     231             :     break;
     232           1 :   case NCG:
     233           1 :     ka_ = 1.29;
     234           1 :     kb_ = -0.49;
     235           1 :     kc_ = 0.37;
     236           1 :     kshift_ = 0.0;
     237           2 :     log << plumed.cite("Perez C, Löhr F, Rüterjans H, Schmidt J, J. Am. Chem. Soc. 123, 7081 (2001)");
     238           1 :     log<<"\n";
     239           1 :     log.printf("  J-coupling type is NCG, with A: %f, B: %f, C: %f, angle shift: %f\n", ka_, kb_, kc_, kshift_);
     240             :     break;
     241           0 :   case CUSTOM:
     242           0 :     log<<"\n";
     243           0 :     log.printf("  J-coupling type is custom, with A: %f, B: %f, C: %f, angle shift: %f\n", ka_, kb_, kc_, kshift_);
     244             :     break;
     245             :   }
     246             : 
     247          34 :   for (unsigned i = 0; i < ncoupl_; ++i) {
     248          28 :     log.printf("  The %uth J-Coupling is calculated from atoms : %d %d %d %d.",
     249          28 :                i+1, atoms[6*i].serial(), atoms[6*i+1].serial(), atoms[6*i+3].serial(), atoms[6*i+5].serial());
     250          28 :     if (addcoupling) {
     251           7 :       log.printf(" Experimental J-Coupling is %f.", coupl[i]);
     252             :     }
     253          28 :     log.printf("\n");
     254             :   }
     255             : 
     256           6 :   if (pbc) {
     257           0 :     log.printf("  using periodic boundary conditions\n");
     258             :   } else {
     259           6 :     log.printf("  without periodic boundary conditions\n");
     260             :   }
     261             : 
     262           6 :   if(!getDoScore()) {
     263          26 :     for (unsigned i = 0; i < ncoupl_; i++) {
     264          21 :       std::string num; Tools::convert(i, num);
     265          21 :       addComponentWithDerivatives("j-" + num);
     266          42 :       componentIsNotPeriodic("j-" + num);
     267             :     }
     268             :   } else {
     269           8 :     for (unsigned i = 0; i < ncoupl_; i++) {
     270           7 :       std::string num; Tools::convert(i, num);
     271           7 :       addComponent("j-" + num);
     272          14 :       componentIsNotPeriodic("j-" + num);
     273             :     }
     274             :   }
     275             : 
     276           6 :   if (addcoupling||getDoScore()) {
     277           8 :     for (unsigned i = 0; i < ncoupl_; i++) {
     278           7 :       std::string num; Tools::convert(i, num);
     279           7 :       addComponent("exp-" + num);
     280           7 :       componentIsNotPeriodic("exp-" + num);
     281           7 :       Value* comp = getPntrToComponent("exp-" + num);
     282           7 :       comp->set(coupl[i]);
     283             :     }
     284             :   }
     285             : 
     286           6 :   requestAtoms(atoms, false);
     287           6 :   if(getDoScore()) {
     288           1 :     setParameters(coupl);
     289           1 :     Initialise(ncoupl_);
     290             :   }
     291           6 :   setDerivatives();
     292           6 :   checkRead();
     293           6 : }
     294             : 
     295          16 : void JCoupling::calculate()
     296             : {
     297          16 :   if (pbc) makeWhole();
     298          16 :   std::vector<Vector> deriv(ncoupl_*6);
     299          16 :   std::vector<double> j(ncoupl_,0.);
     300             : 
     301          16 :   #pragma omp parallel num_threads(OpenMP::getNumThreads())
     302             :   {
     303             :     #pragma omp for
     304             :     // Loop through atoms, with steps of 6 atoms (one iteration per datapoint)
     305             :     for (unsigned r=0; r<ncoupl_; r++) {
     306             :       // Index is the datapoint index
     307             :       unsigned a0 = 6*r;
     308             : 
     309             :       // 6 atoms -> 3 vectors
     310             :       Vector d0 = delta(getPosition(a0+1), getPosition(a0));
     311             :       Vector d1 = delta(getPosition(a0+3), getPosition(a0+2));
     312             :       Vector d2 = delta(getPosition(a0+5), getPosition(a0+4));
     313             : 
     314             :       // Calculate dihedral with 3 vectors, get the derivatives
     315             :       Vector dd0, dd1, dd2;
     316             :       PLMD::Torsion t;
     317             :       double torsion = t.compute(d0, d1, d2, dd0, dd1, dd2);
     318             : 
     319             :       // Calculate the Karplus relation and its derivative
     320             :       double theta = torsion + kshift_;
     321             :       double cos_theta = std::cos(theta);
     322             :       double sin_theta = std::sin(theta);
     323             :       j[r] = ka_*cos_theta*cos_theta + kb_*cos_theta + kc_;
     324             :       double derj = -2.*ka_*sin_theta*cos_theta - kb_*sin_theta;
     325             : 
     326             :       dd0 *= derj;
     327             :       dd1 *= derj;
     328             :       dd2 *= derj;
     329             : 
     330             :       if(getDoScore()) setCalcData(r, j[r]);
     331             :       deriv[a0] =  dd0;
     332             :       deriv[a0+1] = -dd0;
     333             :       deriv[a0+2] =  dd1;
     334             :       deriv[a0+3] = -dd1;
     335             :       deriv[a0+4] =  dd2;
     336             :       deriv[a0+5] = -dd2;
     337             :     }
     338             :   }
     339             : 
     340          16 :   if(getDoScore()) {
     341             :     /* Metainference */
     342           6 :     double score = getScore();
     343             :     setScore(score);
     344             : 
     345             :     /* calculate final derivatives */
     346           6 :     Tensor virial;
     347           6 :     Value* val=getPntrToComponent("score");
     348          48 :     for (unsigned r=0; r<ncoupl_; r++) {
     349          42 :       const unsigned a0 = 6*r;
     350          42 :       setAtomsDerivatives(val, a0, deriv[a0]*getMetaDer(r));
     351          42 :       setAtomsDerivatives(val, a0+1, deriv[a0+1]*getMetaDer(r));
     352          42 :       setAtomsDerivatives(val, a0+2, deriv[a0+2]*getMetaDer(r));
     353          42 :       setAtomsDerivatives(val, a0+3, deriv[a0+3]*getMetaDer(r));
     354          42 :       setAtomsDerivatives(val, a0+4, deriv[a0+4]*getMetaDer(r));
     355          42 :       setAtomsDerivatives(val, a0+5, deriv[a0+5]*getMetaDer(r));
     356          42 :       virial-=Tensor(getPosition(a0), deriv[a0]*getMetaDer(r));
     357          42 :       virial-=Tensor(getPosition(a0+1), deriv[a0+1]*getMetaDer(r));
     358          42 :       virial-=Tensor(getPosition(a0+2), deriv[a0+2]*getMetaDer(r));
     359          42 :       virial-=Tensor(getPosition(a0+3), deriv[a0+3]*getMetaDer(r));
     360          42 :       virial-=Tensor(getPosition(a0+4), deriv[a0+4]*getMetaDer(r));
     361          42 :       virial-=Tensor(getPosition(a0+5), deriv[a0+5]*getMetaDer(r));
     362             :     }
     363           6 :     setBoxDerivatives(val, virial);
     364             :   } else {
     365          66 :     for (unsigned r=0; r<ncoupl_; r++) {
     366          56 :       const unsigned a0 = 6*r;
     367          56 :       std::string num; Tools::convert(r,num);
     368          56 :       Value* val=getPntrToComponent("j-"+num);
     369          56 :       val->set(j[r]);
     370          56 :       setAtomsDerivatives(val, a0, deriv[a0]);
     371          56 :       setAtomsDerivatives(val, a0+1, deriv[a0+1]);
     372          56 :       setAtomsDerivatives(val, a0+2, deriv[a0+2]);
     373          56 :       setAtomsDerivatives(val, a0+3, deriv[a0+3]);
     374          56 :       setAtomsDerivatives(val, a0+4, deriv[a0+4]);
     375          56 :       setAtomsDerivatives(val, a0+5, deriv[a0+5]);
     376          56 :       Tensor virial;
     377          56 :       virial-=Tensor(getPosition(a0), deriv[a0]);
     378          56 :       virial-=Tensor(getPosition(a0+1), deriv[a0+1]);
     379          56 :       virial-=Tensor(getPosition(a0+2), deriv[a0+2]);
     380          56 :       virial-=Tensor(getPosition(a0+3), deriv[a0+3]);
     381          56 :       virial-=Tensor(getPosition(a0+4), deriv[a0+4]);
     382          56 :       virial-=Tensor(getPosition(a0+5), deriv[a0+5]);
     383          56 :       setBoxDerivatives(val, virial);
     384             :     }
     385             :   }
     386          16 : }
     387             : 
     388          16 : void JCoupling::update() {
     389             :   // write status file
     390          16 :   if(getWstride()>0&& (getStep()%getWstride()==0 || getCPT()) ) writeStatus();
     391          16 : }
     392             : 
     393             : }
     394             : }

Generated by: LCOV version 1.15