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.
45 #include "gromacs/commandline/pargs.h"
46 #include "gromacs/commandline/viewit.h"
47 #include "gromacs/fileio/confio.h"
48 #include "gromacs/fileio/matio.h"
49 #include "gromacs/fileio/trxio.h"
50 #include "gromacs/fileio/xvgr.h"
51 #include "gromacs/gmxana/cmat.h"
52 #include "gromacs/gmxana/gmx_ana.h"
53 #include "gromacs/linearalgebra/eigensolver.h"
54 #include "gromacs/math/do_fit.h"
55 #include "gromacs/math/vec.h"
56 #include "gromacs/pbcutil/pbc.h"
57 #include "gromacs/pbcutil/rmpbc.h"
58 #include "gromacs/random/threefry.h"
59 #include "gromacs/random/uniformintdistribution.h"
60 #include "gromacs/random/uniformrealdistribution.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/smalloc.h"
68 #include "gromacs/utility/stringutil.h"
70 /* print to two file pointers at once (i.e. stderr and log) */
71 static inline void lo_ffprintf(FILE* fp1, FILE* fp2, const char* buf)
73 fprintf(fp1, "%s", buf);
74 fprintf(fp2, "%s", buf);
77 /* just print a prepared buffer to fp1 and fp2 */
78 static inline void ffprintf(FILE* fp1, FILE* fp2, const char* buf)
80 lo_ffprintf(fp1, fp2, buf);
83 /* prepare buffer with one argument, then print to fp1 and fp2 */
84 static inline void ffprintf_d(FILE* fp1, FILE* fp2, char* buf, const char* fmt, int arg)
86 sprintf(buf, fmt, arg);
87 lo_ffprintf(fp1, fp2, buf);
90 /* prepare buffer with one argument, then print to fp1 and fp2 */
91 static inline void ffprintf_g(FILE* fp1, FILE* fp2, char* buf, const char* fmt, real arg)
93 sprintf(buf, fmt, arg);
94 lo_ffprintf(fp1, fp2, buf);
97 /* prepare buffer with one argument, then print to fp1 and fp2 */
98 static inline void ffprintf_s(FILE* fp1, FILE* fp2, char* buf, const char* fmt, const char* arg)
100 sprintf(buf, fmt, arg);
101 lo_ffprintf(fp1, fp2, buf);
104 /* prepare buffer with two arguments, then print to fp1 and fp2 */
105 static inline void ffprintf_dd(FILE* fp1, FILE* fp2, char* buf, const char* fmt, int arg1, int arg2)
107 sprintf(buf, fmt, arg1, arg2);
108 lo_ffprintf(fp1, fp2, buf);
111 /* prepare buffer with two arguments, then print to fp1 and fp2 */
112 static inline void ffprintf_gg(FILE* fp1, FILE* fp2, char* buf, const char* fmt, real arg1, real arg2)
114 sprintf(buf, fmt, arg1, arg2);
115 lo_ffprintf(fp1, fp2, buf);
118 /* prepare buffer with two arguments, then print to fp1 and fp2 */
119 static inline void ffprintf_ss(FILE* fp1, FILE* fp2, char* buf, const char* fmt, const char* arg1, const char* arg2)
121 sprintf(buf, fmt, arg1, arg2);
122 lo_ffprintf(fp1, fp2, buf);
137 static void mc_optimize(FILE* log,
145 gmx_output_env_t* oenv)
148 real ecur, enext, emin, prob, enorm;
149 int i, j, iswap, jswap, nn, nuphill = 0;
154 seed = static_cast<int>(gmx::makeRandomSeed());
156 gmx::DefaultRandomEngine rng(seed);
160 fprintf(stderr, "Can not do Monte Carlo optimization with a non-square matrix.\n");
163 printf("\nDoing Monte Carlo optimization to find the smoothest trajectory\n");
164 printf("by reordering the frames to minimize the path between the two structures\n");
165 printf("that have the largest pairwise RMSD.\n");
166 printf("Using random seed %d.\n", seed);
169 enorm = m->mat[0][0];
170 for (i = 0; (i < m->n1); i++)
172 for (j = 0; (j < m->nn); j++)
174 if (m->mat[i][j] > enorm)
176 enorm = m->mat[i][j];
182 if ((iswap == -1) || (jswap == -1))
184 fprintf(stderr, "Matrix contains identical values in all fields\n");
187 swap_rows(m, 0, iswap);
188 swap_rows(m, m->n1 - 1, jswap);
189 emin = ecur = mat_energy(m);
190 printf("Largest distance %g between %d and %d. Energy: %g.\n", enorm, iswap, jswap, emin);
194 /* Initiate and store global minimum */
195 minimum = init_mat(nn, m->b1D);
197 copy_t_mat(minimum, m);
201 fp = xvgropen(conv, "Convergence of the MC optimization", "Energy", "Step", oenv);
204 gmx::UniformIntDistribution<int> intDistNN(1, nn - 2); // [1,nn-2]
205 gmx::UniformRealDistribution<real> realDistOne; // [0,1)
207 for (i = 0; (i < maxiter); i++)
209 /* Generate new swapping candidates */
212 iswap = intDistNN(rng);
213 jswap = intDistNN(rng);
214 } while ((iswap == jswap) || (iswap >= nn - 1) || (jswap >= nn - 1));
216 /* Apply swap and compute energy */
217 swap_rows(m, iswap, jswap);
218 enext = mat_energy(m);
220 /* Compute probability */
222 if ((enext < ecur) || (i < nrandom))
227 /* Store global minimum */
228 copy_t_mat(minimum, m);
234 /* Try Monte Carlo step */
235 prob = std::exp(-(enext - ecur) / (enorm * kT));
238 if (prob == 1 || realDistOne(rng) < prob)
245 fprintf(log, "Iter: %d Swapped %4d and %4d (energy: %g prob: %g)\n", i, iswap, jswap,
249 fprintf(fp, "%6d %10g\n", i, enext);
255 swap_rows(m, jswap, iswap);
258 fprintf(log, "%d uphill steps were taken during optimization\n", nuphill);
260 /* Now swap the matrix to get it into global minimum mode */
261 copy_t_mat(m, minimum);
263 fprintf(log, "Global minimum energy %g\n", mat_energy(minimum));
264 fprintf(log, "Global minimum energy %g\n", mat_energy(m));
265 fprintf(log, "Swapped time and frame indices and RMSD to next neighbor:\n");
266 for (i = 0; (i < m->nn); i++)
268 fprintf(log, "%10g %5d %10g\n", time[m->m_ind[i]], m->m_ind[i],
269 (i < m->nn - 1) ? m->mat[m->m_ind[i]][m->m_ind[i + 1]] : 0);
278 static void calc_dist(int nind, rvec x[], real** d)
284 for (i = 0; (i < nind - 1); i++)
287 for (j = i + 1; (j < nind); j++)
289 /* Should use pbc_dx when analysing multiple molecueles,
290 * but the box is not stored for every frame.
292 rvec_sub(xi, x[j], dx);
298 static real rms_dist(int isize, real** d, real** d_r)
304 for (i = 0; (i < isize - 1); i++)
306 for (j = i + 1; (j < isize); j++)
308 r = d[i][j] - d_r[i][j];
312 r2 /= gmx::exactDiv(isize * (isize - 1), 2);
314 return std::sqrt(r2);
317 static bool rms_dist_comp(const t_dist& a, const t_dist& b)
319 return a.dist < b.dist;
322 static bool clust_id_comp(const t_clustid& a, const t_clustid& b)
324 return a.clust < b.clust;
327 static bool nrnb_comp(const t_nnb& a, const t_nnb& b)
329 /* return b<a, we want highest first */
333 static void gather(t_mat* m, real cutoff, t_clusters* clust)
337 int i, j, k, nn, cid, n1, diff;
340 /* First we sort the entries in the RMSD matrix */
342 nn = ((n1 - 1) * n1) / 2;
344 for (i = k = 0; (i < n1); i++)
346 for (j = i + 1; (j < n1); j++, k++)
350 d[k].dist = m->mat[i][j];
355 gmx_incons("gather algortihm");
357 std::sort(d, d + nn, rms_dist_comp);
359 /* Now we make a cluster index for all of the conformations */
362 /* Now we check the closest structures, and equalize their cluster numbers */
363 fprintf(stderr, "Linking structures ");
366 fprintf(stderr, "*");
368 for (k = 0; (k < nn) && (d[k].dist < cutoff); k++)
370 diff = c[d[k].j].clust - c[d[k].i].clust;
376 c[d[k].j].clust = c[d[k].i].clust;
380 c[d[k].i].clust = c[d[k].j].clust;
385 fprintf(stderr, "\nSorting and renumbering clusters\n");
386 /* Sort on cluster number */
387 std::sort(c, c + n1, clust_id_comp);
389 /* Renumber clusters */
391 for (k = 1; k < n1; k++)
393 if (c[k].clust != c[k - 1].clust)
395 c[k - 1].clust = cid;
400 c[k - 1].clust = cid;
403 c[k - 1].clust = cid;
406 for (k = 0; (k < n1); k++)
408 fprintf(debug, "Cluster index for conformation %d: %d\n", c[k].conf, c[k].clust);
412 for (k = 0; k < n1; k++)
414 clust->cl[c[k].conf] = c[k].clust;
421 static gmx_bool jp_same(int** nnb, int i, int j, int P)
427 for (k = 0; nnb[i][k] >= 0; k++)
429 bIn = bIn || (nnb[i][k] == j);
437 for (k = 0; nnb[j][k] >= 0; k++)
439 bIn = bIn || (nnb[j][k] == i);
447 for (ii = 0; nnb[i][ii] >= 0; ii++)
449 for (jj = 0; nnb[j][jj] >= 0; jj++)
451 if ((nnb[i][ii] == nnb[j][jj]) && (nnb[i][ii] != -1))
461 static void jarvis_patrick(int n1, real** mat, int M, int P, real rmsdcut, t_clusters* clust)
466 int i, j, k, cid, diff, maxval;
468 real** mcpy = nullptr;
475 /* First we sort the entries in the RMSD matrix row by row.
476 * This gives us the nearest neighbor list.
480 for (i = 0; (i < n1); i++)
482 for (j = 0; (j < n1); j++)
485 row[j].dist = mat[i][j];
487 std::sort(row, row + n1, rms_dist_comp);
490 /* Put the M nearest neighbors in the list */
492 for (j = k = 0; (k < M) && (j < n1) && (mat[i][row[j].j] < rmsdcut); j++)
496 nnb[i][k] = row[j].j;
504 /* Put all neighbors nearer than rmsdcut in the list */
507 for (j = 0; (j < n1) && (mat[i][row[j].j] < rmsdcut); j++)
514 srenew(nnb[i], maxval);
516 nnb[i][k] = row[j].j;
522 srenew(nnb[i], maxval + 1);
530 fprintf(debug, "Nearest neighborlist. M = %d, P = %d\n", M, P);
531 for (i = 0; (i < n1); i++)
533 fprintf(debug, "i:%5d nbs:", i);
534 for (j = 0; nnb[i][j] >= 0; j++)
536 fprintf(debug, "%5d[%5.3f]", nnb[i][j], mat[i][nnb[i][j]]);
538 fprintf(debug, "\n");
543 fprintf(stderr, "Linking structures ");
544 /* Use mcpy for temporary storage of booleans */
545 mcpy = mk_matrix(n1, n1, FALSE);
546 for (i = 0; i < n1; i++)
548 for (j = i + 1; j < n1; j++)
550 mcpy[i][j] = static_cast<real>(jp_same(nnb, i, j, P));
555 fprintf(stderr, "*");
557 for (i = 0; i < n1; i++)
559 for (j = i + 1; j < n1; j++)
561 if (mcpy[i][j] != 0.0F)
563 diff = c[j].clust - c[i].clust;
569 c[j].clust = c[i].clust;
573 c[i].clust = c[j].clust;
581 fprintf(stderr, "\nSorting and renumbering clusters\n");
582 /* Sort on cluster number */
583 std::sort(c, c + n1, clust_id_comp);
585 /* Renumber clusters */
587 for (k = 1; k < n1; k++)
589 if (c[k].clust != c[k - 1].clust)
591 c[k - 1].clust = cid;
596 c[k - 1].clust = cid;
599 c[k - 1].clust = cid;
601 for (k = 0; k < n1; k++)
603 clust->cl[c[k].conf] = c[k].clust;
607 for (k = 0; (k < n1); k++)
609 fprintf(debug, "Cluster index for conformation %d: %d\n", c[k].conf, c[k].clust);
613 done_matrix(n1, &mcpy);
616 for (i = 0; (i < n1); i++)
623 static void dump_nnb(FILE* fp, const char* title, int n1, t_nnb* nnb)
627 /* dump neighbor list */
628 fprintf(fp, "%s", title);
629 for (i = 0; (i < n1); i++)
631 fprintf(fp, "i:%5d #:%5d nbs:", i, nnb[i].nr);
632 for (j = 0; j < nnb[i].nr; j++)
634 fprintf(fp, "%5d", nnb[i].nb[j]);
640 static void gromos(int n1, real** mat, real rmsdcut, t_clusters* clust)
643 int i, j, k, j1, maxval;
645 /* Put all neighbors nearer than rmsdcut in the list */
646 fprintf(stderr, "Making list of neighbors within cutoff ");
648 for (i = 0; (i < n1); i++)
652 /* put all neighbors within cut-off in list */
653 for (j = 0; j < n1; j++)
655 if (mat[i][j] < rmsdcut)
660 srenew(nnb[i].nb, maxval);
666 /* store nr of neighbors, we'll need that */
668 if (i % (1 + n1 / 100) == 0)
670 fprintf(stderr, "%3d%%\b\b\b\b", (i * 100 + 1) / n1);
673 fprintf(stderr, "%3d%%\n", 100);
675 /* sort neighbor list on number of neighbors, largest first */
676 std::sort(nnb, nnb + n1, nrnb_comp);
680 dump_nnb(debug, "Nearest neighborlist after sort.\n", n1, nnb);
683 /* turn first structure with all its neighbors (largest) into cluster
684 remove them from pool of structures and repeat for all remaining */
685 fprintf(stderr, "Finding clusters %4d", 0);
686 /* cluster id's start at 1: */
690 /* set cluster id (k) for first item in neighborlist */
691 for (j = 0; j < nnb[0].nr; j++)
693 clust->cl[nnb[0].nb[j]] = k;
699 /* adjust number of neighbors for others, taking removals into account: */
700 for (i = 1; i < n1 && nnb[i].nr; i++)
703 for (j = 0; j < nnb[i].nr; j++)
705 /* if this neighbor wasn't removed */
706 if (clust->cl[nnb[i].nb[j]] == 0)
708 /* shift the rest (j1<=j) */
709 nnb[i].nb[j1] = nnb[i].nb[j];
714 /* now j1 is the new number of neighbors */
717 /* sort again on nnb[].nr, because we have new # neighbors: */
718 /* but we only need to sort upto i, i.e. when nnb[].nr>0 */
719 std::sort(nnb, nnb + i, nrnb_comp);
721 fprintf(stderr, "\b\b\b\b%4d", k);
725 fprintf(stderr, "\n");
729 fprintf(debug, "Clusters (%d):\n", k);
730 for (i = 0; i < n1; i++)
732 fprintf(debug, " %3d", clust->cl[i]);
734 fprintf(debug, "\n");
740 static rvec** read_whole_trj(const char* fn,
748 const gmx_output_env_t* oenv,
763 natom = read_first_x(oenv, &status, fn, &t, &x, box);
765 int clusterIndex = 0;
770 gmx_rmpbc(gpbc, natom, box, x);
772 if (clusterIndex >= max_nf)
776 srenew(*time, max_nf);
777 srenew(*boxes, max_nf);
778 srenew(*frameindices, max_nf);
782 snew(xx[clusterIndex], isize);
783 /* Store only the interesting atoms */
784 for (j = 0; (j < isize); j++)
786 copy_rvec(x[index[j]], xx[clusterIndex][j]);
788 (*time)[clusterIndex] = t;
789 copy_mat(box, (*boxes)[clusterIndex]);
790 (*frameindices)[clusterIndex] = nframes_read(status);
794 } while (read_next_x(oenv, status, &t, x, box));
795 fprintf(stderr, "Allocated %zu bytes for frames\n", (max_nf * isize * sizeof(**xx)));
796 fprintf(stderr, "Read %d frames from trajectory %s\n", clusterIndex, fn);
797 *nframe = clusterIndex;
803 static int plot_clusters(int nf, real** mat, t_clusters* clust, int minstruct)
805 int i, j, ncluster, ci;
806 int *cl_id, *nstruct, *strind;
811 for (i = 0; i < nf; i++)
814 cl_id[i] = clust->cl[i];
818 for (i = 0; i < nf; i++)
820 if (nstruct[i] >= minstruct)
823 for (j = 0; (j < nf); j++)
827 strind[j] = ncluster;
833 fprintf(stderr, "There are %d clusters with at least %d conformations\n", ncluster, minstruct);
835 for (i = 0; (i < nf); i++)
838 for (j = 0; j < i; j++)
840 if ((ci == cl_id[j]) && (nstruct[ci] >= minstruct))
842 /* color different clusters with different colors, as long as
843 we don't run out of colors */
844 mat[i][j] = strind[i];
859 static void mark_clusters(int nf, real** mat, real val, t_clusters* clust)
863 for (i = 0; i < nf; i++)
865 for (j = 0; j < i; j++)
867 if (clust->cl[i] == clust->cl[j])
879 static char* parse_filename(const char* fn, int maxnr)
886 if (std::strchr(fn, '%'))
888 gmx_fatal(FARGS, "will not number filename %s containing '%c'", fn, '%');
890 /* number of digits needed in numbering */
891 i = static_cast<int>((std::log(static_cast<real>(maxnr)) / std::log(10.0)) + 1);
892 /* split fn and ext */
893 ext = std::strrchr(fn, '.');
896 gmx_fatal(FARGS, "cannot separate extension in filename %s", fn);
899 /* insert e.g. '%03d' between fn and ext */
900 sprintf(buf, "%s%%0%dd.%s", fn, i, ext);
901 snew(fnout, std::strlen(buf) + 1);
902 std::strcpy(fnout, buf);
907 static void ana_trans(t_clusters* clust,
910 const char* ntransfn,
914 const gmx_output_env_t* oenv)
919 int i, ntranst, maxtrans;
922 snew(ntrans, clust->ncl);
923 snew(trans, clust->ncl);
924 snew(axis, clust->ncl);
925 for (i = 0; i < clust->ncl; i++)
928 snew(trans[i], clust->ncl);
932 for (i = 1; i < nf; i++)
934 if (clust->cl[i] != clust->cl[i - 1])
937 ntrans[clust->cl[i - 1] - 1]++;
938 ntrans[clust->cl[i] - 1]++;
939 trans[clust->cl[i - 1] - 1][clust->cl[i] - 1]++;
940 maxtrans = static_cast<int>(std::max(static_cast<real>(maxtrans),
941 trans[clust->cl[i] - 1][clust->cl[i - 1] - 1]));
944 ffprintf_dd(stderr, log, buf,
945 "Counted %d transitions in total, "
946 "max %d between two specific clusters\n",
950 fp = gmx_ffopen(transfn, "w");
951 i = std::min(maxtrans + 1, 80);
952 write_xpm(fp, 0, "Cluster Transitions", "# transitions", "from cluster", "to cluster",
953 clust->ncl, clust->ncl, axis, axis, trans, 0, maxtrans, rlo, rhi, &i);
958 fp = xvgropen(ntransfn, "Cluster Transitions", "Cluster #", "# transitions", oenv);
959 for (i = 0; i < clust->ncl; i++)
961 fprintf(fp, "%5d %5d\n", i + 1, ntrans[i]);
966 for (i = 0; i < clust->ncl; i++)
974 static void analyze_clusters(int nf,
992 const char* ntransfn,
993 const char* clustidfn,
994 const char* clustndxfn,
1003 const gmx_output_env_t* oenv)
1005 FILE* size_fp = nullptr;
1006 FILE* ndxfn = nullptr;
1007 char buf[STRLEN], buf1[40], buf2[40], buf3[40], *trxsfn;
1008 t_trxstatus* trxout = nullptr;
1009 t_trxstatus* trxsout = nullptr;
1010 int i, i1, cl, nstr, *structure, first = 0, midstr;
1011 gmx_bool* bWrite = nullptr;
1012 real r, clrmsd, midrmsd;
1013 rvec* xav = nullptr;
1018 ffprintf_d(stderr, log, buf, "\nFound %d clusters\n\n", clust->ncl);
1022 /* do we write all structures? */
1025 trxsfn = parse_filename(trxfn, std::max(write_ncl, clust->ncl));
1028 ffprintf_ss(stderr, log, buf, "Writing %s structure for each cluster to %s\n",
1029 bAverage ? "average" : "middle", trxfn);
1032 /* find out what we want to tell the user:
1033 Writing [all structures|structures with rmsd > %g] for
1034 {all|first %d} clusters {with more than %d structures} to %s */
1037 sprintf(buf1, "structures with rmsd > %g", rmsmin);
1041 sprintf(buf1, "all structures");
1043 buf2[0] = buf3[0] = '\0';
1044 if (write_ncl >= clust->ncl)
1048 sprintf(buf2, "all ");
1053 sprintf(buf2, "the first %d ", write_ncl);
1057 sprintf(buf3, " with more than %d structures", write_nst);
1059 sprintf(buf, "Writing %s for %sclusters%s to %s\n", buf1, buf2, buf3, trxsfn);
1060 ffprintf(stderr, log, buf);
1063 /* Prepare a reference structure for the orientation of the clusters */
1066 reset_x(ifsize, fitidx, natom, nullptr, xtps, mass);
1068 trxout = open_trx(trxfn, "w");
1069 /* Calculate the average structure in each cluster, *
1070 * all structures are fitted to the first struture of the cluster */
1072 GMX_ASSERT(xav, "");
1075 if (transfn || ntransfn)
1077 ana_trans(clust, nf, transfn, ntransfn, log, rlo, rhi, oenv);
1082 FILE* fp = xvgropen(clustidfn, "Clusters", output_env_get_xvgr_tlabel(oenv), "Cluster #", oenv);
1083 if (output_env_get_print_xvgr_codes(oenv))
1085 fprintf(fp, "@ s0 symbol 2\n");
1086 fprintf(fp, "@ s0 symbol size 0.2\n");
1087 fprintf(fp, "@ s0 linestyle 0\n");
1089 for (i = 0; i < nf; i++)
1091 fprintf(fp, "%8g %8d\n", time[i], clust->cl[i]);
1097 size_fp = xvgropen(sizefn, "Cluster Sizes", "Cluster #", "# Structures", oenv);
1098 if (output_env_get_print_xvgr_codes(oenv))
1100 fprintf(size_fp, "@g%d type %s\n", 0, "bar");
1103 if (clustndxfn && frameindices)
1105 ndxfn = gmx_ffopen(clustndxfn, "w");
1108 snew(structure, nf);
1109 fprintf(log, "\n%3s | %3s %4s | %6s %4s | cluster members\n", "cl.", "#st", "rmsd", "middle",
1111 for (cl = 1; cl <= clust->ncl; cl++)
1113 /* prepare structures (fit, middle, average) */
1116 for (i = 0; i < natom; i++)
1122 for (i1 = 0; i1 < nf; i1++)
1124 if (clust->cl[i1] == cl)
1126 structure[nstr] = i1;
1128 if (trxfn && (bAverage || write_ncl))
1132 reset_x(ifsize, fitidx, natom, nullptr, xx[i1], mass);
1140 do_fit(natom, mass, xx[first], xx[i1]);
1144 for (i = 0; i < natom; i++)
1146 rvec_inc(xav[i], xx[i1][i]);
1154 fprintf(size_fp, "%8d %8d\n", cl, nstr);
1158 fprintf(ndxfn, "[Cluster_%04d]\n", cl);
1163 for (i1 = 0; i1 < nstr; i1++)
1168 for (i = 0; i < nstr; i++)
1172 r += rmsd[structure[i]][structure[i1]];
1176 r += rmsd[structure[i1]][structure[i]];
1183 midstr = structure[i1];
1190 /* dump cluster info to logfile */
1193 sprintf(buf1, "%6.3f", clrmsd);
1198 sprintf(buf2, "%5.3f", midrmsd);
1206 sprintf(buf1, "%5s", "");
1207 sprintf(buf2, "%5s", "");
1209 fprintf(log, "%3d | %3d %s | %6g%s |", cl, nstr, buf1, time[midstr], buf2);
1210 for (i = 0; i < nstr; i++)
1212 if ((i % 7 == 0) && i)
1214 sprintf(buf, "\n%3s | %3s %4s | %6s %4s |", "", "", "", "", "");
1217 fprintf(ndxfn, "\n");
1225 fprintf(log, "%s %6g", buf, time[i1]);
1228 fprintf(ndxfn, " %6d", frameindices[i1] + 1);
1234 fprintf(ndxfn, "\n");
1237 /* write structures to trajectory file(s) */
1242 for (i = 0; i < nstr; i++)
1247 if (cl < write_ncl + 1 && nstr > write_nst)
1249 /* Dump all structures for this cluster */
1250 /* generate numbered filename (there is a %d in trxfn!) */
1251 sprintf(buf, trxsfn, cl);
1252 trxsout = open_trx(buf, "w");
1253 for (i = 0; i < nstr; i++)
1258 for (i1 = 0; i1 < i && bWrite[i]; i1++)
1262 bWrite[i] = rmsd[structure[i1]][structure[i]] > rmsmin;
1268 write_trx(trxsout, iosize, outidx, atoms, i, time[structure[i]],
1269 boxes[structure[i]], xx[structure[i]], nullptr, nullptr);
1274 /* Dump the average structure for this cluster */
1277 for (i = 0; i < natom; i++)
1279 svmul(1.0 / nstr, xav[i], xav[i]);
1284 for (i = 0; i < natom; i++)
1286 copy_rvec(xx[midstr][i], xav[i]);
1290 reset_x(ifsize, fitidx, natom, nullptr, xav, mass);
1295 do_fit(natom, mass, xtps, xav);
1297 write_trx(trxout, iosize, outidx, atoms, cl, time[midstr], boxes[midstr], xav, nullptr, nullptr);
1326 static void convert_mat(t_matrix* mat, t_mat* rms)
1331 matrix2real(mat, rms->mat);
1333 for (i = 0; i < mat->nx; i++)
1335 for (j = i; j < mat->nx; j++)
1337 rms->sumrms += rms->mat[i][j];
1338 rms->maxrms = std::max(rms->maxrms, rms->mat[i][j]);
1341 rms->minrms = std::min(rms->minrms, rms->mat[i][j]);
1348 int gmx_cluster(int argc, char* argv[])
1350 const char* desc[] = {
1351 "[THISMODULE] can cluster structures using several different methods.",
1352 "Distances between structures can be determined from a trajectory",
1353 "or read from an [REF].xpm[ref] matrix file with the [TT]-dm[tt] option.",
1354 "RMS deviation after fitting or RMS deviation of atom-pair distances",
1355 "can be used to define the distance between structures.[PAR]",
1357 "single linkage: add a structure to a cluster when its distance to any",
1358 "element of the cluster is less than [TT]cutoff[tt].[PAR]",
1360 "Jarvis Patrick: add a structure to a cluster when this structure",
1361 "and a structure in the cluster have each other as neighbors and",
1362 "they have a least [TT]P[tt] neighbors in common. The neighbors",
1363 "of a structure are the M closest structures or all structures within",
1364 "[TT]cutoff[tt].[PAR]",
1366 "Monte Carlo: reorder the RMSD matrix using Monte Carlo such that",
1367 "the order of the frames is using the smallest possible increments.",
1368 "With this it is possible to make a smooth animation going from one",
1369 "structure to another with the largest possible (e.g.) RMSD between",
1370 "them, however the intermediate steps should be as small as possible.",
1371 "Applications could be to visualize a potential of mean force",
1372 "ensemble of simulations or a pulling simulation. Obviously the user",
1373 "has to prepare the trajectory well (e.g. by not superimposing frames).",
1374 "The final result can be inspect visually by looking at the matrix",
1375 "[REF].xpm[ref] file, which should vary smoothly from bottom to top.[PAR]",
1377 "diagonalization: diagonalize the RMSD matrix.[PAR]",
1379 "gromos: use algorithm as described in Daura [IT]et al.[it]",
1380 "([IT]Angew. Chem. Int. Ed.[it] [BB]1999[bb], [IT]38[it], pp 236-240).",
1381 "Count number of neighbors using cut-off, take structure with",
1382 "largest number of neighbors with all its neighbors as cluster",
1383 "and eliminate it from the pool of clusters. Repeat for remaining",
1384 "structures in pool.[PAR]",
1386 "When the clustering algorithm assigns each structure to exactly one",
1387 "cluster (single linkage, Jarvis Patrick and gromos) and a trajectory",
1388 "file is supplied, the structure with",
1389 "the smallest average distance to the others or the average structure",
1390 "or all structures for each cluster will be written to a trajectory",
1391 "file. When writing all structures, separate numbered files are made",
1392 "for each cluster.[PAR]",
1394 "Two output files are always written:",
1396 " * [TT]-o[tt] writes the RMSD values in the upper left half of the matrix",
1397 " and a graphical depiction of the clusters in the lower right half",
1398 " When [TT]-minstruct[tt] = 1 the graphical depiction is black",
1399 " when two structures are in the same cluster.",
1400 " When [TT]-minstruct[tt] > 1 different colors will be used for each",
1402 " * [TT]-g[tt] writes information on the options used and a detailed list",
1403 " of all clusters and their members.",
1406 "Additionally, a number of optional output files can be written:",
1408 " * [TT]-dist[tt] writes the RMSD distribution.",
1409 " * [TT]-ev[tt] writes the eigenvectors of the RMSD matrix",
1410 " diagonalization.",
1411 " * [TT]-sz[tt] writes the cluster sizes.",
1412 " * [TT]-tr[tt] writes a matrix of the number transitions between",
1414 " * [TT]-ntr[tt] writes the total number of transitions to or from",
1416 " * [TT]-clid[tt] writes the cluster number as a function of time.",
1417 " * [TT]-clndx[tt] writes the frame numbers corresponding to the clusters to the",
1418 " specified index file to be read into trjconv.",
1419 " * [TT]-cl[tt] writes average (with option [TT]-av[tt]) or central",
1420 " structure of each cluster or writes numbered files with cluster members",
1421 " for a selected set of clusters (with option [TT]-wcl[tt], depends on",
1422 " [TT]-nst[tt] and [TT]-rmsmin[tt]). The center of a cluster is the",
1423 " structure with the smallest average RMSD from all other structures",
1428 int nf = 0, i, i1, i2, j;
1432 matrix* boxes = nullptr;
1433 rvec * xtps, *usextps, *x1, **xx = nullptr;
1434 const char *fn, *trx_out_fn;
1436 t_mat * rms, *orig = nullptr;
1443 int isize = 0, ifsize = 0, iosize = 0;
1444 int * index = nullptr, *fitidx = nullptr, *outidx = nullptr, *frameindices = nullptr;
1446 real rmsd, **d1, **d2, *time = nullptr, time_invfac, *mass = nullptr;
1447 char buf[STRLEN], buf1[80];
1448 gmx_bool bAnalyze, bUseRmsdCut, bJP_RMSD = FALSE, bReadMat, bReadTraj, bPBC = TRUE;
1450 int method, ncluster = 0;
1451 static const char* methodname[] = { nullptr, "linkage", "jarvis-patrick",
1452 "monte-carlo", "diagonalization", "gromos",
1464 /* Set colors for plotting: white = zero RMS, black = maximum */
1465 static t_rgb rlo_top = { 1.0, 1.0, 1.0 };
1466 static t_rgb rhi_top = { 0.0, 0.0, 0.0 };
1467 static t_rgb rlo_bot = { 1.0, 1.0, 1.0 };
1468 static t_rgb rhi_bot = { 0.0, 0.0, 1.0 };
1469 static int nlevels = 40, skip = 1;
1470 static real scalemax = -1.0, rmsdcut = 0.1, rmsmin = 0.0;
1471 gmx_bool bRMSdist = FALSE, bBinary = FALSE, bAverage = FALSE, bFit = TRUE;
1472 static int niter = 10000, nrandom = 0, seed = 0, write_ncl = 0, write_nst = 1, minstruct = 1;
1473 static real kT = 1e-3;
1474 static int M = 10, P = 3;
1475 gmx_output_env_t* oenv;
1476 gmx_rmpbc_t gpbc = nullptr;
1479 { "-dista", FALSE, etBOOL, { &bRMSdist }, "Use RMSD of distances instead of RMS deviation" },
1484 "Discretize RMSD matrix in this number of levels" },
1489 "RMSD cut-off (nm) for two structures to be neighbor" },
1490 { "-fit", FALSE, etBOOL, { &bFit }, "Use least squares fitting before RMSD calculation" },
1491 { "-max", FALSE, etREAL, { &scalemax }, "Maximum level in RMSD matrix" },
1492 { "-skip", FALSE, etINT, { &skip }, "Only analyze every nr-th frame" },
1497 "Write average instead of middle structure for each cluster" },
1502 "Write the structures for this number of clusters to numbered files" },
1507 "Only write all structures if more than this number of structures per cluster" },
1512 "minimum rms difference with rest of cluster for writing structures" },
1513 { "-method", FALSE, etENUM, { methodname }, "Method for cluster determination" },
1518 "Minimum number of structures in cluster for coloring in the [REF].xpm[ref] file" },
1523 "Treat the RMSD matrix as consisting of 0 and 1, where the cut-off "
1524 "is given by [TT]-cutoff[tt]" },
1529 "Number of nearest neighbors considered for Jarvis-Patrick algorithm, "
1530 "0 is use cutoff" },
1535 "Number of identical nearest neighbors required to form a cluster" },
1540 "Random number seed for Monte Carlo clustering algorithm (0 means generate)" },
1541 { "-niter", FALSE, etINT, { &niter }, "Number of iterations for MC" },
1546 "The first iterations for MC may be done complete random, to shuffle the frames" },
1551 "Boltzmann weighting factor for Monte Carlo optimization "
1552 "(zero turns off uphill steps)" },
1553 { "-pbc", FALSE, etBOOL, { &bPBC }, "PBC check" }
1556 { efTRX, "-f", nullptr, ffOPTRD }, { efTPS, "-s", nullptr, ffREAD },
1557 { efNDX, nullptr, nullptr, ffOPTRD }, { efXPM, "-dm", "rmsd", ffOPTRD },
1558 { efXPM, "-om", "rmsd-raw", ffWRITE }, { efXPM, "-o", "rmsd-clust", ffWRITE },
1559 { efLOG, "-g", "cluster", ffWRITE }, { efXVG, "-dist", "rmsd-dist", ffOPTWR },
1560 { efXVG, "-ev", "rmsd-eig", ffOPTWR }, { efXVG, "-conv", "mc-conv", ffOPTWR },
1561 { efXVG, "-sz", "clust-size", ffOPTWR }, { efXPM, "-tr", "clust-trans", ffOPTWR },
1562 { efXVG, "-ntr", "clust-trans", ffOPTWR }, { efXVG, "-clid", "clust-id", ffOPTWR },
1563 { efTRX, "-cl", "clusters.pdb", ffOPTWR }, { efNDX, "-clndx", "clusters.ndx", ffOPTWR }
1565 #define NFILE asize(fnm)
1567 if (!parse_common_args(&argc, argv, PCA_CAN_VIEW | PCA_CAN_TIME | PCA_TIME_UNIT, NFILE, fnm,
1568 asize(pa), pa, asize(desc), desc, 0, nullptr, &oenv))
1574 bReadMat = opt2bSet("-dm", NFILE, fnm);
1575 bReadTraj = opt2bSet("-f", NFILE, fnm) || !bReadMat;
1576 if (opt2parg_bSet("-av", asize(pa), pa) || opt2parg_bSet("-wcl", asize(pa), pa)
1577 || opt2parg_bSet("-nst", asize(pa), pa) || opt2parg_bSet("-rmsmin", asize(pa), pa)
1578 || opt2bSet("-cl", NFILE, fnm))
1580 trx_out_fn = opt2fn("-cl", NFILE, fnm);
1584 trx_out_fn = nullptr;
1586 if (bReadMat && output_env_get_time_factor(oenv) != 1)
1588 fprintf(stderr, "\nWarning: assuming the time unit in %s is %s\n",
1589 opt2fn("-dm", NFILE, fnm), output_env_get_time_unit(oenv).c_str());
1591 if (trx_out_fn && !bReadTraj)
1595 "cannot write cluster structures without reading trajectory\n"
1596 " ignoring option -cl %s\n",
1601 while (method < m_nr && gmx_strcasecmp(methodname[0], methodname[method]) != 0)
1607 gmx_fatal(FARGS, "Invalid method");
1610 bAnalyze = (method == m_linkage || method == m_jarvis_patrick || method == m_gromos);
1613 log = ftp2FILE(efLOG, NFILE, fnm, "w");
1615 fprintf(stderr, "Using %s method for clustering\n", methodname[0]);
1616 fprintf(log, "Using %s method for clustering\n", methodname[0]);
1618 /* check input and write parameters to log file */
1619 bUseRmsdCut = FALSE;
1620 if (method == m_jarvis_patrick)
1622 bJP_RMSD = (M == 0) || opt2parg_bSet("-cutoff", asize(pa), pa);
1623 if ((M < 0) || (M == 1))
1625 gmx_fatal(FARGS, "M (%d) must be 0 or larger than 1", M);
1629 sprintf(buf1, "Will use P=%d and RMSD cutoff (%g)", P, rmsdcut);
1636 gmx_fatal(FARGS, "Number of neighbors required (P) must be less than M");
1640 sprintf(buf1, "Will use P=%d, M=%d and RMSD cutoff (%g)", P, M, rmsdcut);
1645 sprintf(buf1, "Will use P=%d, M=%d", P, M);
1648 ffprintf_s(stderr, log, buf, "%s for determining the neighbors\n\n", buf1);
1650 else /* method != m_jarvis */
1652 bUseRmsdCut = (bBinary || method == m_linkage || method == m_gromos);
1654 if (bUseRmsdCut && method != m_jarvis_patrick)
1656 fprintf(log, "Using RMSD cutoff %g nm\n", rmsdcut);
1658 if (method == m_monte_carlo)
1660 fprintf(log, "Using %d iterations\n", niter);
1665 gmx_fatal(FARGS, "skip (%d) should be >= 1", skip);
1671 /* don't read mass-database as masses (and top) are not used */
1672 read_tps_conf(ftp2fn(efTPS, NFILE, fnm), &top, &ePBC, &xtps, nullptr, box, TRUE);
1675 gpbc = gmx_rmpbc_init(&top.idef, ePBC, top.atoms.nr);
1678 fprintf(stderr, "\nSelect group for least squares fit%s:\n", bReadMat ? "" : " and RMSD calculation");
1679 get_index(&(top.atoms), ftp2fn_null(efNDX, NFILE, fnm), 1, &ifsize, &fitidx, &grpname);
1682 fprintf(stderr, "\nSelect group for output:\n");
1683 get_index(&(top.atoms), ftp2fn_null(efNDX, NFILE, fnm), 1, &iosize, &outidx, &grpname);
1684 /* merge and convert both index groups: */
1685 /* first copy outidx to index. let outidx refer to elements in index */
1686 snew(index, iosize);
1688 for (i = 0; i < iosize; i++)
1690 index[i] = outidx[i];
1693 /* now lookup elements from fitidx in index, add them if necessary
1694 and also let fitidx refer to elements in index */
1695 for (i = 0; i < ifsize; i++)
1698 while (j < isize && index[j] != fitidx[i])
1704 /* slow this way, but doesn't matter much */
1706 srenew(index, isize);
1708 index[j] = fitidx[i];
1712 else /* !trx_out_fn */
1716 for (i = 0; i < ifsize; i++)
1718 index[i] = fitidx[i];
1726 /* Loop over first coordinate file */
1727 fn = opt2fn("-f", NFILE, fnm);
1729 xx = read_whole_trj(fn, isize, index, skip, &nf, &time, &boxes, &frameindices, oenv, bPBC, gpbc);
1730 output_env_conv_times(oenv, nf, time);
1731 if (!bRMSdist || bAnalyze)
1733 /* Center all frames on zero */
1735 for (i = 0; i < ifsize; i++)
1737 mass[fitidx[i]] = top.atoms.atom[index[fitidx[i]]].m;
1741 for (i = 0; i < nf; i++)
1743 reset_x(ifsize, fitidx, isize, nullptr, xx[i], mass);
1749 gmx_rmpbc_done(gpbc);
1753 std::vector<t_matrix> readmat;
1756 fprintf(stderr, "Reading rms distance matrix ");
1757 readmat = read_xpm_matrix(opt2fn("-dm", NFILE, fnm));
1758 fprintf(stderr, "\n");
1759 if (readmat[0].nx != readmat[0].ny)
1761 gmx_fatal(FARGS, "Matrix (%dx%d) is not square", readmat[0].nx, readmat[0].ny);
1763 if (bReadTraj && bAnalyze && (readmat[0].nx != nf))
1766 "Matrix size (%dx%d) does not match the number of "
1768 readmat[0].nx, readmat[0].ny, nf);
1773 time = readmat[0].axis_x.data();
1774 time_invfac = output_env_get_time_invfactor(oenv);
1775 for (i = 0; i < nf; i++)
1777 time[i] *= time_invfac;
1780 rms = init_mat(readmat[0].nx, method == m_diagonalize);
1781 convert_mat(&(readmat[0]), rms);
1783 nlevels = gmx::ssize(readmat[0].map);
1785 else /* !bReadMat */
1787 rms = init_mat(nf, method == m_diagonalize);
1788 nrms = (static_cast<int64_t>(nf) * static_cast<int64_t>(nf - 1)) / 2;
1791 fprintf(stderr, "Computing %dx%d RMS deviation matrix\n", nf, nf);
1792 /* Initialize work array */
1794 for (i1 = 0; i1 < nf; i1++)
1796 for (i2 = i1 + 1; i2 < nf; i2++)
1798 for (i = 0; i < isize; i++)
1800 copy_rvec(xx[i1][i], x1[i]);
1804 do_fit(isize, mass, xx[i2], x1);
1806 rmsd = rmsdev(isize, mass, xx[i2], x1);
1807 set_mat_entry(rms, i1, i2, rmsd);
1809 nrms -= nf - i1 - 1;
1811 "\r# RMSD calculations left: "
1820 fprintf(stderr, "Computing %dx%d RMS distance deviation matrix\n", nf, nf);
1822 /* Initiate work arrays */
1825 for (i = 0; (i < isize); i++)
1830 for (i1 = 0; i1 < nf; i1++)
1832 calc_dist(isize, xx[i1], d1);
1833 for (i2 = i1 + 1; (i2 < nf); i2++)
1835 calc_dist(isize, xx[i2], d2);
1836 set_mat_entry(rms, i1, i2, rms_dist(isize, d1, d2));
1838 nrms -= nf - i1 - 1;
1840 "\r# RMSD calculations left: "
1845 /* Clean up work arrays */
1846 for (i = 0; (i < isize); i++)
1854 fprintf(stderr, "\n\n");
1856 ffprintf_gg(stderr, log, buf, "The RMSD ranges from %g to %g nm\n", rms->minrms, rms->maxrms);
1857 ffprintf_g(stderr, log, buf, "Average RMSD is %g\n", 2 * rms->sumrms / (nf * (nf - 1)));
1858 ffprintf_d(stderr, log, buf, "Number of structures for matrix %d\n", nf);
1859 ffprintf_g(stderr, log, buf, "Energy of the matrix is %g.\n", mat_energy(rms));
1860 if (bUseRmsdCut && (rmsdcut < rms->minrms || rmsdcut > rms->maxrms))
1863 "WARNING: rmsd cutoff %g is outside range of rmsd values "
1865 rmsdcut, rms->minrms, rms->maxrms);
1867 if (bAnalyze && (rmsmin < rms->minrms))
1869 fprintf(stderr, "WARNING: rmsd minimum %g is below lowest rmsd value %g\n", rmsmin, rms->minrms);
1871 if (bAnalyze && (rmsmin > rmsdcut))
1873 fprintf(stderr, "WARNING: rmsd minimum %g is above rmsd cutoff %g\n", rmsmin, rmsdcut);
1876 /* Plot the rmsd distribution */
1877 rmsd_distribution(opt2fn("-dist", NFILE, fnm), rms, oenv);
1881 for (i1 = 0; (i1 < nf); i1++)
1883 for (i2 = 0; (i2 < nf); i2++)
1885 if (rms->mat[i1][i2] < rmsdcut)
1887 rms->mat[i1][i2] = 0;
1891 rms->mat[i1][i2] = 1;
1901 /* Now sort the matrix and write it out again */
1902 gather(rms, rmsdcut, &clust);
1905 /* Do a diagonalization */
1906 snew(eigenvalues, nf);
1907 snew(eigenvectors, nf * nf);
1908 std::memcpy(eigenvectors, rms->mat[0], nf * nf * sizeof(real));
1909 eigensolver(eigenvectors, nf, 0, nf, eigenvalues, rms->mat[0]);
1910 sfree(eigenvectors);
1912 fp = xvgropen(opt2fn("-ev", NFILE, fnm), "RMSD matrix Eigenvalues", "Eigenvector index",
1913 "Eigenvalues (nm\\S2\\N)", oenv);
1914 for (i = 0; (i < nf); i++)
1916 fprintf(fp, "%10d %10g\n", i, eigenvalues[i]);
1921 orig = init_mat(rms->nn, FALSE);
1923 copy_t_mat(orig, rms);
1924 mc_optimize(log, rms, time, niter, nrandom, seed, kT, opt2fn_null("-conv", NFILE, fnm), oenv);
1926 case m_jarvis_patrick:
1927 jarvis_patrick(rms->nn, rms->mat, M, P, bJP_RMSD ? rmsdcut : -1, &clust);
1929 case m_gromos: gromos(rms->nn, rms->mat, rmsdcut, &clust); break;
1930 default: gmx_fatal(FARGS, "DEATH HORROR unknown method \"%s\"", methodname[0]);
1933 if (method == m_monte_carlo || method == m_diagonalize)
1935 fprintf(stderr, "Energy of the matrix after clustering is %g.\n", mat_energy(rms));
1942 ncluster = plot_clusters(nf, rms->mat, &clust, minstruct);
1946 mark_clusters(nf, rms->mat, rms->maxrms, &clust);
1948 init_t_atoms(&useatoms, isize, FALSE);
1949 snew(usextps, isize);
1950 useatoms.resinfo = top.atoms.resinfo;
1951 for (i = 0; i < isize; i++)
1953 useatoms.atomname[i] = top.atoms.atomname[index[i]];
1954 useatoms.atom[i].resind = top.atoms.atom[index[i]].resind;
1955 useatoms.nres = std::max(useatoms.nres, useatoms.atom[i].resind + 1);
1956 copy_rvec(xtps[index[i]], usextps[i]);
1958 useatoms.nr = isize;
1959 analyze_clusters(nf, &clust, rms->mat, isize, &useatoms, usextps, mass, xx, time, boxes,
1960 frameindices, ifsize, fitidx, iosize, outidx,
1961 bReadTraj ? trx_out_fn : nullptr, opt2fn_null("-sz", NFILE, fnm),
1962 opt2fn_null("-tr", NFILE, fnm), opt2fn_null("-ntr", NFILE, fnm),
1963 opt2fn_null("-clid", NFILE, fnm), opt2fn_null("-clndx", NFILE, fnm),
1964 bAverage, write_ncl, write_nst, rmsmin, bFit, log, rlo_bot, rhi_bot, oenv);
1966 sfree(frameindices);
1970 if (bBinary && !bAnalyze)
1972 /* Make the clustering visible */
1973 for (i2 = 0; (i2 < nf); i2++)
1975 for (i1 = i2 + 1; (i1 < nf); i1++)
1977 if (rms->mat[i1][i2] != 0.0F)
1979 rms->mat[i1][i2] = rms->maxrms;
1985 fp = opt2FILE("-o", NFILE, fnm, "w");
1986 fprintf(stderr, "Writing rms distance/clustering matrix ");
1989 write_xpm(fp, 0, readmat[0].title, readmat[0].legend, readmat[0].label_x,
1990 readmat[0].label_y, nf, nf, readmat[0].axis_x.data(), readmat[0].axis_y.data(),
1991 rms->mat, 0.0, rms->maxrms, rlo_top, rhi_top, &nlevels);
1995 auto timeLabel = output_env_get_time_label(oenv);
1996 auto title = gmx::formatString("RMS%sDeviation / Cluster Index", bRMSdist ? " Distance " : " ");
1999 write_xpm_split(fp, 0, title, "RMSD (nm)", timeLabel, timeLabel, nf, nf, time, time,
2000 rms->mat, 0.0, rms->maxrms, &nlevels, rlo_top, rhi_top, 0.0, ncluster,
2001 &ncluster, TRUE, rlo_bot, rhi_bot);
2005 write_xpm(fp, 0, title, "RMSD (nm)", timeLabel, timeLabel, nf, nf, time, time, rms->mat,
2006 0.0, rms->maxrms, rlo_top, rhi_top, &nlevels);
2009 fprintf(stderr, "\n");
2011 if (nullptr != orig)
2013 fp = opt2FILE("-om", NFILE, fnm, "w");
2014 auto timeLabel = output_env_get_time_label(oenv);
2015 auto title = gmx::formatString("RMS%sDeviation", bRMSdist ? " Distance " : " ");
2016 write_xpm(fp, 0, title, "RMSD (nm)", timeLabel, timeLabel, nf, nf, time, time, orig->mat,
2017 0.0, orig->maxrms, rlo_top, rhi_top, &nlevels);
2022 /* now show what we've done */
2023 do_view(oenv, opt2fn("-o", NFILE, fnm), "-nxy");
2024 do_view(oenv, opt2fn_null("-sz", NFILE, fnm), "-nxy");
2025 if (method == m_diagonalize)
2027 do_view(oenv, opt2fn_null("-ev", NFILE, fnm), "-nxy");
2029 do_view(oenv, opt2fn("-dist", NFILE, fnm), "-nxy");
2032 do_view(oenv, opt2fn_null("-tr", NFILE, fnm), "-nxy");
2033 do_view(oenv, opt2fn_null("-ntr", NFILE, fnm), "-nxy");
2034 do_view(oenv, opt2fn_null("-clid", NFILE, fnm), "-nxy");
2036 do_view(oenv, opt2fn_null("-conv", NFILE, fnm), nullptr);