LCOV - code coverage report
Current view: top level - colvar - PRE.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 117 134 87.3 %
Date: 2018-12-19 07:49:13 Functions: 11 13 84.6 %

          Line data    Source code
       1             : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
       2             :    Copyright (c) 2015-2018 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 "ActionRegister.h"
      24             : #include "tools/NeighborList.h"
      25             : #include "tools/OpenMP.h"
      26             : 
      27             : #include <string>
      28             : #include <cmath>
      29             : 
      30             : using namespace std;
      31             : 
      32             : namespace PLMD {
      33             : namespace colvar {
      34             : 
      35             : //+PLUMEDOC COLVAR PRE
      36             : /*
      37             : Calculates the Paramegnetic Resonance Enhancement  intensity ratio between two atoms.
      38             : The reference atom for the spin label is added with SPINLABEL, the affected atom(s)
      39             : are give as numbered GROUPA1, GROUPA2, ...
      40             : The additional parameters needed for the calculation are given as INEPT, the inept
      41             : time, TAUC the correlation time, OMEGA, the larmor frequency and RTWO for the relaxation
      42             : time.
      43             : 
      44             : \par Examples
      45             : 
      46             : In the following example five PRE intensities are calculated using the distance between the
      47             : oxigen of the spin label and the backbone hydrogens. Omega is the NMR frequency, RTWO the
      48             : R2 for the hydrogens, INEPT of 8 ms for the experiment and a TAUC of 1.21 ns
      49             : 
      50             : \verbatim
      51             : PRE ...
      52             : LABEL=HN_pre
      53             : INEPT=8
      54             : TAUC=1.21
      55             : OMEGA=900
      56             : SPINLABEL=1818
      57             : GROUPA1=86  RTWO1=0.0120272827
      58             : GROUPA2=177 RTWO2=0.0263953158
      59             : GROUPA3=285 RTWO3=0.0058899829
      60             : GROUPA4=335 RTWO4=0.0102072646
      61             : GROUPA5=451 RTWO5=0.0086341843
      62             : ... PRE
      63             : 
      64             : PRINT ARG=HN_pre.* FILE=PRE.dat STRIDE=1
      65             : 
      66             : \endverbatim
      67             : 
      68             : */
      69             : //+ENDPLUMEDOC
      70             : 
      71             : class PRE : public Colvar {
      72             : private:
      73             :   bool             pbc;
      74             :   double           constant, inept;
      75             :   vector<double>   rtwo;
      76             :   vector<unsigned> nga;
      77             :   NeighborList     *nl;
      78             : public:
      79             :   static void registerKeywords( Keywords& keys );
      80             :   explicit PRE(const ActionOptions&);
      81             :   ~PRE();
      82             :   virtual void calculate();
      83             : };
      84             : 
      85        2525 : PLUMED_REGISTER_ACTION(PRE,"PRE")
      86             : 
      87           3 : void PRE::registerKeywords( Keywords& keys ) {
      88           3 :   Colvar::registerKeywords( keys );
      89           3 :   componentsAreNotOptional(keys);
      90           3 :   useCustomisableComponents(keys);
      91           3 :   keys.add("compulsory","INEPT","is the INEPT time (in ms).");
      92           3 :   keys.add("compulsory","TAUC","is the correlation time (in ns) for this electron-nuclear interaction.");
      93           3 :   keys.add("compulsory","OMEGA","is the Larmor frequency of the nuclear spin (in MHz).");
      94           3 :   keys.add("atoms","SPINLABEL","The atom to be used as the paramagnetic center.");
      95             :   keys.add("numbered","GROUPA","the atoms involved in each of the contacts you wish to calculate. "
      96             :            "Keywords like GROUPA1, GROUPA2, GROUPA3,... should be listed and one contact will be "
      97           3 :            "calculated for each ATOM keyword you specify.");
      98           3 :   keys.reset_style("GROUPA","atoms");
      99             :   keys.add("numbered","RTWO","The relaxation of the atom/atoms in the corresponding GROUPA of atoms. "
     100           3 :            "Keywords like RTWO1, RTWO2, RTWO3,... should be listed.");
     101           3 :   keys.addFlag("ADDEXP",false,"Set to TRUE if you want to have fixed components with the experimetnal values.");
     102           3 :   keys.add("numbered","PREINT","Add an experimental value for each PRE.");
     103           3 :   keys.addOutputComponent("pre","default","the # PRE");
     104           3 :   keys.addOutputComponent("exp","ADDEXP","the # PRE experimental intensity");
     105           3 : }
     106             : 
     107           2 : PRE::PRE(const ActionOptions&ao):
     108             :   PLUMED_COLVAR_INIT(ao),
     109           2 :   pbc(true)
     110             : {
     111           2 :   bool nopbc=!pbc;
     112           2 :   parseFlag("NOPBC",nopbc);
     113           2 :   pbc=!nopbc;
     114             : 
     115           2 :   vector<AtomNumber> atom;
     116           2 :   parseAtomList("SPINLABEL",atom);
     117           2 :   if(atom.size()!=1) error("Number of specified atom should be 1");
     118             : 
     119             :   // Read in the atoms
     120           4 :   vector<AtomNumber> t, ga_lista, gb_lista;
     121           8 :   for(int i=1;; ++i ) {
     122           8 :     parseAtomList("GROUPA", i, t );
     123           8 :     if( t.empty() ) break;
     124           6 :     for(unsigned j=0; j<t.size(); j++) {ga_lista.push_back(t[j]); gb_lista.push_back(atom[0]);}
     125           6 :     nga.push_back(t.size());
     126           6 :     t.resize(0);
     127           6 :   }
     128             : 
     129             :   // Read in reference values
     130           2 :   rtwo.resize( nga.size() );
     131           2 :   unsigned ntarget=0;
     132           2 :   for(unsigned i=0; i<nga.size(); ++i) {
     133           2 :     if( !parseNumbered( "RTWO", i+1, rtwo[i] ) ) break;
     134           0 :     ntarget++;
     135             :   }
     136           2 :   if( ntarget==0 ) {
     137           2 :     parse("RTWO",rtwo[0]);
     138           2 :     for(unsigned i=1; i<nga.size(); ++i) rtwo[i]=rtwo[0];
     139           0 :   } else if( ntarget!=nga.size() ) error("found wrong number of RTWO values");
     140             : 
     141           2 :   double tauc=0.;
     142           2 :   parse("TAUC",tauc);
     143           2 :   if(tauc==0.) error("TAUC must be set");
     144             : 
     145           2 :   double omega=0.;
     146           2 :   parse("OMEGA",omega);
     147           2 :   if(omega==0.) error("OMEGA must be set");
     148             : 
     149           2 :   inept=0.;
     150           2 :   parse("INEPT",inept);
     151           2 :   if(inept==0.) error("INEPT must be set");
     152           2 :   inept *= 0.001; // ms2s
     153             : 
     154           2 :   const double ns2s   = 0.000000001;
     155           2 :   const double MHz2Hz = 1000000.;
     156           2 :   const double Kappa  = 12300000000.00; // this is 1/15*S*(S+1)*gamma^2*g^2*beta^2
     157             :   // where gamma is the nuclear gyromagnetic ratio,
     158             :   // g is the electronic g factor, and beta is the Bohr magneton
     159             :   // in nm^6/s^2
     160           2 :   constant = (4.*tauc*ns2s+(3.*tauc*ns2s)/(1+omega*omega*MHz2Hz*MHz2Hz*tauc*tauc*ns2s*ns2s))*Kappa;
     161             : 
     162           2 :   bool addexp=false;
     163           2 :   parseFlag("ADDEXP",addexp);
     164             : 
     165           4 :   vector<double> exppre;
     166           2 :   if(addexp) {
     167           0 :     exppre.resize( nga.size() );
     168           0 :     unsigned ntarget=0;
     169             : 
     170           0 :     for(unsigned i=0; i<nga.size(); ++i) {
     171           0 :       if( !parseNumbered( "PREINT", i+1, exppre[i] ) ) break;
     172           0 :       ntarget++;
     173             :     }
     174           0 :     if( ntarget!=nga.size() ) error("found wrong number of PREINT values");
     175             :   }
     176             : 
     177             :   // Create neighbour lists
     178           2 :   nl= new NeighborList(gb_lista,ga_lista,true,pbc,getPbc());
     179             : 
     180             :   // Ouput details of all contacts
     181           2 :   unsigned index=0;
     182           8 :   for(unsigned i=0; i<nga.size(); ++i) {
     183           6 :     log.printf("  The %uth PRE is calculated using %u equivalent atoms:\n", i, nga[i]);
     184           6 :     log.printf("    %d", ga_lista[index].serial());
     185           6 :     index++;
     186           8 :     for(unsigned j=1; j<nga[i]; j++) {
     187           2 :       log.printf(" %d", ga_lista[index].serial());
     188           2 :       index++;
     189             :     }
     190           6 :     log.printf("\n");
     191             :   }
     192             : 
     193           2 :   if(pbc)      log.printf("  using periodic boundary conditions\n");
     194           0 :   else         log.printf("  without periodic boundary conditions\n");
     195             : 
     196           8 :   for(unsigned i=0; i<nga.size(); i++) {
     197           6 :     string num; Tools::convert(i,num);
     198           6 :     addComponentWithDerivatives("pre_"+num);
     199           6 :     componentIsNotPeriodic("pre_"+num);
     200           6 :   }
     201             : 
     202           2 :   if(addexp) {
     203           0 :     for(unsigned i=0; i<nga.size(); i++) {
     204           0 :       string num; Tools::convert(i,num);
     205           0 :       addComponent("exp_"+num);
     206           0 :       componentIsNotPeriodic("exp_"+num);
     207           0 :       Value* comp=getPntrToComponent("exp_"+num);
     208           0 :       comp->set(exppre[i]);
     209           0 :     }
     210             :   }
     211             : 
     212           2 :   requestAtoms(nl->getFullAtomList());
     213           4 :   checkRead();
     214           2 : }
     215             : 
     216           8 : PRE::~PRE() {
     217           2 :   delete nl;
     218           6 : }
     219             : 
     220         175 : void PRE::calculate()
     221             : {
     222             : // cycle over the number of PRE
     223         523 :   #pragma omp parallel for num_threads(OpenMP::getNumThreads())
     224         348 :   for(unsigned i=0; i<nga.size(); i++) {
     225         525 :     vector<Vector> deriv;
     226         524 :     Tensor dervir;
     227         525 :     double pre=0;
     228         525 :     unsigned index=0;
     229        1050 :     for(unsigned k=0; k<i; k++) index+=nga[k];
     230             :     // cycle over equivalent atoms
     231        1225 :     for(unsigned j=0; j<nga[i]; j++) {
     232         700 :       const double c_aver=constant/((double)nga[i]);
     233             :       // the first atom is always the same (the paramagnetic group)
     234         700 :       const unsigned i0=nl->getClosePair(index+j).first;
     235         700 :       const unsigned i1=nl->getClosePair(index+j).second;
     236             : 
     237         700 :       Vector distance;
     238        1396 :       if(pbc) distance=pbcDistance(getPosition(i0),getPosition(i1));
     239           0 :       else    distance=delta(getPosition(i0),getPosition(i1));
     240             : 
     241         700 :       const double r2=distance.modulo2();
     242         698 :       const double r6=r2*r2*r2;
     243         698 :       const double r8=r6*r2;
     244         698 :       const double tmpir6=c_aver/r6;
     245         698 :       const double tmpir8=-6.*c_aver/r8;
     246             : 
     247         698 :       pre += tmpir6;
     248             : 
     249         698 :       Vector tmpv = -tmpir8*distance;
     250         699 :       deriv.push_back(tmpv);
     251         700 :       dervir   +=  Tensor(distance,tmpv);
     252             :     }
     253         525 :     const double ratio = rtwo[i]*exp(-pre*inept) / (rtwo[i]+pre);
     254         525 :     const double fact = -ratio*(inept+1./(rtwo[i]+pre));
     255             : 
     256         525 :     Value* val=getPntrToComponent(i);
     257         524 :     val->set(ratio);
     258         524 :     setBoxDerivatives(val, fact*dervir);
     259             : 
     260        1225 :     for(unsigned j=0; j<nga[i]; j++) {
     261         700 :       const unsigned i0=nl->getClosePair(index+j).first;
     262         700 :       const unsigned i1=nl->getClosePair(index+j).second;
     263         700 :       setAtomsDerivatives(val, i0,  fact*deriv[j]);
     264         700 :       setAtomsDerivatives(val, i1, -fact*deriv[j]);
     265         525 :     }
     266             :   }
     267         175 : }
     268             : 
     269             : }
     270        2523 : }

Generated by: LCOV version 1.13