static void calc_x_av_stddev(int n, rvec *x, rvec av, rvec stddev) { dvec s1, s2; int i, d; clear_dvec(s1); clear_dvec(s2); for (i = 0; i < n; i++) { for (d = 0; d < DIM; d++) { s1[d] += x[i][d]; s2[d] += x[i][d]*x[i][d]; } } dsvmul(1.0/n, s1, s1); dsvmul(1.0/n, s2, s2); for (d = 0; d < DIM; d++) { av[d] = s1[d]; stddev[d] = std::sqrt(s2[d] - s1[d]*s1[d]); } }
static void init_pullgrp(t_pullgrp *pg,char *wbuf, bool bRef,int eGeom,char *s_vec) { double d; int n,m; dvec vec; pg->nweight = 0; while (sscanf(wbuf,"%lf %n",&d,&n) == 1) { if (pg->nweight % 100 == 0) { srenew(pg->weight,pg->nweight+100); } pg->weight[pg->nweight++] = d; wbuf += n; } if (!bRef) { if (eGeom == epullgDIST) { clear_dvec(vec); } else { string2dvec(s_vec,vec); if (eGeom == epullgDIR || eGeom == epullgCYL || (eGeom == epullgPOS && dnorm(vec) != 0)) /* Normalize the direction vector */ dsvmul(1/dnorm(vec),vec,vec); } for(m=0; m<DIM; m++) pg->vec[m] = vec[m]; } }
static void init_pull_coord(t_pull_coord *pcrd, int eGeom, const char *origin_buf, const char *vec_buf) { int m; dvec origin, vec; string2dvec(origin_buf, origin); if (pcrd->group[0] != 0 && dnorm(origin) > 0) { gmx_fatal(FARGS, "The pull origin can only be set with an absolute reference"); } if (eGeom == epullgDIST) { clear_dvec(vec); } else { string2dvec(vec_buf, vec); if (eGeom == epullgDIR || eGeom == epullgCYL) { /* Normalize the direction vector */ dsvmul(1/dnorm(vec), vec, vec); } } for (m = 0; m < DIM; m++) { pcrd->origin[m] = origin[m]; pcrd->vec[m] = vec[m]; } }
/* Determine center of structure from collective positions x */ extern void get_center(rvec x[], real weight[], const int nr, rvec rcenter) { dvec dcenter; double weight_sum, denom; weight_sum = get_sum_of_positions(x, weight, nr, dcenter); if (weight != NULL) denom = weight_sum; /* Divide by the sum of weight */ else denom = nr; /* Divide by the number of atoms */ dsvmul(1.0/denom, dcenter, dcenter); rcenter[XX] = dcenter[XX]; rcenter[YY] = dcenter[YY]; rcenter[ZZ] = dcenter[ZZ]; }
int gmx_polystat(int argc, char *argv[]) { const char *desc[] = { "[THISMODULE] plots static properties of polymers as a function of time", "and prints the average.[PAR]", "By default it determines the average end-to-end distance and radii", "of gyration of polymers. It asks for an index group and split this", "into molecules. The end-to-end distance is then determined using", "the first and the last atom in the index group for each molecules.", "For the radius of gyration the total and the three principal components", "for the average gyration tensor are written.", "With option [TT]-v[tt] the eigenvectors are written.", "With option [TT]-pc[tt] also the average eigenvalues of the individual", "gyration tensors are written.", "With option [TT]-i[tt] the mean square internal distances are", "written.[PAR]", "With option [TT]-p[tt] the persistence length is determined.", "The chosen index group should consist of atoms that are", "consecutively bonded in the polymer mainchains.", "The persistence length is then determined from the cosine of", "the angles between bonds with an index difference that is even,", "the odd pairs are not used, because straight polymer backbones", "are usually all trans and therefore only every second bond aligns.", "The persistence length is defined as number of bonds where", "the average cos reaches a value of 1/e. This point is determined", "by a linear interpolation of [LOG]<cos>[log]." }; static gmx_bool bMW = TRUE, bPC = FALSE; t_pargs pa[] = { { "-mw", FALSE, etBOOL, {&bMW}, "Use the mass weighting for radii of gyration" }, { "-pc", FALSE, etBOOL, {&bPC}, "Plot average eigenvalues" } }; t_filenm fnm[] = { { efTPR, nullptr, nullptr, ffREAD }, { efTRX, "-f", nullptr, ffREAD }, { efNDX, nullptr, nullptr, ffOPTRD }, { efXVG, "-o", "polystat", ffWRITE }, { efXVG, "-v", "polyvec", ffOPTWR }, { efXVG, "-p", "persist", ffOPTWR }, { efXVG, "-i", "intdist", ffOPTWR } }; #define NFILE asize(fnm) t_topology *top; gmx_output_env_t *oenv; int ePBC; int isize, *index, nmol, *molind, mol, nat_min = 0, nat_max = 0; char *grpname; t_trxstatus *status; real t; rvec *x, *bond = nullptr; matrix box; int natoms, i, j, frame, ind0, ind1, a, d, d2, ord[DIM] = {0}; dvec cm, sum_eig = {0, 0, 0}; double **gyr, **gyr_all, eig[DIM], **eigv; double sum_eed2, sum_eed2_tot, sum_gyro, sum_gyro_tot, sum_pers_tot; int *ninp = nullptr; double *sum_inp = nullptr, pers; double *intd, ymax, ymin; double mmol, m; char title[STRLEN]; FILE *out, *outv, *outp, *outi; const char *leg[8] = { "end to end", "<R\\sg\\N>", "<R\\sg\\N> eig1", "<R\\sg\\N> eig2", "<R\\sg\\N> eig3", "<R\\sg\\N eig1>", "<R\\sg\\N eig2>", "<R\\sg\\N eig3>" }; char **legp, buf[STRLEN]; gmx_rmpbc_t gpbc = nullptr; if (!parse_common_args(&argc, argv, PCA_CAN_VIEW | PCA_CAN_TIME | PCA_TIME_UNIT, NFILE, fnm, asize(pa), pa, asize(desc), desc, 0, nullptr, &oenv)) { return 0; } snew(top, 1); ePBC = read_tpx_top(ftp2fn(efTPR, NFILE, fnm), nullptr, box, &natoms, nullptr, nullptr, top); fprintf(stderr, "Select a group of polymer mainchain atoms:\n"); get_index(&top->atoms, ftp2fn_null(efNDX, NFILE, fnm), 1, &isize, &index, &grpname); snew(molind, top->mols.nr+1); nmol = 0; mol = -1; for (i = 0; i < isize; i++) { if (i == 0 || index[i] >= top->mols.index[mol+1]) { molind[nmol++] = i; do { mol++; } while (index[i] >= top->mols.index[mol+1]); } } molind[nmol] = i; nat_min = top->atoms.nr; nat_max = 0; for (mol = 0; mol < nmol; mol++) { nat_min = std::min(nat_min, molind[mol+1]-molind[mol]); nat_max = std::max(nat_max, molind[mol+1]-molind[mol]); } fprintf(stderr, "Group %s consists of %d molecules\n", grpname, nmol); fprintf(stderr, "Group size per molecule, min: %d atoms, max %d atoms\n", nat_min, nat_max); sprintf(title, "Size of %d polymers", nmol); out = xvgropen(opt2fn("-o", NFILE, fnm), title, output_env_get_xvgr_tlabel(oenv), "(nm)", oenv); xvgr_legend(out, bPC ? 8 : 5, leg, oenv); if (opt2bSet("-v", NFILE, fnm)) { outv = xvgropen(opt2fn("-v", NFILE, fnm), "Principal components", output_env_get_xvgr_tlabel(oenv), "(nm)", oenv); snew(legp, DIM*DIM); for (d = 0; d < DIM; d++) { for (d2 = 0; d2 < DIM; d2++) { sprintf(buf, "eig%d %c", d+1, 'x'+d2); legp[d*DIM+d2] = gmx_strdup(buf); } } xvgr_legend(outv, DIM*DIM, (const char**)legp, oenv); } else { outv = nullptr; } if (opt2bSet("-p", NFILE, fnm)) { outp = xvgropen(opt2fn("-p", NFILE, fnm), "Persistence length", output_env_get_xvgr_tlabel(oenv), "bonds", oenv); snew(bond, nat_max-1); snew(sum_inp, nat_min/2); snew(ninp, nat_min/2); } else { outp = nullptr; } if (opt2bSet("-i", NFILE, fnm)) { outi = xvgropen(opt2fn("-i", NFILE, fnm), "Internal distances", "n", "<R\\S2\\N(n)>/n (nm\\S2\\N)", oenv); i = index[molind[1]-1] - index[molind[0]]; /* Length of polymer -1 */ snew(intd, i); } else { intd = nullptr; outi = nullptr; } natoms = read_first_x(oenv, &status, ftp2fn(efTRX, NFILE, fnm), &t, &x, box); snew(gyr, DIM); snew(gyr_all, DIM); snew(eigv, DIM); for (d = 0; d < DIM; d++) { snew(gyr[d], DIM); snew(gyr_all[d], DIM); snew(eigv[d], DIM); } frame = 0; sum_eed2_tot = 0; sum_gyro_tot = 0; sum_pers_tot = 0; gpbc = gmx_rmpbc_init(&top->idef, ePBC, natoms); do { gmx_rmpbc(gpbc, natoms, box, x); sum_eed2 = 0; for (d = 0; d < DIM; d++) { clear_dvec(gyr_all[d]); } if (bPC) { clear_dvec(sum_eig); } if (outp) { for (i = 0; i < nat_min/2; i++) { sum_inp[i] = 0; ninp[i] = 0; } } for (mol = 0; mol < nmol; mol++) { ind0 = molind[mol]; ind1 = molind[mol+1]; /* Determine end to end distance */ sum_eed2 += distance2(x[index[ind0]], x[index[ind1-1]]); /* Determine internal distances */ if (outi) { calc_int_dist(intd, x, index[ind0], index[ind1-1]); } /* Determine the radius of gyration */ clear_dvec(cm); for (d = 0; d < DIM; d++) { clear_dvec(gyr[d]); } mmol = 0; for (i = ind0; i < ind1; i++) { a = index[i]; if (bMW) { m = top->atoms.atom[a].m; } else { m = 1; } mmol += m; for (d = 0; d < DIM; d++) { cm[d] += m*x[a][d]; for (d2 = 0; d2 < DIM; d2++) { gyr[d][d2] += m*x[a][d]*x[a][d2]; } } } dsvmul(1/mmol, cm, cm); for (d = 0; d < DIM; d++) { for (d2 = 0; d2 < DIM; d2++) { gyr[d][d2] = gyr[d][d2]/mmol - cm[d]*cm[d2]; gyr_all[d][d2] += gyr[d][d2]; } } if (bPC) { gyro_eigen(gyr, eig, eigv, ord); for (d = 0; d < DIM; d++) { sum_eig[d] += eig[ord[d]]; } } if (outp) { for (i = ind0; i < ind1-1; i++) { rvec_sub(x[index[i+1]], x[index[i]], bond[i-ind0]); unitv(bond[i-ind0], bond[i-ind0]); } for (i = ind0; i < ind1-1; i++) { for (j = 0; (i+j < ind1-1 && j < nat_min/2); j += 2) { sum_inp[j] += iprod(bond[i-ind0], bond[i-ind0+j]); ninp[j]++; } } } } sum_eed2 /= nmol; sum_gyro = 0; for (d = 0; d < DIM; d++) { for (d2 = 0; d2 < DIM; d2++) { gyr_all[d][d2] /= nmol; } sum_gyro += gyr_all[d][d]; } gyro_eigen(gyr_all, eig, eigv, ord); fprintf(out, "%10.3f %8.4f %8.4f %8.4f %8.4f %8.4f", t*output_env_get_time_factor(oenv), std::sqrt(sum_eed2), sqrt(sum_gyro), std::sqrt(eig[ord[0]]), std::sqrt(eig[ord[1]]), std::sqrt(eig[ord[2]])); if (bPC) { for (d = 0; d < DIM; d++) { fprintf(out, " %8.4f", std::sqrt(sum_eig[d]/nmol)); } } fprintf(out, "\n"); if (outv) { fprintf(outv, "%10.3f", t*output_env_get_time_factor(oenv)); for (d = 0; d < DIM; d++) { for (d2 = 0; d2 < DIM; d2++) { fprintf(outv, " %6.3f", eigv[ord[d]][d2]); } } fprintf(outv, "\n"); } sum_eed2_tot += sum_eed2; sum_gyro_tot += sum_gyro; if (outp) { i = -1; for (j = 0; j < nat_min/2; j += 2) { sum_inp[j] /= ninp[j]; if (i == -1 && sum_inp[j] <= std::exp(-1.0)) { i = j; } } if (i == -1) { pers = j; } else { /* Do linear interpolation on a log scale */ pers = i - 2.0 + 2.0*(std::log(sum_inp[i-2]) + 1.0)/(std::log(sum_inp[i-2]) - std::log(sum_inp[i])); } fprintf(outp, "%10.3f %8.4f\n", t*output_env_get_time_factor(oenv), pers); sum_pers_tot += pers; } frame++; } while (read_next_x(oenv, status, &t, x, box)); gmx_rmpbc_done(gpbc); close_trx(status); xvgrclose(out); if (outv) { xvgrclose(outv); } if (outp) { xvgrclose(outp); } sum_eed2_tot /= frame; sum_gyro_tot /= frame; sum_pers_tot /= frame; fprintf(stdout, "\nAverage end to end distance: %.3f (nm)\n", std::sqrt(sum_eed2_tot)); fprintf(stdout, "\nAverage radius of gyration: %.3f (nm)\n", std::sqrt(sum_gyro_tot)); if (opt2bSet("-p", NFILE, fnm)) { fprintf(stdout, "\nAverage persistence length: %.2f bonds\n", sum_pers_tot); } /* Handle printing of internal distances. */ if (outi) { if (output_env_get_print_xvgr_codes(oenv)) { fprintf(outi, "@ xaxes scale Logarithmic\n"); } ymax = -1; ymin = 1e300; j = index[molind[1]-1] - index[molind[0]]; /* Polymer length -1. */ for (i = 0; i < j; i++) { intd[i] /= (i + 1) * frame * nmol; if (intd[i] > ymax) { ymax = intd[i]; } if (intd[i] < ymin) { ymin = intd[i]; } } xvgr_world(outi, 1, ymin, j, ymax, oenv); for (i = 0; i < j; i++) { fprintf(outi, "%d %8.4f\n", i+1, intd[i]); } xvgrclose(outi); } do_view(oenv, opt2fn("-o", NFILE, fnm), "-nxy"); if (opt2bSet("-v", NFILE, fnm)) { do_view(oenv, opt2fn("-v", NFILE, fnm), "-nxy"); } if (opt2bSet("-p", NFILE, fnm)) { do_view(oenv, opt2fn("-p", NFILE, fnm), "-nxy"); } return 0; }
static void calc_cgcm_av_stddev(t_block *cgs, int n, rvec *x, rvec av, rvec stddev, t_commrec *cr_sum) { int *cgindex; dvec s1, s2; double buf[7]; int cg, d, k0, k1, k, nrcg; real inv_ncg; rvec cg_cm; clear_dvec(s1); clear_dvec(s2); cgindex = cgs->index; for (cg = 0; cg < n; cg++) { k0 = cgindex[cg]; k1 = cgindex[cg+1]; nrcg = k1 - k0; if (nrcg == 1) { copy_rvec(x[k0], cg_cm); } else { inv_ncg = 1.0/nrcg; clear_rvec(cg_cm); for (k = k0; (k < k1); k++) { rvec_inc(cg_cm, x[k]); } for (d = 0; (d < DIM); d++) { cg_cm[d] *= inv_ncg; } } for (d = 0; d < DIM; d++) { s1[d] += cg_cm[d]; s2[d] += cg_cm[d]*cg_cm[d]; } } if (cr_sum != NULL) { for (d = 0; d < DIM; d++) { buf[d] = s1[d]; buf[DIM+d] = s2[d]; } buf[6] = n; gmx_sumd(7, buf, cr_sum); for (d = 0; d < DIM; d++) { s1[d] = buf[d]; s2[d] = buf[DIM+d]; } n = (int)(buf[6] + 0.5); } dsvmul(1.0/n, s1, s1); dsvmul(1.0/n, s2, s2); for (d = 0; d < DIM; d++) { av[d] = s1[d]; stddev[d] = sqrt(s2[d] - s1[d]*s1[d]); } }
static void init_pull_coord(t_pull_coord *pcrd, int coord_index_for_output, char *dim_buf, const char *origin_buf, const char *vec_buf, warninp_t wi) { int m; dvec origin, vec; char buf[STRLEN]; if (pcrd->eType == epullCONSTRAINT && (pcrd->eGeom == epullgCYL || pcrd->eGeom == epullgDIRRELATIVE || pcrd->eGeom == epullgANGLE || pcrd->eGeom == epullgANGLEAXIS || pcrd->eGeom == epullgDIHEDRAL)) { 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[epullUMBRELLA]); } if (pcrd->eType == epullEXTERNAL) { if (pcrd->externalPotentialProvider[0] == '\0') { sprintf(buf, "The use of pull type '%s' for pull coordinate %d requires that the name of the module providing the potential external is set with the option %s%d%s", epull_names[pcrd->eType], coord_index_for_output, "pull-coord", coord_index_for_output, "-potential-provider"); warning_error(wi, buf); } if (pcrd->rate != 0) { sprintf(buf, "The use of pull type '%s' for pull coordinate %d requires that the pull rate is zero", epull_names[pcrd->eType], coord_index_for_output); warning_error(wi, buf); } if (pcrd->eGeom == epullgCYL) { /* Warn the user of a PBC restriction, caused by the fact that * there is no reference value with an external pull potential. */ sprintf(buf, "With pull type '%s' and geometry '%s', the distance component along the cylinder axis between atoms in the cylinder group and the COM of the pull group should be smaller than half the box length", epull_names[pcrd->eType], epullg_names[pcrd->eGeom]); warning_note(wi, buf); } } process_pull_dim(dim_buf, pcrd->dim, pcrd); string2dvec(origin_buf, origin); if (pcrd->group[0] != 0 && dnorm(origin) > 0) { gmx_fatal(FARGS, "The pull origin can only be set with an absolute reference"); } /* Check the given initial reference value and warn for dangerous values */ if (pcrd->eGeom == epullgDIST) { if (pcrd->bStart && pcrd->init < 0) { sprintf(buf, "The initial reference distance set by pull-coord-init is set to a negative value (%g) with geometry %s while distances need to be non-negative. " "This may work, since you have set pull-coord-start to 'yes' which modifies this value, but only for certain starting distances. " "If this is a mistake you may want to use geometry %s instead.", pcrd->init, EPULLGEOM(pcrd->eGeom), EPULLGEOM(epullgDIR)); warning(wi, buf); } } else if (pcrd->eGeom == epullgANGLE || pcrd->eGeom == epullgANGLEAXIS) { if (pcrd->bStart && (pcrd->init < 0 || pcrd->init > 180)) { /* This value of pcrd->init may be ok depending on pcrd->bStart which modifies pcrd->init later on */ sprintf(buf, "The initial reference angle set by pull-coord-init (%g) is outside of the allowed range [0, 180] degrees for geometry (%s). " "This may work, since you have set pull-coord-start to 'yes' which modifies this value, but only for certain starting angles.", pcrd->init, EPULLGEOM(pcrd->eGeom)); warning(wi, buf); } } else if (pcrd->eGeom == epullgDIHEDRAL) { if (pcrd->bStart && (pcrd->init < -180 || pcrd->init > 180)) { sprintf(buf, "The initial reference angle set by pull-coord-init (%g) is outside of the allowed range [-180, 180] degrees for geometry (%s). " "This may work, since you have set pull-coord-start to 'yes' which modifies this value, but only for certain starting angles.", pcrd->init, EPULLGEOM(pcrd->eGeom)); warning(wi, buf); } } /* Check and set the pull vector */ clear_dvec(vec); string2dvec(vec_buf, vec); if (pcrd->eGeom == epullgDIR || pcrd->eGeom == epullgCYL || pcrd->eGeom == epullgDIRPBC || pcrd->eGeom == epullgANGLEAXIS) { if (dnorm2(vec) == 0) { gmx_fatal(FARGS, "With pull geometry %s the pull vector can not be 0,0,0", epullg_names[pcrd->eGeom]); } for (int d = 0; d < DIM; d++) { if (vec[d] != 0 && pcrd->dim[d] == 0) { gmx_fatal(FARGS, "pull-coord-vec has non-zero %c-component while pull_dim for the %c-dimension is set to N", 'x'+d, 'x'+d); } } /* Normalize the direction vector */ dsvmul(1/dnorm(vec), vec, vec); } else /* This case is for are all the geometries where the pull vector is not used */ { if (dnorm2(vec) > 0) { sprintf(buf, "A pull vector is given (%g %g %g) but will not be used with geometry %s. If you really want to use this " "vector, consider using geometry %s instead.", vec[0], vec[1], vec[2], EPULLGEOM(pcrd->eGeom), pcrd->eGeom == epullgANGLE ? EPULLGEOM(epullgANGLEAXIS) : EPULLGEOM(epullgDIR)); warning(wi, buf); } } for (m = 0; m < DIM; m++) { pcrd->origin[m] = origin[m]; pcrd->vec[m] = vec[m]; } }
static void make_cyl_refgrps(t_commrec *cr, struct pull_t *pull, t_mdatoms *md, t_pbc *pbc, double t, rvec *x) { /* The size and stride per coord for the reduction buffer */ const int stride = 9; int c, i, ii, m, start, end; rvec g_x, dx, dir; double inv_cyl_r2; pull_comm_t *comm; gmx_ga2la_t *ga2la = NULL; comm = &pull->comm; if (comm->dbuf_cyl == NULL) { snew(comm->dbuf_cyl, pull->ncoord*stride); } if (cr && DOMAINDECOMP(cr)) { ga2la = cr->dd->ga2la; } start = 0; end = md->homenr; inv_cyl_r2 = 1.0/gmx::square(pull->params.cylinder_r); /* loop over all groups to make a reference group for each*/ for (c = 0; c < pull->ncoord; c++) { pull_coord_work_t *pcrd; double sum_a, wmass, wwmass; dvec radf_fac0, radf_fac1; pcrd = &pull->coord[c]; sum_a = 0; wmass = 0; wwmass = 0; clear_dvec(radf_fac0); clear_dvec(radf_fac1); if (pcrd->params.eGeom == epullgCYL) { pull_group_work_t *pref, *pgrp, *pdyna; /* pref will be the same group for all pull coordinates */ pref = &pull->group[pcrd->params.group[0]]; pgrp = &pull->group[pcrd->params.group[1]]; pdyna = &pull->dyna[c]; copy_rvec(pcrd->vec, dir); pdyna->nat_loc = 0; /* We calculate distances with respect to the reference location * of this cylinder group (g_x), which we already have now since * we reduced the other group COM over the ranks. This resolves * any PBC issues and we don't need to use a PBC-atom here. */ if (pcrd->params.rate != 0) { /* With rate=0, value_ref is set initially */ pcrd->value_ref = pcrd->params.init + pcrd->params.rate*t; } for (m = 0; m < DIM; m++) { g_x[m] = pgrp->x[m] - pcrd->vec[m]*pcrd->value_ref; } /* loop over all atoms in the main ref group */ for (i = 0; i < pref->params.nat; i++) { ii = pref->params.ind[i]; if (ga2la) { if (!ga2la_get_home(ga2la, pref->params.ind[i], &ii)) { ii = -1; } } if (ii >= start && ii < end) { double dr2, dr2_rel, inp; dvec dr; pbc_dx_aiuc(pbc, x[ii], g_x, dx); inp = iprod(dir, dx); dr2 = 0; for (m = 0; m < DIM; m++) { /* Determine the radial components */ dr[m] = dx[m] - inp*dir[m]; dr2 += dr[m]*dr[m]; } dr2_rel = dr2*inv_cyl_r2; if (dr2_rel < 1) { double mass, weight, dweight_r; dvec mdw; /* add to index, to sum of COM, to weight array */ if (pdyna->nat_loc >= pdyna->nalloc_loc) { pdyna->nalloc_loc = over_alloc_large(pdyna->nat_loc+1); srenew(pdyna->ind_loc, pdyna->nalloc_loc); srenew(pdyna->weight_loc, pdyna->nalloc_loc); srenew(pdyna->mdw, pdyna->nalloc_loc); srenew(pdyna->dv, pdyna->nalloc_loc); } pdyna->ind_loc[pdyna->nat_loc] = ii; mass = md->massT[ii]; /* The radial weight function is 1-2x^2+x^4, * where x=r/cylinder_r. Since this function depends * on the radial component, we also get radial forces * on both groups. */ weight = 1 + (-2 + dr2_rel)*dr2_rel; dweight_r = (-4 + 4*dr2_rel)*inv_cyl_r2; pdyna->weight_loc[pdyna->nat_loc] = weight; sum_a += mass*weight*inp; wmass += mass*weight; wwmass += mass*weight*weight; dsvmul(mass*dweight_r, dr, mdw); copy_dvec(mdw, pdyna->mdw[pdyna->nat_loc]); /* Currently we only have the axial component of the * distance (inp) up to an unkown offset. We add this * offset after the reduction needs to determine the * COM of the cylinder group. */ pdyna->dv[pdyna->nat_loc] = inp; for (m = 0; m < DIM; m++) { radf_fac0[m] += mdw[m]; radf_fac1[m] += mdw[m]*inp; } pdyna->nat_loc++; } } } } comm->dbuf_cyl[c*stride+0] = wmass; comm->dbuf_cyl[c*stride+1] = wwmass; comm->dbuf_cyl[c*stride+2] = sum_a; comm->dbuf_cyl[c*stride+3] = radf_fac0[XX]; comm->dbuf_cyl[c*stride+4] = radf_fac0[YY]; comm->dbuf_cyl[c*stride+5] = radf_fac0[ZZ]; comm->dbuf_cyl[c*stride+6] = radf_fac1[XX]; comm->dbuf_cyl[c*stride+7] = radf_fac1[YY]; comm->dbuf_cyl[c*stride+8] = radf_fac1[ZZ]; } if (cr != NULL && PAR(cr)) { /* Sum the contributions over the ranks */ pull_reduce_double(cr, comm, pull->ncoord*stride, comm->dbuf_cyl); } for (c = 0; c < pull->ncoord; c++) { pull_coord_work_t *pcrd; pcrd = &pull->coord[c]; if (pcrd->params.eGeom == epullgCYL) { pull_group_work_t *pdyna, *pgrp; double wmass, wwmass, dist; pdyna = &pull->dyna[c]; pgrp = &pull->group[pcrd->params.group[1]]; wmass = comm->dbuf_cyl[c*stride+0]; wwmass = comm->dbuf_cyl[c*stride+1]; pdyna->mwscale = 1.0/wmass; /* Cylinder pulling can't be used with constraints, but we set * wscale and invtm anyhow, in case someone would like to use them. */ pdyna->wscale = wmass/wwmass; pdyna->invtm = wwmass/(wmass*wmass); /* We store the deviation of the COM from the reference location * used above, since we need it when we apply the radial forces * to the atoms in the cylinder group. */ pcrd->cyl_dev = 0; for (m = 0; m < DIM; m++) { g_x[m] = pgrp->x[m] - pcrd->vec[m]*pcrd->value_ref; dist = -pcrd->vec[m]*comm->dbuf_cyl[c*stride+2]*pdyna->mwscale; pdyna->x[m] = g_x[m] - dist; pcrd->cyl_dev += dist; } /* Now we know the exact COM of the cylinder reference group, * we can determine the radial force factor (ffrad) that when * multiplied with the axial pull force will give the radial * force on the pulled (non-cylinder) group. */ for (m = 0; m < DIM; m++) { pcrd->ffrad[m] = (comm->dbuf_cyl[c*stride+6+m] + comm->dbuf_cyl[c*stride+3+m]*pcrd->cyl_dev)/wmass; } if (debug) { fprintf(debug, "Pull cylinder group %d:%8.3f%8.3f%8.3f m:%8.3f\n", c, pdyna->x[0], pdyna->x[1], pdyna->x[2], 1.0/pdyna->invtm); fprintf(debug, "ffrad %8.3f %8.3f %8.3f\n", pcrd->ffrad[XX], pcrd->ffrad[YY], pcrd->ffrad[ZZ]); } } } }
static void init_pull_coord(t_pull_coord *pcrd, char *dim_buf, const char *origin_buf, const char *vec_buf, warninp_t wi) { int m; dvec origin, vec; char buf[STRLEN]; if (pcrd->eType == epullCONSTRAINT && (pcrd->eGeom == epullgCYL || pcrd->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[epullUMBRELLA]); } process_pull_dim(dim_buf, pcrd->dim); string2dvec(origin_buf, origin); if (pcrd->group[0] != 0 && dnorm(origin) > 0) { gmx_fatal(FARGS, "The pull origin can only be set with an absolute reference"); } /* Check and set the pull vector */ clear_dvec(vec); if (pcrd->eGeom == epullgDIST) { if (pcrd->init < 0) { sprintf(buf, "The initial pull distance is negative with geometry %s, while a distance can not be negative. Use geometry %s instead.", EPULLGEOM(pcrd->eGeom), EPULLGEOM(epullgDIR)); warning_error(wi, buf); } /* TODO: With a positive init but a negative rate things could still * go wrong, but it might be fine if you don't pull too far. * We should give a warning or note when there is only one pull dim * active, since that is usually the problematic case when you should * be using direction. We will do this later, since an already planned * generalization of the pull code makes pull dim available here. */ } else if (pcrd->eGeom != epullgDIRRELATIVE) { string2dvec(vec_buf, vec); if (dnorm2(vec) == 0) { gmx_fatal(FARGS, "With pull geometry %s the pull vector can not be 0,0,0", epullg_names[pcrd->eGeom]); } if (pcrd->eGeom == epullgDIR || pcrd->eGeom == epullgCYL) { /* Normalize the direction vector */ dsvmul(1/dnorm(vec), vec, vec); } } for (m = 0; m < DIM; m++) { pcrd->origin[m] = origin[m]; pcrd->vec[m] = vec[m]; } }
/* 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); }
/* 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); }