LCOV - code coverage report
Current view: top level - generic - DumpPDB.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 129 139 92.8 %
Date: 2025-12-04 11:19:34 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           7 :   void calculate() override {}
     132           7 :   void apply() override {}
     133             :   void update() override ;
     134             : };
     135             : 
     136             : PLUMED_REGISTER_ACTION(DumpPDB,"DUMPPDB")
     137             : 
     138          21 : void DumpPDB::registerKeywords( Keywords& keys ) {
     139          21 :   Action::registerKeywords( keys );
     140          21 :   ActionPilot::registerKeywords( keys );
     141          21 :   ActionWithArguments::registerKeywords( keys );
     142          42 :   keys.addInputKeyword("optional","ARG","vector/matrix","the values that are being output in the PDB file");
     143          21 :   keys.add("optional","ATOMS","value containing positions of atoms that should be output");
     144          21 :   keys.add("compulsory","STRIDE","0","the frequency with which the atoms should be output");
     145          21 :   keys.add("hidden","FMT","the format that should be used to output real numbers in the title");
     146          21 :   keys.add("compulsory","FILE","the name of the file on which to output these quantities");
     147          21 :   keys.add("compulsory","OCCUPANCY","1.0","vector of values to output in the occupancy column of the pdb file");
     148          21 :   keys.add("compulsory","BETA","1.0","vector of values to output in the beta column of the pdb file");
     149          21 :   keys.add("optional","DESCRIPTION","the title to use for your PDB output");
     150          21 :   keys.add("optional","ATOM_INDICES","the indices of the atoms in your PDB output");
     151          21 :   keys.add("optional","RESIDUE_INDICES","the indices of the residues in your PDB output");
     152          21 :   keys.add("optional","ARG_NAMES","the names of the arguments that are being output");
     153          21 : }
     154             : 
     155          15 : DumpPDB::DumpPDB(const ActionOptions&ao):
     156             :   Action(ao),
     157             :   ActionWithArguments(ao),
     158          15 :   ActionPilot(ao) {
     159          30 :   parse("FILE",file);
     160          15 :   if(file.length()==0) {
     161           0 :     error("name out output file was not specified");
     162             :   }
     163             :   std::vector<std::string> atoms;
     164          30 :   parseVector("ATOMS",atoms);
     165          15 :   if( atoms.size()>0 ) {
     166             :     std::vector<Value*> atom_args;
     167           8 :     interpretArgumentList( atoms, plumed.getActionSet(), this, atom_args );
     168           8 :     if( atom_args.size()!=1 ) {
     169           0 :       error("only one action should appear in input to atom args");
     170             :     }
     171           8 :     std::vector<Value*> args( getArguments() );
     172           8 :     args.push_back( atom_args[0] );
     173           8 :     requestArguments( args );
     174             :     std::vector<std::string> indices;
     175          16 :     parseVector("ATOM_INDICES",indices);
     176             :     std::vector<Value*> xvals,yvals,zvals,masv,chargev;
     177           8 :     ActionAtomistic::getAtomValuesFromPlumedObject(plumed,xvals,yvals,zvals,masv,chargev);
     178           8 :     ActionAtomistic::interpretAtomList( indices, xvals, this, pdb_atom_indices );
     179           8 :     log.printf("  printing atoms : ");
     180         153 :     for(unsigned i=0; i<indices.size(); ++i) {
     181         145 :       log.printf("%d ", pdb_atom_indices[i].serial() );
     182             :     }
     183           8 :     log.printf("\n");
     184           8 :     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           8 :     beta.resize( atom_args[0]->getShape()[1]/3 );
     188           8 :     occupancy.resize( atom_args[0]->getShape()[1]/3 );
     189           8 :     parseVector("OCCUPANCY", occupancy );
     190           8 :     parseVector("BETA", beta );
     191          16 :     parseVector("RESIDUE_INDICES",pdb_resid_indices);
     192           8 :     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           3 :     } 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           8 :   }
     201          15 :   log.printf("  printing configurations to PDB file to file named %s \n", file.c_str() );
     202          30 :   parseVector("ARG_NAMES",argnames);
     203          15 :   if( argnames.size()==0 ) {
     204          14 :     buildArgnames();
     205             :   }
     206             :   fmt="%f";
     207          15 :   parse("FMT",fmt);
     208          15 :   fmt=" "+fmt;
     209          15 :   if( getStride()==0 ) {
     210           8 :     setStride(0);
     211           8 :     log.printf("  printing pdb at end of calculation \n");
     212             :   }
     213             : 
     214          30 :   parse("DESCRIPTION",description);
     215          15 :   if( getPntrToArgument(0)->getRank()==0 || getPntrToArgument(0)->hasDerivatives() ) {
     216           0 :     error("argument for printing of PDB should be vector or matrix");
     217             :   }
     218          15 :   unsigned nv=getPntrToArgument(0)->getShape()[0];
     219          44 :   for(unsigned i=0; i<getNumberOfArguments(); ++i) {
     220          29 :     if( getPntrToArgument(i)->getRank()==0 || getPntrToArgument(i)->hasDerivatives() ) {
     221           0 :       error("argument for printing of PDB should be vector or matrix");
     222             :     }
     223          29 :     if( getPntrToArgument(i)->getShape()[0]!=nv ) {
     224           0 :       error("for printing to pdb files all arguments must have same number of values");
     225             :     }
     226             :   }
     227          15 : }
     228             : 
     229        2143 : void DumpPDB::printAtom( OFile& opdbf, const unsigned& anum, const unsigned& rnum, const Vector& pos, const double& m, const double& q ) const {
     230        2143 :   if( rnum>999 ) {
     231           0 :     plumed_merror("atom number too large to be used as residue number");
     232             :   }
     233             :   std::array<char,6> at;
     234        2143 :   const char* msg = h36::hy36encode(5,anum,&at[0]);
     235        2143 :   plumed_assert(msg==nullptr) << msg;
     236        2143 :   at[5]=0;
     237        2143 :   double lunits = plumed.getUnits().getLength()/0.1;
     238        2143 :   opdbf.printf("ATOM  %s  X   RES  %4u    %8.3f%8.3f%8.3f%6.2f%6.2f\n",
     239             :                &at[0], rnum, lunits*pos[0], lunits*pos[1], lunits*pos[2], m, q );
     240        2143 : }
     241             : 
     242          23 : void DumpPDB::buildArgnames() {
     243          23 :   unsigned nargs = getNumberOfArguments();
     244          23 :   if( pdb_atom_indices.size()>0 ) {
     245          17 :     nargs = nargs - 1;
     246             :   }
     247             :   if( nargs<0 ) {
     248             :     return;
     249             :   }
     250             : 
     251          23 :   argnames.resize(0);
     252          23 :   unsigned nvals = getPntrToArgument(0)->getShape()[0];
     253             :   if( getPntrToArgument(0)->getRank()==2 ) {
     254             :     nvals = getPntrToArgument(0)->getShape()[0];
     255             :   }
     256          48 :   for(unsigned i=0; i<nargs; ++i) {
     257          25 :     if( getPntrToArgument(i)->getShape()[0]!=nvals ) {
     258           0 :       error("all arguments should have same number of values");
     259             :     }
     260          25 :     if( getPntrToArgument(i)->getRank()==1 ) {
     261          17 :       argnames.push_back( getPntrToArgument(i)->getName() );
     262           8 :     } else if( getPntrToArgument(i)->getRank()==2 ) {
     263           8 :       (getPntrToArgument(i)->getPntrToAction())->getMatrixColumnTitles( argnames );
     264             :     }
     265             :   }
     266             : }
     267             : 
     268          19 : void DumpPDB::update() {
     269          19 :   OFile opdbf;
     270          19 :   opdbf.link(*this);
     271          19 :   opdbf.setBackupString("analysis");
     272          19 :   opdbf.open( file );
     273          19 :   std::size_t psign=fmt.find("%");
     274          19 :   plumed_assert( psign!=std::string::npos );
     275          38 :   std::string descr2="%s=%-" + fmt.substr(psign+1) + " ";
     276             : 
     277             :   unsigned totargs = 0;
     278          19 :   unsigned nvals = getPntrToArgument(0)->getShape()[0];
     279          56 :   for(unsigned i=0; i<getNumberOfArguments(); ++i) {
     280          37 :     if( getPntrToArgument(i)->getShape()[0]!=nvals ) {
     281           0 :       error("all arguments should have same number of values");
     282             :     }
     283          37 :     if( getPntrToArgument(i)->getRank()==1 ) {
     284          19 :       totargs += 1;
     285          18 :     } else if( getPntrToArgument(i)->getRank()==2 ) {
     286          18 :       totargs += getPntrToArgument(i)->getShape()[1];
     287             :     }
     288             :   }
     289          19 :   if( totargs!=argnames.size() ) {
     290           9 :     buildArgnames();
     291             :   }
     292             : 
     293          19 :   if( description.length()>0 ) {
     294          11 :     opdbf.printf("# %s AT STEP %lld TIME %f \n", description.c_str(), getStep(), getTime() );
     295             :   }
     296          19 :   unsigned nargs = getNumberOfArguments(), atomarg=0;
     297          19 :   if( pdb_atom_indices.size()>0 ) {
     298           9 :     if( nargs>1 ) {
     299           3 :       atomarg = nargs - 1;
     300             :       nargs = nargs-1;
     301             :     } else {
     302             :       nargs = 0;
     303             :     }
     304             :   }
     305         448 :   for(unsigned i=0; i<nvals; ++i) {
     306             :     unsigned n=0;
     307        1269 :     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         429 :     if( pdb_atom_indices.size()>0 ) {
     323         138 :       unsigned npos = pdb_atom_indices.size();
     324             :       Vector pos;
     325        2281 :       for(unsigned k=0; k<npos; ++k) {
     326        2143 :         pos[0]=getPntrToArgument(atomarg)->get(npos*(3*i+0) + k);
     327        2143 :         pos[1]=getPntrToArgument(atomarg)->get(npos*(3*i+1) + k);
     328        2143 :         pos[2]=getPntrToArgument(atomarg)->get(npos*(3*i+2) + k);
     329        2143 :         printAtom( opdbf, pdb_atom_indices[k].serial(), pdb_resid_indices[k], pos, occupancy[k], beta[k] );
     330             :       }
     331             :     }
     332         429 :     opdbf.printf("END\n");
     333             :   }
     334          19 :   opdbf.close();
     335          19 : }
     336             : 
     337             : }
     338             : }

Generated by: LCOV version 1.16