LCOV - code coverage report
Current view: top level - generic - DumpPDB.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 136 148 91.9 %
Date: 2026-11-05 10:19:04 Functions: 7 8 87.5 %

          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 "core/ActionWithArguments.h"
      23             : #include "core/ActionWithValue.h"
      24             : #include "core/ActionAtomistic.h"
      25             : #include "core/ActionPilot.h"
      26             : #include "core/ActionRegister.h"
      27             : #include "core/PlumedMain.h"
      28             : #include "tools/OFile.h"
      29             : #include "tools/h36.h"
      30             : 
      31             : namespace PLMD {
      32             : namespace generic {
      33             : 
      34             : //+PLUMEDOC PRINTANALYSIS DUMPPDB
      35             : /*
      36             : Output PDB file.
      37             : 
      38             : This command can be used to output a PDB file that contains the result from a dimensionality reduction
      39             : calculation.  To understand how to use this command you are best off looking at the examples of dimensionality
      40             : reduction calculations that are explained in the documentation of the actions in the [dimred](module_dimred.md) module.
      41             : 
      42             : Please note that this command __cannot__ be used in place of [DUMPATOMS](DUMPATOMS.md) to output a pdb file rather than
      43             : a gro or xyz file.  This is an example where this command is used to output atomic positions:
      44             : 
      45             : ```plumed
      46             : ff: COLLECT_FRAMES ATOMS=1-22 STRIDE=1
      47             : DUMPPDB ATOM_INDICES=1-22 ATOMS=ff_data FILE=pos.pdb
      48             : ```
      49             : 
      50             : From this example alone it is clear that this command works very differently to the [DUMPATOMS](DUMPATOMS.md) command.
      51             : 
      52             : As indcated below, you can specify what should appear in the title line of your PDB file as well as the residue indices, occupancies
      53             : and beta values for each of the collected atoms.
      54             : 
      55             : ```plumed
      56             : ff: COLLECT_FRAMES ATOMS=1-4 STRIDE=1 CLEAR=100
      57             : DUMPPDB ...
      58             :   ATOM_INDICES=1-4 ATOMS=ff_data
      59             :   DESCRIPTION=title
      60             :   RESIDUE_INDICES=1,1,2,2
      61             :   OCCUPANCY=1,0.5,0.5,0.75
      62             :   BETA=0.5,1,0.2,0.6
      63             :   FILE=pos.pdb
      64             :   STRIDE=100
      65             : ...
      66             : ```
      67             : 
      68             : Notice, furthermore, that we store 100 and output 100 frame blocks of data here from the trajectory.  The input above will thus output
      69             : multiple pdb files.
      70             : 
      71             : Notice, furthermore, that you can output the positions of atoms along with the argument values that correspond to each
      72             : set of atomic positions as follows:
      73             : 
      74             : ```plumed
      75             : # Calculate the distance between atoms 1 and 2
      76             : d: DISTANCE ATOMS=1,2
      77             : # Collect the distance between atoms 1 and 2
      78             : cd: COLLECT ARG=d
      79             : # Calculate the angle involving atoms 1, 2 and 3
      80             : a: ANGLE ATOMS=1,2,3
      81             : # Collect the angle involving atoms 1, 2 and 3
      82             : ca: COLLECT ARG=a
      83             : # Now collect the positions
      84             : ff: COLLECT_FRAMES ATOMS=1-22 STRIDE=1
      85             : # Output a PDB file that contains the collected atomic positions
      86             : # and the collected distances and angles
      87             : DUMPPDB ATOM_INDICES=1-22 ATOMS=ff_data ARG=cd,ca FILE=pos.pdb
      88             : ```
      89             : 
      90             : The outputted pdb file has the format described in the documentation for the [PDB2CONSTANT](PDB2CONSTANT.md) action.
      91             : The names that are used for each of the arguments in the pdb file are the same as the labels of the values in the input above (`d` and `a` in the above case).
      92             : If for any reason you want to use different names for each of the arguments in the PDB file you can use the `ARG_NAMES` keyword as
      93             : shown below:
      94             : 
      95             : ```plumed
      96             : # Calculate the distance between atoms 1 and 2
      97             : d: DISTANCE ATOMS=1,2
      98             : # Collect the distance between atoms 1 and 2
      99             : cd: COLLECT ARG=d
     100             : # Calculate the angle involving atoms 1, 2 and 3
     101             : a: ANGLE ATOMS=1,2,3
     102             : # Collect the angle involving atoms 1, 2 and 3
     103             : ca: COLLECT ARG=a
     104             : # Now collect the positions
     105             : ff: COLLECT_FRAMES ATOMS=1-22 STRIDE=1
     106             : # Output a PDB file that contains the collected atomic positions
     107             : # and the collected distances and angles
     108             : DUMPPDB ATOM_INDICES=1-22 ATOMS=ff_data ARG=cd,ca ARG_NAMES=m,n FILE=pos.pdb
     109             : ```
     110             : 
     111             : The distance will be shown with the name `m` for the input above while the angle will have the name `n`.
     112             : 
     113             : */
     114             : //+ENDPLUMEDOC
     115             : 
     116             : class DumpPDB :
     117             :   public ActionWithArguments,
     118             :   public ActionPilot {
     119             :   std::string fmt;
     120             :   std::string file;
     121             :   std::string description;
     122             :   std::vector<unsigned> pdb_resid_indices;
     123             :   std::vector<double> beta, occupancy;
     124             :   std::vector<std::string> argnames;
     125             :   std::vector<AtomNumber> pdb_atom_indices;
     126             :   void buildArgnames();
     127             :   void printAtom( OFile& opdbf, const unsigned& anum, const unsigned& rnum, const Vector& pos, const double& m, const double& q ) const ;
     128             : public:
     129             :   static void registerKeywords( Keywords& keys );
     130             :   explicit DumpPDB(const ActionOptions&);
     131          13 :   void calculate() override {}
     132          13 :   void apply() override {}
     133             :   void update() override ;
     134             : };
     135             : 
     136             : PLUMED_REGISTER_ACTION(DumpPDB,"DUMPPDB")
     137             : 
     138          25 : void DumpPDB::registerKeywords( Keywords& keys ) {
     139          25 :   Action::registerKeywords( keys );
     140          25 :   ActionPilot::registerKeywords( keys );
     141          25 :   ActionWithArguments::registerKeywords( keys );
     142          50 :   keys.addInputKeyword("optional","ARG","vector/matrix","the values that are being output in the PDB file");
     143          25 :   keys.add("optional","ATOMS","value containing positions of atoms that should be output");
     144          25 :   keys.add("compulsory","STRIDE","0","the frequency with which the atoms should be output");
     145          25 :   keys.add("hidden","FMT","the format that should be used to output real numbers in the title");
     146          25 :   keys.add("compulsory","FILE","the name of the file on which to output these quantities");
     147          25 :   keys.add("compulsory","OCCUPANCY","1.0","vector of values to output in the occupancy column of the pdb file");
     148          25 :   keys.add("compulsory","BETA","1.0","vector of values to output in the beta column of the pdb file");
     149          25 :   keys.add("optional","DESCRIPTION","the title to use for your PDB output");
     150          25 :   keys.add("optional","ATOM_INDICES","the indices of the atoms in your PDB output");
     151          25 :   keys.add("optional","RESIDUE_INDICES","the indices of the residues in your PDB output");
     152          25 :   keys.add("optional","ARG_NAMES","the names of the arguments that are being output");
     153          25 : }
     154             : 
     155          16 : DumpPDB::DumpPDB(const ActionOptions&ao):
     156             :   Action(ao),
     157             :   ActionWithArguments(ao),
     158          16 :   ActionPilot(ao) {
     159          32 :   parse("FILE",file);
     160          16 :   if(file.length()==0) {
     161           0 :     error("name out output file was not specified");
     162             :   }
     163          16 :   std::vector<std::string> atoms;
     164          32 :   parseVector("ATOMS",atoms);
     165          16 :   if( atoms.size()>0 ) {
     166           9 :     std::vector<Value*> atom_args;
     167           9 :     interpretArgumentList( atoms, plumed.getActionSet(), this, atom_args );
     168           9 :     if( atom_args.size()!=1 ) {
     169           0 :       error("only one action should appear in input to atom args");
     170             :     }
     171           9 :     std::vector<Value*> args( getArguments() );
     172           9 :     args.push_back( atom_args[0] );
     173           9 :     requestArguments( args );
     174           9 :     std::vector<std::string> indices;
     175           9 :     parseVector("ATOM_INDICES",indices);
     176           9 :     std::vector<Value*> xvals,yvals,zvals,masv,chargev;
     177           9 :     ActionAtomistic::getAtomValuesFromPlumedObject(plumed,xvals,yvals,zvals,masv,chargev);
     178           9 :     ActionAtomistic::interpretAtomList( indices, xvals, this, pdb_atom_indices );
     179           9 :     log.printf("  printing atoms : ");
     180         167 :     for(unsigned i=0; i<indices.size(); ++i) {
     181         158 :       log.printf("%d ", pdb_atom_indices[i].serial() );
     182             :     }
     183           9 :     log.printf("\n");
     184           9 :     if( pdb_atom_indices.size()!=atom_args[0]->getShape()[1]/3 ) {
     185           0 :       error("mismatch between size of matrix containing positions and vector of atom indices");
     186             :     }
     187           9 :     beta.resize( atom_args[0]->getShape()[1]/3 );
     188           9 :     occupancy.resize( atom_args[0]->getShape()[1]/3 );
     189           9 :     parseVector("OCCUPANCY", occupancy );
     190           9 :     parseVector("BETA", beta );
     191          18 :     parseVector("RESIDUE_INDICES",pdb_resid_indices);
     192           9 :     if( pdb_resid_indices.size()==0 ) {
     193           5 :       pdb_resid_indices.resize( pdb_atom_indices.size() );
     194         111 :       for(unsigned i=0; i<pdb_resid_indices.size(); ++i) {
     195         106 :         pdb_resid_indices[i] = pdb_atom_indices[i].serial();
     196             :       }
     197           4 :     } else if( pdb_resid_indices.size()!=pdb_atom_indices.size() ) {
     198           0 :       error("mismatch between number of atom indices provided and number of residue indices provided");
     199             :     }
     200           9 :   }
     201          16 :   log.printf("  printing configurations to PDB file to file named %s \n", file.c_str() );
     202          16 :   if( getNumberOfArguments()==0 ) {
     203           0 :     error("nothing to print: use ARG and/or ATOMS");
     204             :   }
     205          32 :   parseVector("ARG_NAMES",argnames);
     206          16 :   if( argnames.size()==0 ) {
     207          15 :     buildArgnames();
     208             :   }
     209          16 :   fmt="%f";
     210          16 :   parse("FMT",fmt);
     211          16 :   fmt=" "+fmt;
     212          16 :   if( getStride()==0 ) {
     213           8 :     setStride(0);
     214           8 :     log.printf("  printing pdb at end of calculation \n");
     215             :   }
     216             : 
     217          32 :   parse("DESCRIPTION",description);
     218          16 :   if( getPntrToArgument(0)->getRank()==0 || getPntrToArgument(0)->hasDerivatives() ) {
     219           0 :     error("argument for printing of PDB should be vector or matrix");
     220             :   }
     221          16 :   unsigned nv=getPntrToArgument(0)->getShape()[0];
     222          46 :   for(unsigned i=0; i<getNumberOfArguments(); ++i) {
     223          30 :     if( getPntrToArgument(i)->getRank()==0 || getPntrToArgument(i)->hasDerivatives() ) {
     224           0 :       error("argument for printing of PDB should be vector or matrix");
     225             :     }
     226          30 :     if( getPntrToArgument(i)->getShape()[0]!=nv ) {
     227           0 :       error("for printing to pdb files all arguments must have same number of values");
     228             :     }
     229             :   }
     230          16 : }
     231             : 
     232        2845 : void DumpPDB::printAtom( OFile& opdbf, const unsigned& anum, const unsigned& rnum, const Vector& pos, const double& m, const double& q ) const {
     233        2845 :   if( rnum>999 ) {
     234           0 :     plumed_merror("atom number too large to be used as residue number");
     235             :   }
     236             :   std::array<char,6> at;
     237        2845 :   const char* msg = h36::hy36encode(5,anum,&at[0]);
     238        2845 :   plumed_assert(msg==nullptr) << msg;
     239        2845 :   at[5]=0;
     240        2845 :   double lunits = plumed.getUnits().getLength()/0.1;
     241        2845 :   opdbf.printf("ATOM  %s  X   RES  %4u    %8.3f%8.3f%8.3f%6.2f%6.2f\n",
     242             :                &at[0], rnum, lunits*pos[0], lunits*pos[1], lunits*pos[2], m, q );
     243        2845 : }
     244             : 
     245          15 : void DumpPDB::buildArgnames() {
     246          15 :   unsigned nargs = getNumberOfArguments();
     247          15 :   if( pdb_atom_indices.size()>0 ) {
     248           9 :     nargs = nargs - 1;
     249             :   }
     250             : 
     251          15 :   argnames.resize(0);
     252          15 :   unsigned nvals = getPntrToArgument(0)->getShape()[0];
     253             :   if( getPntrToArgument(0)->getRank()==2 ) {
     254             :     nvals = getPntrToArgument(0)->getShape()[0];
     255             :   }
     256          35 :   for(unsigned i=0; i<nargs; ++i) {
     257          20 :     if( getPntrToArgument(i)->getShape()[0]!=nvals ) {
     258           0 :       error("all arguments should have same number of values");
     259             :     }
     260          20 :     if( getPntrToArgument(i)->getRank()==1 ) {
     261          14 :       argnames.push_back( getPntrToArgument(i)->getName() );
     262           6 :     } else if( getPntrToArgument(i)->getRank()==2 ) {
     263           6 :       (getPntrToArgument(i)->getPntrToAction())->getMatrixColumnTitles( argnames );
     264             :     }
     265             :   }
     266          15 : }
     267             : 
     268          25 : void DumpPDB::update() {
     269          25 :   OFile opdbf;
     270          25 :   opdbf.link(*this);
     271          25 :   opdbf.setBackupString("analysis");
     272          25 :   opdbf.open( file );
     273          25 :   std::size_t psign=fmt.find("%");
     274          25 :   plumed_assert( psign!=std::string::npos );
     275          25 :   std::string descr2="%s=%-" + fmt.substr(psign+1) + " ";
     276             : 
     277             :   // the positions of the atoms (if any) are the last argument
     278          25 :   unsigned nargs = getNumberOfArguments(), atomarg=0;
     279          25 :   if( pdb_atom_indices.size()>0 ) {
     280          15 :     atomarg = nargs - 1;
     281             :     nargs = nargs - 1;
     282             :   }
     283             :   unsigned totargs = 0;
     284          25 :   unsigned nvals = getPntrToArgument(0)->getShape()[0];
     285          68 :   for(unsigned i=0; i<getNumberOfArguments(); ++i) {
     286          43 :     if( getPntrToArgument(i)->getShape()[0]!=nvals ) {
     287           0 :       error("all arguments should have same number of values");
     288             :     }
     289             :   }
     290             :   // the names of the arguments (but not of the positions) are rebuilt if the shapes changed
     291          53 :   for(unsigned i=0; i<nargs; ++i) {
     292          28 :     if( getPntrToArgument(i)->getRank()==1 ) {
     293          19 :       totargs += 1;
     294           9 :     } else if( getPntrToArgument(i)->getRank()==2 ) {
     295           9 :       totargs += getPntrToArgument(i)->getShape()[1];
     296             :     }
     297             :   }
     298          25 :   if( totargs!=argnames.size() ) {
     299           0 :     buildArgnames();
     300             :   }
     301             : 
     302          25 :   if( description.length()>0 ) {
     303          17 :     opdbf.printf("# %s AT STEP %lld TIME %f \n", description.c_str(), getStep(), getTime() );
     304             :   }
     305         508 :   for(unsigned i=0; i<nvals; ++i) {
     306             :     unsigned n=0;
     307        1323 :     for(unsigned j=0; j<nargs; j++) {
     308             :       Value* thisarg=getPntrToArgument(j);
     309         840 :       opdbf.printf("REMARK ");
     310         840 :       if( thisarg->getRank()==1 ) {
     311         405 :         opdbf.printf( descr2.c_str(), argnames[n].c_str(), thisarg->get(i) );
     312         405 :         n++;
     313         435 :       } else if( thisarg->getRank()==2 ) {
     314         435 :         unsigned ncols = thisarg->getShape()[1];
     315        1297 :         for(unsigned k=0; k<ncols; ++k) {
     316         862 :           opdbf.printf( descr2.c_str(), argnames[n].c_str(), thisarg->get(i*ncols+k) );
     317         862 :           n++;
     318             :         }
     319             :       }
     320         840 :       opdbf.printf("\n");
     321             :     }
     322         483 :     if( pdb_atom_indices.size()>0 ) {
     323         192 :       unsigned npos = pdb_atom_indices.size();
     324         192 :       Vector pos;
     325        3037 :       for(unsigned k=0; k<npos; ++k) {
     326        2845 :         pos[0]=getPntrToArgument(atomarg)->get(npos*(3*i+0) + k);
     327        2845 :         pos[1]=getPntrToArgument(atomarg)->get(npos*(3*i+1) + k);
     328        2845 :         pos[2]=getPntrToArgument(atomarg)->get(npos*(3*i+2) + k);
     329        2845 :         printAtom( opdbf, pdb_atom_indices[k].serial(), pdb_resid_indices[k], pos, occupancy[k], beta[k] );
     330             :       }
     331             :     }
     332         483 :     opdbf.printf("END\n");
     333             :   }
     334          25 :   opdbf.close();
     335          25 : }
     336             : 
     337             : }
     338             : }

Generated by: LCOV version 1.16