Introduce SimulatorBuilder
[gromacs.git] / src / gromacs / pulling / output.cpp
blob0306e3f9ad3b8d56a36f33f930886489a306e552
1 /*
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,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.
37 #include "gmxpre.h"
39 #include "output.h"
41 #include <cstdio>
43 #include <memory>
45 #include "gromacs/commandline/filenm.h"
46 #include "gromacs/fileio/gmxfio.h"
47 #include "gromacs/fileio/xvgr.h"
48 #include "gromacs/math/vec.h"
49 #include "gromacs/mdrunutility/handlerestart.h"
50 #include "gromacs/mdtypes/mdrunoptions.h"
51 #include "gromacs/mdtypes/observableshistory.h"
52 #include "gromacs/mdtypes/pullhistory.h"
53 #include "gromacs/pulling/pull.h"
54 #include "gromacs/utility/cstringutil.h"
55 #include "gromacs/utility/exceptions.h"
56 #include "gromacs/utility/fatalerror.h"
57 #include "gromacs/utility/gmxassert.h"
59 #include "pull_internal.h"
61 static std::string append_before_extension(const std::string &pathname,
62 const std::string &to_append)
64 /* Appends to_append before last '.' in pathname */
65 size_t extPos = pathname.find_last_of('.');
66 if (extPos == std::string::npos)
68 return pathname + to_append;
70 else
72 return pathname.substr(0, extPos) + to_append +
73 pathname.substr(extPos, std::string::npos);
77 static void addToPullxHistory(pull_t *pull)
79 GMX_RELEASE_ASSERT(pull->coordForceHistory, "Pull history does not exist.");
80 pull->coordForceHistory->numValuesInXSum++;
81 for (size_t c = 0; c < pull->coord.size(); c++)
83 const pull_coord_work_t &pcrd = pull->coord[c];
84 PullCoordinateHistory &pcrdHistory = pull->coordForceHistory->pullCoordinateSums[c];
86 pcrdHistory.value += pcrd.spatialData.value;
87 pcrdHistory.valueRef += pcrd.value_ref;
89 for (int m = 0; m < DIM; m++)
91 pcrdHistory.dr01[m] += pcrd.spatialData.dr01[m];
92 pcrdHistory.dr23[m] += pcrd.spatialData.dr23[m];
93 pcrdHistory.dr45[m] += pcrd.spatialData.dr45[m];
95 if (pcrd.params.eGeom == epullgCYL)
97 for (int m = 0; m < DIM; m++)
99 pcrdHistory.dynaX[m] += pull->dyna[c].x[m];
103 for (size_t g = 0; g < pull->group.size(); g++)
105 PullGroupHistory &pgrpHistory = pull->coordForceHistory->pullGroupSums[g];
106 for (int m = 0; m < DIM; m++)
108 pgrpHistory.x[m] += pull->group[g].x[m];
113 static void addToPullfHistory(pull_t *pull)
115 GMX_RELEASE_ASSERT(pull->coordForceHistory, "Pull history does not exist.");
116 pull->coordForceHistory->numValuesInFSum++;
117 for (size_t c = 0; c < pull->coord.size(); c++)
119 const pull_coord_work_t &pcrd = pull->coord[c];;
120 PullCoordinateHistory &pcrdHistory = pull->coordForceHistory->pullCoordinateSums[c];
122 pcrdHistory.scalarForce += pcrd.scalarForce;
126 static void pullResetHistory(PullHistory *history,
127 bool resetXHistory,
128 bool resetFHistory)
130 if (resetXHistory)
132 history->numValuesInXSum = 0;
134 for (PullCoordinateHistory &pcrdHistory : history->pullCoordinateSums)
136 pcrdHistory.value = 0;
137 pcrdHistory.valueRef = 0;
139 clear_dvec(pcrdHistory.dr01);
140 clear_dvec(pcrdHistory.dr23);
141 clear_dvec(pcrdHistory.dr45);
142 clear_dvec(pcrdHistory.dynaX);
145 for (PullGroupHistory &pgrpHistory : history->pullGroupSums)
147 clear_dvec(pgrpHistory.x);
150 if (resetFHistory)
152 history->numValuesInFSum = 0;
153 for (PullCoordinateHistory &pcrdHistory : history->pullCoordinateSums)
155 pcrdHistory.scalarForce = 0;
160 static void pull_print_coord_dr_components(FILE *out, const ivec dim, const dvec dr,
161 const int numValuesInSum)
163 for (int m = 0; m < DIM; m++)
165 if (dim[m])
167 fprintf(out, "\t%g", dr[m] / numValuesInSum);
172 template <typename T>
173 static void pull_print_coord_dr(FILE *out,
174 const pull_params_t &pullParams,
175 const t_pull_coord &coordParams,
176 const T &pcrdData,
177 double referenceValue,
178 const int numValuesInSum)
180 const double unit_factor = pull_conversion_factor_internal2userinput(&coordParams);
182 fprintf(out, "\t%g", pcrdData.value*unit_factor/numValuesInSum);
184 if (pullParams.bPrintRefValue && coordParams.eType != epullEXTERNAL)
186 fprintf(out, "\t%g", referenceValue*unit_factor/numValuesInSum);
189 if (pullParams.bPrintComp)
191 pull_print_coord_dr_components(out, coordParams.dim, pcrdData.dr01, numValuesInSum);
192 if (coordParams.ngroup >= 4)
194 pull_print_coord_dr_components(out, coordParams.dim, pcrdData.dr23, numValuesInSum);
196 if (coordParams.ngroup >= 6)
198 pull_print_coord_dr_components(out, coordParams.dim, pcrdData.dr45, numValuesInSum);
203 static void pull_print_x(FILE *out, pull_t *pull, double t)
205 fprintf(out, "%.4f", t);
207 for (size_t c = 0; c < pull->coord.size(); c++)
209 const pull_coord_work_t &pcrd = pull->coord[c];
210 int numValuesInSum = 1;
211 const PullCoordinateHistory *pcrdHistory = nullptr;
213 if (pull->bXOutAverage)
215 pcrdHistory = &pull->coordForceHistory->pullCoordinateSums[c];
217 numValuesInSum = pull->coordForceHistory->numValuesInXSum;
218 pull_print_coord_dr(out, pull->params, pcrd.params,
219 *pcrdHistory, pcrdHistory->valueRef,
220 numValuesInSum);
222 else
224 pull_print_coord_dr(out, pull->params, pcrd.params,
225 pcrd.spatialData, pcrd.value_ref,
226 numValuesInSum);
229 if (pull->params.bPrintCOM)
231 if (pcrd.params.eGeom == epullgCYL)
233 for (int m = 0; m < DIM; m++)
235 if (pcrd.params.dim[m])
237 /* This equates to if (pull->bXOutAverage) */
238 if (pcrdHistory)
240 fprintf(out, "\t%g", pcrdHistory->dynaX[m] / numValuesInSum);
242 else
244 fprintf(out, "\t%g", pull->dyna[c].x[m]);
249 else
251 for (int m = 0; m < DIM; m++)
253 if (pcrd.params.dim[m])
255 if (pull->bXOutAverage)
257 fprintf(out, "\t%g", pull->coordForceHistory->pullGroupSums[pcrd.params.group[0]].x[m] / numValuesInSum);
259 else
261 fprintf(out, "\t%g", pull->group[pcrd.params.group[0]].x[m]);
266 for (int g = 1; g < pcrd.params.ngroup; g++)
268 for (int m = 0; m < DIM; m++)
270 if (pcrd.params.dim[m])
272 if (pull->bXOutAverage)
274 fprintf(out, "\t%g", pull->coordForceHistory->pullGroupSums[pcrd.params.group[g]].x[m] / numValuesInSum);
276 else
278 fprintf(out, "\t%g", pull->group[pcrd.params.group[g]].x[m]);
285 fprintf(out, "\n");
287 if (pull->bXOutAverage)
289 pullResetHistory(pull->coordForceHistory, TRUE, FALSE);
293 static void pull_print_f(FILE *out, const pull_t *pull, double t)
295 fprintf(out, "%.4f", t);
297 if (pull->bFOutAverage)
299 for (size_t c = 0; c < pull->coord.size(); c++)
301 fprintf(out, "\t%g", pull->coordForceHistory->pullCoordinateSums[c].scalarForce / pull->coordForceHistory->numValuesInFSum);
304 else
306 for (const pull_coord_work_t &coord : pull->coord)
308 fprintf(out, "\t%g", coord.scalarForce);
311 fprintf(out, "\n");
313 if (pull->bFOutAverage)
315 pullResetHistory(pull->coordForceHistory, FALSE, TRUE);
319 void pull_print_output(struct pull_t *pull, int64_t step, double time)
321 GMX_ASSERT(pull->numExternalPotentialsStillToBeAppliedThisStep == 0, "pull_print_output called before all external pull potentials have been applied");
323 if (pull->params.nstxout != 0)
325 /* Do not update the average if the number of observations already equal (or are
326 * higher than) what should be in each average output. This can happen when
327 * appending to a file from a checkpoint, which would otherwise include the
328 * last value twice.*/
329 if (pull->bXOutAverage && !pull->coord.empty() && pull->coordForceHistory->numValuesInXSum < pull->params.nstxout)
331 addToPullxHistory(pull);
333 if (step % pull->params.nstxout == 0)
335 pull_print_x(pull->out_x, pull, time);
339 if (pull->params.nstfout != 0)
341 /* Do not update the average if the number of observations already equal (or are
342 * higher than) what should be in each average output. This can happen when
343 * appending to a file from a checkpoint, which would otherwise include the
344 * last value twice.*/
345 if (pull->bFOutAverage && !pull->coord.empty() && pull->coordForceHistory->numValuesInFSum < pull->params.nstfout)
347 addToPullfHistory(pull);
349 if (step % pull->params.nstfout == 0)
351 pull_print_f(pull->out_f, pull, time);
356 static void set_legend_for_coord_components(const pull_coord_work_t *pcrd, int coord_index, char **setname, int *nsets_ptr)
358 /* Loop over the distance vectors and print their components. Each vector is made up of two consecutive groups. */
359 for (int g = 0; g < pcrd->params.ngroup; g += 2)
361 /* Loop over the components */
362 for (int m = 0; m < DIM; m++)
364 if (pcrd->params.dim[m])
366 char legend[STRLEN];
368 if (g == 0 && pcrd->params.ngroup <= 2)
370 /* For the simplest case we print a simplified legend without group indices, just the cooordinate index
371 and which dimensional component it is. */
372 sprintf(legend, "%d d%c", coord_index + 1, 'X' + m);
374 else
376 /* Otherwise, print also the group indices (relative to the coordinate, not the global ones). */
377 sprintf(legend, "%d g %d-%d d%c", coord_index + 1, g + 1, g + 2, 'X' + m);
380 setname[*nsets_ptr] = gmx_strdup(legend);
381 (*nsets_ptr)++;
387 static FILE *open_pull_out(const char *fn, struct pull_t *pull,
388 const gmx_output_env_t *oenv,
389 gmx_bool bCoord,
390 const bool restartWithAppending)
392 FILE *fp;
393 int nsets, m;
394 char **setname, buf[50];
396 if (restartWithAppending)
398 fp = gmx_fio_fopen(fn, "a+");
400 else
402 fp = gmx_fio_fopen(fn, "w+");
403 if (bCoord)
405 sprintf(buf, "Position (nm%s)", pull->bAngle ? ", deg" : "");
406 if (pull->bXOutAverage)
408 xvgr_header(fp, "Pull Average COM", "Time (ps)", buf,
409 exvggtXNY, oenv);
411 else
413 xvgr_header(fp, "Pull COM", "Time (ps)", buf,
414 exvggtXNY, oenv);
417 else
419 sprintf(buf, "Force (kJ/mol/nm%s)", pull->bAngle ? ", kJ/mol/rad" : "");
420 if (pull->bFOutAverage)
422 xvgr_header(fp, "Pull Average force", "Time (ps)", buf,
423 exvggtXNY, oenv);
425 else
427 xvgr_header(fp, "Pull force", "Time (ps)", buf,
428 exvggtXNY, oenv);
432 /* With default mdp options only the actual coordinate value is printed (1),
433 * but optionally the reference value (+ 1),
434 * the group COMs for all the groups (+ ngroups_max*DIM)
435 * and the components of the distance vectors can be printed (+ (ngroups_max/2)*DIM).
437 snew(setname, pull->coord.size()*(1 + 1 + c_pullCoordNgroupMax*DIM + c_pullCoordNgroupMax/2*DIM));
439 nsets = 0;
440 for (size_t c = 0; c < pull->coord.size(); c++)
442 if (bCoord)
444 /* The order of this legend should match the order of printing
445 * the data in print_pull_x above.
448 /* The pull coord distance */
449 sprintf(buf, "%zu", c+1);
450 setname[nsets] = gmx_strdup(buf);
451 nsets++;
452 if (pull->params.bPrintRefValue &&
453 pull->coord[c].params.eType != epullEXTERNAL)
455 sprintf(buf, "%zu ref", c+1);
456 setname[nsets] = gmx_strdup(buf);
457 nsets++;
459 if (pull->params.bPrintComp)
461 set_legend_for_coord_components(&pull->coord[c], c, setname, &nsets);
464 if (pull->params.bPrintCOM)
466 for (int g = 0; g < pull->coord[c].params.ngroup; g++)
468 /* Legend for reference group position */
469 for (m = 0; m < DIM; m++)
471 if (pull->coord[c].params.dim[m])
473 sprintf(buf, "%zu g %d %c", c+1, g + 1, 'X'+m);
474 setname[nsets] = gmx_strdup(buf);
475 nsets++;
481 else
483 /* For the pull force we always only use one scalar */
484 sprintf(buf, "%zu", c+1);
485 setname[nsets] = gmx_strdup(buf);
486 nsets++;
489 if (nsets > 1)
491 xvgr_legend(fp, nsets, setname, oenv);
493 for (int c = 0; c < nsets; c++)
495 sfree(setname[c]);
497 sfree(setname);
500 return fp;
503 void init_pull_output_files(pull_t *pull,
504 int nfile,
505 const t_filenm fnm[],
506 const gmx_output_env_t *oenv,
507 const gmx::StartingBehavior startingBehavior)
509 /* Check for px and pf filename collision, if we are writing
510 both files */
511 std::string px_filename, pf_filename;
512 std::string px_appended, pf_appended;
515 px_filename = std::string(opt2fn("-px", nfile, fnm));
516 pf_filename = std::string(opt2fn("-pf", nfile, fnm));
518 GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR;
520 bool restartWithAppending = startingBehavior == gmx::StartingBehavior::RestartWithAppending;
521 if ((pull->params.nstxout != 0) &&
522 (pull->params.nstfout != 0) &&
523 (px_filename == pf_filename))
525 if (!opt2bSet("-px", nfile, fnm) && !opt2bSet("-pf", nfile, fnm))
527 /* We are writing both pull files but neither set directly. */
530 px_appended = append_before_extension(px_filename, "_pullx");
531 pf_appended = append_before_extension(pf_filename, "_pullf");
533 GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR;
534 pull->out_x = open_pull_out(px_appended.c_str(), pull, oenv,
535 TRUE, restartWithAppending);
536 pull->out_f = open_pull_out(pf_appended.c_str(), pull, oenv,
537 FALSE, restartWithAppending);
538 return;
540 else
542 /* If at least one of -px and -pf is set but the filenames are identical: */
543 gmx_fatal(FARGS, "Identical pull_x and pull_f output filenames %s",
544 px_filename.c_str());
547 if (pull->params.nstxout != 0)
549 pull->out_x = open_pull_out(opt2fn("-px", nfile, fnm), pull, oenv,
550 TRUE, restartWithAppending);
552 if (pull->params.nstfout != 0)
554 pull->out_f = open_pull_out(opt2fn("-pf", nfile, fnm), pull, oenv,
555 FALSE, restartWithAppending);
559 void initPullHistory(pull_t *pull,
560 ObservablesHistory *observablesHistory)
562 GMX_RELEASE_ASSERT(pull, "Need a valid pull object");
564 if (observablesHistory == nullptr)
566 pull->coordForceHistory = nullptr;
567 return;
569 /* If pull->coordForceHistory is already set we are starting from a checkpoint. Do not reset it. */
570 if (observablesHistory->pullHistory == nullptr)
572 observablesHistory->pullHistory = std::make_unique<PullHistory>();
573 pull->coordForceHistory = observablesHistory->pullHistory.get();
574 pull->coordForceHistory->numValuesInXSum = 0;
575 pull->coordForceHistory->numValuesInFSum = 0;
576 pull->coordForceHistory->pullCoordinateSums.resize(pull->coord.size());
577 pull->coordForceHistory->pullGroupSums.resize(pull->group.size());
579 else
581 pull->coordForceHistory = observablesHistory->pullHistory.get();