LCOV - code coverage report
Current view: top level - core - GenericMolInfo.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 257 303 84.8 %
Date: 2026-11-05 10:19:04 Functions: 18 20 90.0 %

          Line data    Source code
       1             : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
       2             :    Copyright (c) 2012-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 "GenericMolInfo.h"
      23             : #include "ActionRegister.h"
      24             : #include "ActionSet.h"
      25             : #include "PlumedMain.h"
      26             : #include "tools/MolDataClass.h"
      27             : #include "tools/PDB.h"
      28             : #include "tools/Communicator.h"
      29             : #include "tools/Pbc.h"
      30             : #include "tools/Tools.h"
      31             : #include "config/Config.h"
      32             : #include <cmath>
      33             : #include <algorithm>
      34             : #include <map>
      35             : 
      36             : namespace PLMD {
      37             : 
      38             : 
      39             : /*
      40             : This action is defined in core/ as it is used by other actions.
      41             : Anyway, it is registered in setup/, so that excluding that module from
      42             : compilation will exclude it from plumed.
      43             : */
      44             : 
      45             : 
      46         168 : void GenericMolInfo::registerKeywords( Keywords& keys ) {
      47         168 :   ActionSetup::registerKeywords(keys);
      48         168 :   keys.add("compulsory","STRUCTURE","a file in pdb format containing a reference structure. "
      49             :            "This is used to defines the atoms in the various residues, chains, etc . "
      50             :            "For more details on the PDB file format visit http://www.wwpdb.org/docs.html");
      51         168 :   keys.add("compulsory","MOLTYPE","protein","what kind of molecule is contained in the pdb file - usually not needed since protein/RNA/DNA are compatible");
      52         168 :   keys.add("compulsory","PYTHON_BIN","default","python interpreter");
      53         168 :   keys.add("atoms","CHAIN","(for masochists ( mostly Davide Branduardi ) ) The atoms involved in each of the chains of interest in the structure.");
      54         168 :   keys.add("hidden","STRIDE","frequency for resetting the python interpreter. Should be 1.");
      55         168 :   keys.addFlag("WHOLE", false, "The reference structure is whole, i.e. not broken by PBC. If the pdb file has a CRYST1 record with space group P 1, this is checked along each chain, and a warning is written to the log if consecutive atoms of a residue, or of two consecutive protein or nucleic-acid residues, are on opposite sides of the periodic boundary");
      56         168 : }
      57             : 
      58         328 : GenericMolInfo::~GenericMolInfo() {
      59             : // empty destructor to delete unique_ptr
      60         492 : }
      61             : 
      62             : namespace {
      63             : // MOLINFO WHOLE takes the reference structure as whole. Check it along each chain in the box of the
      64             : // CRYST1 record: consecutive atoms of a residue, or of two consecutive protein or nucleic-acid residues,
      65             : // are close, so a step between them that is not its own minimum image crosses the periodic boundary.
      66             : // Returns a warning, or an empty string.
      67          16 : std::string checkWhole(const PDB & pdb,double scale) {
      68             :   // skip a cell that cannot be the periodic box: none or degenerate, the 1 A cube written when there
      69             :   // is no unit cell, or a crystallographic cell (simulation programs write the space group P 1)
      70          16 :   const Tensor & box(pdb.getBoxVec());
      71          16 :   const double det=box.determinant();
      72             :   bool unitcube=true;
      73          64 :   for(unsigned k=0; k<3; k++) {
      74          48 :     unitcube=unitcube && std::fabs(pdb.getBoxAxs()[k]-scale)<1e-3*scale && std::fabs(pdb.getBoxAng()[k]-90.0)<1e-3;
      75             :   }
      76          16 :   std::string group=pdb.getSpaceGroup();
      77          16 :   group.erase(std::remove(group.begin(),group.end(),' '),group.end());
      78          16 :   if(unitcube || (group.length()>0 && group!="P1") || !std::isfinite(det) || det<=0.0) {
      79           7 :     return "";
      80             :   }
      81           9 :   Pbc pbc;
      82           9 :   pbc.setBox(box);
      83         268 :   auto polymer=[](std::string r) {
      84         268 :     Tools::ltrim(r);
      85         268 :     Tools::trim(r);
      86         380 :     return MolDataClass::allowedResidue("protein",r) || MolDataClass::allowedResidue("dna",r) || MolDataClass::allowedResidue("rna",r);
      87             :   };
      88           9 :   const auto & at(pdb.getAtomNumbers());
      89           9 :   const auto & pos(pdb.getPositions());
      90             :   unsigned n=0;
      91             :   std::string list;
      92        2240 :   for(unsigned i=1; i<at.size(); i++) {
      93        2231 :     if(pdb.getChainID(at[i])!=pdb.getChainID(at[i-1])) {
      94           2 :       continue;
      95             :     }
      96        2229 :     if(pdb.getResidueNumber(at[i])!=pdb.getResidueNumber(at[i-1])
      97        2497 :         && !(polymer(pdb.getResidueName(at[i-1])) && polymer(pdb.getResidueName(at[i])))) {
      98          34 :       continue;
      99             :     }
     100             :     // the difference is a lattice vector, or zero up to rounding
     101        2195 :     if((delta(pos[i-1],pos[i])-pbc.distance(pos[i-1],pos[i])).modulo()>1e-3*std::cbrt(det)) {
     102          15 :       if(++n<=10) {
     103          11 :         std::string r=pdb.getResidueName(at[i]);
     104          11 :         Tools::ltrim(r);
     105          11 :         Tools::trim(r);
     106          33 :         list+=" "+std::to_string(at[i].serial())+" ("+r+std::to_string(pdb.getResidueNumber(at[i]))+")";
     107             :       }
     108             :     }
     109             :   }
     110           9 :   if(n==0) {
     111           7 :     return "";
     112             :   }
     113             :   return "the reference structure in the MOLINFO pdb file may not be whole: in the box of its CRYST1 record, "
     114           6 :          +std::to_string(n)+" atoms are across the periodic boundary from the atom before them in their chain:"+list
     115           7 :          +(n>10 ? " and "+std::to_string(n-10)+" more" : "")
     116           2 :          +". WHOLEMOLECULES will then split these molecules in every frame: make the pdb whole"
     117             :          +" (for example with gmx trjconv -pbc whole), or check that its CRYST1 record is the box the structure was written in";
     118             : }
     119             : }
     120             : 
     121         164 : GenericMolInfo::GenericMolInfo( const ActionOptions&ao ):
     122             :   Action(ao),
     123             :   ActionAnyorder(ao),
     124             :   ActionPilot(ao),
     125             :   ActionAtomistic(ao),
     126         164 :   iswhole_(false) {
     127         164 :   plumed_assert(getStride()==1);
     128             :   // Read what is contained in the pdb file
     129         164 :   parse("MOLTYPE",mytype);
     130             : 
     131             :   // check if whole
     132         164 :   parseFlag("WHOLE", iswhole_);
     133             : 
     134         164 :   auto* latestmoldat=plumed.getActionSet().selectLatest<GenericMolInfo*>(this);
     135         164 :   if( latestmoldat ) {
     136           4 :     log<<"  overriding last MOLINFO with label " << latestmoldat->getLabel()<<"\n";
     137             :   }
     138             : 
     139         164 :   std::vector<AtomNumber> backbone;
     140         328 :   parseAtomList("CHAIN",backbone);
     141         164 :   if( read_backbone.size()==0 ) {
     142           0 :     for(unsigned i=1;; ++i) {
     143         328 :       parseAtomList("CHAIN",i,backbone);
     144         164 :       if( backbone.size()==0 ) {
     145             :         break;
     146             :       }
     147           0 :       read_backbone.push_back(backbone);
     148           0 :       backbone.resize(0);
     149             :     }
     150             :   } else {
     151           0 :     read_backbone.push_back(backbone);
     152             :   }
     153         164 :   if( read_backbone.size()==0 ) {
     154         164 :     parse("STRUCTURE",reference);
     155             : 
     156         164 :     if( ! pdb.read(reference,usingNaturalUnits(),0.1/getUnits().getLength())) {
     157           0 :       plumed_merror("missing input file " + reference );
     158             :     }
     159         164 :     if( iswhole_ ) {
     160          16 :       const std::string msg=checkWhole(pdb,usingNaturalUnits() ? 1.0 : 0.1/getUnits().getLength());
     161          16 :       if( msg.length()>0 ) {
     162           2 :         warning(msg);
     163             :       }
     164             :     }
     165             : 
     166         164 :     std::vector<std::string> chains;
     167         164 :     pdb.getChainNames( chains );
     168         164 :     log.printf("  pdb file named %s contains %u chains \n",reference.c_str(), static_cast<unsigned>(chains.size()));
     169         478 :     for(unsigned i=0; i<chains.size(); ++i) {
     170             :       int start,end;
     171             :       std::string errmsg;
     172         314 :       pdb.getResidueRange( chains[i], start, end, errmsg );
     173         314 :       if( errmsg.length()!=0 ) {
     174           0 :         error( errmsg );
     175             :       }
     176             :       AtomNumber astart,aend;
     177         314 :       pdb.getAtomRange( chains[i], astart, aend, errmsg );
     178         314 :       if( errmsg.length()!=0 ) {
     179           0 :         error( errmsg );
     180             :       }
     181         314 :       log.printf("  chain named %s contains residues %d to %d and atoms %u to %u \n",chains[i].c_str(),start,end,astart.serial(),aend.serial());
     182             :     }
     183             : 
     184             :     std::string python_bin;
     185         164 :     parse("PYTHON_BIN",python_bin);
     186         164 :     if(python_bin=="no") {
     187           0 :       log<<"  python interpreter disabled\n";
     188             :     } else {
     189         164 :       pythonCmd=config::getEnvCommand();
     190         164 :       if(python_bin!="default") {
     191           0 :         log<<"  forcing python interpreter: "<<python_bin<<"\n";
     192           0 :         pythonCmd+=" env PLUMED_PYTHON_BIN="+python_bin;
     193             :       }
     194             :       bool sorted=true;
     195         164 :       const auto & at=pdb.getAtomNumbers();
     196      135762 :       for(unsigned i=0; i<at.size(); i++) {
     197      135598 :         if(at[i].index()!=i) {
     198             :           sorted=false;
     199             :         }
     200             :       }
     201         164 :       if(!sorted) {
     202           2 :         log<<"  PDB is not sorted, python interpreter will be disabled\n";
     203         162 :       } else if(!Subprocess::available()) {
     204           0 :         log<<"  subprocess is not available, python interpreter will be disabled\n";
     205             :       } else {
     206         162 :         enablePythonInterpreter=true;
     207             :       }
     208             :     }
     209         164 :   }
     210         164 : }
     211             : 
     212           3 : std::map<std::string,std::string> GenericMolInfo::getSpecialKeywords() {
     213             :   std::map<std::string,std::string> mapkeys;
     214           3 :   mapkeys.insert( std::pair<std::string,std::string>("@mda:","atom selection built using syntax for <a href=\\\"https://docs.mdanalysis.org/stable/documentation_pages/selections.html\\\">MDAnalysis</a>") );
     215           3 :   mapkeys.insert( std::pair<std::string,std::string>("@mdt:","atom selection built using syntax for <a href=\\\"https://www.mdtraj.org/1.9.8.dev0/index.html\\\">mdtraj</a>") );
     216           3 :   mapkeys.insert( std::pair<std::string,std::string>("@vmdexec:","atom selection built using syntax for <a href=\\\"https://www.ks.uiuc.edu/Research/vmd/\\\">VMD</a>") );
     217           3 :   mapkeys.insert( std::pair<std::string,std::string>("@vmd:","atom selection built using syntax for <a href=\\\"https://www.ks.uiuc.edu/Research/vmd/\\\">VMD</a> python module") );
     218           3 :   mapkeys.insert( std::pair<std::string,std::string>("@nucleic","all atoms that are part of a DNA or RNA molecule") );
     219           3 :   mapkeys.insert( std::pair<std::string,std::string>("@protein","all atoms that are part of a protein") );
     220           3 :   mapkeys.insert( std::pair<std::string,std::string>("@water","all water molecules") );
     221           3 :   mapkeys.insert( std::pair<std::string,std::string>("@ions","all the ions") );
     222           3 :   mapkeys.insert( std::pair<std::string,std::string>("@hydrogens","all hydrogen atoms") );
     223           3 :   mapkeys.insert( std::pair<std::string,std::string>("@nonhydrogens","all non hydrogen atoms") );
     224           3 :   mapkeys.insert( std::pair<std::string,std::string>("@phi-","the four atoms that are required to calculate the phi dihedral for") );
     225           3 :   mapkeys.insert( std::pair<std::string,std::string>("@psi-","the four atoms that are required to calculate the psi dihedral for") );
     226           3 :   mapkeys.insert( std::pair<std::string,std::string>("@omega-","the four atoms that are required to calculate the omega dihedral for") );
     227           3 :   mapkeys.insert( std::pair<std::string,std::string>("@chi1-","the four atoms that are required to calculate the chi1 dihedral for") );
     228           3 :   mapkeys.insert( std::pair<std::string,std::string>("@chi2-","the four atoms that are required to calculate the chi2 dihedral for") );
     229           3 :   mapkeys.insert( std::pair<std::string,std::string>("@chi3-","the four atoms that are required to calculate the chi3 dihedral for") );
     230           3 :   mapkeys.insert( std::pair<std::string,std::string>("@chi4-","the four atoms that are required to calculate the chi4 dihedral for") );
     231           3 :   mapkeys.insert( std::pair<std::string,std::string>("@chi5-","the four atoms that are required to calculate the chi5 dihedral for") );
     232           3 :   mapkeys.insert( std::pair<std::string,std::string>("@sidechain-","the protein sidechain atoms in") );
     233           3 :   mapkeys.insert( std::pair<std::string,std::string>("@back-","the protein/dna/rna backbone atoms in") );
     234           3 :   mapkeys.insert( std::pair<std::string,std::string>("@alpha-","the four atoms that are required to calculate the alpha backbone dihedral for") );
     235           3 :   mapkeys.insert( std::pair<std::string,std::string>("@beta-","the four atoms that are required to calculate the beta backbone dihedral for") );
     236           3 :   mapkeys.insert( std::pair<std::string,std::string>("@gamma-","the four atoms that are required to calculate the gamma backbone dihedral for") );
     237           3 :   mapkeys.insert( std::pair<std::string,std::string>("@delta-","the four atoms that are required to calculate the delta backbone dihedral for") );
     238           3 :   mapkeys.insert( std::pair<std::string,std::string>("@epsilon-","the four atoms that are required to calculate the backbone epsilon dihedral for") );
     239           3 :   mapkeys.insert( std::pair<std::string,std::string>("@zeta-","the four atoms that are required to calculate the zeta backbone dihedral for") );
     240           3 :   mapkeys.insert( std::pair<std::string,std::string>("@v0-","the four atoms that are required to calculate the v0 sugar dihedral for") );
     241           3 :   mapkeys.insert( std::pair<std::string,std::string>("@v1-","the four atoms that are required to calculate the v1 sugar dihedral for") );
     242           3 :   mapkeys.insert( std::pair<std::string,std::string>("@v2-","the four atoms that are required to calculate the v2 sugar dihedral for") );
     243           3 :   mapkeys.insert( std::pair<std::string,std::string>("@v3-","the four atoms that are required to calculate the v3 sugar dihedral for") );
     244           3 :   mapkeys.insert( std::pair<std::string,std::string>("@v4-","the four atoms that are required to calculate the v4 sugar dihedral for") );
     245           3 :   mapkeys.insert( std::pair<std::string,std::string>("@chi-","the four atoms that are required to alcullate the chi dihedral for") );
     246           3 :   mapkeys.insert( std::pair<std::string,std::string>("@sugar-","the heavy atoms of the sugar in") );
     247           3 :   mapkeys.insert( std::pair<std::string,std::string>("@base-","the heavy atoms of the base in") );
     248           3 :   mapkeys.insert( std::pair<std::string,std::string>("@lcs-","an ordered triplet of atoms on the 6-membered ring of the nucleobase in") );
     249           3 :   return mapkeys;
     250             : }
     251             : 
     252          60 : void GenericMolInfo::getBackbone( std::vector<std::string>& restrings, const std::string& fortype, std::vector< std::vector<AtomNumber> >& backbone ) {
     253          60 :   if( fortype!=mytype ) {
     254           0 :     error("cannot calculate a variable designed for " + fortype + " molecules for molecule type " + mytype );
     255             :   }
     256          60 :   if( MolDataClass::numberOfAtomsPerResidueInBackbone( mytype )==0 ) {
     257           0 :     error("backbone is not defined for molecule type " + mytype );
     258             :   }
     259             : 
     260          60 :   if( read_backbone.size()!=0 ) {
     261           0 :     if( restrings.size()!=1 ) {
     262           0 :       error("cannot interpret anything other than all for residues when using CHAIN keywords");
     263             :     }
     264           0 :     if( restrings[0]!="all" ) {
     265           0 :       error("cannot interpret anything other than all for residues when using CHAIN keywords");
     266             :     }
     267           0 :     backbone.resize( read_backbone.size() );
     268           0 :     for(unsigned i=0; i<read_backbone.size(); ++i) {
     269           0 :       backbone[i].resize( read_backbone[i].size() );
     270           0 :       for(unsigned j=0; j<read_backbone[i].size(); ++j) {
     271           0 :         backbone[i][j]=read_backbone[i][j];
     272             :       }
     273             :     }
     274             :   } else {
     275             :     bool useter=false; // This is used to deal with terminal groups in WHOLEMOLECULES
     276             :     // residues are identified by (chain, residue number), so that chains that use
     277             :     // the same residue numbers (e.g. all starting from 1) are not mixed up
     278          60 :     std::vector< std::pair<std::string,int> > residues;
     279          60 :     std::vector<std::string> chains;
     280             :     {
     281          60 :       std::vector<std::string> allchains;
     282          60 :       pdb.getChainNames( allchains );
     283             :       // a chain can appear in more than one block of the pdb file: list it once
     284         756 :       for(const auto & c : allchains) {
     285         696 :         if( std::find(chains.begin(),chains.end(),c)==chains.end() ) {
     286         696 :           chains.push_back(c);
     287             :         }
     288             :       }
     289          60 :     }
     290          60 :     if( restrings.size()==1 ) {
     291          58 :       useter=( restrings[0].find("ter")!=std::string::npos );
     292             :     }
     293          60 :     if( restrings.size()==1 && restrings[0].find("all")!=std::string::npos ) {
     294             :       // list explicitly the residues that are present in each chain:
     295             :       // this works also with negative residue numbers and with gaps in the numbering
     296         748 :       for(const auto & c : chains) {
     297         692 :         const std::vector<int> res = pdb.getResiduesInChain( c );
     298         692 :         if( res.empty() ) {
     299             :           continue;
     300             :         }
     301             :         std::size_t first=0, last=res.size();
     302         692 :         if( !useter ) {
     303        1384 :           if( MolDataClass::isTerminalGroup( mytype, pdb.getResidueName( res.front(), c ) ) ) {
     304             :             first++;
     305             :           }
     306        1384 :           if( last>first && MolDataClass::isTerminalGroup( mytype, pdb.getResidueName( res.back(), c ) ) ) {
     307         690 :             last--;
     308             :           }
     309             :         }
     310        6310 :         for(std::size_t k=first; k<last; ++k) {
     311        5618 :           residues.emplace_back( c, res[k] );
     312             :         }
     313             :       }
     314             :     } else {
     315           4 :       Tools::interpretRanges(restrings);
     316             :       // chains containing each residue number
     317             :       std::map< int, std::vector<std::string> > chains_of_residue;
     318           8 :       for(const auto & c : chains) {
     319          60 :         for(const int r : pdb.getResiduesInChain( c )) {
     320          56 :           chains_of_residue[r].push_back( c );
     321             :         }
     322             :       }
     323             :       std::string ambiguous;
     324          28 :       for(const auto & rs : restrings) {
     325             :         int r;
     326          24 :         if( !Tools::convertNoexcept( rs, r ) ) {
     327           0 :           error("cannot interpret " + rs + " as a residue number");
     328             :         }
     329             :         const auto p=chains_of_residue.find( r );
     330          24 :         if( p==chains_of_residue.end() ) {
     331           0 :           error("residue " + rs + " is not in the reference pdb file");
     332             :         }
     333             :         // a residue number present in more than one chain is taken from the first chain, as in previous versions
     334          24 :         if( p->second.size()>1 ) {
     335           0 :           ambiguous += " " + rs;
     336             :         }
     337          24 :         residues.emplace_back( p->second[0], r );
     338             :       }
     339           4 :       if( ambiguous.length()>0 ) {
     340           0 :         warning("residues" + ambiguous + " are present in more than one chain of the reference pdb file: they are taken from the first chain that contains them. Use RESIDUES=all to include all the chains");
     341             :       }
     342             :     }
     343          60 :     if( residues.empty() ) {
     344           0 :       error("no residues found in the reference pdb file");
     345             :     }
     346             : 
     347             :     // Convert the list of involved residues into a list of segments of consecutive residues of the same chain
     348          60 :     std::vector< std::vector< std::pair<std::string,int> > > segments;
     349          60 :     std::vector< std::pair<std::string,int> > thissegment;
     350          60 :     thissegment.push_back( residues[0] );
     351        5642 :     for(unsigned i=1; i<residues.size(); ++i) {
     352        5582 :       if( residues[i-1].second+1!=residues[i].second || residues[i-1].first!=residues[i].first ) {
     353         638 :         segments.push_back(thissegment);
     354         638 :         thissegment.resize(0);
     355             :       }
     356        5582 :       thissegment.push_back( residues[i] );
     357             :     }
     358          60 :     segments.push_back( thissegment );
     359             : 
     360             :     // And now get the backbone atoms from each segment
     361          60 :     backbone.resize( segments.size() );
     362          60 :     std::vector<AtomNumber> atomnumbers;
     363         758 :     for(unsigned i=0; i<segments.size(); ++i) {
     364        6340 :       for(unsigned j=0; j<segments[i].size(); ++j) {
     365        5642 :         const std::string & chain=segments[i][j].first;
     366        5642 :         const int resnum=segments[i][j].second;
     367             :         std::string num;
     368        5642 :         Tools::convert( resnum, num );
     369        5642 :         num += " of chain " + chain;
     370        5642 :         std::string resname=pdb.getResidueName( resnum, chain );
     371        5642 :         if( !MolDataClass::allowedResidue(mytype, resname) ) {
     372           0 :           error("residue " + num + " is not recognized for moltype " + mytype );
     373             :         }
     374        5642 :         if( !useter && MolDataClass::isTerminalGroup( mytype, resname ) ) {
     375           0 :           error("residue " + num + " appears to be a terminal group");
     376             :         }
     377        5642 :         if( resname=="GLY" ) {
     378           0 :           warning("GLY residues are achiral - assuming HA1 atom is in CB position");
     379             :         }
     380        5642 :         MolDataClass::getBackboneForResidue( mytype, resnum, chain, pdb, atomnumbers );
     381        5642 :         if( atomnumbers.size()==0 ) {
     382           0 :           error("Could not find required backbone atom in residue number " + num );
     383             :         } else {
     384       33852 :           for(unsigned k=0; k<atomnumbers.size(); ++k) {
     385       28210 :             backbone[i].push_back( atomnumbers[k] );
     386             :           }
     387             :         }
     388        5642 :         atomnumbers.resize(0);
     389             :       }
     390             :     }
     391          60 :   }
     392          60 : }
     393             : 
     394         801 : void GenericMolInfo::interpretSymbol( const std::string& symbol, std::vector<AtomNumber>& atoms ) {
     395         801 :   if(Tools::startWith(symbol,"mdt:") || Tools::startWith(symbol,"mda:") || Tools::startWith(symbol,"vmd:") || Tools::startWith(symbol,"vmdexec:")) {
     396             : 
     397          24 :     plumed_assert(enablePythonInterpreter);
     398             : 
     399          48 :     log<<"  symbol " + symbol + " will be sent to python interpreter\n";
     400          24 :     if(!selector_running) {
     401           3 :       log<<"  MOLINFO "<<getLabel()<<": starting python interpreter\n";
     402           3 :       if(comm.Get_rank()==0) {
     403           8 :         selector=Tools::make_unique<Subprocess>(pythonCmd+" \""+config::getPlumedRoot()+"\"/scripts/selector.sh --pdb " + reference);
     404           2 :         selector->stop();
     405             :       }
     406           3 :       selector_running=true;
     407             :     }
     408             : 
     409          24 :     atoms.resize(0);
     410             : 
     411          24 :     if(comm.Get_rank()==0) {
     412          16 :       int ok=0;
     413             :       std::string error_msg;
     414             :       // this is a complicated way to have the exception propagated with MPI.
     415             :       // It is necessary since only one process calls the selector.
     416             :       // Probably I should recycle the exception propagation at library boundaries
     417             :       // to allow transferring the exception to other processes.
     418             :       try {
     419          16 :         plumed_assert(selector) << "Python interpreter is disabled, selection " + symbol + " cannot be interpreted";
     420             :         auto h=selector->contStop(); // stops again when it goes out of scope
     421          16 :         (*selector) << symbol << "\n";
     422          16 :         selector->flush();
     423             :         std::string res;
     424          16 :         std::vector<std::string> words;
     425             :         while(true) {
     426          16 :           selector->getline(res);
     427          32 :           words=Tools::getWords(res);
     428          16 :           if(!words.empty() && words[0]=="Error") {
     429           0 :             plumed_error()<<res;
     430             :           }
     431          16 :           if(!words.empty() && words[0]=="Selection:") {
     432             :             break;
     433             :           }
     434             :         }
     435             :         words.erase(words.begin());
     436         712 :         for(const auto & w : words) {
     437             :           int n;
     438         696 :           if(w.empty()) {
     439           0 :             continue;
     440             :           }
     441         696 :           Tools::convert(w,n);
     442        1392 :           atoms.push_back(AtomNumber::serial(n));
     443             :         }
     444          16 :         ok=1;
     445          32 :       } catch (const Exception & e) {
     446           0 :         error_msg=e.what();
     447           0 :       }
     448          16 :       comm.Bcast(ok,0);
     449          16 :       if(!ok) {
     450           0 :         size_t s=error_msg.length();
     451           0 :         comm.Bcast(s,0);
     452           0 :         comm.Bcast(error_msg,0);
     453           0 :         throw Exception()<<error_msg;
     454             :       }
     455          16 :       size_t nat=atoms.size();
     456          16 :       comm.Bcast(nat,0);
     457          16 :       comm.Bcast(atoms,0);
     458             :     } else {
     459           8 :       int ok=0;
     460             :       std::string error_msg;
     461           8 :       comm.Bcast(ok,0);
     462           8 :       if(!ok) {
     463             :         size_t s;
     464           0 :         comm.Bcast(s,0);
     465           0 :         error_msg.resize(s);
     466           0 :         comm.Bcast(error_msg,0);
     467           0 :         throw Exception()<<error_msg;
     468             :       }
     469           8 :       size_t nat=0;
     470           8 :       comm.Bcast(nat,0);
     471           8 :       atoms.resize(nat);
     472           8 :       comm.Bcast(atoms,0);
     473             :     }
     474          24 :     log<<"  selection interpreted using ";
     475          24 :     if(Tools::startWith(symbol,"mdt:")) {
     476          12 :       log<<"mdtraj "<<cite("McGibbon et al, Biophys. J., 109, 1528 (2015)")<<"\n";
     477             :     }
     478          24 :     if(Tools::startWith(symbol,"mda:")) {
     479          36 :       log<<"MDAnalysis "<<cite("Gowers et al, Proceedings of the 15th Python in Science Conference, doi:10.25080/majora-629e541a-00e (2016)")<<"\n";
     480             :     }
     481          24 :     if(Tools::startWith(symbol,"vmdexec:")) {
     482           0 :       log<<"VMD "<<cite("Humphrey, Dalke, and Schulten, K., J. Molec. Graphics, 14, 33 (1996)")<<"\n";
     483             :     }
     484          24 :     if(Tools::startWith(symbol,"vmd:")) {
     485           0 :       log<<"VMD (github.com/Eigenstate/vmd-python) "<<cite("Humphrey, Dalke, and Schulten, K., J. Molec. Graphics, 14, 33 (1996)")<<"\n";
     486             :     }
     487          24 :     return;
     488             :   }
     489         777 :   MolDataClass::specialSymbol( mytype, symbol, pdb, atoms );
     490         777 :   if(atoms.empty()) {
     491           0 :     error(symbol + " not found in your MOLINFO structure");
     492             :   }
     493             : }
     494             : 
     495       72126 : std::string GenericMolInfo::getAtomName(AtomNumber a)const {
     496       72126 :   return pdb.getAtomName(a);
     497             : }
     498             : 
     499        3846 : bool GenericMolInfo::checkForAtom(AtomNumber a)const {
     500        3846 :   return pdb.checkForAtom(a);
     501             : }
     502             : 
     503       62194 : int GenericMolInfo::getResidueNumber(AtomNumber a)const {
     504       62194 :   return pdb.getResidueNumber(a);
     505             : }
     506             : 
     507       19512 : unsigned GenericMolInfo::getPDBsize()const {
     508       19512 :   return pdb.size();
     509             : }
     510             : 
     511       61190 : std::string GenericMolInfo::getResidueName(AtomNumber a)const {
     512       61190 :   return pdb.getResidueName(a);
     513             : }
     514             : 
     515         468 : std::string GenericMolInfo::getChainID(AtomNumber a)const {
     516         468 :   return pdb.getChainID(a);
     517             : }
     518             : 
     519        3789 : Vector GenericMolInfo::getPosition(AtomNumber a)const {
     520        3789 :   return pdb.getPosition(a);
     521             : }
     522             : 
     523       29503 : bool GenericMolInfo::isWhole() const {
     524       29503 :   return iswhole_;
     525             : }
     526             : 
     527        9653 : void GenericMolInfo::prepare() {
     528        9653 :   if(selector_running) {
     529           3 :     log<<"  MOLINFO "<<getLabel()<<": killing python interpreter\n";
     530             :     selector.reset();
     531           3 :     selector_running=false;
     532             :   }
     533        9653 : }
     534             : 
     535             : }

Generated by: LCOV version 1.16