2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
5 * Copyright (c) 2001-2004, The GROMACS development team.
6 * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
7 * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
8 * and including many others, as listed in the AUTHORS file in the
9 * top-level source directory and at http://www.gromacs.org.
11 * GROMACS is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU Lesser General Public License
13 * as published by the Free Software Foundation; either version 2.1
14 * of the License, or (at your option) any later version.
16 * GROMACS is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * Lesser General Public License for more details.
21 * You should have received a copy of the GNU Lesser General Public
22 * License along with GROMACS; if not, see
23 * http://www.gnu.org/licenses, or write to the Free Software Foundation,
24 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
26 * If you want to redistribute modifications to GROMACS, please
27 * consider that scientific software is very special. Version
28 * control is crucial - bugs must be traceable. We will be happy to
29 * consider code for inclusion in the official distribution, but
30 * derived work must not be called official GROMACS. Details are found
31 * in the README & COPYING files - if they are missing, get the
32 * official version at http://www.gromacs.org.
34 * To help us fund GROMACS development, we humbly ask that you cite
35 * the research papers on the package. Check out http://www.gromacs.org.
47 #include "gromacs/commandline/pargs.h"
48 #include "gromacs/commandline/viewit.h"
49 #include "gromacs/fileio/confio.h"
50 #include "gromacs/fileio/matio.h"
51 #include "gromacs/fileio/pdbio.h"
52 #include "gromacs/fileio/tpxio.h"
53 #include "gromacs/fileio/trxio.h"
54 #include "gromacs/fileio/xvgr.h"
55 #include "gromacs/gmxana/eigio.h"
56 #include "gromacs/gmxana/gmx_ana.h"
57 #include "gromacs/math/do_fit.h"
58 #include "gromacs/math/functions.h"
59 #include "gromacs/math/vec.h"
60 #include "gromacs/pbcutil/rmpbc.h"
61 #include "gromacs/topology/index.h"
62 #include "gromacs/topology/topology.h"
63 #include "gromacs/utility/arraysize.h"
64 #include "gromacs/utility/cstringutil.h"
65 #include "gromacs/utility/fatalerror.h"
66 #include "gromacs/utility/futil.h"
67 #include "gromacs/utility/gmxassert.h"
68 #include "gromacs/utility/smalloc.h"
70 #include "thermochemistry.h"
72 static const char* proj_unit;
74 static real tick_spacing(real range, int minticks)
83 sp = 0.2 * std::exp(std::log(10.0) * std::ceil(std::log(range) / std::log(10.0)));
84 while (range / sp < minticks - 1)
92 static void write_xvgr_graphs(const char* file,
97 const std::string& xlabel,
106 const gmx_output_env_t* oenv)
110 real ymin, ymax, xsp, ysp;
112 out = gmx_ffopen(file, "w");
113 if (output_env_get_xvg_format(oenv) == exvgXMGRACE)
115 fprintf(out, "@ autoscale onread none\n");
117 for (g = 0; g < ngraphs; g++)
123 for (i = 0; i < n; i++)
140 for (s = 0; s < nsetspergraph; s++)
142 for (i = 0; i < n; i++)
144 if (sy[g][s][i] < ymin)
148 if (sy[g][s][i] > ymax)
161 ymin = ymin - 0.1 * (ymax - ymin);
163 ymax = ymax + 0.1 * (ymax - ymin);
164 xsp = tick_spacing((x[n - 1] - x[0]) * scale_x, 4);
165 ysp = tick_spacing(ymax - ymin, 3);
166 if (output_env_get_print_xvgr_codes(oenv))
168 fprintf(out, "@ with g%d\n@ g%d on\n", g, g);
171 fprintf(out, "@ title \"%s\"\n", title);
174 fprintf(out, "@ subtitle \"%s\"\n", subtitle);
177 if (g == ngraphs - 1)
179 fprintf(out, "@ xaxis label \"%s\"\n", xlabel.c_str());
183 fprintf(out, "@ xaxis ticklabel off\n");
187 fprintf(out, "@ world xmin %g\n", x[0] * scale_x);
188 fprintf(out, "@ world xmax %g\n", x[n - 1] * scale_x);
189 fprintf(out, "@ world ymin %g\n", ymin);
190 fprintf(out, "@ world ymax %g\n", ymax);
192 fprintf(out, "@ view xmin 0.15\n");
193 fprintf(out, "@ view xmax 0.85\n");
194 fprintf(out, "@ view ymin %g\n", 0.15 + (ngraphs - 1 - g) * 0.7 / ngraphs);
195 fprintf(out, "@ view ymax %g\n", 0.15 + (ngraphs - g) * 0.7 / ngraphs);
196 fprintf(out, "@ yaxis label \"%s\"\n", ylabel[g]);
197 fprintf(out, "@ xaxis tick major %g\n", xsp);
198 fprintf(out, "@ xaxis tick minor %g\n", xsp / 2);
199 fprintf(out, "@ xaxis ticklabel start type spec\n");
200 fprintf(out, "@ xaxis ticklabel start %g\n", std::ceil(ymin / xsp) * xsp);
201 fprintf(out, "@ yaxis tick major %g\n", ysp);
202 fprintf(out, "@ yaxis tick minor %g\n", ysp / 2);
203 fprintf(out, "@ yaxis ticklabel start type spec\n");
204 fprintf(out, "@ yaxis ticklabel start %g\n", std::ceil(ymin / ysp) * ysp);
205 if ((ymin < 0) && (ymax > 0))
207 fprintf(out, "@ zeroxaxis bar on\n");
208 fprintf(out, "@ zeroxaxis bar linestyle 3\n");
211 for (s = 0; s < nsetspergraph; s++)
213 for (i = 0; i < n; i++)
215 if (bSplit && i > 0 && std::abs(x[i]) < 1e-5)
217 fprintf(out, "%s\n", output_env_get_print_xvgr_codes(oenv) ? "&" : "");
219 fprintf(out, "%10.4f %10.5f\n", x[i] * scale_x, y ? y[g][i] : sy[g][s][i]);
221 fprintf(out, "%s\n", output_env_get_print_xvgr_codes(oenv) ? "&" : "");
228 compare(int natoms, int n1, rvec** eigvec1, int n2, rvec** eigvec2, real* eigval1, int neig1, real* eigval2, int neig2)
232 double sum1, sum2, trace1, trace2, sab, samsb2, tmp, ip;
234 n = std::min(n1, n2);
236 n = std::min(n, std::min(neig1, neig2));
237 fprintf(stdout, "Will compare the covariance matrices using %d dimensions\n", n);
240 for (i = 0; i < n; i++)
247 eigval1[i] = std::sqrt(eigval1[i]);
250 for (i = n; i < neig1; i++)
252 trace1 += eigval1[i];
255 for (i = 0; i < n; i++)
262 eigval2[i] = std::sqrt(eigval2[i]);
267 // The static analyzer appears to be confused by the fact that the loop below
268 // starts from n instead of 0. However, given all the complex code it's
269 // better to be safe than sorry, so we check it with an assert.
270 // If we are in this comparison routine in the first place, neig2 should not be 0,
271 // so eigval2 should always be a valid pointer.
272 GMX_RELEASE_ASSERT(eigval2 != nullptr, "NULL pointer provided for eigval2");
274 for (i = n; i < neig2; i++)
276 trace2 += eigval2[i];
279 fprintf(stdout, "Trace of the two matrices: %g and %g\n", sum1, sum2);
280 if (neig1 != n || neig2 != n)
282 fprintf(stdout, "this is %d%% and %d%% of the total trace\n",
283 gmx::roundToInt(100 * sum1 / trace1), gmx::roundToInt(100 * sum2 / trace2));
285 fprintf(stdout, "Square root of the traces: %g and %g\n", std::sqrt(sum1), std::sqrt(sum2));
288 for (i = 0; i < n; i++)
291 for (j = 0; j < n; j++)
294 for (k = 0; k < natoms; k++)
296 ip += iprod(eigvec1[i][k], eigvec2[j][k]);
298 tmp += eigval2[j] * ip * ip;
300 sab += eigval1[i] * tmp;
303 samsb2 = sum1 + sum2 - 2 * sab;
309 fprintf(stdout, "The overlap of the covariance matrices:\n");
310 fprintf(stdout, " normalized: %.3f\n", 1 - std::sqrt(samsb2 / (sum1 + sum2)));
311 tmp = 1 - sab / std::sqrt(sum1 * sum2);
316 fprintf(stdout, " shape: %.3f\n", 1 - std::sqrt(tmp));
320 static void inprod_matrix(const char* matfile,
334 int i, x1, y1, x, y, nlevels;
336 real inp, *t_x, *t_y, maxval;
344 for (y1 = 0; y1 < nx; y1++)
346 if (outvec[y1] < nvec2)
348 t_y[ny] = eignr2[outvec[y1]] + 1;
357 for (y = 0; y < ny; y++)
359 t_y[y] = eignr2[y] + 1;
363 fprintf(stderr, "Calculating inner-product matrix of %dx%d eigenvectors\n", nx, nvec2);
368 for (x1 = 0; x1 < nx; x1++)
379 t_x[x1] = eignr1[x] + 1;
380 fprintf(stderr, " %d", eignr1[x] + 1);
381 for (y1 = 0; y1 < ny; y1++)
386 while (outvec[y1] >= nvec2)
396 for (i = 0; i < natoms; i++)
398 inp += iprod(eigvec1[x][i], eigvec2[y][i]);
400 mat[x1][y1] = std::abs(inp);
401 if (mat[x1][y1] > maxval)
403 maxval = mat[x1][y1];
407 fprintf(stderr, "\n");
415 out = gmx_ffopen(matfile, "w");
416 write_xpm(out, 0, "Eigenvector inner-products", "in.prod.", "run 1", "run 2", nx, ny, t_x, t_y,
417 mat, 0.0, maxval, rlo, rhi, &nlevels);
421 static void overlap(const char* outfile,
429 const gmx_output_env_t* oenv)
435 fprintf(stderr, "Calculating overlap between eigenvectors of set 2 with eigenvectors\n");
436 for (i = 0; i < noutvec; i++)
438 fprintf(stderr, "%d ", outvec[i] + 1);
440 fprintf(stderr, "\n");
442 out = xvgropen(outfile, "Subspace overlap", "Eigenvectors of trajectory 2", "Overlap", oenv);
443 if (output_env_get_print_xvgr_codes(oenv))
445 fprintf(out, "@ subtitle \"using %d eigenvectors of trajectory 1\"\n", noutvec);
448 for (x = 0; x < nvec2; x++)
450 for (v = 0; v < noutvec; v++)
454 for (i = 0; i < natoms; i++)
456 inp += iprod(eigvec1[vec][i], eigvec2[x][i]);
458 overlap += gmx::square(inp);
460 fprintf(out, "%5d %5.3f\n", eignr2[x] + 1, overlap / noutvec);
466 static void project(const char* trajfile,
467 const t_topology* top,
470 const char* projfile,
471 const char* twodplotfile,
472 const char* threedplotfile,
473 const char* filterfile,
475 const char* extremefile,
479 const t_atoms* atoms,
494 const gmx_output_env_t* oenv)
496 FILE* xvgrout = nullptr;
497 int nat, i, j, d, v, vec, nfr, nframes = 0, snew_size, frame;
498 t_trxstatus* out = nullptr;
500 int noutvec_extr, imin, imax;
505 real t, inp, **inprod = nullptr;
506 char str[STRLEN], str2[STRLEN], *c;
509 gmx_rmpbc_t gpbc = nullptr;
515 noutvec_extr = noutvec;
525 snew(inprod, noutvec + 1);
529 fprintf(stderr, "Writing a filtered trajectory to %s using eigenvectors\n", filterfile);
530 for (i = 0; i < noutvec; i++)
532 fprintf(stderr, "%d ", outvec[i] + 1);
534 fprintf(stderr, "\n");
535 out = open_trx(filterfile, "w");
540 nat = read_first_x(oenv, &status, trajfile, &t, &xread, box);
544 "the number of atoms in your trajectory (%d) is larger than the number of "
545 "atoms in your structure file (%d)",
552 gpbc = gmx_rmpbc_init(&top->idef, ePBC, nat);
555 for (i = 0; i < nat; i++)
565 gmx_rmpbc(gpbc, nat, box, xread);
567 if (nframes >= snew_size)
570 for (i = 0; i < noutvec + 1; i++)
572 srenew(inprod[i], snew_size);
575 inprod[noutvec][nframes] = t;
576 /* calculate x: a fitted struture of the selected atoms */
579 reset_x(nfit, ifit, nat, nullptr, xread, w_rls);
580 do_fit(nat, w_rls, xref, xread);
582 for (i = 0; i < natoms; i++)
584 copy_rvec(xread[index[i]], x[i]);
587 for (v = 0; v < noutvec; v++)
590 /* calculate (mass-weighted) projection */
592 for (i = 0; i < natoms; i++)
594 inp += (eigvec[vec][i][0] * (x[i][0] - xav[i][0])
595 + eigvec[vec][i][1] * (x[i][1] - xav[i][1])
596 + eigvec[vec][i][2] * (x[i][2] - xav[i][2]))
599 inprod[v][nframes] = inp;
603 for (i = 0; i < natoms; i++)
605 for (d = 0; d < DIM; d++)
607 /* misuse xread for output */
608 xread[index[i]][d] = xav[i][d];
609 for (v = 0; v < noutvec; v++)
611 xread[index[i]][d] +=
612 inprod[v][nframes] * eigvec[outvec[v]][i][d] / sqrtm[i];
616 write_trx(out, natoms, index, atoms, 0, t, box, xread, nullptr, nullptr);
621 } while (read_next_x(oenv, status, &t, xread, box));
631 snew(xread, atoms->nr);
636 gmx_rmpbc_done(gpbc);
642 GMX_RELEASE_ASSERT(inprod != nullptr, "inprod must be non-NULL if projfile is non-NULL");
643 snew(ylabel, noutvec);
644 for (v = 0; v < noutvec; v++)
646 sprintf(str, "vec %d", eignr[outvec[v]] + 1);
647 ylabel[v] = gmx_strdup(str);
649 sprintf(str, "projection on eigenvectors (%s)", proj_unit);
650 write_xvgr_graphs(projfile, noutvec, 1, str, nullptr, output_env_get_xvgr_tlabel(oenv),
651 ylabel, nframes, inprod[noutvec], inprod, nullptr,
652 output_env_get_time_factor(oenv), FALSE, bSplit, oenv);
657 sprintf(str, "projection on eigenvector %d (%s)", eignr[outvec[0]] + 1, proj_unit);
658 sprintf(str2, "projection on eigenvector %d (%s)", eignr[outvec[noutvec - 1]] + 1, proj_unit);
659 xvgrout = xvgropen(twodplotfile, "2D projection of trajectory", str, str2, oenv);
660 for (i = 0; i < nframes; i++)
662 if (bSplit && i > 0 && std::abs(inprod[noutvec][i]) < 1e-5)
664 fprintf(xvgrout, "%s\n", output_env_get_print_xvgr_codes(oenv) ? "&" : "");
666 fprintf(xvgrout, "%10.5f %10.5f\n", inprod[0][i], inprod[noutvec - 1][i]);
683 gmx_fatal(FARGS, "You have selected less than 3 eigenvectors");
687 bPDB = fn2ftp(threedplotfile) == efPDB;
689 box[XX][XX] = box[YY][YY] = box[ZZ][ZZ] = 1;
691 b4D = bPDB && (noutvec >= 4);
695 "You have selected four or more eigenvectors:\n"
696 "fourth eigenvector will be plotted "
697 "in bfactor field of pdb file\n");
698 sprintf(str, "4D proj. of traj. on eigenv. %d, %d, %d and %d", eignr[outvec[0]] + 1,
699 eignr[outvec[1]] + 1, eignr[outvec[2]] + 1, eignr[outvec[3]] + 1);
703 sprintf(str, "3D proj. of traj. on eigenv. %d, %d and %d", eignr[outvec[0]] + 1,
704 eignr[outvec[1]] + 1, eignr[outvec[2]] + 1);
706 init_t_atoms(&atoms, nframes, FALSE);
709 atnm = gmx_strdup("C");
710 resnm = gmx_strdup("PRJ");
714 fact = 10000.0 / nframes;
721 for (i = 0; i < nframes; i++)
723 atoms.atomname[i] = &atnm;
724 atoms.atom[i].resind = i;
725 atoms.resinfo[i].name = &resnm;
726 atoms.resinfo[i].nr = static_cast<int>(std::ceil(i * fact));
727 atoms.resinfo[i].ic = ' ';
728 x[i][XX] = inprod[0][i];
729 x[i][YY] = inprod[1][i];
730 x[i][ZZ] = inprod[2][i];
736 if ((b4D || bSplit) && bPDB)
738 GMX_RELEASE_ASSERT(inprod != nullptr,
739 "inprod must be non-NULL with 4D or split PDB output options");
741 out = gmx_ffopen(threedplotfile, "w");
742 fprintf(out, "HEADER %s\n", str);
745 fprintf(out, "REMARK %s\n", "fourth dimension plotted as B-factor");
748 for (i = 0; i < atoms.nr; i++)
750 if (j > 0 && bSplit && std::abs(inprod[noutvec][i]) < 1e-5)
752 fprintf(out, "TER\n");
755 gmx_fprintf_pdb_atomline(out, epdbATOM, i + 1, "C", ' ', "PRJ", ' ', j + 1, ' ',
756 10 * x[i][XX], 10 * x[i][YY], 10 * x[i][ZZ], 1.0,
760 fprintf(out, "CONECT%5d%5d\n", i, i + 1);
764 fprintf(out, "TER\n");
769 write_sto_conf(threedplotfile, str, &atoms, x, nullptr, ePBC, box);
776 snew(pmin, noutvec_extr);
777 snew(pmax, noutvec_extr);
780 GMX_RELEASE_ASSERT(inprod != nullptr, "inprod must be non-NULL");
781 fprintf(stderr, "%11s %17s %17s\n", "eigenvector", "Minimum", "Maximum");
782 fprintf(stderr, "%11s %10s %10s %10s %10s\n", "", "value", "frame", "value", "frame");
785 for (v = 0; v < noutvec_extr; v++)
787 for (i = 0; i < nframes; i++)
789 if (inprod[v][i] < inprod[v][imin])
793 if (inprod[v][i] > inprod[v][imax])
798 pmin[v] = inprod[v][imin];
799 pmax[v] = inprod[v][imax];
800 fprintf(stderr, "%7d %10.6f %10d %10.6f %10d\n", eignr[outvec[v]] + 1, pmin[v],
801 imin, pmax[v], imax);
809 /* build format string for filename: */
810 std::strcpy(str, extremefile); /* copy filename */
811 c = std::strrchr(str, '.'); /* find where extention begins */
812 std::strcpy(str2, c); /* get extention */
813 sprintf(c, "%%d%s", str2); /* append '%s' and extention to filename */
814 for (v = 0; v < noutvec_extr; v++)
816 /* make filename using format string */
817 if (noutvec_extr == 1)
819 std::strcpy(str2, extremefile);
823 sprintf(str2, str, eignr[outvec[v]] + 1);
825 fprintf(stderr, "Writing %d frames along eigenvector %d to %s\n", nextr, outvec[v] + 1, str2);
826 out = open_trx(str2, "w");
827 for (frame = 0; frame < nextr; frame++)
829 if ((extreme == 0) && (nextr <= 3))
831 for (i = 0; i < natoms; i++)
833 atoms->resinfo[atoms->atom[index[i]].resind].chainid = 'A' + frame;
836 for (i = 0; i < natoms; i++)
838 for (d = 0; d < DIM; d++)
842 + (pmin[v] * (nextr - frame - 1) + pmax[v] * frame) / (nextr - 1)
843 * eigvec[outvec[v]][i][d] / sqrtm[i]);
846 write_trx(out, natoms, index, atoms, 0, frame, topbox, xread, nullptr, nullptr);
853 fprintf(stderr, "\n");
856 static void components(const char* outfile,
862 const gmx_output_env_t* oenv)
869 fprintf(stderr, "Writing eigenvector components to %s\n", outfile);
871 snew(ylabel, noutvec);
874 for (i = 0; i < natoms; i++)
878 for (g = 0; g < noutvec; g++)
881 sprintf(str, "vec %d", eignr[v] + 1);
882 ylabel[g] = gmx_strdup(str);
884 for (s = 0; s < 4; s++)
886 snew(y[g][s], natoms);
888 for (i = 0; i < natoms; i++)
890 y[g][0][i] = norm(eigvec[v][i]);
891 for (s = 0; s < 3; s++)
893 y[g][s + 1][i] = eigvec[v][i][s];
897 write_xvgr_graphs(outfile, noutvec, 4, "Eigenvector components",
898 "black: total, red: x, green: y, blue: z", "Atom number", ylabel, natoms, x,
899 nullptr, y, 1, FALSE, FALSE, oenv);
900 fprintf(stderr, "\n");
903 static void rmsf(const char* outfile,
912 const gmx_output_env_t* oenv)
919 for (i = 0; i < neig; i++)
927 fprintf(stderr, "Writing rmsf to %s\n", outfile);
929 snew(ylabel, noutvec);
932 for (i = 0; i < natoms; i++)
936 for (g = 0; g < noutvec; g++)
939 if (eignr[v] >= neig)
941 gmx_fatal(FARGS, "Selected vector %d is larger than the number of eigenvalues (%d)",
944 sprintf(str, "vec %d", eignr[v] + 1);
945 ylabel[g] = gmx_strdup(str);
947 for (i = 0; i < natoms; i++)
949 y[g][i] = std::sqrt(eigval[eignr[v]] * norm2(eigvec[v][i])) / sqrtm[i];
952 write_xvgr_graphs(outfile, noutvec, 1, "RMS fluctuation (nm) ", nullptr, "Atom number", ylabel,
953 natoms, x, y, nullptr, 1, TRUE, FALSE, oenv);
954 fprintf(stderr, "\n");
957 int gmx_anaeig(int argc, char* argv[])
959 static const char* desc[] = {
960 "[THISMODULE] analyzes eigenvectors. The eigenvectors can be of a",
961 "covariance matrix ([gmx-covar]) or of a Normal Modes analysis",
962 "([gmx-nmeig]).[PAR]",
964 "When a trajectory is projected on eigenvectors, all structures are",
965 "fitted to the structure in the eigenvector file, if present, otherwise",
966 "to the structure in the structure file. When no run input file is",
967 "supplied, periodicity will not be taken into account. Most analyses",
968 "are performed on eigenvectors [TT]-first[tt] to [TT]-last[tt], but when",
969 "[TT]-first[tt] is set to -1 you will be prompted for a selection.[PAR]",
971 "[TT]-comp[tt]: plot the vector components per atom of eigenvectors",
972 "[TT]-first[tt] to [TT]-last[tt].[PAR]",
974 "[TT]-rmsf[tt]: plot the RMS fluctuation per atom of eigenvectors",
975 "[TT]-first[tt] to [TT]-last[tt] (requires [TT]-eig[tt]).[PAR]",
977 "[TT]-proj[tt]: calculate projections of a trajectory on eigenvectors",
978 "[TT]-first[tt] to [TT]-last[tt].",
979 "The projections of a trajectory on the eigenvectors of its",
980 "covariance matrix are called principal components (pc's).",
981 "It is often useful to check the cosine content of the pc's,",
982 "since the pc's of random diffusion are cosines with the number",
983 "of periods equal to half the pc index.",
984 "The cosine content of the pc's can be calculated with the program",
985 "[gmx-analyze].[PAR]",
987 "[TT]-2d[tt]: calculate a 2d projection of a trajectory on eigenvectors",
988 "[TT]-first[tt] and [TT]-last[tt].[PAR]",
990 "[TT]-3d[tt]: calculate a 3d projection of a trajectory on the first",
991 "three selected eigenvectors.[PAR]",
993 "[TT]-filt[tt]: filter the trajectory to show only the motion along",
994 "eigenvectors [TT]-first[tt] to [TT]-last[tt].[PAR]",
996 "[TT]-extr[tt]: calculate the two extreme projections along a trajectory",
997 "on the average structure and interpolate [TT]-nframes[tt] frames",
998 "between them, or set your own extremes with [TT]-max[tt]. The",
999 "eigenvector [TT]-first[tt] will be written unless [TT]-first[tt] and",
1000 "[TT]-last[tt] have been set explicitly, in which case all eigenvectors",
1001 "will be written to separate files. Chain identifiers will be added",
1002 "when writing a [REF].pdb[ref] file with two or three structures (you",
1003 "can use [TT]rasmol -nmrpdb[tt] to view such a [REF].pdb[ref] file).[PAR]",
1005 "Overlap calculations between covariance analysis",
1006 "^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^",
1008 "[BB]Note:[bb] the analysis should use the same fitting structure",
1010 "[TT]-over[tt]: calculate the subspace overlap of the eigenvectors in",
1011 "file [TT]-v2[tt] with eigenvectors [TT]-first[tt] to [TT]-last[tt]",
1012 "in file [TT]-v[tt].[PAR]",
1014 "[TT]-inpr[tt]: calculate a matrix of inner-products between",
1015 "eigenvectors in files [TT]-v[tt] and [TT]-v2[tt]. All eigenvectors",
1016 "of both files will be used unless [TT]-first[tt] and [TT]-last[tt]",
1017 "have been set explicitly.[PAR]",
1019 "When [TT]-v[tt] and [TT]-v2[tt] are given, a single number for the",
1020 "overlap between the covariance matrices is generated. Note that the",
1021 "eigenvalues are by default read from the timestamp field in the",
1022 "eigenvector input files, but when [TT]-eig[tt], or [TT]-eig2[tt] are",
1023 "given, the corresponding eigenvalues are used instead. The formulas are::",
1025 " difference = sqrt(tr((sqrt(M1) - sqrt(M2))^2))",
1026 " normalized overlap = 1 - difference/sqrt(tr(M1) + tr(M2))",
1027 " shape overlap = 1 - sqrt(tr((sqrt(M1/tr(M1)) - sqrt(M2/tr(M2)))^2))",
1029 "where M1 and M2 are the two covariance matrices and tr is the trace",
1030 "of a matrix. The numbers are proportional to the overlap of the square",
1031 "root of the fluctuations. The normalized overlap is the most useful",
1032 "number, it is 1 for identical matrices and 0 when the sampled",
1033 "subspaces are orthogonal.[PAR]",
1034 "When the [TT]-entropy[tt] flag is given an entropy estimate will be",
1035 "computed based on the Quasiharmonic approach and based on",
1036 "Schlitter's formula."
1038 static int first = 1, last = -1, skip = 1, nextr = 2, nskip = 6;
1039 static real max = 0.0, temp = 298.15;
1040 static gmx_bool bSplit = FALSE, bEntropy = FALSE;
1042 { "-first", FALSE, etINT, { &first }, "First eigenvector for analysis (-1 is select)" },
1043 { "-last", FALSE, etINT, { &last }, "Last eigenvector for analysis (-1 is till the last)" },
1044 { "-skip", FALSE, etINT, { &skip }, "Only analyse every nr-th frame" },
1049 "Maximum for projection of the eigenvector on the average structure, "
1050 "max=0 gives the extremes" },
1051 { "-nframes", FALSE, etINT, { &nextr }, "Number of frames for the extremes output" },
1052 { "-split", FALSE, etBOOL, { &bSplit }, "Split eigenvector projections where time is zero" },
1057 "Compute entropy according to the Quasiharmonic formula or Schlitter's method." },
1058 { "-temp", FALSE, etREAL, { &temp }, "Temperature for entropy calculations" },
1063 "Number of eigenvalues to skip when computing the entropy due to the quasi harmonic "
1064 "approximation. When you do a rotational and/or translational fit prior to the "
1065 "covariance analysis, you get 3 or 6 eigenvalues that are very close to zero, and which "
1066 "should not be taken into account when computing the entropy." }
1068 #define NPA asize(pa)
1072 const t_atoms* atoms = nullptr;
1073 rvec * xtop, *xref1, *xref2, *xrefp = nullptr;
1074 gmx_bool bDMR1, bDMA1, bDMR2, bDMA2;
1075 int nvec1, nvec2, *eignr1 = nullptr, *eignr2 = nullptr;
1076 rvec * xav1, *xav2, **eigvec1 = nullptr, **eigvec2 = nullptr;
1078 real totmass, *sqrtm, *w_rls, t;
1081 const char* indexfile;
1083 int nout, *iout, noutvec, *outvec, nfit;
1084 int * index = nullptr, *ifit = nullptr;
1085 const char * VecFile, *Vec2File, *topfile;
1086 const char * EigFile, *Eig2File;
1087 const char * CompFile, *RmsfFile, *ProjOnVecFile;
1088 const char * TwoDPlotFile, *ThreeDPlotFile;
1089 const char * FilterFile, *ExtremeFile;
1090 const char * OverlapFile, *InpMatFile;
1091 gmx_bool bFit1, bFit2, bM, bIndex, bTPS, bTop, bVec2, bProj;
1092 gmx_bool bFirstToLast, bFirstLastSet, bTraj, bCompare, bPDB3D;
1093 real * eigval1 = nullptr, *eigval2 = nullptr;
1096 gmx_output_env_t* oenv;
1100 { efTRN, "-v", "eigenvec", ffREAD }, { efTRN, "-v2", "eigenvec2", ffOPTRD },
1101 { efTRX, "-f", nullptr, ffOPTRD }, { efTPS, nullptr, nullptr, ffOPTRD },
1102 { efNDX, nullptr, nullptr, ffOPTRD }, { efXVG, "-eig", "eigenval", ffOPTRD },
1103 { efXVG, "-eig2", "eigenval2", ffOPTRD }, { efXVG, "-comp", "eigcomp", ffOPTWR },
1104 { efXVG, "-rmsf", "eigrmsf", ffOPTWR }, { efXVG, "-proj", "proj", ffOPTWR },
1105 { efXVG, "-2d", "2dproj", ffOPTWR }, { efSTO, "-3d", "3dproj.pdb", ffOPTWR },
1106 { efTRX, "-filt", "filtered", ffOPTWR }, { efTRX, "-extr", "extreme.pdb", ffOPTWR },
1107 { efXVG, "-over", "overlap", ffOPTWR }, { efXPM, "-inpr", "inprod", ffOPTWR }
1109 #define NFILE asize(fnm)
1111 if (!parse_common_args(&argc, argv, PCA_CAN_TIME | PCA_TIME_UNIT | PCA_CAN_VIEW, NFILE, fnm,
1112 NPA, pa, asize(desc), desc, 0, nullptr, &oenv))
1117 indexfile = ftp2fn_null(efNDX, NFILE, fnm);
1119 VecFile = opt2fn("-v", NFILE, fnm);
1120 Vec2File = opt2fn_null("-v2", NFILE, fnm);
1121 topfile = ftp2fn(efTPS, NFILE, fnm);
1122 EigFile = opt2fn_null("-eig", NFILE, fnm);
1123 Eig2File = opt2fn_null("-eig2", NFILE, fnm);
1124 CompFile = opt2fn_null("-comp", NFILE, fnm);
1125 RmsfFile = opt2fn_null("-rmsf", NFILE, fnm);
1126 ProjOnVecFile = opt2fn_null("-proj", NFILE, fnm);
1127 TwoDPlotFile = opt2fn_null("-2d", NFILE, fnm);
1128 ThreeDPlotFile = opt2fn_null("-3d", NFILE, fnm);
1129 FilterFile = opt2fn_null("-filt", NFILE, fnm);
1130 ExtremeFile = opt2fn_null("-extr", NFILE, fnm);
1131 OverlapFile = opt2fn_null("-over", NFILE, fnm);
1132 InpMatFile = ftp2fn_null(efXPM, NFILE, fnm);
1134 bProj = (ProjOnVecFile != nullptr) || (TwoDPlotFile != nullptr) || (ThreeDPlotFile != nullptr)
1135 || (FilterFile != nullptr) || (ExtremeFile != nullptr);
1136 bFirstLastSet = opt2parg_bSet("-first", NPA, pa) && opt2parg_bSet("-last", NPA, pa);
1137 bFirstToLast = (CompFile != nullptr) || (RmsfFile != nullptr) || (ProjOnVecFile != nullptr)
1138 || (FilterFile != nullptr) || (OverlapFile != nullptr)
1139 || (((ExtremeFile != nullptr) || (InpMatFile != nullptr)) && bFirstLastSet);
1140 bVec2 = (Vec2File != nullptr) || (OverlapFile != nullptr) || (InpMatFile != nullptr);
1141 bM = (RmsfFile != nullptr) || bProj;
1142 bTraj = (ProjOnVecFile != nullptr) || (FilterFile != nullptr)
1143 || ((ExtremeFile != nullptr) && (max == 0)) || (TwoDPlotFile != nullptr)
1144 || (ThreeDPlotFile != nullptr);
1145 bIndex = bM || bProj;
1146 bTPS = ftp2bSet(efTPS, NFILE, fnm) || bM || bTraj || (FilterFile != nullptr)
1147 || (bIndex && (indexfile != nullptr));
1148 bCompare = (Vec2File != nullptr) || (Eig2File != nullptr);
1149 bPDB3D = fn2ftp(ThreeDPlotFile) == efPDB;
1151 read_eigenvectors(VecFile, &natoms, &bFit1, &xref1, &bDMR1, &xav1, &bDMA1, &nvec1, &eignr1,
1152 &eigvec1, &eigval1);
1153 neig1 = std::min(nvec1, DIM * natoms);
1154 if (nvec1 != DIM * natoms)
1157 "Warning: number of eigenvectors %d does not match three times\n"
1158 "the number of atoms %d in %s. Using %d eigenvectors.\n\n",
1159 nvec1, natoms, VecFile, neig1);
1162 /* Overwrite eigenvalues from separate files if the user provides them */
1163 if (EigFile != nullptr)
1165 int neig_tmp = read_xvg(EigFile, &xvgdata, &i);
1166 if (neig_tmp != neig1)
1169 "Warning: number of eigenvalues in xvg file (%d) does not mtch trr file (%d)\n",
1173 srenew(eigval1, neig1);
1174 for (j = 0; j < neig1; j++)
1176 real tmp = eigval1[j];
1177 eigval1[j] = xvgdata[1][j];
1178 if (debug && (eigval1[j] != tmp))
1180 fprintf(debug, "Replacing eigenvalue %d. From trr: %10g, from xvg: %10g\n", j, tmp,
1184 for (j = 0; j < i; j++)
1189 fprintf(stderr, "Read %d eigenvalues from %s\n", neig1, EigFile);
1197 "Can not calculate entropies from mass-weighted eigenvalues, redo the "
1198 "analysis without mass-weighting");
1200 printf("The Entropy due to the Schlitter formula is %g J/mol K\n",
1201 calcSchlitterEntropy(gmx::arrayRefFromArray(eigval1, neig1), temp, FALSE));
1202 printf("The Entropy due to the Quasiharmonic analysis is %g J/mol K\n",
1203 calcQuasiHarmonicEntropy(gmx::arrayRefFromArray(eigval1, neig1), temp, FALSE, 1.0));
1210 gmx_fatal(FARGS, "Need a second eigenvector file to do this analysis.");
1213 read_eigenvectors(Vec2File, &natoms2, &bFit2, &xref2, &bDMR2, &xav2, &bDMA2, &nvec2,
1214 &eignr2, &eigvec2, &eigval2);
1216 neig2 = std::min(nvec2, DIM * natoms2);
1219 gmx_fatal(FARGS, "Dimensions in the eigenvector files don't match");
1228 if (Eig2File != nullptr)
1230 neig2 = read_xvg(Eig2File, &xvgdata, &i);
1231 srenew(eigval2, neig2);
1232 for (j = 0; j < neig2; j++)
1234 eigval2[j] = xvgdata[1][j];
1236 for (j = 0; j < i; j++)
1241 fprintf(stderr, "Read %d eigenvalues from %s\n", neig2, Eig2File);
1245 if ((!bFit1 || xref1) && !bDMR1 && !bDMA1)
1249 if ((xref1 == nullptr) && (bM || bTraj))
1265 bTop = read_tps_conf(ftp2fn(efTPS, NFILE, fnm), &top, &ePBC, &xtop, nullptr, topbox, bM);
1267 gpbc = gmx_rmpbc_init(&top.idef, ePBC, atoms->nr);
1268 gmx_rmpbc(gpbc, atoms->nr, topbox, xtop);
1269 /* Fitting is only required for the projection */
1272 if (xref1 == nullptr)
1274 printf("\nNote: the structure in %s should be the same\n"
1275 " as the one used for the fit in g_covar\n",
1278 printf("\nSelect the index group that was used for the least squares fit in g_covar\n");
1279 get_index(atoms, indexfile, 1, &nfit, &ifit, &grpname);
1281 snew(w_rls, atoms->nr);
1282 for (i = 0; (i < nfit); i++)
1286 w_rls[ifit[i]] = atoms->atom[ifit[i]].m;
1290 w_rls[ifit[i]] = 1.0;
1294 snew(xrefp, atoms->nr);
1295 if (xref1 != nullptr)
1297 /* Safety check between selected fit-group and reference structure read from the eigenvector file */
1301 "you selected a group with %d elements instead of %d, your selection "
1302 "does not fit the reference structure in the eigenvector file.",
1305 for (i = 0; (i < nfit); i++)
1307 copy_rvec(xref1[i], xrefp[ifit[i]]);
1312 /* The top coordinates are the fitting reference */
1313 for (i = 0; (i < nfit); i++)
1315 copy_rvec(xtop[ifit[i]], xrefp[ifit[i]]);
1317 reset_x(nfit, ifit, atoms->nr, nullptr, xrefp, w_rls);
1320 gmx_rmpbc_done(gpbc);
1325 printf("\nSelect an index group of %d elements that corresponds to the eigenvectors\n", natoms);
1326 get_index(atoms, indexfile, 1, &i, &index, &grpname);
1329 gmx_fatal(FARGS, "you selected a group with %d elements instead of %d", i, natoms);
1334 snew(sqrtm, natoms);
1337 proj_unit = "u\\S1/2\\Nnm";
1338 for (i = 0; (i < natoms); i++)
1340 sqrtm[i] = std::sqrt(atoms->atom[index[i]].m);
1346 for (i = 0; (i < natoms); i++)
1356 for (i = 0; (i < natoms); i++)
1358 for (d = 0; (d < DIM); d++)
1360 t += gmx::square((xav1[i][d] - xav2[i][d]) * sqrtm[i]);
1361 totmass += gmx::square(sqrtm[i]);
1365 "RMSD (without fit) between the two average structures:"
1367 std::sqrt(t / totmass));
1372 last = natoms * DIM;
1378 /* make an index from first to last */
1379 nout = last - first + 1;
1381 for (i = 0; i < nout; i++)
1383 iout[i] = first - 1 + i;
1386 else if (ThreeDPlotFile)
1388 /* make an index of first+(0,1,2) and last */
1389 nout = bPDB3D ? 4 : 3;
1390 nout = std::min(last - first + 1, nout);
1392 iout[0] = first - 1;
1396 iout[2] = first + 1;
1398 iout[nout - 1] = last - 1;
1402 /* make an index of first and last */
1405 iout[0] = first - 1;
1411 printf("Select eigenvectors for output, end your selection with 0\n");
1418 srenew(iout, nout + 1);
1419 if (1 != scanf("%d", &iout[nout]))
1421 gmx_fatal(FARGS, "Error reading user input");
1424 } while (iout[nout] >= 0);
1428 /* make an index of the eigenvectors which are present */
1431 for (i = 0; i < nout; i++)
1434 while ((j < nvec1) && (eignr1[j] != iout[i]))
1438 if ((j < nvec1) && (eignr1[j] == iout[i]))
1440 outvec[noutvec] = j;
1444 fprintf(stderr, "%d eigenvectors selected for output", noutvec);
1447 fprintf(stderr, ":");
1448 for (j = 0; j < noutvec; j++)
1450 fprintf(stderr, " %d", eignr1[outvec[j]] + 1);
1453 fprintf(stderr, "\n");
1457 components(CompFile, natoms, eignr1, eigvec1, noutvec, outvec, oenv);
1462 rmsf(RmsfFile, natoms, sqrtm, eignr1, eigvec1, noutvec, outvec, eigval1, neig1, oenv);
1467 project(bTraj ? opt2fn("-f", NFILE, fnm) : nullptr, bTop ? &top : nullptr, ePBC, topbox,
1468 ProjOnVecFile, TwoDPlotFile, ThreeDPlotFile, FilterFile, skip, ExtremeFile,
1469 bFirstLastSet, max, nextr, atoms, natoms, index, bFit1, xrefp, nfit, ifit, w_rls,
1470 sqrtm, xav1, eignr1, eigvec1, noutvec, outvec, bSplit, oenv);
1475 overlap(OverlapFile, natoms, eigvec1, nvec2, eignr2, eigvec2, noutvec, outvec, oenv);
1480 inprod_matrix(InpMatFile, natoms, nvec1, eignr1, eigvec1, nvec2, eignr2, eigvec2,
1481 bFirstLastSet, noutvec, outvec);
1486 compare(natoms, nvec1, eigvec1, nvec2, eigvec2, eigval1, neig1, eigval2, neig2);
1490 if (!CompFile && !bProj && !OverlapFile && !InpMatFile && !bCompare && !bEntropy)
1493 "\nIf you want some output,"
1494 " set one (or two or ...) of the output file options\n");
1498 view_all(oenv, NFILE, fnm);