/* 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); } }
/* calculates center of mass of selection index from all coordinates x */ void pull_calc_coms(t_commrec *cr, struct pull_t *pull, t_mdatoms *md, t_pbc *pbc, double t, rvec x[], rvec *xp) { int g; real twopi_box = 0; pull_comm_t *comm; comm = &pull->comm; if (comm->rbuf == NULL) { snew(comm->rbuf, pull->ngroup); } if (comm->dbuf == NULL) { snew(comm->dbuf, 3*pull->ngroup); } if (pull->bRefAt && pull->bSetPBCatoms) { pull_set_pbcatoms(cr, pull, x, comm->rbuf); if (cr != NULL && DOMAINDECOMP(cr)) { /* We can keep these PBC reference coordinates fixed for nstlist * steps, since atoms won't jump over PBC. * This avoids a global reduction at the next nstlist-1 steps. * Note that the exact values of the pbc reference coordinates * are irrelevant, as long all atoms in the group are within * half a box distance of the reference coordinate. */ pull->bSetPBCatoms = FALSE; } } if (pull->cosdim >= 0) { int m; assert(pull->npbcdim <= DIM); 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++) { pull_group_work_t *pgrp; pgrp = &pull->group[g]; if (pgrp->bCalcCOM) { if (pgrp->epgrppbc != epgrppbcCOS) { dvec com, comp; double wmass, wwmass; rvec x_pbc = { 0, 0, 0 }; int i; clear_dvec(com); clear_dvec(comp); wmass = 0; wwmass = 0; if (pgrp->epgrppbc == epgrppbcREFAT) { /* Set the pbc atom */ copy_rvec(comm->rbuf[g], x_pbc); } for (i = 0; i < pgrp->nat_loc; i++) { int ii, m; real mass, wm; ii = pgrp->ind_loc[i]; mass = md->massT[ii]; if (pgrp->weight_loc == NULL) { wm = mass; wmass += wm; } else { real w; 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 { rvec dx; /* 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]); } } } } /* We do this check after the loop above to avoid more nesting. * If we have a single-atom group the mass is irrelevant, so * we can remove the mass factor to avoid division by zero. * Note that with constraint pulling the mass does matter, but * in that case a check group mass != 0 has been done before. */ if (pgrp->params.nat == 1 && pgrp->nat_loc == 1 && wmass == 0) { int m; /* Copy the single atom coordinate */ for (m = 0; m < DIM; m++) { com[m] = x[pgrp->ind_loc[0]][m]; } /* Set all mass factors to 1 to get the correct COM */ wmass = 1; wwmass = 1; } if (pgrp->weight_loc == NULL) { wwmass = wmass; } /* Copy local sums to a buffer for global summing */ copy_dvec(com, comm->dbuf[g*3]); copy_dvec(comp, comm->dbuf[g*3 + 1]); comm->dbuf[g*3 + 2][0] = wmass; comm->dbuf[g*3 + 2][1] = wwmass; comm->dbuf[g*3 + 2][2] = 0; } else { /* Cosine weighting geometry */ double cm, sm, cmp, smp, ccm, csm, ssm, csw, snw; int i; cm = 0; sm = 0; cmp = 0; smp = 0; ccm = 0; csm = 0; ssm = 0; for (i = 0; i < pgrp->nat_loc; i++) { int ii; real mass; ii = pgrp->ind_loc[i]; mass = md->massT[ii]; /* 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 */ comm->dbuf[g*3 ][0] = cm; comm->dbuf[g*3 ][1] = sm; comm->dbuf[g*3 ][2] = 0; comm->dbuf[g*3+1][0] = ccm; comm->dbuf[g*3+1][1] = csm; comm->dbuf[g*3+1][2] = ssm; comm->dbuf[g*3+2][0] = cmp; comm->dbuf[g*3+2][1] = smp; comm->dbuf[g*3+2][2] = 0; } } } pull_reduce_double(cr, comm, pull->ngroup*3*DIM, comm->dbuf[0]); for (g = 0; g < pull->ngroup; g++) { pull_group_work_t *pgrp; pgrp = &pull->group[g]; if (pgrp->params.nat > 0 && pgrp->bCalcCOM) { if (pgrp->epgrppbc != epgrppbcCOS) { double wmass, wwmass; int m; /* Determine the inverse mass */ wmass = comm->dbuf[g*3+2][0]; wwmass = comm->dbuf[g*3+2][1]; pgrp->mwscale = 1.0/wmass; /* invtm==0 signals a frozen group, so then we should keep it zero */ if (pgrp->invtm != 0) { pgrp->wscale = wmass/wwmass; pgrp->invtm = wwmass/(wmass*wmass); } /* Divide by the total mass */ for (m = 0; m < DIM; m++) { pgrp->x[m] = comm->dbuf[g*3 ][m]*pgrp->mwscale; if (xp) { pgrp->xp[m] = comm->dbuf[g*3+1][m]*pgrp->mwscale; } if (pgrp->epgrppbc == epgrppbcREFAT) { pgrp->x[m] += comm->rbuf[g][m]; if (xp) { pgrp->xp[m] += comm->rbuf[g][m]; } } } } else { /* Cosine weighting geometry */ double csw, snw, wmass, wwmass; int i, ii; /* Determine the optimal location of the cosine weight */ csw = comm->dbuf[g*3][0]; snw = comm->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 = (comm->dbuf[g*3+1][0]*csw*csw + comm->dbuf[g*3+1][1]*csw*snw + comm->dbuf[g*3+1][2]*snw*snw)/(wmass*wmass); pgrp->mwscale = 1.0/wmass; pgrp->wscale = wmass/wwmass; pgrp->invtm = wwmass/(wmass*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 = comm->dbuf[g*3+2][0]; snw = comm->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 invtm %f\n", g, 1.0/pgrp->mwscale, pgrp->invtm); } } } if (pull->bCylinder) { /* Calculate the COMs for the cyclinder reference groups */ make_cyl_refgrps(cr, pull, md, pbc, t, x); } }