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.
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
;
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
,
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
);
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
++)
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
,
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
,
224 pull_print_coord_dr(out
, pull
->params
, pcrd
.params
,
225 pcrd
.spatialData
, pcrd
.value_ref
,
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) */
240 fprintf(out
, "\t%g", pcrdHistory
->dynaX
[m
] / numValuesInSum
);
244 fprintf(out
, "\t%g", pull
->dyna
[c
].x
[m
]);
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
);
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
);
278 fprintf(out
, "\t%g", pull
->group
[pcrd
.params
.group
[g
]].x
[m
]);
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
);
306 for (const pull_coord_work_t
&coord
: pull
->coord
)
308 fprintf(out
, "\t%g", coord
.scalarForce
);
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
])
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
);
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
);
387 static FILE *open_pull_out(const char *fn
, struct pull_t
*pull
,
388 const gmx_output_env_t
*oenv
,
390 const bool restartWithAppending
)
394 char **setname
, buf
[50];
396 if (restartWithAppending
)
398 fp
= gmx_fio_fopen(fn
, "a+");
402 fp
= gmx_fio_fopen(fn
, "w+");
405 sprintf(buf
, "Position (nm%s)", pull
->bAngle
? ", deg" : "");
406 if (pull
->bXOutAverage
)
408 xvgr_header(fp
, "Pull Average COM", "Time (ps)", buf
,
413 xvgr_header(fp
, "Pull COM", "Time (ps)", buf
,
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
,
427 xvgr_header(fp
, "Pull force", "Time (ps)", buf
,
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
));
440 for (size_t c
= 0; c
< pull
->coord
.size(); c
++)
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
);
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
);
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
);
483 /* For the pull force we always only use one scalar */
484 sprintf(buf
, "%zu", c
+1);
485 setname
[nsets
] = gmx_strdup(buf
);
491 xvgr_legend(fp
, nsets
, setname
, oenv
);
493 for (int c
= 0; c
< nsets
; c
++)
503 void init_pull_output_files(pull_t
*pull
,
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
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
);
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;
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());
581 pull
->coordForceHistory
= observablesHistory
->pullHistory
.get();