static void pull_set_pbcatoms(t_commrec *cr, t_pull *pull, t_mdatoms *md, rvec *x, rvec *x_pbc) { int g, n, m; n = 0; for (g = 0; g < pull->ngroup; g++) { if ((g == 0 && PULL_CYL(pull)) || pull->group[g].pbcatom == -1) { clear_rvec(x_pbc[g]); } else { pull_set_pbcatom(cr, &pull->group[g], md, x, x_pbc[g]); for (m = 0; m < DIM; m++) { if (pull->dim[m] == 0) { x_pbc[g][m] = 0.0; } } n++; } } if (cr && PAR(cr) && n > 0) { /* Sum over the nodes to get x_pbc from the home node of pbcatom */ gmx_sum(pull->ngroup*DIM, x_pbc[0], cr); } }
static void pull_print_x(FILE *out, t_pull *pull, double t) { int c; const t_pull_coord *pcrd; fprintf(out, "%.4f", t); for (c = 0; c < pull->ncoord; c++) { pcrd = &pull->coord[c]; if (pull->bPrintRef) { if (PULL_CYL(pull)) { pull_print_group_x(out, pull->dim, &pull->dyna[c]); } else { pull_print_group_x(out, pull->dim, &pull->group[pcrd->group[0]]); } } pull_print_coord_dr(out, pull->dim, pcrd); } fprintf(out, "\n"); }
static void get_pull_coord_dr(const t_pull *pull, int coord_ind, const t_pbc *pbc, double t, dvec dr) { double md2; const t_pull_coord *pcrd; if (pull->eGeom == epullgDIRPBC) { md2 = -1; } else { md2 = max_pull_distance2(pull, pbc); } pcrd = &pull->coord[coord_ind]; low_get_pull_coord_dr(pull, pcrd, pbc, t, pull->group[pcrd->group[1]].x, PULL_CYL(pull) ? pull->dyna[coord_ind].x : pull->group[pcrd->group[0]].x, md2, dr); }
static void pull_print_x(FILE *out,t_pull *pull,double t) { int g; fprintf(out, "%.4f", t); if (PULL_CYL(pull)) { for (g=1; g<1+pull->ngrp; g++) { pull_print_x_grp(out,TRUE ,pull->dim,&pull->dyna[g]); pull_print_x_grp(out,FALSE,pull->dim,&pull->grp[g]); } } else { for (g=0; g<1+pull->ngrp; g++) { if (pull->grp[g].nat > 0) { pull_print_x_grp(out,g==0,pull->dim,&pull->grp[g]); } } } fprintf(out,"\n"); }
static void get_pullgrp_dr(const t_pull *pull,const t_pbc *pbc,int g,double t, dvec dr) { double md2; if (pull->eGeom == epullgDIRPBC) { md2 = -1; } else { md2 = max_pull_distance2(pull,pbc); } get_pullgrps_dr(pull,pbc,g,t, pull->grp[g].x, PULL_CYL(pull) ? pull->dyna[g].x : pull->grp[0].x, md2, dr); }
/* Apply forces in a mass weighted fashion */ static void apply_forces(t_pull * pull, t_mdatoms * md, rvec *f) { int c; const t_pull_coord *pcrd; for (c = 0; c < pull->ncoord; c++) { pcrd = &pull->coord[c]; if (PULL_CYL(pull)) { apply_forces_grp(&pull->dyna[c], md, pcrd->f, -1, f); } else { if (pull->group[pcrd->group[0]].nat > 0) { apply_forces_grp(&pull->group[pcrd->group[0]], md, pcrd->f, -1, f); } } apply_forces_grp(&pull->group[pcrd->group[1]], md, pcrd->f, 1, f); } }
/* Apply forces in a mass weighted fashion */ static void apply_forces(t_pull * pull, t_mdatoms * md, gmx_ga2la_t ga2la, rvec *f) { int i; t_pullgrp *pgrp; for(i=1; i<pull->ngrp+1; i++) { pgrp = &(pull->grp[i]); apply_forces_grp(pgrp,md,ga2la,pgrp->f,1,f); if (pull->grp[0].nat) { if (PULL_CYL(pull)) { apply_forces_grp(&(pull->dyna[i]),md,ga2la,pgrp->f,-1,f); } else { apply_forces_grp(&(pull->grp[0]),md,ga2la,pgrp->f,-1,f); } } } }
/* calculates center of mass of selection index from all coordinates x */ void pull_calc_coms(t_commrec *cr, t_pull *pull, t_mdatoms *md, t_pbc *pbc, double t, rvec x[], rvec *xp) { int g, i, ii, m; real mass, w, wm, twopi_box = 0; double wmass, wwmass, invwmass; dvec com, comp; double cm, sm, cmp, smp, ccm, csm, ssm, csw, snw; rvec *xx[2], x_pbc = {0, 0, 0}, dx; t_pull_group *pgrp; if (pull->rbuf == NULL) { snew(pull->rbuf, pull->ngroup); } if (pull->dbuf == NULL) { snew(pull->dbuf, 3*pull->ngroup); } if (pull->bRefAt) { pull_set_pbcatoms(cr, pull, md, x, pull->rbuf); } if (pull->cosdim >= 0) { for (m = pull->cosdim+1; m < pull->npbcdim; m++) { if (pbc->box[m][pull->cosdim] != 0) { gmx_fatal(FARGS, "Can not do cosine weighting for trilinic dimensions"); } } twopi_box = 2.0*M_PI/pbc->box[pull->cosdim][pull->cosdim]; } for (g = 0; g < pull->ngroup; g++) { pgrp = &pull->group[g]; clear_dvec(com); clear_dvec(comp); wmass = 0; wwmass = 0; cm = 0; sm = 0; cmp = 0; smp = 0; ccm = 0; csm = 0; ssm = 0; if (!(g == 0 && PULL_CYL(pull))) { if (pgrp->epgrppbc == epgrppbcREFAT) { /* Set the pbc atom */ copy_rvec(pull->rbuf[g], x_pbc); } w = 1; for (i = 0; i < pgrp->nat_loc; i++) { ii = pgrp->ind_loc[i]; mass = md->massT[ii]; if (pgrp->epgrppbc != epgrppbcCOS) { if (pgrp->weight_loc) { w = pgrp->weight_loc[i]; } wm = w*mass; wmass += wm; wwmass += wm*w; if (pgrp->epgrppbc == epgrppbcNONE) { /* Plain COM: sum the coordinates */ for (m = 0; m < DIM; m++) { com[m] += wm*x[ii][m]; } if (xp) { for (m = 0; m < DIM; m++) { comp[m] += wm*xp[ii][m]; } } } else { /* Sum the difference with the reference atom */ pbc_dx(pbc, x[ii], x_pbc, dx); for (m = 0; m < DIM; m++) { com[m] += wm*dx[m]; } if (xp) { /* For xp add the difference between xp and x to dx, * such that we use the same periodic image, * also when xp has a large displacement. */ for (m = 0; m < DIM; m++) { comp[m] += wm*(dx[m] + xp[ii][m] - x[ii][m]); } } } } else { /* Determine cos and sin sums */ csw = cos(x[ii][pull->cosdim]*twopi_box); snw = sin(x[ii][pull->cosdim]*twopi_box); cm += csw*mass; sm += snw*mass; ccm += csw*csw*mass; csm += csw*snw*mass; ssm += snw*snw*mass; if (xp) { csw = cos(xp[ii][pull->cosdim]*twopi_box); snw = sin(xp[ii][pull->cosdim]*twopi_box); cmp += csw*mass; smp += snw*mass; } } } } /* Copy local sums to a buffer for global summing */ switch (pgrp->epgrppbc) { case epgrppbcNONE: case epgrppbcREFAT: copy_dvec(com, pull->dbuf[g*3]); copy_dvec(comp, pull->dbuf[g*3+1]); pull->dbuf[g*3+2][0] = wmass; pull->dbuf[g*3+2][1] = wwmass; pull->dbuf[g*3+2][2] = 0; break; case epgrppbcCOS: pull->dbuf[g*3 ][0] = cm; pull->dbuf[g*3 ][1] = sm; pull->dbuf[g*3 ][2] = 0; pull->dbuf[g*3+1][0] = ccm; pull->dbuf[g*3+1][1] = csm; pull->dbuf[g*3+1][2] = ssm; pull->dbuf[g*3+2][0] = cmp; pull->dbuf[g*3+2][1] = smp; pull->dbuf[g*3+2][2] = 0; break; } } if (cr && PAR(cr)) { /* Sum the contributions over the nodes */ gmx_sumd(pull->ngroup*3*DIM, pull->dbuf[0], cr); } for (g = 0; g < pull->ngroup; g++) { pgrp = &pull->group[g]; if (pgrp->nat > 0 && !(g == 0 && PULL_CYL(pull))) { if (pgrp->epgrppbc != epgrppbcCOS) { /* Determine the inverse mass */ wmass = pull->dbuf[g*3+2][0]; wwmass = pull->dbuf[g*3+2][1]; invwmass = 1/wmass; /* invtm==0 signals a frozen group, so then we should keep it zero */ if (pgrp->invtm > 0) { pgrp->wscale = wmass/wwmass; pgrp->invtm = 1.0/(pgrp->wscale*wmass); } /* Divide by the total mass */ for (m = 0; m < DIM; m++) { pgrp->x[m] = pull->dbuf[g*3 ][m]*invwmass; if (xp) { pgrp->xp[m] = pull->dbuf[g*3+1][m]*invwmass; } if (pgrp->epgrppbc == epgrppbcREFAT) { pgrp->x[m] += pull->rbuf[g][m]; if (xp) { pgrp->xp[m] += pull->rbuf[g][m]; } } } } else { /* Determine the optimal location of the cosine weight */ csw = pull->dbuf[g*3][0]; snw = pull->dbuf[g*3][1]; pgrp->x[pull->cosdim] = atan2_0_2pi(snw, csw)/twopi_box; /* Set the weights for the local atoms */ wmass = sqrt(csw*csw + snw*snw); wwmass = (pull->dbuf[g*3+1][0]*csw*csw + pull->dbuf[g*3+1][1]*csw*snw + pull->dbuf[g*3+1][2]*snw*snw)/(wmass*wmass); pgrp->wscale = wmass/wwmass; pgrp->invtm = 1.0/(pgrp->wscale*wmass); /* Set the weights for the local atoms */ csw *= pgrp->invtm; snw *= pgrp->invtm; for (i = 0; i < pgrp->nat_loc; i++) { ii = pgrp->ind_loc[i]; pgrp->weight_loc[i] = csw*cos(twopi_box*x[ii][pull->cosdim]) + snw*sin(twopi_box*x[ii][pull->cosdim]); } if (xp) { csw = pull->dbuf[g*3+2][0]; snw = pull->dbuf[g*3+2][1]; pgrp->xp[pull->cosdim] = atan2_0_2pi(snw, csw)/twopi_box; } } if (debug) { fprintf(debug, "Pull group %d wmass %f wwmass %f invtm %f\n", g, wmass, wwmass, pgrp->invtm); } } } if (PULL_CYL(pull)) { /* Calculate the COMs for the cyclinder reference groups */ make_cyl_refgrps(cr, pull, md, pbc, t, x, xp); } }
/* Apply constraint using SHAKE */ static void do_constraint(t_pull *pull, t_pbc *pbc, rvec *x, rvec *v, gmx_bool bMaster, tensor vir, double dt, double t) { dvec *r_ij; /* x[i] com of i in prev. step. Obeys constr. -> r_ij[i] */ dvec unc_ij; /* xp[i] com of i this step, before constr. -> unc_ij */ dvec *rnew; /* current 'new' positions of the groups */ double *dr_tot; /* the total update of the coords */ double ref; dvec vec; double d0, inpr; double lambda, rm, mass, invdt = 0; gmx_bool bConverged_all, bConverged = FALSE; int niter = 0, g, c, ii, j, m, max_iter = 100; double a; dvec f; /* the pull force */ dvec tmp, tmp3; t_pull_group *pdyna, *pgrp0, *pgrp1; t_pull_coord *pcrd; snew(r_ij, pull->ncoord); snew(dr_tot, pull->ncoord); snew(rnew, pull->ngroup); /* copy the current unconstrained positions for use in iterations. We iterate until rinew[i] and rjnew[j] obey the constraints. Then rinew - pull.x_unc[i] is the correction dr to group i */ for (g = 0; g < pull->ngroup; g++) { copy_dvec(pull->group[g].xp, rnew[g]); } if (PULL_CYL(pull)) { /* There is only one pull coordinate and reference group */ copy_dvec(pull->dyna[0].xp, rnew[pull->coord[0].group[0]]); } /* Determine the constraint directions from the old positions */ for (c = 0; c < pull->ncoord; c++) { get_pull_coord_dr(pull, c, pbc, t, r_ij[c]); /* Store the difference vector at time t for printing */ copy_dvec(r_ij[c], pull->coord[c].dr); if (debug) { fprintf(debug, "Pull coord %d dr %f %f %f\n", c, r_ij[c][XX], r_ij[c][YY], r_ij[c][ZZ]); } if (pull->eGeom == epullgDIR || pull->eGeom == epullgDIRPBC) { /* Select the component along vec */ a = 0; for (m = 0; m < DIM; m++) { a += pull->coord[c].vec[m]*r_ij[c][m]; } for (m = 0; m < DIM; m++) { r_ij[c][m] = a*pull->coord[c].vec[m]; } } } bConverged_all = FALSE; while (!bConverged_all && niter < max_iter) { bConverged_all = TRUE; /* loop over all constraints */ for (c = 0; c < pull->ncoord; c++) { dvec dr0, dr1; pcrd = &pull->coord[c]; pgrp0 = &pull->group[pcrd->group[0]]; pgrp1 = &pull->group[pcrd->group[1]]; /* Get the current difference vector */ low_get_pull_coord_dr(pull, pcrd, pbc, t, rnew[pcrd->group[1]], rnew[pcrd->group[0]], -1, unc_ij); ref = pcrd->init + pcrd->rate*t; if (debug) { fprintf(debug, "Pull coord %d, iteration %d\n", c, niter); } rm = 1.0/(pgrp0->invtm + pgrp1->invtm); switch (pull->eGeom) { case epullgDIST: if (ref <= 0) { gmx_fatal(FARGS, "The pull constraint reference distance for group %d is <= 0 (%f)", c, ref); } { double q, c_a, c_b, c_c; c_a = diprod(r_ij[c], r_ij[c]); c_b = diprod(unc_ij, r_ij[c])*2; c_c = diprod(unc_ij, unc_ij) - dsqr(ref); if (c_b < 0) { q = -0.5*(c_b - sqrt(c_b*c_b - 4*c_a*c_c)); lambda = -q/c_a; } else { q = -0.5*(c_b + sqrt(c_b*c_b - 4*c_a*c_c)); lambda = -c_c/q; } if (debug) { fprintf(debug, "Pull ax^2+bx+c=0: a=%e b=%e c=%e lambda=%e\n", c_a, c_b, c_c, lambda); } } /* The position corrections dr due to the constraints */ dsvmul(-lambda*rm*pgrp1->invtm, r_ij[c], dr1); dsvmul( lambda*rm*pgrp0->invtm, r_ij[c], dr0); dr_tot[c] += -lambda*dnorm(r_ij[c]); break; case epullgDIR: case epullgDIRPBC: case epullgCYL: /* A 1-dimensional constraint along a vector */ a = 0; for (m = 0; m < DIM; m++) { vec[m] = pcrd->vec[m]; a += unc_ij[m]*vec[m]; } /* Select only the component along the vector */ dsvmul(a, vec, unc_ij); lambda = a - ref; if (debug) { fprintf(debug, "Pull inpr %e lambda: %e\n", a, lambda); } /* The position corrections dr due to the constraints */ dsvmul(-lambda*rm*pgrp1->invtm, vec, dr1); dsvmul( lambda*rm*pgrp0->invtm, vec, dr0); dr_tot[c] += -lambda; break; } /* DEBUG */ if (debug) { int g0, g1; g0 = pcrd->group[0]; g1 = pcrd->group[1]; low_get_pull_coord_dr(pull, pcrd, pbc, t, rnew[g1], rnew[g0], -1, tmp); low_get_pull_coord_dr(pull, pcrd, pbc, t, dr1, dr0, -1, tmp3); fprintf(debug, "Pull cur %8.5f %8.5f %8.5f j:%8.5f %8.5f %8.5f d: %8.5f\n", rnew[g0][0], rnew[g0][1], rnew[g0][2], rnew[g1][0], rnew[g1][1], rnew[g1][2], dnorm(tmp)); fprintf(debug, "Pull ref %8s %8s %8s %8s %8s %8s d: %8.5f\n", "", "", "", "", "", "", ref); fprintf(debug, "Pull cor %8.5f %8.5f %8.5f j:%8.5f %8.5f %8.5f d: %8.5f\n", dr0[0], dr0[1], dr0[2], dr1[0], dr1[1], dr1[2], dnorm(tmp3)); } /* END DEBUG */ /* Update the COMs with dr */ dvec_inc(rnew[pcrd->group[1]], dr1); dvec_inc(rnew[pcrd->group[0]], dr0); } /* Check if all constraints are fullfilled now */ for (c = 0; c < pull->ncoord; c++) { pcrd = &pull->coord[c]; low_get_pull_coord_dr(pull, pcrd, pbc, t, rnew[pcrd->group[1]], rnew[pcrd->group[0]], -1, unc_ij); switch (pull->eGeom) { case epullgDIST: bConverged = fabs(dnorm(unc_ij) - ref) < pull->constr_tol; break; case epullgDIR: case epullgDIRPBC: case epullgCYL: for (m = 0; m < DIM; m++) { vec[m] = pcrd->vec[m]; } inpr = diprod(unc_ij, vec); dsvmul(inpr, vec, unc_ij); bConverged = fabs(diprod(unc_ij, vec) - ref) < pull->constr_tol; break; } if (!bConverged) { if (debug) { fprintf(debug, "NOT CONVERGED YET: Group %d:" "d_ref = %f, current d = %f\n", g, ref, dnorm(unc_ij)); } bConverged_all = FALSE; } } niter++; /* if after all constraints are dealt with and bConverged is still TRUE we're finished, if not we do another iteration */ } if (niter > max_iter) { gmx_fatal(FARGS, "Too many iterations for constraint run: %d", niter); } /* DONE ITERATING, NOW UPDATE COORDINATES AND CALC. CONSTRAINT FORCES */ if (v) { invdt = 1/dt; } /* update atoms in the groups */ for (g = 0; g < pull->ngroup; g++) { const t_pull_group *pgrp; dvec dr; if (PULL_CYL(pull) && g == pull->coord[0].group[0]) { pgrp = &pull->dyna[0]; } else { pgrp = &pull->group[g]; } /* get the final constraint displacement dr for group g */ dvec_sub(rnew[g], pgrp->xp, dr); /* select components of dr */ for (m = 0; m < DIM; m++) { dr[m] *= pull->dim[m]; } /* update the atom positions */ copy_dvec(dr, tmp); for (j = 0; j < pgrp->nat_loc; j++) { ii = pgrp->ind_loc[j]; if (pgrp->weight_loc) { dsvmul(pgrp->wscale*pgrp->weight_loc[j], dr, tmp); } for (m = 0; m < DIM; m++) { x[ii][m] += tmp[m]; } if (v) { for (m = 0; m < DIM; m++) { v[ii][m] += invdt*tmp[m]; } } } } /* calculate the constraint forces, used for output and virial only */ for (c = 0; c < pull->ncoord; c++) { pcrd = &pull->coord[c]; pcrd->f_scal = dr_tot[c]/((pull->group[pcrd->group[0]].invtm + pull->group[pcrd->group[1]].invtm)*dt*dt); if (vir && bMaster) { double f_invr; /* Add the pull contribution to the virial */ f_invr = pcrd->f_scal/dnorm(r_ij[c]); for (j = 0; j < DIM; j++) { for (m = 0; m < DIM; m++) { vir[j][m] -= 0.5*f_invr*r_ij[c][j]*r_ij[c][m]; } } } } /* finished! I hope. Give back some memory */ sfree(r_ij); sfree(dr_tot); sfree(rnew); }
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) { t_pull *pull; t_pull_group *pgrp; int c, g, start = 0, end = 0, m; pull = ir->pull; pull->ePBC = ir->ePBC; switch (pull->ePBC) { case epbcNONE: pull->npbcdim = 0; break; case epbcXY: pull->npbcdim = 2; break; default: pull->npbcdim = 3; break; } if (fplog) { gmx_bool bAbs, bCos; 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) { bAbs = TRUE; } } fprintf(fplog, "\nWill apply %s COM pulling in geometry '%s'\n", EPULLTYPE(ir->ePull), EPULLGEOM(pull->eGeom)); fprintf(fplog, "with %d pull coordinate%s and %d group%s\n", pull->ncoord, pull->ncoord == 1 ? "" : "s", pull->ngroup, pull->ngroup == 1 ? "" : "s"); if (bAbs) { fprintf(fplog, "with an absolute reference\n"); } bCos = FALSE; for (g = 0; g < pull->ngroup; g++) { if (pull->group[g].nat > 1 && pull->group[g].pbcatom < 0) { /* We are using cosine weighting */ fprintf(fplog, "Cosine weighting is used for group %d\n", g); bCos = TRUE; } } if (bCos) { please_cite(fplog, "Engin2010"); } } /* We always add the virial contribution, * except for geometry = direction_periodic where this is impossible. */ pull->bVirial = (pull->eGeom != epullgDIRPBC); if (getenv("GMX_NO_PULLVIR") != NULL) { if (fplog) { fprintf(fplog, "Found env. var., will not add the virial contribution of the COM pull forces\n"); } pull->bVirial = FALSE; } if (cr && PARTDECOMP(cr)) { pd_at_range(cr, &start, &end); } pull->rbuf = NULL; pull->dbuf = NULL; pull->dbuf_cyl = NULL; pull->bRefAt = FALSE; pull->cosdim = -1; for (g = 0; g < pull->ngroup; g++) { pgrp = &pull->group[g]; pgrp->epgrppbc = epgrppbcNONE; if (pgrp->nat > 0) { /* Determine if we need to take PBC into account for calculating * the COM's of the pull groups. */ for (m = 0; m < pull->npbcdim; m++) { if (pull->dim[m] && pgrp->nat > 1) { if (pgrp->pbcatom >= 0) { pgrp->epgrppbc = epgrppbcREFAT; pull->bRefAt = TRUE; } else { if (pgrp->weight) { gmx_fatal(FARGS, "Pull groups can not have relative weights and cosine weighting at same time"); } pgrp->epgrppbc = epgrppbcCOS; if (pull->cosdim >= 0 && pull->cosdim != m) { gmx_fatal(FARGS, "Can only use cosine weighting with pulling in one dimension (use mdp option pull_dim)"); } pull->cosdim = m; } } } /* Set the indices */ init_pull_group_index(fplog, cr, start, end, g, pgrp, pull->dim, mtop, ir, lambda); if (PULL_CYL(pull) && pgrp->invtm == 0) { gmx_fatal(FARGS, "Can not have frozen atoms in a cylinder pull group"); } } else { /* Absolute reference, set the inverse mass to zero */ pgrp->invtm = 0; pgrp->wscale = 1; } } /* if we use dynamic reference groups, do some initialising for them */ if (PULL_CYL(pull)) { if (ir->ePull == epullCONSTRAINT && pull->ncoord > 1) { /* We can't easily update the single reference group with multiple * constraints. This would require recalculating COMs. */ gmx_fatal(FARGS, "Constraint COM pulling supports only one coordinate with geometry=cylinder, you can use umbrella pulling with multiple coordinates"); } for (c = 0; c < pull->ncoord; c++) { if (pull->group[pull->coord[c].group[0]].nat == 0) { gmx_fatal(FARGS, "Dynamic reference groups are not supported when using absolute reference!\n"); } } snew(pull->dyna, pull->ncoord); } /* Only do I/O when we are doing dynamics and if we are the MASTER */ pull->out_x = NULL; pull->out_f = NULL; if (bOutFile) { if (pull->nstxout > 0) { pull->out_x = open_pull_out(opt2fn("-px", nfile, fnm), pull, oenv, TRUE, Flags); } if (pull->nstfout > 0) { pull->out_f = open_pull_out(opt2fn("-pf", nfile, fnm), pull, oenv, FALSE, Flags); } } }
/* Apply constraint using SHAKE */ static void do_constraint(t_pull *pull, t_mdatoms *md, t_pbc *pbc, rvec *x, rvec *v, gmx_bool bMaster, tensor vir, double dt, double t) { dvec *r_ij; /* x[i] com of i in prev. step. Obeys constr. -> r_ij[i] */ dvec unc_ij; /* xp[i] com of i this step, before constr. -> unc_ij */ dvec *rinew; /* current 'new' position of group i */ dvec *rjnew; /* current 'new' position of group j */ dvec ref,vec; double d0,inpr; double lambda, rm, mass, invdt=0; gmx_bool bConverged_all,bConverged=FALSE; int niter=0,g,ii,j,m,max_iter=100; double q,a,b,c; /* for solving the quadratic equation, see Num. Recipes in C ed 2 p. 184 */ dvec *dr; /* correction for group i */ dvec ref_dr; /* correction for group j */ dvec f; /* the pull force */ dvec tmp,tmp3; t_pullgrp *pdyna,*pgrp,*pref; snew(r_ij,pull->ngrp+1); if (PULL_CYL(pull)) { snew(rjnew,pull->ngrp+1); } else { snew(rjnew,1); } snew(dr,pull->ngrp+1); snew(rinew,pull->ngrp+1); /* copy the current unconstrained positions for use in iterations. We iterate until rinew[i] and rjnew[j] obey the constraints. Then rinew - pull.x_unc[i] is the correction dr to group i */ for(g=1; g<1+pull->ngrp; g++) { copy_dvec(pull->grp[g].xp,rinew[g]); } if (PULL_CYL(pull)) { for(g=1; g<1+pull->ngrp; g++) { copy_dvec(pull->dyna[g].xp,rjnew[g]); } } else { copy_dvec(pull->grp[0].xp,rjnew[0]); } /* Determine the constraint directions from the old positions */ for(g=1; g<1+pull->ngrp; g++) { get_pullgrp_dr(pull,pbc,g,t,r_ij[g]); /* Store the difference vector at time t for printing */ copy_dvec(r_ij[g],pull->grp[g].dr); if (debug) { fprintf(debug,"Pull group %d dr %f %f %f\n", g,r_ij[g][XX],r_ij[g][YY],r_ij[g][ZZ]); } if (pull->eGeom == epullgDIR || pull->eGeom == epullgDIRPBC) { /* Select the component along vec */ a = 0; for(m=0; m<DIM; m++) { a += pull->grp[g].vec[m]*r_ij[g][m]; } for(m=0; m<DIM; m++) { r_ij[g][m] = a*pull->grp[g].vec[m]; } } } bConverged_all = FALSE; while (!bConverged_all && niter < max_iter) { bConverged_all = TRUE; /* loop over all constraints */ for(g=1; g<1+pull->ngrp; g++) { pgrp = &pull->grp[g]; if (PULL_CYL(pull)) pref = &pull->dyna[g]; else pref = &pull->grp[0]; /* Get the current difference vector */ get_pullgrps_dr(pull,pbc,g,t,rinew[g],rjnew[PULL_CYL(pull) ? g : 0], -1,unc_ij); if (pull->eGeom == epullgPOS) { for(m=0; m<DIM; m++) { ref[m] = pgrp->init[m] + pgrp->rate*t*pgrp->vec[m]; } } else { ref[0] = pgrp->init[0] + pgrp->rate*t; /* Keep the compiler happy */ ref[1] = 0; ref[2] = 0; } if (debug) { fprintf(debug,"Pull group %d, iteration %d\n",g,niter); } rm = 1.0/(pull->grp[g].invtm + pref->invtm); switch (pull->eGeom) { case epullgDIST: if (ref[0] <= 0) { gmx_fatal(FARGS,"The pull constraint reference distance for group %d is <= 0 (%f)",g,ref[0]); } a = diprod(r_ij[g],r_ij[g]); b = diprod(unc_ij,r_ij[g])*2; c = diprod(unc_ij,unc_ij) - dsqr(ref[0]); if (b < 0) { q = -0.5*(b - sqrt(b*b - 4*a*c)); lambda = -q/a; } else { q = -0.5*(b + sqrt(b*b - 4*a*c)); lambda = -c/q; } if (debug) { fprintf(debug, "Pull ax^2+bx+c=0: a=%e b=%e c=%e lambda=%e\n", a,b,c,lambda); } /* The position corrections dr due to the constraints */ dsvmul(-lambda*rm*pgrp->invtm, r_ij[g], dr[g]); dsvmul( lambda*rm*pref->invtm, r_ij[g], ref_dr); break; case epullgDIR: case epullgDIRPBC: case epullgCYL: /* A 1-dimensional constraint along a vector */ a = 0; for(m=0; m<DIM; m++) { vec[m] = pgrp->vec[m]; a += unc_ij[m]*vec[m]; } /* Select only the component along the vector */ dsvmul(a,vec,unc_ij); lambda = a - ref[0]; if (debug) { fprintf(debug,"Pull inpr %e lambda: %e\n",a,lambda); } /* The position corrections dr due to the constraints */ dsvmul(-lambda*rm*pull->grp[g].invtm, vec, dr[g]); dsvmul( lambda*rm* pref->invtm, vec,ref_dr); break; case epullgPOS: for(m=0; m<DIM; m++) { if (pull->dim[m]) { lambda = r_ij[g][m] - ref[m]; /* The position corrections dr due to the constraints */ dr[g][m] = -lambda*rm*pull->grp[g].invtm; ref_dr[m] = lambda*rm*pref->invtm; } else { dr[g][m] = 0; ref_dr[m] = 0; } } break; } /* DEBUG */ if (debug) { j = (PULL_CYL(pull) ? g : 0); get_pullgrps_dr(pull,pbc,g,t,rinew[g],rjnew[j],-1,tmp); get_pullgrps_dr(pull,pbc,g,t,dr[g] ,ref_dr ,-1,tmp3); fprintf(debug, "Pull cur %8.5f %8.5f %8.5f j:%8.5f %8.5f %8.5f d: %8.5f\n", rinew[g][0],rinew[g][1],rinew[g][2], rjnew[j][0],rjnew[j][1],rjnew[j][2], dnorm(tmp)); if (pull->eGeom == epullgPOS) { fprintf(debug, "Pull ref %8.5f %8.5f %8.5f\n", pgrp->vec[0],pgrp->vec[1],pgrp->vec[2]); } else { fprintf(debug, "Pull ref %8s %8s %8s %8s %8s %8s d: %8.5f %8.5f %8.5f\n", "","","","","","",ref[0],ref[1],ref[2]); } fprintf(debug, "Pull cor %8.5f %8.5f %8.5f j:%8.5f %8.5f %8.5f d: %8.5f\n", dr[g][0],dr[g][1],dr[g][2], ref_dr[0],ref_dr[1],ref_dr[2], dnorm(tmp3)); fprintf(debug, "Pull cor %10.7f %10.7f %10.7f\n", dr[g][0],dr[g][1],dr[g][2]); } /* END DEBUG */ /* Update the COMs with dr */ dvec_inc(rinew[g], dr[g]); dvec_inc(rjnew[PULL_CYL(pull) ? g : 0],ref_dr); } /* Check if all constraints are fullfilled now */ for(g=1; g<1+pull->ngrp; g++) { pgrp = &pull->grp[g]; get_pullgrps_dr(pull,pbc,g,t,rinew[g],rjnew[PULL_CYL(pull) ? g : 0], -1,unc_ij); switch (pull->eGeom) { case epullgDIST: bConverged = fabs(dnorm(unc_ij) - ref[0]) < pull->constr_tol; break; case epullgDIR: case epullgDIRPBC: case epullgCYL: for(m=0; m<DIM; m++) { vec[m] = pgrp->vec[m]; } inpr = diprod(unc_ij,vec); dsvmul(inpr,vec,unc_ij); bConverged = fabs(diprod(unc_ij,vec) - ref[0]) < pull->constr_tol; break; case epullgPOS: bConverged = TRUE; for(m=0; m<DIM; m++) { if (pull->dim[m] && fabs(unc_ij[m] - ref[m]) >= pull->constr_tol) { bConverged = FALSE; } } break; } if (!bConverged) { if (debug) { fprintf(debug,"NOT CONVERGED YET: Group %d:" "d_ref = %f %f %f, current d = %f\n", g,ref[0],ref[1],ref[2],dnorm(unc_ij)); } bConverged_all = FALSE; } } niter++; /* if after all constraints are dealt with and bConverged is still TRUE we're finished, if not we do another iteration */ } if (niter > max_iter) { gmx_fatal(FARGS,"Too many iterations for constraint run: %d",niter); } /* DONE ITERATING, NOW UPDATE COORDINATES AND CALC. CONSTRAINT FORCES */ if (v) { invdt = 1/dt; } /* update the normal groups */ for(g=1; g<1+pull->ngrp; g++) { pgrp = &pull->grp[g]; /* get the final dr and constraint force for group i */ dvec_sub(rinew[g],pgrp->xp,dr[g]); /* select components of dr */ for(m=0; m<DIM; m++) { dr[g][m] *= pull->dim[m]; } dsvmul(1.0/(pgrp->invtm*dt*dt),dr[g],f); dvec_inc(pgrp->f,f); switch (pull->eGeom) { case epullgDIST: for(m=0; m<DIM; m++) { pgrp->f_scal += r_ij[g][m]*f[m]/dnorm(r_ij[g]); } break; case epullgDIR: case epullgDIRPBC: case epullgCYL: for(m=0; m<DIM; m++) { pgrp->f_scal += pgrp->vec[m]*f[m]; } break; case epullgPOS: break; } if (vir && bMaster) { /* Add the pull contribution to the virial */ for(j=0; j<DIM; j++) { for(m=0; m<DIM; m++) { vir[j][m] -= 0.5*f[j]*r_ij[g][m]; } } } /* update the atom positions */ copy_dvec(dr[g],tmp); for(j=0;j<pgrp->nat_loc;j++) { ii = pgrp->ind_loc[j]; if (pgrp->weight_loc) { dsvmul(pgrp->wscale*pgrp->weight_loc[j],dr[g],tmp); } for(m=0; m<DIM; m++) { x[ii][m] += tmp[m]; } if (v) { for(m=0; m<DIM; m++) { v[ii][m] += invdt*tmp[m]; } } } } /* update the reference groups */ if (PULL_CYL(pull)) { /* update the dynamic reference groups */ for(g=1; g<1+pull->ngrp; g++) { pdyna = &pull->dyna[g]; dvec_sub(rjnew[g],pdyna->xp,ref_dr); /* select components of ref_dr */ for(m=0; m<DIM; m++) { ref_dr[m] *= pull->dim[m]; } for(j=0;j<pdyna->nat_loc;j++) { /* reset the atoms with dr, weighted by w_i */ dsvmul(pdyna->wscale*pdyna->weight_loc[j],ref_dr,tmp); ii = pdyna->ind_loc[j]; for(m=0; m<DIM; m++) { x[ii][m] += tmp[m]; } if (v) { for(m=0; m<DIM; m++) { v[ii][m] += invdt*tmp[m]; } } } } } else { pgrp = &pull->grp[0]; /* update the reference group */ dvec_sub(rjnew[0],pgrp->xp, ref_dr); /* select components of ref_dr */ for(m=0;m<DIM;m++) { ref_dr[m] *= pull->dim[m]; } copy_dvec(ref_dr,tmp); for(j=0; j<pgrp->nat_loc;j++) { ii = pgrp->ind_loc[j]; if (pgrp->weight_loc) { dsvmul(pgrp->wscale*pgrp->weight_loc[j],ref_dr,tmp); } for(m=0; m<DIM; m++) { x[ii][m] += tmp[m]; } if (v) { for(m=0; m<DIM; m++) { v[ii][m] += invdt*tmp[m]; } } } } /* finished! I hope. Give back some memory */ sfree(r_ij); sfree(rinew); sfree(rjnew); sfree(dr); }
static FILE *open_pull_out(const char *fn,t_pull *pull,const output_env_t oenv, gmx_bool bCoord, unsigned long Flags) { FILE *fp; int nsets,g,m; char **setname,buf[10]; if(Flags & MD_APPENDFILES) { fp = gmx_fio_fopen(fn,"a+"); } else { fp = gmx_fio_fopen(fn,"w+"); if (bCoord) { xvgr_header(fp,"Pull COM", "Time (ps)","Position (nm)", exvggtXNY,oenv); } else { xvgr_header(fp,"Pull force","Time (ps)","Force (kJ/mol/nm)", exvggtXNY,oenv); } snew(setname,(1+pull->ngrp)*DIM); nsets = 0; for(g=0; g<1+pull->ngrp; g++) { if (pull->grp[g].nat > 0 && (g > 0 || (bCoord && !PULL_CYL(pull)))) { if (bCoord || pull->eGeom == epullgPOS) { if (PULL_CYL(pull)) { for(m=0; m<DIM; m++) { if (pull->dim[m]) { sprintf(buf,"%d %s%c",g,"c",'X'+m); setname[nsets] = strdup(buf); nsets++; } } } for(m=0; m<DIM; m++) { if (pull->dim[m]) { sprintf(buf,"%d %s%c", g,(bCoord && g > 0)?"d":"",'X'+m); setname[nsets] = strdup(buf); nsets++; } } } else { sprintf(buf,"%d",g); setname[nsets] = strdup(buf); nsets++; } } } if (bCoord || nsets > 1) { xvgr_legend(fp,nsets,(const char**)setname,oenv); } for(g=0; g<nsets; g++) { sfree(setname[g]); } sfree(setname); } return fp; }