2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
5 * Copyright (c) 2001-2004, The GROMACS development team.
6 * Copyright (c) 2013,2014,2015,2016,2017 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.
48 #include "gromacs/commandline/pargs.h"
49 #include "gromacs/fileio/enxio.h"
50 #include "gromacs/fileio/trxio.h"
51 #include "gromacs/listed_forces/disre.h"
52 #include "gromacs/math/functions.h"
53 #include "gromacs/math/vec.h"
54 #include "gromacs/mdtypes/md_enums.h"
55 #include "gromacs/trajectory/energyframe.h"
56 #include "gromacs/utility/arrayref.h"
57 #include "gromacs/utility/arraysize.h"
58 #include "gromacs/utility/cstringutil.h"
59 #include "gromacs/utility/fatalerror.h"
60 #include "gromacs/utility/int64_to_int.h"
61 #include "gromacs/utility/smalloc.h"
62 #include "gromacs/utility/strconvert.h"
64 #define TIME_EXPLICIT 0
65 #define TIME_CONTINUE 1
71 static int* select_it(int nre, gmx_enxnm_t* nm, int* nset)
76 gmx_bool bVerbose = TRUE;
78 if ((getenv("GMX_ENER_VERBOSE")) != nullptr)
83 fprintf(stderr, "Select the terms you want to scale from the following list\n");
84 fprintf(stderr, "End your selection with 0\n");
88 for (k = 0; (k < nre);)
90 for (j = 0; (j < 4) && (k < nre); j++, k++)
92 fprintf(stderr, " %3d=%14s", k + 1, nm[k].name);
94 fprintf(stderr, "\n");
101 if (1 != scanf("%d", &n))
103 gmx_fatal(FARGS, "Cannot read energy term");
105 if ((n > 0) && (n <= nre))
112 for (i = (*nset) = 0; (i < nre); i++)
125 static void sort_files(gmx::ArrayRef<std::string> files, real* settime)
127 for (gmx::index i = 0; i < files.ssize(); i++)
129 gmx::index minidx = i;
130 for (gmx::index j = i + 1; j < files.ssize(); j++)
132 if (settime[j] < settime[minidx])
139 real timeswap = settime[i];
140 settime[i] = settime[minidx];
141 settime[minidx] = timeswap;
142 std::string tmp = files[i];
143 files[i] = files[minidx];
150 static int scan_ene_files(const std::vector<std::string>& files, real* readtime, real* timestep, int* nremax)
152 /* Check number of energy terms and start time of all files */
153 int nre, nremin = 0, nresav = 0;
156 char inputstring[STRLEN];
162 for (size_t f = 0; f < files.size(); f++)
164 in = open_enx(files[f].c_str(), "r");
166 do_enxnms(in, &nre, &enm);
183 nremin = std::min(nremin, fr->nre);
184 *nremax = std::max(*nremax, fr->nre);
188 "Energy files don't match, different number of energies:\n"
189 " %s: %d\n %s: %d\n",
190 files[f - 1].c_str(), nresav, files[f].c_str(), fr->nre);
192 "\nContinue conversion using only the first %d terms (n/y)?\n"
193 "(you should be sure that the energy terms match)\n",
195 if (nullptr == fgets(inputstring, STRLEN - 1, stdin))
197 gmx_fatal(FARGS, "Error reading user input");
199 if (inputstring[0] != 'y' && inputstring[0] != 'Y')
201 fprintf(stderr, "Will not convert\n");
210 fprintf(stderr, "\n");
211 free_enxnms(nre, enm);
221 static void edit_files(gmx::ArrayRef<std::string> files,
229 char inputstring[STRLEN], *chptr;
233 if (files.size() == 1)
235 fprintf(stderr, "\n\nEnter the new start time:\n\n");
240 "\n\nEnter the new start time for each file.\n"
241 "There are two special options, both disables sorting:\n\n"
242 "c (continue) - The start time is taken from the end\n"
243 "of the previous file. Use it when your continuation run\n"
244 "restarts with t=0 and there is no overlap.\n\n"
245 "l (last) - The time in this file will be changed the\n"
246 "same amount as in the previous. Use it when the time in the\n"
247 "new run continues from the end of the previous one,\n"
248 "since this takes possible overlap into account.\n\n");
252 " File Current start New start\n"
253 "---------------------------------------------------------\n");
255 for (gmx::index i = 0; i < files.ssize(); i++)
257 fprintf(stderr, "%25s %10.3f ", files[i].c_str(), readtime[i]);
261 if (nullptr == fgets(inputstring, STRLEN - 1, stdin))
263 gmx_fatal(FARGS, "Error reading user input");
265 inputstring[std::strlen(inputstring) - 1] = 0;
267 if (inputstring[0] == 'c' || inputstring[0] == 'C')
269 cont_type[i] = TIME_CONTINUE;
272 settime[i] = FLT_MAX;
274 else if (inputstring[0] == 'l' || inputstring[0] == 'L')
276 cont_type[i] = TIME_LAST;
279 settime[i] = FLT_MAX;
283 settime[i] = strtod(inputstring, &chptr);
284 if (chptr == inputstring)
286 fprintf(stderr, "Try that again: ");
290 cont_type[i] = TIME_EXPLICIT;
296 if (cont_type[0] != TIME_EXPLICIT)
298 cont_type[0] = TIME_EXPLICIT;
304 for (gmx::index i = 0; i < files.ssize(); i++)
306 settime[i] = readtime[i];
310 if (bSort && files.size() > 1)
312 sort_files(files, settime);
316 fprintf(stderr, "Sorting disabled.\n");
320 /* Write out the new order and start times */
322 "\nSummary of files and start times used:\n\n"
324 "-----------------------------------------\n");
325 for (gmx::index i = 0; i < files.ssize(); i++)
327 switch (cont_type[i])
330 fprintf(stderr, "%25s %10.3f\n", files[i].c_str(), settime[i]);
333 fprintf(stderr, "%25s Continue from end of last file\n", files[i].c_str());
336 fprintf(stderr, "%25s Change by same amount as last file\n", files[i].c_str());
340 fprintf(stderr, "\n");
342 settime[files.size()] = FLT_MAX;
343 cont_type[files.size()] = TIME_EXPLICIT;
344 readtime[files.size()] = FLT_MAX;
348 static void update_ee_sum(int nre,
349 int64_t* ee_sum_step,
350 int64_t* ee_sum_nsteps,
351 int64_t* ee_sum_nsum,
356 int64_t nsteps, nsum, fr_nsum;
359 nsteps = *ee_sum_nsteps;
372 for (i = 0; i < nre; i++)
374 ee_sum[i].esum = fr->ener[i].e;
380 for (i = 0; i < nre; i++)
382 ee_sum[i].esum = fr->ener[i].esum;
383 ee_sum[i].eav = fr->ener[i].eav;
389 else if (out_step + *ee_sum_nsum - *ee_sum_step == nsteps + fr->nsteps)
393 for (i = 0; i < nre; i++)
395 ee_sum[i].eav += gmx::square(ee_sum[i].esum / nsum
396 - (ee_sum[i].esum + fr->ener[i].e) / (nsum + 1))
398 ee_sum[i].esum += fr->ener[i].e;
403 for (i = 0; i < fr->nre; i++)
405 ee_sum[i].eav += fr->ener[i].eav
406 + gmx::square(ee_sum[i].esum / nsum
407 - (ee_sum[i].esum + fr->ener[i].esum) / (nsum + fr->nsum))
408 * nsum * (nsum + fr->nsum) / static_cast<double>(fr->nsum);
409 ee_sum[i].esum += fr->ener[i].esum;
412 nsteps += fr->nsteps;
419 fprintf(stderr, "\nWARNING: missing energy sums at time %f\n", fr->t);
425 *ee_sum_step = out_step;
426 *ee_sum_nsteps = nsteps;
430 int gmx_eneconv(int argc, char* argv[])
432 const char* desc[] = {
433 "With [IT]multiple files[it] specified for the [TT]-f[tt] option:[PAR]",
434 "Concatenates several energy files in sorted order.",
435 "In the case of double time frames, the one",
436 "in the later file is used. By specifying [TT]-settime[tt] you will be",
437 "asked for the start time of each file. The input files are taken",
438 "from the command line,",
439 "such that the command [TT]gmx eneconv -f *.edr -o fixed.edr[tt] should do",
441 "With [IT]one file[it] specified for [TT]-f[tt]:[PAR]",
442 "Reads one energy file and writes another, applying the [TT]-dt[tt],",
443 "[TT]-offset[tt], [TT]-t0[tt] and [TT]-settime[tt] options and",
444 "converting to a different format if necessary (indicated by file",
446 "[TT]-settime[tt] is applied first, then [TT]-dt[tt]/[TT]-offset[tt]",
447 "followed by [TT]-b[tt] and [TT]-e[tt] to select which frames to write."
449 const char* bugs[] = {
450 "When combining trajectories the sigma and E^2 (necessary for statistics) are not "
451 "updated correctly. Only the actual energy is correct. One thus has to compute "
452 "statistics in another way."
454 ener_file_t in = nullptr, out = nullptr;
455 gmx_enxnm_t* enm = nullptr;
457 ener_file_t in, out = NULL;
458 gmx_enxnm_t *enm = NULL;
460 t_enxframe * fr, *fro;
461 int64_t ee_sum_step = 0, ee_sum_nsteps, ee_sum_nsum;
463 int64_t lastfilestep, laststep, startstep_file = 0;
465 int nre, nremax, this_nre, i, kkk, nset, *set = nullptr;
467 real * readtime, *settime, timestep, tadjust;
468 char buf[22], buf2[22];
470 gmx_bool bNewFile, bFirst, bNewOutput;
471 gmx_output_env_t* oenv;
472 gmx_bool warned_about_dh = FALSE;
473 t_enxblock* blocks = nullptr;
475 int nblocks_alloc = 0;
478 { efEDR, "-f", nullptr, ffRDMULT },
479 { efEDR, "-o", "fixed", ffWRITE },
482 #define NFILE asize(fnm)
484 static real delta_t = 0.0, toffset = 0, scalefac = 1;
485 static gmx_bool bSetTime = FALSE;
486 static gmx_bool bSort = TRUE, bError = TRUE;
487 static real begin = -1;
488 static real end = -1;
489 gmx_bool remove_dh = FALSE;
492 { "-b", FALSE, etREAL, { &begin }, "First time to use" },
493 { "-e", FALSE, etREAL, { &end }, "Last time to use" },
494 { "-dt", FALSE, etREAL, { &delta_t }, "Only write out frame when t MOD dt = offset" },
495 { "-offset", FALSE, etREAL, { &toffset }, "Time offset for [TT]-dt[tt] option" },
496 { "-settime", FALSE, etBOOL, { &bSetTime }, "Change starting time interactively" },
497 { "-sort", FALSE, etBOOL, { &bSort }, "Sort energy files (not frames)" },
498 { "-rmdh", FALSE, etBOOL, { &remove_dh }, "Remove free energy block data" },
499 { "-scalefac", FALSE, etREAL, { &scalefac }, "Multiply energy component by this factor" },
500 { "-error", FALSE, etBOOL, { &bError }, "Stop on errors in the file" }
503 if (!parse_common_args(&argc, argv, 0, NFILE, fnm, asize(pa), pa, asize(desc), desc,
504 asize(bugs), bugs, &oenv))
509 "Note that major changes are planned in future for "
510 "eneconv, to improve usability and utility.");
519 auto files = gmx::copyOf(opt2fns("-f", NFILE, fnm));
523 gmx_fatal(FARGS, "No input files!");
526 snew(settime, files.size() + 1);
527 snew(readtime, files.size() + 1);
528 snew(cont_type, files.size() + 1);
530 nre = scan_ene_files(files, readtime, ×tep, &nremax);
531 edit_files(files, readtime, settime, cont_type, bSetTime, bSort);
535 snew(ee_sum, nremax);
541 snew(fro->ener, nremax);
547 for (size_t f = 0; f < files.size(); f++)
551 in = open_enx(files[f].c_str(), "r");
553 do_enxnms(in, &this_nre, &enm);
558 set = select_it(nre, enm, &nset);
561 /* write names to the output file */
562 out = open_enx(opt2fn("-o", NFILE, fnm), "w");
563 do_enxnms(out, &nre, &enm);
566 /* start reading from the next file */
567 while ((fro->t <= (settime[f + 1] + GMX_REAL_EPS)) && do_enx(in, fr))
571 startstep_file = fr->step;
572 tadjust = settime[f] - fr->t;
573 if (cont_type[f + 1] == TIME_LAST)
575 settime[f + 1] = readtime[f + 1] - readtime[f] + settime[f];
576 cont_type[f + 1] = TIME_EXPLICIT;
581 if (tadjust + fr->t <= last_t)
583 /* Skip this frame, since we already have it / past it */
586 fprintf(debug, "fr->step %s, fr->t %.4f\n", gmx_step_str(fr->step, buf), fr->t);
587 fprintf(debug, "tadjust %12.6e + fr->t %12.6e <= t %12.6e\n", tadjust, fr->t, last_t);
592 fro->step = lastfilestep + fr->step - startstep_file;
593 fro->t = tadjust + fr->t;
595 bWrite = ((begin < 0 || (fro->t >= begin - GMX_REAL_EPS))
596 && (end < 0 || (fro->t <= end + GMX_REAL_EPS))
597 && (fro->t <= settime[f + 1] + 0.5 * timestep));
601 fprintf(debug, "fr->step %s, fr->t %.4f, fro->step %s fro->t %.4f, w %s\n",
602 gmx_step_str(fr->step, buf), fr->t, gmx_step_str(fro->step, buf2), fro->t,
603 gmx::boolToString(bWrite));
608 if ((end > 0) && (fro->t > end + GMX_REAL_EPS))
615 if (fro->t >= begin - GMX_REAL_EPS)
623 update_ee_sum(nre, &ee_sum_step, &ee_sum_nsteps, &ee_sum_nsum, ee_sum, fr, fro->step);
627 /* determine if we should write it */
628 if (bWrite && (delta_t == 0 || bRmod(fro->t, toffset, delta_t)))
630 laststep = fro->step;
635 fprintf(stderr, "\nContinue writing frames from t=%g, step=%s\n", fro->t,
636 gmx_step_str(fro->step, buf));
639 /* Copy the energies */
640 for (i = 0; i < nre; i++)
642 fro->ener[i].e = fr->ener[i].e;
645 fro->nsteps = ee_sum_nsteps;
648 if (ee_sum_nsum <= 1)
654 fro->nsum = int64_to_int(ee_sum_nsum, "energy average summation");
655 /* Copy the energy sums */
656 for (i = 0; i < nre; i++)
658 fro->ener[i].esum = ee_sum[i].esum;
659 fro->ener[i].eav = ee_sum[i].eav;
662 /* We wrote the energies, so reset the counts */
668 for (kkk = 0; kkk < nset; kkk++)
670 fro->ener[set[kkk]].e *= scalefac;
673 fro->ener[set[kkk]].eav *= scalefac * scalefac;
674 fro->ener[set[kkk]].esum *= scalefac;
678 /* Copy restraint stuff */
679 /*fro->ndisre = fr->ndisre;
680 fro->disre_rm3tav = fr->disre_rm3tav;
681 fro->disre_rt = fr->disre_rt;*/
682 fro->nblock = fr->nblock;
683 /*fro->nr = fr->nr;*/
684 fro->block = fr->block;
686 /* check if we have blocks with delta_h data and are throwing
693 if (!blocks || nblocks_alloc < fr->nblock)
695 /* we pre-allocate the blocks */
696 nblocks_alloc = fr->nblock;
697 snew(blocks, nblocks_alloc);
699 nblocks = 0; /* number of blocks so far */
701 for (i = 0; i < fr->nblock; i++)
703 if ((fr->block[i].id != enxDHCOLL) && (fr->block[i].id != enxDH)
704 && (fr->block[i].id != enxDHHIST))
706 /* copy everything verbatim */
707 blocks[nblocks] = fr->block[i];
711 /* now set the block pointer to the new blocks */
712 fro->nblock = nblocks;
715 else if (delta_t > 0)
717 if (!warned_about_dh)
719 for (i = 0; i < fr->nblock; i++)
721 if (fr->block[i].id == enxDH || fr->block[i].id == enxDHHIST)
724 if (fr->block[i].id == enxDH)
726 size = fr->block[i].sub[2].nr;
734 printf("\nWARNING: %s contains delta H blocks or "
735 "histograms for which\n"
736 " some data is thrown away on a "
737 "block-by-block basis, where each block\n"
738 " contains up to %d samples.\n"
739 " This is almost certainly not what you "
741 " Use the -rmdh option to throw all delta H "
743 " Use g_energy -odh option to extract these "
745 files[f].c_str(), size);
746 warned_about_dh = TRUE;
756 if (noutfr % 1000 == 0)
758 fprintf(stderr, "Writing frame time %g ", fro->t);
763 if (f == files.size())
767 printf("\nLast step written from %s: t %g, step %s\n", files[f].c_str(), last_t,
768 gmx_step_str(laststep, buf));
769 lastfilestep = laststep;
771 /* set the next time from the last in previous file */
772 if (cont_type[f + 1] == TIME_CONTINUE)
774 settime[f + 1] = fro->t;
775 /* in this case we have already written the last frame of
776 * previous file, so update begin to avoid doubling it
777 * with the start of the next file
779 begin = fro->t + 0.5 * timestep;
780 /* cont_type[f+1]==TIME_EXPLICIT; */
783 if ((fro->t < end) && (f < files.size() - 1) && (fro->t < settime[f + 1] - 1.5 * timestep))
785 fprintf(stderr, "\nWARNING: There might be a gap around t=%g\n", fro->t);
788 /* move energies to lastee */
790 free_enxnms(this_nre, enm);
792 fprintf(stderr, "\n");
796 fprintf(stderr, "No frames written.\n");
800 fprintf(stderr, "Last frame written was at step %s, time %f\n",
801 gmx_step_str(fro->step, buf), fro->t);
802 fprintf(stderr, "Wrote %d frames\n", noutfr);