Added pull coordinate geometry angle-axis
[gromacs.git] / src / gromacs / pulling / pull.cpp
blobe6f9d8a5c128b3d1d40ec64c8840f0870cfe026d
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, 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 "pull.h"
41 #include "config.h"
43 #include <assert.h>
44 #include <math.h>
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <string.h>
49 #include <algorithm>
51 #include "gromacs/commandline/filenm.h"
52 #include "gromacs/domdec/domdec_struct.h"
53 #include "gromacs/domdec/ga2la.h"
54 #include "gromacs/fileio/gmxfio.h"
55 #include "gromacs/fileio/xvgr.h"
56 #include "gromacs/gmxlib/network.h"
57 #include "gromacs/math/functions.h"
58 #include "gromacs/math/units.h"
59 #include "gromacs/math/vec.h"
60 #include "gromacs/math/vectypes.h"
61 #include "gromacs/mdlib/mdrun.h"
62 #include "gromacs/mdtypes/commrec.h"
63 #include "gromacs/mdtypes/inputrec.h"
64 #include "gromacs/mdtypes/md_enums.h"
65 #include "gromacs/mdtypes/mdatom.h"
66 #include "gromacs/pbcutil/pbc.h"
67 #include "gromacs/pulling/pull_internal.h"
68 #include "gromacs/topology/mtop_util.h"
69 #include "gromacs/topology/topology.h"
70 #include "gromacs/utility/exceptions.h"
71 #include "gromacs/utility/fatalerror.h"
72 #include "gromacs/utility/futil.h"
73 #include "gromacs/utility/gmxassert.h"
74 #include "gromacs/utility/pleasecite.h"
75 #include "gromacs/utility/real.h"
76 #include "gromacs/utility/smalloc.h"
78 static bool pull_coordinate_is_angletype(const t_pull_coord *pcrd)
80 return (pcrd->eGeom == epullgANGLE ||
81 pcrd->eGeom == epullgDIHEDRAL ||
82 pcrd->eGeom == epullgANGLEAXIS);
85 const char *pull_coordinate_units(const t_pull_coord *pcrd)
87 return pull_coordinate_is_angletype(pcrd) ? "deg" : "nm";
90 double pull_conversion_factor_userinput2internal(const t_pull_coord *pcrd)
92 if (pull_coordinate_is_angletype(pcrd))
94 return DEG2RAD;
96 else
98 return 1.0;
102 double pull_conversion_factor_internal2userinput(const t_pull_coord *pcrd)
104 if (pull_coordinate_is_angletype(pcrd))
106 return RAD2DEG;
108 else
110 return 1.0;
114 static std::string append_before_extension(std::string pathname,
115 std::string to_append)
117 /* Appends to_append before last '.' in pathname */
118 size_t extPos = pathname.find_last_of('.');
119 if (extPos == std::string::npos)
121 return pathname + to_append;
123 else
125 return pathname.substr(0, extPos) + to_append +
126 pathname.substr(extPos, std::string::npos);
130 static void pull_print_group_x(FILE *out, ivec dim,
131 const pull_group_work_t *pgrp)
133 int m;
135 for (m = 0; m < DIM; m++)
137 if (dim[m])
139 fprintf(out, "\t%g", pgrp->x[m]);
144 static void pull_print_coord_dr_components(FILE *out, const ivec dim, const dvec dr)
146 for (int m = 0; m < DIM; m++)
148 if (dim[m])
150 fprintf(out, "\t%g", dr[m]);
155 static void pull_print_coord_dr(FILE *out, const pull_coord_work_t *pcrd,
156 gmx_bool bPrintRefValue,
157 gmx_bool bPrintComponents)
159 double unit_factor = pull_conversion_factor_internal2userinput(&pcrd->params);
161 fprintf(out, "\t%g", pcrd->value*unit_factor);
163 if (bPrintRefValue)
165 fprintf(out, "\t%g", pcrd->value_ref*unit_factor);
168 if (bPrintComponents)
170 pull_print_coord_dr_components(out, pcrd->params.dim, pcrd->dr01);
171 if (pcrd->params.ngroup >= 4)
173 pull_print_coord_dr_components(out, pcrd->params.dim, pcrd->dr23);
175 if (pcrd->params.ngroup >= 6)
177 pull_print_coord_dr_components(out, pcrd->params.dim, pcrd->dr45);
182 static void pull_print_x(FILE *out, struct pull_t *pull, double t)
184 int c;
186 fprintf(out, "%.4f", t);
188 for (c = 0; c < pull->ncoord; c++)
190 pull_coord_work_t *pcrd;
192 pcrd = &pull->coord[c];
194 pull_print_coord_dr(out, pcrd,
195 pull->params.bPrintRefValue,
196 pull->params.bPrintComp);
198 if (pull->params.bPrintCOM)
200 if (pcrd->params.eGeom == epullgCYL)
202 pull_print_group_x(out, pcrd->params.dim, &pull->dyna[c]);
204 else
206 pull_print_group_x(out, pcrd->params.dim,
207 &pull->group[pcrd->params.group[0]]);
209 for (int g = 1; g < pcrd->params.ngroup; g++)
211 pull_print_group_x(out, pcrd->params.dim, &pull->group[pcrd->params.group[g]]);
215 fprintf(out, "\n");
218 static void pull_print_f(FILE *out, struct pull_t *pull, double t)
220 int c;
222 fprintf(out, "%.4f", t);
224 for (c = 0; c < pull->ncoord; c++)
226 fprintf(out, "\t%g", pull->coord[c].f_scal);
228 fprintf(out, "\n");
231 void pull_print_output(struct pull_t *pull, gmx_int64_t step, double time)
233 if ((pull->params.nstxout != 0) && (step % pull->params.nstxout == 0))
235 pull_print_x(pull->out_x, pull, time);
238 if ((pull->params.nstfout != 0) && (step % pull->params.nstfout == 0))
240 pull_print_f(pull->out_f, pull, time);
244 static void set_legend_for_coord_components(const pull_coord_work_t *pcrd, int coord_index, char **setname, int *nsets_ptr)
246 /* Loop over the distance vectors and print their components. Each vector is made up of two consecutive groups. */
247 for (int g = 0; g < pcrd->params.ngroup; g += 2)
249 /* Loop over the components */
250 for (int m = 0; m < DIM; m++)
252 if (pcrd->params.dim[m])
254 char legend[STRLEN];
256 if (g == 0 && pcrd->params.ngroup <= 2)
258 /* For the simplest case we print a simplified legend without group indices, just the cooordinate index
259 and which dimensional component it is. */
260 sprintf(legend, "%d d%c", coord_index + 1, 'X' + m);
262 else
264 /* Otherwise, print also the group indices (relative to the coordinate, not the global ones). */
265 sprintf(legend, "%d g %d-%d d%c", coord_index + 1, g + 1, g + 2, 'X' + m);
268 setname[*nsets_ptr] = gmx_strdup(legend);
269 (*nsets_ptr)++;
275 static FILE *open_pull_out(const char *fn, struct pull_t *pull,
276 const gmx_output_env_t *oenv,
277 gmx_bool bCoord, unsigned long Flags)
279 FILE *fp;
280 int nsets, c, m;
281 char **setname, buf[50];
283 if (Flags & MD_APPENDFILES)
285 fp = gmx_fio_fopen(fn, "a+");
287 else
289 fp = gmx_fio_fopen(fn, "w+");
290 if (bCoord)
292 sprintf(buf, "Position (nm%s)", pull->bAngle ? ", deg" : "");
293 xvgr_header(fp, "Pull COM", "Time (ps)", buf,
294 exvggtXNY, oenv);
296 else
298 sprintf(buf, "Force (kJ/mol/nm%s)", pull->bAngle ? ", kJ/mol/rad" : "");
299 xvgr_header(fp, "Pull force", "Time (ps)", buf,
300 exvggtXNY, oenv);
303 /* With default mdp options only the actual coordinate value is printed (1),
304 * but optionally the reference value (+ 1),
305 * the group COMs for all the groups (+ ngroups_max*DIM)
306 * and the components of the distance vectors can be printed (+ (ngroups_max/2)*DIM).
308 snew(setname, pull->ncoord*(1 + 1 + c_pullCoordNgroupMax*DIM + c_pullCoordNgroupMax/2*DIM));
310 nsets = 0;
311 for (c = 0; c < pull->ncoord; c++)
313 if (bCoord)
315 /* The order of this legend should match the order of printing
316 * the data in print_pull_x above.
319 /* The pull coord distance */
320 sprintf(buf, "%d", c+1);
321 setname[nsets] = gmx_strdup(buf);
322 nsets++;
323 if (pull->params.bPrintRefValue)
325 sprintf(buf, "%d ref", c+1);
326 setname[nsets] = gmx_strdup(buf);
327 nsets++;
329 if (pull->params.bPrintComp)
331 set_legend_for_coord_components(&pull->coord[c], c, setname, &nsets);
334 if (pull->params.bPrintCOM)
336 for (int g = 0; g < pull->coord[c].params.ngroup; g++)
338 /* Legend for reference group position */
339 for (m = 0; m < DIM; m++)
341 if (pull->coord[c].params.dim[m])
343 sprintf(buf, "%d g %d %c", c+1, g + 1, 'X'+m);
344 setname[nsets] = gmx_strdup(buf);
345 nsets++;
351 else
353 /* For the pull force we always only use one scalar */
354 sprintf(buf, "%d", c+1);
355 setname[nsets] = gmx_strdup(buf);
356 nsets++;
359 if (nsets > 1)
361 xvgr_legend(fp, nsets, (const char**)setname, oenv);
363 for (c = 0; c < nsets; c++)
365 sfree(setname[c]);
367 sfree(setname);
370 return fp;
373 /* Apply forces in a mass weighted fashion */
374 static void apply_forces_grp(const pull_group_work_t *pgrp,
375 const t_mdatoms *md,
376 const dvec f_pull, int sign, rvec *f)
378 int i, ii, m;
379 double wmass, inv_wm;
381 inv_wm = pgrp->mwscale;
383 if (pgrp->params.nat == 1 && pgrp->nat_loc == 1)
385 /* Only one atom and our rank has this atom: we can skip
386 * the mass weighting, which means that this code also works
387 * for mass=0, e.g. with a virtual site.
389 for (m = 0; m < DIM; m++)
391 f[pgrp->ind_loc[0]][m] += sign*f_pull[m];
394 else
396 for (i = 0; i < pgrp->nat_loc; i++)
398 ii = pgrp->ind_loc[i];
399 wmass = md->massT[ii];
400 if (pgrp->weight_loc)
402 wmass *= pgrp->weight_loc[i];
405 for (m = 0; m < DIM; m++)
407 f[ii][m] += sign * wmass * f_pull[m] * inv_wm;
413 /* Apply forces in a mass weighted fashion to a cylinder group */
414 static void apply_forces_cyl_grp(const pull_group_work_t *pgrp,
415 double dv_corr,
416 const t_mdatoms *md,
417 const dvec f_pull, double f_scal,
418 int sign, rvec *f)
420 int i, ii, m;
421 double mass, weight, inv_wm, dv_com;
423 inv_wm = pgrp->mwscale;
425 for (i = 0; i < pgrp->nat_loc; i++)
427 ii = pgrp->ind_loc[i];
428 mass = md->massT[ii];
429 weight = pgrp->weight_loc[i];
430 /* The stored axial distance from the cylinder center (dv) needs
431 * to be corrected for an offset (dv_corr), which was unknown when
432 * we calculated dv.
434 dv_com = pgrp->dv[i] + dv_corr;
436 /* Here we not only add the pull force working along vec (f_pull),
437 * but also a radial component, due to the dependence of the weights
438 * on the radial distance.
440 for (m = 0; m < DIM; m++)
442 f[ii][m] += sign*inv_wm*(mass*weight*f_pull[m] +
443 pgrp->mdw[i][m]*dv_com*f_scal);
448 /* Apply torque forces in a mass weighted fashion to the groups that define
449 * the pull vector direction for pull coordinate pcrd.
451 static void apply_forces_vec_torque(const struct pull_t *pull,
452 const pull_coord_work_t *pcrd,
453 const t_mdatoms *md,
454 rvec *f)
456 double inpr;
457 int m;
458 dvec f_perp;
460 /* The component inpr along the pull vector is accounted for in the usual
461 * way. Here we account for the component perpendicular to vec.
463 inpr = 0;
464 for (m = 0; m < DIM; m++)
466 inpr += pcrd->dr01[m]*pcrd->vec[m];
468 /* The torque force works along the component of the distance vector
469 * of between the two "usual" pull groups that is perpendicular to
470 * the pull vector. The magnitude of this force is the "usual" scale force
471 * multiplied with the ratio of the distance between the two "usual" pull
472 * groups and the distance between the two groups that define the vector.
474 for (m = 0; m < DIM; m++)
476 f_perp[m] = (pcrd->dr01[m] - inpr*pcrd->vec[m])/pcrd->vec_len*pcrd->f_scal;
479 /* Apply the force to the groups defining the vector using opposite signs */
480 apply_forces_grp(&pull->group[pcrd->params.group[2]], md, f_perp, -1, f);
481 apply_forces_grp(&pull->group[pcrd->params.group[3]], md, f_perp, 1, f);
484 /* Apply forces in a mass weighted fashion */
485 static void apply_forces_coord(struct pull_t * pull, int coord,
486 const t_mdatoms * md,
487 rvec *f)
489 const pull_coord_work_t *pcrd;
491 pcrd = &pull->coord[coord];
493 if (pcrd->params.eGeom == epullgCYL)
495 dvec f_tot;
496 int m;
498 apply_forces_cyl_grp(&pull->dyna[coord], pcrd->cyl_dev, md,
499 pcrd->f01, pcrd->f_scal, -1, f);
501 /* Sum the force along the vector and the radial force */
502 for (m = 0; m < DIM; m++)
504 f_tot[m] = pcrd->f01[m] + pcrd->f_scal*pcrd->ffrad[m];
506 apply_forces_grp(&pull->group[pcrd->params.group[1]], md, f_tot, 1, f);
508 else
510 if (pcrd->params.eGeom == epullgDIRRELATIVE)
512 /* We need to apply the torque forces to the pull groups
513 * that define the pull vector.
515 apply_forces_vec_torque(pull, pcrd, md, f);
518 if (pull->group[pcrd->params.group[0]].params.nat > 0)
520 apply_forces_grp(&pull->group[pcrd->params.group[0]], md, pcrd->f01, -1, f);
522 apply_forces_grp(&pull->group[pcrd->params.group[1]], md, pcrd->f01, 1, f);
524 if (pcrd->params.ngroup >= 4)
526 apply_forces_grp(&pull->group[pcrd->params.group[2]], md, pcrd->f23, -1, f);
527 apply_forces_grp(&pull->group[pcrd->params.group[3]], md, pcrd->f23, 1, f);
529 if (pcrd->params.ngroup >= 6)
531 apply_forces_grp(&pull->group[pcrd->params.group[4]], md, pcrd->f45, -1, f);
532 apply_forces_grp(&pull->group[pcrd->params.group[5]], md, pcrd->f45, 1, f);
537 static double max_pull_distance2(const pull_coord_work_t *pcrd,
538 const t_pbc *pbc)
540 double max_d2;
541 int m;
543 max_d2 = GMX_DOUBLE_MAX;
545 for (m = 0; m < pbc->ndim_ePBC; m++)
547 if (pcrd->params.dim[m] != 0)
549 max_d2 = std::min(max_d2, static_cast<double>(norm2(pbc->box[m])));
553 return 0.25*max_d2;
556 /* This function returns the distance based on coordinates xg and xref.
557 * Note that the pull coordinate struct pcrd is not modified.
559 static void low_get_pull_coord_dr(const struct pull_t *pull,
560 const pull_coord_work_t *pcrd,
561 const t_pbc *pbc,
562 dvec xg, dvec xref, double max_dist2,
563 dvec dr)
565 const pull_group_work_t *pgrp0;
566 int m;
567 dvec xrefr, dref = {0, 0, 0};
568 double dr2;
570 pgrp0 = &pull->group[pcrd->params.group[0]];
572 /* Only the first group can be an absolute reference, in that case nat=0 */
573 if (pgrp0->params.nat == 0)
575 for (m = 0; m < DIM; m++)
577 xref[m] = pcrd->params.origin[m];
581 copy_dvec(xref, xrefr);
583 if (pcrd->params.eGeom == epullgDIRPBC)
585 for (m = 0; m < DIM; m++)
587 dref[m] = pcrd->value_ref*pcrd->vec[m];
589 /* Add the reference position, so we use the correct periodic image */
590 dvec_inc(xrefr, dref);
593 pbc_dx_d(pbc, xg, xrefr, dr);
594 dr2 = 0;
595 for (m = 0; m < DIM; m++)
597 dr[m] *= pcrd->params.dim[m];
598 dr2 += dr[m]*dr[m];
600 if (max_dist2 >= 0 && dr2 > 0.98*0.98*max_dist2)
602 gmx_fatal(FARGS, "Distance between pull groups %d and %d (%f nm) is larger than 0.49 times the box size (%f).\nYou might want to consider using \"pull-geometry = direction-periodic\" instead.\n",
603 pcrd->params.group[0], pcrd->params.group[1],
604 sqrt(dr2), sqrt(0.98*0.98*max_dist2));
607 if (pcrd->params.eGeom == epullgDIRPBC)
609 dvec_inc(dr, dref);
613 /* This function returns the distance based on the contents of the pull struct.
614 * pull->coord[coord_ind].dr, and potentially vector, are updated.
616 static void get_pull_coord_dr(struct pull_t *pull,
617 int coord_ind,
618 const t_pbc *pbc)
620 double md2;
621 pull_coord_work_t *pcrd;
622 pull_group_work_t *pgrp0, *pgrp1;
624 pcrd = &pull->coord[coord_ind];
626 if (pcrd->params.eGeom == epullgDIRPBC)
628 md2 = -1;
630 else
632 md2 = max_pull_distance2(pcrd, pbc);
635 if (pcrd->params.eGeom == epullgDIRRELATIVE)
637 /* We need to determine the pull vector */
638 const pull_group_work_t *pgrp2, *pgrp3;
639 dvec vec;
640 int m;
642 pgrp2 = &pull->group[pcrd->params.group[2]];
643 pgrp3 = &pull->group[pcrd->params.group[3]];
645 pbc_dx_d(pbc, pgrp3->x, pgrp2->x, vec);
647 for (m = 0; m < DIM; m++)
649 vec[m] *= pcrd->params.dim[m];
651 pcrd->vec_len = dnorm(vec);
652 for (m = 0; m < DIM; m++)
654 pcrd->vec[m] = vec[m]/pcrd->vec_len;
656 if (debug)
658 fprintf(debug, "pull coord %d vector: %6.3f %6.3f %6.3f normalized: %6.3f %6.3f %6.3f\n",
659 coord_ind,
660 vec[XX], vec[YY], vec[ZZ],
661 pcrd->vec[XX], pcrd->vec[YY], pcrd->vec[ZZ]);
665 pgrp0 = &pull->group[pcrd->params.group[0]];
666 pgrp1 = &pull->group[pcrd->params.group[1]];
668 low_get_pull_coord_dr(pull, pcrd, pbc,
669 pgrp1->x,
670 pcrd->params.eGeom == epullgCYL ? pull->dyna[coord_ind].x : pgrp0->x,
671 md2,
672 pcrd->dr01);
674 if (pcrd->params.ngroup >= 4)
676 pull_group_work_t *pgrp2, *pgrp3;
677 pgrp2 = &pull->group[pcrd->params.group[2]];
678 pgrp3 = &pull->group[pcrd->params.group[3]];
680 low_get_pull_coord_dr(pull, pcrd, pbc,
681 pgrp3->x,
682 pgrp2->x,
683 md2,
684 pcrd->dr23);
686 if (pcrd->params.ngroup >= 6)
688 pull_group_work_t *pgrp4, *pgrp5;
689 pgrp4 = &pull->group[pcrd->params.group[4]];
690 pgrp5 = &pull->group[pcrd->params.group[5]];
692 low_get_pull_coord_dr(pull, pcrd, pbc,
693 pgrp5->x,
694 pgrp4->x,
695 md2,
696 pcrd->dr45);
700 /* Modify x so that it is periodic in [-pi, pi)
701 * It is assumed that x is in [-3pi, 3pi) so that x
702 * needs to be shifted by at most one period.
704 static void make_periodic_2pi(double *x)
706 if (*x >= M_PI)
708 *x -= M_2PI;
710 else if (*x < -M_PI)
712 *x += M_2PI;
716 /* This function should always be used to modify pcrd->value_ref */
717 static void low_set_pull_coord_reference_value(pull_coord_work_t *pcrd,
718 int coord_ind,
719 double value_ref)
721 if (pcrd->params.eGeom == epullgDIST)
723 if (value_ref < 0)
725 gmx_fatal(FARGS, "Pull reference distance for coordinate %d (%f) needs to be non-negative", coord_ind + 1, value_ref);
728 else if (pcrd->params.eGeom == epullgANGLE || pcrd->params.eGeom == epullgANGLEAXIS)
730 if (value_ref < 0 || value_ref > M_PI)
732 gmx_fatal(FARGS, "Pull reference angle for coordinate %d (%f) needs to be in the allowed interval [0,180] deg", coord_ind + 1, value_ref*pull_conversion_factor_internal2userinput(&pcrd->params));
735 else if (pcrd->params.eGeom == epullgDIHEDRAL)
737 /* Allow pulling to be periodic for dihedral angles by remapping the reference value to the interval [-pi, pi). */
738 make_periodic_2pi(&value_ref);
741 pcrd->value_ref = value_ref;
744 static void update_pull_coord_reference_value(pull_coord_work_t *pcrd, int coord_ind, double t)
746 /* With zero rate the reference value is set initially and doesn't change */
747 if (pcrd->params.rate != 0)
749 double value_ref = (pcrd->params.init + pcrd->params.rate*t)*pull_conversion_factor_userinput2internal(&pcrd->params);
750 low_set_pull_coord_reference_value(pcrd, coord_ind, value_ref);
754 /* Returns the dihedral angle. Updates the plane normal vectors m, n. */
755 static double get_dihedral_angle_coord(pull_coord_work_t *pcrd)
757 double phi, sign;
758 dvec dr32; /* store instead of dr23? */
760 dsvmul(-1, pcrd->dr23, dr32);
761 dcprod(pcrd->dr01, dr32, pcrd->planevec_m); /* Normal of first plane */
762 dcprod(dr32, pcrd->dr45, pcrd->planevec_n); /* Normal of second plane */
763 phi = gmx_angle_between_dvecs(pcrd->planevec_m, pcrd->planevec_n);
765 /* Note 1: the sign below is opposite of that in the bondeds or Bekker 1994
766 * because there r_ij = ri - rj, while here dr01 = r_1 - r_0
767 * Note 2: the angle between the plane normal vectors equals pi only when
768 * both planes coincide. Thus, when phi = pi dr01 will lie in both planes and
769 * we get a positive sign below. Thus, the range of the dihedral angle is (-180, 180].
771 sign = (diprod(pcrd->dr01, pcrd->planevec_n) < 0.0) ? 1.0 : -1.0;
772 return sign*phi;
775 /* Calculates pull->coord[coord_ind].value.
776 * This function also updates pull->coord[coord_ind].dr.
778 static void get_pull_coord_distance(struct pull_t *pull,
779 int coord_ind,
780 const t_pbc *pbc)
782 pull_coord_work_t *pcrd;
783 int m;
785 pcrd = &pull->coord[coord_ind];
787 get_pull_coord_dr(pull, coord_ind, pbc);
789 switch (pcrd->params.eGeom)
791 case epullgDIST:
792 /* Pull along the vector between the com's */
793 pcrd->value = dnorm(pcrd->dr01);
794 break;
795 case epullgDIR:
796 case epullgDIRPBC:
797 case epullgDIRRELATIVE:
798 case epullgCYL:
799 /* Pull along vec */
800 pcrd->value = 0;
801 for (m = 0; m < DIM; m++)
803 pcrd->value += pcrd->vec[m]*pcrd->dr01[m];
805 break;
806 case epullgANGLE:
807 pcrd->value = gmx_angle_between_dvecs(pcrd->dr01, pcrd->dr23);
808 break;
809 case epullgDIHEDRAL:
810 pcrd->value = get_dihedral_angle_coord(pcrd);
811 break;
812 case epullgANGLEAXIS:
813 pcrd->value = gmx_angle_between_dvecs(pcrd->dr01, pcrd->vec);
814 break;
815 default:
816 gmx_incons("Unsupported pull type in get_pull_coord_distance");
820 /* Returns the deviation from the reference value.
821 * Updates pull->coord[coord_ind].dr, .value and .value_ref.
823 static double get_pull_coord_deviation(struct pull_t *pull,
824 int coord_ind,
825 const t_pbc *pbc,
826 double t)
828 pull_coord_work_t *pcrd;
829 double dev = 0;
831 pcrd = &pull->coord[coord_ind];
833 get_pull_coord_distance(pull, coord_ind, pbc);
835 update_pull_coord_reference_value(pcrd, coord_ind, t);
837 /* Determine the deviation */
838 dev = pcrd->value - pcrd->value_ref;
840 /* Check that values are allowed */
841 if (pcrd->params.eGeom == epullgDIST && pcrd->value == 0)
843 /* With no vector we can not determine the direction for the force,
844 * so we set the force to zero.
846 dev = 0;
848 else if (pcrd->params.eGeom == epullgDIHEDRAL)
850 /* The reference value is in [-pi, pi). The coordinate value is in (-pi, pi].
851 Thus, the unwrapped deviation is here in (-2pi, 2pi].
852 After making it periodic, the deviation will be in [-pi, pi). */
853 make_periodic_2pi(&dev);
856 return dev;
859 void get_pull_coord_value(struct pull_t *pull,
860 int coord_ind,
861 const t_pbc *pbc,
862 double *value)
864 get_pull_coord_distance(pull, coord_ind, pbc);
866 *value = pull->coord[coord_ind].value;
869 static void clear_pull_forces_coord(pull_coord_work_t *pcrd)
871 clear_dvec(pcrd->f01);
872 clear_dvec(pcrd->f23);
873 clear_dvec(pcrd->f45);
874 pcrd->f_scal = 0;
877 void clear_pull_forces(struct pull_t *pull)
879 int i;
881 /* Zeroing the forces is only required for constraint pulling.
882 * It can happen that multiple constraint steps need to be applied
883 * and therefore the constraint forces need to be accumulated.
885 for (i = 0; i < pull->ncoord; i++)
887 clear_pull_forces_coord(&pull->coord[i]);
891 /* Apply constraint using SHAKE */
892 static void do_constraint(struct pull_t *pull, t_pbc *pbc,
893 rvec *x, rvec *v,
894 gmx_bool bMaster, tensor vir,
895 double dt, double t)
898 dvec *r_ij; /* x[i] com of i in prev. step. Obeys constr. -> r_ij[i] */
899 dvec unc_ij; /* xp[i] com of i this step, before constr. -> unc_ij */
900 dvec *rnew; /* current 'new' positions of the groups */
901 double *dr_tot; /* the total update of the coords */
902 dvec vec;
903 double inpr;
904 double lambda, rm, invdt = 0;
905 gmx_bool bConverged_all, bConverged = FALSE;
906 int niter = 0, g, c, ii, j, m, max_iter = 100;
907 double a;
908 dvec tmp, tmp3;
910 snew(r_ij, pull->ncoord);
911 snew(dr_tot, pull->ncoord);
913 snew(rnew, pull->ngroup);
915 /* copy the current unconstrained positions for use in iterations. We
916 iterate until rinew[i] and rjnew[j] obey the constraints. Then
917 rinew - pull.x_unc[i] is the correction dr to group i */
918 for (g = 0; g < pull->ngroup; g++)
920 copy_dvec(pull->group[g].xp, rnew[g]);
923 /* Determine the constraint directions from the old positions */
924 for (c = 0; c < pull->ncoord; c++)
926 pull_coord_work_t *pcrd;
928 pcrd = &pull->coord[c];
930 if (pcrd->params.eType != epullCONSTRAINT)
932 continue;
935 /* Note that get_pull_coord_distance also sets pcrd->dr and pcrd->value.
936 * We don't modify dr and value anymore, so these values are also used
937 * for printing.
939 get_pull_coord_distance(pull, c, pbc);
941 if (debug)
943 fprintf(debug, "Pull coord %d dr %f %f %f\n",
944 c, pcrd->dr01[XX], pcrd->dr01[YY], pcrd->dr01[ZZ]);
947 if (pcrd->params.eGeom == epullgDIR ||
948 pcrd->params.eGeom == epullgDIRPBC)
950 /* Select the component along vec */
951 a = 0;
952 for (m = 0; m < DIM; m++)
954 a += pcrd->vec[m]*pcrd->dr01[m];
956 for (m = 0; m < DIM; m++)
958 r_ij[c][m] = a*pcrd->vec[m];
961 else
963 copy_dvec(pcrd->dr01, r_ij[c]);
966 if (dnorm2(r_ij[c]) == 0)
968 gmx_fatal(FARGS, "Distance for pull coordinate %d is zero with constraint pulling, which is not allowed.", c + 1);
972 bConverged_all = FALSE;
973 while (!bConverged_all && niter < max_iter)
975 bConverged_all = TRUE;
977 /* loop over all constraints */
978 for (c = 0; c < pull->ncoord; c++)
980 pull_coord_work_t *pcrd;
981 pull_group_work_t *pgrp0, *pgrp1;
982 dvec dr0, dr1;
984 pcrd = &pull->coord[c];
986 if (pcrd->params.eType != epullCONSTRAINT)
988 continue;
991 update_pull_coord_reference_value(pcrd, c, t);
993 pgrp0 = &pull->group[pcrd->params.group[0]];
994 pgrp1 = &pull->group[pcrd->params.group[1]];
996 /* Get the current difference vector */
997 low_get_pull_coord_dr(pull, pcrd, pbc,
998 rnew[pcrd->params.group[1]],
999 rnew[pcrd->params.group[0]],
1000 -1, unc_ij);
1002 if (debug)
1004 fprintf(debug, "Pull coord %d, iteration %d\n", c, niter);
1007 rm = 1.0/(pgrp0->invtm + pgrp1->invtm);
1009 switch (pcrd->params.eGeom)
1011 case epullgDIST:
1012 if (pcrd->value_ref <= 0)
1014 gmx_fatal(FARGS, "The pull constraint reference distance for group %d is <= 0 (%f)", c, pcrd->value_ref);
1018 double q, c_a, c_b, c_c;
1020 c_a = diprod(r_ij[c], r_ij[c]);
1021 c_b = diprod(unc_ij, r_ij[c])*2;
1022 c_c = diprod(unc_ij, unc_ij) - gmx::square(pcrd->value_ref);
1024 if (c_b < 0)
1026 q = -0.5*(c_b - std::sqrt(c_b*c_b - 4*c_a*c_c));
1027 lambda = -q/c_a;
1029 else
1031 q = -0.5*(c_b + std::sqrt(c_b*c_b - 4*c_a*c_c));
1032 lambda = -c_c/q;
1035 if (debug)
1037 fprintf(debug,
1038 "Pull ax^2+bx+c=0: a=%e b=%e c=%e lambda=%e\n",
1039 c_a, c_b, c_c, lambda);
1043 /* The position corrections dr due to the constraints */
1044 dsvmul(-lambda*rm*pgrp1->invtm, r_ij[c], dr1);
1045 dsvmul( lambda*rm*pgrp0->invtm, r_ij[c], dr0);
1046 dr_tot[c] += -lambda*dnorm(r_ij[c]);
1047 break;
1048 case epullgDIR:
1049 case epullgDIRPBC:
1050 case epullgCYL:
1051 /* A 1-dimensional constraint along a vector */
1052 a = 0;
1053 for (m = 0; m < DIM; m++)
1055 vec[m] = pcrd->vec[m];
1056 a += unc_ij[m]*vec[m];
1058 /* Select only the component along the vector */
1059 dsvmul(a, vec, unc_ij);
1060 lambda = a - pcrd->value_ref;
1061 if (debug)
1063 fprintf(debug, "Pull inpr %e lambda: %e\n", a, lambda);
1066 /* The position corrections dr due to the constraints */
1067 dsvmul(-lambda*rm*pgrp1->invtm, vec, dr1);
1068 dsvmul( lambda*rm*pgrp0->invtm, vec, dr0);
1069 dr_tot[c] += -lambda;
1070 break;
1071 default:
1072 gmx_incons("Invalid enumeration value for eGeom");
1073 /* Keep static analyzer happy */
1074 clear_dvec(dr0);
1075 clear_dvec(dr1);
1078 /* DEBUG */
1079 if (debug)
1081 int g0, g1;
1083 g0 = pcrd->params.group[0];
1084 g1 = pcrd->params.group[1];
1085 low_get_pull_coord_dr(pull, pcrd, pbc, rnew[g1], rnew[g0], -1, tmp);
1086 low_get_pull_coord_dr(pull, pcrd, pbc, dr1, dr0, -1, tmp3);
1087 fprintf(debug,
1088 "Pull cur %8.5f %8.5f %8.5f j:%8.5f %8.5f %8.5f d: %8.5f\n",
1089 rnew[g0][0], rnew[g0][1], rnew[g0][2],
1090 rnew[g1][0], rnew[g1][1], rnew[g1][2], dnorm(tmp));
1091 fprintf(debug,
1092 "Pull ref %8s %8s %8s %8s %8s %8s d: %8.5f\n",
1093 "", "", "", "", "", "", pcrd->value_ref);
1094 fprintf(debug,
1095 "Pull cor %8.5f %8.5f %8.5f j:%8.5f %8.5f %8.5f d: %8.5f\n",
1096 dr0[0], dr0[1], dr0[2],
1097 dr1[0], dr1[1], dr1[2],
1098 dnorm(tmp3));
1099 } /* END DEBUG */
1101 /* Update the COMs with dr */
1102 dvec_inc(rnew[pcrd->params.group[1]], dr1);
1103 dvec_inc(rnew[pcrd->params.group[0]], dr0);
1106 /* Check if all constraints are fullfilled now */
1107 for (c = 0; c < pull->ncoord; c++)
1109 pull_coord_work_t *pcrd;
1111 pcrd = &pull->coord[c];
1113 if (pcrd->params.eType != epullCONSTRAINT)
1115 continue;
1118 low_get_pull_coord_dr(pull, pcrd, pbc,
1119 rnew[pcrd->params.group[1]],
1120 rnew[pcrd->params.group[0]],
1121 -1, unc_ij);
1123 switch (pcrd->params.eGeom)
1125 case epullgDIST:
1126 bConverged =
1127 fabs(dnorm(unc_ij) - pcrd->value_ref) < pull->params.constr_tol;
1128 break;
1129 case epullgDIR:
1130 case epullgDIRPBC:
1131 case epullgCYL:
1132 for (m = 0; m < DIM; m++)
1134 vec[m] = pcrd->vec[m];
1136 inpr = diprod(unc_ij, vec);
1137 dsvmul(inpr, vec, unc_ij);
1138 bConverged =
1139 fabs(diprod(unc_ij, vec) - pcrd->value_ref) < pull->params.constr_tol;
1140 break;
1143 if (!bConverged)
1145 if (debug)
1147 fprintf(debug, "NOT CONVERGED YET: Group %d:"
1148 "d_ref = %f, current d = %f\n",
1149 g, pcrd->value_ref, dnorm(unc_ij));
1152 bConverged_all = FALSE;
1156 niter++;
1157 /* if after all constraints are dealt with and bConverged is still TRUE
1158 we're finished, if not we do another iteration */
1160 if (niter > max_iter)
1162 gmx_fatal(FARGS, "Too many iterations for constraint run: %d", niter);
1165 /* DONE ITERATING, NOW UPDATE COORDINATES AND CALC. CONSTRAINT FORCES */
1167 if (v)
1169 invdt = 1/dt;
1172 /* update atoms in the groups */
1173 for (g = 0; g < pull->ngroup; g++)
1175 const pull_group_work_t *pgrp;
1176 dvec dr;
1178 pgrp = &pull->group[g];
1180 /* get the final constraint displacement dr for group g */
1181 dvec_sub(rnew[g], pgrp->xp, dr);
1183 if (dnorm2(dr) == 0)
1185 /* No displacement, no update necessary */
1186 continue;
1189 /* update the atom positions */
1190 copy_dvec(dr, tmp);
1191 for (j = 0; j < pgrp->nat_loc; j++)
1193 ii = pgrp->ind_loc[j];
1194 if (pgrp->weight_loc)
1196 dsvmul(pgrp->wscale*pgrp->weight_loc[j], dr, tmp);
1198 for (m = 0; m < DIM; m++)
1200 x[ii][m] += tmp[m];
1202 if (v)
1204 for (m = 0; m < DIM; m++)
1206 v[ii][m] += invdt*tmp[m];
1212 /* calculate the constraint forces, used for output and virial only */
1213 for (c = 0; c < pull->ncoord; c++)
1215 pull_coord_work_t *pcrd;
1217 pcrd = &pull->coord[c];
1219 if (pcrd->params.eType != epullCONSTRAINT)
1221 continue;
1224 pcrd->f_scal = dr_tot[c]/((pull->group[pcrd->params.group[0]].invtm + pull->group[pcrd->params.group[1]].invtm)*dt*dt);
1226 if (vir != NULL && pcrd->params.eGeom != epullgDIRPBC && bMaster)
1228 double f_invr;
1230 /* Add the pull contribution to the virial */
1231 /* We have already checked above that r_ij[c] != 0 */
1232 f_invr = pcrd->f_scal/dnorm(r_ij[c]);
1234 for (j = 0; j < DIM; j++)
1236 for (m = 0; m < DIM; m++)
1238 vir[j][m] -= 0.5*f_invr*r_ij[c][j]*r_ij[c][m];
1244 /* finished! I hope. Give back some memory */
1245 sfree(r_ij);
1246 sfree(dr_tot);
1247 sfree(rnew);
1250 static void add_virial_coord_dr(tensor vir, const dvec dr, const dvec f)
1252 for (int j = 0; j < DIM; j++)
1254 for (int m = 0; m < DIM; m++)
1256 vir[j][m] -= 0.5*f[j]*dr[m];
1261 /* Adds the pull contribution to the virial */
1262 static void add_virial_coord(tensor vir, const pull_coord_work_t *pcrd)
1264 if (vir != NULL && pcrd->params.eGeom != epullgDIRPBC)
1266 /* Add the pull contribution for each distance vector to the virial. */
1267 add_virial_coord_dr(vir, pcrd->dr01, pcrd->f01);
1268 if (pcrd->params.ngroup >= 4)
1270 add_virial_coord_dr(vir, pcrd->dr23, pcrd->f23);
1272 if (pcrd->params.ngroup >= 6)
1274 add_virial_coord_dr(vir, pcrd->dr45, pcrd->f45);
1279 static void calc_pull_coord_force(pull_coord_work_t *pcrd,
1280 double dev, real lambda,
1281 real *V, tensor vir, real *dVdl)
1283 int m;
1284 real k, dkdl;
1286 k = (1.0 - lambda)*pcrd->params.k + lambda*pcrd->params.kB;
1287 dkdl = pcrd->params.kB - pcrd->params.k;
1289 switch (pcrd->params.eType)
1291 case epullUMBRELLA:
1292 case epullFLATBOTTOM:
1293 case epullFLATBOTTOMHIGH:
1294 /* The only difference between an umbrella and a flat-bottom
1295 * potential is that a flat-bottom is zero above or below
1296 the reference value.
1298 if ((pcrd->params.eType == epullFLATBOTTOM && dev < 0) ||
1299 (pcrd->params.eType == epullFLATBOTTOMHIGH && dev > 0))
1301 dev = 0;
1304 pcrd->f_scal = -k*dev;
1305 *V += 0.5* k*gmx::square(dev);
1306 *dVdl += 0.5*dkdl*gmx::square(dev);
1307 break;
1308 case epullCONST_F:
1309 pcrd->f_scal = -k;
1310 *V += k*pcrd->value;
1311 *dVdl += dkdl*pcrd->value;
1312 break;
1313 default:
1314 gmx_incons("Unsupported pull type in do_pull_pot");
1317 if (pcrd->params.eGeom == epullgDIST)
1319 double invdr01 = pcrd->value > 0 ? 1./pcrd->value : 0.;
1320 for (m = 0; m < DIM; m++)
1322 pcrd->f01[m] = pcrd->f_scal*pcrd->dr01[m]*invdr01;
1325 else if (pcrd->params.eGeom == epullgANGLE)
1328 double cos_theta, cos_theta2;
1330 cos_theta = cos(pcrd->value);
1331 cos_theta2 = gmx::square(cos_theta);
1333 /* The force at theta = 0, pi is undefined so we should not apply any force.
1334 * cos(theta) = 1 for theta = 0, pi so this is what we check for below.
1335 * Note that dr01 = 0 or dr23 = 0 evaluates to theta = 0 so we do not
1336 * have to check for this before dividing by their norm below.
1338 if (cos_theta2 < 1)
1340 /* The forces f01 and f23 can be decomposed into one vector parallel to dr01
1341 * and another vector parallel to dr23:
1342 * f01 = -dV/dtheta*(a*dr23/|dr23| - b*dr01*|dr01|)/|dr01|,
1343 * f23 = -dV/dtheta*(a*dr01/|dr01| - b*dr23*|dr23|)/|dr23|,
1345 double a = -gmx::invsqrt(1 - cos_theta2); /* comes from d/dx acos(x) */
1346 double b = a*cos_theta;
1347 double invdr01 = 1./dnorm(pcrd->dr01);
1348 double invdr23 = 1./dnorm(pcrd->dr23);
1349 dvec normalized_dr01, normalized_dr23;
1350 dsvmul(invdr01, pcrd->dr01, normalized_dr01);
1351 dsvmul(invdr23, pcrd->dr23, normalized_dr23);
1353 for (m = 0; m < DIM; m++)
1355 /* Here, f_scal is -dV/dtheta */
1356 pcrd->f01[m] = pcrd->f_scal*invdr01*(a*normalized_dr23[m] - b*normalized_dr01[m]);
1357 pcrd->f23[m] = pcrd->f_scal*invdr23*(a*normalized_dr01[m] - b*normalized_dr23[m]);
1360 else
1362 /* No forces to apply for ill-defined cases*/
1363 clear_pull_forces_coord(pcrd);
1366 else if (pcrd->params.eGeom == epullgANGLEAXIS)
1368 double cos_theta, cos_theta2;
1370 /* The angle-axis force is exactly the same as the angle force (above) except that in
1371 this case the second vector (dr23) is replaced by the pull vector. */
1372 cos_theta = cos(pcrd->value);
1373 cos_theta2 = gmx::square(cos_theta);
1375 if (cos_theta2 < 1)
1377 double a, b;
1378 double invdr01;
1379 dvec normalized_dr01;
1381 invdr01 = 1./dnorm(pcrd->dr01);
1382 dsvmul(invdr01, pcrd->dr01, normalized_dr01);
1383 a = -gmx::invsqrt(1 - cos_theta2); /* comes from d/dx acos(x) */
1384 b = a*cos_theta;
1386 for (m = 0; m < DIM; m++)
1388 pcrd->f01[m] = pcrd->f_scal*invdr01*(a*pcrd->vec[m] - b*normalized_dr01[m]);
1391 else
1393 clear_pull_forces_coord(pcrd);
1396 else if (pcrd->params.eGeom == epullgDIHEDRAL)
1398 double m2, n2, tol, sqrdist_32;
1399 dvec dr32;
1400 /* Note: there is a small difference here compared to the
1401 dihedral force calculations in the bondeds (ref: Bekker 1994).
1402 There rij = ri - rj, while here dr01 = r1 - r0.
1403 However, all distance vectors occur in form of cross or inner products
1404 so that two signs cancel and we end up with the same expressions.
1405 Also, we treat the more general case of 6 groups (0..5) instead of 4 (i, j, k, l).
1407 m2 = diprod(pcrd->planevec_m, pcrd->planevec_m);
1408 n2 = diprod(pcrd->planevec_n, pcrd->planevec_n);
1409 dsvmul(-1, pcrd->dr23, dr32);
1410 sqrdist_32 = diprod(dr32, dr32);
1411 tol = sqrdist_32*GMX_REAL_EPS; /* Avoid tiny angles */
1412 if ((m2 > tol) && (n2 > tol))
1414 double a_01, a_23_01, a_23_45, a_45;
1415 double inv_dist_32, inv_sqrdist_32, dist_32;
1416 dvec u, v;
1417 inv_dist_32 = gmx::invsqrt(sqrdist_32);
1418 inv_sqrdist_32 = inv_dist_32*inv_dist_32;
1419 dist_32 = sqrdist_32*inv_dist_32;
1421 /* Forces on groups 0, 1 */
1422 a_01 = pcrd->f_scal*dist_32/m2; /* f_scal is -dV/dphi */
1423 dsvmul(-a_01, pcrd->planevec_m, pcrd->f01); /* added sign to get force on group 1, not 0 */
1425 /* Forces on groups 4, 5 */
1426 a_45 = -pcrd->f_scal*dist_32/n2;
1427 dsvmul(a_45, pcrd->planevec_n, pcrd->f45); /* force on group 5 */
1429 /* Force on groups 2, 3 (defining the axis) */
1430 a_23_01 = -diprod(pcrd->dr01, dr32)*inv_sqrdist_32;
1431 a_23_45 = -diprod(pcrd->dr45, dr32)*inv_sqrdist_32;
1432 dsvmul(-a_23_01, pcrd->f01, u); /* added sign to get force from group 0, not 1 */
1433 dsvmul(a_23_45, pcrd->f45, v);
1434 dvec_sub(u, v, pcrd->f23); /* force on group 3 */
1436 else
1438 /* No force to apply for ill-defined cases */
1439 clear_pull_forces_coord(pcrd);
1442 else
1444 for (m = 0; m < DIM; m++)
1446 pcrd->f01[m] = pcrd->f_scal*pcrd->vec[m];
1450 add_virial_coord(vir, pcrd);
1453 void set_pull_coord_reference_value(struct pull_t *pull,
1454 int coord_ind, real value_ref,
1455 const struct t_pbc *pbc,
1456 const t_mdatoms *md,
1457 real lambda,
1458 gmx_bool bUpdateForce, rvec *f, tensor vir)
1460 pull_coord_work_t *pcrd;
1462 pcrd = &pull->coord[coord_ind];
1464 if (pcrd->params.rate != 0)
1466 gmx_incons("Can not update the reference value for pull coordinates with rate!=0");
1469 /* Update the reference value */
1470 low_set_pull_coord_reference_value(pcrd, coord_ind, value_ref);
1472 if (bUpdateForce)
1474 real V = 0, dVdl = 0;
1475 double f_scal_old;
1476 dvec f01_old, f23_old, f45_old;
1477 double dev;
1478 int m;
1480 if (pcrd->params.eType == epullCONSTRAINT)
1482 gmx_incons("Can not update the pull force for constraint coordinates");
1485 /* The time parameter is not used here, so we pass 0.0 */
1486 dev = get_pull_coord_deviation(pull, coord_ind, pbc, 0.0);
1488 f_scal_old = pcrd->f_scal;
1489 copy_dvec(pcrd->f01, f01_old);
1491 /* Note: f23, f45 will only actually be used for certain geometries */
1492 copy_dvec(pcrd->f23, f23_old);
1493 copy_dvec(pcrd->f45, f45_old);
1495 /* Calculate the new forces, ingnore V, vir and dVdl */
1496 calc_pull_coord_force(pcrd, dev, lambda, &V, NULL, &dVdl);
1498 /* Here we determine the force correction:
1499 * substract the half step contribution we added already,
1500 * add the half step contribution for the new force.
1501 * Note that the printing of f_scal is done in pull_potential, which
1502 * might be called before this function is called, so the output might
1503 * contain values without the correction below.
1505 pcrd->f_scal = 0.5*(-f_scal_old + pcrd->f_scal);
1506 for (m = 0; m < DIM; m++)
1508 pcrd->f01[m] = 0.5*(-f01_old[m] + pcrd->f01[m]);
1509 pcrd->f23[m] = 0.5*(-f23_old[m] + pcrd->f23[m]);
1510 pcrd->f45[m] = 0.5*(-f45_old[m] + pcrd->f45[m]);
1513 add_virial_coord(vir, pcrd);
1515 apply_forces_coord(pull, coord_ind, md, f);
1519 /* Calculate the pull potential and scalar force for a pull coordinate */
1520 static void do_pull_pot_coord(struct pull_t *pull, int coord_ind, t_pbc *pbc,
1521 double t, real lambda,
1522 real *V, tensor vir, real *dVdl)
1524 pull_coord_work_t *pcrd;
1525 double dev;
1527 pcrd = &pull->coord[coord_ind];
1529 assert(pcrd->params.eType != epullCONSTRAINT);
1531 dev = get_pull_coord_deviation(pull, coord_ind, pbc, t);
1533 calc_pull_coord_force(pcrd, dev, lambda, V, vir, dVdl);
1536 real pull_potential(struct pull_t *pull, t_mdatoms *md, t_pbc *pbc,
1537 t_commrec *cr, double t, real lambda,
1538 rvec *x, rvec *f, tensor vir, real *dvdlambda)
1540 real V = 0;
1542 assert(pull != NULL);
1544 if (pull->comm.bParticipate)
1546 real dVdl = 0;
1548 pull_calc_coms(cr, pull, md, pbc, t, x, NULL);
1550 for (int c = 0; c < pull->ncoord; c++)
1552 if (pull->coord[c].params.eType == epullCONSTRAINT)
1554 continue;
1557 do_pull_pot_coord(pull, c, pbc, t, lambda,
1558 &V, MASTER(cr) ? vir : NULL, &dVdl);
1560 /* Distribute the force over the atoms in the pulled groups */
1561 apply_forces_coord(pull, c, md, f);
1564 if (MASTER(cr))
1566 *dvdlambda += dVdl;
1570 return (MASTER(cr) ? V : 0.0);
1573 void pull_constraint(struct pull_t *pull, t_mdatoms *md, t_pbc *pbc,
1574 t_commrec *cr, double dt, double t,
1575 rvec *x, rvec *xp, rvec *v, tensor vir)
1577 assert(pull != NULL);
1579 if (pull->comm.bParticipate)
1581 pull_calc_coms(cr, pull, md, pbc, t, x, xp);
1583 do_constraint(pull, pbc, xp, v, MASTER(cr), vir, dt, t);
1587 static void make_local_pull_group(gmx_ga2la_t *ga2la,
1588 pull_group_work_t *pg, int start, int end)
1590 int i, ii;
1592 pg->nat_loc = 0;
1593 for (i = 0; i < pg->params.nat; i++)
1595 ii = pg->params.ind[i];
1596 if (ga2la)
1598 if (!ga2la_get_home(ga2la, ii, &ii))
1600 ii = -1;
1603 if (ii >= start && ii < end)
1605 /* This is a home atom, add it to the local pull group */
1606 if (pg->nat_loc >= pg->nalloc_loc)
1608 pg->nalloc_loc = over_alloc_dd(pg->nat_loc+1);
1609 srenew(pg->ind_loc, pg->nalloc_loc);
1610 if (pg->epgrppbc == epgrppbcCOS || pg->params.weight != NULL)
1612 srenew(pg->weight_loc, pg->nalloc_loc);
1615 pg->ind_loc[pg->nat_loc] = ii;
1616 if (pg->params.weight != NULL)
1618 pg->weight_loc[pg->nat_loc] = pg->params.weight[i];
1620 pg->nat_loc++;
1625 void dd_make_local_pull_groups(t_commrec *cr, struct pull_t *pull, t_mdatoms *md)
1627 gmx_domdec_t *dd;
1628 pull_comm_t *comm;
1629 gmx_ga2la_t *ga2la;
1630 gmx_bool bMustParticipate;
1631 int g;
1633 dd = cr->dd;
1635 comm = &pull->comm;
1637 if (dd)
1639 ga2la = dd->ga2la;
1641 else
1643 ga2la = NULL;
1646 /* We always make the master node participate, such that it can do i/o
1647 * and to simplify MC type extensions people might have.
1649 bMustParticipate = (comm->bParticipateAll || dd == NULL || DDMASTER(dd));
1651 for (g = 0; g < pull->ngroup; g++)
1653 int a;
1655 make_local_pull_group(ga2la, &pull->group[g],
1656 0, md->homenr);
1658 /* We should participate if we have pull or pbc atoms */
1659 if (!bMustParticipate &&
1660 (pull->group[g].nat_loc > 0 ||
1661 (pull->group[g].params.pbcatom >= 0 &&
1662 ga2la_get_home(dd->ga2la, pull->group[g].params.pbcatom, &a))))
1664 bMustParticipate = TRUE;
1668 if (!comm->bParticipateAll)
1670 /* Keep currently not required ranks in the communicator
1671 * if they needed to participate up to 20 decompositions ago.
1672 * This avoids frequent rebuilds due to atoms jumping back and forth.
1674 const gmx_int64_t history_count = 20;
1675 gmx_bool bWillParticipate;
1676 int count[2];
1678 /* Increase the decomposition counter for the current call */
1679 comm->setup_count++;
1681 if (bMustParticipate)
1683 comm->must_count = comm->setup_count;
1686 bWillParticipate =
1687 bMustParticipate ||
1688 (comm->bParticipate &&
1689 comm->must_count >= comm->setup_count - history_count);
1691 if (debug && dd != NULL)
1693 fprintf(debug, "Our DD rank (%3d) pull #atoms>0 or master: %d, will be part %d\n",
1694 dd->rank, bMustParticipate, bWillParticipate);
1697 if (bWillParticipate)
1699 /* Count the number of ranks that we want to have participating */
1700 count[0] = 1;
1701 /* Count the number of ranks that need to be added */
1702 count[1] = comm->bParticipate ? 0 : 1;
1704 else
1706 count[0] = 0;
1707 count[1] = 0;
1710 /* The cost of this global operation will be less that the cost
1711 * of the extra MPI_Comm_split calls that we can avoid.
1713 gmx_sumi(2, count, cr);
1715 /* If we are missing ranks or if we have 20% more ranks than needed
1716 * we make a new sub-communicator.
1718 if (count[1] > 0 || 6*count[0] < 5*comm->nparticipate)
1720 if (debug)
1722 fprintf(debug, "Creating new pull subcommunicator of size %d\n",
1723 count[0]);
1726 #if GMX_MPI
1727 if (comm->mpi_comm_com != MPI_COMM_NULL)
1729 MPI_Comm_free(&comm->mpi_comm_com);
1732 /* This might be an extremely expensive operation, so we try
1733 * to avoid this splitting as much as possible.
1735 assert(dd != NULL);
1736 MPI_Comm_split(dd->mpi_comm_all, bWillParticipate ? 0 : 1, dd->rank,
1737 &comm->mpi_comm_com);
1738 #endif
1740 comm->bParticipate = bWillParticipate;
1741 comm->nparticipate = count[0];
1745 /* Since the PBC of atoms might have changed, we need to update the PBC */
1746 pull->bSetPBCatoms = TRUE;
1749 static void init_pull_group_index(FILE *fplog, t_commrec *cr,
1750 int g, pull_group_work_t *pg,
1751 gmx_bool bConstraint, ivec pulldim_con,
1752 const gmx_mtop_t *mtop,
1753 const t_inputrec *ir, real lambda)
1755 int i, ii, d, nfrozen, ndim;
1756 real m, w, mbd;
1757 double tmass, wmass, wwmass;
1758 const gmx_groups_t *groups;
1759 gmx_mtop_atomlookup_t alook;
1760 t_atom *atom;
1762 if (EI_ENERGY_MINIMIZATION(ir->eI) || ir->eI == eiBD)
1764 /* There are no masses in the integrator.
1765 * But we still want to have the correct mass-weighted COMs.
1766 * So we store the real masses in the weights.
1767 * We do not set nweight, so these weights do not end up in the tpx file.
1769 if (pg->params.nweight == 0)
1771 snew(pg->params.weight, pg->params.nat);
1775 if (cr && PAR(cr))
1777 pg->nat_loc = 0;
1778 pg->nalloc_loc = 0;
1779 pg->ind_loc = NULL;
1780 pg->weight_loc = NULL;
1782 else
1784 pg->nat_loc = pg->params.nat;
1785 pg->ind_loc = pg->params.ind;
1786 if (pg->epgrppbc == epgrppbcCOS)
1788 snew(pg->weight_loc, pg->params.nat);
1790 else
1792 pg->weight_loc = pg->params.weight;
1796 groups = &mtop->groups;
1798 alook = gmx_mtop_atomlookup_init(mtop);
1800 nfrozen = 0;
1801 tmass = 0;
1802 wmass = 0;
1803 wwmass = 0;
1804 for (i = 0; i < pg->params.nat; i++)
1806 ii = pg->params.ind[i];
1807 gmx_mtop_atomnr_to_atom(alook, ii, &atom);
1808 if (bConstraint && ir->opts.nFreeze)
1810 for (d = 0; d < DIM; d++)
1812 if (pulldim_con[d] == 1 &&
1813 ir->opts.nFreeze[ggrpnr(groups, egcFREEZE, ii)][d])
1815 nfrozen++;
1819 if (ir->efep == efepNO)
1821 m = atom->m;
1823 else
1825 m = (1 - lambda)*atom->m + lambda*atom->mB;
1827 if (pg->params.nweight > 0)
1829 w = pg->params.weight[i];
1831 else
1833 w = 1;
1835 if (EI_ENERGY_MINIMIZATION(ir->eI))
1837 /* Move the mass to the weight */
1838 w *= m;
1839 m = 1;
1840 pg->params.weight[i] = w;
1842 else if (ir->eI == eiBD)
1844 if (ir->bd_fric)
1846 mbd = ir->bd_fric*ir->delta_t;
1848 else
1850 if (groups->grpnr[egcTC] == NULL)
1852 mbd = ir->delta_t/ir->opts.tau_t[0];
1854 else
1856 mbd = ir->delta_t/ir->opts.tau_t[groups->grpnr[egcTC][ii]];
1859 w *= m/mbd;
1860 m = mbd;
1861 pg->params.weight[i] = w;
1863 tmass += m;
1864 wmass += m*w;
1865 wwmass += m*w*w;
1868 gmx_mtop_atomlookup_destroy(alook);
1870 if (wmass == 0)
1872 /* We can have single atom groups with zero mass with potential pulling
1873 * without cosine weighting.
1875 if (pg->params.nat == 1 && !bConstraint && pg->epgrppbc != epgrppbcCOS)
1877 /* With one atom the mass doesn't matter */
1878 wwmass = 1;
1880 else
1882 gmx_fatal(FARGS, "The total%s mass of pull group %d is zero",
1883 pg->params.weight ? " weighted" : "", g);
1886 if (fplog)
1888 fprintf(fplog,
1889 "Pull group %d: %5d atoms, mass %9.3f",
1890 g, pg->params.nat, tmass);
1891 if (pg->params.weight ||
1892 EI_ENERGY_MINIMIZATION(ir->eI) || ir->eI == eiBD)
1894 fprintf(fplog, ", weighted mass %9.3f", wmass*wmass/wwmass);
1896 if (pg->epgrppbc == epgrppbcCOS)
1898 fprintf(fplog, ", cosine weighting will be used");
1900 fprintf(fplog, "\n");
1903 if (nfrozen == 0)
1905 /* A value != 0 signals not frozen, it is updated later */
1906 pg->invtm = -1.0;
1908 else
1910 ndim = 0;
1911 for (d = 0; d < DIM; d++)
1913 ndim += pulldim_con[d]*pg->params.nat;
1915 if (fplog && nfrozen > 0 && nfrozen < ndim)
1917 fprintf(fplog,
1918 "\nWARNING: In pull group %d some, but not all of the degrees of freedom\n"
1919 " that are subject to pulling are frozen.\n"
1920 " For constraint pulling the whole group will be frozen.\n\n",
1923 pg->invtm = 0.0;
1924 pg->wscale = 1.0;
1928 struct pull_t *
1929 init_pull(FILE *fplog, const pull_params_t *pull_params, const t_inputrec *ir,
1930 int nfile, const t_filenm fnm[],
1931 gmx_mtop_t *mtop, t_commrec *cr,
1932 const gmx_output_env_t *oenv, real lambda,
1933 gmx_bool bOutFile, unsigned long Flags)
1935 struct pull_t *pull;
1936 pull_comm_t *comm;
1937 int g, c, m;
1939 snew(pull, 1);
1941 /* Copy the pull parameters */
1942 pull->params = *pull_params;
1943 /* Avoid pointer copies */
1944 pull->params.group = NULL;
1945 pull->params.coord = NULL;
1947 pull->ncoord = pull_params->ncoord;
1948 pull->ngroup = pull_params->ngroup;
1949 snew(pull->coord, pull->ncoord);
1950 snew(pull->group, pull->ngroup);
1952 pull->bPotential = FALSE;
1953 pull->bConstraint = FALSE;
1954 pull->bCylinder = FALSE;
1955 pull->bAngle = FALSE;
1957 for (g = 0; g < pull->ngroup; g++)
1959 pull_group_work_t *pgrp;
1960 int i;
1962 pgrp = &pull->group[g];
1964 /* Copy the pull group parameters */
1965 pgrp->params = pull_params->group[g];
1967 /* Avoid pointer copies by allocating and copying arrays */
1968 snew(pgrp->params.ind, pgrp->params.nat);
1969 for (i = 0; i < pgrp->params.nat; i++)
1971 pgrp->params.ind[i] = pull_params->group[g].ind[i];
1973 if (pgrp->params.nweight > 0)
1975 snew(pgrp->params.weight, pgrp->params.nweight);
1976 for (i = 0; i < pgrp->params.nweight; i++)
1978 pgrp->params.weight[i] = pull_params->group[g].weight[i];
1983 for (c = 0; c < pull->ncoord; c++)
1985 pull_coord_work_t *pcrd;
1986 int calc_com_start, calc_com_end, g;
1988 pcrd = &pull->coord[c];
1990 /* Copy all pull coordinate parameters */
1991 pcrd->params = pull_params->coord[c];
1993 switch (pcrd->params.eGeom)
1995 case epullgDIST:
1996 case epullgDIRRELATIVE: /* Direction vector is determined at each step */
1997 case epullgANGLE:
1998 case epullgDIHEDRAL:
1999 break;
2000 case epullgDIR:
2001 case epullgDIRPBC:
2002 case epullgCYL:
2003 case epullgANGLEAXIS:
2004 copy_rvec_to_dvec(pull_params->coord[c].vec, pcrd->vec);
2005 break;
2006 default:
2007 /* We allow reading of newer tpx files with new pull geometries,
2008 * but with the same tpx format, with old code. A new geometry
2009 * only adds a new enum value, which will be out of range for
2010 * old code. The first place we need to generate an error is
2011 * here, since the pull code can't handle this.
2012 * The enum can be used before arriving here only for printing
2013 * the string corresponding to the geometry, which will result
2014 * in printing "UNDEFINED".
2016 gmx_fatal(FARGS, "Pull geometry not supported for pull coordinate %d. The geometry enum %d in the input is larger than that supported by the code (up to %d). You are probably reading a tpr file generated with a newer version of Gromacs with an binary from an older version of Gromacs.",
2017 c + 1, pcrd->params.eGeom, epullgNR - 1);
2020 if (pcrd->params.eType == epullCONSTRAINT)
2022 /* Check restrictions of the constraint pull code */
2023 if (pcrd->params.eGeom == epullgCYL ||
2024 pcrd->params.eGeom == epullgDIRRELATIVE ||
2025 pcrd->params.eGeom == epullgANGLE ||
2026 pcrd->params.eGeom == epullgDIHEDRAL ||
2027 pcrd->params.eGeom == epullgANGLEAXIS)
2029 gmx_fatal(FARGS, "Pulling of type %s can not be combined with geometry %s. Consider using pull type %s.",
2030 epull_names[pcrd->params.eType],
2031 epullg_names[pcrd->params.eGeom],
2032 epull_names[epullUMBRELLA]);
2035 pull->bConstraint = TRUE;
2037 else
2039 pull->bPotential = TRUE;
2042 if (pcrd->params.eGeom == epullgCYL)
2044 pull->bCylinder = TRUE;
2046 else if (pcrd->params.eGeom == epullgANGLE || pcrd->params.eGeom == epullgDIHEDRAL || pcrd->params.eGeom == epullgANGLEAXIS)
2048 pull->bAngle = TRUE;
2051 /* We only need to calculate the plain COM of a group
2052 * when it is not only used as a cylinder group.
2054 calc_com_start = (pcrd->params.eGeom == epullgCYL ? 1 : 0);
2055 calc_com_end = pcrd->params.ngroup;
2057 for (g = calc_com_start; g <= calc_com_end; g++)
2059 pull->group[pcrd->params.group[g]].bCalcCOM = TRUE;
2062 /* With non-zero rate the reference value is set at every step */
2063 if (pcrd->params.rate == 0)
2065 /* Initialize the constant reference value */
2066 low_set_pull_coord_reference_value(pcrd, c, pcrd->params.init*pull_conversion_factor_userinput2internal(&pcrd->params));
2070 pull->ePBC = ir->ePBC;
2071 switch (pull->ePBC)
2073 case epbcNONE: pull->npbcdim = 0; break;
2074 case epbcXY: pull->npbcdim = 2; break;
2075 default: pull->npbcdim = 3; break;
2078 if (fplog)
2080 gmx_bool bAbs, bCos;
2082 bAbs = FALSE;
2083 for (c = 0; c < pull->ncoord; c++)
2085 if (pull->group[pull->coord[c].params.group[0]].params.nat == 0 ||
2086 pull->group[pull->coord[c].params.group[1]].params.nat == 0)
2088 bAbs = TRUE;
2092 fprintf(fplog, "\n");
2093 if (pull->bPotential)
2095 fprintf(fplog, "Will apply potential COM pulling\n");
2097 if (pull->bConstraint)
2099 fprintf(fplog, "Will apply constraint COM pulling\n");
2101 fprintf(fplog, "with %d pull coordinate%s and %d group%s\n",
2102 pull->ncoord, pull->ncoord == 1 ? "" : "s",
2103 pull->ngroup, pull->ngroup == 1 ? "" : "s");
2104 if (bAbs)
2106 fprintf(fplog, "with an absolute reference\n");
2108 bCos = FALSE;
2109 for (g = 0; g < pull->ngroup; g++)
2111 if (pull->group[g].params.nat > 1 &&
2112 pull->group[g].params.pbcatom < 0)
2114 /* We are using cosine weighting */
2115 fprintf(fplog, "Cosine weighting is used for group %d\n", g);
2116 bCos = TRUE;
2119 if (bCos)
2121 please_cite(fplog, "Engin2010");
2125 pull->bRefAt = FALSE;
2126 pull->cosdim = -1;
2127 for (g = 0; g < pull->ngroup; g++)
2129 pull_group_work_t *pgrp;
2131 pgrp = &pull->group[g];
2132 pgrp->epgrppbc = epgrppbcNONE;
2133 if (pgrp->params.nat > 0)
2135 /* There is an issue when a group is used in multiple coordinates
2136 * and constraints are applied in different dimensions with atoms
2137 * frozen in some, but not all dimensions.
2138 * Since there is only one mass array per group, we can't have
2139 * frozen/non-frozen atoms for different coords at the same time.
2140 * But since this is a very exotic case, we don't check for this.
2141 * A warning is printed in init_pull_group_index.
2144 gmx_bool bConstraint;
2145 ivec pulldim, pulldim_con;
2147 /* Loop over all pull coordinates to see along which dimensions
2148 * this group is pulled and if it is involved in constraints.
2150 bConstraint = FALSE;
2151 clear_ivec(pulldim);
2152 clear_ivec(pulldim_con);
2153 for (c = 0; c < pull->ncoord; c++)
2155 pull_coord_work_t *pcrd;
2156 int gi;
2157 gmx_bool bGroupUsed;
2159 pcrd = &pull->coord[c];
2161 bGroupUsed = FALSE;
2162 for (gi = 0; gi < pcrd->params.ngroup; gi++)
2164 if (pcrd->params.group[gi] == g)
2166 bGroupUsed = TRUE;
2170 if (bGroupUsed)
2172 for (m = 0; m < DIM; m++)
2174 if (pcrd->params.dim[m] == 1)
2176 pulldim[m] = 1;
2178 if (pcrd->params.eType == epullCONSTRAINT)
2180 bConstraint = TRUE;
2181 pulldim_con[m] = 1;
2188 /* Determine if we need to take PBC into account for calculating
2189 * the COM's of the pull groups.
2191 GMX_RELEASE_ASSERT(pull->npbcdim <= DIM, "npbcdim must be <= the number of dimensions");
2192 for (m = 0; m < pull->npbcdim; m++)
2194 GMX_RELEASE_ASSERT(m <= DIM, "npbcdim must be <= the number of dimensions");
2195 if (pulldim[m] == 1 && pgrp->params.nat > 1)
2197 if (pgrp->params.pbcatom >= 0)
2199 pgrp->epgrppbc = epgrppbcREFAT;
2200 pull->bRefAt = TRUE;
2202 else
2204 if (pgrp->params.weight != NULL)
2206 gmx_fatal(FARGS, "Pull groups can not have relative weights and cosine weighting at same time");
2208 pgrp->epgrppbc = epgrppbcCOS;
2209 if (pull->cosdim >= 0 && pull->cosdim != m)
2211 gmx_fatal(FARGS, "Can only use cosine weighting with pulling in one dimension (use mdp option pull-coord?-dim)");
2213 pull->cosdim = m;
2217 /* Set the indices */
2218 init_pull_group_index(fplog, cr, g, pgrp,
2219 bConstraint, pulldim_con,
2220 mtop, ir, lambda);
2222 else
2224 /* Absolute reference, set the inverse mass to zero.
2225 * This is only relevant (and used) with constraint pulling.
2227 pgrp->invtm = 0;
2228 pgrp->wscale = 1;
2232 /* If we use cylinder coordinates, do some initialising for them */
2233 if (pull->bCylinder)
2235 snew(pull->dyna, pull->ncoord);
2237 for (c = 0; c < pull->ncoord; c++)
2239 const pull_coord_work_t *pcrd;
2241 pcrd = &pull->coord[c];
2243 if (pcrd->params.eGeom == epullgCYL)
2245 if (pull->group[pcrd->params.group[0]].params.nat == 0)
2247 gmx_fatal(FARGS, "A cylinder pull group is not supported when using absolute reference!\n");
2253 comm = &pull->comm;
2255 #if GMX_MPI
2256 /* Use a sub-communicator when we have more than 32 ranks */
2257 comm->bParticipateAll = (cr == NULL || !DOMAINDECOMP(cr) ||
2258 cr->dd->nnodes <= 32 ||
2259 getenv("GMX_PULL_PARTICIPATE_ALL") != NULL);
2260 /* This sub-commicator is not used with comm->bParticipateAll,
2261 * so we can always initialize it to NULL.
2263 comm->mpi_comm_com = MPI_COMM_NULL;
2264 comm->nparticipate = 0;
2265 #else
2266 /* No MPI: 1 rank: all ranks pull */
2267 comm->bParticipateAll = TRUE;
2268 #endif
2269 comm->bParticipate = comm->bParticipateAll;
2270 comm->setup_count = 0;
2271 comm->must_count = 0;
2273 if (!comm->bParticipateAll && fplog != NULL)
2275 fprintf(fplog, "Will use a sub-communicator for pull communication\n");
2278 comm->rbuf = NULL;
2279 comm->dbuf = NULL;
2280 comm->dbuf_cyl = NULL;
2282 /* We still need to initialize the PBC reference coordinates */
2283 pull->bSetPBCatoms = TRUE;
2285 /* Only do I/O when we are doing dynamics and if we are the MASTER */
2286 pull->out_x = NULL;
2287 pull->out_f = NULL;
2288 if (bOutFile)
2290 /* Check for px and pf filename collision, if we are writing
2291 both files */
2292 std::string px_filename, pf_filename;
2293 std::string px_appended, pf_appended;
2296 px_filename = std::string(opt2fn("-px", nfile, fnm));
2297 pf_filename = std::string(opt2fn("-pf", nfile, fnm));
2299 GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR;
2302 if ((pull->params.nstxout != 0) &&
2303 (pull->params.nstfout != 0) &&
2304 (px_filename == pf_filename))
2306 if (!opt2bSet("-px", nfile, fnm) && !opt2bSet("-pf", nfile, fnm))
2308 /* We are writing both pull files but neither set directly. */
2311 px_appended = append_before_extension(px_filename, "_pullx");
2312 pf_appended = append_before_extension(pf_filename, "_pullf");
2314 GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR;
2315 pull->out_x = open_pull_out(px_appended.c_str(), pull, oenv,
2316 TRUE, Flags);
2317 pull->out_f = open_pull_out(pf_appended.c_str(), pull, oenv,
2318 FALSE, Flags);
2319 return pull;
2321 else
2323 /* If one of -px and -pf is set but the filenames are identical: */
2324 gmx_fatal(FARGS, "Identical pull_x and pull_f output filenames %s",
2325 px_filename.c_str());
2328 if (pull->params.nstxout != 0)
2330 pull->out_x = open_pull_out(opt2fn("-px", nfile, fnm), pull, oenv,
2331 TRUE, Flags);
2333 if (pull->params.nstfout != 0)
2335 pull->out_f = open_pull_out(opt2fn("-pf", nfile, fnm), pull, oenv,
2336 FALSE, Flags);
2340 return pull;
2343 static void destroy_pull_group(pull_group_work_t *pgrp)
2345 if (pgrp->ind_loc != pgrp->params.ind)
2347 sfree(pgrp->ind_loc);
2349 if (pgrp->weight_loc != pgrp->params.weight)
2351 sfree(pgrp->weight_loc);
2353 sfree(pgrp->mdw);
2354 sfree(pgrp->dv);
2356 sfree(pgrp->params.ind);
2357 sfree(pgrp->params.weight);
2360 static void destroy_pull(struct pull_t *pull)
2362 int i;
2364 for (i = 0; i < pull->ngroup; i++)
2366 destroy_pull_group(&pull->group[i]);
2368 for (i = 0; i < pull->ncoord; i++)
2370 if (pull->coord[i].params.eGeom == epullgCYL)
2372 destroy_pull_group(&(pull->dyna[i]));
2375 sfree(pull->group);
2376 sfree(pull->dyna);
2377 sfree(pull->coord);
2379 #if GMX_MPI
2380 if (pull->comm.mpi_comm_com != MPI_COMM_NULL)
2382 MPI_Comm_free(&pull->comm.mpi_comm_com);
2384 #endif
2385 sfree(pull->comm.rbuf);
2386 sfree(pull->comm.dbuf);
2387 sfree(pull->comm.dbuf_cyl);
2389 sfree(pull);
2392 void finish_pull(struct pull_t *pull)
2394 if (pull->out_x)
2396 gmx_fio_fclose(pull->out_x);
2398 if (pull->out_f)
2400 gmx_fio_fclose(pull->out_f);
2403 destroy_pull(pull);
2406 gmx_bool pull_have_potential(const struct pull_t *pull)
2408 return pull->bPotential;
2411 gmx_bool pull_have_constraint(const struct pull_t *pull)
2413 return pull->bConstraint;