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, 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.
43 #include "gromacs/commandline/filenm.h"
44 #include "gromacs/fileio/gmxfio.h"
45 #include "gromacs/fileio/xvgr.h"
46 #include "gromacs/pulling/pull.h"
47 #include "gromacs/utility/cstringutil.h"
48 #include "gromacs/utility/exceptions.h"
49 #include "gromacs/utility/fatalerror.h"
51 #include "pull_internal.h"
53 static std::string append_before_extension(const std::string &pathname,
54 const std::string &to_append)
56 /* Appends to_append before last '.' in pathname */
57 size_t extPos = pathname.find_last_of('.');
58 if (extPos == std::string::npos)
60 return pathname + to_append;
64 return pathname.substr(0, extPos) + to_append +
65 pathname.substr(extPos, std::string::npos);
69 static void pull_print_group_x(FILE *out, const ivec dim,
70 const pull_group_work_t *pgrp)
74 for (m = 0; m < DIM; m++)
78 fprintf(out, "\t%g", pgrp->x[m]);
83 static void pull_print_coord_dr_components(FILE *out, const ivec dim, const dvec dr)
85 for (int m = 0; m < DIM; m++)
89 fprintf(out, "\t%g", dr[m]);
94 static void pull_print_coord_dr(FILE *out, const pull_coord_work_t *pcrd,
95 gmx_bool bPrintRefValue,
96 gmx_bool bPrintComponents)
98 double unit_factor = pull_conversion_factor_internal2userinput(&pcrd->params);
100 fprintf(out, "\t%g", pcrd->spatialData.value*unit_factor);
104 fprintf(out, "\t%g", pcrd->value_ref*unit_factor);
107 if (bPrintComponents)
109 pull_print_coord_dr_components(out, pcrd->params.dim, pcrd->spatialData.dr01);
110 if (pcrd->params.ngroup >= 4)
112 pull_print_coord_dr_components(out, pcrd->params.dim, pcrd->spatialData.dr23);
114 if (pcrd->params.ngroup >= 6)
116 pull_print_coord_dr_components(out, pcrd->params.dim, pcrd->spatialData.dr45);
121 static void pull_print_x(FILE *out, struct pull_t *pull, double t)
123 fprintf(out, "%.4f", t);
125 for (size_t c = 0; c < pull->coord.size(); c++)
127 pull_coord_work_t *pcrd;
129 pcrd = &pull->coord[c];
131 pull_print_coord_dr(out, pcrd,
132 pull->params.bPrintRefValue && pcrd->params.eType != epullEXTERNAL,
133 pull->params.bPrintComp);
135 if (pull->params.bPrintCOM)
137 if (pcrd->params.eGeom == epullgCYL)
139 pull_print_group_x(out, pcrd->params.dim, &pull->dyna[c]);
143 pull_print_group_x(out, pcrd->params.dim,
144 &pull->group[pcrd->params.group[0]]);
146 for (int g = 1; g < pcrd->params.ngroup; g++)
148 pull_print_group_x(out, pcrd->params.dim, &pull->group[pcrd->params.group[g]]);
155 static void pull_print_f(FILE *out, const pull_t *pull, double t)
157 fprintf(out, "%.4f", t);
159 for (const pull_coord_work_t &coord : pull->coord)
161 fprintf(out, "\t%g", coord.scalarForce);
166 void pull_print_output(struct pull_t *pull, int64_t step, double time)
168 GMX_ASSERT(pull->numExternalPotentialsStillToBeAppliedThisStep == 0, "pull_print_output called before all external pull potentials have been applied");
170 if ((pull->params.nstxout != 0) && (step % pull->params.nstxout == 0))
172 pull_print_x(pull->out_x, pull, time);
175 if ((pull->params.nstfout != 0) && (step % pull->params.nstfout == 0))
177 pull_print_f(pull->out_f, pull, time);
181 static void set_legend_for_coord_components(const pull_coord_work_t *pcrd, int coord_index, char **setname, int *nsets_ptr)
183 /* Loop over the distance vectors and print their components. Each vector is made up of two consecutive groups. */
184 for (int g = 0; g < pcrd->params.ngroup; g += 2)
186 /* Loop over the components */
187 for (int m = 0; m < DIM; m++)
189 if (pcrd->params.dim[m])
193 if (g == 0 && pcrd->params.ngroup <= 2)
195 /* For the simplest case we print a simplified legend without group indices, just the cooordinate index
196 and which dimensional component it is. */
197 sprintf(legend, "%d d%c", coord_index + 1, 'X' + m);
201 /* Otherwise, print also the group indices (relative to the coordinate, not the global ones). */
202 sprintf(legend, "%d g %d-%d d%c", coord_index + 1, g + 1, g + 2, 'X' + m);
205 setname[*nsets_ptr] = gmx_strdup(legend);
212 static FILE *open_pull_out(const char *fn, struct pull_t *pull,
213 const gmx_output_env_t *oenv,
215 const ContinuationOptions &continuationOptions)
219 char **setname, buf[50];
221 if (continuationOptions.appendFiles)
223 fp = gmx_fio_fopen(fn, "a+");
227 fp = gmx_fio_fopen(fn, "w+");
230 sprintf(buf, "Position (nm%s)", pull->bAngle ? ", deg" : "");
231 xvgr_header(fp, "Pull COM", "Time (ps)", buf,
236 sprintf(buf, "Force (kJ/mol/nm%s)", pull->bAngle ? ", kJ/mol/rad" : "");
237 xvgr_header(fp, "Pull force", "Time (ps)", buf,
241 /* With default mdp options only the actual coordinate value is printed (1),
242 * but optionally the reference value (+ 1),
243 * the group COMs for all the groups (+ ngroups_max*DIM)
244 * and the components of the distance vectors can be printed (+ (ngroups_max/2)*DIM).
246 snew(setname, pull->coord.size()*(1 + 1 + c_pullCoordNgroupMax*DIM + c_pullCoordNgroupMax/2*DIM));
249 for (size_t c = 0; c < pull->coord.size(); c++)
253 /* The order of this legend should match the order of printing
254 * the data in print_pull_x above.
257 /* The pull coord distance */
258 sprintf(buf, "%zu", c+1);
259 setname[nsets] = gmx_strdup(buf);
261 if (pull->params.bPrintRefValue &&
262 pull->coord[c].params.eType != epullEXTERNAL)
264 sprintf(buf, "%zu ref", c+1);
265 setname[nsets] = gmx_strdup(buf);
268 if (pull->params.bPrintComp)
270 set_legend_for_coord_components(&pull->coord[c], c, setname, &nsets);
273 if (pull->params.bPrintCOM)
275 for (int g = 0; g < pull->coord[c].params.ngroup; g++)
277 /* Legend for reference group position */
278 for (m = 0; m < DIM; m++)
280 if (pull->coord[c].params.dim[m])
282 sprintf(buf, "%zu g %d %c", c+1, g + 1, 'X'+m);
283 setname[nsets] = gmx_strdup(buf);
292 /* For the pull force we always only use one scalar */
293 sprintf(buf, "%zu", c+1);
294 setname[nsets] = gmx_strdup(buf);
300 xvgr_legend(fp, nsets, setname, oenv);
302 for (int c = 0; c < nsets; c++)
312 void init_pull_output_files(pull_t *pull,
314 const t_filenm fnm[],
315 const gmx_output_env_t *oenv,
316 const ContinuationOptions &continuationOptions)
318 /* Check for px and pf filename collision, if we are writing
320 std::string px_filename, pf_filename;
321 std::string px_appended, pf_appended;
324 px_filename = std::string(opt2fn("-px", nfile, fnm));
325 pf_filename = std::string(opt2fn("-pf", nfile, fnm));
327 GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR;
329 if ((pull->params.nstxout != 0) &&
330 (pull->params.nstfout != 0) &&
331 (px_filename == pf_filename))
333 if (!opt2bSet("-px", nfile, fnm) && !opt2bSet("-pf", nfile, fnm))
335 /* We are writing both pull files but neither set directly. */
338 px_appended = append_before_extension(px_filename, "_pullx");
339 pf_appended = append_before_extension(pf_filename, "_pullf");
341 GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR;
342 pull->out_x = open_pull_out(px_appended.c_str(), pull, oenv,
343 TRUE, continuationOptions);
344 pull->out_f = open_pull_out(pf_appended.c_str(), pull, oenv,
345 FALSE, continuationOptions);
350 /* If at least one of -px and -pf is set but the filenames are identical: */
351 gmx_fatal(FARGS, "Identical pull_x and pull_f output filenames %s",
352 px_filename.c_str());
355 if (pull->params.nstxout != 0)
357 pull->out_x = open_pull_out(opt2fn("-px", nfile, fnm), pull, oenv,
358 TRUE, continuationOptions);
360 if (pull->params.nstfout != 0)
362 pull->out_f = open_pull_out(opt2fn("-pf", nfile, fnm), pull, oenv,
363 FALSE, continuationOptions);