Refactored pull data structures
[alexxy/gromacs.git] / src / gromacs / pulling / pull.cpp
index 4635654dc8236452b6d7ad19fde557a5a4779d4b..3c970da0e43bfa5edd4b00df9e7bf36376bbc3b3 100644 (file)
 #include "gromacs/legacyheaders/types/commrec.h"
 #include "gromacs/math/vec.h"
 #include "gromacs/pbcutil/pbc.h"
+#include "gromacs/pulling/pull_internal.h"
 #include "gromacs/topology/mtop_util.h"
 #include "gromacs/utility/futil.h"
 #include "gromacs/utility/gmxassert.h"
 #include "gromacs/utility/smalloc.h"
 
-static void pull_print_group_x(FILE *out, ivec dim, const t_pull_group *pgrp)
+static void pull_print_group_x(FILE *out, ivec dim,
+                               const pull_group_work_t *pgrp)
 {
     int m;
 
@@ -77,7 +79,7 @@ static void pull_print_group_x(FILE *out, ivec dim, const t_pull_group *pgrp)
     }
 }
 
-static void pull_print_coord_dr(FILE *out, const t_pull_coord *pcrd,
+static void pull_print_coord_dr(FILE *out, const pull_coord_work_t *pcrd,
                                 gmx_bool bPrintRefValue,
                                 gmx_bool bPrintComponents)
 {
@@ -94,7 +96,7 @@ static void pull_print_coord_dr(FILE *out, const t_pull_coord *pcrd,
 
         for (m = 0; m < DIM; m++)
         {
-            if (pcrd->dim[m])
+            if (pcrd->params.dim[m])
             {
                 fprintf(out, "\t%g", pcrd->dr[m]);
             }
@@ -102,39 +104,43 @@ static void pull_print_coord_dr(FILE *out, const t_pull_coord *pcrd,
     }
 }
 
-static void pull_print_x(FILE *out, t_pull *pull, double t)
+static void pull_print_x(FILE *out, struct pull_t *pull, double t)
 {
-    int           c;
-    t_pull_coord *pcrd;
+    int c;
 
     fprintf(out, "%.4f", t);
 
     for (c = 0; c < pull->ncoord; c++)
     {
+        pull_coord_work_t *pcrd;
+
         pcrd = &pull->coord[c];
 
-        if (pull->bPrintCOM1)
+        if (pull->params.bPrintCOM1)
         {
-            if (pcrd->eGeom == epullgCYL)
+            if (pcrd->params.eGeom == epullgCYL)
             {
-                pull_print_group_x(out, pcrd->dim, &pull->dyna[c]);
+                pull_print_group_x(out, pcrd->params.dim, &pull->dyna[c]);
             }
             else
             {
-                pull_print_group_x(out, pcrd->dim, &pull->group[pcrd->group[0]]);
+                pull_print_group_x(out, pcrd->params.dim,
+                                   &pull->group[pcrd->params.group[0]]);
             }
         }
-        if (pull->bPrintCOM2)
+        if (pull->params.bPrintCOM2)
         {
-            pull_print_group_x(out, pcrd->dim, &pull->group[pcrd->group[1]]);
+            pull_print_group_x(out, pcrd->params.dim,
+                               &pull->group[pcrd->params.group[1]]);
         }
         pull_print_coord_dr(out, pcrd,
-                            pull->bPrintRefValue, pull->bPrintComp);
+                            pull->params.bPrintRefValue,
+                            pull->params.bPrintComp);
     }
     fprintf(out, "\n");
 }
 
-static void pull_print_f(FILE *out, t_pull *pull, double t)
+static void pull_print_f(FILE *out, struct pull_t *pull, double t)
 {
     int c;
 
@@ -147,20 +153,20 @@ static void pull_print_f(FILE *out, t_pull *pull, double t)
     fprintf(out, "\n");
 }
 
-void pull_print_output(t_pull *pull, gmx_int64_t step, double time)
+void pull_print_output(struct pull_t *pull, gmx_int64_t step, double time)
 {
-    if ((pull->nstxout != 0) && (step % pull->nstxout == 0))
+    if ((pull->params.nstxout != 0) && (step % pull->params.nstxout == 0))
     {
         pull_print_x(pull->out_x, pull, time);
     }
 
-    if ((pull->nstfout != 0) && (step % pull->nstfout == 0))
+    if ((pull->params.nstfout != 0) && (step % pull->params.nstfout == 0))
     {
         pull_print_f(pull->out_f, pull, time);
     }
 }
 
-static FILE *open_pull_out(const char *fn, t_pull *pull, const output_env_t oenv,
+static FILE *open_pull_out(const char *fn, struct pull_t *pull, const output_env_t oenv,
                            gmx_bool bCoord, unsigned long Flags)
 {
     FILE  *fp;
@@ -199,12 +205,12 @@ static FILE *open_pull_out(const char *fn, t_pull *pull, const output_env_t oenv
                  * the data in print_pull_x above.
                  */
 
-                if (pull->bPrintCOM1)
+                if (pull->params.bPrintCOM1)
                 {
                     /* Legend for reference group position */
                     for (m = 0; m < DIM; m++)
                     {
-                        if (pull->coord[c].dim[m])
+                        if (pull->coord[c].params.dim[m])
                         {
                             sprintf(buf, "%d %s%d%c", c+1, "c", 1, 'X'+m);
                             setname[nsets] = gmx_strdup(buf);
@@ -212,12 +218,12 @@ static FILE *open_pull_out(const char *fn, t_pull *pull, const output_env_t oenv
                         }
                     }
                 }
-                if (pull->bPrintCOM2)
+                if (pull->params.bPrintCOM2)
                 {
                     /* Legend for reference group position */
                     for (m = 0; m < DIM; m++)
                     {
-                        if (pull->coord[c].dim[m])
+                        if (pull->coord[c].params.dim[m])
                         {
                             sprintf(buf, "%d %s%d%c", c+1, "c", 2, 'X'+m);
                             setname[nsets] = gmx_strdup(buf);
@@ -229,18 +235,18 @@ static FILE *open_pull_out(const char *fn, t_pull *pull, const output_env_t oenv
                 sprintf(buf, "%d", c+1);
                 setname[nsets] = gmx_strdup(buf);
                 nsets++;
-                if (pull->bPrintRefValue)
+                if (pull->params.bPrintRefValue)
                 {
                     sprintf(buf, "%c%d", 'r', c+1);
                     setname[nsets] = gmx_strdup(buf);
                     nsets++;
                 }
-                if (pull->bPrintComp)
+                if (pull->params.bPrintComp)
                 {
                     /* The pull coord distance components */
                     for (m = 0; m < DIM; m++)
                     {
-                        if (pull->coord[c].dim[m])
+                        if (pull->coord[c].params.dim[m])
                         {
                             sprintf(buf, "%d %s%c", c+1, "d", 'X'+m);
                             setname[nsets] = gmx_strdup(buf);
@@ -272,7 +278,8 @@ static FILE *open_pull_out(const char *fn, t_pull *pull, const output_env_t oenv
 }
 
 /* Apply forces in a mass weighted fashion */
-static void apply_forces_grp(const t_pull_group *pgrp, const t_mdatoms *md,
+static void apply_forces_grp(const pull_group_work_t *pgrp,
+                             const t_mdatoms *md,
                              const dvec f_pull, int sign, rvec *f)
 {
     int    i, ii, m;
@@ -280,7 +287,7 @@ static void apply_forces_grp(const t_pull_group *pgrp, const t_mdatoms *md,
 
     inv_wm = pgrp->mwscale;
 
-    if (pgrp->nat == 1 && pgrp->nat_loc == 1)
+    if (pgrp->params.nat == 1 && pgrp->nat_loc == 1)
     {
         /* Only one atom and our rank has this atom: we can skip
          * the mass weighting, which means that this code also works
@@ -311,7 +318,8 @@ static void apply_forces_grp(const t_pull_group *pgrp, const t_mdatoms *md,
 }
 
 /* Apply forces in a mass weighted fashion to a cylinder group */
-static void apply_forces_cyl_grp(const t_pull_group *pgrp, double dv_corr,
+static void apply_forces_cyl_grp(const pull_group_work_t *pgrp,
+                                 double dv_corr,
                                  const t_mdatoms *md,
                                  const dvec f_pull, double f_scal,
                                  int sign, rvec *f)
@@ -347,10 +355,10 @@ static void apply_forces_cyl_grp(const t_pull_group *pgrp, double dv_corr,
 /* Apply torque forces in a mass weighted fashion to the groups that define
  * the pull vector direction for pull coordinate pcrd.
  */
-static void apply_forces_vec_torque(const t_pull       *pull,
-                                    const t_pull_coord *pcrd,
-                                    const t_mdatoms    *md,
-                                    rvec               *f)
+static void apply_forces_vec_torque(const struct pull_t     *pull,
+                                    const pull_coord_work_t *pcrd,
+                                    const t_mdatoms         *md,
+                                    rvec                    *f)
 {
     double inpr;
     int    m;
@@ -376,21 +384,22 @@ static void apply_forces_vec_torque(const t_pull       *pull,
     }
 
     /* Apply the force to the groups defining the vector using opposite signs */
-    apply_forces_grp(&pull->group[pcrd->group[2]], md, f_perp, -1, f);
-    apply_forces_grp(&pull->group[pcrd->group[3]], md, f_perp,  1, f);
+    apply_forces_grp(&pull->group[pcrd->params.group[2]], md, f_perp, -1, f);
+    apply_forces_grp(&pull->group[pcrd->params.group[3]], md, f_perp,  1, f);
 }
 
 /* Apply forces in a mass weighted fashion */
-static void apply_forces(t_pull * pull, t_mdatoms * md, rvec *f)
+static void apply_forces(struct pull_t * pull, t_mdatoms * md, rvec *f)
 {
-    int                 c;
-    const t_pull_coord *pcrd;
+    int c;
 
     for (c = 0; c < pull->ncoord; c++)
     {
+        const pull_coord_work_t *pcrd;
+
         pcrd = &pull->coord[c];
 
-        if (pcrd->eGeom == epullgCYL)
+        if (pcrd->params.eGeom == epullgCYL)
         {
             dvec f_tot;
             int  m;
@@ -403,11 +412,11 @@ static void apply_forces(t_pull * pull, t_mdatoms * md, rvec *f)
             {
                 f_tot[m] = pcrd->f[m] + pcrd->f_scal*pcrd->ffrad[m];
             }
-            apply_forces_grp(&pull->group[pcrd->group[1]], md, f_tot, 1, f);
+            apply_forces_grp(&pull->group[pcrd->params.group[1]], md, f_tot, 1, f);
         }
         else
         {
-            if (pcrd->eGeom == epullgDIRRELATIVE)
+            if (pcrd->params.eGeom == epullgDIRRELATIVE)
             {
                 /* We need to apply the torque forces to the pull groups
                  * that define the pull vector.
@@ -415,16 +424,17 @@ static void apply_forces(t_pull * pull, t_mdatoms * md, rvec *f)
                 apply_forces_vec_torque(pull, pcrd, md, f);
             }
 
-            if (pull->group[pcrd->group[0]].nat > 0)
+            if (pull->group[pcrd->params.group[0]].params.nat > 0)
             {
-                apply_forces_grp(&pull->group[pcrd->group[0]], md, pcrd->f, -1, f);
+                apply_forces_grp(&pull->group[pcrd->params.group[0]], md, pcrd->f, -1, f);
             }
-            apply_forces_grp(&pull->group[pcrd->group[1]], md, pcrd->f, 1, f);
+            apply_forces_grp(&pull->group[pcrd->params.group[1]], md, pcrd->f, 1, f);
         }
     }
 }
 
-static double max_pull_distance2(const t_pull_coord *pcrd, const t_pbc *pbc)
+static double max_pull_distance2(const pull_coord_work_t *pcrd,
+                                 const t_pbc             *pbc)
 {
     double max_d2;
     int    m;
@@ -433,7 +443,7 @@ static double max_pull_distance2(const t_pull_coord *pcrd, const t_pbc *pbc)
 
     for (m = 0; m < pbc->ndim_ePBC; m++)
     {
-        if (pcrd->dim[m] != 0)
+        if (pcrd->params.dim[m] != 0)
         {
             max_d2 = std::min(max_d2, static_cast<double>(norm2(pbc->box[m])));
         }
@@ -445,31 +455,31 @@ static double max_pull_distance2(const t_pull_coord *pcrd, const t_pbc *pbc)
 /* This function returns the distance based on coordinates xg and xref.
  * Note that the pull coordinate struct pcrd is not modified.
  */
-static void low_get_pull_coord_dr(const t_pull *pull,
-                                  const t_pull_coord *pcrd,
+static void low_get_pull_coord_dr(const struct pull_t *pull,
+                                  const pull_coord_work_t *pcrd,
                                   const t_pbc *pbc,
                                   dvec xg, dvec xref, double max_dist2,
                                   dvec dr)
 {
-    const t_pull_group *pgrp0;
-    int                 m;
-    dvec                xrefr, dref = {0, 0, 0};
-    double              dr2;
+    const pull_group_work_t *pgrp0;
+    int                      m;
+    dvec                     xrefr, dref = {0, 0, 0};
+    double                   dr2;
 
-    pgrp0 = &pull->group[pcrd->group[0]];
+    pgrp0 = &pull->group[pcrd->params.group[0]];
 
     /* Only the first group can be an absolute reference, in that case nat=0 */
-    if (pgrp0->nat == 0)
+    if (pgrp0->params.nat == 0)
     {
         for (m = 0; m < DIM; m++)
         {
-            xref[m] = pcrd->origin[m];
+            xref[m] = pcrd->params.origin[m];
         }
     }
 
     copy_dvec(xref, xrefr);
 
-    if (pcrd->eGeom == epullgDIRPBC)
+    if (pcrd->params.eGeom == epullgDIRPBC)
     {
         for (m = 0; m < DIM; m++)
         {
@@ -483,16 +493,17 @@ static void low_get_pull_coord_dr(const t_pull *pull,
     dr2 = 0;
     for (m = 0; m < DIM; m++)
     {
-        dr[m] *= pcrd->dim[m];
+        dr[m] *= pcrd->params.dim[m];
         dr2   += dr[m]*dr[m];
     }
     if (max_dist2 >= 0 && dr2 > 0.98*0.98*max_dist2)
     {
         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",
-                  pcrd->group[0], pcrd->group[1], sqrt(dr2), sqrt(max_dist2));
+                  pcrd->params.group[0], pcrd->params.group[1],
+                  sqrt(dr2), sqrt(max_dist2));
     }
 
-    if (pcrd->eGeom == epullgDIRPBC)
+    if (pcrd->params.eGeom == epullgDIRPBC)
     {
         dvec_inc(dr, dref);
     }
@@ -501,16 +512,16 @@ static void low_get_pull_coord_dr(const t_pull *pull,
 /* This function returns the distance based on the contents of the pull struct.
  * pull->coord[coord_ind].dr, and potentially vector, are updated.
  */
-static void get_pull_coord_dr(t_pull      *pull,
-                              int          coord_ind,
-                              const t_pbc *pbc)
+static void get_pull_coord_dr(struct pull_t *pull,
+                              int            coord_ind,
+                              const t_pbc   *pbc)
 {
-    double        md2;
-    t_pull_coord *pcrd;
+    double             md2;
+    pull_coord_work_t *pcrd;
 
     pcrd = &pull->coord[coord_ind];
 
-    if (pcrd->eGeom == epullgDIRPBC)
+    if (pcrd->params.eGeom == epullgDIRPBC)
     {
         md2 = -1;
     }
@@ -519,21 +530,21 @@ static void get_pull_coord_dr(t_pull      *pull,
         md2 = max_pull_distance2(pcrd, pbc);
     }
 
-    if (pcrd->eGeom == epullgDIRRELATIVE)
+    if (pcrd->params.eGeom == epullgDIRRELATIVE)
     {
         /* We need to determine the pull vector */
-        const t_pull_group *pgrp2, *pgrp3;
-        dvec                vec;
-        int                 m;
+        const pull_group_work_t *pgrp2, *pgrp3;
+        dvec                     vec;
+        int                      m;
 
-        pgrp2 = &pull->group[pcrd->group[2]];
-        pgrp3 = &pull->group[pcrd->group[3]];
+        pgrp2 = &pull->group[pcrd->params.group[2]];
+        pgrp3 = &pull->group[pcrd->params.group[3]];
 
         pbc_dx_d(pbc, pgrp3->x, pgrp2->x, vec);
 
         for (m = 0; m < DIM; m++)
         {
-            vec[m] *= pcrd->dim[m];
+            vec[m] *= pcrd->params.dim[m];
         }
         pcrd->vec_len = dnorm(vec);
         for (m = 0; m < DIM; m++)
@@ -550,36 +561,36 @@ static void get_pull_coord_dr(t_pull      *pull,
     }
 
     low_get_pull_coord_dr(pull, pcrd, pbc,
-                          pull->group[pcrd->group[1]].x,
-                          pcrd->eGeom == epullgCYL ? pull->dyna[coord_ind].x : pull->group[pcrd->group[0]].x,
+                          pull->group[pcrd->params.group[1]].x,
+                          pcrd->params.eGeom == epullgCYL ? pull->dyna[coord_ind].x : pull->group[pcrd->params.group[0]].x,
                           md2,
                           pcrd->dr);
 }
 
-static void update_pull_coord_reference_value(t_pull_coord *pcrd, double t)
+static void update_pull_coord_reference_value(pull_coord_work_t *pcrd, double t)
 {
     /* With zero rate the reference value is set initially and doesn't change */
-    if (pcrd->rate != 0)
+    if (pcrd->params.rate != 0)
     {
-        pcrd->value_ref = pcrd->init + pcrd->rate*t;
+        pcrd->value_ref = pcrd->params.init + pcrd->params.rate*t;
     }
 }
 
 /* Calculates pull->coord[coord_ind].value.
  * This function also updates pull->coord[coord_ind].dr.
  */
-static void get_pull_coord_distance(t_pull      *pull,
-                                    int          coord_ind,
-                                    const t_pbc *pbc)
+static void get_pull_coord_distance(struct pull_t *pull,
+                                    int            coord_ind,
+                                    const t_pbc   *pbc)
 {
-    t_pull_coord *pcrd;
-    int           m;
+    pull_coord_work_t *pcrd;
+    int                m;
 
     pcrd = &pull->coord[coord_ind];
 
     get_pull_coord_dr(pull, coord_ind, pbc);
 
-    switch (pcrd->eGeom)
+    switch (pcrd->params.eGeom)
     {
         case epullgDIST:
             /* Pull along the vector between the com's */
@@ -602,15 +613,15 @@ static void get_pull_coord_distance(t_pull      *pull,
 /* Returns the deviation from the reference value.
  * Updates pull->coord[coord_ind].dr, .value and .value_ref.
  */
-static double get_pull_coord_deviation(t_pull      *pull,
-                                       int          coord_ind,
-                                       const t_pbc *pbc,
-                                       double       t)
+static double get_pull_coord_deviation(struct pull_t *pull,
+                                       int            coord_ind,
+                                       const t_pbc   *pbc,
+                                       double         t)
 {
-    static gmx_bool  bWarned = FALSE; /* TODO: this should be fixed for thread-safety,
-                                         but is fairly benign */
-    t_pull_coord    *pcrd;
-    double           dev = 0;
+    static gmx_bool    bWarned = FALSE; /* TODO: this should be fixed for thread-safety,
+                                           but is fairly benign */
+    pull_coord_work_t *pcrd;
+    double             dev = 0;
 
     pcrd = &pull->coord[coord_ind];
 
@@ -618,7 +629,7 @@ static double get_pull_coord_deviation(t_pull      *pull,
 
     update_pull_coord_reference_value(pcrd, t);
 
-    switch (pcrd->eGeom)
+    switch (pcrd->params.eGeom)
     {
         case epullgDIST:
             /* Pull along the vector between the com's */
@@ -652,17 +663,17 @@ static double get_pull_coord_deviation(t_pull      *pull,
     return dev;
 }
 
-void get_pull_coord_value(t_pull      *pull,
-                          int          coord_ind,
-                          const t_pbc *pbc,
-                          double      *value)
+void get_pull_coord_value(struct pull_t *pull,
+                          int            coord_ind,
+                          const t_pbc   *pbc,
+                          double        *value)
 {
     get_pull_coord_distance(pull, coord_ind, pbc);
 
     *value = pull->coord[coord_ind].value;
 }
 
-void clear_pull_forces(t_pull *pull)
+void clear_pull_forces(struct pull_t *pull)
 {
     int i;
 
@@ -678,7 +689,7 @@ void clear_pull_forces(t_pull *pull)
 }
 
 /* Apply constraint using SHAKE */
-static void do_constraint(t_pull *pull, t_pbc *pbc,
+static void do_constraint(struct pull_t *pull, t_pbc *pbc,
                           rvec *x, rvec *v,
                           gmx_bool bMaster, tensor vir,
                           double dt, double t)
@@ -695,8 +706,6 @@ static void do_constraint(t_pull *pull, t_pbc *pbc,
     int           niter = 0, g, c, ii, j, m, max_iter = 100;
     double        a;
     dvec          tmp, tmp3;
-    t_pull_group *pgrp0, *pgrp1;
-    t_pull_coord *pcrd;
 
     snew(r_ij,   pull->ncoord);
     snew(dr_tot, pull->ncoord);
@@ -714,9 +723,11 @@ static void do_constraint(t_pull *pull, t_pbc *pbc,
     /* Determine the constraint directions from the old positions */
     for (c = 0; c < pull->ncoord; c++)
     {
+        pull_coord_work_t *pcrd;
+
         pcrd = &pull->coord[c];
 
-        if (pcrd->eType != epullCONSTRAINT)
+        if (pcrd->params.eType != epullCONSTRAINT)
         {
             continue;
         }
@@ -733,7 +744,8 @@ static void do_constraint(t_pull *pull, t_pbc *pbc,
                     c, pcrd->dr[XX], pcrd->dr[YY], pcrd->dr[ZZ]);
         }
 
-        if (pcrd->eGeom == epullgDIR || pcrd->eGeom == epullgDIRPBC)
+        if (pcrd->params.eGeom == epullgDIR ||
+            pcrd->params.eGeom == epullgDIRPBC)
         {
             /* Select the component along vec */
             a = 0;
@@ -765,24 +777,26 @@ static void do_constraint(t_pull *pull, t_pbc *pbc,
         /* loop over all constraints */
         for (c = 0; c < pull->ncoord; c++)
         {
-            dvec dr0, dr1;
+            pull_coord_work_t *pcrd;
+            pull_group_work_t *pgrp0, *pgrp1;
+            dvec               dr0, dr1;
 
             pcrd  = &pull->coord[c];
 
-            if (pcrd->eType != epullCONSTRAINT)
+            if (pcrd->params.eType != epullCONSTRAINT)
             {
                 continue;
             }
 
             update_pull_coord_reference_value(pcrd, t);
 
-            pgrp0 = &pull->group[pcrd->group[0]];
-            pgrp1 = &pull->group[pcrd->group[1]];
+            pgrp0 = &pull->group[pcrd->params.group[0]];
+            pgrp1 = &pull->group[pcrd->params.group[1]];
 
             /* Get the current difference vector */
             low_get_pull_coord_dr(pull, pcrd, pbc,
-                                  rnew[pcrd->group[1]],
-                                  rnew[pcrd->group[0]],
+                                  rnew[pcrd->params.group[1]],
+                                  rnew[pcrd->params.group[0]],
                                   -1, unc_ij);
 
             if (debug)
@@ -792,7 +806,7 @@ static void do_constraint(t_pull *pull, t_pbc *pbc,
 
             rm = 1.0/(pgrp0->invtm + pgrp1->invtm);
 
-            switch (pcrd->eGeom)
+            switch (pcrd->params.eGeom)
             {
                 case epullgDIST:
                     if (pcrd->value_ref <= 0)
@@ -866,8 +880,8 @@ static void do_constraint(t_pull *pull, t_pbc *pbc,
             {
                 int g0, g1;
 
-                g0 = pcrd->group[0];
-                g1 = pcrd->group[1];
+                g0 = pcrd->params.group[0];
+                g1 = pcrd->params.group[1];
                 low_get_pull_coord_dr(pull, pcrd, pbc, rnew[g1], rnew[g0], -1, tmp);
                 low_get_pull_coord_dr(pull, pcrd, pbc, dr1, dr0, -1, tmp3);
                 fprintf(debug,
@@ -885,30 +899,32 @@ static void do_constraint(t_pull *pull, t_pbc *pbc,
             } /* END DEBUG */
 
             /* Update the COMs with dr */
-            dvec_inc(rnew[pcrd->group[1]], dr1);
-            dvec_inc(rnew[pcrd->group[0]], dr0);
+            dvec_inc(rnew[pcrd->params.group[1]], dr1);
+            dvec_inc(rnew[pcrd->params.group[0]], dr0);
         }
 
         /* Check if all constraints are fullfilled now */
         for (c = 0; c < pull->ncoord; c++)
         {
+            pull_coord_work_t *pcrd;
+
             pcrd = &pull->coord[c];
 
-            if (pcrd->eType != epullCONSTRAINT)
+            if (pcrd->params.eType != epullCONSTRAINT)
             {
                 continue;
             }
 
             low_get_pull_coord_dr(pull, pcrd, pbc,
-                                  rnew[pcrd->group[1]],
-                                  rnew[pcrd->group[0]],
+                                  rnew[pcrd->params.group[1]],
+                                  rnew[pcrd->params.group[0]],
                                   -1, unc_ij);
 
-            switch (pcrd->eGeom)
+            switch (pcrd->params.eGeom)
             {
                 case epullgDIST:
                     bConverged =
-                        fabs(dnorm(unc_ij) - pcrd->value_ref) < pull->constr_tol;
+                        fabs(dnorm(unc_ij) - pcrd->value_ref) < pull->params.constr_tol;
                     break;
                 case epullgDIR:
                 case epullgDIRPBC:
@@ -920,7 +936,7 @@ static void do_constraint(t_pull *pull, t_pbc *pbc,
                     inpr = diprod(unc_ij, vec);
                     dsvmul(inpr, vec, unc_ij);
                     bConverged =
-                        fabs(diprod(unc_ij, vec) - pcrd->value_ref) < pull->constr_tol;
+                        fabs(diprod(unc_ij, vec) - pcrd->value_ref) < pull->params.constr_tol;
                     break;
             }
 
@@ -956,8 +972,8 @@ static void do_constraint(t_pull *pull, t_pbc *pbc,
     /* update atoms in the groups */
     for (g = 0; g < pull->ngroup; g++)
     {
-        const t_pull_group *pgrp;
-        dvec                dr;
+        const pull_group_work_t *pgrp;
+        dvec                     dr;
 
         pgrp = &pull->group[g];
 
@@ -996,16 +1012,18 @@ static void do_constraint(t_pull *pull, t_pbc *pbc,
     /* calculate the constraint forces, used for output and virial only */
     for (c = 0; c < pull->ncoord; c++)
     {
+        pull_coord_work_t *pcrd;
+
         pcrd = &pull->coord[c];
 
-        if (pcrd->eType != epullCONSTRAINT)
+        if (pcrd->params.eType != epullCONSTRAINT)
         {
             continue;
         }
 
-        pcrd->f_scal = dr_tot[c]/((pull->group[pcrd->group[0]].invtm + pull->group[pcrd->group[1]].invtm)*dt*dt);
+        pcrd->f_scal = dr_tot[c]/((pull->group[pcrd->params.group[0]].invtm + pull->group[pcrd->params.group[1]].invtm)*dt*dt);
 
-        if (vir != NULL && pcrd->eGeom != epullgDIRPBC && bMaster)
+        if (vir != NULL && pcrd->params.eGeom != epullgDIRPBC && bMaster)
         {
             double f_invr;
 
@@ -1030,32 +1048,33 @@ static void do_constraint(t_pull *pull, t_pbc *pbc,
 }
 
 /* Pulling with a harmonic umbrella potential or constant force */
-static void do_pull_pot(t_pull *pull, t_pbc *pbc, double t, real lambda,
+static void do_pull_pot(struct pull_t *pull, t_pbc *pbc, double t, real lambda,
                         real *V, tensor vir, real *dVdl)
 {
-    int           c, j, m;
-    double        dev, ndr, invdr = 0;
-    real          k, dkdl;
-    t_pull_coord *pcrd;
+    int    c, j, m;
+    double dev, ndr, invdr = 0;
+    real   k, dkdl;
 
     /* loop over the pull coordinates */
     *V    = 0;
     *dVdl = 0;
     for (c = 0; c < pull->ncoord; c++)
     {
+        pull_coord_work_t *pcrd;
+
         pcrd = &pull->coord[c];
 
-        if (pcrd->eType == epullCONSTRAINT)
+        if (pcrd->params.eType == epullCONSTRAINT)
         {
             continue;
         }
 
         dev = get_pull_coord_deviation(pull, c, pbc, t);
 
-        k    = (1.0 - lambda)*pcrd->k + lambda*pcrd->kB;
-        dkdl = pcrd->kB - pcrd->k;
+        k    = (1.0 - lambda)*pcrd->params.k + lambda*pcrd->params.kB;
+        dkdl = pcrd->params.kB - pcrd->params.k;
 
-        if (pcrd->eGeom == epullgDIST)
+        if (pcrd->params.eGeom == epullgDIST)
         {
             ndr   = dnorm(pcrd->dr);
             if (ndr > 0)
@@ -1081,14 +1100,14 @@ static void do_pull_pot(t_pull *pull, t_pbc *pbc, double t, real lambda,
             }
         }
 
-        switch (pcrd->eType)
+        switch (pcrd->params.eType)
         {
             case epullUMBRELLA:
             case epullFLATBOTTOM:
                 /* The only difference between an umbrella and a flat-bottom
                  * potential is that a flat-bottom is zero below zero.
                  */
-                if (pcrd->eType == epullFLATBOTTOM && dev < 0)
+                if (pcrd->params.eType == epullFLATBOTTOM && dev < 0)
                 {
                     dev = 0;
                 }
@@ -1106,7 +1125,7 @@ static void do_pull_pot(t_pull *pull, t_pbc *pbc, double t, real lambda,
                 gmx_incons("Unsupported pull type in do_pull_pot");
         }
 
-        if (pcrd->eGeom == epullgDIST)
+        if (pcrd->params.eGeom == epullgDIST)
         {
             for (m = 0; m < DIM; m++)
             {
@@ -1121,7 +1140,7 @@ static void do_pull_pot(t_pull *pull, t_pbc *pbc, double t, real lambda,
             }
         }
 
-        if (vir != NULL && pcrd->eGeom != epullgDIRPBC)
+        if (vir != NULL && pcrd->params.eGeom != epullgDIRPBC)
         {
             /* Add the pull contribution to the virial */
             for (j = 0; j < DIM; j++)
@@ -1135,12 +1154,14 @@ static void do_pull_pot(t_pull *pull, t_pbc *pbc, double t, real lambda,
     }
 }
 
-real pull_potential(t_pull *pull, t_mdatoms *md, t_pbc *pbc,
+real pull_potential(struct pull_t *pull, t_mdatoms *md, t_pbc *pbc,
                     t_commrec *cr, double t, real lambda,
                     rvec *x, rvec *f, tensor vir, real *dvdlambda)
 {
     real V, dVdl;
 
+    assert(pull != NULL);
+
     pull_calc_coms(cr, pull, md, pbc, t, x, NULL);
 
     do_pull_pot(pull, pbc, t, lambda,
@@ -1157,24 +1178,26 @@ real pull_potential(t_pull *pull, t_mdatoms *md, t_pbc *pbc,
     return (MASTER(cr) ? V : 0.0);
 }
 
-void pull_constraint(t_pull *pull, t_mdatoms *md, t_pbc *pbc,
+void pull_constraint(struct pull_t *pull, t_mdatoms *md, t_pbc *pbc,
                      t_commrec *cr, double dt, double t,
                      rvec *x, rvec *xp, rvec *v, tensor vir)
 {
+    assert(pull != NULL);
+
     pull_calc_coms(cr, pull, md, pbc, t, x, xp);
 
     do_constraint(pull, pbc, xp, v, MASTER(cr), vir, dt, t);
 }
 
 static void make_local_pull_group(gmx_ga2la_t ga2la,
-                                  t_pull_group *pg, int start, int end)
+                                  pull_group_work_t *pg, int start, int end)
 {
     int i, ii;
 
     pg->nat_loc = 0;
-    for (i = 0; i < pg->nat; i++)
+    for (i = 0; i < pg->params.nat; i++)
     {
-        ii = pg->ind[i];
+        ii = pg->params.ind[i];
         if (ga2la)
         {
             if (!ga2la_get_home(ga2la, ii, &ii))
@@ -1189,22 +1212,22 @@ static void make_local_pull_group(gmx_ga2la_t ga2la,
             {
                 pg->nalloc_loc = over_alloc_dd(pg->nat_loc+1);
                 srenew(pg->ind_loc, pg->nalloc_loc);
-                if (pg->epgrppbc == epgrppbcCOS || pg->weight)
+                if (pg->epgrppbc == epgrppbcCOS || pg->params.weight != NULL)
                 {
                     srenew(pg->weight_loc, pg->nalloc_loc);
                 }
             }
             pg->ind_loc[pg->nat_loc] = ii;
-            if (pg->weight)
+            if (pg->params.weight != NULL)
             {
-                pg->weight_loc[pg->nat_loc] = pg->weight[i];
+                pg->weight_loc[pg->nat_loc] = pg->params.weight[i];
             }
             pg->nat_loc++;
         }
     }
 }
 
-void dd_make_local_pull_groups(gmx_domdec_t *dd, t_pull *pull, t_mdatoms *md)
+void dd_make_local_pull_groups(gmx_domdec_t *dd, struct pull_t *pull, t_mdatoms *md)
 {
     gmx_ga2la_t ga2la;
     int         g;
@@ -1229,14 +1252,15 @@ void dd_make_local_pull_groups(gmx_domdec_t *dd, t_pull *pull, t_mdatoms *md)
 }
 
 static void init_pull_group_index(FILE *fplog, t_commrec *cr,
-                                  int g, t_pull_group *pg,
+                                  int g, pull_group_work_t *pg,
                                   gmx_bool bConstraint, ivec pulldim_con,
-                                  gmx_mtop_t *mtop, t_inputrec *ir, real lambda)
+                                  const gmx_mtop_t *mtop,
+                                  const t_inputrec *ir, real lambda)
 {
     int                   i, ii, d, nfrozen, ndim;
     real                  m, w, mbd;
     double                tmass, wmass, wwmass;
-    gmx_groups_t         *groups;
+    const gmx_groups_t   *groups;
     gmx_mtop_atomlookup_t alook;
     t_atom               *atom;
 
@@ -1247,9 +1271,9 @@ static void init_pull_group_index(FILE *fplog, t_commrec *cr,
          * So we store the real masses in the weights.
          * We do not set nweight, so these weights do not end up in the tpx file.
          */
-        if (pg->nweight == 0)
+        if (pg->params.nweight == 0)
         {
-            snew(pg->weight, pg->nat);
+            snew(pg->params.weight, pg->params.nat);
         }
     }
 
@@ -1262,15 +1286,15 @@ static void init_pull_group_index(FILE *fplog, t_commrec *cr,
     }
     else
     {
-        pg->nat_loc = pg->nat;
-        pg->ind_loc = pg->ind;
+        pg->nat_loc = pg->params.nat;
+        pg->ind_loc = pg->params.ind;
         if (pg->epgrppbc == epgrppbcCOS)
         {
-            snew(pg->weight_loc, pg->nat);
+            snew(pg->weight_loc, pg->params.nat);
         }
         else
         {
-            pg->weight_loc = pg->weight;
+            pg->weight_loc = pg->params.weight;
         }
     }
 
@@ -1282,9 +1306,9 @@ static void init_pull_group_index(FILE *fplog, t_commrec *cr,
     tmass   = 0;
     wmass   = 0;
     wwmass  = 0;
-    for (i = 0; i < pg->nat; i++)
+    for (i = 0; i < pg->params.nat; i++)
     {
-        ii = pg->ind[i];
+        ii = pg->params.ind[i];
         gmx_mtop_atomnr_to_atom(alook, ii, &atom);
         if (bConstraint && ir->opts.nFreeze)
         {
@@ -1305,9 +1329,9 @@ static void init_pull_group_index(FILE *fplog, t_commrec *cr,
         {
             m = (1 - lambda)*atom->m + lambda*atom->mB;
         }
-        if (pg->nweight > 0)
+        if (pg->params.nweight > 0)
         {
-            w = pg->weight[i];
+            w = pg->params.weight[i];
         }
         else
         {
@@ -1316,9 +1340,9 @@ static void init_pull_group_index(FILE *fplog, t_commrec *cr,
         if (EI_ENERGY_MINIMIZATION(ir->eI))
         {
             /* Move the mass to the weight */
-            w            *= m;
-            m             = 1;
-            pg->weight[i] = w;
+            w                   *= m;
+            m                    = 1;
+            pg->params.weight[i] = w;
         }
         else if (ir->eI == eiBD)
         {
@@ -1337,9 +1361,9 @@ static void init_pull_group_index(FILE *fplog, t_commrec *cr,
                     mbd = ir->delta_t/ir->opts.tau_t[groups->grpnr[egcTC][ii]];
                 }
             }
-            w            *= m/mbd;
-            m             = mbd;
-            pg->weight[i] = w;
+            w                   *= m/mbd;
+            m                    = mbd;
+            pg->params.weight[i] = w;
         }
         tmass  += m;
         wmass  += m*w;
@@ -1353,7 +1377,7 @@ static void init_pull_group_index(FILE *fplog, t_commrec *cr,
         /* We can have single atom groups with zero mass with potential pulling
          * without cosine weighting.
          */
-        if (pg->nat == 1 && !bConstraint && pg->epgrppbc != epgrppbcCOS)
+        if (pg->params.nat == 1 && !bConstraint && pg->epgrppbc != epgrppbcCOS)
         {
             /* With one atom the mass doesn't matter */
             wwmass = 1;
@@ -1361,14 +1385,16 @@ static void init_pull_group_index(FILE *fplog, t_commrec *cr,
         else
         {
             gmx_fatal(FARGS, "The total%s mass of pull group %d is zero",
-                      pg->weight ? " weighted" : "", g);
+                      pg->params.weight ? " weighted" : "", g);
         }
     }
     if (fplog)
     {
         fprintf(fplog,
-                "Pull group %d: %5d atoms, mass %9.3f", g, pg->nat, tmass);
-        if (pg->weight || EI_ENERGY_MINIMIZATION(ir->eI) || ir->eI == eiBD)
+                "Pull group %d: %5d atoms, mass %9.3f",
+                g, pg->params.nat, tmass);
+        if (pg->params.weight ||
+            EI_ENERGY_MINIMIZATION(ir->eI) || ir->eI == eiBD)
         {
             fprintf(fplog, ", weighted mass %9.3f", wmass*wmass/wwmass);
         }
@@ -1389,7 +1415,7 @@ static void init_pull_group_index(FILE *fplog, t_commrec *cr,
         ndim = 0;
         for (d = 0; d < DIM; d++)
         {
-            ndim += pulldim_con[d]*pg->nat;
+            ndim += pulldim_con[d]*pg->params.nat;
         }
         if (fplog && nfrozen > 0 && nfrozen < ndim)
         {
@@ -1404,35 +1430,92 @@ static void init_pull_group_index(FILE *fplog, t_commrec *cr,
     }
 }
 
-void init_pull(FILE *fplog, t_inputrec *ir, int nfile, const t_filenm fnm[],
-               gmx_mtop_t *mtop, t_commrec *cr, const output_env_t oenv, real lambda,
-               gmx_bool bOutFile, unsigned long Flags)
+struct pull_t *
+init_pull(FILE *fplog, const pull_params_t *pull_params, const t_inputrec *ir,
+          int nfile, const t_filenm fnm[],
+          gmx_mtop_t *mtop, t_commrec *cr, const output_env_t oenv, real lambda,
+          gmx_bool bOutFile, unsigned long Flags)
 {
-    t_pull       *pull;
-    t_pull_group *pgrp;
-    int           c, g, m;
+    struct pull_t *pull;
+    int            g, c, m;
+
+    snew(pull, 1);
 
-    pull = ir->pull;
+    /* Copy the pull parameters */
+    pull->params       = *pull_params;
+    /* Avoid pointer copies */
+    pull->params.group = NULL;
+    pull->params.coord = NULL;
+
+    pull->ncoord       = pull_params->ncoord;
+    pull->ngroup       = pull_params->ngroup;
+    snew(pull->coord, pull->ncoord);
+    snew(pull->group, pull->ngroup);
 
     pull->bPotential  = FALSE;
     pull->bConstraint = FALSE;
     pull->bCylinder   = FALSE;
+
+    for (g = 0; g < pull->ngroup; g++)
+    {
+        pull_group_work_t *pgrp;
+        int                i;
+
+        pgrp = &pull->group[g];
+
+        /* Copy the pull group parameters */
+        pgrp->params = pull_params->group[g];
+
+        /* Avoid pointer copies by allocating and copying arrays */
+        snew(pgrp->params.ind, pgrp->params.nat);
+        for (i = 0; i < pgrp->params.nat; i++)
+        {
+            pgrp->params.ind[i] = pull_params->group[g].ind[i];
+        }
+        if (pgrp->params.nweight > 0)
+        {
+            snew(pgrp->params.ind, pgrp->params.nweight);
+            for (i = 0; i < pgrp->params.nweight; i++)
+            {
+                pgrp->params.weight[i] = pull_params->group[g].weight[i];
+            }
+        }
+    }
+
     for (c = 0; c < pull->ncoord; c++)
     {
-        t_pull_coord *pcrd;
-        int           calc_com_start, calc_com_end, g;
+        pull_coord_work_t *pcrd;
+        int                calc_com_start, calc_com_end, g;
 
         pcrd = &pull->coord[c];
 
-        if (pcrd->eType == epullCONSTRAINT)
+        /* Copy all pull coordinate parameters */
+        pcrd->params = pull_params->coord[c];
+
+        switch (pcrd->params.eGeom)
+        {
+            case epullgDIST:
+            case epullgDIRRELATIVE:
+                /* Direction vector is determined at each step */
+                break;
+            case epullgDIR:
+            case epullgDIRPBC:
+            case epullgCYL:
+                copy_rvec(pull_params->coord[c].vec, pcrd->vec);
+                break;
+            default:
+                gmx_incons("Pull geometry not handled");
+        }
+
+        if (pcrd->params.eType == epullCONSTRAINT)
         {
             /* Check restrictions of the constraint pull code */
-            if (pcrd->eGeom == epullgCYL ||
-                pcrd->eGeom == epullgDIRRELATIVE)
+            if (pcrd->params.eGeom == epullgCYL ||
+                pcrd->params.eGeom == epullgDIRRELATIVE)
             {
                 gmx_fatal(FARGS, "Pulling of type %s can not be combined with geometry %s. Consider using pull type %s.",
-                          epull_names[pcrd->eType],
-                          epullg_names[pcrd->eGeom],
+                          epull_names[pcrd->params.eType],
+                          epullg_names[pcrd->params.eGeom],
                           epull_names[epullUMBRELLA]);
             }
 
@@ -1443,26 +1526,26 @@ void init_pull(FILE *fplog, t_inputrec *ir, int nfile, const t_filenm fnm[],
             pull->bPotential = TRUE;
         }
 
-        if (pcrd->eGeom == epullgCYL)
+        if (pcrd->params.eGeom == epullgCYL)
         {
             pull->bCylinder = TRUE;
         }
         /* We only need to calculate the plain COM of a group
          * when it is not only used as a cylinder group.
          */
-        calc_com_start = (pcrd->eGeom == epullgCYL         ? 1 : 0);
-        calc_com_end   = (pcrd->eGeom == epullgDIRRELATIVE ? 4 : 2);
+        calc_com_start = (pcrd->params.eGeom == epullgCYL         ? 1 : 0);
+        calc_com_end   = (pcrd->params.eGeom == epullgDIRRELATIVE ? 4 : 2);
 
         for (g = calc_com_start; g <= calc_com_end; g++)
         {
-            pull->group[pcrd->group[g]].bCalcCOM = TRUE;
+            pull->group[pcrd->params.group[g]].bCalcCOM = TRUE;
         }
 
         /* With non-zero rate the reference value is set at every step */
-        if (pcrd->rate == 0)
+        if (pcrd->params.rate == 0)
         {
             /* Initialize the constant reference value */
-            pcrd->value_ref = pcrd->init;
+            pcrd->value_ref = pcrd->params.init;
         }
     }
 
@@ -1481,8 +1564,8 @@ void init_pull(FILE *fplog, t_inputrec *ir, int nfile, const t_filenm fnm[],
         bAbs = FALSE;
         for (c = 0; c < pull->ncoord; c++)
         {
-            if (pull->group[pull->coord[c].group[0]].nat == 0 ||
-                pull->group[pull->coord[c].group[1]].nat == 0)
+            if (pull->group[pull->coord[c].params.group[0]].params.nat == 0 ||
+                pull->group[pull->coord[c].params.group[1]].params.nat == 0)
             {
                 bAbs = TRUE;
             }
@@ -1507,8 +1590,8 @@ void init_pull(FILE *fplog, t_inputrec *ir, int nfile, const t_filenm fnm[],
         bCos = FALSE;
         for (g = 0; g < pull->ngroup; g++)
         {
-            if (pull->group[g].nat > 1 &&
-                pull->group[g].pbcatom < 0)
+            if (pull->group[g].params.nat > 1 &&
+                pull->group[g].params.pbcatom < 0)
             {
                 /* We are using cosine weighting */
                 fprintf(fplog, "Cosine weighting is used for group %d\n", g);
@@ -1528,9 +1611,11 @@ void init_pull(FILE *fplog, t_inputrec *ir, int nfile, const t_filenm fnm[],
     pull->cosdim   = -1;
     for (g = 0; g < pull->ngroup; g++)
     {
+        pull_group_work_t *pgrp;
+
         pgrp           = &pull->group[g];
         pgrp->epgrppbc = epgrppbcNONE;
-        if (pgrp->nat > 0)
+        if (pgrp->params.nat > 0)
         {
             /* There is an issue when a group is used in multiple coordinates
              * and constraints are applied in different dimensions with atoms
@@ -1552,19 +1637,19 @@ void init_pull(FILE *fplog, t_inputrec *ir, int nfile, const t_filenm fnm[],
             clear_ivec(pulldim_con);
             for (c = 0; c < pull->ncoord; c++)
             {
-                if (pull->coord[c].group[0] == g ||
-                    pull->coord[c].group[1] == g ||
-                    (pull->coord[c].eGeom == epullgDIRRELATIVE &&
-                     (pull->coord[c].group[2] == g ||
-                      pull->coord[c].group[3] == g)))
+                if (pull->coord[c].params.group[0] == g ||
+                    pull->coord[c].params.group[1] == g ||
+                    (pull->coord[c].params.eGeom == epullgDIRRELATIVE &&
+                     (pull->coord[c].params.group[2] == g ||
+                      pull->coord[c].params.group[3] == g)))
                 {
                     for (m = 0; m < DIM; m++)
                     {
-                        if (pull->coord[c].dim[m] == 1)
+                        if (pull->coord[c].params.dim[m] == 1)
                         {
                             pulldim[m] = 1;
 
-                            if (pull->coord[c].eType == epullCONSTRAINT)
+                            if (pull->coord[c].params.eType == epullCONSTRAINT)
                             {
                                 bConstraint    = TRUE;
                                 pulldim_con[m] = 1;
@@ -1581,16 +1666,16 @@ void init_pull(FILE *fplog, t_inputrec *ir, int nfile, const t_filenm fnm[],
             for (m = 0; m < pull->npbcdim; m++)
             {
                 GMX_RELEASE_ASSERT(m <= DIM, "npbcdim must be <= the number of dimensions");
-                if (pulldim[m] == 1 && pgrp->nat > 1)
+                if (pulldim[m] == 1 && pgrp->params.nat > 1)
                 {
-                    if (pgrp->pbcatom >= 0)
+                    if (pgrp->params.pbcatom >= 0)
                     {
                         pgrp->epgrppbc = epgrppbcREFAT;
                         pull->bRefAt   = TRUE;
                     }
                     else
                     {
-                        if (pgrp->weight)
+                        if (pgrp->params.weight != NULL)
                         {
                             gmx_fatal(FARGS, "Pull groups can not have relative weights and cosine weighting at same time");
                         }
@@ -1625,13 +1710,13 @@ void init_pull(FILE *fplog, t_inputrec *ir, int nfile, const t_filenm fnm[],
 
         for (c = 0; c < pull->ncoord; c++)
         {
-            const t_pull_coord *pcrd;
+            const pull_coord_work_t *pcrd;
 
             pcrd = &pull->coord[c];
 
-            if (pcrd->eGeom == epullgCYL)
+            if (pcrd->params.eGeom == epullgCYL)
             {
-                if (pull->group[pcrd->group[0]].nat == 0)
+                if (pull->group[pcrd->params.group[0]].params.nat == 0)
                 {
                     gmx_fatal(FARGS, "A cylinder pull group is not supported when using absolute reference!\n");
                 }
@@ -1647,20 +1732,65 @@ void init_pull(FILE *fplog, t_inputrec *ir, int nfile, const t_filenm fnm[],
     pull->out_f = NULL;
     if (bOutFile)
     {
-        if (pull->nstxout > 0)
+        if (pull->params.nstxout > 0)
         {
             pull->out_x = open_pull_out(opt2fn("-px", nfile, fnm), pull, oenv,
                                         TRUE, Flags);
         }
-        if (pull->nstfout > 0)
+        if (pull->params.nstfout > 0)
         {
             pull->out_f = open_pull_out(opt2fn("-pf", nfile, fnm), pull, oenv,
                                         FALSE, Flags);
         }
     }
+
+    return pull;
+}
+
+static void destroy_pull_group(pull_group_work_t *pgrp)
+{
+    if (pgrp->ind_loc != pgrp->params.ind)
+    {
+        sfree(pgrp->ind_loc);
+    }
+    if (pgrp->weight_loc != pgrp->params.weight)
+    {
+        sfree(pgrp->weight_loc);
+    }
+    sfree(pgrp->mdw);
+    sfree(pgrp->dv);
+
+    sfree(pgrp->params.ind);
+    sfree(pgrp->params.weight);
+}
+
+static void destroy_pull(struct pull_t *pull)
+{
+    int i;
+
+    for (i = 0; i < pull->ngroup; i++)
+    {
+        destroy_pull_group(&pull->group[i]);
+    }
+    for (i = 0; i < pull->ncoord; i++)
+    {
+        if (pull->coord[i].params.eGeom == epullgCYL)
+        {
+            destroy_pull_group(&(pull->dyna[i]));
+        }
+    }
+    sfree(pull->group);
+    sfree(pull->dyna);
+    sfree(pull->coord);
+
+    sfree(pull->rbuf);
+    sfree(pull->dbuf);
+    sfree(pull->dbuf_cyl);
+
+    sfree(pull);
 }
 
-void finish_pull(t_pull *pull)
+void finish_pull(struct pull_t *pull)
 {
     if (pull->out_x)
     {
@@ -1670,4 +1800,16 @@ void finish_pull(t_pull *pull)
     {
         gmx_fio_fclose(pull->out_f);
     }
+
+    destroy_pull(pull);
+}
+
+gmx_bool pull_have_potential(const struct pull_t *pull)
+{
+    return pull->bPotential;
+}
+
+gmx_bool pull_have_constraint(const struct pull_t *pull)
+{
+    return pull->bConstraint;
 }