LCOV - code coverage report
Current view: top level - isdb - PRE.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 149 161 92.5 %
Date: 2019-08-13 10:15:31 Functions: 12 13 92.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 "MetainferenceBase.h"
      23             : #include "core/ActionRegister.h"
      24             : #include "tools/NeighborList.h"
      25             : #include "tools/Pbc.h"
      26             : 
      27             : #include <string>
      28             : #include <cmath>
      29             : #include <memory>
      30             : 
      31             : using namespace std;
      32             : 
      33             : namespace PLMD {
      34             : namespace isdb {
      35             : 
      36             : //+PLUMEDOC ISDB_COLVAR PRE
      37             : /*
      38             : Calculates the Paramagnetic Resonance Enhancement intensity ratio between a spin label atom and a list of atoms .
      39             : 
      40             : The reference atom for the spin label is added with SPINLABEL, the affected atom(s)
      41             : are give as numbered GROUPA1, GROUPA2, ...
      42             : The additional parameters needed for the calculation are given as INEPT, the inept
      43             : time, TAUC the correlation time, OMEGA, the Larmor frequency and RTWO for the relaxation
      44             : time.
      45             : 
      46             : \ref METAINFERENCE can be activated using DOSCORE and the other relevant keywords.
      47             : 
      48             : \par Examples
      49             : 
      50             : In the following example five PRE intensities are calculated using the distance between the
      51             : oxygen of the spin label and the backbone hydrogen atoms. Omega is the NMR frequency, RTWO the
      52             : R2 for the hydrogen atoms, INEPT of 8 ms for the experiment and a TAUC of 1.21 ns
      53             : 
      54             : \plumedfile
      55             : PRE ...
      56             : LABEL=HN_pre
      57             : INEPT=8
      58             : TAUC=1.21
      59             : OMEGA=900
      60             : SPINLABEL=1818
      61             : GROUPA1=86  RTWO1=0.0120272827
      62             : GROUPA2=177 RTWO2=0.0263953158
      63             : GROUPA3=285 RTWO3=0.0058899829
      64             : GROUPA4=335 RTWO4=0.0102072646
      65             : GROUPA5=451 RTWO5=0.0086341843
      66             : ... PRE
      67             : 
      68             : PRINT ARG=HN_pre.* FILE=PRE.dat STRIDE=1
      69             : 
      70             : \endplumedfile
      71             : 
      72             : */
      73             : //+ENDPLUMEDOC
      74             : 
      75          12 : class PRE :
      76             :   public MetainferenceBase
      77             : {
      78             : private:
      79             :   bool             pbc;
      80             :   bool             doratio;
      81             :   double           constant;
      82             :   double           inept;
      83             :   vector<double>   rtwo;
      84             :   vector<unsigned> nga;
      85             :   std::unique_ptr<NeighborList> nl;
      86             :   unsigned         tot_size;
      87             : public:
      88             :   static void registerKeywords( Keywords& keys );
      89             :   explicit PRE(const ActionOptions&);
      90             :   void calculate() override;
      91             :   void update() override;
      92             : };
      93             : 
      94        7840 : PLUMED_REGISTER_ACTION(PRE,"PRE")
      95             : 
      96           5 : void PRE::registerKeywords( Keywords& keys ) {
      97           5 :   componentsAreNotOptional(keys);
      98           5 :   MetainferenceBase::registerKeywords(keys);
      99          15 :   keys.addFlag("NOPBC",false,"ignore the periodic boundary conditions when calculating distances");
     100          15 :   keys.addFlag("NORATIO",false,"Set to TRUE if you want to compute PRE without Intensity Ratio");
     101          20 :   keys.add("compulsory","INEPT","is the INEPT time (in ms).");
     102          20 :   keys.add("compulsory","TAUC","is the correlation time (in ns) for this electron-nuclear interaction.");
     103          20 :   keys.add("compulsory","OMEGA","is the Larmor frequency of the nuclear spin (in MHz).");
     104          20 :   keys.add("atoms","SPINLABEL","The atom to be used as the paramagnetic center.");
     105             :   keys.add("numbered","GROUPA","the atoms involved in each of the contacts you wish to calculate. "
     106             :            "Keywords like GROUPA1, GROUPA2, GROUPA3,... should be listed and one contact will be "
     107          20 :            "calculated for each ATOM keyword you specify.");
     108          15 :   keys.reset_style("GROUPA","atoms");
     109             :   keys.add("numbered","RTWO","The relaxation of the atom/atoms in the corresponding GROUPA of atoms. "
     110          20 :            "Keywords like RTWO1, RTWO2, RTWO3,... should be listed.");
     111          20 :   keys.add("numbered","PREINT","Add an experimental value for each PRE.");
     112          20 :   keys.addOutputComponent("pre","default","the # PRE");
     113          20 :   keys.addOutputComponent("exp","PREINT","the # PRE experimental intensity");
     114           5 : }
     115             : 
     116           4 : PRE::PRE(const ActionOptions&ao):
     117             :   PLUMED_METAINF_INIT(ao),
     118             :   pbc(true),
     119           8 :   doratio(true)
     120             : {
     121           4 :   bool nopbc=!pbc;
     122           8 :   parseFlag("NOPBC",nopbc);
     123           4 :   pbc=!nopbc;
     124             : 
     125           4 :   bool noratio=!doratio;
     126           8 :   parseFlag("NORATIO",noratio);
     127           4 :   doratio=!noratio;
     128             : 
     129             :   vector<AtomNumber> atom;
     130           8 :   parseAtomList("SPINLABEL",atom);
     131           4 :   if(atom.size()!=1) error("Number of specified atom should be 1");
     132             : 
     133             :   // Read in the atoms
     134             :   vector<AtomNumber> t, ga_lista, gb_lista;
     135          12 :   for(int i=1;; ++i ) {
     136          32 :     parseAtomList("GROUPA", i, t );
     137          16 :     if( t.empty() ) break;
     138          60 :     for(unsigned j=0; j<t.size(); j++) {ga_lista.push_back(t[j]); gb_lista.push_back(atom[0]);}
     139          24 :     nga.push_back(t.size());
     140          12 :     t.resize(0);
     141          12 :   }
     142             : 
     143             :   // Read in reference values
     144           4 :   rtwo.resize( nga.size() );
     145           4 :   if(doratio) {
     146             :     unsigned ntarget=0;
     147           8 :     for(unsigned i=0; i<nga.size(); ++i) {
     148           8 :       if( !parseNumbered( "RTWO", i+1, rtwo[i] ) ) break;
     149           0 :       ntarget++;
     150             :     }
     151           4 :     if( ntarget==0 ) {
     152           8 :       parse("RTWO",rtwo[0]);
     153          24 :       for(unsigned i=1; i<nga.size(); ++i) rtwo[i]=rtwo[0];
     154           0 :     } else if( ntarget!=nga.size() ) error("found wrong number of RTWO values");
     155             :   }
     156             : 
     157           4 :   double tauc=0.;
     158           8 :   parse("TAUC",tauc);
     159           4 :   if(tauc==0.) error("TAUC must be set");
     160             : 
     161           4 :   double omega=0.;
     162           8 :   parse("OMEGA",omega);
     163           4 :   if(omega==0.) error("OMEGA must be set");
     164             : 
     165           4 :   inept=0.;
     166           4 :   if(doratio) {
     167           8 :     parse("INEPT",inept);
     168           4 :     if(inept==0.) error("INEPT must be set");
     169           4 :     inept *= 0.001; // ms2s
     170             :   }
     171             : 
     172             :   const double ns2s   = 0.000000001;
     173             :   const double MHz2Hz = 1000000.;
     174             :   const double Kappa  = 12300000000.00; // this is 1/15*S*(S+1)*gamma^2*g^2*beta^2
     175             :   // where gamma is the nuclear gyromagnetic ratio,
     176             :   // g is the electronic g factor, and beta is the Bohr magneton
     177             :   // in nm^6/s^2
     178           4 :   constant = (4.*tauc*ns2s+(3.*tauc*ns2s)/(1+omega*omega*MHz2Hz*MHz2Hz*tauc*tauc*ns2s*ns2s))*Kappa;
     179             : 
     180             :   // Optionally add an experimental value (like with RDCs)
     181             :   vector<double> exppre;
     182           4 :   exppre.resize( nga.size() );
     183             :   unsigned ntarget=0;
     184          20 :   for(unsigned i=0; i<nga.size(); ++i) {
     185          16 :     if( !parseNumbered( "PREINT", i+1, exppre[i] ) ) break;
     186           6 :     ntarget++;
     187             :   }
     188             :   bool addexp=false;
     189           8 :   if(ntarget!=nga.size() && ntarget!=0) error("found wrong number of PREINT values");
     190           4 :   if(ntarget==nga.size()) addexp=true;
     191           4 :   if(getDoScore()&&!addexp) error("with DOSCORE you need to set the PREINT values");
     192             : 
     193             :   // Create neighbour lists
     194           8 :   nl.reset( new NeighborList(gb_lista,ga_lista,true,pbc,getPbc()) );
     195             : 
     196             :   // Ouput details of all contacts
     197             :   unsigned index=0;
     198          32 :   for(unsigned i=0; i<nga.size(); ++i) {
     199          12 :     log.printf("  The %uth PRE is calculated using %u equivalent atoms:\n", i, nga[i]);
     200          24 :     log.printf("    %d", ga_lista[index].serial());
     201          12 :     index++;
     202          32 :     for(unsigned j=1; j<nga[i]; j++) {
     203           8 :       log.printf(" %d", ga_lista[index].serial());
     204           4 :       index++;
     205             :     }
     206          12 :     log.printf("\n");
     207             :   }
     208           4 :   tot_size = index;
     209             : 
     210           4 :   if(pbc)      log.printf("  using periodic boundary conditions\n");
     211           0 :   else         log.printf("  without periodic boundary conditions\n");
     212             : 
     213          12 :   log << " Bibliography" << plumed.cite("Bonomi, Camilloni, Bioinformatics, 33, 3999 (2017)") << "\n";
     214             : 
     215           4 :   if(!getDoScore()) {
     216          14 :     for(unsigned i=0; i<nga.size(); i++) {
     217           6 :       string num; Tools::convert(i,num);
     218          12 :       addComponentWithDerivatives("pre-"+num);
     219          12 :       componentIsNotPeriodic("pre-"+num);
     220             :     }
     221           2 :     if(addexp) {
     222           0 :       for(unsigned i=0; i<nga.size(); i++) {
     223           0 :         string num; Tools::convert(i,num);
     224           0 :         addComponent("exp-"+num);
     225           0 :         componentIsNotPeriodic("exp-"+num);
     226           0 :         Value* comp=getPntrToComponent("exp-"+num);
     227           0 :         comp->set(exppre[i]);
     228             :       }
     229             :     }
     230             :   } else {
     231          14 :     for(unsigned i=0; i<nga.size(); i++) {
     232           6 :       string num; Tools::convert(i,num);
     233          12 :       addComponent("pre-"+num);
     234          12 :       componentIsNotPeriodic("pre-"+num);
     235             :     }
     236          14 :     for(unsigned i=0; i<nga.size(); i++) {
     237           6 :       string num; Tools::convert(i,num);
     238          12 :       addComponent("exp-"+num);
     239          12 :       componentIsNotPeriodic("exp-"+num);
     240          12 :       Value* comp=getPntrToComponent("exp-"+num);
     241           6 :       comp->set(exppre[i]);
     242             :     }
     243             :   }
     244             : 
     245           4 :   requestAtoms(nl->getFullAtomList(), false);
     246           4 :   if(getDoScore()) {
     247           2 :     setParameters(exppre);
     248           2 :     Initialise(nga.size());
     249             :   }
     250           4 :   setDerivatives();
     251           4 :   checkRead();
     252           4 : }
     253             : 
     254         350 : void PRE::calculate()
     255             : {
     256         350 :   vector<Vector> deriv(tot_size, Vector{0,0,0});
     257         700 :   vector<double> fact(nga.size(), 0.);
     258             : 
     259             :   // cycle over the number of PRE
     260        1049 :   #pragma omp parallel for num_threads(OpenMP::getNumThreads())
     261         699 :   for(unsigned i=0; i<nga.size(); i++) {
     262        1049 :     Tensor dervir;
     263             :     double pre=0;
     264             :     unsigned index=0;
     265        3149 :     for(unsigned k=0; k<i; k++) index+=nga[k];
     266        2098 :     const double c_aver=constant/static_cast<double>(nga[i]);
     267        1049 :     string num; Tools::convert(i,num);
     268        2092 :     Value* val=getPntrToComponent("pre-"+num);
     269             :     // cycle over equivalent atoms
     270        4896 :     for(unsigned j=0; j<nga[i]; j++) {
     271             :       // the first atom is always the same (the paramagnetic group)
     272        2800 :       const unsigned i0=nl->getClosePair(index+j).first;
     273        1400 :       const unsigned i1=nl->getClosePair(index+j).second;
     274             : 
     275        1400 :       Vector distance;
     276        5596 :       if(pbc) distance=pbcDistance(getPosition(i0),getPosition(i1));
     277           0 :       else    distance=delta(getPosition(i0),getPosition(i1));
     278             : 
     279        1400 :       const double r2=distance.modulo2();
     280        1400 :       const double r6=r2*r2*r2;
     281        1400 :       const double r8=r6*r2;
     282        1400 :       const double tmpir6=c_aver/r6;
     283        1400 :       const double tmpir8=-6.*c_aver/r8;
     284             : 
     285        1400 :       pre += tmpir6;
     286        2800 :       deriv[index+j] = -tmpir8*distance;
     287        2800 :       if(!getDoScore()) dervir   +=  Tensor(distance,deriv[index+j]);
     288             :     }
     289             :     double tmpratio;
     290        1048 :     if(!doratio) {
     291             :       tmpratio = pre ; //prova a caso per vedere se lui da problemi
     292           0 :       fact[i] = 1.; //prova a caso per vedere se lui da problemi
     293             :     } else {
     294        2096 :       tmpratio = rtwo[i]*exp(-pre*inept) / (rtwo[i]+pre);
     295        2096 :       fact[i] = -tmpratio*(inept+1./(rtwo[i]+pre));
     296             :     }
     297             :     const double ratio = tmpratio;
     298             :     val->set(ratio) ;
     299        1048 :     if(!getDoScore()) {
     300        1048 :       setBoxDerivatives(val, fact[i]*dervir);
     301        2450 :       for(unsigned j=0; j<nga[i]; j++) {
     302        1400 :         const unsigned i0=nl->getClosePair(index+j).first;
     303         700 :         const unsigned i1=nl->getClosePair(index+j).second;
     304        2100 :         setAtomsDerivatives(val, i0,  fact[i]*deriv[index+j]);
     305        2100 :         setAtomsDerivatives(val, i1, -fact[i]*deriv[index+j]);
     306             :       }
     307             :     } else setCalcData(i, ratio);
     308             :   }
     309             : 
     310         350 :   if(getDoScore()) {
     311             :     /* Metainference */
     312         175 :     Tensor dervir;
     313         175 :     double score = getScore();
     314             :     setScore(score);
     315             : 
     316             :     /* calculate final derivatives */
     317         350 :     Value* val=getPntrToComponent("score");
     318        1400 :     for(unsigned i=0; i<nga.size(); i++) {
     319             :       unsigned index=0;
     320        1050 :       for(unsigned k=0; k<i; k++) index+=nga[k];
     321             :       // cycle over equivalent atoms
     322        1925 :       for(unsigned j=0; j<nga[i]; j++) {
     323        1400 :         const unsigned i0=nl->getClosePair(index+j).first;
     324         700 :         const unsigned i1=nl->getClosePair(index+j).second;
     325             : 
     326         700 :         Vector distance;
     327        2100 :         if(pbc) distance=pbcDistance(getPosition(i0),getPosition(i1));
     328           0 :         else    distance=delta(getPosition(i0),getPosition(i1));
     329             : 
     330        1400 :         dervir += Tensor(distance,fact[i]*deriv[index+j]*getMetaDer(i));
     331         700 :         setAtomsDerivatives(val, i0,  fact[i]*deriv[index+j]*getMetaDer(i));
     332         700 :         setAtomsDerivatives(val, i1, -fact[i]*deriv[index+j]*getMetaDer(i));
     333             :       }
     334             :     }
     335         175 :     setBoxDerivatives(val, dervir);
     336             :   }
     337         350 : }
     338             : 
     339          20 : void PRE::update() {
     340             :   // write status file
     341          40 :   if(getWstride()>0&& (getStep()%getWstride()==0 || getCPT()) ) writeStatus();
     342          20 : }
     343             : 
     344             : }
     345        5874 : }

Generated by: LCOV version 1.14