3 * This source code is part of
7 * GROningen MAchine for Chemical Simulations
10 * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
11 * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
12 * Copyright (c) 2001-2004, The GROMACS development team,
13 * check out http://www.gromacs.org for more information.
15 * This program is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU General Public License
17 * as published by the Free Software Foundation; either version 2
18 * of the License, or (at your option) any later version.
20 * If you want to redistribute modifications, please consider that
21 * scientific software is very special. Version control is crucial -
22 * bugs must be traceable. We will be happy to consider code for
23 * inclusion in the official distribution, but derived work must not
24 * be called official GROMACS. Details are found in the README & COPYING
25 * files - if they are missing, get the official version at www.gromacs.org.
27 * To help us fund GROMACS development, we humbly ask that you cite
28 * the papers on the package - you can find them in the top README file.
30 * For more info, check our website at http://www.gromacs.org
33 * Green Red Orange Magenta Azure Cyan Skyblue
50 #include "gmx_random.h"
62 #include "gromacs/linearalgebra/eigensolver.h"
64 /* print to two file pointers at once (i.e. stderr and log) */
66 void lo_ffprintf(FILE *fp1, FILE *fp2, const char *buf)
68 fprintf(fp1, "%s", buf);
69 fprintf(fp2, "%s", buf);
72 /* just print a prepared buffer to fp1 and fp2 */
74 void ffprintf(FILE *fp1, FILE *fp2, const char *buf)
76 lo_ffprintf(fp1, fp2, buf);
79 /* prepare buffer with one argument, then print to fp1 and fp2 */
81 void ffprintf_d(FILE *fp1, FILE *fp2, char *buf, const char *fmt, int arg)
83 sprintf(buf, fmt, arg);
84 lo_ffprintf(fp1, fp2, buf);
87 /* prepare buffer with one argument, then print to fp1 and fp2 */
89 void ffprintf_g(FILE *fp1, FILE *fp2, char *buf, const char *fmt, real arg)
91 sprintf(buf, fmt, arg);
92 lo_ffprintf(fp1, fp2, buf);
95 /* prepare buffer with one argument, then print to fp1 and fp2 */
97 void ffprintf_s(FILE *fp1, FILE *fp2, char *buf, const char *fmt, const char *arg)
99 sprintf(buf, fmt, arg);
100 lo_ffprintf(fp1, fp2, buf);
103 /* prepare buffer with two arguments, then print to fp1 and fp2 */
105 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 */
113 void ffprintf_gg(FILE *fp1, FILE *fp2, char *buf, const char *fmt, real arg1, real arg2)
115 sprintf(buf, fmt, arg1, arg2);
116 lo_ffprintf(fp1, fp2, buf);
119 /* prepare buffer with two arguments, then print to fp1 and fp2 */
121 void ffprintf_ss(FILE *fp1, FILE *fp2, char *buf, const char *fmt, const char *arg1, const char *arg2)
123 sprintf(buf, fmt, arg1, arg2);
124 lo_ffprintf(fp1, fp2, buf);
137 void pr_energy(FILE *fp, real e)
139 fprintf(fp, "Energy: %8.4f\n", e);
142 void cp_index(int nn, int from[], int to[])
146 for (i = 0; (i < nn); i++)
152 void mc_optimize(FILE *log, t_mat *m, real *time,
153 int maxiter, int nrandom,
155 const char *conv, output_env_t oenv)
158 real ecur, enext, emin, prob, enorm;
159 int i, j, iswap, jswap, nn, nuphill = 0;
165 fprintf(stderr, "Can not do Monte Carlo optimization with a non-square matrix.\n");
168 printf("\nDoing Monte Carlo optimization to find the smoothest trajectory\n");
169 printf("by reordering the frames to minimize the path between the two structures\n");
170 printf("that have the largest pairwise RMSD.\n");
173 enorm = m->mat[0][0];
174 for (i = 0; (i < m->n1); i++)
176 for (j = 0; (j < m->nn); j++)
178 if (m->mat[i][j] > enorm)
180 enorm = m->mat[i][j];
186 if ((iswap == -1) || (jswap == -1))
188 fprintf(stderr, "Matrix contains identical values in all fields\n");
191 swap_rows(m, 0, iswap);
192 swap_rows(m, m->n1-1, jswap);
193 emin = ecur = mat_energy(m);
194 printf("Largest distance %g between %d and %d. Energy: %g.\n",
195 enorm, iswap, jswap, emin);
197 rng = gmx_rng_init(seed);
200 /* Initiate and store global minimum */
201 minimum = init_mat(nn, m->b1D);
203 copy_t_mat(minimum, m);
207 fp = xvgropen(conv, "Convergence of the MC optimization",
208 "Energy", "Step", oenv);
210 for (i = 0; (i < maxiter); i++)
212 /* Generate new swapping candidates */
215 iswap = 1+(nn-2)*gmx_rng_uniform_real(rng);
216 jswap = 1+(nn-2)*gmx_rng_uniform_real(rng);
218 while ((iswap == jswap) || (iswap >= nn-1) || (jswap >= nn-1));
220 /* Apply swap and compute energy */
221 swap_rows(m, iswap, jswap);
222 enext = mat_energy(m);
224 /* Compute probability */
226 if ((enext < ecur) || (i < nrandom))
231 /* Store global minimum */
232 copy_t_mat(minimum, m);
238 /* Try Monte Carlo step */
239 prob = exp(-(enext-ecur)/(enorm*kT));
242 if ((prob == 1) || (gmx_rng_uniform_real(rng) < prob))
249 fprintf(log, "Iter: %d Swapped %4d and %4d (energy: %g prob: %g)\n",
250 i, iswap, jswap, enext, prob);
253 fprintf(fp, "%6d %10g\n", i, enext);
259 swap_rows(m, jswap, iswap);
262 fprintf(log, "%d uphill steps were taken during optimization\n",
265 /* Now swap the matrix to get it into global minimum mode */
266 copy_t_mat(m, minimum);
268 fprintf(log, "Global minimum energy %g\n", mat_energy(minimum));
269 fprintf(log, "Global minimum energy %g\n", mat_energy(m));
270 fprintf(log, "Swapped time and frame indices and RMSD to next neighbor:\n");
271 for (i = 0; (i < m->nn); i++)
273 fprintf(log, "%10g %5d %10g\n",
276 (i < m->nn-1) ? m->mat[m->m_ind[i]][m->m_ind[i+1]] : 0);
285 static void calc_dist(int nind, rvec x[], real **d)
291 for (i = 0; (i < nind-1); i++)
294 for (j = i+1; (j < nind); j++)
296 /* Should use pbc_dx when analysing multiple molecueles,
297 * but the box is not stored for every frame.
299 rvec_sub(xi, x[j], dx);
305 static real rms_dist(int isize, real **d, real **d_r)
311 for (i = 0; (i < isize-1); i++)
313 for (j = i+1; (j < isize); j++)
315 r = d[i][j]-d_r[i][j];
319 r2 /= (isize*(isize-1))/2;
324 static int rms_dist_comp(const void *a, const void *b)
331 if (da->dist - db->dist < 0)
335 else if (da->dist - db->dist > 0)
342 static int clust_id_comp(const void *a, const void *b)
349 return da->clust - db->clust;
352 static int nrnb_comp(const void *a, const void *b)
359 /* return the b-a, we want highest first */
360 return db->nr - da->nr;
363 void gather(t_mat *m, real cutoff, t_clusters *clust)
367 int i, j, k, nn, cid, n1, diff;
370 /* First we sort the entries in the RMSD matrix */
374 for (i = k = 0; (i < n1); i++)
376 for (j = i+1; (j < n1); j++, k++)
380 d[k].dist = m->mat[i][j];
385 gmx_incons("gather algortihm");
387 qsort(d, nn, sizeof(d[0]), rms_dist_comp);
389 /* Now we make a cluster index for all of the conformations */
392 /* Now we check the closest structures, and equalize their cluster numbers */
393 fprintf(stderr, "Linking structures ");
396 fprintf(stderr, "*");
398 for (k = 0; (k < nn) && (d[k].dist < cutoff); k++)
400 diff = c[d[k].j].clust - c[d[k].i].clust;
406 c[d[k].j].clust = c[d[k].i].clust;
410 c[d[k].i].clust = c[d[k].j].clust;
416 fprintf(stderr, "\nSorting and renumbering clusters\n");
417 /* Sort on cluster number */
418 qsort(c, n1, sizeof(c[0]), clust_id_comp);
420 /* Renumber clusters */
422 for (k = 1; k < n1; k++)
424 if (c[k].clust != c[k-1].clust)
437 for (k = 0; (k < n1); k++)
439 fprintf(debug, "Cluster index for conformation %d: %d\n",
440 c[k].conf, c[k].clust);
444 for (k = 0; k < n1; k++)
446 clust->cl[c[k].conf] = c[k].clust;
453 gmx_bool jp_same(int **nnb, int i, int j, int P)
459 for (k = 0; nnb[i][k] >= 0; k++)
461 bIn = bIn || (nnb[i][k] == j);
469 for (k = 0; nnb[j][k] >= 0; k++)
471 bIn = bIn || (nnb[j][k] == i);
479 for (ii = 0; nnb[i][ii] >= 0; ii++)
481 for (jj = 0; nnb[j][jj] >= 0; jj++)
483 if ((nnb[i][ii] == nnb[j][jj]) && (nnb[i][ii] != -1))
493 static void jarvis_patrick(int n1, real **mat, int M, int P,
494 real rmsdcut, t_clusters *clust)
499 int i, j, k, cid, diff, max;
508 /* First we sort the entries in the RMSD matrix row by row.
509 * This gives us the nearest neighbor list.
513 for (i = 0; (i < n1); i++)
515 for (j = 0; (j < n1); j++)
518 row[j].dist = mat[i][j];
520 qsort(row, n1, sizeof(row[0]), rms_dist_comp);
523 /* Put the M nearest neighbors in the list */
525 for (j = k = 0; (k < M) && (j < n1) && (mat[i][row[j].j] < rmsdcut); j++)
529 nnb[i][k] = row[j].j;
537 /* Put all neighbors nearer than rmsdcut in the list */
540 for (j = 0; (j < n1) && (mat[i][row[j].j] < rmsdcut); j++)
549 nnb[i][k] = row[j].j;
555 srenew(nnb[i], max+1);
563 fprintf(debug, "Nearest neighborlist. M = %d, P = %d\n", M, P);
564 for (i = 0; (i < n1); i++)
566 fprintf(debug, "i:%5d nbs:", i);
567 for (j = 0; nnb[i][j] >= 0; j++)
569 fprintf(debug, "%5d[%5.3f]", nnb[i][j], mat[i][nnb[i][j]]);
571 fprintf(debug, "\n");
576 fprintf(stderr, "Linking structures ");
577 /* Use mcpy for temporary storage of booleans */
578 mcpy = mk_matrix(n1, n1, FALSE);
579 for (i = 0; i < n1; i++)
581 for (j = i+1; j < n1; j++)
583 mcpy[i][j] = jp_same(nnb, i, j, P);
588 fprintf(stderr, "*");
590 for (i = 0; i < n1; i++)
592 for (j = i+1; j < n1; j++)
596 diff = c[j].clust - c[i].clust;
602 c[j].clust = c[i].clust;
606 c[i].clust = c[j].clust;
615 fprintf(stderr, "\nSorting and renumbering clusters\n");
616 /* Sort on cluster number */
617 qsort(c, n1, sizeof(c[0]), clust_id_comp);
619 /* Renumber clusters */
621 for (k = 1; k < n1; k++)
623 if (c[k].clust != c[k-1].clust)
635 for (k = 0; k < n1; k++)
637 clust->cl[c[k].conf] = c[k].clust;
641 for (k = 0; (k < n1); k++)
643 fprintf(debug, "Cluster index for conformation %d: %d\n",
644 c[k].conf, c[k].clust);
648 /* Again, I don't see the point in this... (AF) */
649 /* for(i=0; (i<n1); i++) { */
650 /* for(j=0; (j<n1); j++) */
651 /* mcpy[c[i].conf][c[j].conf] = mat[i][j]; */
653 /* for(i=0; (i<n1); i++) { */
654 /* for(j=0; (j<n1); j++) */
655 /* mat[i][j] = mcpy[i][j]; */
657 done_matrix(n1, &mcpy);
660 for (i = 0; (i < n1); i++)
667 static void dump_nnb (FILE *fp, const char *title, int n1, t_nnb *nnb)
671 /* dump neighbor list */
672 fprintf(fp, "%s", title);
673 for (i = 0; (i < n1); i++)
675 fprintf(fp, "i:%5d #:%5d nbs:", i, nnb[i].nr);
676 for (j = 0; j < nnb[i].nr; j++)
678 fprintf(fp, "%5d", nnb[i].nb[j]);
684 static void gromos(int n1, real **mat, real rmsdcut, t_clusters *clust)
688 int i, j, k, j1, max;
690 /* Put all neighbors nearer than rmsdcut in the list */
691 fprintf(stderr, "Making list of neighbors within cutoff ");
694 for (i = 0; (i < n1); i++)
698 /* put all neighbors within cut-off in list */
699 for (j = 0; j < n1; j++)
701 if (mat[i][j] < rmsdcut)
706 srenew(nnb[i].nb, max);
712 /* store nr of neighbors, we'll need that */
714 if (i%(1+n1/100) == 0)
716 fprintf(stderr, "%3d%%\b\b\b\b", (i*100+1)/n1);
719 fprintf(stderr, "%3d%%\n", 100);
722 /* sort neighbor list on number of neighbors, largest first */
723 qsort(nnb, n1, sizeof(nnb[0]), nrnb_comp);
727 dump_nnb(debug, "Nearest neighborlist after sort.\n", n1, nnb);
730 /* turn first structure with all its neighbors (largest) into cluster
731 remove them from pool of structures and repeat for all remaining */
732 fprintf(stderr, "Finding clusters %4d", 0);
733 /* cluster id's start at 1: */
737 /* set cluster id (k) for first item in neighborlist */
738 for (j = 0; j < nnb[0].nr; j++)
740 clust->cl[nnb[0].nb[j]] = k;
746 /* adjust number of neighbors for others, taking removals into account: */
747 for (i = 1; i < n1 && nnb[i].nr; i++)
750 for (j = 0; j < nnb[i].nr; j++)
752 /* if this neighbor wasn't removed */
753 if (clust->cl[nnb[i].nb[j]] == 0)
755 /* shift the rest (j1<=j) */
756 nnb[i].nb[j1] = nnb[i].nb[j];
761 /* now j1 is the new number of neighbors */
764 /* sort again on nnb[].nr, because we have new # neighbors: */
765 /* but we only need to sort upto i, i.e. when nnb[].nr>0 */
766 qsort(nnb, i, sizeof(nnb[0]), nrnb_comp);
768 fprintf(stderr, "\b\b\b\b%4d", k);
772 fprintf(stderr, "\n");
776 fprintf(debug, "Clusters (%d):\n", k);
777 for (i = 0; i < n1; i++)
779 fprintf(debug, " %3d", clust->cl[i]);
781 fprintf(debug, "\n");
787 rvec **read_whole_trj(const char *fn, int isize, atom_id index[], int skip,
788 int *nframe, real **time, const output_env_t oenv, gmx_bool bPBC, gmx_rmpbc_t gpbc)
793 int i, i0, j, max_nf;
801 natom = read_first_x(oenv, &status, fn, &t, &x, box);
808 gmx_rmpbc(gpbc, natom, box, x);
814 srenew(*time, max_nf);
819 /* Store only the interesting atoms */
820 for (j = 0; (j < isize); j++)
822 copy_rvec(x[index[j]], xx[i0][j]);
829 while (read_next_x(oenv, status, &t, x, box));
830 fprintf(stderr, "Allocated %lu bytes for frames\n",
831 (unsigned long) (max_nf*isize*sizeof(**xx)));
832 fprintf(stderr, "Read %d frames from trajectory %s\n", i0, fn);
839 static int plot_clusters(int nf, real **mat, t_clusters *clust,
842 int i, j, ncluster, ci;
843 int *cl_id, *nstruct, *strind;
848 for (i = 0; i < nf; i++)
851 cl_id[i] = clust->cl[i];
855 for (i = 0; i < nf; i++)
857 if (nstruct[i] >= minstruct)
860 for (j = 0; (j < nf); j++)
864 strind[j] = ncluster;
870 fprintf(stderr, "There are %d clusters with at least %d conformations\n",
871 ncluster, minstruct);
873 for (i = 0; (i < nf); i++)
876 for (j = 0; j < i; j++)
878 if ((ci == cl_id[j]) && (nstruct[ci] >= minstruct))
880 /* color different clusters with different colors, as long as
881 we don't run out of colors */
882 mat[i][j] = strind[i];
897 static void mark_clusters(int nf, real **mat, real val, t_clusters *clust)
901 for (i = 0; i < nf; i++)
903 for (j = 0; j < i; j++)
905 if (clust->cl[i] == clust->cl[j])
917 static char *parse_filename(const char *fn, int maxnr)
926 gmx_fatal(FARGS, "will not number filename %s containing '%c'", fn, '%');
928 /* number of digits needed in numbering */
929 i = (int)(log(maxnr)/log(10)) + 1;
930 /* split fn and ext */
931 ext = strrchr(fn, '.');
934 gmx_fatal(FARGS, "cannot separate extension in filename %s", fn);
937 /* insert e.g. '%03d' between fn and ext */
938 sprintf(buf, "%s%%0%dd.%s", fn, i, ext);
939 snew(fnout, strlen(buf)+1);
945 static void ana_trans(t_clusters *clust, int nf,
946 const char *transfn, const char *ntransfn, FILE *log,
947 t_rgb rlo, t_rgb rhi, const output_env_t oenv)
952 int i, ntranst, maxtrans;
955 snew(ntrans, clust->ncl);
956 snew(trans, clust->ncl);
957 snew(axis, clust->ncl);
958 for (i = 0; i < clust->ncl; i++)
961 snew(trans[i], clust->ncl);
965 for (i = 1; i < nf; i++)
967 if (clust->cl[i] != clust->cl[i-1])
970 ntrans[clust->cl[i-1]-1]++;
971 ntrans[clust->cl[i]-1]++;
972 trans[clust->cl[i-1]-1][clust->cl[i]-1]++;
973 maxtrans = max(maxtrans, trans[clust->cl[i]-1][clust->cl[i-1]-1]);
976 ffprintf_dd(stderr, log, buf, "Counted %d transitions in total, "
977 "max %d between two specific clusters\n", ntranst, maxtrans);
980 fp = ffopen(transfn, "w");
981 i = min(maxtrans+1, 80);
982 write_xpm(fp, 0, "Cluster Transitions", "# transitions",
983 "from cluster", "to cluster",
984 clust->ncl, clust->ncl, axis, axis, trans,
985 0, maxtrans, rlo, rhi, &i);
990 fp = xvgropen(ntransfn, "Cluster Transitions", "Cluster #", "# transitions",
992 for (i = 0; i < clust->ncl; i++)
994 fprintf(fp, "%5d %5d\n", i+1, ntrans[i]);
999 for (i = 0; i < clust->ncl; i++)
1007 static void analyze_clusters(int nf, t_clusters *clust, real **rmsd,
1008 int natom, t_atoms *atoms, rvec *xtps,
1009 real *mass, rvec **xx, real *time,
1010 int ifsize, atom_id *fitidx,
1011 int iosize, atom_id *outidx,
1012 const char *trxfn, const char *sizefn,
1013 const char *transfn, const char *ntransfn,
1014 const char *clustidfn, gmx_bool bAverage,
1015 int write_ncl, int write_nst, real rmsmin,
1016 gmx_bool bFit, FILE *log, t_rgb rlo, t_rgb rhi,
1017 const output_env_t oenv)
1020 char buf[STRLEN], buf1[40], buf2[40], buf3[40], *trxsfn;
1021 t_trxstatus *trxout = NULL;
1022 t_trxstatus *trxsout = NULL;
1023 int i, i1, cl, nstr, *structure, first = 0, midstr;
1024 gmx_bool *bWrite = NULL;
1025 real r, clrmsd, midrmsd;
1031 ffprintf_d(stderr, log, buf, "\nFound %d clusters\n\n", clust->ncl);
1035 /* do we write all structures? */
1038 trxsfn = parse_filename(trxfn, max(write_ncl, clust->ncl));
1041 ffprintf_ss(stderr, log, buf, "Writing %s structure for each cluster to %s\n",
1042 bAverage ? "average" : "middle", trxfn);
1045 /* find out what we want to tell the user:
1046 Writing [all structures|structures with rmsd > %g] for
1047 {all|first %d} clusters {with more than %d structures} to %s */
1050 sprintf(buf1, "structures with rmsd > %g", rmsmin);
1054 sprintf(buf1, "all structures");
1056 buf2[0] = buf3[0] = '\0';
1057 if (write_ncl >= clust->ncl)
1061 sprintf(buf2, "all ");
1066 sprintf(buf2, "the first %d ", write_ncl);
1070 sprintf(buf3, " with more than %d structures", write_nst);
1072 sprintf(buf, "Writing %s for %sclusters%s to %s\n", buf1, buf2, buf3, trxsfn);
1073 ffprintf(stderr, log, buf);
1076 /* Prepare a reference structure for the orientation of the clusters */
1079 reset_x(ifsize, fitidx, natom, NULL, xtps, mass);
1081 trxout = open_trx(trxfn, "w");
1082 /* Calculate the average structure in each cluster, *
1083 * all structures are fitted to the first struture of the cluster */
1087 if (transfn || ntransfn)
1089 ana_trans(clust, nf, transfn, ntransfn, log, rlo, rhi, oenv);
1094 fp = xvgropen(clustidfn, "Clusters", output_env_get_xvgr_tlabel(oenv), "Cluster #", oenv);
1095 fprintf(fp, "@ s0 symbol 2\n");
1096 fprintf(fp, "@ s0 symbol size 0.2\n");
1097 fprintf(fp, "@ s0 linestyle 0\n");
1098 for (i = 0; i < nf; i++)
1100 fprintf(fp, "%8g %8d\n", time[i], clust->cl[i]);
1106 fp = xvgropen(sizefn, "Cluster Sizes", "Cluster #", "# Structures", oenv);
1107 fprintf(fp, "@g%d type %s\n", 0, "bar");
1109 snew(structure, nf);
1110 fprintf(log, "\n%3s | %3s %4s | %6s %4s | cluster members\n",
1111 "cl.", "#st", "rmsd", "middle", "rmsd");
1112 for (cl = 1; cl <= clust->ncl; cl++)
1114 /* prepare structures (fit, middle, average) */
1117 for (i = 0; i < natom; i++)
1123 for (i1 = 0; i1 < nf; i1++)
1125 if (clust->cl[i1] == cl)
1127 structure[nstr] = i1;
1129 if (trxfn && (bAverage || write_ncl) )
1133 reset_x(ifsize, fitidx, natom, NULL, xx[i1], mass);
1141 do_fit(natom, mass, xx[first], xx[i1]);
1145 for (i = 0; i < natom; i++)
1147 rvec_inc(xav[i], xx[i1][i]);
1155 fprintf(fp, "%8d %8d\n", cl, nstr);
1160 for (i1 = 0; i1 < nstr; i1++)
1165 for (i = 0; i < nstr; i++)
1169 r += rmsd[structure[i]][structure[i1]];
1173 r += rmsd[structure[i1]][structure[i]];
1180 midstr = structure[i1];
1187 /* dump cluster info to logfile */
1190 sprintf(buf1, "%6.3f", clrmsd);
1195 sprintf(buf2, "%5.3f", midrmsd);
1203 sprintf(buf1, "%5s", "");
1204 sprintf(buf2, "%5s", "");
1206 fprintf(log, "%3d | %3d %s | %6g%s |", cl, nstr, buf1, time[midstr], buf2);
1207 for (i = 0; i < nstr; i++)
1209 if ((i % 7 == 0) && i)
1211 sprintf(buf, "\n%3s | %3s %4s | %6s %4s |", "", "", "", "", "");
1218 fprintf(log, "%s %6g", buf, time[i1]);
1222 /* write structures to trajectory file(s) */
1227 for (i = 0; i < nstr; i++)
1232 if (cl < write_ncl+1 && nstr > write_nst)
1234 /* Dump all structures for this cluster */
1235 /* generate numbered filename (there is a %d in trxfn!) */
1236 sprintf(buf, trxsfn, cl);
1237 trxsout = open_trx(buf, "w");
1238 for (i = 0; i < nstr; i++)
1243 for (i1 = 0; i1 < i && bWrite[i]; i1++)
1247 bWrite[i] = rmsd[structure[i1]][structure[i]] > rmsmin;
1253 write_trx(trxsout, iosize, outidx, atoms, i, time[structure[i]], zerobox,
1254 xx[structure[i]], NULL, NULL);
1259 /* Dump the average structure for this cluster */
1262 for (i = 0; i < natom; i++)
1264 svmul(1.0/nstr, xav[i], xav[i]);
1269 for (i = 0; i < natom; i++)
1271 copy_rvec(xx[midstr][i], xav[i]);
1275 reset_x(ifsize, fitidx, natom, NULL, xav, mass);
1280 do_fit(natom, mass, xtps, xav);
1283 write_trx(trxout, iosize, outidx, atoms, cl, time[midstr], zerobox, xav, NULL, NULL);
1303 static void convert_mat(t_matrix *mat, t_mat *rms)
1308 matrix2real(mat, rms->mat);
1309 /* free input xpm matrix data */
1310 for (i = 0; i < mat->nx; i++)
1312 sfree(mat->matrix[i]);
1316 for (i = 0; i < mat->nx; i++)
1318 for (j = i; j < mat->nx; j++)
1320 rms->sumrms += rms->mat[i][j];
1321 rms->maxrms = max(rms->maxrms, rms->mat[i][j]);
1324 rms->minrms = min(rms->minrms, rms->mat[i][j]);
1331 int gmx_cluster(int argc, char *argv[])
1333 const char *desc[] = {
1334 "[TT]g_cluster[tt] can cluster structures using several different methods.",
1335 "Distances between structures can be determined from a trajectory",
1336 "or read from an [TT].xpm[tt] matrix file with the [TT]-dm[tt] option.",
1337 "RMS deviation after fitting or RMS deviation of atom-pair distances",
1338 "can be used to define the distance between structures.[PAR]",
1340 "single linkage: add a structure to a cluster when its distance to any",
1341 "element of the cluster is less than [TT]cutoff[tt].[PAR]",
1343 "Jarvis Patrick: add a structure to a cluster when this structure",
1344 "and a structure in the cluster have each other as neighbors and",
1345 "they have a least [TT]P[tt] neighbors in common. The neighbors",
1346 "of a structure are the M closest structures or all structures within",
1347 "[TT]cutoff[tt].[PAR]",
1349 "Monte Carlo: reorder the RMSD matrix using Monte Carlo such that",
1350 "the order of the frames is using the smallest possible increments.",
1351 "With this it is possible to make a smooth animation going from one",
1352 "structure to another with the largest possible (e.g.) RMSD between",
1353 "them, however the intermediate steps should be as small as possible.",
1354 "Applications could be to visualize a potential of mean force",
1355 "ensemble of simulations or a pulling simulation. Obviously the user",
1356 "has to prepare the trajectory well (e.g. by not superimposing frames).",
1357 "The final result can be inspect visually by looking at the matrix",
1358 "[TT].xpm[tt] file, which should vary smoothly from bottom to top.[PAR]",
1360 "diagonalization: diagonalize the RMSD matrix.[PAR]",
1362 "gromos: use algorithm as described in Daura [IT]et al.[it]",
1363 "([IT]Angew. Chem. Int. Ed.[it] [BB]1999[bb], [IT]38[it], pp 236-240).",
1364 "Count number of neighbors using cut-off, take structure with",
1365 "largest number of neighbors with all its neighbors as cluster",
1366 "and eliminate it from the pool of clusters. Repeat for remaining",
1367 "structures in pool.[PAR]",
1369 "When the clustering algorithm assigns each structure to exactly one",
1370 "cluster (single linkage, Jarvis Patrick and gromos) and a trajectory",
1371 "file is supplied, the structure with",
1372 "the smallest average distance to the others or the average structure",
1373 "or all structures for each cluster will be written to a trajectory",
1374 "file. When writing all structures, separate numbered files are made",
1375 "for each cluster.[PAR]",
1377 "Two output files are always written:[BR]",
1378 "[TT]-o[tt] writes the RMSD values in the upper left half of the matrix",
1379 "and a graphical depiction of the clusters in the lower right half",
1380 "When [TT]-minstruct[tt] = 1 the graphical depiction is black",
1381 "when two structures are in the same cluster.",
1382 "When [TT]-minstruct[tt] > 1 different colors will be used for each",
1384 "[TT]-g[tt] writes information on the options used and a detailed list",
1385 "of all clusters and their members.[PAR]",
1387 "Additionally, a number of optional output files can be written:[BR]",
1388 "[TT]-dist[tt] writes the RMSD distribution.[BR]",
1389 "[TT]-ev[tt] writes the eigenvectors of the RMSD matrix",
1390 "diagonalization.[BR]",
1391 "[TT]-sz[tt] writes the cluster sizes.[BR]",
1392 "[TT]-tr[tt] writes a matrix of the number transitions between",
1393 "cluster pairs.[BR]",
1394 "[TT]-ntr[tt] writes the total number of transitions to or from",
1395 "each cluster.[BR]",
1396 "[TT]-clid[tt] writes the cluster number as a function of time.[BR]",
1397 "[TT]-cl[tt] writes average (with option [TT]-av[tt]) or central",
1398 "structure of each cluster or writes numbered files with cluster members",
1399 "for a selected set of clusters (with option [TT]-wcl[tt], depends on",
1400 "[TT]-nst[tt] and [TT]-rmsmin[tt]). The center of a cluster is the",
1401 "structure with the smallest average RMSD from all other structures",
1402 "of the cluster.[BR]",
1406 int i, i1, i2, j, nf, nrms;
1409 rvec *xtps, *usextps, *x1, **xx = NULL;
1410 const char *fn, *trx_out_fn;
1412 t_mat *rms, *orig = NULL;
1417 t_matrix *readmat = NULL;
1420 int isize = 0, ifsize = 0, iosize = 0;
1421 atom_id *index = NULL, *fitidx, *outidx;
1423 real rmsd, **d1, **d2, *time = NULL, time_invfac, *mass = NULL;
1424 char buf[STRLEN], buf1[80], title[STRLEN];
1425 gmx_bool bAnalyze, bUseRmsdCut, bJP_RMSD = FALSE, bReadMat, bReadTraj, bPBC = TRUE;
1427 int method, ncluster = 0;
1428 static const char *methodname[] = {
1429 NULL, "linkage", "jarvis-patrick", "monte-carlo",
1430 "diagonalization", "gromos", NULL
1433 m_null, m_linkage, m_jarvis_patrick,
1434 m_monte_carlo, m_diagonalize, m_gromos, m_nr
1436 /* Set colors for plotting: white = zero RMS, black = maximum */
1437 static t_rgb rlo_top = { 1.0, 1.0, 1.0 };
1438 static t_rgb rhi_top = { 0.0, 0.0, 0.0 };
1439 static t_rgb rlo_bot = { 1.0, 1.0, 1.0 };
1440 static t_rgb rhi_bot = { 0.0, 0.0, 1.0 };
1441 static int nlevels = 40, skip = 1;
1442 static real scalemax = -1.0, rmsdcut = 0.1, rmsmin = 0.0;
1443 gmx_bool bRMSdist = FALSE, bBinary = FALSE, bAverage = FALSE, bFit = TRUE;
1444 static int niter = 10000, nrandom = 0, seed = 1993, write_ncl = 0, write_nst = 1, minstruct = 1;
1445 static real kT = 1e-3;
1446 static int M = 10, P = 3;
1448 gmx_rmpbc_t gpbc = NULL;
1451 { "-dista", FALSE, etBOOL, {&bRMSdist},
1452 "Use RMSD of distances instead of RMS deviation" },
1453 { "-nlevels", FALSE, etINT, {&nlevels},
1454 "Discretize RMSD matrix in this number of levels" },
1455 { "-cutoff", FALSE, etREAL, {&rmsdcut},
1456 "RMSD cut-off (nm) for two structures to be neighbor" },
1457 { "-fit", FALSE, etBOOL, {&bFit},
1458 "Use least squares fitting before RMSD calculation" },
1459 { "-max", FALSE, etREAL, {&scalemax},
1460 "Maximum level in RMSD matrix" },
1461 { "-skip", FALSE, etINT, {&skip},
1462 "Only analyze every nr-th frame" },
1463 { "-av", FALSE, etBOOL, {&bAverage},
1464 "Write average iso middle structure for each cluster" },
1465 { "-wcl", FALSE, etINT, {&write_ncl},
1466 "Write the structures for this number of clusters to numbered files" },
1467 { "-nst", FALSE, etINT, {&write_nst},
1468 "Only write all structures if more than this number of structures per cluster" },
1469 { "-rmsmin", FALSE, etREAL, {&rmsmin},
1470 "minimum rms difference with rest of cluster for writing structures" },
1471 { "-method", FALSE, etENUM, {methodname},
1472 "Method for cluster determination" },
1473 { "-minstruct", FALSE, etINT, {&minstruct},
1474 "Minimum number of structures in cluster for coloring in the [TT].xpm[tt] file" },
1475 { "-binary", FALSE, etBOOL, {&bBinary},
1476 "Treat the RMSD matrix as consisting of 0 and 1, where the cut-off "
1477 "is given by [TT]-cutoff[tt]" },
1478 { "-M", FALSE, etINT, {&M},
1479 "Number of nearest neighbors considered for Jarvis-Patrick algorithm, "
1480 "0 is use cutoff" },
1481 { "-P", FALSE, etINT, {&P},
1482 "Number of identical nearest neighbors required to form a cluster" },
1483 { "-seed", FALSE, etINT, {&seed},
1484 "Random number seed for Monte Carlo clustering algorithm: <= 0 means generate" },
1485 { "-niter", FALSE, etINT, {&niter},
1486 "Number of iterations for MC" },
1487 { "-nrandom", FALSE, etINT, {&nrandom},
1488 "The first iterations for MC may be done complete random, to shuffle the frames" },
1489 { "-kT", FALSE, etREAL, {&kT},
1490 "Boltzmann weighting factor for Monte Carlo optimization "
1491 "(zero turns off uphill steps)" },
1492 { "-pbc", FALSE, etBOOL,
1493 { &bPBC }, "PBC check" }
1496 { efTRX, "-f", NULL, ffOPTRD },
1497 { efTPS, "-s", NULL, ffOPTRD },
1498 { efNDX, NULL, NULL, ffOPTRD },
1499 { efXPM, "-dm", "rmsd", ffOPTRD },
1500 { efXPM, "-om", "rmsd-raw", ffWRITE },
1501 { efXPM, "-o", "rmsd-clust", ffWRITE },
1502 { efLOG, "-g", "cluster", ffWRITE },
1503 { efXVG, "-dist", "rmsd-dist", ffOPTWR },
1504 { efXVG, "-ev", "rmsd-eig", ffOPTWR },
1505 { efXVG, "-conv", "mc-conv", ffOPTWR },
1506 { efXVG, "-sz", "clust-size", ffOPTWR},
1507 { efXPM, "-tr", "clust-trans", ffOPTWR},
1508 { efXVG, "-ntr", "clust-trans", ffOPTWR},
1509 { efXVG, "-clid", "clust-id.xvg", ffOPTWR},
1510 { efTRX, "-cl", "clusters.pdb", ffOPTWR }
1512 #define NFILE asize(fnm)
1514 if (!parse_common_args(&argc, argv,
1515 PCA_CAN_VIEW | PCA_CAN_TIME | PCA_TIME_UNIT | PCA_BE_NICE,
1516 NFILE, fnm, asize(pa), pa, asize(desc), desc, 0, NULL,
1523 bReadMat = opt2bSet("-dm", NFILE, fnm);
1524 bReadTraj = opt2bSet("-f", NFILE, fnm) || !bReadMat;
1525 if (opt2parg_bSet("-av", asize(pa), pa) ||
1526 opt2parg_bSet("-wcl", asize(pa), pa) ||
1527 opt2parg_bSet("-nst", asize(pa), pa) ||
1528 opt2parg_bSet("-rmsmin", asize(pa), pa) ||
1529 opt2bSet("-cl", NFILE, fnm) )
1531 trx_out_fn = opt2fn("-cl", NFILE, fnm);
1537 if (bReadMat && output_env_get_time_factor(oenv) != 1)
1540 "\nWarning: assuming the time unit in %s is %s\n",
1541 opt2fn("-dm", NFILE, fnm), output_env_get_time_unit(oenv));
1543 if (trx_out_fn && !bReadTraj)
1545 fprintf(stderr, "\nWarning: "
1546 "cannot write cluster structures without reading trajectory\n"
1547 " ignoring option -cl %s\n", trx_out_fn);
1551 while (method < m_nr && gmx_strcasecmp(methodname[0], methodname[method]) != 0)
1557 gmx_fatal(FARGS, "Invalid method");
1560 bAnalyze = (method == m_linkage || method == m_jarvis_patrick ||
1561 method == m_gromos );
1564 log = ftp2FILE(efLOG, NFILE, fnm, "w");
1566 fprintf(stderr, "Using %s method for clustering\n", methodname[0]);
1567 fprintf(log, "Using %s method for clustering\n", methodname[0]);
1569 /* check input and write parameters to log file */
1570 bUseRmsdCut = FALSE;
1571 if (method == m_jarvis_patrick)
1573 bJP_RMSD = (M == 0) || opt2parg_bSet("-cutoff", asize(pa), pa);
1574 if ((M < 0) || (M == 1))
1576 gmx_fatal(FARGS, "M (%d) must be 0 or larger than 1", M);
1580 sprintf(buf1, "Will use P=%d and RMSD cutoff (%g)", P, rmsdcut);
1587 gmx_fatal(FARGS, "Number of neighbors required (P) must be less than M");
1591 sprintf(buf1, "Will use P=%d, M=%d and RMSD cutoff (%g)", P, M, rmsdcut);
1596 sprintf(buf1, "Will use P=%d, M=%d", P, M);
1599 ffprintf_s(stderr, log, buf, "%s for determining the neighbors\n\n", buf1);
1601 else /* method != m_jarvis */
1603 bUseRmsdCut = ( bBinary || method == m_linkage || method == m_gromos );
1605 if (bUseRmsdCut && method != m_jarvis_patrick)
1607 fprintf(log, "Using RMSD cutoff %g nm\n", rmsdcut);
1609 if (method == m_monte_carlo)
1611 fprintf(log, "Using %d iterations\n", niter);
1616 gmx_fatal(FARGS, "skip (%d) should be >= 1", skip);
1622 /* don't read mass-database as masses (and top) are not used */
1623 read_tps_conf(ftp2fn(efTPS, NFILE, fnm), buf, &top, &ePBC, &xtps, NULL, box,
1627 gpbc = gmx_rmpbc_init(&top.idef, ePBC, top.atoms.nr);
1630 fprintf(stderr, "\nSelect group for least squares fit%s:\n",
1631 bReadMat ? "" : " and RMSD calculation");
1632 get_index(&(top.atoms), ftp2fn_null(efNDX, NFILE, fnm),
1633 1, &ifsize, &fitidx, &grpname);
1636 fprintf(stderr, "\nSelect group for output:\n");
1637 get_index(&(top.atoms), ftp2fn_null(efNDX, NFILE, fnm),
1638 1, &iosize, &outidx, &grpname);
1639 /* merge and convert both index groups: */
1640 /* first copy outidx to index. let outidx refer to elements in index */
1641 snew(index, iosize);
1643 for (i = 0; i < iosize; i++)
1645 index[i] = outidx[i];
1648 /* now lookup elements from fitidx in index, add them if necessary
1649 and also let fitidx refer to elements in index */
1650 for (i = 0; i < ifsize; i++)
1653 while (j < isize && index[j] != fitidx[i])
1659 /* slow this way, but doesn't matter much */
1661 srenew(index, isize);
1663 index[j] = fitidx[i];
1667 else /* !trx_out_fn */
1671 for (i = 0; i < ifsize; i++)
1673 index[i] = fitidx[i];
1678 /* Initiate arrays */
1681 for (i = 0; (i < isize); i++)
1689 /* Loop over first coordinate file */
1690 fn = opt2fn("-f", NFILE, fnm);
1692 xx = read_whole_trj(fn, isize, index, skip, &nf, &time, oenv, bPBC, gpbc);
1693 output_env_conv_times(oenv, nf, time);
1694 if (!bRMSdist || bAnalyze)
1696 /* Center all frames on zero */
1698 for (i = 0; i < ifsize; i++)
1700 mass[fitidx[i]] = top.atoms.atom[index[fitidx[i]]].m;
1704 for (i = 0; i < nf; i++)
1706 reset_x(ifsize, fitidx, isize, NULL, xx[i], mass);
1712 gmx_rmpbc_done(gpbc);
1718 fprintf(stderr, "Reading rms distance matrix ");
1719 read_xpm_matrix(opt2fn("-dm", NFILE, fnm), &readmat);
1720 fprintf(stderr, "\n");
1721 if (readmat[0].nx != readmat[0].ny)
1723 gmx_fatal(FARGS, "Matrix (%dx%d) is not square",
1724 readmat[0].nx, readmat[0].ny);
1726 if (bReadTraj && bAnalyze && (readmat[0].nx != nf))
1728 gmx_fatal(FARGS, "Matrix size (%dx%d) does not match the number of "
1729 "frames (%d)", readmat[0].nx, readmat[0].ny, nf);
1734 time = readmat[0].axis_x;
1735 time_invfac = output_env_get_time_invfactor(oenv);
1736 for (i = 0; i < nf; i++)
1738 time[i] *= time_invfac;
1741 rms = init_mat(readmat[0].nx, method == m_diagonalize);
1742 convert_mat(&(readmat[0]), rms);
1744 nlevels = readmat[0].nmap;
1746 else /* !bReadMat */
1748 rms = init_mat(nf, method == m_diagonalize);
1749 nrms = (nf*(nf-1))/2;
1752 fprintf(stderr, "Computing %dx%d RMS deviation matrix\n", nf, nf);
1754 for (i1 = 0; (i1 < nf); i1++)
1756 for (i2 = i1+1; (i2 < nf); i2++)
1758 for (i = 0; i < isize; i++)
1760 copy_rvec(xx[i1][i], x1[i]);
1764 do_fit(isize, mass, xx[i2], x1);
1766 rmsd = rmsdev(isize, mass, xx[i2], x1);
1767 set_mat_entry(rms, i1, i2, rmsd);
1770 fprintf(stderr, "\r# RMSD calculations left: %d ", nrms);
1775 fprintf(stderr, "Computing %dx%d RMS distance deviation matrix\n", nf, nf);
1776 for (i1 = 0; (i1 < nf); i1++)
1778 calc_dist(isize, xx[i1], d1);
1779 for (i2 = i1+1; (i2 < nf); i2++)
1781 calc_dist(isize, xx[i2], d2);
1782 set_mat_entry(rms, i1, i2, rms_dist(isize, d1, d2));
1785 fprintf(stderr, "\r# RMSD calculations left: %d ", nrms);
1788 fprintf(stderr, "\n\n");
1790 ffprintf_gg(stderr, log, buf, "The RMSD ranges from %g to %g nm\n",
1791 rms->minrms, rms->maxrms);
1792 ffprintf_g(stderr, log, buf, "Average RMSD is %g\n", 2*rms->sumrms/(nf*(nf-1)));
1793 ffprintf_d(stderr, log, buf, "Number of structures for matrix %d\n", nf);
1794 ffprintf_g(stderr, log, buf, "Energy of the matrix is %g.\n", mat_energy(rms));
1795 if (bUseRmsdCut && (rmsdcut < rms->minrms || rmsdcut > rms->maxrms) )
1797 fprintf(stderr, "WARNING: rmsd cutoff %g is outside range of rmsd values "
1798 "%g to %g\n", rmsdcut, rms->minrms, rms->maxrms);
1800 if (bAnalyze && (rmsmin < rms->minrms) )
1802 fprintf(stderr, "WARNING: rmsd minimum %g is below lowest rmsd value %g\n",
1803 rmsmin, rms->minrms);
1805 if (bAnalyze && (rmsmin > rmsdcut) )
1807 fprintf(stderr, "WARNING: rmsd minimum %g is above rmsd cutoff %g\n",
1811 /* Plot the rmsd distribution */
1812 rmsd_distribution(opt2fn("-dist", NFILE, fnm), rms, oenv);
1816 for (i1 = 0; (i1 < nf); i1++)
1818 for (i2 = 0; (i2 < nf); i2++)
1820 if (rms->mat[i1][i2] < rmsdcut)
1822 rms->mat[i1][i2] = 0;
1826 rms->mat[i1][i2] = 1;
1836 /* Now sort the matrix and write it out again */
1837 gather(rms, rmsdcut, &clust);
1840 /* Do a diagonalization */
1841 snew(eigenvalues, nf);
1842 snew(eigenvectors, nf*nf);
1843 memcpy(eigenvectors, rms->mat[0], nf*nf*sizeof(real));
1844 eigensolver(eigenvectors, nf, 0, nf, eigenvalues, rms->mat[0]);
1845 sfree(eigenvectors);
1847 fp = xvgropen(opt2fn("-ev", NFILE, fnm), "RMSD matrix Eigenvalues",
1848 "Eigenvector index", "Eigenvalues (nm\\S2\\N)", oenv);
1849 for (i = 0; (i < nf); i++)
1851 fprintf(fp, "%10d %10g\n", i, eigenvalues[i]);
1856 orig = init_mat(rms->nn, FALSE);
1858 copy_t_mat(orig, rms);
1859 mc_optimize(log, rms, time, niter, nrandom, seed, kT,
1860 opt2fn_null("-conv", NFILE, fnm), oenv);
1862 case m_jarvis_patrick:
1863 jarvis_patrick(rms->nn, rms->mat, M, P, bJP_RMSD ? rmsdcut : -1, &clust);
1866 gromos(rms->nn, rms->mat, rmsdcut, &clust);
1869 gmx_fatal(FARGS, "DEATH HORROR unknown method \"%s\"", methodname[0]);
1872 if (method == m_monte_carlo || method == m_diagonalize)
1874 fprintf(stderr, "Energy of the matrix after clustering is %g.\n",
1882 ncluster = plot_clusters(nf, rms->mat, &clust, minstruct);
1886 mark_clusters(nf, rms->mat, rms->maxrms, &clust);
1888 init_t_atoms(&useatoms, isize, FALSE);
1889 snew(usextps, isize);
1890 useatoms.resinfo = top.atoms.resinfo;
1891 for (i = 0; i < isize; i++)
1893 useatoms.atomname[i] = top.atoms.atomname[index[i]];
1894 useatoms.atom[i].resind = top.atoms.atom[index[i]].resind;
1895 useatoms.nres = max(useatoms.nres, useatoms.atom[i].resind+1);
1896 copy_rvec(xtps[index[i]], usextps[i]);
1898 useatoms.nr = isize;
1899 analyze_clusters(nf, &clust, rms->mat, isize, &useatoms, usextps, mass, xx, time,
1900 ifsize, fitidx, iosize, outidx,
1901 bReadTraj ? trx_out_fn : NULL,
1902 opt2fn_null("-sz", NFILE, fnm),
1903 opt2fn_null("-tr", NFILE, fnm),
1904 opt2fn_null("-ntr", NFILE, fnm),
1905 opt2fn_null("-clid", NFILE, fnm),
1906 bAverage, write_ncl, write_nst, rmsmin, bFit, log,
1907 rlo_bot, rhi_bot, oenv);
1911 if (bBinary && !bAnalyze)
1913 /* Make the clustering visible */
1914 for (i2 = 0; (i2 < nf); i2++)
1916 for (i1 = i2+1; (i1 < nf); i1++)
1918 if (rms->mat[i1][i2])
1920 rms->mat[i1][i2] = rms->maxrms;
1926 fp = opt2FILE("-o", NFILE, fnm, "w");
1927 fprintf(stderr, "Writing rms distance/clustering matrix ");
1930 write_xpm(fp, 0, readmat[0].title, readmat[0].legend, readmat[0].label_x,
1931 readmat[0].label_y, nf, nf, readmat[0].axis_x, readmat[0].axis_y,
1932 rms->mat, 0.0, rms->maxrms, rlo_top, rhi_top, &nlevels);
1936 sprintf(buf, "Time (%s)", output_env_get_time_unit(oenv));
1937 sprintf(title, "RMS%sDeviation / Cluster Index",
1938 bRMSdist ? " Distance " : " ");
1941 write_xpm_split(fp, 0, title, "RMSD (nm)", buf, buf,
1942 nf, nf, time, time, rms->mat, 0.0, rms->maxrms, &nlevels,
1943 rlo_top, rhi_top, 0.0, (real) ncluster,
1944 &ncluster, TRUE, rlo_bot, rhi_bot);
1948 write_xpm(fp, 0, title, "RMSD (nm)", buf, buf,
1949 nf, nf, time, time, rms->mat, 0.0, rms->maxrms,
1950 rlo_top, rhi_top, &nlevels);
1953 fprintf(stderr, "\n");
1957 fp = opt2FILE("-om", NFILE, fnm, "w");
1958 sprintf(buf, "Time (%s)", output_env_get_time_unit(oenv));
1959 sprintf(title, "RMS%sDeviation", bRMSdist ? " Distance " : " ");
1960 write_xpm(fp, 0, title, "RMSD (nm)", buf, buf,
1961 nf, nf, time, time, orig->mat, 0.0, orig->maxrms,
1962 rlo_top, rhi_top, &nlevels);
1967 /* now show what we've done */
1968 do_view(oenv, opt2fn("-o", NFILE, fnm), "-nxy");
1969 do_view(oenv, opt2fn_null("-sz", NFILE, fnm), "-nxy");
1970 if (method == m_diagonalize)
1972 do_view(oenv, opt2fn_null("-ev", NFILE, fnm), "-nxy");
1974 do_view(oenv, opt2fn("-dist", NFILE, fnm), "-nxy");
1977 do_view(oenv, opt2fn_null("-tr", NFILE, fnm), "-nxy");
1978 do_view(oenv, opt2fn_null("-ntr", NFILE, fnm), "-nxy");
1979 do_view(oenv, opt2fn_null("-clid", NFILE, fnm), "-nxy");
1981 do_view(oenv, opt2fn_null("-conv", NFILE, fnm), NULL);