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-2013, The GROMACS development team.
6 * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
7 * Copyright (c) 2018,2019,2020, 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.
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.
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.
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.
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.
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.
46 #include "gromacs/commandline/pargs.h"
47 #include "gromacs/fileio/confio.h"
48 #include "gromacs/fileio/enxio.h"
49 #include "gromacs/fileio/gmxfio.h"
50 #include "gromacs/fileio/tpxio.h"
51 #include "gromacs/fileio/trxio.h"
52 #include "gromacs/fileio/xtcio.h"
53 #include "gromacs/math/functions.h"
54 #include "gromacs/math/units.h"
55 #include "gromacs/math/vec.h"
56 #include "gromacs/mdrun/mdmodules.h"
57 #include "gromacs/mdtypes/inputrec.h"
58 #include "gromacs/mdtypes/md_enums.h"
59 #include "gromacs/mdtypes/state.h"
60 #include "gromacs/pbcutil/pbc.h"
61 #include "gromacs/topology/atomprop.h"
62 #include "gromacs/topology/block.h"
63 #include "gromacs/topology/ifunc.h"
64 #include "gromacs/topology/index.h"
65 #include "gromacs/topology/mtop_util.h"
66 #include "gromacs/topology/topology.h"
67 #include "gromacs/trajectory/energyframe.h"
68 #include "gromacs/trajectory/trajectoryframe.h"
69 #include "gromacs/utility/arraysize.h"
70 #include "gromacs/utility/fatalerror.h"
71 #include "gromacs/utility/futil.h"
72 #include "gromacs/utility/smalloc.h"
96 static void comp_tpx(const char* fn1, const char* fn2, gmx_bool bRMSD, real ftol, real abstol)
106 for (i = 0; i < (fn2 ? 2 : 1); i++)
108 ir[i] = new t_inputrec();
109 read_tpx_state(ff[i], ir[i], &state[i], &(mtop[i]));
110 gmx::MDModules().adjustInputrecBasedOnModules(ir[i]);
114 cmp_inputrec(stdout, ir[0], ir[1], ftol, abstol);
115 compareMtop(stdout, mtop[0], mtop[1], ftol, abstol);
116 comp_state(&state[0], &state[1], bRMSD, ftol, abstol);
120 if (ir[0]->efep == efepNO)
122 fprintf(stdout, "inputrec->efep = %s\n", efep_names[ir[0]->efep]);
128 comp_pull_AB(stdout, *ir[0]->pull, ftol, abstol);
130 compareMtopAB(stdout, mtop[0], ftol, abstol);
137 static void comp_trx(const gmx_output_env_t* oenv, const char* fn1, const char* fn2, gmx_bool bRMSD, real ftol, real abstol)
142 t_trxstatus* status[2];
147 fprintf(stderr, "Comparing trajectory files %s and %s\n", fn1, fn2);
148 for (i = 0; i < 2; i++)
150 b[i] = read_first_frame(oenv, &status[i], fn[i], &fr[i], TRX_READ_X | TRX_READ_V | TRX_READ_F);
157 comp_frame(stdout, &(fr[0]), &(fr[1]), bRMSD, ftol, abstol);
159 for (i = 0; i < 2; i++)
161 b[i] = read_next_frame(oenv, status[i], &fr[i]);
163 } while (b[0] && b[1]);
165 for (i = 0; i < 2; i++)
167 if (b[i] && !b[1 - i])
169 fprintf(stdout, "\nEnd of file on %s but not on %s\n", fn[1 - i], fn[i]);
171 close_trx(status[i]);
176 fprintf(stdout, "\nBoth files read correctly\n");
180 static void chk_coords(int frame, int natoms, rvec* x, matrix box, real fac, real tol)
186 for (i = 0; (i < natoms); i++)
188 for (j = 0; (j < DIM); j++)
190 if ((vol > 0) && (fabs(x[i][j]) > fac * box[j][j]))
192 printf("Warning at frame %d: coordinates for atom %d are large (%g)\n", frame, i, x[i][j]);
195 if ((fabs(x[i][XX]) < tol) && (fabs(x[i][YY]) < tol) && (fabs(x[i][ZZ]) < tol))
202 printf("Warning at frame %d: there are %d particles with all coordinates zero\n", frame, nNul);
206 static void chk_vels(int frame, int natoms, rvec* v)
210 for (i = 0; (i < natoms); i++)
212 for (j = 0; (j < DIM); j++)
214 if (fabs(v[i][j]) > 500)
216 printf("Warning at frame %d. Velocities for atom %d are large (%g)\n", frame, i, v[i][j]);
222 static void chk_forces(int frame, int natoms, rvec* f)
226 for (i = 0; (i < natoms); i++)
228 for (j = 0; (j < DIM); j++)
230 if (fabs(f[i][j]) > 10000)
232 printf("Warning at frame %d. Forces for atom %d are large (%g)\n", frame, i, f[i][j]);
238 static void chk_bonds(const InteractionDefinitions* idef, PbcType pbcType, rvec* x, matrix box, real tol)
240 int ftype, k, ai, aj, type;
241 real b0, blen, deviation;
245 gmx::ArrayRef<const t_iparams> iparams = idef->iparams;
247 set_pbc(&pbc, pbcType, box);
248 for (ftype = 0; (ftype < F_NRE); ftype++)
250 if ((interaction_function[ftype].flags & IF_CHEMBOND) == IF_CHEMBOND)
252 for (k = 0; (k < idef->il[ftype].size());)
254 type = idef->il[ftype].iatoms[k++];
255 ai = idef->il[ftype].iatoms[k++];
256 aj = idef->il[ftype].iatoms[k++];
260 case F_BONDS: b0 = iparams[type].harmonic.rA; break;
261 case F_G96BONDS: b0 = std::sqrt(iparams[type].harmonic.rA); break;
262 case F_MORSE: b0 = iparams[type].morse.b0A; break;
263 case F_CUBICBONDS: b0 = iparams[type].cubic.b0; break;
264 case F_CONSTR: b0 = iparams[type].constr.dA; break;
269 pbc_dx(&pbc, x[ai], x[aj], dx);
271 deviation = gmx::square(blen - b0);
272 if (std::sqrt(deviation / gmx::square(b0)) > tol)
274 fprintf(stderr, "Distance between atoms %d and %d is %.3f, should be %.3f\n",
275 ai + 1, aj + 1, blen, b0);
283 static void chk_trj(const gmx_output_env_t* oenv, const char* fn, const char* tpr, real tol)
287 t_fr_time first, last;
288 int j = -1, new_natoms, natoms;
290 gmx_bool bShowTimestep = TRUE, newline = FALSE;
296 std::unique_ptr<gmx_localtop_t> top;
299 read_tpx_state(tpr, &ir, &state, &mtop);
300 top = std::make_unique<gmx_localtop_t>(mtop.ffparams);
301 gmx_mtop_generate_local_top(mtop, top.get(), ir.efep != efepNO);
306 printf("Checking file %s\n", fn);
336 read_first_frame(oenv, &status, fn, &fr, TRX_READ_X | TRX_READ_V | TRX_READ_F);
342 fprintf(stderr, "\n# Atoms %d\n", fr.natoms);
345 fprintf(stderr, "Precision %g (nm)\n", 1 / fr.prec);
349 if ((natoms > 0) && (new_natoms != natoms))
351 fprintf(stderr, "\nNumber of atoms at t=%g don't match (%d, %d)\n", old_t1, natoms, new_natoms);
356 if (std::fabs((fr.time - old_t1) - (old_t1 - old_t2))
357 > 0.1 * (std::fabs(fr.time - old_t1) + std::fabs(old_t1 - old_t2)))
359 bShowTimestep = FALSE;
360 fprintf(stderr, "%sTimesteps at t=%g don't match (%g, %g)\n", newline ? "\n" : "",
361 old_t1, old_t1 - old_t2, fr.time - old_t1);
367 chk_bonds(&top->idef, ir.pbcType, fr.x, fr.box, tol);
371 chk_coords(j, natoms, fr.x, fr.box, 1e5, tol);
375 chk_vels(j, natoms, fr.v);
379 chk_forces(j, natoms, fr.f);
385 new_natoms = fr.natoms;
386 #define INC(s, n, f, l, item) \
391 first.item = fr.time; \
393 last.item = fr.time; \
396 INC(fr, count, first, last, bStep)
397 INC(fr, count, first, last, bTime)
398 INC(fr, count, first, last, bLambda)
399 INC(fr, count, first, last, bX)
400 INC(fr, count, first, last, bV)
401 INC(fr, count, first, last, bF)
402 INC(fr, count, first, last, bBox)
404 } while (read_next_frame(oenv, status, &fr));
406 fprintf(stderr, "\n");
410 fprintf(stderr, "\nItem #frames");
413 fprintf(stderr, " Timestep (ps)");
415 fprintf(stderr, "\n");
416 #define PRINTITEM(label, item) \
417 fprintf(stderr, "%-10s %6d", label, count.item); \
418 if ((bShowTimestep) && (count.item > 1)) \
420 fprintf(stderr, " %g\n", (last.item - first.item) / (count.item - 1)); \
423 fprintf(stderr, "\n")
424 PRINTITEM("Step", bStep);
425 PRINTITEM("Time", bTime);
426 PRINTITEM("Lambda", bLambda);
427 PRINTITEM("Coords", bX);
428 PRINTITEM("Velocities", bV);
429 PRINTITEM("Forces", bF);
430 PRINTITEM("Box", bBox);
433 static void chk_tps(const char* fn, real vdw_fac, real bon_lo, real bon_hi)
443 gmx_bool bV, bX, bB, bFirst, bOut;
444 real r2, ekin, temp1, temp2, dist2, vdwfac2, bonlo2, bonhi2;
447 fprintf(stderr, "Checking coordinate file %s\n", fn);
448 read_tps_conf(fn, &top, &pbcType, &x, &v, box, TRUE);
451 fprintf(stderr, "%d atoms in file\n", atoms->nr);
453 /* check coordinates and box */
456 for (i = 0; (i < natom) && !(bV && bX); i++)
458 for (j = 0; (j < DIM) && !(bV && bX); j++)
460 bV = bV || (v[i][j] != 0);
461 bX = bX || (x[i][j] != 0);
465 for (i = 0; (i < DIM) && !bB; i++)
467 for (j = 0; (j < DIM) && !bB; j++)
469 bB = bB || (box[i][j] != 0);
473 fprintf(stderr, "coordinates %s\n", bX ? "found" : "absent");
474 fprintf(stderr, "box %s\n", bB ? "found" : "absent");
475 fprintf(stderr, "velocities %s\n", bV ? "found" : "absent");
476 fprintf(stderr, "\n");
478 /* check velocities */
482 for (i = 0; (i < natom); i++)
484 for (j = 0; (j < DIM); j++)
486 ekin += 0.5 * atoms->atom[i].m * v[i][j] * v[i][j];
489 temp1 = (2.0 * ekin) / (natom * DIM * BOLTZ);
490 temp2 = (2.0 * ekin) / (natom * (DIM - 1) * BOLTZ);
491 fprintf(stderr, "Kinetic energy: %g (kJ/mol)\n", ekin);
493 "Assuming the number of degrees of freedom to be "
494 "Natoms * %d or Natoms * %d,\n"
495 "the velocities correspond to a temperature of the system\n"
496 "of %g K or %g K respectively.\n\n",
497 DIM, DIM - 1, temp1, temp2);
500 /* check coordinates */
503 vdwfac2 = gmx::square(vdw_fac);
504 bonlo2 = gmx::square(bon_lo);
505 bonhi2 = gmx::square(bon_hi);
508 "Checking for atoms closer than %g and not between %g and %g,\n"
509 "relative to sum of Van der Waals distance:\n",
510 vdw_fac, bon_lo, bon_hi);
511 snew(atom_vdw, natom);
513 for (i = 0; (i < natom); i++)
515 aps.setAtomProperty(epropVDW, *(atoms->resinfo[atoms->atom[i].resind].name),
516 *(atoms->atomname[i]), &(atom_vdw[i]));
519 fprintf(debug, "%5d %4s %4s %7g\n", i + 1, *(atoms->resinfo[atoms->atom[i].resind].name),
520 *(atoms->atomname[i]), atom_vdw[i]);
525 set_pbc(&pbc, pbcType, box);
529 for (i = 0; (i < natom); i++)
531 if (((i + 1) % 10) == 0)
533 fprintf(stderr, "\r%5d", i + 1);
536 for (j = i + 1; (j < natom); j++)
540 pbc_dx(&pbc, x[i], x[j], dx);
544 rvec_sub(x[i], x[j], dx);
547 dist2 = gmx::square(atom_vdw[i] + atom_vdw[j]);
548 if ((r2 <= dist2 * bonlo2) || ((r2 >= dist2 * bonhi2) && (r2 <= dist2 * vdwfac2)))
552 fprintf(stderr, "\r%5s %4s %8s %5s %5s %4s %8s %5s %6s\n", "atom#", "name",
553 "residue", "r_vdw", "atom#", "name", "residue", "r_vdw", "distance");
556 fprintf(stderr, "\r%5d %4s %4s%4d %-5.3g %5d %4s %4s%4d %-5.3g %-6.4g\n", i + 1,
557 *(atoms->atomname[i]), *(atoms->resinfo[atoms->atom[i].resind].name),
558 atoms->resinfo[atoms->atom[i].resind].nr, atom_vdw[i], j + 1,
559 *(atoms->atomname[j]), *(atoms->resinfo[atoms->atom[j].resind].name),
560 atoms->resinfo[atoms->atom[j].resind].nr, atom_vdw[j], std::sqrt(r2));
566 fprintf(stderr, "\rno close atoms found\n");
568 fprintf(stderr, "\r \n");
575 for (i = 0; (i < natom) && (k < 10); i++)
578 for (j = 0; (j < DIM) && !bOut; j++)
580 bOut = bOut || (x[i][j] < 0) || (x[i][j] > box[j][j]);
587 fprintf(stderr, "Atoms outside box ( ");
588 for (j = 0; (j < DIM); j++)
590 fprintf(stderr, "%g ", box[j][j]);
594 "(These may occur often and are normally not a problem)\n"
595 "%5s %4s %8s %5s %s\n",
596 "atom#", "name", "residue", "r_vdw", "coordinate");
599 fprintf(stderr, "%5d %4s %4s%4d %-5.3g", i, *(atoms->atomname[i]),
600 *(atoms->resinfo[atoms->atom[i].resind].name),
601 atoms->resinfo[atoms->atom[i].resind].nr, atom_vdw[i]);
602 for (j = 0; (j < DIM); j++)
604 fprintf(stderr, " %6.3g", x[i][j]);
606 fprintf(stderr, "\n");
611 fprintf(stderr, "(maybe more)\n");
615 fprintf(stderr, "no atoms found outside box\n");
617 fprintf(stderr, "\n");
622 static void chk_ndx(const char* fn)
628 grps = init_index(fn, &grpname);
631 pr_blocka(debug, 0, fn, grps, FALSE);
635 printf("Contents of index file %s\n", fn);
636 printf("--------------------------------------------------\n");
637 printf("Nr. Group #Entries First Last\n");
638 for (i = 0; (i < grps->nr); i++)
640 printf("%4d %-20s%8d%8d%8d\n", i, grpname[i], grps->index[i + 1] - grps->index[i],
641 grps->a[grps->index[i]] + 1, grps->a[grps->index[i + 1] - 1] + 1);
644 for (i = 0; (i < grps->nr); i++)
652 static void chk_enx(const char* fn)
656 gmx_enxnm_t* enm = nullptr;
660 real t0, old_t1, old_t2;
663 fprintf(stderr, "Checking energy file %s\n\n", fn);
665 in = open_enx(fn, "r");
666 do_enxnms(in, &nre, &enm);
667 fprintf(stderr, "%d groups in energy file", nre);
676 while (do_enx(in, fr))
680 if (fabs((fr->t - old_t1) - (old_t1 - old_t2))
681 > 0.1 * (fabs(fr->t - old_t1) + std::fabs(old_t1 - old_t2)))
684 fprintf(stderr, "\nTimesteps at t=%g don't match (%g, %g)\n", old_t1,
685 old_t1 - old_t2, fr->t - old_t1);
697 fprintf(stderr, "\rframe: %6s (index %6d), t: %10.3f\n", gmx_step_str(fr->step, buf),
702 fprintf(stderr, "\n\nFound %d frames", fnr);
703 if (bShowTStep && fnr > 1)
705 fprintf(stderr, " with a timestep of %g ps", (old_t1 - t0) / (fnr - 1));
707 fprintf(stderr, ".\n");
710 free_enxnms(nre, enm);
714 int gmx_check(int argc, char* argv[])
716 const char* desc[] = {
717 "[THISMODULE] reads a trajectory ([REF].tng[ref], [REF].trr[ref] or ",
718 "[REF].xtc[ref]), an energy file ([REF].edr[ref])",
719 "or an index file ([REF].ndx[ref])",
720 "and prints out useful information about them.[PAR]",
721 "Option [TT]-c[tt] checks for presence of coordinates,",
722 "velocities and box in the file, for close contacts (smaller than",
723 "[TT]-vdwfac[tt] and not bonded, i.e. not between [TT]-bonlo[tt]",
724 "and [TT]-bonhi[tt], all relative to the sum of both Van der Waals",
725 "radii) and atoms outside the box (these may occur often and are",
726 "no problem). If velocities are present, an estimated temperature",
727 "will be calculated from them.[PAR]",
728 "If an index file, is given its contents will be summarized.[PAR]",
729 "If both a trajectory and a [REF].tpr[ref] file are given (with [TT]-s1[tt])",
730 "the program will check whether the bond lengths defined in the tpr",
731 "file are indeed correct in the trajectory. If not you may have",
732 "non-matching files due to e.g. deshuffling or due to problems with",
733 "virtual sites. With these flags, [TT]gmx check[tt] provides a quick check for ",
734 "such problems.[PAR]",
735 "The program can compare two run input ([REF].tpr[ref])",
737 "when both [TT]-s1[tt] and [TT]-s2[tt] are supplied. When comparing",
738 "run input files this way, the default relative tolerance is reduced",
739 "to 0.000001 and the absolute tolerance set to zero to find any differences",
740 "not due to minor compiler optimization differences, although you can",
741 "of course still set any other tolerances through the options.",
742 "Similarly a pair of trajectory files can be compared (using the [TT]-f2[tt]",
743 "option), or a pair of energy files (using the [TT]-e2[tt] option).[PAR]",
744 "For free energy simulations the A and B state topology from one",
745 "run input file can be compared with options [TT]-s1[tt] and [TT]-ab[tt].[PAR]"
747 t_filenm fnm[] = { { efTRX, "-f", nullptr, ffOPTRD }, { efTRX, "-f2", nullptr, ffOPTRD },
748 { efTPR, "-s1", "top1", ffOPTRD }, { efTPR, "-s2", "top2", ffOPTRD },
749 { efTPS, "-c", nullptr, ffOPTRD }, { efEDR, "-e", nullptr, ffOPTRD },
750 { efEDR, "-e2", "ener2", ffOPTRD }, { efNDX, "-n", nullptr, ffOPTRD },
751 { efTEX, "-m", nullptr, ffOPTWR } };
752 #define NFILE asize(fnm)
753 const char *fn1 = nullptr, *fn2 = nullptr, *tex = nullptr;
755 gmx_output_env_t* oenv;
756 static real vdw_fac = 0.8;
757 static real bon_lo = 0.4;
758 static real bon_hi = 0.7;
759 static gmx_bool bRMSD = FALSE;
760 static real ftol = 0.001;
761 static real abstol = 0.001;
762 static gmx_bool bCompAB = FALSE;
763 static char* lastener = nullptr;
764 static t_pargs pa[] = {
769 "Fraction of sum of VdW radii used as warning cutoff" },
770 { "-bonlo", FALSE, etREAL, { &bon_lo }, "Min. fract. of sum of VdW radii for bonded atoms" },
771 { "-bonhi", FALSE, etREAL, { &bon_hi }, "Max. fract. of sum of VdW radii for bonded atoms" },
772 { "-rmsd", FALSE, etBOOL, { &bRMSD }, "Print RMSD for x, v and f" },
777 "Relative tolerance for comparing real values defined as "
778 "[MATH]2*(a-b)/([MAG]a[mag]+[MAG]b[mag])[math]" },
783 "Absolute tolerance, useful when sums are close to zero." },
784 { "-ab", FALSE, etBOOL, { &bCompAB }, "Compare the A and B topology from one file" },
789 "Last energy term to compare (if not given all are tested). It makes sense to go up "
790 "until the Pressure." }
793 if (!parse_common_args(&argc, argv, 0, NFILE, fnm, asize(pa), pa, asize(desc), desc, 0, nullptr, &oenv))
798 fn1 = opt2fn_null("-f", NFILE, fnm);
799 fn2 = opt2fn_null("-f2", NFILE, fnm);
800 tex = opt2fn_null("-m", NFILE, fnm);
805 "LaTeX file writing has been removed from gmx check. "
806 "Please use gmx report-methods instead for it.\n");
810 comp_trx(oenv, fn1, fn2, bRMSD, ftol, abstol);
814 chk_trj(oenv, fn1, opt2fn_null("-s1", NFILE, fnm), ftol);
818 fprintf(stderr, "Please give me TWO trajectory (.xtc/.trr/.tng) files!\n");
820 output_env_done(oenv);
822 fn1 = opt2fn_null("-s1", NFILE, fnm);
823 fn2 = opt2fn_null("-s2", NFILE, fnm);
824 if ((fn1 && fn2) || bCompAB)
830 gmx_fatal(FARGS, "With -ab you need to set the -s1 option");
835 fprintf(stderr, "Note: When comparing run input files, default tolerances are reduced.\n");
836 if (!opt2parg_bSet("-tol", asize(pa), pa))
840 if (!opt2parg_bSet("-abstol", asize(pa), pa))
844 comp_tpx(fn1, fn2, bRMSD, ftol, abstol);
846 else if ((fn1 && !opt2fn_null("-f", NFILE, fnm)) || (!fn1 && fn2))
848 fprintf(stderr, "Please give me TWO run input (.tpr) files\n");
851 fn1 = opt2fn_null("-e", NFILE, fnm);
852 fn2 = opt2fn_null("-e2", NFILE, fnm);
855 comp_enx(fn1, fn2, ftol, abstol, lastener);
859 chk_enx(ftp2fn(efEDR, NFILE, fnm));
863 fprintf(stderr, "Please give me TWO energy (.edr/.ene) files!\n");
866 if (ftp2bSet(efTPS, NFILE, fnm))
868 chk_tps(ftp2fn(efTPS, NFILE, fnm), vdw_fac, bon_lo, bon_hi);
871 if (ftp2bSet(efNDX, NFILE, fnm))
873 chk_ndx(ftp2fn(efNDX, NFILE, fnm));