Line data Source code
1 : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 : Copyright (c) 2014-2019 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 "Colvar.h"
23 : #include "core/Atoms.h"
24 : #include "core/PlumedMain.h"
25 : #include "ActionRegister.h"
26 : #include "tools/PDB.h"
27 : #include "tools/RMSD.h"
28 : #include "tools/Tools.h"
29 : #include <memory>
30 :
31 : using namespace std;
32 :
33 : namespace PLMD {
34 : namespace colvar {
35 :
36 4 : class PCARMSD : public Colvar {
37 :
38 : std::unique_ptr<PLMD::RMSD> rmsd;
39 : bool squared;
40 : bool nopbc;
41 : std::vector< std::vector<Vector> > eigenvectors;
42 : std::vector<PDB> pdbv;
43 : std::vector<string> pca_names;
44 : public:
45 : explicit PCARMSD(const ActionOptions&);
46 : void calculate() override;
47 : static void registerKeywords(Keywords& keys);
48 : };
49 :
50 :
51 : using namespace std;
52 :
53 : //+PLUMEDOC DCOLVAR PCARMSD
54 : /*
55 : Calculate the PCA components ( see \cite Sutto:2010 and \cite spiwok ) for a number of provided eigenvectors and an average structure. Performs optimal alignment at every step and reports the rmsd so you know if you are far or close from the average structure.
56 : It takes the average structure and eigenvectors in form of a pdb.
57 : Note that beta and occupancy values in the pdb are neglected and all the weights are placed to 1 (differently from the RMSD colvar for example)
58 :
59 : \par Examples
60 :
61 : \plumedfile
62 : PCARMSD AVERAGE=file.pdb EIGENVECTORS=eigenvectors.pdb
63 : \endplumedfile
64 :
65 : The input is taken so to be compatible with the output you get from g_covar utility of gromacs (suitably adapted to have a pdb input format).
66 : The reference configuration (file.pdb) will thus be in a file that looks something like this:
67 :
68 : \auxfile{file.pdb}
69 : TITLE Average structure
70 : MODEL 1
71 : ATOM 1 CL ALA 1 1.042 -3.070 0.946 1.00 0.00
72 : ATOM 5 CLP ALA 1 0.416 -2.033 0.132 1.00 0.00
73 : ATOM 6 OL ALA 1 0.415 -2.082 -0.976 1.00 0.00
74 : ATOM 7 NL ALA 1 -0.134 -1.045 0.677 1.00 0.00
75 : ATOM 9 CA ALA 1 -0.774 0.053 0.003 1.00 0.00
76 : TER
77 : ENDMDL
78 : \endauxfile
79 :
80 : while the eigenvectors will be in a pdb file (eigenvectors.pdb) that looks something like this:
81 :
82 : \auxfile{eigenvectors.pdb}
83 : TITLE frame t= -1.000
84 : MODEL 1
85 : ATOM 1 CL ALA 1 1.194 -2.988 0.724 1.00 0.00
86 : ATOM 5 CLP ALA 1 -0.996 0.042 0.144 1.00 0.00
87 : ATOM 6 OL ALA 1 -1.246 -0.178 -0.886 1.00 0.00
88 : ATOM 7 NL ALA 1 -2.296 0.272 0.934 1.00 0.00
89 : ATOM 9 CA ALA 1 -0.436 2.292 0.814 1.00 0.00
90 : TER
91 : ENDMDL
92 : TITLE frame t= 0.000
93 : MODEL 1
94 : ATOM 1 CL ALA 1 1.042 -3.070 0.946 1.00 0.00
95 : ATOM 5 CLP ALA 1 -0.774 0.053 0.003 1.00 0.00
96 : ATOM 6 OL ALA 1 -0.849 -0.166 -1.034 1.00 0.00
97 : ATOM 7 NL ALA 1 -2.176 0.260 0.563 1.00 0.00
98 : ATOM 9 CA ALA 1 0.314 1.825 0.962 1.00 0.00
99 : TER
100 : ENDMDL
101 :
102 : \endauxfile
103 :
104 : */
105 : //+ENDPLUMEDOC
106 :
107 7836 : PLUMED_REGISTER_ACTION(PCARMSD,"PCARMSD")
108 :
109 3 : void PCARMSD::registerKeywords(Keywords& keys) {
110 3 : Colvar::registerKeywords(keys);
111 12 : keys.add("compulsory","AVERAGE","a file in pdb format containing the reference structure and the atoms involved in the CV.");
112 12 : keys.add("compulsory","EIGENVECTORS","a file in pdb format containing the reference structure and the atoms involved in the CV.");
113 12 : keys.addOutputComponent("eig","default","the projections on each eigenvalue are stored on values labeled eig-1, eig-2, ...");
114 12 : keys.addOutputComponent("residual","default","the distance of the present configuration from the configuration supplied as AVERAGE in terms of mean squared displacement after optimal alignment ");
115 9 : keys.addFlag("SQUARED_ROOT",false," This should be set if you want RMSD instead of mean squared displacement ");
116 3 : }
117 :
118 2 : PCARMSD::PCARMSD(const ActionOptions&ao):
119 : PLUMED_COLVAR_INIT(ao),
120 : squared(true),
121 2 : nopbc(false)
122 : {
123 : string f_average;
124 4 : parse("AVERAGE",f_average);
125 : string type;
126 2 : type.assign("OPTIMAL");
127 : string f_eigenvectors;
128 4 : parse("EIGENVECTORS",f_eigenvectors);
129 4 : bool sq; parseFlag("SQUARED_ROOT",sq);
130 2 : if (sq) { squared=false; }
131 4 : parseFlag("NOPBC",nopbc);
132 2 : checkRead();
133 :
134 4 : PDB pdb;
135 :
136 : // read everything in ang and transform to nm if we are not in natural units
137 4 : if( !pdb.read(f_average,plumed.getAtoms().usingNaturalUnits(),0.1/atoms.getUnits().getLength()) )
138 0 : error("missing input file " + f_average );
139 :
140 2 : rmsd.reset( new RMSD() );
141 : bool remove_com=true;
142 : bool normalize_weights=true;
143 : // here align and displace are a simple vector of ones
144 48 : std::vector<double> align; align=pdb.getOccupancy(); for(unsigned i=0; i<align.size(); i++) {align[i]=1.;} ;
145 48 : std::vector<double> displace; displace=pdb.getBeta(); for(unsigned i=0; i<displace.size(); i++) {displace[i]=1.;} ;
146 : // reset again to reimpose unifrom weights (safe to disable this)
147 4 : rmsd->set(align,displace,pdb.getPositions(),type,remove_com,normalize_weights);
148 2 : requestAtoms( pdb.getAtomNumbers() );
149 :
150 6 : addComponentWithDerivatives("residual"); componentIsNotPeriodic("residual");
151 :
152 2 : log.printf(" average from file %s\n",f_average.c_str());
153 2 : log.printf(" which contains %d atoms\n",getNumberOfAtoms());
154 2 : log.printf(" with indices : ");
155 46 : for(unsigned i=0; i<pdb.getAtomNumbers().size(); ++i) {
156 22 : if(i%25==0) log<<"\n";
157 44 : log.printf("%d ",pdb.getAtomNumbers()[i].serial());
158 : }
159 2 : log.printf("\n");
160 2 : log.printf(" method for alignment : %s \n",type.c_str() );
161 2 : if(nopbc) log.printf(" without periodic boundary conditions\n");
162 1 : else log.printf(" using periodic boundary conditions\n");
163 :
164 6 : log<<" Bibliography "<<plumed.cite("Spiwok, Lipovova and Kralova, JPCB, 111, 3073 (2007) ");
165 6 : log<<" "<<plumed.cite( "Sutto, D'Abramo, Gervasio, JCTC, 6, 3640 (2010)");
166 :
167 : // now get the eigenvectors
168 : // open the file
169 2 : FILE* fp=fopen(f_eigenvectors.c_str(),"r");
170 : std::vector<AtomNumber> aaa;
171 : unsigned neigenvects;
172 2 : neigenvects=0;
173 2 : if (fp!=NULL)
174 : {
175 2 : log<<" Opening the eigenvectors file "<<f_eigenvectors.c_str()<<"\n";
176 : bool do_read=true;
177 74 : while (do_read) {
178 72 : PDB mypdb;
179 : // check the units for reading this file: how can they make sense?
180 144 : do_read=mypdb.readFromFilepointer(fp,plumed.getAtoms().usingNaturalUnits(),0.1/atoms.getUnits().getLength());
181 72 : if(do_read) {
182 70 : neigenvects++;
183 140 : if(mypdb.getAtomNumbers().size()==0) error("number of atoms in a frame should be more than zero");
184 70 : unsigned nat=mypdb.getAtomNumbers().size();
185 140 : if(nat!=mypdb.getAtomNumbers().size()) error("frames should have the same number of atoms");
186 70 : if(aaa.empty()) aaa=mypdb.getAtomNumbers();
187 140 : if(aaa!=mypdb.getAtomNumbers()) error("frames should contain same atoms in same order");
188 140 : log<<" Found eigenvector: "<<neigenvects<<" containing "<<mypdb.getAtomNumbers().size()<<" atoms\n";
189 70 : pdbv.push_back(mypdb);
190 70 : eigenvectors.push_back(mypdb.getPositions());
191 : } else {break ;}
192 70 : }
193 2 : fclose (fp);
194 2 : log<<" Found total "<<neigenvects<< " eigenvectors in the file "<<f_eigenvectors.c_str()<<" \n";
195 2 : if(neigenvects==0) error("at least one eigenvector is expected");
196 : }
197 : // the components
198 70 : for(unsigned i=0; i<neigenvects; i++) {
199 70 : std::string num; Tools::convert( i, num );
200 210 : string name; name=string("eig-")+num;
201 70 : pca_names.push_back(name);
202 70 : addComponentWithDerivatives(name); componentIsNotPeriodic(name);
203 : }
204 2 : turnOnDerivatives();
205 :
206 2 : }
207 :
208 : // calculator
209 1092 : void PCARMSD::calculate() {
210 1092 : if(!nopbc) makeWhole();
211 1092 : Tensor rotation,invrotation;
212 : Matrix<std::vector<Vector> > drotdpos(3,3);
213 : std::vector<Vector> alignedpos;
214 : std::vector<Vector> centeredpos;
215 : std::vector<Vector> centeredref;
216 : std::vector<Vector> ddistdpos;
217 2184 : double r=rmsd->calc_PCAelements( getPositions(), ddistdpos, rotation, drotdpos, alignedpos,centeredpos, centeredref,squared);
218 1092 : invrotation=rotation.transpose();
219 :
220 2184 : Value* verr=getPntrToComponent("residual");
221 : verr->set(r);
222 25116 : for(unsigned iat=0; iat<getNumberOfAtoms(); iat++) {
223 24024 : setAtomsDerivatives (verr,iat,ddistdpos[iat]);
224 : }
225 :
226 : std::vector< Vector > der;
227 1092 : der.resize(getNumberOfAtoms());
228 :
229 :
230 77532 : for(unsigned i=0; i<eigenvectors.size(); i++) {
231 76440 : Value* value=getPntrToComponent(pca_names[i].c_str());
232 : double val; val=0.;
233 917280 : for(unsigned iat=0; iat<getNumberOfAtoms(); iat++) {
234 1261260 : val+=dotProduct(alignedpos[iat]-centeredref[iat],eigenvectors[i][iat]); der[iat].zero();
235 : }
236 : value->set(val);
237 : // here the loop is reversed to better suit the structure of the derivative of the rotation matrix
238 : double tmp1;
239 114660 : for(unsigned a=0; a<3; a++) {
240 343980 : for(unsigned b=0; b<3; b++) {
241 : tmp1=0.;
242 7911540 : for(unsigned n=0; n<getNumberOfAtoms(); n++) {
243 11351340 : tmp1+=centeredpos[n][b]*eigenvectors[i][n][a];
244 : }
245 7911540 : for(unsigned iat=0; iat<getNumberOfAtoms(); iat++) {
246 11351340 : der[iat]+=drotdpos[a][b][iat]*tmp1;
247 : }
248 : }
249 : }
250 38220 : Vector v1;
251 917280 : for(unsigned n=0; n<getNumberOfAtoms(); n++) {
252 1261260 : v1+=(1./getNumberOfAtoms())*matmul(invrotation,eigenvectors[i][n]);
253 : }
254 879060 : for(unsigned iat=0; iat<getNumberOfAtoms(); iat++) {
255 1261260 : der[iat]+=matmul(invrotation,eigenvectors[i][iat])-v1;
256 420420 : setAtomsDerivatives (value,iat,der[iat]);
257 : }
258 : }
259 :
260 79716 : for(int i=0; i<getNumberOfComponents(); ++i) setBoxDerivativesNoPbc( getPntrToComponent(i) );
261 :
262 1092 : }
263 :
264 : }
265 5874 : }
266 :
267 :
268 :
|