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,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/fileio/confio.h"
47 #include "gromacs/fileio/enxio.h"
48 #include "gromacs/fileio/gmxfio.h"
49 #include "gromacs/fileio/tpxio.h"
50 #include "gromacs/fileio/trxio.h"
51 #include "gromacs/fileio/xtcio.h"
52 #include "gromacs/math/functions.h"
53 #include "gromacs/math/units.h"
54 #include "gromacs/math/vec.h"
55 #include "gromacs/mdrun/mdmodules.h"
56 #include "gromacs/mdtypes/inputrec.h"
57 #include "gromacs/mdtypes/md_enums.h"
58 #include "gromacs/mdtypes/state.h"
59 #include "gromacs/pbcutil/pbc.h"
60 #include "gromacs/topology/atomprop.h"
61 #include "gromacs/topology/block.h"
62 #include "gromacs/topology/ifunc.h"
63 #include "gromacs/topology/index.h"
64 #include "gromacs/topology/mtop_util.h"
65 #include "gromacs/topology/topology.h"
66 #include "gromacs/trajectory/energyframe.h"
67 #include "gromacs/trajectory/trajectoryframe.h"
68 #include "gromacs/utility/arraysize.h"
69 #include "gromacs/utility/fatalerror.h"
70 #include "gromacs/utility/futil.h"
71 #include "gromacs/utility/smalloc.h"
95 static void comp_tpx(const char* fn1, const char* fn2, gmx_bool bRMSD, real ftol, real abstol)
105 for (i = 0; i < (fn2 ? 2 : 1); i++)
107 ir[i] = new t_inputrec();
108 read_tpx_state(ff[i], ir[i], &state[i], &(mtop[i]));
109 gmx::MDModules().adjustInputrecBasedOnModules(ir[i]);
113 cmp_inputrec(stdout, ir[0], ir[1], ftol, abstol);
114 compareMtop(stdout, mtop[0], mtop[1], ftol, abstol);
115 comp_state(&state[0], &state[1], bRMSD, ftol, abstol);
119 if (ir[0]->efep == efepNO)
121 fprintf(stdout, "inputrec->efep = %s\n", efep_names[ir[0]->efep]);
127 comp_pull_AB(stdout, ir[0]->pull, ftol, abstol);
129 compareMtopAB(stdout, mtop[0], ftol, abstol);
134 static void comp_trx(const gmx_output_env_t* oenv, const char* fn1, const char* fn2, gmx_bool bRMSD, real ftol, real abstol)
139 t_trxstatus* status[2];
144 fprintf(stderr, "Comparing trajectory files %s and %s\n", fn1, fn2);
145 for (i = 0; i < 2; i++)
147 b[i] = read_first_frame(oenv, &status[i], fn[i], &fr[i], TRX_READ_X | TRX_READ_V | TRX_READ_F);
154 comp_frame(stdout, &(fr[0]), &(fr[1]), bRMSD, ftol, abstol);
156 for (i = 0; i < 2; i++)
158 b[i] = read_next_frame(oenv, status[i], &fr[i]);
160 } while (b[0] && b[1]);
162 for (i = 0; i < 2; i++)
164 if (b[i] && !b[1 - i])
166 fprintf(stdout, "\nEnd of file on %s but not on %s\n", fn[1 - i], fn[i]);
168 close_trx(status[i]);
173 fprintf(stdout, "\nBoth files read correctly\n");
177 static void chk_coords(int frame, int natoms, rvec* x, matrix box, real fac, real tol)
183 for (i = 0; (i < natoms); i++)
185 for (j = 0; (j < DIM); j++)
187 if ((vol > 0) && (fabs(x[i][j]) > fac * box[j][j]))
189 printf("Warning at frame %d: coordinates for atom %d are large (%g)\n", frame, i, x[i][j]);
192 if ((fabs(x[i][XX]) < tol) && (fabs(x[i][YY]) < tol) && (fabs(x[i][ZZ]) < tol))
199 printf("Warning at frame %d: there are %d particles with all coordinates zero\n", frame, nNul);
203 static void chk_vels(int frame, int natoms, rvec* v)
207 for (i = 0; (i < natoms); i++)
209 for (j = 0; (j < DIM); j++)
211 if (fabs(v[i][j]) > 500)
213 printf("Warning at frame %d. Velocities for atom %d are large (%g)\n", frame, i, v[i][j]);
219 static void chk_forces(int frame, int natoms, rvec* f)
223 for (i = 0; (i < natoms); i++)
225 for (j = 0; (j < DIM); j++)
227 if (fabs(f[i][j]) > 10000)
229 printf("Warning at frame %d. Forces for atom %d are large (%g)\n", frame, i, f[i][j]);
235 static void chk_bonds(t_idef* idef, int ePBC, rvec* x, matrix box, real tol)
237 int ftype, k, ai, aj, type;
238 real b0, blen, deviation;
242 set_pbc(&pbc, ePBC, box);
243 for (ftype = 0; (ftype < F_NRE); ftype++)
245 if ((interaction_function[ftype].flags & IF_CHEMBOND) == IF_CHEMBOND)
247 for (k = 0; (k < idef->il[ftype].nr);)
249 type = idef->il[ftype].iatoms[k++];
250 ai = idef->il[ftype].iatoms[k++];
251 aj = idef->il[ftype].iatoms[k++];
255 case F_BONDS: b0 = idef->iparams[type].harmonic.rA; break;
256 case F_G96BONDS: b0 = std::sqrt(idef->iparams[type].harmonic.rA); break;
257 case F_MORSE: b0 = idef->iparams[type].morse.b0A; break;
258 case F_CUBICBONDS: b0 = idef->iparams[type].cubic.b0; break;
259 case F_CONSTR: b0 = idef->iparams[type].constr.dA; break;
264 pbc_dx(&pbc, x[ai], x[aj], dx);
266 deviation = gmx::square(blen - b0);
267 if (std::sqrt(deviation / gmx::square(b0)) > tol)
269 fprintf(stderr, "Distance between atoms %d and %d is %.3f, should be %.3f\n",
270 ai + 1, aj + 1, blen, b0);
278 static void chk_trj(const gmx_output_env_t* oenv, const char* fn, const char* tpr, real tol)
282 t_fr_time first, last;
283 int j = -1, new_natoms, natoms;
285 gmx_bool bShowTimestep = TRUE, newline = FALSE;
294 read_tpx_state(tpr, &ir, &state, &mtop);
295 gmx_mtop_generate_local_top(mtop, &top, ir.efep != efepNO);
300 printf("Checking file %s\n", fn);
330 read_first_frame(oenv, &status, fn, &fr, TRX_READ_X | TRX_READ_V | TRX_READ_F);
336 fprintf(stderr, "\n# Atoms %d\n", fr.natoms);
339 fprintf(stderr, "Precision %g (nm)\n", 1 / fr.prec);
343 if ((natoms > 0) && (new_natoms != natoms))
345 fprintf(stderr, "\nNumber of atoms at t=%g don't match (%d, %d)\n", old_t1, natoms, new_natoms);
350 if (std::fabs((fr.time - old_t1) - (old_t1 - old_t2))
351 > 0.1 * (std::fabs(fr.time - old_t1) + std::fabs(old_t1 - old_t2)))
353 bShowTimestep = FALSE;
354 fprintf(stderr, "%sTimesteps at t=%g don't match (%g, %g)\n", newline ? "\n" : "",
355 old_t1, old_t1 - old_t2, fr.time - old_t1);
361 chk_bonds(&top.idef, ir.ePBC, fr.x, fr.box, tol);
365 chk_coords(j, natoms, fr.x, fr.box, 1e5, tol);
369 chk_vels(j, natoms, fr.v);
373 chk_forces(j, natoms, fr.f);
379 new_natoms = fr.natoms;
380 #define INC(s, n, f, l, item) \
385 first.item = fr.time; \
387 last.item = fr.time; \
390 INC(fr, count, first, last, bStep)
391 INC(fr, count, first, last, bTime)
392 INC(fr, count, first, last, bLambda)
393 INC(fr, count, first, last, bX)
394 INC(fr, count, first, last, bV)
395 INC(fr, count, first, last, bF)
396 INC(fr, count, first, last, bBox)
398 } while (read_next_frame(oenv, status, &fr));
400 fprintf(stderr, "\n");
404 fprintf(stderr, "\nItem #frames");
407 fprintf(stderr, " Timestep (ps)");
409 fprintf(stderr, "\n");
410 #define PRINTITEM(label, item) \
411 fprintf(stderr, "%-10s %6d", label, count.item); \
412 if ((bShowTimestep) && (count.item > 1)) \
414 fprintf(stderr, " %g\n", (last.item - first.item) / (count.item - 1)); \
417 fprintf(stderr, "\n")
418 PRINTITEM("Step", bStep);
419 PRINTITEM("Time", bTime);
420 PRINTITEM("Lambda", bLambda);
421 PRINTITEM("Coords", bX);
422 PRINTITEM("Velocities", bV);
423 PRINTITEM("Forces", bF);
424 PRINTITEM("Box", bBox);
427 static void chk_tps(const char* fn, real vdw_fac, real bon_lo, real bon_hi)
437 gmx_bool bV, bX, bB, bFirst, bOut;
438 real r2, ekin, temp1, temp2, dist2, vdwfac2, bonlo2, bonhi2;
441 fprintf(stderr, "Checking coordinate file %s\n", fn);
442 read_tps_conf(fn, &top, &ePBC, &x, &v, box, TRUE);
445 fprintf(stderr, "%d atoms in file\n", atoms->nr);
447 /* check coordinates and box */
450 for (i = 0; (i < natom) && !(bV && bX); i++)
452 for (j = 0; (j < DIM) && !(bV && bX); j++)
454 bV = bV || (v[i][j] != 0);
455 bX = bX || (x[i][j] != 0);
459 for (i = 0; (i < DIM) && !bB; i++)
461 for (j = 0; (j < DIM) && !bB; j++)
463 bB = bB || (box[i][j] != 0);
467 fprintf(stderr, "coordinates %s\n", bX ? "found" : "absent");
468 fprintf(stderr, "box %s\n", bB ? "found" : "absent");
469 fprintf(stderr, "velocities %s\n", bV ? "found" : "absent");
470 fprintf(stderr, "\n");
472 /* check velocities */
476 for (i = 0; (i < natom); i++)
478 for (j = 0; (j < DIM); j++)
480 ekin += 0.5 * atoms->atom[i].m * v[i][j] * v[i][j];
483 temp1 = (2.0 * ekin) / (natom * DIM * BOLTZ);
484 temp2 = (2.0 * ekin) / (natom * (DIM - 1) * BOLTZ);
485 fprintf(stderr, "Kinetic energy: %g (kJ/mol)\n", ekin);
487 "Assuming the number of degrees of freedom to be "
488 "Natoms * %d or Natoms * %d,\n"
489 "the velocities correspond to a temperature of the system\n"
490 "of %g K or %g K respectively.\n\n",
491 DIM, DIM - 1, temp1, temp2);
494 /* check coordinates */
497 vdwfac2 = gmx::square(vdw_fac);
498 bonlo2 = gmx::square(bon_lo);
499 bonhi2 = gmx::square(bon_hi);
502 "Checking for atoms closer than %g and not between %g and %g,\n"
503 "relative to sum of Van der Waals distance:\n",
504 vdw_fac, bon_lo, bon_hi);
505 snew(atom_vdw, natom);
507 for (i = 0; (i < natom); i++)
509 aps.setAtomProperty(epropVDW, *(atoms->resinfo[atoms->atom[i].resind].name),
510 *(atoms->atomname[i]), &(atom_vdw[i]));
513 fprintf(debug, "%5d %4s %4s %7g\n", i + 1, *(atoms->resinfo[atoms->atom[i].resind].name),
514 *(atoms->atomname[i]), atom_vdw[i]);
519 set_pbc(&pbc, ePBC, box);
523 for (i = 0; (i < natom); i++)
525 if (((i + 1) % 10) == 0)
527 fprintf(stderr, "\r%5d", i + 1);
530 for (j = i + 1; (j < natom); j++)
534 pbc_dx(&pbc, x[i], x[j], dx);
538 rvec_sub(x[i], x[j], dx);
541 dist2 = gmx::square(atom_vdw[i] + atom_vdw[j]);
542 if ((r2 <= dist2 * bonlo2) || ((r2 >= dist2 * bonhi2) && (r2 <= dist2 * vdwfac2)))
546 fprintf(stderr, "\r%5s %4s %8s %5s %5s %4s %8s %5s %6s\n", "atom#", "name",
547 "residue", "r_vdw", "atom#", "name", "residue", "r_vdw", "distance");
550 fprintf(stderr, "\r%5d %4s %4s%4d %-5.3g %5d %4s %4s%4d %-5.3g %-6.4g\n", i + 1,
551 *(atoms->atomname[i]), *(atoms->resinfo[atoms->atom[i].resind].name),
552 atoms->resinfo[atoms->atom[i].resind].nr, atom_vdw[i], j + 1,
553 *(atoms->atomname[j]), *(atoms->resinfo[atoms->atom[j].resind].name),
554 atoms->resinfo[atoms->atom[j].resind].nr, atom_vdw[j], std::sqrt(r2));
560 fprintf(stderr, "\rno close atoms found\n");
562 fprintf(stderr, "\r \n");
569 for (i = 0; (i < natom) && (k < 10); i++)
572 for (j = 0; (j < DIM) && !bOut; j++)
574 bOut = bOut || (x[i][j] < 0) || (x[i][j] > box[j][j]);
581 fprintf(stderr, "Atoms outside box ( ");
582 for (j = 0; (j < DIM); j++)
584 fprintf(stderr, "%g ", box[j][j]);
588 "(These may occur often and are normally not a problem)\n"
589 "%5s %4s %8s %5s %s\n",
590 "atom#", "name", "residue", "r_vdw", "coordinate");
593 fprintf(stderr, "%5d %4s %4s%4d %-5.3g", i, *(atoms->atomname[i]),
594 *(atoms->resinfo[atoms->atom[i].resind].name),
595 atoms->resinfo[atoms->atom[i].resind].nr, atom_vdw[i]);
596 for (j = 0; (j < DIM); j++)
598 fprintf(stderr, " %6.3g", x[i][j]);
600 fprintf(stderr, "\n");
605 fprintf(stderr, "(maybe more)\n");
609 fprintf(stderr, "no atoms found outside box\n");
611 fprintf(stderr, "\n");
616 static void chk_ndx(const char* fn)
622 grps = init_index(fn, &grpname);
625 pr_blocka(debug, 0, fn, grps, FALSE);
629 printf("Contents of index file %s\n", fn);
630 printf("--------------------------------------------------\n");
631 printf("Nr. Group #Entries First Last\n");
632 for (i = 0; (i < grps->nr); i++)
634 printf("%4d %-20s%8d%8d%8d\n", i, grpname[i], grps->index[i + 1] - grps->index[i],
635 grps->a[grps->index[i]] + 1, grps->a[grps->index[i + 1] - 1] + 1);
638 for (i = 0; (i < grps->nr); i++)
646 static void chk_enx(const char* fn)
650 gmx_enxnm_t* enm = nullptr;
654 real t0, old_t1, old_t2;
657 fprintf(stderr, "Checking energy file %s\n\n", fn);
659 in = open_enx(fn, "r");
660 do_enxnms(in, &nre, &enm);
661 fprintf(stderr, "%d groups in energy file", nre);
670 while (do_enx(in, fr))
674 if (fabs((fr->t - old_t1) - (old_t1 - old_t2))
675 > 0.1 * (fabs(fr->t - old_t1) + std::fabs(old_t1 - old_t2)))
678 fprintf(stderr, "\nTimesteps at t=%g don't match (%g, %g)\n", old_t1,
679 old_t1 - old_t2, fr->t - old_t1);
691 fprintf(stderr, "\rframe: %6s (index %6d), t: %10.3f\n", gmx_step_str(fr->step, buf),
696 fprintf(stderr, "\n\nFound %d frames", fnr);
697 if (bShowTStep && fnr > 1)
699 fprintf(stderr, " with a timestep of %g ps", (old_t1 - t0) / (fnr - 1));
701 fprintf(stderr, ".\n");
704 free_enxnms(nre, enm);
708 int gmx_check(int argc, char* argv[])
710 const char* desc[] = {
711 "[THISMODULE] reads a trajectory ([REF].tng[ref], [REF].trr[ref] or ",
712 "[REF].xtc[ref]), an energy file ([REF].edr[ref])",
713 "or an index file ([REF].ndx[ref])",
714 "and prints out useful information about them.[PAR]",
715 "Option [TT]-c[tt] checks for presence of coordinates,",
716 "velocities and box in the file, for close contacts (smaller than",
717 "[TT]-vdwfac[tt] and not bonded, i.e. not between [TT]-bonlo[tt]",
718 "and [TT]-bonhi[tt], all relative to the sum of both Van der Waals",
719 "radii) and atoms outside the box (these may occur often and are",
720 "no problem). If velocities are present, an estimated temperature",
721 "will be calculated from them.[PAR]",
722 "If an index file, is given its contents will be summarized.[PAR]",
723 "If both a trajectory and a [REF].tpr[ref] file are given (with [TT]-s1[tt])",
724 "the program will check whether the bond lengths defined in the tpr",
725 "file are indeed correct in the trajectory. If not you may have",
726 "non-matching files due to e.g. deshuffling or due to problems with",
727 "virtual sites. With these flags, [TT]gmx check[tt] provides a quick check for ",
728 "such problems.[PAR]",
729 "The program can compare two run input ([REF].tpr[ref])",
731 "when both [TT]-s1[tt] and [TT]-s2[tt] are supplied. When comparing",
732 "run input files this way, the default relative tolerance is reduced",
733 "to 0.000001 and the absolute tolerance set to zero to find any differences",
734 "not due to minor compiler optimization differences, although you can",
735 "of course still set any other tolerances through the options.",
736 "Similarly a pair of trajectory files can be compared (using the [TT]-f2[tt]",
737 "option), or a pair of energy files (using the [TT]-e2[tt] option).[PAR]",
738 "For free energy simulations the A and B state topology from one",
739 "run input file can be compared with options [TT]-s1[tt] and [TT]-ab[tt].[PAR]"
741 t_filenm fnm[] = { { efTRX, "-f", nullptr, ffOPTRD }, { efTRX, "-f2", nullptr, ffOPTRD },
742 { efTPR, "-s1", "top1", ffOPTRD }, { efTPR, "-s2", "top2", ffOPTRD },
743 { efTPS, "-c", nullptr, ffOPTRD }, { efEDR, "-e", nullptr, ffOPTRD },
744 { efEDR, "-e2", "ener2", ffOPTRD }, { efNDX, "-n", nullptr, ffOPTRD },
745 { efTEX, "-m", nullptr, ffOPTWR } };
746 #define NFILE asize(fnm)
747 const char *fn1 = nullptr, *fn2 = nullptr, *tex = nullptr;
749 gmx_output_env_t* oenv;
750 static real vdw_fac = 0.8;
751 static real bon_lo = 0.4;
752 static real bon_hi = 0.7;
753 static gmx_bool bRMSD = FALSE;
754 static real ftol = 0.001;
755 static real abstol = 0.001;
756 static gmx_bool bCompAB = FALSE;
757 static char* lastener = nullptr;
758 static t_pargs pa[] = {
763 "Fraction of sum of VdW radii used as warning cutoff" },
764 { "-bonlo", FALSE, etREAL, { &bon_lo }, "Min. fract. of sum of VdW radii for bonded atoms" },
765 { "-bonhi", FALSE, etREAL, { &bon_hi }, "Max. fract. of sum of VdW radii for bonded atoms" },
766 { "-rmsd", FALSE, etBOOL, { &bRMSD }, "Print RMSD for x, v and f" },
771 "Relative tolerance for comparing real values defined as "
772 "[MATH]2*(a-b)/([MAG]a[mag]+[MAG]b[mag])[math]" },
777 "Absolute tolerance, useful when sums are close to zero." },
778 { "-ab", FALSE, etBOOL, { &bCompAB }, "Compare the A and B topology from one file" },
783 "Last energy term to compare (if not given all are tested). It makes sense to go up "
784 "until the Pressure." }
787 if (!parse_common_args(&argc, argv, 0, NFILE, fnm, asize(pa), pa, asize(desc), desc, 0, nullptr, &oenv))
792 fn1 = opt2fn_null("-f", NFILE, fnm);
793 fn2 = opt2fn_null("-f2", NFILE, fnm);
794 tex = opt2fn_null("-m", NFILE, fnm);
799 "LaTeX file writing has been removed from gmx check. "
800 "Please use gmx report-methods instead for it.\n");
804 comp_trx(oenv, fn1, fn2, bRMSD, ftol, abstol);
808 chk_trj(oenv, fn1, opt2fn_null("-s1", NFILE, fnm), ftol);
812 fprintf(stderr, "Please give me TWO trajectory (.xtc/.trr/.tng) files!\n");
815 fn1 = opt2fn_null("-s1", NFILE, fnm);
816 fn2 = opt2fn_null("-s2", NFILE, fnm);
817 if ((fn1 && fn2) || bCompAB)
823 gmx_fatal(FARGS, "With -ab you need to set the -s1 option");
828 fprintf(stderr, "Note: When comparing run input files, default tolerances are reduced.\n");
829 if (!opt2parg_bSet("-tol", asize(pa), pa))
833 if (!opt2parg_bSet("-abstol", asize(pa), pa))
837 comp_tpx(fn1, fn2, bRMSD, ftol, abstol);
839 else if ((fn1 && !opt2fn_null("-f", NFILE, fnm)) || (!fn1 && fn2))
841 fprintf(stderr, "Please give me TWO run input (.tpr) files\n");
844 fn1 = opt2fn_null("-e", NFILE, fnm);
845 fn2 = opt2fn_null("-e2", NFILE, fnm);
848 comp_enx(fn1, fn2, ftol, abstol, lastener);
852 chk_enx(ftp2fn(efEDR, NFILE, fnm));
856 fprintf(stderr, "Please give me TWO energy (.edr/.ene) files!\n");
859 if (ftp2bSet(efTPS, NFILE, fnm))
861 chk_tps(ftp2fn(efTPS, NFILE, fnm), vdw_fac, bon_lo, bon_hi);
864 if (ftp2bSet(efNDX, NFILE, fnm))
866 chk_ndx(ftp2fn(efNDX, NFILE, fnm));