Modernize atom enums
[alexxy/gromacs.git] / src / gromacs / gmxana / gmx_anaeig.cpp
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
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 by the GROMACS development team.
7  * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
8  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
9  * and including many others, as listed in the AUTHORS file in the
10  * top-level source directory and at http://www.gromacs.org.
11  *
12  * GROMACS is free software; you can redistribute it and/or
13  * modify it under the terms of the GNU Lesser General Public License
14  * as published by the Free Software Foundation; either version 2.1
15  * of the License, or (at your option) any later version.
16  *
17  * GROMACS is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
20  * Lesser General Public License for more details.
21  *
22  * You should have received a copy of the GNU Lesser General Public
23  * License along with GROMACS; if not, see
24  * http://www.gnu.org/licenses, or write to the Free Software Foundation,
25  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
26  *
27  * If you want to redistribute modifications to GROMACS, please
28  * consider that scientific software is very special. Version
29  * control is crucial - bugs must be traceable. We will be happy to
30  * consider code for inclusion in the official distribution, but
31  * derived work must not be called official GROMACS. Details are found
32  * in the README & COPYING files - if they are missing, get the
33  * official version at http://www.gromacs.org.
34  *
35  * To help us fund GROMACS development, we humbly ask that you cite
36  * the research papers on the package. Check out http://www.gromacs.org.
37  */
38 #include "gmxpre.h"
39
40 #include <cassert>
41 #include <cmath>
42 #include <cstdlib>
43 #include <cstring>
44
45 #include <algorithm>
46 #include <string>
47
48 #include "gromacs/commandline/pargs.h"
49 #include "gromacs/commandline/viewit.h"
50 #include "gromacs/fileio/confio.h"
51 #include "gromacs/fileio/matio.h"
52 #include "gromacs/fileio/pdbio.h"
53 #include "gromacs/fileio/tpxio.h"
54 #include "gromacs/fileio/trxio.h"
55 #include "gromacs/fileio/xvgr.h"
56 #include "gromacs/gmxana/eigio.h"
57 #include "gromacs/gmxana/gmx_ana.h"
58 #include "gromacs/math/do_fit.h"
59 #include "gromacs/math/functions.h"
60 #include "gromacs/math/vec.h"
61 #include "gromacs/pbcutil/rmpbc.h"
62 #include "gromacs/topology/index.h"
63 #include "gromacs/topology/topology.h"
64 #include "gromacs/utility/arraysize.h"
65 #include "gromacs/utility/cstringutil.h"
66 #include "gromacs/utility/fatalerror.h"
67 #include "gromacs/utility/futil.h"
68 #include "gromacs/utility/gmxassert.h"
69 #include "gromacs/utility/smalloc.h"
70
71 #include "thermochemistry.h"
72
73 static const char* proj_unit;
74
75 static real tick_spacing(real range, int minticks)
76 {
77     real sp;
78
79     if (range <= 0)
80     {
81         return 1.0;
82     }
83
84     sp = 0.2 * std::exp(std::log(10.0) * std::ceil(std::log(range) / std::log(10.0)));
85     while (range / sp < minticks - 1)
86     {
87         sp = sp / 2;
88     }
89
90     return sp;
91 }
92
93 static void write_xvgr_graphs(const char*             file,
94                               int                     ngraphs,
95                               int                     nsetspergraph,
96                               const char*             title,
97                               const char*             subtitle,
98                               const std::string&      xlabel,
99                               const char**            ylabel,
100                               int                     n,
101                               real*                   x,
102                               real**                  y,
103                               real***                 sy,
104                               real                    scale_x,
105                               gmx_bool                bZero,
106                               gmx_bool                bSplit,
107                               const gmx_output_env_t* oenv)
108 {
109     FILE* out;
110     int   g, s, i;
111     real  ymin, ymax, xsp, ysp;
112
113     out = gmx_ffopen(file, "w");
114     if (output_env_get_xvg_format(oenv) == XvgFormat::Xmgrace)
115     {
116         fprintf(out, "@ autoscale onread none\n");
117     }
118     for (g = 0; g < ngraphs; g++)
119     {
120         if (y)
121         {
122             ymin = y[g][0];
123             ymax = y[g][0];
124             for (i = 0; i < n; i++)
125             {
126                 if (y[g][i] < ymin)
127                 {
128                     ymin = y[g][i];
129                 }
130                 if (y[g][i] > ymax)
131                 {
132                     ymax = y[g][i];
133                 }
134             }
135         }
136         else
137         {
138             assert(sy);
139             ymin = sy[g][0][0];
140             ymax = sy[g][0][0];
141             for (s = 0; s < nsetspergraph; s++)
142             {
143                 for (i = 0; i < n; i++)
144                 {
145                     if (sy[g][s][i] < ymin)
146                     {
147                         ymin = sy[g][s][i];
148                     }
149                     if (sy[g][s][i] > ymax)
150                     {
151                         ymax = sy[g][s][i];
152                     }
153                 }
154             }
155         }
156         if (bZero)
157         {
158             ymin = 0;
159         }
160         else
161         {
162             ymin = ymin - 0.1 * (ymax - ymin);
163         }
164         ymax = ymax + 0.1 * (ymax - ymin);
165         xsp  = tick_spacing((x[n - 1] - x[0]) * scale_x, 4);
166         ysp  = tick_spacing(ymax - ymin, 3);
167         if (output_env_get_print_xvgr_codes(oenv))
168         {
169             fprintf(out, "@ with g%d\n@ g%d on\n", g, g);
170             if (g == 0)
171             {
172                 fprintf(out, "@ title \"%s\"\n", title);
173                 if (subtitle)
174                 {
175                     fprintf(out, "@ subtitle \"%s\"\n", subtitle);
176                 }
177             }
178             if (g == ngraphs - 1)
179             {
180                 fprintf(out, "@ xaxis  label \"%s\"\n", xlabel.c_str());
181             }
182             else
183             {
184                 fprintf(out, "@ xaxis  ticklabel off\n");
185             }
186             if (n > 1)
187             {
188                 fprintf(out, "@ world xmin %g\n", x[0] * scale_x);
189                 fprintf(out, "@ world xmax %g\n", x[n - 1] * scale_x);
190                 fprintf(out, "@ world ymin %g\n", ymin);
191                 fprintf(out, "@ world ymax %g\n", ymax);
192             }
193             fprintf(out, "@ view xmin 0.15\n");
194             fprintf(out, "@ view xmax 0.85\n");
195             fprintf(out, "@ view ymin %g\n", 0.15 + (ngraphs - 1 - g) * 0.7 / ngraphs);
196             fprintf(out, "@ view ymax %g\n", 0.15 + (ngraphs - g) * 0.7 / ngraphs);
197             fprintf(out, "@ yaxis  label \"%s\"\n", ylabel[g]);
198             fprintf(out, "@ xaxis tick major %g\n", xsp);
199             fprintf(out, "@ xaxis tick minor %g\n", xsp / 2);
200             fprintf(out, "@ xaxis ticklabel start type spec\n");
201             fprintf(out, "@ xaxis ticklabel start %g\n", std::ceil(ymin / xsp) * xsp);
202             fprintf(out, "@ yaxis tick major %g\n", ysp);
203             fprintf(out, "@ yaxis tick minor %g\n", ysp / 2);
204             fprintf(out, "@ yaxis ticklabel start type spec\n");
205             fprintf(out, "@ yaxis ticklabel start %g\n", std::ceil(ymin / ysp) * ysp);
206             if ((ymin < 0) && (ymax > 0))
207             {
208                 fprintf(out, "@ zeroxaxis bar on\n");
209                 fprintf(out, "@ zeroxaxis bar linestyle 3\n");
210             }
211         }
212         for (s = 0; s < nsetspergraph; s++)
213         {
214             for (i = 0; i < n; i++)
215             {
216                 if (bSplit && i > 0 && std::abs(x[i]) < 1e-5)
217                 {
218                     fprintf(out, "%s\n", output_env_get_print_xvgr_codes(oenv) ? "&" : "");
219                 }
220                 fprintf(out, "%10.4f %10.5f\n", x[i] * scale_x, y ? y[g][i] : sy[g][s][i]);
221             }
222             fprintf(out, "%s\n", output_env_get_print_xvgr_codes(oenv) ? "&" : "");
223         }
224     }
225     gmx_ffclose(out);
226 }
227
228 static void
229 compare(int natoms, int n1, rvec** eigvec1, int n2, rvec** eigvec2, real* eigval1, int neig1, real* eigval2, int neig2)
230 {
231     int    n;
232     int    i, j, k;
233     double sum1, sum2, trace1, trace2, sab, samsb2, tmp, ip;
234
235     n = std::min(n1, n2);
236
237     n = std::min(n, std::min(neig1, neig2));
238     fprintf(stdout, "Will compare the covariance matrices using %d dimensions\n", n);
239
240     sum1 = 0;
241     for (i = 0; i < n; i++)
242     {
243         if (eigval1[i] < 0)
244         {
245             eigval1[i] = 0;
246         }
247         sum1 += eigval1[i];
248         eigval1[i] = std::sqrt(eigval1[i]);
249     }
250     trace1 = sum1;
251     for (i = n; i < neig1; i++)
252     {
253         trace1 += eigval1[i];
254     }
255     sum2 = 0;
256     for (i = 0; i < n; i++)
257     {
258         if (eigval2[i] < 0)
259         {
260             eigval2[i] = 0;
261         }
262         sum2 += eigval2[i];
263         eigval2[i] = std::sqrt(eigval2[i]);
264     }
265     trace2 = sum2;
266
267
268     // The static analyzer appears to be confused by the fact that the loop below
269     // starts from n instead of 0. However, given all the complex code it's
270     // better to be safe than sorry, so we check it with an assert.
271     // If we are in this comparison routine in the first place, neig2 should not be 0,
272     // so eigval2 should always be a valid pointer.
273     GMX_RELEASE_ASSERT(eigval2 != nullptr, "NULL pointer provided for eigval2");
274
275     for (i = n; i < neig2; i++)
276     {
277         trace2 += eigval2[i];
278     }
279
280     fprintf(stdout, "Trace of the two matrices: %g and %g\n", sum1, sum2);
281     if (neig1 != n || neig2 != n)
282     {
283         fprintf(stdout,
284                 "this is %d%% and %d%% of the total trace\n",
285                 gmx::roundToInt(100 * sum1 / trace1),
286                 gmx::roundToInt(100 * sum2 / trace2));
287     }
288     fprintf(stdout, "Square root of the traces: %g and %g\n", std::sqrt(sum1), std::sqrt(sum2));
289
290     sab = 0;
291     for (i = 0; i < n; i++)
292     {
293         tmp = 0;
294         for (j = 0; j < n; j++)
295         {
296             ip = 0;
297             for (k = 0; k < natoms; k++)
298             {
299                 ip += iprod(eigvec1[i][k], eigvec2[j][k]);
300             }
301             tmp += eigval2[j] * ip * ip;
302         }
303         sab += eigval1[i] * tmp;
304     }
305
306     samsb2 = sum1 + sum2 - 2 * sab;
307     if (samsb2 < 0)
308     {
309         samsb2 = 0;
310     }
311
312     fprintf(stdout, "The overlap of the covariance matrices:\n");
313     fprintf(stdout, "  normalized:  %.3f\n", 1 - std::sqrt(samsb2 / (sum1 + sum2)));
314     tmp = 1 - sab / std::sqrt(sum1 * sum2);
315     if (tmp < 0)
316     {
317         tmp = 0;
318     }
319     fprintf(stdout, "       shape:  %.3f\n", 1 - std::sqrt(tmp));
320 }
321
322
323 static void inprod_matrix(const char* matfile,
324                           int         natoms,
325                           int         nvec1,
326                           int*        eignr1,
327                           rvec**      eigvec1,
328                           int         nvec2,
329                           const int*  eignr2,
330                           rvec**      eigvec2,
331                           gmx_bool    bSelect,
332                           int         noutvec,
333                           const int*  outvec)
334 {
335     FILE*  out;
336     real** mat;
337     int    i, x1, y1, x, y, nlevels;
338     int    nx, ny;
339     real   inp, *t_x, *t_y, maxval;
340     t_rgb  rlo, rhi;
341
342     snew(t_y, nvec2);
343     if (bSelect)
344     {
345         nx = noutvec;
346         ny = 0;
347         for (y1 = 0; y1 < nx; y1++)
348         {
349             if (outvec[y1] < nvec2)
350             {
351                 t_y[ny] = eignr2[outvec[y1]] + 1;
352                 ny++;
353             }
354         }
355     }
356     else
357     {
358         nx = nvec1;
359         ny = nvec2;
360         for (y = 0; y < ny; y++)
361         {
362             t_y[y] = eignr2[y] + 1;
363         }
364     }
365
366     fprintf(stderr, "Calculating inner-product matrix of %dx%d eigenvectors\n", nx, nvec2);
367
368     snew(mat, nx);
369     snew(t_x, nx);
370     maxval = 0;
371     for (x1 = 0; x1 < nx; x1++)
372     {
373         snew(mat[x1], ny);
374         if (bSelect)
375         {
376             x = outvec[x1];
377         }
378         else
379         {
380             x = x1;
381         }
382         t_x[x1] = eignr1[x] + 1;
383         fprintf(stderr, " %d", eignr1[x] + 1);
384         for (y1 = 0; y1 < ny; y1++)
385         {
386             inp = 0;
387             if (bSelect)
388             {
389                 while (outvec[y1] >= nvec2)
390                 {
391                     y1++;
392                 }
393                 y = outvec[y1];
394             }
395             else
396             {
397                 y = y1;
398             }
399             for (i = 0; i < natoms; i++)
400             {
401                 inp += iprod(eigvec1[x][i], eigvec2[y][i]);
402             }
403             mat[x1][y1] = std::abs(inp);
404             if (mat[x1][y1] > maxval)
405             {
406                 maxval = mat[x1][y1];
407             }
408         }
409     }
410     fprintf(stderr, "\n");
411     rlo.r   = 1;
412     rlo.g   = 1;
413     rlo.b   = 1;
414     rhi.r   = 0;
415     rhi.g   = 0;
416     rhi.b   = 0;
417     nlevels = 41;
418     out     = gmx_ffopen(matfile, "w");
419     write_xpm(out, 0, "Eigenvector inner-products", "in.prod.", "run 1", "run 2", nx, ny, t_x, t_y, mat, 0.0, maxval, rlo, rhi, &nlevels);
420     gmx_ffclose(out);
421 }
422
423 static void overlap(const char*             outfile,
424                     int                     natoms,
425                     rvec**                  eigvec1,
426                     int                     nvec2,
427                     int*                    eignr2,
428                     rvec**                  eigvec2,
429                     int                     noutvec,
430                     int*                    outvec,
431                     const gmx_output_env_t* oenv)
432 {
433     FILE* out;
434     int   i, v, vec, x;
435     real  overlap, inp;
436
437     fprintf(stderr, "Calculating overlap between eigenvectors of set 2 with eigenvectors\n");
438     for (i = 0; i < noutvec; i++)
439     {
440         fprintf(stderr, "%d ", outvec[i] + 1);
441     }
442     fprintf(stderr, "\n");
443
444     out = xvgropen(outfile, "Subspace overlap", "Eigenvectors of trajectory 2", "Overlap", oenv);
445     if (output_env_get_print_xvgr_codes(oenv))
446     {
447         fprintf(out, "@ subtitle \"using %d eigenvectors of trajectory 1\"\n", noutvec);
448     }
449     overlap = 0;
450     for (x = 0; x < nvec2; x++)
451     {
452         for (v = 0; v < noutvec; v++)
453         {
454             vec = outvec[v];
455             inp = 0;
456             for (i = 0; i < natoms; i++)
457             {
458                 inp += iprod(eigvec1[vec][i], eigvec2[x][i]);
459             }
460             overlap += gmx::square(inp);
461         }
462         fprintf(out, "%5d  %5.3f\n", eignr2[x] + 1, overlap / noutvec);
463     }
464
465     xvgrclose(out);
466 }
467
468 static void project(const char*             trajfile,
469                     const t_topology*       top,
470                     PbcType                 pbcType,
471                     matrix                  topbox,
472                     const char*             projfile,
473                     const char*             twodplotfile,
474                     const char*             threedplotfile,
475                     const char*             filterfile,
476                     int                     skip,
477                     const char*             extremefile,
478                     gmx_bool                bExtrAll,
479                     real                    extreme,
480                     int                     nextr,
481                     const t_atoms*          atoms,
482                     int                     natoms,
483                     int*                    index,
484                     gmx_bool                bFit,
485                     rvec*                   xref,
486                     int                     nfit,
487                     int*                    ifit,
488                     real*                   w_rls,
489                     const real*             sqrtm,
490                     rvec*                   xav,
491                     int*                    eignr,
492                     rvec**                  eigvec,
493                     int                     noutvec,
494                     int*                    outvec,
495                     gmx_bool                bSplit,
496                     const gmx_output_env_t* oenv)
497 {
498     FILE*        xvgrout = nullptr;
499     int          nat, i, j, d, v, vec, nfr, nframes = 0, snew_size, frame;
500     t_trxstatus* out = nullptr;
501     t_trxstatus* status;
502     int          noutvec_extr, imin, imax;
503     real *       pmin, *pmax;
504     int*         all_at;
505     matrix       box;
506     rvec *       xread, *x;
507     real         t, inp, **inprod = nullptr;
508     char         str[STRLEN], str2[STRLEN], *c;
509     const char** ylabel;
510     real         fact;
511     gmx_rmpbc_t  gpbc = nullptr;
512
513     snew(x, natoms);
514
515     if (bExtrAll)
516     {
517         noutvec_extr = noutvec;
518     }
519     else
520     {
521         noutvec_extr = 1;
522     }
523
524
525     if (trajfile)
526     {
527         snew(inprod, noutvec + 1);
528
529         if (filterfile)
530         {
531             fprintf(stderr, "Writing a filtered trajectory to %s using eigenvectors\n", filterfile);
532             for (i = 0; i < noutvec; i++)
533             {
534                 fprintf(stderr, "%d ", outvec[i] + 1);
535             }
536             fprintf(stderr, "\n");
537             out = open_trx(filterfile, "w");
538         }
539         snew_size = 0;
540         nfr       = 0;
541         nframes   = 0;
542         nat       = read_first_x(oenv, &status, trajfile, &t, &xread, box);
543         if (nat > atoms->nr)
544         {
545             gmx_fatal(FARGS,
546                       "the number of atoms in your trajectory (%d) is larger than the number of "
547                       "atoms in your structure file (%d)",
548                       nat,
549                       atoms->nr);
550         }
551         snew(all_at, nat);
552
553         if (top)
554         {
555             gpbc = gmx_rmpbc_init(&top->idef, pbcType, nat);
556         }
557
558         for (i = 0; i < nat; i++)
559         {
560             all_at[i] = i;
561         }
562         do
563         {
564             if (nfr % skip == 0)
565             {
566                 if (top)
567                 {
568                     gmx_rmpbc(gpbc, nat, box, xread);
569                 }
570                 if (nframes >= snew_size)
571                 {
572                     snew_size += 100;
573                     for (i = 0; i < noutvec + 1; i++)
574                     {
575                         srenew(inprod[i], snew_size);
576                     }
577                 }
578                 inprod[noutvec][nframes] = t;
579                 /* calculate x: a fitted struture of the selected atoms */
580                 if (bFit)
581                 {
582                     reset_x(nfit, ifit, nat, nullptr, xread, w_rls);
583                     do_fit(nat, w_rls, xref, xread);
584                 }
585                 for (i = 0; i < natoms; i++)
586                 {
587                     copy_rvec(xread[index[i]], x[i]);
588                 }
589
590                 for (v = 0; v < noutvec; v++)
591                 {
592                     vec = outvec[v];
593                     /* calculate (mass-weighted) projection */
594                     inp = 0;
595                     for (i = 0; i < natoms; i++)
596                     {
597                         inp += (eigvec[vec][i][0] * (x[i][0] - xav[i][0])
598                                 + eigvec[vec][i][1] * (x[i][1] - xav[i][1])
599                                 + eigvec[vec][i][2] * (x[i][2] - xav[i][2]))
600                                * sqrtm[i];
601                     }
602                     inprod[v][nframes] = inp;
603                 }
604                 if (filterfile)
605                 {
606                     for (i = 0; i < natoms; i++)
607                     {
608                         for (d = 0; d < DIM; d++)
609                         {
610                             /* misuse xread for output */
611                             xread[index[i]][d] = xav[i][d];
612                             for (v = 0; v < noutvec; v++)
613                             {
614                                 xread[index[i]][d] +=
615                                         inprod[v][nframes] * eigvec[outvec[v]][i][d] / sqrtm[i];
616                             }
617                         }
618                     }
619                     write_trx(out, natoms, index, atoms, 0, t, box, xread, nullptr, nullptr);
620                 }
621                 nframes++;
622             }
623             nfr++;
624         } while (read_next_x(oenv, status, &t, xread, box));
625         close_trx(status);
626         sfree(x);
627         if (filterfile)
628         {
629             close_trx(out);
630         }
631     }
632     else
633     {
634         snew(xread, atoms->nr);
635     }
636
637     if (top)
638     {
639         gmx_rmpbc_done(gpbc);
640     }
641
642
643     if (projfile)
644     {
645         GMX_RELEASE_ASSERT(inprod != nullptr, "inprod must be non-NULL if projfile is non-NULL");
646         snew(ylabel, noutvec);
647         for (v = 0; v < noutvec; v++)
648         {
649             sprintf(str, "vec %d", eignr[outvec[v]] + 1);
650             ylabel[v] = gmx_strdup(str);
651         }
652         sprintf(str, "projection on eigenvectors (%s)", proj_unit);
653         write_xvgr_graphs(projfile,
654                           noutvec,
655                           1,
656                           str,
657                           nullptr,
658                           output_env_get_xvgr_tlabel(oenv),
659                           ylabel,
660                           nframes,
661                           inprod[noutvec],
662                           inprod,
663                           nullptr,
664                           output_env_get_time_factor(oenv),
665                           FALSE,
666                           bSplit,
667                           oenv);
668     }
669
670     if (twodplotfile)
671     {
672         sprintf(str, "projection on eigenvector %d (%s)", eignr[outvec[0]] + 1, proj_unit);
673         sprintf(str2, "projection on eigenvector %d (%s)", eignr[outvec[noutvec - 1]] + 1, proj_unit);
674         xvgrout = xvgropen(twodplotfile, "2D projection of trajectory", str, str2, oenv);
675         for (i = 0; i < nframes; i++)
676         {
677             if (bSplit && i > 0 && std::abs(inprod[noutvec][i]) < 1e-5)
678             {
679                 fprintf(xvgrout, "%s\n", output_env_get_print_xvgr_codes(oenv) ? "&" : "");
680             }
681             fprintf(xvgrout, "%10.5f %10.5f\n", inprod[0][i], inprod[noutvec - 1][i]);
682         }
683         xvgrclose(xvgrout);
684     }
685
686     if (threedplotfile)
687     {
688         t_atoms  atoms;
689         rvec*    x;
690         real*    b = nullptr;
691         matrix   box;
692         char *   resnm, *atnm;
693         gmx_bool bPDB, b4D;
694         FILE*    out;
695
696         if (noutvec < 3)
697         {
698             gmx_fatal(FARGS, "You have selected less than 3 eigenvectors");
699         }
700
701         /* initialize */
702         bPDB = fn2ftp(threedplotfile) == efPDB;
703         clear_mat(box);
704         box[XX][XX] = box[YY][YY] = box[ZZ][ZZ] = 1;
705
706         b4D = bPDB && (noutvec >= 4);
707         if (b4D)
708         {
709             fprintf(stderr,
710                     "You have selected four or more eigenvectors:\n"
711                     "fourth eigenvector will be plotted "
712                     "in bfactor field of pdb file\n");
713             sprintf(str,
714                     "4D proj. of traj. on eigenv. %d, %d, %d and %d",
715                     eignr[outvec[0]] + 1,
716                     eignr[outvec[1]] + 1,
717                     eignr[outvec[2]] + 1,
718                     eignr[outvec[3]] + 1);
719         }
720         else
721         {
722             sprintf(str,
723                     "3D proj. of traj. on eigenv. %d, %d and %d",
724                     eignr[outvec[0]] + 1,
725                     eignr[outvec[1]] + 1,
726                     eignr[outvec[2]] + 1);
727         }
728         init_t_atoms(&atoms, nframes, FALSE);
729         snew(x, nframes);
730         snew(b, nframes);
731         atnm  = gmx_strdup("C");
732         resnm = gmx_strdup("PRJ");
733
734         if (nframes > 10000)
735         {
736             fact = 10000.0 / nframes;
737         }
738         else
739         {
740             fact = 1.0;
741         }
742
743         for (i = 0; i < nframes; i++)
744         {
745             atoms.atomname[i]     = &atnm;
746             atoms.atom[i].resind  = i;
747             atoms.resinfo[i].name = &resnm;
748             atoms.resinfo[i].nr   = static_cast<int>(std::ceil(i * fact));
749             atoms.resinfo[i].ic   = ' ';
750             x[i][XX]              = inprod[0][i];
751             x[i][YY]              = inprod[1][i];
752             x[i][ZZ]              = inprod[2][i];
753             if (b4D)
754             {
755                 b[i] = inprod[3][i];
756             }
757         }
758         if ((b4D || bSplit) && bPDB)
759         {
760             GMX_RELEASE_ASSERT(inprod != nullptr,
761                                "inprod must be non-NULL with 4D or split PDB output options");
762
763             out = gmx_ffopen(threedplotfile, "w");
764             fprintf(out, "HEADER    %s\n", str);
765             if (b4D)
766             {
767                 fprintf(out, "REMARK    %s\n", "fourth dimension plotted as B-factor");
768             }
769             j = 0;
770             for (i = 0; i < atoms.nr; i++)
771             {
772                 if (j > 0 && bSplit && std::abs(inprod[noutvec][i]) < 1e-5)
773                 {
774                     fprintf(out, "TER\n");
775                     j = 0;
776                 }
777                 gmx_fprintf_pdb_atomline(out,
778                                          PdbRecordType::Atom,
779                                          i + 1,
780                                          "C",
781                                          ' ',
782                                          "PRJ",
783                                          ' ',
784                                          j + 1,
785                                          ' ',
786                                          10 * x[i][XX],
787                                          10 * x[i][YY],
788                                          10 * x[i][ZZ],
789                                          1.0,
790                                          10 * b[i],
791                                          "");
792                 if (j > 0)
793                 {
794                     fprintf(out, "CONECT%5d%5d\n", i, i + 1);
795                 }
796                 j++;
797             }
798             fprintf(out, "TER\n");
799             gmx_ffclose(out);
800         }
801         else
802         {
803             write_sto_conf(threedplotfile, str, &atoms, x, nullptr, pbcType, box);
804         }
805         done_atom(&atoms);
806     }
807
808     if (extremefile)
809     {
810         snew(pmin, noutvec_extr);
811         snew(pmax, noutvec_extr);
812         if (extreme == 0)
813         {
814             GMX_RELEASE_ASSERT(inprod != nullptr, "inprod must be non-NULL");
815             fprintf(stderr, "%11s %17s %17s\n", "eigenvector", "Minimum", "Maximum");
816             fprintf(stderr, "%11s %10s %10s %10s %10s\n", "", "value", "frame", "value", "frame");
817             imin = 0;
818             imax = 0;
819             for (v = 0; v < noutvec_extr; v++)
820             {
821                 for (i = 0; i < nframes; i++)
822                 {
823                     if (inprod[v][i] < inprod[v][imin])
824                     {
825                         imin = i;
826                     }
827                     if (inprod[v][i] > inprod[v][imax])
828                     {
829                         imax = i;
830                     }
831                 }
832                 pmin[v] = inprod[v][imin];
833                 pmax[v] = inprod[v][imax];
834                 fprintf(stderr, "%7d     %10.6f %10d %10.6f %10d\n", eignr[outvec[v]] + 1, pmin[v], imin, pmax[v], imax);
835             }
836         }
837         else
838         {
839             pmin[0] = -extreme;
840             pmax[0] = extreme;
841         }
842         /* build format string for filename: */
843         std::strcpy(str, extremefile); /* copy filename */
844         c = std::strrchr(str, '.');    /* find where extention begins */
845         std::strcpy(str2, c);          /* get extention */
846         sprintf(c, "%%d%s", str2);     /* append '%s' and extention to filename */
847         for (v = 0; v < noutvec_extr; v++)
848         {
849             /* make filename using format string */
850             if (noutvec_extr == 1)
851             {
852                 std::strcpy(str2, extremefile);
853             }
854             else
855             {
856                 sprintf(str2, str, eignr[outvec[v]] + 1);
857             }
858             fprintf(stderr, "Writing %d frames along eigenvector %d to %s\n", nextr, outvec[v] + 1, str2);
859             out = open_trx(str2, "w");
860             for (frame = 0; frame < nextr; frame++)
861             {
862                 if ((extreme == 0) && (nextr <= 3))
863                 {
864                     for (i = 0; i < natoms; i++)
865                     {
866                         atoms->resinfo[atoms->atom[index[i]].resind].chainid = 'A' + frame;
867                     }
868                 }
869                 for (i = 0; i < natoms; i++)
870                 {
871                     for (d = 0; d < DIM; d++)
872                     {
873                         xread[index[i]][d] =
874                                 (xav[i][d]
875                                  + (pmin[v] * (nextr - frame - 1) + pmax[v] * frame) / (nextr - 1)
876                                            * eigvec[outvec[v]][i][d] / sqrtm[i]);
877                     }
878                 }
879                 write_trx(out, natoms, index, atoms, 0, frame, topbox, xread, nullptr, nullptr);
880             }
881             close_trx(out);
882         }
883         sfree(pmin);
884         sfree(pmax);
885     }
886     fprintf(stderr, "\n");
887 }
888
889 static void components(const char*             outfile,
890                        int                     natoms,
891                        int*                    eignr,
892                        rvec**                  eigvec,
893                        int                     noutvec,
894                        const int*              outvec,
895                        const gmx_output_env_t* oenv)
896 {
897     int          g, s, v, i;
898     real *       x, ***y;
899     char         str[STRLEN];
900     const char** ylabel;
901
902     fprintf(stderr, "Writing eigenvector components to %s\n", outfile);
903
904     snew(ylabel, noutvec);
905     snew(y, noutvec);
906     snew(x, natoms);
907     for (i = 0; i < natoms; i++)
908     {
909         x[i] = i + 1;
910     }
911     for (g = 0; g < noutvec; g++)
912     {
913         v = outvec[g];
914         sprintf(str, "vec %d", eignr[v] + 1);
915         ylabel[g] = gmx_strdup(str);
916         snew(y[g], 4);
917         for (s = 0; s < 4; s++)
918         {
919             snew(y[g][s], natoms);
920         }
921         for (i = 0; i < natoms; i++)
922         {
923             y[g][0][i] = norm(eigvec[v][i]);
924             for (s = 0; s < 3; s++)
925             {
926                 y[g][s + 1][i] = eigvec[v][i][s];
927             }
928         }
929     }
930     write_xvgr_graphs(outfile,
931                       noutvec,
932                       4,
933                       "Eigenvector components",
934                       "black: total, red: x, green: y, blue: z",
935                       "Atom number",
936                       ylabel,
937                       natoms,
938                       x,
939                       nullptr,
940                       y,
941                       1,
942                       FALSE,
943                       FALSE,
944                       oenv);
945     fprintf(stderr, "\n");
946 }
947
948 static void rmsf(const char*             outfile,
949                  int                     natoms,
950                  const real*             sqrtm,
951                  int*                    eignr,
952                  rvec**                  eigvec,
953                  int                     noutvec,
954                  const int*              outvec,
955                  real*                   eigval,
956                  int                     neig,
957                  const gmx_output_env_t* oenv)
958 {
959     int          g, v, i;
960     real *       x, **y;
961     char         str[STRLEN];
962     const char** ylabel;
963
964     for (i = 0; i < neig; i++)
965     {
966         if (eigval[i] < 0)
967         {
968             eigval[i] = 0;
969         }
970     }
971
972     fprintf(stderr, "Writing rmsf to %s\n", outfile);
973
974     snew(ylabel, noutvec);
975     snew(y, noutvec);
976     snew(x, natoms);
977     for (i = 0; i < natoms; i++)
978     {
979         x[i] = i + 1;
980     }
981     for (g = 0; g < noutvec; g++)
982     {
983         v = outvec[g];
984         if (eignr[v] >= neig)
985         {
986             gmx_fatal(FARGS,
987                       "Selected vector %d is larger than the number of eigenvalues (%d)",
988                       eignr[v] + 1,
989                       neig);
990         }
991         sprintf(str, "vec %d", eignr[v] + 1);
992         ylabel[g] = gmx_strdup(str);
993         snew(y[g], natoms);
994         for (i = 0; i < natoms; i++)
995         {
996             y[g][i] = std::sqrt(eigval[eignr[v]] * norm2(eigvec[v][i])) / sqrtm[i];
997         }
998     }
999     write_xvgr_graphs(
1000             outfile, noutvec, 1, "RMS fluctuation (nm) ", nullptr, "Atom number", ylabel, natoms, x, y, nullptr, 1, TRUE, FALSE, oenv);
1001     fprintf(stderr, "\n");
1002 }
1003
1004 int gmx_anaeig(int argc, char* argv[])
1005 {
1006     static const char* desc[] = {
1007         "[THISMODULE] analyzes eigenvectors. The eigenvectors can be of a",
1008         "covariance matrix ([gmx-covar]) or of a Normal Modes analysis",
1009         "([gmx-nmeig]).[PAR]",
1010
1011         "When a trajectory is projected on eigenvectors, all structures are",
1012         "fitted to the structure in the eigenvector file, if present, otherwise",
1013         "to the structure in the structure file. When no run input file is",
1014         "supplied, periodicity will not be taken into account. Most analyses",
1015         "are performed on eigenvectors [TT]-first[tt] to [TT]-last[tt], but when",
1016         "[TT]-first[tt] is set to -1 you will be prompted for a selection.[PAR]",
1017
1018         "[TT]-comp[tt]: plot the vector components per atom of eigenvectors",
1019         "[TT]-first[tt] to [TT]-last[tt].[PAR]",
1020
1021         "[TT]-rmsf[tt]: plot the RMS fluctuation per atom of eigenvectors",
1022         "[TT]-first[tt] to [TT]-last[tt] (requires [TT]-eig[tt]).[PAR]",
1023
1024         "[TT]-proj[tt]: calculate projections of a trajectory on eigenvectors",
1025         "[TT]-first[tt] to [TT]-last[tt].",
1026         "The projections of a trajectory on the eigenvectors of its",
1027         "covariance matrix are called principal components (pc's).",
1028         "It is often useful to check the cosine content of the pc's,",
1029         "since the pc's of random diffusion are cosines with the number",
1030         "of periods equal to half the pc index.",
1031         "The cosine content of the pc's can be calculated with the program",
1032         "[gmx-analyze].[PAR]",
1033
1034         "[TT]-2d[tt]: calculate a 2d projection of a trajectory on eigenvectors",
1035         "[TT]-first[tt] and [TT]-last[tt].[PAR]",
1036
1037         "[TT]-3d[tt]: calculate a 3d projection of a trajectory on the first",
1038         "three selected eigenvectors.[PAR]",
1039
1040         "[TT]-filt[tt]: filter the trajectory to show only the motion along",
1041         "eigenvectors [TT]-first[tt] to [TT]-last[tt].[PAR]",
1042
1043         "[TT]-extr[tt]: calculate the two extreme projections along a trajectory",
1044         "on the average structure and interpolate [TT]-nframes[tt] frames",
1045         "between them, or set your own extremes with [TT]-max[tt]. The",
1046         "eigenvector [TT]-first[tt] will be written unless [TT]-first[tt] and",
1047         "[TT]-last[tt] have been set explicitly, in which case all eigenvectors",
1048         "will be written to separate files. Chain identifiers will be added",
1049         "when writing a [REF].pdb[ref] file with two or three structures (you",
1050         "can use [TT]rasmol -nmrpdb[tt] to view such a [REF].pdb[ref] file).[PAR]",
1051
1052         "Overlap calculations between covariance analysis",
1053         "^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^",
1054         "",
1055         "[BB]Note:[bb] the analysis should use the same fitting structure",
1056         "",
1057         "[TT]-over[tt]: calculate the subspace overlap of the eigenvectors in",
1058         "file [TT]-v2[tt] with eigenvectors [TT]-first[tt] to [TT]-last[tt]",
1059         "in file [TT]-v[tt].[PAR]",
1060
1061         "[TT]-inpr[tt]: calculate a matrix of inner-products between",
1062         "eigenvectors in files [TT]-v[tt] and [TT]-v2[tt]. All eigenvectors",
1063         "of both files will be used unless [TT]-first[tt] and [TT]-last[tt]",
1064         "have been set explicitly.[PAR]",
1065
1066         "When [TT]-v[tt] and [TT]-v2[tt] are given, a single number for the",
1067         "overlap between the covariance matrices is generated. Note that the",
1068         "eigenvalues are by default read from the timestamp field in the",
1069         "eigenvector input files, but when [TT]-eig[tt], or [TT]-eig2[tt] are",
1070         "given, the corresponding eigenvalues are used instead. The formulas are::",
1071         "",
1072         "         difference = sqrt(tr((sqrt(M1) - sqrt(M2))^2))",
1073         " normalized overlap = 1 - difference/sqrt(tr(M1) + tr(M2))",
1074         "      shape overlap = 1 - sqrt(tr((sqrt(M1/tr(M1)) - sqrt(M2/tr(M2)))^2))",
1075         "",
1076         "where M1 and M2 are the two covariance matrices and tr is the trace",
1077         "of a matrix. The numbers are proportional to the overlap of the square",
1078         "root of the fluctuations. The normalized overlap is the most useful",
1079         "number, it is 1 for identical matrices and 0 when the sampled",
1080         "subspaces are orthogonal.[PAR]",
1081         "When the [TT]-entropy[tt] flag is given an entropy estimate will be",
1082         "computed based on the Quasiharmonic approach and based on",
1083         "Schlitter's formula."
1084     };
1085     static int      first = 1, last = -1, skip = 1, nextr = 2, nskip = 6;
1086     static real     max = 0.0, temp = 298.15;
1087     static gmx_bool bSplit = FALSE, bEntropy = FALSE;
1088     t_pargs         pa[] = {
1089         { "-first", FALSE, etINT, { &first }, "First eigenvector for analysis (-1 is select)" },
1090         { "-last", FALSE, etINT, { &last }, "Last eigenvector for analysis (-1 is till the last)" },
1091         { "-skip", FALSE, etINT, { &skip }, "Only analyse every nr-th frame" },
1092         { "-max",
1093           FALSE,
1094           etREAL,
1095           { &max },
1096           "Maximum for projection of the eigenvector on the average structure, "
1097           "max=0 gives the extremes" },
1098         { "-nframes", FALSE, etINT, { &nextr }, "Number of frames for the extremes output" },
1099         { "-split", FALSE, etBOOL, { &bSplit }, "Split eigenvector projections where time is zero" },
1100         { "-entropy",
1101           FALSE,
1102           etBOOL,
1103           { &bEntropy },
1104           "Compute entropy according to the Quasiharmonic formula or Schlitter's method." },
1105         { "-temp", FALSE, etREAL, { &temp }, "Temperature for entropy calculations" },
1106         { "-nevskip",
1107           FALSE,
1108           etINT,
1109           { &nskip },
1110           "Number of eigenvalues to skip when computing the entropy due to the quasi harmonic "
1111           "approximation. When you do a rotational and/or translational fit prior to the "
1112           "covariance analysis, you get 3 or 6 eigenvalues that are very close to zero, and which "
1113           "should not be taken into account when computing the entropy." }
1114     };
1115 #define NPA asize(pa)
1116
1117     t_topology        top;
1118     PbcType           pbcType = PbcType::Unset;
1119     const t_atoms*    atoms   = nullptr;
1120     rvec *            xtop, *xref1, *xref2, *xrefp = nullptr;
1121     gmx_bool          bDMR1, bDMA1, bDMR2, bDMA2;
1122     int               nvec1, nvec2, *eignr1 = nullptr, *eignr2 = nullptr;
1123     rvec *            xav1, *xav2, **eigvec1 = nullptr, **eigvec2 = nullptr;
1124     matrix            topbox;
1125     real              totmass, *sqrtm, *w_rls, t;
1126     int               natoms;
1127     char*             grpname;
1128     const char*       indexfile;
1129     int               i, j, d;
1130     int               nout, *iout, noutvec, *outvec, nfit;
1131     int *             index = nullptr, *ifit = nullptr;
1132     const char *      VecFile, *Vec2File, *topfile;
1133     const char *      EigFile, *Eig2File;
1134     const char *      CompFile, *RmsfFile, *ProjOnVecFile;
1135     const char *      TwoDPlotFile, *ThreeDPlotFile;
1136     const char *      FilterFile, *ExtremeFile;
1137     const char *      OverlapFile, *InpMatFile;
1138     gmx_bool          bFit1, bFit2, bM, bIndex, bTPS, bTop, bVec2, bProj;
1139     gmx_bool          bFirstToLast, bFirstLastSet, bTraj, bCompare, bPDB3D;
1140     real *            eigval1 = nullptr, *eigval2 = nullptr;
1141     int               neig1, neig2;
1142     double**          xvgdata;
1143     gmx_output_env_t* oenv;
1144     gmx_rmpbc_t       gpbc;
1145
1146     t_filenm fnm[] = {
1147         { efTRN, "-v", "eigenvec", ffREAD },      { efTRN, "-v2", "eigenvec2", ffOPTRD },
1148         { efTRX, "-f", nullptr, ffOPTRD },        { efTPS, nullptr, nullptr, ffOPTRD },
1149         { efNDX, nullptr, nullptr, ffOPTRD },     { efXVG, "-eig", "eigenval", ffOPTRD },
1150         { efXVG, "-eig2", "eigenval2", ffOPTRD }, { efXVG, "-comp", "eigcomp", ffOPTWR },
1151         { efXVG, "-rmsf", "eigrmsf", ffOPTWR },   { efXVG, "-proj", "proj", ffOPTWR },
1152         { efXVG, "-2d", "2dproj", ffOPTWR },      { efSTO, "-3d", "3dproj.pdb", ffOPTWR },
1153         { efTRX, "-filt", "filtered", ffOPTWR },  { efTRX, "-extr", "extreme.pdb", ffOPTWR },
1154         { efXVG, "-over", "overlap", ffOPTWR },   { efXPM, "-inpr", "inprod", ffOPTWR }
1155     };
1156 #define NFILE asize(fnm)
1157
1158     if (!parse_common_args(
1159                 &argc, argv, PCA_CAN_TIME | PCA_TIME_UNIT | PCA_CAN_VIEW, NFILE, fnm, NPA, pa, asize(desc), desc, 0, nullptr, &oenv))
1160     {
1161         return 0;
1162     }
1163
1164     indexfile = ftp2fn_null(efNDX, NFILE, fnm);
1165
1166     VecFile        = opt2fn("-v", NFILE, fnm);
1167     Vec2File       = opt2fn_null("-v2", NFILE, fnm);
1168     topfile        = ftp2fn(efTPS, NFILE, fnm);
1169     EigFile        = opt2fn_null("-eig", NFILE, fnm);
1170     Eig2File       = opt2fn_null("-eig2", NFILE, fnm);
1171     CompFile       = opt2fn_null("-comp", NFILE, fnm);
1172     RmsfFile       = opt2fn_null("-rmsf", NFILE, fnm);
1173     ProjOnVecFile  = opt2fn_null("-proj", NFILE, fnm);
1174     TwoDPlotFile   = opt2fn_null("-2d", NFILE, fnm);
1175     ThreeDPlotFile = opt2fn_null("-3d", NFILE, fnm);
1176     FilterFile     = opt2fn_null("-filt", NFILE, fnm);
1177     ExtremeFile    = opt2fn_null("-extr", NFILE, fnm);
1178     OverlapFile    = opt2fn_null("-over", NFILE, fnm);
1179     InpMatFile     = ftp2fn_null(efXPM, NFILE, fnm);
1180
1181     bProj = (ProjOnVecFile != nullptr) || (TwoDPlotFile != nullptr) || (ThreeDPlotFile != nullptr)
1182             || (FilterFile != nullptr) || (ExtremeFile != nullptr);
1183     bFirstLastSet = opt2parg_bSet("-first", NPA, pa) && opt2parg_bSet("-last", NPA, pa);
1184     bFirstToLast  = (CompFile != nullptr) || (RmsfFile != nullptr) || (ProjOnVecFile != nullptr)
1185                    || (FilterFile != nullptr) || (OverlapFile != nullptr)
1186                    || (((ExtremeFile != nullptr) || (InpMatFile != nullptr)) && bFirstLastSet);
1187     bVec2 = (Vec2File != nullptr) || (OverlapFile != nullptr) || (InpMatFile != nullptr);
1188     bM    = (RmsfFile != nullptr) || bProj;
1189     bTraj = (ProjOnVecFile != nullptr) || (FilterFile != nullptr)
1190             || ((ExtremeFile != nullptr) && (max == 0)) || (TwoDPlotFile != nullptr)
1191             || (ThreeDPlotFile != nullptr);
1192     bIndex = bM || bProj;
1193     bTPS   = ftp2bSet(efTPS, NFILE, fnm) || bM || bTraj || (FilterFile != nullptr)
1194            || (bIndex && (indexfile != nullptr));
1195     bCompare = (Vec2File != nullptr) || (Eig2File != nullptr);
1196     bPDB3D   = fn2ftp(ThreeDPlotFile) == efPDB;
1197
1198     read_eigenvectors(
1199             VecFile, &natoms, &bFit1, &xref1, &bDMR1, &xav1, &bDMA1, &nvec1, &eignr1, &eigvec1, &eigval1);
1200     neig1 = std::min(nvec1, DIM * natoms);
1201     if (nvec1 != DIM * natoms)
1202     {
1203         fprintf(stderr,
1204                 "Warning: number of eigenvectors %d does not match three times\n"
1205                 "the number of atoms %d in %s. Using %d eigenvectors.\n\n",
1206                 nvec1,
1207                 natoms,
1208                 VecFile,
1209                 neig1);
1210     }
1211
1212     /* Overwrite eigenvalues from separate files if the user provides them */
1213     if (EigFile != nullptr)
1214     {
1215         int neig_tmp = read_xvg(EigFile, &xvgdata, &i);
1216         if (neig_tmp != neig1)
1217         {
1218             fprintf(stderr,
1219                     "Warning: number of eigenvalues in xvg file (%d) does not mtch trr file (%d)\n",
1220                     neig1,
1221                     natoms);
1222         }
1223         neig1 = neig_tmp;
1224         srenew(eigval1, neig1);
1225         for (j = 0; j < neig1; j++)
1226         {
1227             real tmp   = eigval1[j];
1228             eigval1[j] = xvgdata[1][j];
1229             if (debug && (eigval1[j] != tmp))
1230             {
1231                 fprintf(debug, "Replacing eigenvalue %d. From trr: %10g, from xvg: %10g\n", j, tmp, eigval1[j]);
1232             }
1233         }
1234         for (j = 0; j < i; j++)
1235         {
1236             sfree(xvgdata[j]);
1237         }
1238         sfree(xvgdata);
1239         fprintf(stderr, "Read %d eigenvalues from %s\n", neig1, EigFile);
1240     }
1241
1242     if (bEntropy)
1243     {
1244         if (bDMA1)
1245         {
1246             gmx_fatal(FARGS,
1247                       "Can not calculate entropies from mass-weighted eigenvalues, redo the "
1248                       "analysis without mass-weighting");
1249         }
1250         printf("The Entropy due to the Schlitter formula is %g J/mol K\n",
1251                calcSchlitterEntropy(gmx::arrayRefFromArray(eigval1, neig1), temp, FALSE));
1252         printf("The Entropy due to the Quasiharmonic analysis is %g J/mol K\n",
1253                calcQuasiHarmonicEntropy(gmx::arrayRefFromArray(eigval1, neig1), temp, FALSE, 1.0));
1254     }
1255
1256     if (bVec2)
1257     {
1258         if (!Vec2File)
1259         {
1260             gmx_fatal(FARGS, "Need a second eigenvector file to do this analysis.");
1261         }
1262         int natoms2;
1263         read_eigenvectors(
1264                 Vec2File, &natoms2, &bFit2, &xref2, &bDMR2, &xav2, &bDMA2, &nvec2, &eignr2, &eigvec2, &eigval2);
1265
1266         neig2 = std::min(nvec2, DIM * natoms2);
1267         if (neig2 != neig1)
1268         {
1269             gmx_fatal(FARGS, "Dimensions in the eigenvector files don't match");
1270         }
1271     }
1272     else
1273     {
1274         nvec2 = 0;
1275         neig2 = 0;
1276     }
1277
1278     if (Eig2File != nullptr)
1279     {
1280         neig2 = read_xvg(Eig2File, &xvgdata, &i);
1281         srenew(eigval2, neig2);
1282         for (j = 0; j < neig2; j++)
1283         {
1284             eigval2[j] = xvgdata[1][j];
1285         }
1286         for (j = 0; j < i; j++)
1287         {
1288             sfree(xvgdata[j]);
1289         }
1290         sfree(xvgdata);
1291         fprintf(stderr, "Read %d eigenvalues from %s\n", neig2, Eig2File);
1292     }
1293
1294
1295     if ((!bFit1 || xref1) && !bDMR1 && !bDMA1)
1296     {
1297         bM = FALSE;
1298     }
1299     if ((xref1 == nullptr) && (bM || bTraj))
1300     {
1301         bTPS = TRUE;
1302     }
1303
1304     xtop  = nullptr;
1305     nfit  = 0;
1306     ifit  = nullptr;
1307     w_rls = nullptr;
1308
1309     if (!bTPS)
1310     {
1311         bTop = FALSE;
1312     }
1313     else
1314     {
1315         bTop = read_tps_conf(ftp2fn(efTPS, NFILE, fnm), &top, &pbcType, &xtop, nullptr, topbox, bM);
1316         atoms = &top.atoms;
1317         gpbc  = gmx_rmpbc_init(&top.idef, pbcType, atoms->nr);
1318         gmx_rmpbc(gpbc, atoms->nr, topbox, xtop);
1319         /* Fitting is only required for the projection */
1320         if (bProj && bFit1)
1321         {
1322             if (xref1 == nullptr)
1323             {
1324                 printf("\nNote: the structure in %s should be the same\n"
1325                        "      as the one used for the fit in g_covar\n",
1326                        topfile);
1327             }
1328             printf("\nSelect the index group that was used for the least squares fit in g_covar\n");
1329             get_index(atoms, indexfile, 1, &nfit, &ifit, &grpname);
1330
1331             snew(w_rls, atoms->nr);
1332             for (i = 0; (i < nfit); i++)
1333             {
1334                 if (bDMR1)
1335                 {
1336                     w_rls[ifit[i]] = atoms->atom[ifit[i]].m;
1337                 }
1338                 else
1339                 {
1340                     w_rls[ifit[i]] = 1.0;
1341                 }
1342             }
1343
1344             snew(xrefp, atoms->nr);
1345             if (xref1 != nullptr)
1346             {
1347                 /* Safety check between selected fit-group and reference structure read from the eigenvector file */
1348                 if (natoms != nfit)
1349                 {
1350                     gmx_fatal(FARGS,
1351                               "you selected a group with %d elements instead of %d, your selection "
1352                               "does not fit the reference structure in the eigenvector file.",
1353                               nfit,
1354                               natoms);
1355                 }
1356                 for (i = 0; (i < nfit); i++)
1357                 {
1358                     copy_rvec(xref1[i], xrefp[ifit[i]]);
1359                 }
1360             }
1361             else
1362             {
1363                 /* The top coordinates are the fitting reference */
1364                 for (i = 0; (i < nfit); i++)
1365                 {
1366                     copy_rvec(xtop[ifit[i]], xrefp[ifit[i]]);
1367                 }
1368                 reset_x(nfit, ifit, atoms->nr, nullptr, xrefp, w_rls);
1369             }
1370         }
1371         gmx_rmpbc_done(gpbc);
1372     }
1373
1374     if (bIndex)
1375     {
1376         printf("\nSelect an index group of %d elements that corresponds to the eigenvectors\n", natoms);
1377         get_index(atoms, indexfile, 1, &i, &index, &grpname);
1378         if (i != natoms)
1379         {
1380             gmx_fatal(FARGS, "you selected a group with %d elements instead of %d", i, natoms);
1381         }
1382         printf("\n");
1383     }
1384
1385     snew(sqrtm, natoms);
1386     if (bM && bDMA1)
1387     {
1388         proj_unit = "u\\S1/2\\Nnm";
1389         for (i = 0; (i < natoms); i++)
1390         {
1391             sqrtm[i] = std::sqrt(atoms->atom[index[i]].m);
1392         }
1393     }
1394     else
1395     {
1396         proj_unit = "nm";
1397         for (i = 0; (i < natoms); i++)
1398         {
1399             sqrtm[i] = 1.0;
1400         }
1401     }
1402
1403     if (bVec2)
1404     {
1405         t       = 0;
1406         totmass = 0;
1407         for (i = 0; (i < natoms); i++)
1408         {
1409             for (d = 0; (d < DIM); d++)
1410             {
1411                 t += gmx::square((xav1[i][d] - xav2[i][d]) * sqrtm[i]);
1412                 totmass += gmx::square(sqrtm[i]);
1413             }
1414         }
1415         fprintf(stdout,
1416                 "RMSD (without fit) between the two average structures:"
1417                 " %.3f (nm)\n\n",
1418                 std::sqrt(t / totmass));
1419     }
1420
1421     if (last == -1)
1422     {
1423         last = natoms * DIM;
1424     }
1425     if (first > -1)
1426     {
1427         if (bFirstToLast)
1428         {
1429             /* make an index from first to last */
1430             nout = last - first + 1;
1431             snew(iout, nout);
1432             for (i = 0; i < nout; i++)
1433             {
1434                 iout[i] = first - 1 + i;
1435             }
1436         }
1437         else if (ThreeDPlotFile)
1438         {
1439             /* make an index of first+(0,1,2) and last */
1440             nout = bPDB3D ? 4 : 3;
1441             nout = std::min(last - first + 1, nout);
1442             snew(iout, nout);
1443             iout[0] = first - 1;
1444             iout[1] = first;
1445             if (nout > 3)
1446             {
1447                 iout[2] = first + 1;
1448             }
1449             iout[nout - 1] = last - 1;
1450         }
1451         else
1452         {
1453             /* make an index of first and last */
1454             nout = 2;
1455             snew(iout, nout);
1456             iout[0] = first - 1;
1457             iout[1] = last - 1;
1458         }
1459     }
1460     else
1461     {
1462         printf("Select eigenvectors for output, end your selection with 0\n");
1463         nout = -1;
1464         iout = nullptr;
1465
1466         do
1467         {
1468             nout++;
1469             srenew(iout, nout + 1);
1470             if (1 != scanf("%d", &iout[nout]))
1471             {
1472                 gmx_fatal(FARGS, "Error reading user input");
1473             }
1474             iout[nout]--;
1475         } while (iout[nout] >= 0);
1476
1477         printf("\n");
1478     }
1479     /* make an index of the eigenvectors which are present */
1480     snew(outvec, nout);
1481     noutvec = 0;
1482     for (i = 0; i < nout; i++)
1483     {
1484         j = 0;
1485         while ((j < nvec1) && (eignr1[j] != iout[i]))
1486         {
1487             j++;
1488         }
1489         if ((j < nvec1) && (eignr1[j] == iout[i]))
1490         {
1491             outvec[noutvec] = j;
1492             noutvec++;
1493         }
1494     }
1495     fprintf(stderr, "%d eigenvectors selected for output", noutvec);
1496     if (noutvec <= 100)
1497     {
1498         fprintf(stderr, ":");
1499         for (j = 0; j < noutvec; j++)
1500         {
1501             fprintf(stderr, " %d", eignr1[outvec[j]] + 1);
1502         }
1503     }
1504     fprintf(stderr, "\n");
1505
1506     if (CompFile)
1507     {
1508         components(CompFile, natoms, eignr1, eigvec1, noutvec, outvec, oenv);
1509     }
1510
1511     if (RmsfFile)
1512     {
1513         rmsf(RmsfFile, natoms, sqrtm, eignr1, eigvec1, noutvec, outvec, eigval1, neig1, oenv);
1514     }
1515
1516     if (bProj)
1517     {
1518         project(bTraj ? opt2fn("-f", NFILE, fnm) : nullptr,
1519                 bTop ? &top : nullptr,
1520                 pbcType,
1521                 topbox,
1522                 ProjOnVecFile,
1523                 TwoDPlotFile,
1524                 ThreeDPlotFile,
1525                 FilterFile,
1526                 skip,
1527                 ExtremeFile,
1528                 bFirstLastSet,
1529                 max,
1530                 nextr,
1531                 atoms,
1532                 natoms,
1533                 index,
1534                 bFit1,
1535                 xrefp,
1536                 nfit,
1537                 ifit,
1538                 w_rls,
1539                 sqrtm,
1540                 xav1,
1541                 eignr1,
1542                 eigvec1,
1543                 noutvec,
1544                 outvec,
1545                 bSplit,
1546                 oenv);
1547     }
1548
1549     if (OverlapFile)
1550     {
1551         overlap(OverlapFile, natoms, eigvec1, nvec2, eignr2, eigvec2, noutvec, outvec, oenv);
1552     }
1553
1554     if (InpMatFile)
1555     {
1556         inprod_matrix(
1557                 InpMatFile, natoms, nvec1, eignr1, eigvec1, nvec2, eignr2, eigvec2, bFirstLastSet, noutvec, outvec);
1558     }
1559
1560     if (bCompare)
1561     {
1562         compare(natoms, nvec1, eigvec1, nvec2, eigvec2, eigval1, neig1, eigval2, neig2);
1563     }
1564
1565
1566     if (!CompFile && !bProj && !OverlapFile && !InpMatFile && !bCompare && !bEntropy)
1567     {
1568         fprintf(stderr,
1569                 "\nIf you want some output,"
1570                 " set one (or two or ...) of the output file options\n");
1571     }
1572
1573
1574     view_all(oenv, NFILE, fnm);
1575
1576     return 0;
1577 }