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);
135 static void comp_trx(const gmx_output_env_t* oenv, const char* fn1, const char* fn2, gmx_bool bRMSD, real ftol, real abstol)
140 t_trxstatus* status[2];
145 fprintf(stderr, "Comparing trajectory files %s and %s\n", fn1, fn2);
146 for (i = 0; i < 2; i++)
148 b[i] = read_first_frame(oenv, &status[i], fn[i], &fr[i], TRX_READ_X | TRX_READ_V | TRX_READ_F);
155 comp_frame(stdout, &(fr[0]), &(fr[1]), bRMSD, ftol, abstol);
157 for (i = 0; i < 2; i++)
159 b[i] = read_next_frame(oenv, status[i], &fr[i]);
161 } while (b[0] && b[1]);
163 for (i = 0; i < 2; i++)
165 if (b[i] && !b[1 - i])
167 fprintf(stdout, "\nEnd of file on %s but not on %s\n", fn[1 - i], fn[i]);
169 close_trx(status[i]);
174 fprintf(stdout, "\nBoth files read correctly\n");
178 static void chk_coords(int frame, int natoms, rvec* x, matrix box, real fac, real tol)
184 for (i = 0; (i < natoms); i++)
186 for (j = 0; (j < DIM); j++)
188 if ((vol > 0) && (fabs(x[i][j]) > fac * box[j][j]))
190 printf("Warning at frame %d: coordinates for atom %d are large (%g)\n", frame, i, x[i][j]);
193 if ((fabs(x[i][XX]) < tol) && (fabs(x[i][YY]) < tol) && (fabs(x[i][ZZ]) < tol))
200 printf("Warning at frame %d: there are %d particles with all coordinates zero\n", frame, nNul);
204 static void chk_vels(int frame, int natoms, rvec* v)
208 for (i = 0; (i < natoms); i++)
210 for (j = 0; (j < DIM); j++)
212 if (fabs(v[i][j]) > 500)
214 printf("Warning at frame %d. Velocities for atom %d are large (%g)\n", frame, i, v[i][j]);
220 static void chk_forces(int frame, int natoms, rvec* f)
224 for (i = 0; (i < natoms); i++)
226 for (j = 0; (j < DIM); j++)
228 if (fabs(f[i][j]) > 10000)
230 printf("Warning at frame %d. Forces for atom %d are large (%g)\n", frame, i, f[i][j]);
236 static void chk_bonds(const InteractionDefinitions* idef, PbcType pbcType, rvec* x, matrix box, real tol)
238 int ftype, k, ai, aj, type;
239 real b0, blen, deviation;
243 gmx::ArrayRef<const t_iparams> iparams = idef->iparams;
245 set_pbc(&pbc, pbcType, box);
246 for (ftype = 0; (ftype < F_NRE); ftype++)
248 if ((interaction_function[ftype].flags & IF_CHEMBOND) == IF_CHEMBOND)
250 for (k = 0; (k < idef->il[ftype].size());)
252 type = idef->il[ftype].iatoms[k++];
253 ai = idef->il[ftype].iatoms[k++];
254 aj = idef->il[ftype].iatoms[k++];
258 case F_BONDS: b0 = iparams[type].harmonic.rA; break;
259 case F_G96BONDS: b0 = std::sqrt(iparams[type].harmonic.rA); break;
260 case F_MORSE: b0 = iparams[type].morse.b0A; break;
261 case F_CUBICBONDS: b0 = iparams[type].cubic.b0; break;
262 case F_CONSTR: b0 = iparams[type].constr.dA; break;
267 pbc_dx(&pbc, x[ai], x[aj], dx);
269 deviation = gmx::square(blen - b0);
270 if (std::sqrt(deviation / gmx::square(b0)) > tol)
272 fprintf(stderr, "Distance between atoms %d and %d is %.3f, should be %.3f\n",
273 ai + 1, aj + 1, blen, b0);
281 static void chk_trj(const gmx_output_env_t* oenv, const char* fn, const char* tpr, real tol)
285 t_fr_time first, last;
286 int j = -1, new_natoms, natoms;
288 gmx_bool bShowTimestep = TRUE, newline = FALSE;
294 std::unique_ptr<gmx_localtop_t> top;
297 read_tpx_state(tpr, &ir, &state, &mtop);
298 top = std::make_unique<gmx_localtop_t>(mtop.ffparams);
299 gmx_mtop_generate_local_top(mtop, top.get(), ir.efep != efepNO);
304 printf("Checking file %s\n", fn);
334 read_first_frame(oenv, &status, fn, &fr, TRX_READ_X | TRX_READ_V | TRX_READ_F);
340 fprintf(stderr, "\n# Atoms %d\n", fr.natoms);
343 fprintf(stderr, "Precision %g (nm)\n", 1 / fr.prec);
347 if ((natoms > 0) && (new_natoms != natoms))
349 fprintf(stderr, "\nNumber of atoms at t=%g don't match (%d, %d)\n", old_t1, natoms, new_natoms);
354 if (std::fabs((fr.time - old_t1) - (old_t1 - old_t2))
355 > 0.1 * (std::fabs(fr.time - old_t1) + std::fabs(old_t1 - old_t2)))
357 bShowTimestep = FALSE;
358 fprintf(stderr, "%sTimesteps at t=%g don't match (%g, %g)\n", newline ? "\n" : "",
359 old_t1, old_t1 - old_t2, fr.time - old_t1);
365 chk_bonds(&top->idef, ir.pbcType, fr.x, fr.box, tol);
369 chk_coords(j, natoms, fr.x, fr.box, 1e5, tol);
373 chk_vels(j, natoms, fr.v);
377 chk_forces(j, natoms, fr.f);
383 new_natoms = fr.natoms;
384 #define INC(s, n, f, l, item) \
389 first.item = fr.time; \
391 last.item = fr.time; \
394 INC(fr, count, first, last, bStep)
395 INC(fr, count, first, last, bTime)
396 INC(fr, count, first, last, bLambda)
397 INC(fr, count, first, last, bX)
398 INC(fr, count, first, last, bV)
399 INC(fr, count, first, last, bF)
400 INC(fr, count, first, last, bBox)
402 } while (read_next_frame(oenv, status, &fr));
404 fprintf(stderr, "\n");
408 fprintf(stderr, "\nItem #frames");
411 fprintf(stderr, " Timestep (ps)");
413 fprintf(stderr, "\n");
414 #define PRINTITEM(label, item) \
415 fprintf(stderr, "%-10s %6d", label, count.item); \
416 if ((bShowTimestep) && (count.item > 1)) \
418 fprintf(stderr, " %g\n", (last.item - first.item) / (count.item - 1)); \
421 fprintf(stderr, "\n")
422 PRINTITEM("Step", bStep);
423 PRINTITEM("Time", bTime);
424 PRINTITEM("Lambda", bLambda);
425 PRINTITEM("Coords", bX);
426 PRINTITEM("Velocities", bV);
427 PRINTITEM("Forces", bF);
428 PRINTITEM("Box", bBox);
431 static void chk_tps(const char* fn, real vdw_fac, real bon_lo, real bon_hi)
441 gmx_bool bV, bX, bB, bFirst, bOut;
442 real r2, ekin, temp1, temp2, dist2, vdwfac2, bonlo2, bonhi2;
445 fprintf(stderr, "Checking coordinate file %s\n", fn);
446 read_tps_conf(fn, &top, &pbcType, &x, &v, box, TRUE);
449 fprintf(stderr, "%d atoms in file\n", atoms->nr);
451 /* check coordinates and box */
454 for (i = 0; (i < natom) && !(bV && bX); i++)
456 for (j = 0; (j < DIM) && !(bV && bX); j++)
458 bV = bV || (v[i][j] != 0);
459 bX = bX || (x[i][j] != 0);
463 for (i = 0; (i < DIM) && !bB; i++)
465 for (j = 0; (j < DIM) && !bB; j++)
467 bB = bB || (box[i][j] != 0);
471 fprintf(stderr, "coordinates %s\n", bX ? "found" : "absent");
472 fprintf(stderr, "box %s\n", bB ? "found" : "absent");
473 fprintf(stderr, "velocities %s\n", bV ? "found" : "absent");
474 fprintf(stderr, "\n");
476 /* check velocities */
480 for (i = 0; (i < natom); i++)
482 for (j = 0; (j < DIM); j++)
484 ekin += 0.5 * atoms->atom[i].m * v[i][j] * v[i][j];
487 temp1 = (2.0 * ekin) / (natom * DIM * BOLTZ);
488 temp2 = (2.0 * ekin) / (natom * (DIM - 1) * BOLTZ);
489 fprintf(stderr, "Kinetic energy: %g (kJ/mol)\n", ekin);
491 "Assuming the number of degrees of freedom to be "
492 "Natoms * %d or Natoms * %d,\n"
493 "the velocities correspond to a temperature of the system\n"
494 "of %g K or %g K respectively.\n\n",
495 DIM, DIM - 1, temp1, temp2);
498 /* check coordinates */
501 vdwfac2 = gmx::square(vdw_fac);
502 bonlo2 = gmx::square(bon_lo);
503 bonhi2 = gmx::square(bon_hi);
506 "Checking for atoms closer than %g and not between %g and %g,\n"
507 "relative to sum of Van der Waals distance:\n",
508 vdw_fac, bon_lo, bon_hi);
509 snew(atom_vdw, natom);
511 for (i = 0; (i < natom); i++)
513 aps.setAtomProperty(epropVDW, *(atoms->resinfo[atoms->atom[i].resind].name),
514 *(atoms->atomname[i]), &(atom_vdw[i]));
517 fprintf(debug, "%5d %4s %4s %7g\n", i + 1, *(atoms->resinfo[atoms->atom[i].resind].name),
518 *(atoms->atomname[i]), atom_vdw[i]);
523 set_pbc(&pbc, pbcType, box);
527 for (i = 0; (i < natom); i++)
529 if (((i + 1) % 10) == 0)
531 fprintf(stderr, "\r%5d", i + 1);
534 for (j = i + 1; (j < natom); j++)
538 pbc_dx(&pbc, x[i], x[j], dx);
542 rvec_sub(x[i], x[j], dx);
545 dist2 = gmx::square(atom_vdw[i] + atom_vdw[j]);
546 if ((r2 <= dist2 * bonlo2) || ((r2 >= dist2 * bonhi2) && (r2 <= dist2 * vdwfac2)))
550 fprintf(stderr, "\r%5s %4s %8s %5s %5s %4s %8s %5s %6s\n", "atom#", "name",
551 "residue", "r_vdw", "atom#", "name", "residue", "r_vdw", "distance");
554 fprintf(stderr, "\r%5d %4s %4s%4d %-5.3g %5d %4s %4s%4d %-5.3g %-6.4g\n", i + 1,
555 *(atoms->atomname[i]), *(atoms->resinfo[atoms->atom[i].resind].name),
556 atoms->resinfo[atoms->atom[i].resind].nr, atom_vdw[i], j + 1,
557 *(atoms->atomname[j]), *(atoms->resinfo[atoms->atom[j].resind].name),
558 atoms->resinfo[atoms->atom[j].resind].nr, atom_vdw[j], std::sqrt(r2));
564 fprintf(stderr, "\rno close atoms found\n");
566 fprintf(stderr, "\r \n");
573 for (i = 0; (i < natom) && (k < 10); i++)
576 for (j = 0; (j < DIM) && !bOut; j++)
578 bOut = bOut || (x[i][j] < 0) || (x[i][j] > box[j][j]);
585 fprintf(stderr, "Atoms outside box ( ");
586 for (j = 0; (j < DIM); j++)
588 fprintf(stderr, "%g ", box[j][j]);
592 "(These may occur often and are normally not a problem)\n"
593 "%5s %4s %8s %5s %s\n",
594 "atom#", "name", "residue", "r_vdw", "coordinate");
597 fprintf(stderr, "%5d %4s %4s%4d %-5.3g", i, *(atoms->atomname[i]),
598 *(atoms->resinfo[atoms->atom[i].resind].name),
599 atoms->resinfo[atoms->atom[i].resind].nr, atom_vdw[i]);
600 for (j = 0; (j < DIM); j++)
602 fprintf(stderr, " %6.3g", x[i][j]);
604 fprintf(stderr, "\n");
609 fprintf(stderr, "(maybe more)\n");
613 fprintf(stderr, "no atoms found outside box\n");
615 fprintf(stderr, "\n");
620 static void chk_ndx(const char* fn)
626 grps = init_index(fn, &grpname);
629 pr_blocka(debug, 0, fn, grps, FALSE);
633 printf("Contents of index file %s\n", fn);
634 printf("--------------------------------------------------\n");
635 printf("Nr. Group #Entries First Last\n");
636 for (i = 0; (i < grps->nr); i++)
638 printf("%4d %-20s%8d%8d%8d\n", i, grpname[i], grps->index[i + 1] - grps->index[i],
639 grps->a[grps->index[i]] + 1, grps->a[grps->index[i + 1] - 1] + 1);
642 for (i = 0; (i < grps->nr); i++)
650 static void chk_enx(const char* fn)
654 gmx_enxnm_t* enm = nullptr;
658 real t0, old_t1, old_t2;
661 fprintf(stderr, "Checking energy file %s\n\n", fn);
663 in = open_enx(fn, "r");
664 do_enxnms(in, &nre, &enm);
665 fprintf(stderr, "%d groups in energy file", nre);
674 while (do_enx(in, fr))
678 if (fabs((fr->t - old_t1) - (old_t1 - old_t2))
679 > 0.1 * (fabs(fr->t - old_t1) + std::fabs(old_t1 - old_t2)))
682 fprintf(stderr, "\nTimesteps at t=%g don't match (%g, %g)\n", old_t1,
683 old_t1 - old_t2, fr->t - old_t1);
695 fprintf(stderr, "\rframe: %6s (index %6d), t: %10.3f\n", gmx_step_str(fr->step, buf),
700 fprintf(stderr, "\n\nFound %d frames", fnr);
701 if (bShowTStep && fnr > 1)
703 fprintf(stderr, " with a timestep of %g ps", (old_t1 - t0) / (fnr - 1));
705 fprintf(stderr, ".\n");
708 free_enxnms(nre, enm);
712 int gmx_check(int argc, char* argv[])
714 const char* desc[] = {
715 "[THISMODULE] reads a trajectory ([REF].tng[ref], [REF].trr[ref] or ",
716 "[REF].xtc[ref]), an energy file ([REF].edr[ref])",
717 "or an index file ([REF].ndx[ref])",
718 "and prints out useful information about them.[PAR]",
719 "Option [TT]-c[tt] checks for presence of coordinates,",
720 "velocities and box in the file, for close contacts (smaller than",
721 "[TT]-vdwfac[tt] and not bonded, i.e. not between [TT]-bonlo[tt]",
722 "and [TT]-bonhi[tt], all relative to the sum of both Van der Waals",
723 "radii) and atoms outside the box (these may occur often and are",
724 "no problem). If velocities are present, an estimated temperature",
725 "will be calculated from them.[PAR]",
726 "If an index file, is given its contents will be summarized.[PAR]",
727 "If both a trajectory and a [REF].tpr[ref] file are given (with [TT]-s1[tt])",
728 "the program will check whether the bond lengths defined in the tpr",
729 "file are indeed correct in the trajectory. If not you may have",
730 "non-matching files due to e.g. deshuffling or due to problems with",
731 "virtual sites. With these flags, [TT]gmx check[tt] provides a quick check for ",
732 "such problems.[PAR]",
733 "The program can compare two run input ([REF].tpr[ref])",
735 "when both [TT]-s1[tt] and [TT]-s2[tt] are supplied. When comparing",
736 "run input files this way, the default relative tolerance is reduced",
737 "to 0.000001 and the absolute tolerance set to zero to find any differences",
738 "not due to minor compiler optimization differences, although you can",
739 "of course still set any other tolerances through the options.",
740 "Similarly a pair of trajectory files can be compared (using the [TT]-f2[tt]",
741 "option), or a pair of energy files (using the [TT]-e2[tt] option).[PAR]",
742 "For free energy simulations the A and B state topology from one",
743 "run input file can be compared with options [TT]-s1[tt] and [TT]-ab[tt].[PAR]"
745 t_filenm fnm[] = { { efTRX, "-f", nullptr, ffOPTRD }, { efTRX, "-f2", nullptr, ffOPTRD },
746 { efTPR, "-s1", "top1", ffOPTRD }, { efTPR, "-s2", "top2", ffOPTRD },
747 { efTPS, "-c", nullptr, ffOPTRD }, { efEDR, "-e", nullptr, ffOPTRD },
748 { efEDR, "-e2", "ener2", ffOPTRD }, { efNDX, "-n", nullptr, ffOPTRD },
749 { efTEX, "-m", nullptr, ffOPTWR } };
750 #define NFILE asize(fnm)
751 const char *fn1 = nullptr, *fn2 = nullptr, *tex = nullptr;
753 gmx_output_env_t* oenv;
754 static real vdw_fac = 0.8;
755 static real bon_lo = 0.4;
756 static real bon_hi = 0.7;
757 static gmx_bool bRMSD = FALSE;
758 static real ftol = 0.001;
759 static real abstol = 0.001;
760 static gmx_bool bCompAB = FALSE;
761 static char* lastener = nullptr;
762 static t_pargs pa[] = {
767 "Fraction of sum of VdW radii used as warning cutoff" },
768 { "-bonlo", FALSE, etREAL, { &bon_lo }, "Min. fract. of sum of VdW radii for bonded atoms" },
769 { "-bonhi", FALSE, etREAL, { &bon_hi }, "Max. fract. of sum of VdW radii for bonded atoms" },
770 { "-rmsd", FALSE, etBOOL, { &bRMSD }, "Print RMSD for x, v and f" },
775 "Relative tolerance for comparing real values defined as "
776 "[MATH]2*(a-b)/([MAG]a[mag]+[MAG]b[mag])[math]" },
781 "Absolute tolerance, useful when sums are close to zero." },
782 { "-ab", FALSE, etBOOL, { &bCompAB }, "Compare the A and B topology from one file" },
787 "Last energy term to compare (if not given all are tested). It makes sense to go up "
788 "until the Pressure." }
791 if (!parse_common_args(&argc, argv, 0, NFILE, fnm, asize(pa), pa, asize(desc), desc, 0, nullptr, &oenv))
796 fn1 = opt2fn_null("-f", NFILE, fnm);
797 fn2 = opt2fn_null("-f2", NFILE, fnm);
798 tex = opt2fn_null("-m", NFILE, fnm);
803 "LaTeX file writing has been removed from gmx check. "
804 "Please use gmx report-methods instead for it.\n");
808 comp_trx(oenv, fn1, fn2, bRMSD, ftol, abstol);
812 chk_trj(oenv, fn1, opt2fn_null("-s1", NFILE, fnm), ftol);
816 fprintf(stderr, "Please give me TWO trajectory (.xtc/.trr/.tng) files!\n");
819 fn1 = opt2fn_null("-s1", NFILE, fnm);
820 fn2 = opt2fn_null("-s2", NFILE, fnm);
821 if ((fn1 && fn2) || bCompAB)
827 gmx_fatal(FARGS, "With -ab you need to set the -s1 option");
832 fprintf(stderr, "Note: When comparing run input files, default tolerances are reduced.\n");
833 if (!opt2parg_bSet("-tol", asize(pa), pa))
837 if (!opt2parg_bSet("-abstol", asize(pa), pa))
841 comp_tpx(fn1, fn2, bRMSD, ftol, abstol);
843 else if ((fn1 && !opt2fn_null("-f", NFILE, fnm)) || (!fn1 && fn2))
845 fprintf(stderr, "Please give me TWO run input (.tpr) files\n");
848 fn1 = opt2fn_null("-e", NFILE, fnm);
849 fn2 = opt2fn_null("-e2", NFILE, fnm);
852 comp_enx(fn1, fn2, ftol, abstol, lastener);
856 chk_enx(ftp2fn(efEDR, NFILE, fnm));
860 fprintf(stderr, "Please give me TWO energy (.edr/.ene) files!\n");
863 if (ftp2bSet(efTPS, NFILE, fnm))
865 chk_tps(ftp2fn(efTPS, NFILE, fnm), vdw_fac, bon_lo, bon_hi);
868 if (ftp2bSet(efNDX, NFILE, fnm))
870 chk_ndx(ftp2fn(efNDX, NFILE, fnm));