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)."
204 static gmx_bool bM=TRUE;
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);