LCOV - code coverage report
Current view: top level - tools - PDB.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 248 471 52.7 %
Date: 2026-11-05 10:19:04 Functions: 30 47 63.8 %

          Line data    Source code
       1             : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
       2             :    Copyright (c) 2011-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 "PDB.h"
      23             : #include "Tools.h"
      24             : #include "Log.h"
      25             : #include "h36.h"
      26             : #include <cstdio>
      27             : #include <set>
      28             : #include "core/GenericMolInfo.h"
      29             : #include "Tensor.h"
      30             : 
      31             : //+PLUMEDOC INTERNAL pdbreader
      32             : /*
      33             : PLUMED can use the PDB format in several places
      34             : 
      35             : - To read molecular structure (\ref MOLINFO).
      36             : - To read reference conformations (\ref RMSD, but also many other methods in \ref dists, \ref FIT_TO_TEMPLATE, etc).
      37             : 
      38             : The implemented PDB reader expects a file formatted correctly according to the
      39             : [PDB standard](http://www.wwpdb.org/documentation/file-format-content/format33/v3.3.html).
      40             : In particular, the following columns are read from ATOM records
      41             : \verbatim
      42             : columns | content
      43             : 1-6     | record name (ATOM or HETATM)
      44             : 7-11    | serial number of the atom (starting from 1)
      45             : 13-16   | atom name
      46             : 18-20   | residue name
      47             : 22      | chain id
      48             : 23-26   | residue number
      49             : 31-38   | x coordinate
      50             : 39-46   | y coordinate
      51             : 47-54   | z coordinate
      52             : 55-60   | occupancy
      53             : 61-66   | beta factor
      54             : \endverbatim
      55             : PLUMED parser is slightly more permissive than the official PDB format
      56             : in the fact that the format of real numbers is not fixed. In other words,
      57             : any real number that can be parsed is OK and the dot can be placed anywhere. However,
      58             : __columns are interpret strictly__. A sample PDB should look like the following
      59             : \verbatim
      60             : ATOM      2  CH3 ACE     1      12.932 -14.718  -6.016  1.00  1.00
      61             : ATOM      5  C   ACE     1      21.312  -9.928  -5.946  1.00  1.00
      62             : ATOM      9  CA  ALA     2      19.462 -11.088  -8.986  1.00  1.00
      63             : \endverbatim
      64             : 
      65             : Notice that serial numbers need not to be consecutive. In the three-line example above,
      66             : only the coordinates of three atoms are provided. This is perfectly legal and indicates PLUMED
      67             : that information about these atoms only is available. This could be both for structural
      68             : information in \ref MOLINFO, where the other atoms would have no name assigned, and for
      69             : reference structures used in \ref RMSD, where only the provided atoms would be used to compute RMSD.
      70             : 
      71             : \par Occupancy and beta factors
      72             : 
      73             : PLUMED reads also occupancy and beta factors that however are given a very special meaning.
      74             : In cases where the PDB structure is used as a reference for an alignment (that's the case
      75             : for instance in \ref RMSD and in \ref FIT_TO_TEMPLATE), the occupancy column is used
      76             : to provide the weight of each atom in the alignment. In cases where, perhaps after alignment,
      77             : the displacement between running coordinates and the provided PDB is computed, the beta factors
      78             : are used as weight for the displacement.
      79             : Since setting the weights to zero is the same as __not__ including an atom in the alignment or
      80             : displacement calculation, the two following reference files would be equivalent when used in an \ref RMSD
      81             : calculation. First file:
      82             : \verbatim
      83             : ATOM      2  CH3 ACE     1      12.932 -14.718  -6.016  1.00  1.00
      84             : ATOM      5  C   ACE     1      21.312  -9.928  -5.946  1.00  1.00
      85             : ATOM      9  CA  ALA     2      19.462 -11.088  -8.986  0.00  0.00
      86             : \endverbatim
      87             : Second file:
      88             : \verbatim
      89             : ATOM      2  CH3 ACE     1      12.932 -14.718  -6.016  1.00  1.00
      90             : ATOM      5  C   ACE     1      21.312  -9.928  -5.946  1.00  1.00
      91             : \endverbatim
      92             : However notice that many extra atoms with zero weight might slow down the calculation, so
      93             : removing lines is better than setting their weights to zero.
      94             : In addition, weights for alignment need not to be equivalent to weights for displacement.
      95             : Starting with PLUMED 2.7, if all the weights are set to zero they will be normalized to be equal to the
      96             : inverse of the number of involved atoms. This means that it will be possible to use files with
      97             : the weight columns set to zero obtaining a meaningful result. In previous PLUMED versions,
      98             : setting all weights to zero was resulting in an error instead.
      99             : 
     100             : 
     101             : \par Systems with more than 100k atoms
     102             : 
     103             : Notice that it very likely does not make any sense to compute the \ref RMSD or any other structural
     104             : deviation __using__ so many atoms. However, if the protein for which you want to compute \ref RMSD
     105             : has atoms with large serial numbers (e.g. because it is located __after__ solvent in the sorted list of atoms)
     106             : you might end up with troubles with the limitations of the PDB format. Indeed, since there are 5
     107             : columns available for atom serial number, this number cannot be larger than 99999.
     108             : In addition, providing \ref MOLINFO with names associated to atoms with a serial larger than 99999 would be impossible.
     109             : 
     110             : Since PLUMED 2.4 we allow [hybrid 36](http://cci.lbl.gov/hybrid_36/) format to be used to specify atom numbers.
     111             : This format is not particularly widespread, but has the nice feature that it provides a one-to-one mapping
     112             : between numbers up to approximately 80 millions and strings with 5 characters, plus it is backward compatible
     113             : for numbers smaller than 100000. This is not true for notations like the hex notation exported by VMD.
     114             : Using the hybrid 36 format, the ATOM records for atom ranging from 99997 to 100002 would read like these:
     115             : \verbatim
     116             : ATOM  99997  Ar      X   1      45.349  38.631  15.116  1.00  1.00
     117             : ATOM  99998  Ar      X   1      46.189  38.631  15.956  1.00  1.00
     118             : ATOM  99999  Ar      X   1      46.189  39.471  15.116  1.00  1.00
     119             : ATOM  A0000  Ar      X   1      45.349  39.471  15.956  1.00  1.00
     120             : ATOM  A0000  Ar      X   1      45.349  38.631  16.796  1.00  1.00
     121             : ATOM  A0001  Ar      X   1      46.189  38.631  17.636  1.00  1.00
     122             : \endverbatim
     123             : There are tools that can be found to translate from integers to strings and back using hybrid 36 format
     124             : (a simple python script can be found [here](https://sourceforge.net/p/cctbx/code/HEAD/tree/trunk/iotbx/pdb/hybrid_36.py)).
     125             : In addition, as of PLUMED 2.5, we provide a \ref pdbrenumber "command line tool" that can be used to renumber atoms in a PDB file.
     126             : 
     127             : */
     128             : //+ENDPLUMEDOC
     129             : 
     130             : 
     131             : namespace PLMD {
     132             : 
     133           0 : void PDB::setAtomNumbers( const std::vector<AtomNumber>& atoms ) {
     134           0 :   positions.resize( atoms.size() );
     135           0 :   occupancy.resize( atoms.size() );
     136           0 :   beta.resize( atoms.size() );
     137           0 :   numbers.resize( atoms.size() );
     138           0 :   for(unsigned i=0; i<atoms.size(); ++i) {
     139           0 :     numbers[i]=atoms[i];
     140           0 :     beta[i]=1.0;
     141           0 :     occupancy[i]=1.0;
     142             :   }
     143           0 : }
     144             : 
     145           0 : void PDB::setArgumentNames( const std::vector<std::string>& argument_names ) {
     146           0 :   argnames.resize( argument_names.size() );
     147           0 :   std::vector<double> tmp(1,0);
     148           0 :   for(unsigned i=0; i<argument_names.size(); ++i) {
     149             :     argnames[i]=argument_names[i];
     150           0 :     arg_data.insert( std::pair<std::string,std::vector<double> >( argnames[i], tmp ) );
     151             :   }
     152           0 : }
     153             : 
     154        2148 : bool PDB::getArgumentValue( const std::string& name, std::vector<double>& value ) const {
     155             :   std::map<std::string,std::vector<double> >::const_iterator it = arg_data.find(name);
     156        2148 :   if( it!=arg_data.end() ) {
     157        2148 :     if( value.size()!=it->second.size() ) {
     158             :       return false;
     159             :     }
     160        4300 :     for(unsigned i=0; i<value.size(); ++i) {
     161        2152 :       value[i] = it->second[i];
     162             :     }
     163             :     return true;
     164             :   }
     165             :   return false;
     166             : }
     167             : 
     168           0 : void PDB::setAtomPositions( const std::vector<Vector>& pos ) {
     169           0 :   plumed_assert( pos.size()==positions.size() );
     170           0 :   for(unsigned i=0; i<positions.size(); ++i) {
     171           0 :     positions[i]=pos[i];
     172             :   }
     173           0 : }
     174             : 
     175           0 : void PDB::setArgumentValue( const std::string& argname, const double& val ) {
     176             :   // First set the value of the value of the argument in the map
     177           0 :   arg_data.find(argname)->second.resize(1);
     178           0 :   arg_data.find(argname)->second[0] = val;
     179           0 : }
     180             : 
     181             : // bool PDB::hasRequiredProperties( const std::vector<std::string>& inproperties ){
     182             : //   bool hasprop=false;
     183             : //   for(unsigned i=0;i<remark.size();++i){
     184             : //       if( remark[i].find("PROPERTIES=")!=std::string::npos){ hasprop=true; break; }
     185             : //   }
     186             : //   if( !hasprop ){
     187             : //       std::string mypropstr="PROPERTIES=" + inproperties[0];
     188             : //       for(unsigned i=1;i<inproperties.size();++i) mypropstr += "," + inproperties[i];
     189             : //       remark.push_back( mypropstr );
     190             : //   }
     191             : //   // Now check that all required properties are there
     192             : //   for(unsigned i=0;i<inproperties.size();++i){
     193             : //       hasprop=false;
     194             : //       for(unsigned j=0;j<remark.size();++j){
     195             : //           if( remark[j].find(inproperties[i]+"=")!=std::string::npos){ hasprop=true; break; }
     196             : //       }
     197             : //       if( !hasprop ) return false;
     198             : //   }
     199             : //   return true;
     200             : // }
     201             : 
     202           0 : void PDB::addBlockEnd( const unsigned& end ) {
     203           0 :   block_ends.push_back( end );
     204           0 : }
     205             : 
     206           3 : unsigned PDB::getNumberOfAtomBlocks()const {
     207           3 :   return block_ends.size();
     208             : }
     209             : 
     210           6 : const std::vector<unsigned> & PDB::getAtomBlockEnds()const {
     211           6 :   return block_ends;
     212             : }
     213             : 
     214    23595961 : const std::vector<Vector> & PDB::getPositions()const {
     215    23595961 :   return positions;
     216             : }
     217             : 
     218           0 : void PDB::setPositions(const std::vector<Vector> &v ) {
     219           0 :   plumed_assert( v.size()==positions.size() );
     220           0 :   positions=v;
     221           0 : }
     222             : 
     223      154221 : const std::vector<double> & PDB::getOccupancy()const {
     224      154221 :   return occupancy;
     225             : }
     226             : 
     227      152365 : const std::vector<double> & PDB::getBeta()const {
     228      152365 :   return beta;
     229             : }
     230             : 
     231        4620 : void PDB::addRemark( std::vector<std::string>& v1 ) {
     232        4620 :   Tools::parse(v1,"TYPE",mtype);
     233        4620 :   Tools::parseVector(v1,"ARG",argnames);
     234       12565 :   for(unsigned i=0; i<v1.size(); ++i) {
     235        7945 :     if( v1[i].find("=")!=std::string::npos ) {
     236             :       std::size_t eq=v1[i].find_first_of('=');
     237        7286 :       std::string name=v1[i].substr(0,eq);
     238        7286 :       std::string sval=v1[i].substr(eq+1);
     239        7286 :       std::vector<std::string> words=Tools::getWords(sval,"\t\n ,");
     240        7286 :       std::vector<double> val( words.size() );
     241       14580 :       for(unsigned ii=0; ii<val.size(); ++ii) {
     242        7294 :         Tools::convert( words[ii], val[ii] );
     243             :       }
     244        7286 :       arg_data.insert( std::pair<std::string,std::vector<double> >( name, val ) );
     245        7286 :     } else {
     246         659 :       flags.push_back(v1[i]);
     247             :     }
     248             :   }
     249        4620 : }
     250             : 
     251           0 : bool PDB::hasFlag( const std::string& fname ) const {
     252           0 :   for(unsigned i=0; i<flags.size(); ++i) {
     253           0 :     if( flags[i]==fname ) {
     254             :       return true;
     255             :     }
     256             :   }
     257             :   return false;
     258             : }
     259             : 
     260             : 
     261      318050 : const std::vector<AtomNumber> & PDB::getAtomNumbers()const {
     262      318050 :   return numbers;
     263             : }
     264             : 
     265          18 : const Vector & PDB::getBoxAxs()const {
     266          18 :   return BoxXYZ;
     267             : }
     268             : 
     269           3 : const Vector & PDB::getBoxAng()const {
     270           3 :   return BoxABG;
     271             : }
     272             : 
     273          28 : const Tensor & PDB::getBoxVec()const {
     274          28 :   return Box;
     275             : }
     276             : 
     277          16 : const std::string & PDB::getSpaceGroup()const {
     278          16 :   return spaceGroup;
     279             : }
     280             : 
     281     8575206 : std::string PDB::getAtomName(AtomNumber a)const {
     282             :   const auto p=number2index.find(a);
     283     8575206 :   if(p==number2index.end()) {
     284             :     std::string num;
     285           0 :     Tools::convert( a.serial(), num );
     286           0 :     plumed_merror("Name of atom " + num + " not found" );
     287             :   } else {
     288     8575206 :     return atomsymb[p->second];
     289             :   }
     290             : }
     291             : 
     292      175769 : int PDB::getResidueNumber(AtomNumber a)const {
     293             :   const auto p=number2index.find(a);
     294      175769 :   if(p==number2index.end()) {
     295             :     std::string num;
     296           0 :     Tools::convert( a.serial(), num );
     297           0 :     plumed_merror("Residue for atom " + num + " not found" );
     298             :   } else {
     299      175769 :     return residue[p->second];
     300             :   }
     301             : }
     302             : 
     303      252980 : std::string PDB::getResidueName(AtomNumber a) const {
     304             :   const auto p=number2index.find(a);
     305      252980 :   if(p==number2index.end()) {
     306             :     std::string num;
     307           0 :     Tools::convert( a.serial(), num );
     308           0 :     plumed_merror("Residue for atom " + num + " not found" );
     309             :   } else {
     310      252980 :     return residuenames[p->second];
     311             :   }
     312             : }
     313             : 
     314   248924144 : unsigned PDB::size()const {
     315   248924144 :   return positions.size();
     316             : }
     317             : 
     318        5087 : bool PDB::readFromFilepointer(FILE *fp,bool naturalUnits,double scale) {
     319             :   bool file_is_alive=false;
     320        5087 :   if(naturalUnits) {
     321             :     scale=1.0;
     322             :   }
     323             :   std::string line;
     324             :   fpos_t pos;
     325             :   bool between_ters=true;
     326      516785 :   while(Tools::getline(fp,line)) {
     327      516260 :     fgetpos (fp,&pos);
     328     3929523 :     while(line.length()<80) {
     329     3413263 :       line.push_back(' ');
     330             :     }
     331      516260 :     std::string record=line.substr(0,6);
     332      516260 :     std::string serial=line.substr(6,5);
     333      516260 :     std::string atomname=line.substr(12,4);
     334      516260 :     std::string residuename=line.substr(17,3);
     335      516260 :     std::string chainID=line.substr(21,1);
     336      516260 :     std::string resnum=line.substr(22,4);
     337      516260 :     std::string x=line.substr(30,8);
     338      516260 :     std::string y=line.substr(38,8);
     339      516260 :     std::string z=line.substr(46,8);
     340      516260 :     std::string occ=line.substr(54,6);
     341      516260 :     std::string bet=line.substr(60,6);
     342      516260 :     std::string BoxX=line.substr(6,9);
     343      516260 :     std::string BoxY=line.substr(15,9);
     344      516260 :     std::string BoxZ=line.substr(24,9);
     345      516260 :     std::string BoxA=line.substr(33,7);
     346      516260 :     std::string BoxB=line.substr(40,7);
     347      516260 :     std::string BoxG=line.substr(47,7);
     348      516260 :     Tools::trim(record);
     349      516260 :     if(record=="TER") {
     350             :       between_ters=false;
     351         311 :       block_ends.push_back( positions.size() );
     352             :     }
     353      516260 :     if(record=="END") {
     354             :       file_is_alive=true;
     355             :       break;
     356             :     }
     357      511864 :     if(record=="ENDMDL") {
     358             :       file_is_alive=true;
     359             :       break;
     360             :     }
     361      511699 :     if(record=="REMARK") {
     362        4620 :       std::vector<std::string> v1;
     363        9240 :       v1=Tools::getWords(line.substr(6));
     364        4620 :       addRemark( v1 );
     365        4620 :     }
     366      511699 :     if(record=="CRYST1") {
     367         112 :       Tools::convert(BoxX,BoxXYZ[0]);
     368         112 :       Tools::convert(BoxY,BoxXYZ[1]);
     369         112 :       Tools::convert(BoxZ,BoxXYZ[2]);
     370         112 :       Tools::convert(BoxA,BoxABG[0]);
     371         112 :       Tools::convert(BoxB,BoxABG[1]);
     372         112 :       Tools::convert(BoxG,BoxABG[2]);
     373         112 :       spaceGroup=line.substr(55,11);
     374         112 :       Tools::trim(spaceGroup);
     375             :       BoxXYZ*=scale;
     376         112 :       double cosA=std::cos(BoxABG[0]*pi/180.);
     377         112 :       double cosB=std::cos(BoxABG[1]*pi/180.);
     378         112 :       double cosG=std::cos(BoxABG[2]*pi/180.);
     379         112 :       double sinG=std::sin(BoxABG[2]*pi/180.);
     380         448 :       for (unsigned i=0; i<3; i++) {
     381         336 :         Box[i][0]=0.;
     382         336 :         Box[i][1]=0.;
     383         336 :         Box[i][2]=0.;
     384             :       }
     385         112 :       Box[0][0]=BoxXYZ[0];
     386         112 :       Box[1][0]=BoxXYZ[1]*cosG;
     387         112 :       Box[1][1]=BoxXYZ[1]*sinG;
     388         112 :       Box[2][0]=BoxXYZ[2]*cosB;
     389         112 :       Box[2][1]=(BoxXYZ[2]*BoxXYZ[1]*cosA-Box[2][0]*Box[1][0])/Box[1][1];
     390         112 :       Box[2][2]=std::sqrt(BoxXYZ[2]*BoxXYZ[2]-Box[2][0]*Box[2][0]-Box[2][1]*Box[2][1]);
     391             :     }
     392      511699 :     if(record=="ATOM" || record=="HETATM") {
     393             :       between_ters=true;
     394             :       AtomNumber a;
     395      504907 :       int resno=0; // GB: when resnum string is not present, we set res number to zero
     396             :       double o,b;
     397      504907 :       Vector p;
     398             :       {
     399             :         int result;
     400      504907 :         auto trimmed=serial;
     401      504907 :         Tools::trim(trimmed);
     402      504969 :         while(trimmed.length()<5) {
     403          62 :           trimmed = std::string(" ") + trimmed;
     404             :         }
     405      504907 :         const char* errmsg = h36::hy36decode(5, trimmed.c_str(),trimmed.length(), &result);
     406      504907 :         if(errmsg) {
     407           0 :           std::string msg(errmsg);
     408           0 :           plumed_merror(msg);
     409             :         }
     410      504907 :         a.setSerial(result);
     411             :       }
     412             : 
     413             :       // allow skipping residue number
     414             :       {
     415      504907 :         auto trimmed=resnum;
     416      504907 :         Tools::trim(trimmed);
     417      504907 :         if(trimmed.length()>0) {
     418             :           int result;
     419      504907 :           while(trimmed.length()<4) {
     420           0 :             trimmed = std::string(" ") + trimmed;
     421             :           }
     422      504907 :           const char* errmsg = h36::hy36decode(4, trimmed.c_str(),trimmed.length(), &result);
     423      504907 :           if(errmsg) {
     424           0 :             std::string msg(errmsg);
     425           0 :             plumed_merror(msg);
     426             :           }
     427      504907 :           resno=result;
     428             :         }
     429             :       }
     430             : 
     431             :       // a field that cannot be read is an error that shows the line (it was an assertion that showed only the text)
     432     2524531 :       auto readField=[&line](const std::string& field, double& val, const std::string& what) {
     433     2524531 :         if( !Tools::convertNoexcept(field,val) ) {
     434           4 :           plumed_merror("cannot read the " + what + " from the following line of a PDB file: in a PDB file each field must be in its own columns\n"
     435             :                         + line.substr(0,line.find_last_not_of(' ')+1));
     436             :         }
     437     2524530 :       };
     438      504907 :       readField(occ,o,"occupancy (columns 55-60)");
     439      504906 :       readField(bet,b,"beta (columns 61-66)");
     440      504906 :       readField(x,p[0],"x coordinate (columns 31-38)");
     441      504906 :       readField(y,p[1],"y coordinate (columns 39-46)");
     442     1009812 :       readField(z,p[2],"z coordinate (columns 47-54)");
     443             :       // scale into nm
     444             :       p*=scale;
     445      504906 :       numbers.push_back(a);
     446      504906 :       number2index[a]=positions.size();
     447      504906 :       std::size_t startpos=atomname.find_first_not_of(" \t");
     448      504906 :       std::size_t endpos=atomname.find_last_not_of(" \t");
     449      504907 :       atomsymb.push_back( atomname.substr(startpos, endpos-startpos+1) );
     450      504906 :       residue.push_back(resno);
     451      504906 :       chain.push_back(chainID);
     452      504906 :       occupancy.push_back(o);
     453      504906 :       beta.push_back(b);
     454      504906 :       positions.push_back(p);
     455      504906 :       residuenames.push_back(residuename);
     456             :     }
     457             :   }
     458        5086 :   if( between_ters ) {
     459        4844 :     block_ends.push_back( positions.size() );
     460             :   }
     461        5086 :   return file_is_alive;
     462         124 : }
     463             : 
     464         549 : bool PDB::read(const std::string&file,bool naturalUnits,double scale) {
     465         549 :   FILE* fp=std::fopen(file.c_str(),"r");
     466         549 :   if(!fp) {
     467             :     return false;
     468             :   }
     469             : // call fclose when exiting this function
     470             :   auto deleter=[](auto f) {
     471         548 :     std::fclose(f);
     472             :   };
     473             :   std::unique_ptr<FILE,decltype(deleter)> fp_deleter(fp,deleter);
     474         548 :   readFromFilepointer(fp,naturalUnits,scale);
     475             :   return true;
     476             : }
     477             : 
     478         429 : void PDB::getChainNames( std::vector<std::string>& chains ) const {
     479         429 :   chains.resize(0);
     480         429 :   if(chain.empty()) {
     481             :     return;
     482             :   }
     483         429 :   chains.push_back( chain[0] );
     484      648368 :   for(unsigned i=1; i<size(); ++i) {
     485      647939 :     if( chains[chains.size()-1]!=chain[i] ) {
     486         979 :       chains.push_back( chain[i] );
     487             :     }
     488             :   }
     489             : }
     490             : 
     491       11720 : void PDB::getResidueRange( const std::string& chainname, int& res_start, int& res_end, std::string& errmsg ) const {
     492             :   bool inres=false, foundchain=false;
     493    29683704 :   for(unsigned i=0; i<size(); ++i) {
     494    29671984 :     if( chain[i]==chainname ) {
     495    26606314 :       if(!inres) {
     496       11720 :         if(foundchain) {
     497           0 :           errmsg="found second start of chain named " + chainname;
     498             :         }
     499       11720 :         res_start=residue[i];
     500             :       }
     501             :       inres=true;
     502             :       foundchain=true;
     503     3065670 :     } else if( inres && chain[i]!=chainname ) {
     504             :       inres=false;
     505       11010 :       res_end=residue[i-1];
     506             :     }
     507             :   }
     508       11720 :   if(inres) {
     509         710 :     res_end=residue[size()-1];
     510             :   }
     511       11720 : }
     512             : 
     513         782 : std::vector<int> PDB::getResiduesInChain( const std::string& chainname ) const {
     514         782 :   std::vector<int> res;
     515             :   std::set<int> seen;
     516     1889718 :   for(unsigned i=0; i<size(); ++i) {
     517     1888936 :     if( chain[i]==chainname && seen.insert(residue[i]).second ) {
     518       14546 :       res.push_back(residue[i]);
     519             :     }
     520             :   }
     521         782 :   return res;
     522           0 : }
     523             : 
     524         314 : void PDB::getAtomRange( const std::string& chainname, AtomNumber& a_start, AtomNumber& a_end, std::string& errmsg ) const {
     525             :   bool inres=false, foundchain=false;
     526      659346 :   for(unsigned i=0; i<size(); ++i) {
     527      659032 :     if( chain[i]==chainname ) {
     528      135598 :       if(!inres) {
     529         314 :         if(foundchain) {
     530           0 :           errmsg="found second start of chain named " + chainname;
     531             :         }
     532         314 :         a_start=numbers[i];
     533             :       }
     534             :       inres=true;
     535             :       foundchain=true;
     536      523434 :     } else if( inres && chain[i]!=chainname ) {
     537             :       inres=false;
     538         150 :       a_end=numbers[i-1];
     539             :     }
     540             :   }
     541         314 :   if(inres) {
     542         164 :     a_end=numbers[size()-1];
     543             :   }
     544         314 : }
     545             : 
     546           0 : std::string PDB::getResidueName( int resnum ) const {
     547           0 :   for(unsigned i=0; i<size(); ++i) {
     548           0 :     if( residue[i]==resnum ) {
     549           0 :       return residuenames[i];
     550             :     }
     551             :   }
     552             :   std::string num;
     553           0 :   Tools::convert( resnum, num );
     554           0 :   plumed_merror("residue " + num + " not found" );
     555             : }
     556             : 
     557       65098 : std::string PDB::getResidueName(int resnum,const std::string& chainid ) const {
     558    81655662 :   for(unsigned i=0; i<size(); ++i) {
     559    81655662 :     if( residue[i]==resnum && ( chainid=="*" || chain[i]==chainid) ) {
     560       65098 :       return residuenames[i];
     561             :     }
     562             :   }
     563             :   std::string num;
     564           0 :   Tools::convert( resnum, num );
     565           0 :   plumed_merror("residue " + num + " not found in chain " + chainid );
     566             : }
     567             : 
     568             : 
     569           0 : AtomNumber PDB::getNamedAtomFromResidue( const std::string& aname, int resnum ) const {
     570           0 :   for(unsigned i=0; i<size(); ++i) {
     571           0 :     if( residue[i]==resnum && atomsymb[i]==aname ) {
     572           0 :       return numbers[i];
     573             :     }
     574             :   }
     575             :   std::string num;
     576           0 :   Tools::convert( resnum, num );
     577           0 :   plumed_merror("residue " + num + " does not contain an atom named " + aname );
     578             : }
     579             : 
     580       31164 : AtomNumber PDB::getNamedAtomFromResidueAndChain( const std::string& aname, int resnum, const std::string& chainid ) const {
     581    34828884 :   for(unsigned i=0; i<size(); ++i) {
     582    34828884 :     if( residue[i]==resnum && atomsymb[i]==aname && ( chainid=="*" || chain[i]==chainid) ) {
     583       31164 :       return numbers[i];
     584             :     }
     585             :   }
     586             :   std::string num;
     587           0 :   Tools::convert( resnum, num );
     588           0 :   plumed_merror("residue " + num + " from chain " + chainid + " does not contain an atom named " + aname );
     589             : }
     590             : 
     591       37322 : std::vector<AtomNumber> PDB::getAtomsInResidue(int resnum,const std::string& chainid)const {
     592       37322 :   std::vector<AtomNumber> tmp;
     593    98889080 :   for(unsigned i=0; i<size(); ++i) {
     594    98851758 :     if( residue[i]==resnum && ( chainid=="*" || chain[i]==chainid) ) {
     595      571162 :       tmp.push_back(numbers[i]);
     596             :     }
     597             :   }
     598       37322 :   if(tmp.size()==0) {
     599             :     std::string num;
     600           0 :     Tools::convert( resnum, num );
     601           0 :     plumed_merror("Cannot find residue " + num + " from chain " + chainid  );
     602             :   }
     603       37322 :   return tmp;
     604           0 : }
     605             : 
     606           0 : std::vector<AtomNumber> PDB::getAtomsInChain(const std::string& chainid)const {
     607           0 :   std::vector<AtomNumber> tmp;
     608           0 :   for(unsigned i=0; i<size(); ++i) {
     609           0 :     if( chainid=="*" || chain[i]==chainid ) {
     610           0 :       tmp.push_back(numbers[i]);
     611             :     }
     612             :   }
     613           0 :   if(tmp.size()==0) {
     614           0 :     plumed_merror("Cannot find atoms from chain " + chainid  );
     615             :   }
     616           0 :   return tmp;
     617           0 : }
     618             : 
     619           0 : std::string PDB::getChainID(int resnumber) const {
     620           0 :   for(unsigned i=0; i<size(); ++i) {
     621           0 :     if(resnumber==residue[i]) {
     622           0 :       return chain[i];
     623             :     }
     624             :   }
     625           0 :   plumed_merror("Not enough residues in pdb input file");
     626             : }
     627             : 
     628       54282 : std::string PDB::getChainID(AtomNumber a) const {
     629             :   const auto p=number2index.find(a);
     630       54282 :   if(p==number2index.end()) {
     631             :     std::string num;
     632           0 :     Tools::convert( a.serial(), num );
     633           0 :     plumed_merror("Chain for atom " + num + " not found" );
     634             :   }
     635       54282 :   return chain[p->second];
     636             : }
     637             : 
     638           0 : bool PDB::checkForResidue( const std::string& name ) const {
     639           0 :   for(unsigned i=0; i<size(); ++i) {
     640           0 :     if( residuenames[i]==name ) {
     641             :       return true;
     642             :     }
     643             :   }
     644             :   return false;
     645             : }
     646             : 
     647           0 : bool PDB::checkForAtom( const std::string& name ) const {
     648           0 :   for(unsigned i=0; i<size(); ++i) {
     649           0 :     if( atomsymb[i]==name ) {
     650             :       return true;
     651             :     }
     652             :   }
     653             :   return false;
     654             : }
     655             : 
     656        3846 : bool PDB::checkForAtom( AtomNumber a ) const {
     657             :   const auto p=number2index.find(a);
     658        3846 :   return (p!=number2index.end());
     659             : }
     660             : 
     661           0 : Log& operator<<(Log& ostr, const PDB&  pdb) {
     662             :   const std::size_t bufferlen=1000;
     663             :   char buffer[bufferlen];
     664           0 :   for(unsigned i=0; i<pdb.positions.size(); i++) {
     665           0 :     std::snprintf(buffer,bufferlen,"ATOM %3u %8.3f %8.3f %8.3f\n",pdb.numbers[i].serial(),pdb.positions[i][0],pdb.positions[i][1],pdb.positions[i][2]);
     666           0 :     ostr<<buffer;
     667             :   }
     668           0 :   return ostr;
     669             : }
     670             : 
     671        8645 : Vector PDB::getPosition(AtomNumber a)const {
     672             :   const auto p=number2index.find(a);
     673        8645 :   if(p==number2index.end()) {
     674           0 :     plumed_merror("atom not available");
     675             :   } else {
     676        8645 :     return positions[p->second];
     677             :   }
     678             : }
     679             : 
     680           0 : std::vector<std::string> PDB::getArgumentNames()const {
     681           0 :   return argnames;
     682             : }
     683             : 
     684           0 : std::string PDB::getMtype() const {
     685           0 :   return mtype;
     686             : }
     687             : 
     688           0 : void PDB::print( const double& lunits, GenericMolInfo* mymoldat, OFile& ofile, const std::string& fmt ) {
     689           0 :   if( argnames.size()>0 ) {
     690           0 :     ofile.printf("REMARK ARG=%s", argnames[0].c_str() );
     691           0 :     for(unsigned i=1; i<argnames.size(); ++i) {
     692           0 :       ofile.printf(",%s",argnames[i].c_str() );
     693             :     }
     694           0 :     ofile.printf("\n");
     695           0 :     ofile.printf("REMARK ");
     696             :   }
     697             :   std::string descr2;
     698           0 :   if(fmt.find("-")!=std::string::npos) {
     699           0 :     descr2="%s=" + fmt + " ";
     700             :   } else {
     701             :     // This ensures numbers are left justified (i.e. next to the equals sign
     702           0 :     std::size_t psign=fmt.find("%");
     703           0 :     plumed_assert( psign!=std::string::npos );
     704           0 :     descr2="%s=%-" + fmt.substr(psign+1) + " ";
     705             :   }
     706           0 :   for(std::map<std::string,std::vector<double> >::iterator it=arg_data.begin(); it!=arg_data.end(); ++it) {
     707           0 :     ofile.printf( descr2.c_str(),it->first.c_str(), it->second[0] );
     708             :   }
     709           0 :   if( argnames.size()>0 ) {
     710           0 :     ofile.printf("\n");
     711             :   }
     712           0 :   if( !mymoldat ) {
     713           0 :     for(unsigned i=0; i<positions.size(); ++i) {
     714             :       std::array<char,6> at;
     715             :       {
     716           0 :         const char* msg = h36::hy36encode(5,numbers[i].serial(),&at[0]);
     717           0 :         plumed_assert(msg==nullptr) << msg;
     718           0 :         at[5]=0;
     719             :       }
     720             :       std::array<char,5> res;
     721             :       {
     722           0 :         const char* msg = h36::hy36encode(4,i,&res[0]);
     723           0 :         plumed_assert(msg==nullptr) << msg;
     724           0 :         res[4]=0;
     725             :       }
     726           0 :       ofile.printf("ATOM  %s  X   RES  %s    %8.3f%8.3f%8.3f%6.2f%6.2f\n",
     727             :                    &at[0], &res[0],
     728           0 :                    lunits*positions[i][0], lunits*positions[i][1], lunits*positions[i][2],
     729             :                    occupancy[i], beta[i] );
     730             :     }
     731             :   } else {
     732           0 :     for(unsigned i=0; i<positions.size(); ++i) {
     733             :       std::array<char,6> at;
     734             :       {
     735           0 :         const char* msg = h36::hy36encode(5,numbers[i].serial(),&at[0]);
     736           0 :         plumed_assert(msg==nullptr) << msg;
     737           0 :         at[5]=0;
     738             :       }
     739             :       std::array<char,5> res;
     740             :       {
     741           0 :         const char* msg = h36::hy36encode(4,mymoldat->getResidueNumber(numbers[i]),&res[0]);
     742           0 :         plumed_assert(msg==nullptr) << msg;
     743           0 :         res[4]=0;
     744             :       }
     745           0 :       ofile.printf("ATOM  %s %-4s %3s  %s    %8.3f%8.3f%8.3f%6.2f%6.2f\n",
     746           0 :                    &at[0], mymoldat->getAtomName(numbers[i]).c_str(),
     747           0 :                    mymoldat->getResidueName(numbers[i]).c_str(), &res[0],
     748           0 :                    lunits*positions[i][0], lunits*positions[i][1], lunits*positions[i][2],
     749             :                    occupancy[i], beta[i] );
     750             :     }
     751             :   }
     752           0 :   ofile.printf("END\n");
     753           0 : }
     754             : 
     755       74492 : bool PDB::allowedResidue( const std::string& type, const std::string& residuename ) const {
     756       74492 :   if( type=="protein" ) {
     757       74492 :     if(residuename=="ALA") {
     758             :       return true;
     759       70532 :     } else if(residuename=="ARG") {
     760             :       return true;
     761       66052 :     } else if(residuename=="ASN") {
     762             :       return true;
     763       62212 :     } else if(residuename=="ASP") {
     764             :       return true;
     765       58852 :     } else if(residuename=="CYS") {
     766             :       return true;
     767       58592 :     } else if(residuename=="GLN") {
     768             :       return true;
     769       53112 :     } else if(residuename=="GLU") {
     770             :       return true;
     771       50592 :     } else if(residuename=="GLY") {
     772             :       return true;
     773       47072 :     } else if(residuename=="HIS") {
     774             :       return true;
     775       47072 :     } else if(residuename=="ILE") {
     776             :       return true;
     777       42572 :     } else if(residuename=="LEU") {
     778             :       return true;
     779       34592 :     } else if(residuename=="LYS") {
     780             :       return true;
     781       29192 :     } else if(residuename=="MET") {
     782             :       return true;
     783       26232 :     } else if(residuename=="PHE") {
     784             :       return true;
     785       20952 :     } else if(residuename=="PRO") {
     786             :       return true;
     787       16992 :     } else if(residuename=="SER") {
     788             :       return true;
     789       14210 :     } else if(residuename=="THR") {
     790             :       return true;
     791       11600 :     } else if(residuename=="TRP") {
     792             :       return true;
     793       11600 :     } else if(residuename=="TYR") {
     794             :       return true;
     795        8840 :     } else if(residuename=="VAL") {
     796             :       return true;
     797             :     }
     798             : // Terminal groups
     799        3162 :     else if(residuename=="ACE") {
     800             :       return true;
     801        3162 :     } else if(residuename=="NME") {
     802             :       return true;
     803        3162 :     } else if(residuename=="NH2") {
     804             :       return true;
     805             :     }
     806             : // Alternative residue names in common force fields
     807        3162 :     else if(residuename=="GLH") {
     808             :       return true;  // neutral GLU
     809        3162 :     } else if(residuename=="ASH") {
     810             :       return true;  // neutral ASP
     811        3162 :     } else if(residuename=="HID") {
     812             :       return true;  // HIS-D amber
     813        3162 :     } else if(residuename=="HSD") {
     814             :       return true;  // HIS-D charmm
     815        3162 :     } else if(residuename=="HIE") {
     816             :       return true;  // HIS-E amber
     817        2402 :     } else if(residuename=="HSE") {
     818             :       return true;  // HIS-E charmm
     819        2402 :     } else if(residuename=="HIP") {
     820             :       return true;  // HIS-P amber
     821        2402 :     } else if(residuename=="HSP") {
     822             :       return true;  // HIS-P charmm
     823        2402 :     } else if(residuename=="CYX") {
     824             :       return true;  // disulfide bridge CYS
     825             :     }
     826             : // Weird amino acids
     827        2402 :     else if(residuename=="NLE") {
     828             :       return true;
     829        2402 :     } else if(residuename=="SFO") {
     830             :       return true;
     831             :     } else {
     832        2402 :       return false;
     833             :     }
     834           0 :   } else if( type=="dna" ) {
     835           0 :     if(residuename=="A") {
     836             :       return true;
     837           0 :     } else if(residuename=="A5") {
     838             :       return true;
     839           0 :     } else if(residuename=="A3") {
     840             :       return true;
     841           0 :     } else if(residuename=="AN") {
     842             :       return true;
     843           0 :     } else if(residuename=="G") {
     844             :       return true;
     845           0 :     } else if(residuename=="G5") {
     846             :       return true;
     847           0 :     } else if(residuename=="G3") {
     848             :       return true;
     849           0 :     } else if(residuename=="GN") {
     850             :       return true;
     851           0 :     } else if(residuename=="T") {
     852             :       return true;
     853           0 :     } else if(residuename=="T5") {
     854             :       return true;
     855           0 :     } else if(residuename=="T3") {
     856             :       return true;
     857           0 :     } else if(residuename=="TN") {
     858             :       return true;
     859           0 :     } else if(residuename=="C") {
     860             :       return true;
     861           0 :     } else if(residuename=="C5") {
     862             :       return true;
     863           0 :     } else if(residuename=="C3") {
     864             :       return true;
     865           0 :     } else if(residuename=="CN") {
     866             :       return true;
     867           0 :     } else if(residuename=="DA") {
     868             :       return true;
     869           0 :     } else if(residuename=="DA5") {
     870             :       return true;
     871           0 :     } else if(residuename=="DA3") {
     872             :       return true;
     873           0 :     } else if(residuename=="DAN") {
     874             :       return true;
     875           0 :     } else if(residuename=="DG") {
     876             :       return true;
     877           0 :     } else if(residuename=="DG5") {
     878             :       return true;
     879           0 :     } else if(residuename=="DG3") {
     880             :       return true;
     881           0 :     } else if(residuename=="DGN") {
     882             :       return true;
     883           0 :     } else if(residuename=="DT") {
     884             :       return true;
     885           0 :     } else if(residuename=="DT5") {
     886             :       return true;
     887           0 :     } else if(residuename=="DT3") {
     888             :       return true;
     889           0 :     } else if(residuename=="DTN") {
     890             :       return true;
     891           0 :     } else if(residuename=="DC") {
     892             :       return true;
     893           0 :     } else if(residuename=="DC5") {
     894             :       return true;
     895           0 :     } else if(residuename=="DC3") {
     896             :       return true;
     897           0 :     } else if(residuename=="DCN") {
     898             :       return true;
     899             :     } else {
     900           0 :       return false;
     901             :     }
     902           0 :   } else if( type=="rna" ) {
     903           0 :     if(residuename=="A") {
     904             :       return true;
     905           0 :     } else if(residuename=="A5") {
     906             :       return true;
     907           0 :     } else if(residuename=="A3") {
     908             :       return true;
     909           0 :     } else if(residuename=="AN") {
     910             :       return true;
     911           0 :     } else if(residuename=="G") {
     912             :       return true;
     913           0 :     } else if(residuename=="G5") {
     914             :       return true;
     915           0 :     } else if(residuename=="G3") {
     916             :       return true;
     917           0 :     } else if(residuename=="GN") {
     918             :       return true;
     919           0 :     } else if(residuename=="U") {
     920             :       return true;
     921           0 :     } else if(residuename=="U5") {
     922             :       return true;
     923           0 :     } else if(residuename=="U3") {
     924             :       return true;
     925           0 :     } else if(residuename=="UN") {
     926             :       return true;
     927           0 :     } else if(residuename=="C") {
     928             :       return true;
     929           0 :     } else if(residuename=="C5") {
     930             :       return true;
     931           0 :     } else if(residuename=="C3") {
     932             :       return true;
     933           0 :     } else if(residuename=="CN") {
     934             :       return true;
     935           0 :     } else if(residuename=="RA") {
     936             :       return true;
     937           0 :     } else if(residuename=="RA5") {
     938             :       return true;
     939           0 :     } else if(residuename=="RA3") {
     940             :       return true;
     941           0 :     } else if(residuename=="RAN") {
     942             :       return true;
     943           0 :     } else if(residuename=="RG") {
     944             :       return true;
     945           0 :     } else if(residuename=="RG5") {
     946             :       return true;
     947           0 :     } else if(residuename=="RG3") {
     948             :       return true;
     949           0 :     } else if(residuename=="RGN") {
     950             :       return true;
     951           0 :     } else if(residuename=="RU") {
     952             :       return true;
     953           0 :     } else if(residuename=="RU5") {
     954             :       return true;
     955           0 :     } else if(residuename=="RU3") {
     956             :       return true;
     957           0 :     } else if(residuename=="RUN") {
     958             :       return true;
     959           0 :     } else if(residuename=="RC") {
     960             :       return true;
     961           0 :     } else if(residuename=="RC5") {
     962             :       return true;
     963           0 :     } else if(residuename=="RC3") {
     964             :       return true;
     965           0 :     } else if(residuename=="RCN") {
     966             :       return true;
     967             :     } else {
     968           0 :       return false;
     969             :     }
     970           0 :   } else if( type=="water" ) {
     971           0 :     if(residuename=="SOL") {
     972             :       return true;
     973             :     }
     974           0 :     if(residuename=="WAT") {
     975             :       return true;
     976             :     }
     977           0 :     return false;
     978           0 :   } else if( type=="ion" ) {
     979           0 :     if(residuename=="IB+") {
     980             :       return true;
     981             :     }
     982           0 :     if(residuename=="CA") {
     983             :       return true;
     984             :     }
     985           0 :     if(residuename=="CL") {
     986             :       return true;
     987             :     }
     988           0 :     if(residuename=="NA") {
     989             :       return true;
     990             :     }
     991           0 :     if(residuename=="MG") {
     992             :       return true;
     993             :     }
     994           0 :     if(residuename=="K") {
     995             :       return true;
     996             :     }
     997           0 :     if(residuename=="RB") {
     998             :       return true;
     999             :     }
    1000           0 :     if(residuename=="CS") {
    1001             :       return true;
    1002             :     }
    1003           0 :     if(residuename=="LI") {
    1004             :       return true;
    1005             :     }
    1006           0 :     if(residuename=="ZN") {
    1007             :       return true;
    1008             :     }
    1009           0 :     return false;
    1010             :   }
    1011             :   return false;
    1012             : }
    1013             : 
    1014             : }
    1015             : 

Generated by: LCOV version 1.16