LCOV - code coverage report
Current view: top level - isdb - Jcoupling.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 168 175 96.0 %
Date: 2025-03-25 09:33:27 Functions: 4 5 80.0 %

          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 3J 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             : private:
      90             :   bool pbc;
      91             :   enum { HAN, HAHN, CCG, NCG, CUSTOM };
      92             :   unsigned ncoupl_;
      93             :   double ka_;
      94             :   double kb_;
      95             :   double kc_;
      96             :   double kshift_;
      97             : 
      98             : public:
      99             :   static void registerKeywords(Keywords& keys);
     100             :   explicit JCoupling(const ActionOptions&);
     101             :   void calculate() override;
     102             :   void update() override;
     103             : };
     104             : 
     105             : PLUMED_REGISTER_ACTION(JCoupling, "JCOUPLING")
     106             : 
     107          26 : void JCoupling::registerKeywords(Keywords& keys) {
     108          26 :   MetainferenceBase::registerKeywords(keys);
     109          26 :   keys.addFlag("NOPBC",false,"ignore the periodic boundary conditions when calculating distances");
     110          26 :   keys.add("numbered", "ATOMS", "the 4 atoms involved in each of the bonds for which you wish to calculate the J-coupling. "
     111             :            "Keywords like ATOMS1, ATOMS2, ATOMS3,... should be listed and one J-coupling will be "
     112             :            "calculated for each ATOMS keyword you specify.");
     113          52 :   keys.reset_style("ATOMS", "atoms");
     114          26 :   keys.add("compulsory", "TYPE", "Type of J-coupling to compute (HAN,HAHN,CCG,NCG,CUSTOM)");
     115          26 :   keys.add("optional", "A", "Karplus parameter A");
     116          26 :   keys.add("optional", "B", "Karplus parameter B");
     117          26 :   keys.add("optional", "C", "Karplus parameter C");
     118          26 :   keys.add("optional", "SHIFT", "Angle shift in radians");
     119          26 :   keys.add("numbered", "COUPLING", "Add an experimental value for each coupling");
     120          52 :   keys.addOutputComponent("j", "default","scalar", "the calculated J-coupling");
     121          52 :   keys.addOutputComponent("exp", "COUPLING","scalar", "the experimental J-coupling");
     122          26 : }
     123             : 
     124          24 : JCoupling::JCoupling(const ActionOptions&ao):
     125             :   PLUMED_METAINF_INIT(ao),
     126          24 :   pbc(true) {
     127          24 :   bool nopbc = !pbc;
     128          24 :   parseFlag("NOPBC", nopbc);
     129          24 :   pbc =! nopbc;
     130             : 
     131             :   // Read in the atoms
     132             :   std::vector<AtomNumber> t, atoms;
     133          24 :   for (int i = 1; ; ++i) {
     134         356 :     parseAtomList("ATOMS", i, t );
     135         178 :     if (t.empty()) {
     136             :       break;
     137             :     }
     138             : 
     139         154 :     if (t.size() != 4) {
     140             :       std::string ss;
     141           0 :       Tools::convert(i, ss);
     142           0 :       error("ATOMS" + ss + " keyword has the wrong number of atoms");
     143             :     }
     144             : 
     145             :     // This makes the distance calculation easier later on (see Torsion implementation)
     146         154 :     atoms.push_back(t[0]);
     147         154 :     atoms.push_back(t[1]);
     148         154 :     atoms.push_back(t[1]);
     149         154 :     atoms.push_back(t[2]);
     150         154 :     atoms.push_back(t[2]);
     151         154 :     atoms.push_back(t[3]);
     152         154 :     t.resize(0);
     153         154 :   }
     154             : 
     155             :   // We now have 6 atoms per datapoint
     156          24 :   ncoupl_ = atoms.size()/6;
     157             : 
     158             :   // Parse J-Coupling type, this will determine the Karplus parameters
     159             :   unsigned jtype_ = CUSTOM;
     160             :   std::string string_type;
     161          48 :   parse("TYPE", string_type);
     162          24 :   if(string_type == "HAN") {
     163             :     jtype_ = HAN;
     164          23 :   } else if(string_type == "HAHN") {
     165             :     jtype_ = HAHN;
     166           3 :   } else if(string_type == "CCG") {
     167             :     jtype_ = CCG;
     168           2 :   } else if(string_type == "NCG") {
     169             :     jtype_ = NCG;
     170           1 :   } else if(string_type == "CUSTOM") {
     171             :     jtype_ = CUSTOM;
     172             :   } else {
     173           0 :     error("Unknown J-coupling type!");
     174             :   }
     175             : 
     176             :   // Optionally add an experimental value (like with RDCs)
     177             :   std::vector<double> coupl;
     178          24 :   coupl.resize( ncoupl_ );
     179             :   unsigned ntarget=0;
     180          94 :   for(unsigned i=0; i<ncoupl_; ++i) {
     181         168 :     if( !parseNumbered( "COUPLING", i+1, coupl[i] ) ) {
     182             :       break;
     183             :     }
     184          70 :     ntarget++;
     185             :   }
     186             :   bool addcoupling=false;
     187          24 :   if(ntarget!=ncoupl_ && ntarget!=0) {
     188           0 :     error("found wrong number of COUPLING values");
     189             :   }
     190          24 :   if(ntarget==ncoupl_) {
     191             :     addcoupling=true;
     192             :   }
     193          24 :   if(getDoScore()&&!addcoupling) {
     194           0 :     error("with DOSCORE you need to set the COUPLING values");
     195             :   }
     196             : 
     197             :   // For custom types we allow use of custom Karplus parameters
     198          24 :   if (jtype_ == CUSTOM) {
     199           1 :     parse("A", ka_);
     200           1 :     parse("B", kb_);
     201           1 :     parse("C", kc_);
     202           2 :     parse("SHIFT", kshift_);
     203             :   }
     204             : 
     205          24 :   log << "  Bibliography ";
     206          48 :   log<<plumed.cite("Bonomi, Camilloni, Bioinformatics, 33, 3999 (2017)");
     207             : 
     208             :   // Set Karplus parameters
     209          24 :   switch (jtype_) {
     210           1 :   case HAN:
     211           1 :     ka_ = -0.88;
     212           1 :     kb_ = -0.61;
     213           1 :     kc_ = -0.27;
     214           1 :     kshift_ = pi / 3.0;
     215           2 :     log << plumed.cite("Wang A C, Bax A, J. Am. Chem. Soc. 117, 1810 (1995)");
     216           1 :     log<<"\n";
     217           1 :     log.printf("  J-coupling type is HAN, with A: %f, B: %f, C: %f, angle shift: %f\n", ka_, kb_, kc_, kshift_);
     218             :     break;
     219          20 :   case HAHN:
     220          20 :     ka_ = 7.09;
     221          20 :     kb_ = -1.42;
     222          20 :     kc_ = 1.55;
     223          20 :     kshift_ = -pi / 3.0;
     224          40 :     log << plumed.cite("Hu J-S, Bax A, J. Am. Chem. Soc. 119, 6360 (1997)");
     225          20 :     log<<"\n";
     226          20 :     log.printf("  J-coupling type is HAHN, with A: %f, B: %f, C: %f, angle shift: %f\n", ka_, kb_, kc_, kshift_);
     227             :     break;
     228           1 :   case CCG:
     229           1 :     ka_ = 2.31;
     230           1 :     kb_ = -0.87;
     231           1 :     kc_ = 0.55;
     232           1 :     kshift_ = (2.0 * pi) / 3.0;
     233           2 :     log << plumed.cite("Perez C, Löhr F, Rüterjans H, Schmidt J, J. Am. Chem. Soc. 123, 7081 (2001)");
     234           1 :     log<<"\n";
     235           1 :     log.printf("  J-coupling type is CCG, with A: %f, B: %f, C: %f, angle shift: %f\n", ka_, kb_, kc_, kshift_);
     236             :     break;
     237           1 :   case NCG:
     238           1 :     ka_ = 1.29;
     239           1 :     kb_ = -0.49;
     240           1 :     kc_ = 0.37;
     241           1 :     kshift_ = 0.0;
     242           2 :     log << plumed.cite("Perez C, Löhr F, Rüterjans H, Schmidt J, J. Am. Chem. Soc. 123, 7081 (2001)");
     243           1 :     log<<"\n";
     244           1 :     log.printf("  J-coupling type is NCG, with A: %f, B: %f, C: %f, angle shift: %f\n", ka_, kb_, kc_, kshift_);
     245             :     break;
     246           1 :   case CUSTOM:
     247           1 :     log<<"\n";
     248           1 :     log.printf("  J-coupling type is custom, with A: %f, B: %f, C: %f, angle shift: %f\n", ka_, kb_, kc_, kshift_);
     249             :     break;
     250             :   }
     251             : 
     252         178 :   for (unsigned i = 0; i < ncoupl_; ++i) {
     253         154 :     log.printf("  The %uth J-Coupling is calculated from atoms : %d %d %d %d.",
     254         154 :                i+1, atoms[6*i].serial(), atoms[6*i+1].serial(), atoms[6*i+3].serial(), atoms[6*i+5].serial());
     255         154 :     if (addcoupling) {
     256          70 :       log.printf(" Experimental J-Coupling is %f.", coupl[i]);
     257             :     }
     258         154 :     log.printf("\n");
     259             :   }
     260             : 
     261          24 :   if (pbc) {
     262           0 :     log.printf("  using periodic boundary conditions\n");
     263             :   } else {
     264          24 :     log.printf("  without periodic boundary conditions\n");
     265             :   }
     266             : 
     267          24 :   if(!getDoScore()) {
     268          98 :     for (unsigned i = 0; i < ncoupl_; i++) {
     269             :       std::string num;
     270          84 :       Tools::convert(i, num);
     271         168 :       addComponentWithDerivatives("j-" + num);
     272         168 :       componentIsNotPeriodic("j-" + num);
     273             :     }
     274             :   } else {
     275          80 :     for (unsigned i = 0; i < ncoupl_; i++) {
     276             :       std::string num;
     277          70 :       Tools::convert(i, num);
     278         140 :       addComponent("j-" + num);
     279         140 :       componentIsNotPeriodic("j-" + num);
     280             :     }
     281             :   }
     282             : 
     283          24 :   if (addcoupling||getDoScore()) {
     284          80 :     for (unsigned i = 0; i < ncoupl_; i++) {
     285             :       std::string num;
     286          70 :       Tools::convert(i, num);
     287         140 :       addComponent("exp-" + num);
     288          70 :       componentIsNotPeriodic("exp-" + num);
     289          70 :       Value* comp = getPntrToComponent("exp-" + num);
     290          70 :       comp->set(coupl[i]);
     291             :     }
     292             :   }
     293             : 
     294          24 :   requestAtoms(atoms, false);
     295          24 :   if(getDoScore()) {
     296          10 :     setParameters(coupl);
     297          10 :     Initialise(ncoupl_);
     298             :   }
     299          24 :   setDerivatives();
     300          24 :   checkRead();
     301          24 : }
     302             : 
     303         124 : void JCoupling::calculate() {
     304         124 :   if (pbc) {
     305           0 :     makeWhole();
     306             :   }
     307         124 :   std::vector<Vector> deriv(ncoupl_*6);
     308         124 :   std::vector<double> j(ncoupl_,0.);
     309             : 
     310         124 :   #pragma omp parallel num_threads(OpenMP::getNumThreads())
     311             :   {
     312             :     #pragma omp for
     313             :     // Loop through atoms, with steps of 6 atoms (one iteration per datapoint)
     314             :     for (unsigned r=0; r<ncoupl_; r++) {
     315             :       // Index is the datapoint index
     316             :       unsigned a0 = 6*r;
     317             : 
     318             :       // 6 atoms -> 3 vectors
     319             :       Vector d0 = delta(getPosition(a0+1), getPosition(a0));
     320             :       Vector d1 = delta(getPosition(a0+3), getPosition(a0+2));
     321             :       Vector d2 = delta(getPosition(a0+5), getPosition(a0+4));
     322             : 
     323             :       // Calculate dihedral with 3 vectors, get the derivatives
     324             :       Vector dd0, dd1, dd2;
     325             :       PLMD::Torsion t;
     326             :       double torsion = t.compute(d0, d1, d2, dd0, dd1, dd2);
     327             : 
     328             :       // Calculate the Karplus relation and its derivative
     329             :       double theta = torsion + kshift_;
     330             :       double cos_theta = std::cos(theta);
     331             :       double sin_theta = std::sin(theta);
     332             :       j[r] = ka_*cos_theta*cos_theta + kb_*cos_theta + kc_;
     333             :       double derj = -2.*ka_*sin_theta*cos_theta - kb_*sin_theta;
     334             : 
     335             :       dd0 *= derj;
     336             :       dd1 *= derj;
     337             :       dd2 *= derj;
     338             : 
     339             :       if(getDoScore()) {
     340             :         setCalcData(r, j[r]);
     341             :       }
     342             :       deriv[a0] =  dd0;
     343             :       deriv[a0+1] = -dd0;
     344             :       deriv[a0+2] =  dd1;
     345             :       deriv[a0+3] = -dd1;
     346             :       deriv[a0+4] =  dd2;
     347             :       deriv[a0+5] = -dd2;
     348             :     }
     349             :   }
     350             : 
     351         124 :   if(getDoScore()) {
     352             :     /* Metainference */
     353          60 :     double score = getScore();
     354          60 :     setScore(score);
     355             : 
     356             :     /* calculate final derivatives */
     357          60 :     Tensor virial;
     358          60 :     Value* val=getPntrToComponent("score");
     359         480 :     for (unsigned r=0; r<ncoupl_; r++) {
     360         420 :       const unsigned a0 = 6*r;
     361         420 :       setAtomsDerivatives(val, a0, deriv[a0]*getMetaDer(r));
     362         420 :       setAtomsDerivatives(val, a0+1, deriv[a0+1]*getMetaDer(r));
     363         420 :       setAtomsDerivatives(val, a0+2, deriv[a0+2]*getMetaDer(r));
     364         420 :       setAtomsDerivatives(val, a0+3, deriv[a0+3]*getMetaDer(r));
     365         420 :       setAtomsDerivatives(val, a0+4, deriv[a0+4]*getMetaDer(r));
     366         420 :       setAtomsDerivatives(val, a0+5, deriv[a0+5]*getMetaDer(r));
     367         420 :       virial-=Tensor(getPosition(a0), deriv[a0]*getMetaDer(r));
     368         420 :       virial-=Tensor(getPosition(a0+1), deriv[a0+1]*getMetaDer(r));
     369         420 :       virial-=Tensor(getPosition(a0+2), deriv[a0+2]*getMetaDer(r));
     370         420 :       virial-=Tensor(getPosition(a0+3), deriv[a0+3]*getMetaDer(r));
     371         420 :       virial-=Tensor(getPosition(a0+4), deriv[a0+4]*getMetaDer(r));
     372         420 :       virial-=Tensor(getPosition(a0+5), deriv[a0+5]*getMetaDer(r));
     373             :     }
     374          60 :     setBoxDerivatives(val, virial);
     375             :   } else {
     376         498 :     for (unsigned r=0; r<ncoupl_; r++) {
     377         434 :       const unsigned a0 = 6*r;
     378             :       std::string num;
     379         434 :       Tools::convert(r,num);
     380         434 :       Value* val=getPntrToComponent("j-"+num);
     381         434 :       val->set(j[r]);
     382         434 :       setAtomsDerivatives(val, a0, deriv[a0]);
     383         434 :       setAtomsDerivatives(val, a0+1, deriv[a0+1]);
     384         434 :       setAtomsDerivatives(val, a0+2, deriv[a0+2]);
     385         434 :       setAtomsDerivatives(val, a0+3, deriv[a0+3]);
     386         434 :       setAtomsDerivatives(val, a0+4, deriv[a0+4]);
     387         434 :       setAtomsDerivatives(val, a0+5, deriv[a0+5]);
     388         434 :       Tensor virial;
     389         434 :       virial-=Tensor(getPosition(a0), deriv[a0]);
     390         434 :       virial-=Tensor(getPosition(a0+1), deriv[a0+1]);
     391         434 :       virial-=Tensor(getPosition(a0+2), deriv[a0+2]);
     392         434 :       virial-=Tensor(getPosition(a0+3), deriv[a0+3]);
     393         434 :       virial-=Tensor(getPosition(a0+4), deriv[a0+4]);
     394         434 :       virial-=Tensor(getPosition(a0+5), deriv[a0+5]);
     395         434 :       setBoxDerivatives(val, virial);
     396             :     }
     397             :   }
     398         124 : }
     399             : 
     400         124 : void JCoupling::update() {
     401             :   // write status file
     402         124 :   if(getWstride()>0&& (getStep()%getWstride()==0 || getCPT()) ) {
     403          24 :     writeStatus();
     404             :   }
     405         124 : }
     406             : 
     407             : }
     408             : }

Generated by: LCOV version 1.16