3 * This source code is part of
7 * GROningen MAchine for Chemical Simulations
10 * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
11 * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
12 * Copyright (c) 2001-2004, The GROMACS development team,
13 * check out http://www.gromacs.org for more information.
15 * This program is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU General Public License
17 * as published by the Free Software Foundation; either version 2
18 * of the License, or (at your option) any later version.
20 * If you want to redistribute modifications, please consider that
21 * scientific software is very special. Version control is crucial -
22 * bugs must be traceable. We will be happy to consider code for
23 * inclusion in the official distribution, but derived work must not
24 * be called official GROMACS. Details are found in the README & COPYING
25 * files - if they are missing, get the official version at www.gromacs.org.
27 * To help us fund GROMACS development, we humbly ask that you cite
28 * the papers on the package - you can find them in the top README file.
30 * For more info, check our website at http://www.gromacs.org
33 * Green Red Orange Magenta Azure Cyan Skyblue
57 #include "eigensolver.h"
60 #include "sparsematrix.h"
67 nma_full_hessian(real
* hess
,
80 natoms
= top
->atoms
.nr
;
82 /* divide elements hess[i][j] by sqrt(mas[i])*sqrt(mas[j]) when required */
86 for (i
=0; (i
<natoms
); i
++)
88 for (j
=0; (j
<DIM
); j
++)
90 for (k
=0; (k
<natoms
); k
++)
92 mass_fac
=gmx_invsqrt(top
->atoms
.atom
[i
].m
*top
->atoms
.atom
[k
].m
);
93 for (l
=0; (l
<DIM
); l
++)
94 hess
[(i
*DIM
+j
)*ndim
+k
*DIM
+l
]*=mass_fac
;
100 /* call diagonalization routine. */
102 fprintf(stderr
,"\nDiagonalizing to find vectors %d through %d...\n",begin
,end
);
105 eigensolver(hess
,ndim
,begin
-1,end
-1,eigenvalues
,eigenvectors
);
107 /* And scale the output eigenvectors */
108 if (bM
&& eigenvectors
!=NULL
)
110 for(i
=0;i
<(end
-begin
+1);i
++)
112 for(j
=0;j
<natoms
;j
++)
114 mass_fac
= gmx_invsqrt(top
->atoms
.atom
[j
].m
);
115 for (k
=0; (k
<DIM
); k
++)
117 eigenvectors
[i
*ndim
+j
*DIM
+k
] *= mass_fac
;
127 nma_sparse_hessian(gmx_sparsematrix_t
* sparse_hessian
,
141 natoms
= top
->atoms
.nr
;
144 /* Cannot check symmetry since we only store half matrix */
145 /* divide elements hess[i][j] by sqrt(mas[i])*sqrt(mas[j]) when required */
149 for (iatom
=0; (iatom
<natoms
); iatom
++)
151 for (j
=0; (j
<DIM
); j
++)
154 for(k
=0;k
<sparse_hessian
->ndata
[row
];k
++)
156 col
= sparse_hessian
->data
[row
][k
].col
;
158 mass_fac
=gmx_invsqrt(top
->atoms
.atom
[iatom
].m
*top
->atoms
.atom
[katom
].m
);
159 sparse_hessian
->data
[row
][k
].value
*=mass_fac
;
164 fprintf(stderr
,"\nDiagonalizing to find eigenvectors 1 through %d...\n",neig
);
167 sparse_eigensolver(sparse_hessian
,neig
,eigenvalues
,eigenvectors
,10000000);
169 /* Scale output eigenvectors */
170 if (bM
&& eigenvectors
!=NULL
)
174 for(j
=0;j
<natoms
;j
++)
176 mass_fac
= gmx_invsqrt(top
->atoms
.atom
[j
].m
);
177 for (k
=0; (k
<DIM
); k
++)
179 eigenvectors
[i
*ndim
+j
*DIM
+k
] *= mass_fac
;
188 int gmx_nmeig(int argc
,char *argv
[])
190 const char *desc
[] = {
191 "g_nmeig calculates the eigenvectors/values of a (Hessian) matrix,",
192 "which can be calculated with [TT]mdrun[tt].",
193 "The eigenvectors are written to a trajectory file ([TT]-v[tt]).",
194 "The structure is written first with t=0. The eigenvectors",
195 "are written as frames with the eigenvector number as timestamp.",
196 "The eigenvectors can be analyzed with [TT]g_anaeig[tt].",
197 "An ensemble of structures can be generated from the eigenvectors with",
198 "[TT]g_nmens[tt]. When mass weighting is used, the generated eigenvectors",
199 "will be scaled back to plain cartesian coordinates before generating the",
200 "output - in this case they will no longer be exactly orthogonal in the",
201 "standard cartesian norm (But in the mass weighted norm they would be)."
205 static int begin
=1,end
=50;
208 { "-m", FALSE
, etBOOL
, {&bM
},
209 "Divide elements of Hessian by product of sqrt(mass) of involved "
210 "atoms prior to diagonalization. This should be used for 'Normal Modes' "
212 { "-first", FALSE
, etINT
, {&begin
},
213 "First eigenvector to write away" },
214 { "-last", FALSE
, etINT
, {&end
},
215 "Last eigenvector to write away" }
226 int natoms
,ndim
,nrow
,ncol
,count
;
227 char *grpname
,title
[256];
232 real factor_gmx_to_omega2
;
233 real factor_omega_to_wavenumber
;
237 real
* full_hessian
= NULL
;
238 gmx_sparsematrix_t
* sparse_hessian
= NULL
;
241 { efMTX
, "-f", "hessian", ffREAD
},
242 { efTPS
, NULL
, NULL
, ffREAD
},
243 { efXVG
, "-of", "eigenfreq", ffWRITE
},
244 { efXVG
, "-ol", "eigenval", ffWRITE
},
245 { efTRN
, "-v", "eigenvec", ffWRITE
}
247 #define NFILE asize(fnm)
249 cr
= init_par(&argc
,&argv
);
252 CopyRight(stderr
,argv
[0]);
254 parse_common_args(&argc
,argv
,PCA_BE_NICE
| (MASTER(cr
) ? 0 : PCA_QUIET
),
255 NFILE
,fnm
,asize(pa
),pa
,asize(desc
),desc
,0,NULL
,&oenv
);
257 read_tps_conf(ftp2fn(efTPS
,NFILE
,fnm
),title
,&top
,&ePBC
,&top_x
,NULL
,box
,bM
);
259 natoms
= top
.atoms
.nr
;
267 /*open Hessian matrix */
268 gmx_mtxio_read(ftp2fn(efMTX
,NFILE
,fnm
),&nrow
,&ncol
,&full_hessian
,&sparse_hessian
);
270 /* Memory for eigenvalues and eigenvectors (begin..end) */
271 snew(eigenvalues
,nrow
);
272 snew(eigenvectors
,nrow
*(end
-begin
+1));
274 /* If the Hessian is in sparse format we can calculate max (ndim-1) eigenvectors,
275 * and they must start at the first one. If this is not valid we convert to full matrix
276 * storage, but warn the user that we might run out of memory...
278 if((sparse_hessian
!= NULL
) && (begin
!=1 || end
==ndim
))
282 fprintf(stderr
,"Cannot use sparse Hessian with first eigenvector != 1.\n");
286 fprintf(stderr
,"Cannot use sparse Hessian to calculate all eigenvectors.\n");
289 fprintf(stderr
,"Will try to allocate memory and convert to full matrix representation...\n");
291 snew(full_hessian
,nrow
*ncol
);
292 for(i
=0;i
<nrow
*ncol
;i
++)
295 for(i
=0;i
<sparse_hessian
->nrow
;i
++)
297 for(j
=0;j
<sparse_hessian
->ndata
[i
];j
++)
299 k
= sparse_hessian
->data
[i
][j
].col
;
300 value
= sparse_hessian
->data
[i
][j
].value
;
301 full_hessian
[i
*ndim
+k
] = value
;
302 full_hessian
[k
*ndim
+i
] = value
;
305 gmx_sparsematrix_destroy(sparse_hessian
);
306 sparse_hessian
= NULL
;
307 fprintf(stderr
,"Converted sparse to full matrix storage.\n");
310 if(full_hessian
!= NULL
)
312 /* Using full matrix storage */
313 nma_full_hessian(full_hessian
,nrow
,bM
,&top
,begin
,end
,eigenvalues
,eigenvectors
);
317 /* Sparse memory storage, allocate memory for eigenvectors */
318 snew(eigenvectors
,ncol
*end
);
319 nma_sparse_hessian(sparse_hessian
,bM
,&top
,end
,eigenvalues
,eigenvectors
);
323 /* check the output, first 6 eigenvalues should be reasonably small */
325 for (i
=begin
-1; (i
<6); i
++)
327 if (fabs(eigenvalues
[i
]) > 1.0e-3)
332 fprintf(stderr
,"\nOne of the lowest 6 eigenvalues has a non-zero value.\n");
333 fprintf(stderr
,"This could mean that the reference structure was not\n");
334 fprintf(stderr
,"properly energy minimized.\n");
338 /* now write the output */
339 fprintf (stderr
,"Writing eigenvalues...\n");
340 out
=xvgropen(opt2fn("-ol",NFILE
,fnm
),
341 "Eigenvalues","Eigenvalue index","Eigenvalue [Gromacs units]",
343 if (output_env_get_print_xvgr_codes(oenv
)) {
345 fprintf(out
,"@ subtitle \"mass weighted\"\n");
347 fprintf(out
,"@ subtitle \"not mass weighted\"\n");
350 for (i
=0; i
<=(end
-begin
); i
++)
351 fprintf (out
,"%6d %15g\n",begin
+i
,eigenvalues
[i
]);
356 fprintf(stderr
,"Writing eigenfrequencies - negative eigenvalues will be set to zero.\n");
358 out
=xvgropen(opt2fn("-of",NFILE
,fnm
),
359 "Eigenfrequencies","Eigenvector index","Wavenumber [cm\\S-1\\N]",
361 if (output_env_get_print_xvgr_codes(oenv
)) {
363 fprintf(out
,"@ subtitle \"mass weighted\"\n");
365 fprintf(out
,"@ subtitle \"not mass weighted\"\n");
368 /* Gromacs units are kJ/(mol*nm*nm*amu),
369 * where amu is the atomic mass unit.
371 * For the eigenfrequencies we want to convert this to spectroscopic absorption
372 * wavenumbers given in cm^(-1), which is the frequency divided by the speed of
373 * light. Do this by first converting to omega^2 (units 1/s), take the square
374 * root, and finally divide by the speed of light (nm/ps in gromacs).
376 factor_gmx_to_omega2
= 1.0E21
/(AVOGADRO
*AMU
);
377 factor_omega_to_wavenumber
= 1.0E-5/(2.0*M_PI
*SPEED_OF_LIGHT
);
379 for (i
=0; i
<=(end
-begin
); i
++)
381 value
= eigenvalues
[i
];
384 value
=sqrt(value
*factor_gmx_to_omega2
)*factor_omega_to_wavenumber
;
385 fprintf (out
,"%6d %15g\n",begin
+i
,value
);
389 /* Writing eigenvectors. Note that if mass scaling was used, the eigenvectors
390 * were scaled back from mass weighted cartesian to plain cartesian in the
391 * nma_full_hessian() or nma_sparse_hessian() routines. Mass scaled vectors
392 * will not be strictly orthogonal in plain cartesian scalar products.
394 write_eigenvectors(opt2fn("-v",NFILE
,fnm
),natoms
,eigenvectors
,FALSE
,begin
,end
,
395 eWXR_NO
,NULL
,FALSE
,top_x
,bM
,eigenvalues
);