LCOV - code coverage report
Current view: top level - tools - PDB.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 239 292 81.8 %
Date: 2024-10-11 08:09:47 Functions: 35 43 81.4 %

          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 <iostream>
      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          27 : void PDB::setAtomNumbers( const std::vector<AtomNumber>& atoms ) {
     134          27 :   positions.resize( atoms.size() ); occupancy.resize( atoms.size() );
     135          27 :   beta.resize( atoms.size() ); numbers.resize( atoms.size() );
     136         210 :   for(unsigned i=0; i<atoms.size(); ++i) { numbers[i]=atoms[i]; beta[i]=1.0; occupancy[i]=1.0; }
     137          27 : }
     138             : 
     139          34 : void PDB::setArgumentNames( const std::vector<std::string>& argument_names ) {
     140          34 :   argnames.resize( argument_names.size() );
     141          98 :   for(unsigned i=0; i<argument_names.size(); ++i) {
     142             :     argnames[i]=argument_names[i];
     143          64 :     arg_data.insert( std::pair<std::string,double>( argnames[i], 0.0 ) );
     144             :   }
     145          34 : }
     146             : 
     147     1504260 : bool PDB::getArgumentValue( const std::string& name, double& value ) const {
     148             :   std::map<std::string,double>::const_iterator it = arg_data.find(name);
     149     1504260 :   if( it!=arg_data.end() ) { value = it->second; return true; }
     150             :   return false;
     151             : }
     152             : 
     153      493287 : void PDB::setAtomPositions( const std::vector<Vector>& pos ) {
     154      493287 :   plumed_assert( pos.size()==positions.size() );
     155      519464 :   for(unsigned i=0; i<positions.size(); ++i) positions[i]=pos[i];
     156      493287 : }
     157             : 
     158     1502798 : void PDB::setArgumentValue( const std::string& argname, const double& val ) {
     159             :   // First set the value of the value of the argument in the map
     160     1502798 :   arg_data.find(argname)->second = val;
     161     1502798 : }
     162             : 
     163             : // bool PDB::hasRequiredProperties( const std::vector<std::string>& inproperties ){
     164             : //   bool hasprop=false;
     165             : //   for(unsigned i=0;i<remark.size();++i){
     166             : //       if( remark[i].find("PROPERTIES=")!=std::string::npos){ hasprop=true; break; }
     167             : //   }
     168             : //   if( !hasprop ){
     169             : //       std::string mypropstr="PROPERTIES=" + inproperties[0];
     170             : //       for(unsigned i=1;i<inproperties.size();++i) mypropstr += "," + inproperties[i];
     171             : //       remark.push_back( mypropstr );
     172             : //   }
     173             : //   // Now check that all required properties are there
     174             : //   for(unsigned i=0;i<inproperties.size();++i){
     175             : //       hasprop=false;
     176             : //       for(unsigned j=0;j<remark.size();++j){
     177             : //           if( remark[j].find(inproperties[i]+"=")!=std::string::npos){ hasprop=true; break; }
     178             : //       }
     179             : //       if( !hasprop ) return false;
     180             : //   }
     181             : //   return true;
     182             : // }
     183             : 
     184          17 : void PDB::addBlockEnd( const unsigned& end ) {
     185          17 :   block_ends.push_back( end );
     186          17 : }
     187             : 
     188         468 : unsigned PDB::getNumberOfAtomBlocks()const {
     189         468 :   return block_ends.size();
     190             : }
     191             : 
     192          14 : const std::vector<unsigned> & PDB::getAtomBlockEnds()const {
     193          14 :   return block_ends;
     194             : }
     195             : 
     196    23533788 : const std::vector<Vector> & PDB::getPositions()const {
     197    23533788 :   return positions;
     198             : }
     199             : 
     200           0 : void PDB::setPositions(const std::vector<Vector> &v ) {
     201           0 :   plumed_assert( v.size()==positions.size() );
     202           0 :   positions=v;
     203           0 : }
     204             : 
     205       37820 : const std::vector<double> & PDB::getOccupancy()const {
     206       37820 :   return occupancy;
     207             : }
     208             : 
     209       37820 : const std::vector<double> & PDB::getBeta()const {
     210       37820 :   return beta;
     211             : }
     212             : 
     213        1339 : void PDB::addRemark( std::vector<std::string>& v1 ) {
     214        1339 :   Tools::parse(v1,"TYPE",mtype);
     215        1339 :   Tools::parseVector(v1,"ARG",argnames);
     216        3759 :   for(unsigned i=0; i<v1.size(); ++i) {
     217        2420 :     if( v1[i].find("=")!=std::string::npos ) {
     218             :       std::size_t eq=v1[i].find_first_of('=');
     219        1864 :       std::string name=v1[i].substr(0,eq);
     220        1864 :       std::string sval=v1[i].substr(eq+1);
     221        1864 :       double val; Tools::convert( sval, val );
     222        1864 :       arg_data.insert( std::pair<std::string,double>( name, val ) );
     223             :     } else {
     224         556 :       flags.push_back(v1[i]);
     225             :     }
     226             :   }
     227        1339 : }
     228             : 
     229           8 : bool PDB::hasFlag( const std::string& fname ) const {
     230           8 :   for(unsigned i=0; i<flags.size(); ++i) {
     231           0 :     if( flags[i]==fname ) return true;
     232             :   }
     233             :   return false;
     234             : }
     235             : 
     236             : 
     237      721528 : const std::vector<AtomNumber> & PDB::getAtomNumbers()const {
     238      721528 :   return numbers;
     239             : }
     240             : 
     241           0 : const Vector & PDB::getBoxAxs()const {
     242           0 :   return BoxXYZ;
     243             : }
     244             : 
     245           0 : const Vector & PDB::getBoxAng()const {
     246           0 :   return BoxABG;
     247             : }
     248             : 
     249          12 : const Tensor & PDB::getBoxVec()const {
     250          12 :   return Box;
     251             : }
     252             : 
     253     6770000 : std::string PDB::getAtomName(AtomNumber a)const {
     254             :   const auto p=number2index.find(a);
     255     6770000 :   if(p==number2index.end()) {
     256           0 :     std::string num; Tools::convert( a.serial(), num );
     257           0 :     plumed_merror("Name of atom " + num + " not found" );
     258     6770000 :   } else return atomsymb[p->second];
     259             : }
     260             : 
     261      166013 : unsigned PDB::getResidueNumber(AtomNumber a)const {
     262             :   const auto p=number2index.find(a);
     263      166013 :   if(p==number2index.end()) {
     264           0 :     std::string num; Tools::convert( a.serial(), num );
     265           0 :     plumed_merror("Residue for atom " + num + " not found" );
     266      166013 :   } else return residue[p->second];
     267             : }
     268             : 
     269      101936 : std::string PDB::getResidueName(AtomNumber a) const {
     270             :   const auto p=number2index.find(a);
     271      101936 :   if(p==number2index.end()) {
     272           0 :     std::string num; Tools::convert( a.serial(), num );
     273           0 :     plumed_merror("Residue for atom " + num + " not found" );
     274      101936 :   } else return residuenames[p->second];
     275             : }
     276             : 
     277   205300635 : unsigned PDB::size()const {
     278   205300635 :   return positions.size();
     279             : }
     280             : 
     281        2045 : bool PDB::readFromFilepointer(FILE *fp,bool naturalUnits,double scale) {
     282             :   //cerr<<file<<endl;
     283             :   bool file_is_alive=false;
     284        2045 :   if(naturalUnits) scale=1.0;
     285             :   std::string line;
     286             :   fpos_t pos; bool between_ters=true;
     287      237748 :   while(Tools::getline(fp,line)) {
     288             :     //cerr<<line<<"\n";
     289      237543 :     fgetpos (fp,&pos);
     290     1342583 :     while(line.length()<80) line.push_back(' ');
     291      237543 :     std::string record=line.substr(0,6);
     292      237543 :     std::string serial=line.substr(6,5);
     293      237543 :     std::string atomname=line.substr(12,4);
     294      237543 :     std::string residuename=line.substr(17,3);
     295      237543 :     std::string chainID=line.substr(21,1);
     296      237543 :     std::string resnum=line.substr(22,4);
     297      237543 :     std::string x=line.substr(30,8);
     298      237543 :     std::string y=line.substr(38,8);
     299      237543 :     std::string z=line.substr(46,8);
     300      237543 :     std::string occ=line.substr(54,6);
     301      237543 :     std::string bet=line.substr(60,6);
     302      237543 :     std::string BoxX=line.substr(6,9);
     303      237543 :     std::string BoxY=line.substr(15,9);
     304      237543 :     std::string BoxZ=line.substr(24,9);
     305      237543 :     std::string BoxA=line.substr(33,7);
     306      237543 :     std::string BoxB=line.substr(40,7);
     307      237543 :     std::string BoxG=line.substr(47,7);
     308      237543 :     Tools::trim(record);
     309      237543 :     if(record=="TER") { between_ters=false; block_ends.push_back( positions.size() ); }
     310      237543 :     if(record=="END") { file_is_alive=true;  break;}
     311      235840 :     if(record=="ENDMDL") { file_is_alive=true;  break;}
     312      235703 :     if(record=="REMARK") {
     313        2678 :       std::vector<std::string> v1;  v1=Tools::getWords(line.substr(6));
     314        1339 :       addRemark( v1 );
     315        1339 :     }
     316      235703 :     if(record=="CRYST1") {
     317          69 :       Tools::convert(BoxX,BoxXYZ[0]);
     318          69 :       Tools::convert(BoxY,BoxXYZ[1]);
     319          69 :       Tools::convert(BoxZ,BoxXYZ[2]);
     320          69 :       Tools::convert(BoxA,BoxABG[0]);
     321          69 :       Tools::convert(BoxB,BoxABG[1]);
     322          69 :       Tools::convert(BoxG,BoxABG[2]);
     323          69 :       BoxXYZ*=scale;
     324          69 :       double cosA=std::cos(BoxABG[0]*pi/180.);
     325          69 :       double cosB=std::cos(BoxABG[1]*pi/180.);
     326          69 :       double cosG=std::cos(BoxABG[2]*pi/180.);
     327          69 :       double sinG=std::sin(BoxABG[2]*pi/180.);
     328         276 :       for (unsigned i=0; i<3; i++) {Box[i][0]=0.; Box[i][1]=0.; Box[i][2]=0.;}
     329          69 :       Box[0][0]=BoxXYZ[0];
     330          69 :       Box[1][0]=BoxXYZ[1]*cosG;
     331          69 :       Box[1][1]=BoxXYZ[1]*sinG;
     332          69 :       Box[2][0]=BoxXYZ[2]*cosB;
     333          69 :       Box[2][1]=(BoxXYZ[2]*BoxXYZ[1]*cosA-Box[2][0]*Box[1][0])/Box[1][1];
     334          69 :       Box[2][2]=std::sqrt(BoxXYZ[2]*BoxXYZ[2]-Box[2][0]*Box[2][0]-Box[2][1]*Box[2][1]);
     335             :     }
     336      237742 :     if(record=="ATOM" || record=="HETATM") {
     337             :       between_ters=true;
     338             :       AtomNumber a;
     339      233664 :       unsigned resno=0; // GB: when resnum string is not present, we set res number to zero
     340             :       double o,b;
     341      233664 :       Vector p;
     342             :       {
     343             :         int result;
     344      233664 :         auto trimmed=serial;
     345      233664 :         Tools::trim(trimmed);
     346      233726 :         while(trimmed.length()<5) trimmed = std::string(" ") + trimmed;
     347      233664 :         const char* errmsg = h36::hy36decode(5, trimmed.c_str(),trimmed.length(), &result);
     348      233664 :         if(errmsg) {
     349           0 :           std::string msg(errmsg);
     350           0 :           plumed_merror(msg);
     351             :         }
     352      233664 :         a.setSerial(result);
     353             :       }
     354             : 
     355             :       // allow skipping residue number
     356             :       {
     357      233664 :         auto trimmed=resnum;
     358      233664 :         Tools::trim(trimmed);
     359      233664 :         if(trimmed.length()>0) {
     360             :           int result;
     361      227024 :           while(trimmed.length()<4) trimmed = std::string(" ") + trimmed;
     362      227024 :           const char* errmsg = h36::hy36decode(4, trimmed.c_str(),trimmed.length(), &result);
     363      227024 :           if(errmsg) {
     364           0 :             std::string msg(errmsg);
     365           0 :             plumed_merror(msg);
     366             :           }
     367      227024 :           resno=result;
     368             :         }
     369             :       }
     370             : 
     371      233664 :       Tools::convert(occ,o);
     372      233664 :       Tools::convert(bet,b);
     373      233664 :       Tools::convert(x,p[0]);
     374      233664 :       Tools::convert(y,p[1]);
     375      233664 :       Tools::convert(z,p[2]);
     376             :       // scale into nm
     377      233664 :       p*=scale;
     378      233664 :       numbers.push_back(a);
     379      233664 :       number2index[a]=positions.size();
     380      233664 :       std::size_t startpos=atomname.find_first_not_of(" \t");
     381      233664 :       std::size_t endpos=atomname.find_last_not_of(" \t");
     382      233664 :       atomsymb.push_back( atomname.substr(startpos, endpos-startpos+1) );
     383      233664 :       residue.push_back(resno);
     384      233664 :       chain.push_back(chainID);
     385      233664 :       occupancy.push_back(o);
     386      233664 :       beta.push_back(b);
     387      233664 :       positions.push_back(p);
     388      233664 :       residuenames.push_back(residuename);
     389             :     }
     390             :   }
     391        2045 :   if( between_ters ) block_ends.push_back( positions.size() );
     392        2045 :   return file_is_alive;
     393             : }
     394             : 
     395         299 : bool PDB::read(const std::string&file,bool naturalUnits,double scale) {
     396         299 :   FILE* fp=fopen(file.c_str(),"r");
     397         299 :   if(!fp) return false;
     398         297 :   readFromFilepointer(fp,naturalUnits,scale);
     399         297 :   fclose(fp);
     400         297 :   return true;
     401             : }
     402             : 
     403         231 : void PDB::getChainNames( std::vector<std::string>& chains ) const {
     404         231 :   chains.resize(0);
     405         231 :   chains.push_back( chain[0] );
     406      464610 :   for(unsigned i=1; i<size(); ++i) {
     407      464379 :     if( chains[chains.size()-1]!=chain[i] ) chains.push_back( chain[i] );
     408             :   }
     409         231 : }
     410             : 
     411       11591 : void PDB::getResidueRange( const std::string& chainname, unsigned& res_start, unsigned& res_end, std::string& errmsg ) const {
     412             :   bool inres=false, foundchain=false;
     413    30269701 :   for(unsigned i=0; i<size(); ++i) {
     414    30258110 :     if( chain[i]==chainname ) {
     415    26604174 :       if(!inres) {
     416       11591 :         if(foundchain) errmsg="found second start of chain named " + chainname;
     417       11591 :         res_start=residue[i];
     418             :       }
     419             :       inres=true; foundchain=true;
     420     3653936 :     } else if( inres && chain[i]!=chainname ) {
     421             :       inres=false;
     422       11090 :       res_end=residue[i-1];
     423             :     }
     424             :   }
     425       11591 :   if(inres) res_end=residue[size()-1];
     426       11591 : }
     427             : 
     428         158 : void PDB::getAtomRange( const std::string& chainname, AtomNumber& a_start, AtomNumber& a_end, std::string& errmsg ) const {
     429             :   bool inres=false, foundchain=false;
     430      432520 :   for(unsigned i=0; i<size(); ++i) {
     431      432362 :     if( chain[i]==chainname ) {
     432       89742 :       if(!inres) {
     433         158 :         if(foundchain) errmsg="found second start of chain named " + chainname;
     434         158 :         a_start=numbers[i];
     435             :       }
     436             :       inres=true; foundchain=true;
     437      342620 :     } else if( inres && chain[i]!=chainname ) {
     438             :       inres=false;
     439          74 :       a_end=numbers[i-1];
     440             :     }
     441             :   }
     442         158 :   if(inres) a_end=numbers[size()-1];
     443         158 : }
     444             : 
     445        5958 : std::string PDB::getResidueName( const unsigned& resnum ) const {
     446     7317600 :   for(unsigned i=0; i<size(); ++i) {
     447     7317600 :     if( residue[i]==resnum ) return residuenames[i];
     448             :   }
     449           0 :   std::string num; Tools::convert( resnum, num );
     450           0 :   plumed_merror("residue " + num + " not found" );
     451             : }
     452             : 
     453       46594 : std::string PDB::getResidueName(const unsigned& resnum,const std::string& chainid ) const {
     454    59112773 :   for(unsigned i=0; i<size(); ++i) {
     455    59159547 :     if( residue[i]==resnum && ( chainid=="*" || chain[i]==chainid) ) return residuenames[i];
     456             :   }
     457           0 :   std::string num; Tools::convert( resnum, num );
     458           0 :   plumed_merror("residue " + num + " not found in chain " + chainid );
     459             : }
     460             : 
     461             : 
     462       13260 : AtomNumber PDB::getNamedAtomFromResidue( const std::string& aname, const unsigned& resnum ) const {
     463    16347180 :   for(unsigned i=0; i<size(); ++i) {
     464    16347180 :     if( residue[i]==resnum && atomsymb[i]==aname ) return numbers[i];
     465             :   }
     466           0 :   std::string num; Tools::convert( resnum, num );
     467           0 :   plumed_merror("residue " + num + " does not contain an atom named " + aname );
     468             : }
     469             : 
     470        2216 : AtomNumber PDB::getNamedAtomFromResidueAndChain( const std::string& aname, const unsigned& resnum, const std::string& chainid ) const {
     471     1070584 :   for(unsigned i=0; i<size(); ++i) {
     472     1072800 :     if( residue[i]==resnum && atomsymb[i]==aname && ( chainid=="*" || chain[i]==chainid) ) return numbers[i];
     473             :   }
     474           0 :   std::string num; Tools::convert( resnum, num );
     475           0 :   plumed_merror("residue " + num + " from chain " + chainid + " does not contain an atom named " + aname );
     476             : }
     477             : 
     478       32150 : std::vector<AtomNumber> PDB::getAtomsInResidue(const unsigned& resnum,const std::string& chainid)const {
     479             :   std::vector<AtomNumber> tmp;
     480    84003186 :   for(unsigned i=0; i<size(); ++i) {
     481    84456920 :     if( residue[i]==resnum && ( chainid=="*" || chain[i]==chainid) ) tmp.push_back(numbers[i]);
     482             :   }
     483       32150 :   if(tmp.size()==0) {
     484           0 :     std::string num; Tools::convert( resnum, num );
     485           0 :     plumed_merror("Cannot find residue " + num + " from chain " + chainid  );
     486             :   }
     487       32150 :   return tmp;
     488             : }
     489             : 
     490           0 : std::vector<AtomNumber> PDB::getAtomsInChain(const std::string& chainid)const {
     491             :   std::vector<AtomNumber> tmp;
     492           0 :   for(unsigned i=0; i<size(); ++i) {
     493           0 :     if( chainid=="*" || chain[i]==chainid ) tmp.push_back(numbers[i]);
     494             :   }
     495           0 :   if(tmp.size()==0) {
     496           0 :     plumed_merror("Cannot find atoms from chain " + chainid  );
     497             :   }
     498           0 :   return tmp;
     499             : }
     500             : 
     501        4638 : std::string PDB::getChainID(const unsigned& resnumber) const {
     502     5689172 :   for(unsigned i=0; i<size(); ++i) {
     503     5689172 :     if(resnumber==residue[i]) return chain[i];
     504             :   }
     505           0 :   plumed_merror("Not enough residues in pdb input file");
     506             : }
     507             : 
     508           0 : std::string PDB::getChainID(AtomNumber a) const {
     509             :   const auto p=number2index.find(a);
     510           0 :   if(p==number2index.end()) {
     511           0 :     std::string num; Tools::convert( a.serial(), num );
     512           0 :     plumed_merror("Chain for atom " + num + " not found" );
     513             :   }
     514           0 :   return chain[p->second];
     515             : }
     516             : 
     517           0 : bool PDB::checkForResidue( const std::string& name ) const {
     518           0 :   for(unsigned i=0; i<size(); ++i) {
     519           0 :     if( residuenames[i]==name ) return true;
     520             :   }
     521             :   return false;
     522             : }
     523             : 
     524           0 : bool PDB::checkForAtom( const std::string& name ) const {
     525           0 :   for(unsigned i=0; i<size(); ++i) {
     526           0 :     if( atomsymb[i]==name ) return true;
     527             :   }
     528             :   return false;
     529             : }
     530             : 
     531           4 : bool PDB::checkForAtom( AtomNumber a ) const {
     532             :   const auto p=number2index.find(a);
     533           4 :   return (p!=number2index.end());
     534             : }
     535             : 
     536           0 : Log& operator<<(Log& ostr, const PDB&  pdb) {
     537             :   char buffer[1000];
     538           0 :   for(unsigned i=0; i<pdb.positions.size(); i++) {
     539           0 :     std::sprintf(buffer,"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]);
     540           0 :     ostr<<buffer;
     541             :   }
     542           0 :   return ostr;
     543             : }
     544             : 
     545         854 : Vector PDB::getPosition(AtomNumber a)const {
     546             :   const auto p=number2index.find(a);
     547         854 :   if(p==number2index.end()) plumed_merror("atom not available");
     548         854 :   else return positions[p->second];
     549             : }
     550             : 
     551     2539432 : std::vector<std::string> PDB::getArgumentNames()const {
     552     2539432 :   return argnames;
     553             : }
     554             : 
     555          10 : std::string PDB::getMtype() const {
     556          10 :   return mtype;
     557             : }
     558             : 
     559         497 : void PDB::print( const double& lunits, GenericMolInfo* mymoldat, OFile& ofile, const std::string& fmt ) {
     560         497 :   if( argnames.size()>0 ) {
     561         476 :     ofile.printf("REMARK ARG=%s", argnames[0].c_str() );
     562        1744 :     for(unsigned i=1; i<argnames.size(); ++i) ofile.printf(",%s",argnames[i].c_str() );
     563         476 :     ofile.printf("\n"); ofile.printf("REMARK ");
     564             :   }
     565             :   std::string descr2;
     566         497 :   if(fmt.find("-")!=std::string::npos) {
     567           0 :     descr2="%s=" + fmt + " ";
     568             :   } else {
     569             :     // This ensures numbers are left justified (i.e. next to the equals sign
     570         497 :     std::size_t psign=fmt.find("%");
     571         497 :     plumed_assert( psign!=std::string::npos );
     572         994 :     descr2="%s=%-" + fmt.substr(psign+1) + " ";
     573             :   }
     574        2241 :   for(std::map<std::string,double>::iterator it=arg_data.begin(); it!=arg_data.end(); ++it) ofile.printf( descr2.c_str(),it->first.c_str(), it->second );
     575         497 :   if( argnames.size()>0 ) ofile.printf("\n");
     576         497 :   if( !mymoldat ) {
     577        2263 :     for(unsigned i=0; i<positions.size(); ++i) {
     578             :       std::array<char,6> at;
     579             :       {
     580        1769 :         const char* msg = h36::hy36encode(5,numbers[i].serial(),&at[0]);
     581        1769 :         plumed_assert(msg==nullptr) << msg;
     582        1769 :         at[5]=0;
     583             :       }
     584             :       std::array<char,5> res;
     585             :       {
     586        1769 :         const char* msg = h36::hy36encode(4,i,&res[0]);
     587        1769 :         plumed_assert(msg==nullptr) << msg;
     588        1769 :         res[4]=0;
     589             :       }
     590        1769 :       ofile.printf("ATOM  %s  X   RES  %s    %8.3f%8.3f%8.3f%6.2f%6.2f\n",
     591             :                    &at[0], &res[0],
     592        1769 :                    lunits*positions[i][0], lunits*positions[i][1], lunits*positions[i][2],
     593             :                    occupancy[i], beta[i] );
     594             :     }
     595             :   } else {
     596          69 :     for(unsigned i=0; i<positions.size(); ++i) {
     597             :       std::array<char,6> at;
     598             :       {
     599          66 :         const char* msg = h36::hy36encode(5,numbers[i].serial(),&at[0]);
     600          66 :         plumed_assert(msg==nullptr) << msg;
     601          66 :         at[5]=0;
     602             :       }
     603             :       std::array<char,5> res;
     604             :       {
     605          66 :         const char* msg = h36::hy36encode(4,mymoldat->getResidueNumber(numbers[i]),&res[0]);
     606          66 :         plumed_assert(msg==nullptr) << msg;
     607          66 :         res[4]=0;
     608             :       }
     609          66 :       ofile.printf("ATOM  %s %-4s %3s  %s    %8.3f%8.3f%8.3f%6.2f%6.2f\n",
     610         132 :                    &at[0], mymoldat->getAtomName(numbers[i]).c_str(),
     611          66 :                    mymoldat->getResidueName(numbers[i]).c_str(), &res[0],
     612          66 :                    lunits*positions[i][0], lunits*positions[i][1], lunits*positions[i][2],
     613             :                    occupancy[i], beta[i] );
     614             :     }
     615             :   }
     616         497 :   ofile.printf("END\n");
     617         497 : }
     618             : 
     619             : 
     620             : }
     621             : 

Generated by: LCOV version 1.15