LCOV - code coverage report
Current view: top level - isdb - NOE.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 110 116 94.8 %
Date: 2026-11-05 10:19:04 Functions: 3 4 75.0 %

          Line data    Source code
       1             : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
       2             :    Copyright (c) 2014-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/NeighborList.h"
      25             : #include "tools/Pbc.h"
      26             : #include <memory>
      27             : 
      28             : namespace PLMD {
      29             : namespace isdb {
      30             : 
      31             : //+PLUMEDOC ISDB_COLVAR NOE
      32             : /*
      33             : Calculates NOE intensities as sums of 1/r^6, also averaging over multiple equivalent atoms
      34             :  or ambiguous NOE.
      35             : 
      36             : Each NOE is defined by two groups containing the same number of atoms, distances are
      37             : calculated in pairs, transformed in 1/r^6, summed and saved as components.
      38             : 
      39             : $$
      40             : NOE() = (\frac{1}{N_{eq}}\sum_j^{N_{eq}} (\frac{1}{r_j^6}))
      41             : $$
      42             : 
      43             : NOE can be used to calculate a Metainference score over one or more replicas using the intrinsic implementation
      44             : of [METAINFERENCE](METAINFERENCE.md) that is activated by DOSCORE.
      45             : 
      46             : ## Examples
      47             : 
      48             : In the following examples three noes are defined, the first is calculated based on the distances
      49             : of atom 1-2 and 3-2; the second is defined by the distance 5-7 and the third by the distances
      50             : 4-15,4-16,8-15,8-16. [METAINFERENCE](METAINFERENCE.md) is activated using DOSCORE.
      51             : 
      52             : ```plumed
      53             : NOE ...
      54             : GROUPA1=1,3 GROUPB1=2,2 NOEDIST1=0.6
      55             : GROUPA2=5 GROUPB2=7 NOEDIST2=0.6
      56             : GROUPA3=4,4,8,8 GROUPB3=15,16,15,16 NOEDIST3=0.6
      57             : DOSCORE
      58             : SIGMA_MEAN0=1
      59             : LABEL=noes
      60             : ... NOE
      61             : 
      62             : PRINT ARG=noes.* FILE=colvar
      63             : ```
      64             : 
      65             : */
      66             : //+ENDPLUMEDOC
      67             : 
      68             : class NOE :
      69             :   public MetainferenceBase {
      70             : private:
      71             :   bool             pbc;
      72             :   std::vector<unsigned> nga;
      73             :   std::unique_ptr<NeighborList> nl;
      74             :   unsigned         tot_size;
      75             :   // the components with the back-calculated data
      76             :   std::vector<Value*> dataValues_;
      77             :   // index of the first pair of each group in the neighbor list
      78             :   std::vector<unsigned> nga_offset_;
      79             : public:
      80             :   static void registerKeywords( Keywords& keys );
      81             :   explicit NOE(const ActionOptions&);
      82             :   void calculate() override;
      83             : };
      84             : 
      85             : PLUMED_REGISTER_ACTION(NOE,"NOE")
      86             : 
      87          37 : void NOE::registerKeywords( Keywords& keys ) {
      88          37 :   MetainferenceBase::registerKeywords(keys);
      89          37 :   keys.addFlag("NOPBC",false,"ignore the periodic boundary conditions when calculating distances");
      90          37 :   keys.add("numbered","GROUPA","the atoms involved in each of the contacts you wish to calculate. "
      91             :            "Keywords like GROUPA1, GROUPA2, GROUPA3,... should be listed and one contact will be "
      92             :            "calculated for each ATOM keyword you specify.");
      93          37 :   keys.add("numbered","GROUPB","the atoms involved in each of the contacts you wish to calculate. "
      94             :            "Keywords like GROUPB1, GROUPB2, GROUPB3,... should be listed and one contact will be "
      95             :            "calculated for each ATOM keyword you specify.");
      96          74 :   keys.reset_style("GROUPA","atoms");
      97          74 :   keys.reset_style("GROUPB","atoms");
      98          37 :   keys.add("numbered","NOEDIST","Add an experimental value for each NOE.");
      99          74 :   keys.addOutputComponent("noe","default","scalar","the # NOE");
     100          74 :   keys.addOutputComponent("exp","NOEDIST","scalar","the # NOE experimental distance");
     101          37 : }
     102             : 
     103          33 : NOE::NOE(const ActionOptions&ao):
     104             :   PLUMED_METAINF_INIT(ao),
     105          33 :   pbc(true) {
     106          33 :   bool nopbc=!pbc;
     107          33 :   parseFlag("NOPBC",nopbc);
     108          33 :   pbc=!nopbc;
     109             : 
     110             :   // Read in the atoms
     111          33 :   std::vector<AtomNumber> t, ga_lista, gb_lista;
     112          93 :   for(int i=1;; ++i ) {
     113         252 :     parseAtomList("GROUPA", i, t );
     114         126 :     if( t.empty() ) {
     115             :       break;
     116             :     }
     117         203 :     for(unsigned j=0; j<t.size(); j++) {
     118         110 :       ga_lista.push_back(t[j]);
     119             :     }
     120          93 :     nga.push_back(t.size());
     121          93 :     t.resize(0);
     122          93 :   }
     123          33 :   std::vector<unsigned> ngb;
     124          93 :   for(int i=1;; ++i ) {
     125         252 :     parseAtomList("GROUPB", i, t );
     126         126 :     if( t.empty() ) {
     127             :       break;
     128             :     }
     129         203 :     for(unsigned j=0; j<t.size(); j++) {
     130         110 :       gb_lista.push_back(t[j]);
     131             :     }
     132          93 :     ngb.push_back(t.size());
     133          93 :     if(ngb[i-1]!=nga[i-1]) {
     134           0 :       error("The same number of atoms is expected for the same GROUPA-GROUPB couple");
     135             :     }
     136          93 :     t.resize(0);
     137          93 :   }
     138          33 :   if(nga.size()!=ngb.size()) {
     139           0 :     error("There should be the same number of GROUPA and GROUPB keywords");
     140             :   }
     141             :   // Create neighbour lists
     142          66 :   nl=Tools::make_unique<NeighborList>(ga_lista,gb_lista,false,true,pbc,getPbc(),comm);
     143             : 
     144             :   // Optionally add an experimental value (like with RDCs)
     145          33 :   std::vector<double> noedist;
     146          33 :   noedist.resize( nga.size() );
     147             :   unsigned ntarget=0;
     148          84 :   for(unsigned i=0; i<nga.size(); ++i) {
     149         128 :     if( !parseNumbered( "NOEDIST", i+1, noedist[i] ) ) {
     150             :       break;
     151             :     }
     152          51 :     ntarget++;
     153             :   }
     154             :   bool addexp=false;
     155          33 :   if(ntarget!=nga.size() && ntarget!=0) {
     156           0 :     error("found wrong number of NOEDIST values");
     157             :   }
     158          33 :   if(ntarget==nga.size()) {
     159             :     addexp=true;
     160             :   }
     161          33 :   if(getDoScore()&&!addexp) {
     162           0 :     error("with DOSCORE you need to set the NOEDIST values");
     163             :   }
     164             : 
     165             :   // Output details of all contacts
     166             :   unsigned index=0;
     167         126 :   for(unsigned i=0; i<nga.size(); ++i) {
     168          93 :     log.printf("  The %uth NOE is calculated using %u equivalent couples of atoms\n", i, nga[i]);
     169         203 :     for(unsigned j=0; j<nga[i]; j++) {
     170         110 :       log.printf("    couple %u is %d %d.\n", j, ga_lista[index].serial(), gb_lista[index].serial() );
     171         110 :       index++;
     172             :     }
     173             :   }
     174          33 :   tot_size = index;
     175             : 
     176          33 :   if(pbc) {
     177          33 :     log.printf("  using periodic boundary conditions\n");
     178             :   } else {
     179           0 :     log.printf("  without periodic boundary conditions\n");
     180             :   }
     181             : 
     182          66 :   log << " Bibliography" << plumed.cite("Bonomi, Camilloni, Bioinformatics, 33, 3999 (2017)") << "\n";
     183             : 
     184          33 :   if(!getDoScore()) {
     185          70 :     for(unsigned i=0; i<nga.size(); i++) {
     186             :       std::string num;
     187          52 :       Tools::convert(i,num);
     188         104 :       addComponentWithDerivatives("noe-"+num);
     189          52 :       componentIsNotPeriodic("noe-"+num);
     190          52 :       dataValues_.push_back(getPntrToComponent(getNumberOfComponents()-1));
     191             :     }
     192          18 :     if(addexp) {
     193          15 :       for(unsigned i=0; i<nga.size(); i++) {
     194             :         std::string num;
     195          10 :         Tools::convert(i,num);
     196          20 :         addComponent("exp-"+num);
     197          10 :         componentIsNotPeriodic("exp-"+num);
     198          20 :         Value* comp=getPntrToComponent("exp-"+num);
     199          10 :         comp->set(noedist[i]);
     200             :       }
     201             :     }
     202             :   } else {
     203          56 :     for(unsigned i=0; i<nga.size(); i++) {
     204             :       std::string num;
     205          41 :       Tools::convert(i,num);
     206          82 :       addComponent("noe-"+num);
     207          41 :       componentIsNotPeriodic("noe-"+num);
     208          41 :       dataValues_.push_back(getPntrToComponent(getNumberOfComponents()-1));
     209             :     }
     210          56 :     for(unsigned i=0; i<nga.size(); i++) {
     211             :       std::string num;
     212          41 :       Tools::convert(i,num);
     213          82 :       addComponent("exp-"+num);
     214          41 :       componentIsNotPeriodic("exp-"+num);
     215          82 :       Value* comp=getPntrToComponent("exp-"+num);
     216          41 :       comp->set(noedist[i]);
     217             :     }
     218             :   }
     219             : 
     220          33 :   nga_offset_.resize(nga.size(),0);
     221          93 :   for(unsigned i=1; i<nga.size(); i++) {
     222          60 :     nga_offset_[i]=nga_offset_[i-1]+nga[i-1];
     223             :   }
     224             : 
     225          33 :   requestAtoms(nl->getFullAtomList(), false);
     226          33 :   if(getDoScore()) {
     227          15 :     setParameters(noedist);
     228          15 :     Initialise(nga.size());
     229             :   }
     230          33 :   setDerivatives();
     231          33 :   checkRead();
     232          33 : }
     233             : 
     234         656 : void NOE::calculate() {
     235         656 :   const unsigned ngasz=nga.size();
     236         656 :   std::vector<Vector> deriv(tot_size, Vector{0,0,0});
     237             : 
     238         656 :   #pragma omp parallel for num_threads(OpenMP::getNumThreads())
     239             :   for(unsigned i=0; i<ngasz; i++) {
     240             :     Tensor dervir;
     241             :     double noe=0;
     242             :     const unsigned index=nga_offset_[i];
     243             :     Value* val=dataValues_[i];
     244             :     // cycle over equivalent atoms
     245             :     for(unsigned j=0; j<nga[i]; j++) {
     246             :       const unsigned i0=nl->getClosePair(index+j).first;
     247             :       const unsigned i1=nl->getClosePair(index+j).second;
     248             : 
     249             :       Vector distance;
     250             :       if(pbc) {
     251             :         distance=pbcDistance(getPosition(i0),getPosition(i1));
     252             :       } else {
     253             :         distance=delta(getPosition(i0),getPosition(i1));
     254             :       }
     255             : 
     256             :       const double ir2=1./distance.modulo2();
     257             :       const double ir6=ir2*ir2*ir2;
     258             :       const double ir8=6*ir6*ir2;
     259             : 
     260             :       noe += ir6;
     261             :       deriv[index+j] = ir8*distance;
     262             :       if(!getDoScore()) {
     263             :         dervir += Tensor(distance, deriv[index+j]);
     264             :         setAtomsDerivatives(val, i0,  deriv[index+j]);
     265             :         setAtomsDerivatives(val, i1, -deriv[index+j]);
     266             :       }
     267             :     }
     268             :     val->set(noe);
     269             :     if(!getDoScore()) {
     270             :       setBoxDerivatives(val, dervir);
     271             :     } else {
     272             :       setCalcData(i, noe);
     273             :     }
     274             :   }
     275             : 
     276         656 :   if(getDoScore()) {
     277             :     /* Metainference */
     278         152 :     Tensor dervir;
     279         152 :     double score = getScore();
     280         152 :     setScore(score);
     281             : 
     282             :     /* calculate final derivatives */
     283         152 :     Value* val=getPntrToComponent("score");
     284         544 :     for(unsigned i=0; i<ngasz; i++) {
     285         392 :       const unsigned index=nga_offset_[i];
     286             :       // cycle over equivalent atoms
     287         880 :       for(unsigned j=0; j<nga[i]; j++) {
     288         488 :         const unsigned i0=nl->getClosePair(index+j).first;
     289         488 :         const unsigned i1=nl->getClosePair(index+j).second;
     290             : 
     291         488 :         Vector distance;
     292         488 :         if(pbc) {
     293         488 :           distance=pbcDistance(getPosition(i0),getPosition(i1));
     294             :         } else {
     295           0 :           distance=delta(getPosition(i0),getPosition(i1));
     296             :         }
     297             : 
     298         976 :         dervir += Tensor(distance,deriv[index+j]*getMetaDer(i));
     299         488 :         setAtomsDerivatives(val, i0,  deriv[index+j]*getMetaDer(i));
     300         488 :         setAtomsDerivatives(val, i1, -deriv[index+j]*getMetaDer(i));
     301             :       }
     302             :     }
     303         152 :     setBoxDerivatives(val, dervir);
     304             :   }
     305         656 : }
     306             : 
     307             : }
     308             : }

Generated by: LCOV version 1.16