int gmx_gyrate(int argc, char *argv[]) { const char *desc[] = { "[THISMODULE] computes the radius of gyration of a molecule", "and the radii of gyration about the [IT]x[it]-, [IT]y[it]- and [IT]z[it]-axes,", "as a function of time. The atoms are explicitly mass weighted.[PAR]", "The axis components corresponds to the mass-weighted root-mean-square", "of the radii components orthogonal to each axis, for example:[PAR]", "Rg(x) = sqrt((sum_i m_i (R_i(y)^2 + R_i(z)^2))/(sum_i m_i)).[PAR]", "With the [TT]-nmol[tt] option the radius of gyration will be calculated", "for multiple molecules by splitting the analysis group in equally", "sized parts.[PAR]", "With the option [TT]-nz[tt] 2D radii of gyration in the [IT]x-y[it] plane", "of slices along the [IT]z[it]-axis are calculated." }; static int nmol = 1, nz = 0; static gmx_bool bQ = FALSE, bRot = FALSE, bMOI = FALSE; t_pargs pa[] = { { "-nmol", FALSE, etINT, {&nmol}, "The number of molecules to analyze" }, { "-q", FALSE, etBOOL, {&bQ}, "Use absolute value of the charge of an atom as weighting factor instead of mass" }, { "-p", FALSE, etBOOL, {&bRot}, "Calculate the radii of gyration about the principal axes." }, { "-moi", FALSE, etBOOL, {&bMOI}, "Calculate the moments of inertia (defined by the principal axes)." }, { "-nz", FALSE, etINT, {&nz}, "Calculate the 2D radii of gyration of this number of slices along the z-axis" }, }; FILE *out; t_trxstatus *status; t_topology top; int ePBC; rvec *x, *x_s; rvec xcm, gvec, gvec1; matrix box, trans; gmx_bool bACF; real **moi_trans = nullptr; int max_moi = 0, delta_moi = 100; rvec d, d1; /* eigenvalues of inertia tensor */ real t, t0, tm, gyro; int natoms; char *grpname; int j, m, gnx, nam, mol; int *index; gmx_output_env_t *oenv; gmx_rmpbc_t gpbc = nullptr; const char *leg[] = { "Rg", "Rg\\sX\\N", "Rg\\sY\\N", "Rg\\sZ\\N" }; const char *legI[] = { "Itot", "I1", "I2", "I3" }; #define NLEG asize(leg) t_filenm fnm[] = { { efTRX, "-f", nullptr, ffREAD }, { efTPS, nullptr, nullptr, ffREAD }, { efNDX, nullptr, nullptr, ffOPTRD }, { efXVG, nullptr, "gyrate", ffWRITE }, { efXVG, "-acf", "moi-acf", ffOPTWR }, }; #define NFILE asize(fnm) int npargs; t_pargs *ppa; npargs = asize(pa); ppa = add_acf_pargs(&npargs, pa); if (!parse_common_args(&argc, argv, PCA_CAN_TIME | PCA_CAN_VIEW, NFILE, fnm, npargs, ppa, asize(desc), desc, 0, nullptr, &oenv)) { sfree(ppa); return 0; } bACF = opt2bSet("-acf", NFILE, fnm); if (bACF && nmol != 1) { gmx_fatal(FARGS, "Can only do acf with nmol=1"); } bRot = bRot || bMOI || bACF; /* if (nz > 0) bMOI = TRUE; */ if (bRot) { printf("Will rotate system along principal axes\n"); snew(moi_trans, DIM); } if (bMOI) { printf("Will print moments of inertia\n"); bQ = FALSE; } if (bQ) { printf("Will print radius normalised by charge\n"); } read_tps_conf(ftp2fn(efTPS, NFILE, fnm), &top, &ePBC, &x, nullptr, box, TRUE); get_index(&top.atoms, ftp2fn_null(efNDX, NFILE, fnm), 1, &gnx, &index, &grpname); if (nmol > gnx || gnx % nmol != 0) { gmx_fatal(FARGS, "The number of atoms in the group (%d) is not a multiple of nmol (%d)", gnx, nmol); } nam = gnx/nmol; natoms = read_first_x(oenv, &status, ftp2fn(efTRX, NFILE, fnm), &t, &x, box); snew(x_s, natoms); j = 0; t0 = t; if (bQ) { out = xvgropen(ftp2fn(efXVG, NFILE, fnm), "Radius of Charge (total and around axes)", "Time (ps)", "Rg (nm)", oenv); } else if (bMOI) { out = xvgropen(ftp2fn(efXVG, NFILE, fnm), "Moments of inertia (total and around axes)", "Time (ps)", "I (a.m.u. nm\\S2\\N)", oenv); } else { out = xvgropen(ftp2fn(efXVG, NFILE, fnm), "Radius of gyration (total and around axes)", "Time (ps)", "Rg (nm)", oenv); } if (bMOI) { xvgr_legend(out, NLEG, legI, oenv); } else { if (bRot) { if (output_env_get_print_xvgr_codes(oenv)) { fprintf(out, "@ subtitle \"Axes are principal component axes\"\n"); } } xvgr_legend(out, NLEG, leg, oenv); } if (nz == 0) { gpbc = gmx_rmpbc_init(&top.idef, ePBC, natoms); } do { if (nz == 0) { gmx_rmpbc_copy(gpbc, natoms, box, x, x_s); } gyro = 0; clear_rvec(gvec); clear_rvec(gvec1); clear_rvec(d); clear_rvec(d1); for (mol = 0; mol < nmol; mol++) { tm = sub_xcm(nz == 0 ? x_s : x, nam, index+mol*nam, top.atoms.atom, xcm, bQ); if (nz == 0) { gyro += calc_gyro(x_s, nam, index+mol*nam, top.atoms.atom, tm, gvec1, d1, bQ, bRot, bMOI, trans); } else { calc_gyro_z(x, box, nam, index+mol*nam, top.atoms.atom, nz, t, out); } rvec_inc(gvec, gvec1); rvec_inc(d, d1); } if (nmol > 0) { gyro /= nmol; svmul(1.0/nmol, gvec, gvec); svmul(1.0/nmol, d, d); } if (nz == 0) { if (bRot) { if (j >= max_moi) { max_moi += delta_moi; for (m = 0; (m < DIM); m++) { srenew(moi_trans[m], max_moi*DIM); } } for (m = 0; (m < DIM); m++) { copy_rvec(trans[m], moi_trans[m]+DIM*j); } fprintf(out, "%10g %10g %10g %10g %10g\n", t, gyro, d[XX], d[YY], d[ZZ]); } else { fprintf(out, "%10g %10g %10g %10g %10g\n", t, gyro, gvec[XX], gvec[YY], gvec[ZZ]); } } j++; } while (read_next_x(oenv, status, &t, x, box)); close_trx(status); if (nz == 0) { gmx_rmpbc_done(gpbc); } xvgrclose(out); if (bACF) { int mode = eacVector; do_autocorr(opt2fn("-acf", NFILE, fnm), oenv, "Moment of inertia vector ACF", j, 3, moi_trans, (t-t0)/j, mode, FALSE); do_view(oenv, opt2fn("-acf", NFILE, fnm), "-nxy"); } do_view(oenv, ftp2fn(efXVG, NFILE, fnm), "-nxy"); return 0; }
int gmx_rotacf(int argc, char *argv[]) { const char *desc[] = { "[THISMODULE] calculates the rotational correlation function", "for molecules. Atom triplets (i,j,k) must be given in the index", "file, defining two vectors ij and jk. The rotational ACF", "is calculated as the autocorrelation function of the vector", "n = ij x jk, i.e. the cross product of the two vectors.", "Since three atoms span a plane, the order of the three atoms", "does not matter. Optionally, by invoking the [TT]-d[tt] switch, you can", "calculate the rotational correlation function for linear molecules", "by specifying atom pairs (i,j) in the index file.", "[PAR]", "EXAMPLES[PAR]", "[TT]gmx rotacf -P 1 -nparm 2 -fft -n index -o rotacf-x-P1", "-fa expfit-x-P1 -beginfit 2.5 -endfit 20.0[tt][PAR]", "This will calculate the rotational correlation function using a first", "order Legendre polynomial of the angle of a vector defined by the index", "file. The correlation function will be fitted from 2.5 ps until 20.0 ps", "to a two-parameter exponential." }; static gmx_bool bVec = FALSE, bAver = TRUE; t_pargs pa[] = { { "-d", FALSE, etBOOL, {&bVec}, "Use index doublets (vectors) for correlation function instead of triplets (planes)" }, { "-aver", FALSE, etBOOL, {&bAver}, "Average over molecules" } }; t_trxstatus *status; int isize; atom_id *index; char *grpname; rvec *x, *x_s; matrix box; real **c1; rvec xij, xjk, n; int i, m, teller, n_alloc, natoms, nvec, ai, aj, ak; unsigned long mode; real t, t0, t1, dt; gmx_rmpbc_t gpbc = NULL; t_topology *top; int ePBC; t_filenm fnm[] = { { efTRX, "-f", NULL, ffREAD }, { efTPR, NULL, NULL, ffREAD }, { efNDX, NULL, NULL, ffREAD }, { efXVG, "-o", "rotacf", ffWRITE } }; #define NFILE asize(fnm) int npargs; t_pargs *ppa; output_env_t oenv; npargs = asize(pa); ppa = add_acf_pargs(&npargs, pa); if (!parse_common_args(&argc, argv, PCA_CAN_VIEW | PCA_CAN_TIME, NFILE, fnm, npargs, ppa, asize(desc), desc, 0, NULL, &oenv)) { return 0; } rd_index(ftp2fn(efNDX, NFILE, fnm), 1, &isize, &index, &grpname); if (bVec) { nvec = isize/2; } else { nvec = isize/3; } if (((isize % 3) != 0) && !bVec) { gmx_fatal(FARGS, "number of index elements not multiple of 3, " "these can not be atom triplets\n"); } if (((isize % 2) != 0) && bVec) { gmx_fatal(FARGS, "number of index elements not multiple of 2, " "these can not be atom doublets\n"); } top = read_top(ftp2fn(efTPR, NFILE, fnm), &ePBC); snew(c1, nvec); for (i = 0; (i < nvec); i++) { c1[i] = NULL; } n_alloc = 0; natoms = read_first_x(oenv, &status, ftp2fn(efTRX, NFILE, fnm), &t, &x, box); snew(x_s, natoms); gpbc = gmx_rmpbc_init(&(top->idef), ePBC, natoms); /* Start the loop over frames */ t0 = t; teller = 0; do { if (teller >= n_alloc) { n_alloc += 100; for (i = 0; (i < nvec); i++) { srenew(c1[i], DIM*n_alloc); } } t1 = t; /* Remove periodicity */ gmx_rmpbc_copy(gpbc, natoms, box, x, x_s); /* Compute crossproducts for all vectors, if triplets. * else, just get the vectors in case of doublets. */ if (bVec == FALSE) { for (i = 0; (i < nvec); i++) { ai = index[3*i]; aj = index[3*i+1]; ak = index[3*i+2]; rvec_sub(x_s[ai], x_s[aj], xij); rvec_sub(x_s[aj], x_s[ak], xjk); cprod(xij, xjk, n); for (m = 0; (m < DIM); m++) { c1[i][DIM*teller+m] = n[m]; } } } else { for (i = 0; (i < nvec); i++) { ai = index[2*i]; aj = index[2*i+1]; rvec_sub(x_s[ai], x_s[aj], n); for (m = 0; (m < DIM); m++) { c1[i][DIM*teller+m] = n[m]; } } } /* Increment loop counter */ teller++; } while (read_next_x(oenv, status, &t, x, box)); close_trj(status); fprintf(stderr, "\nDone with trajectory\n"); gmx_rmpbc_done(gpbc); /* Autocorrelation function */ if (teller < 2) { fprintf(stderr, "Not enough frames for correlation function\n"); } else { dt = (t1 - t0)/(teller-1); mode = eacVector; do_autocorr(ftp2fn(efXVG, NFILE, fnm), oenv, "Rotational Correlation Function", teller, nvec, c1, dt, mode, bAver); } do_view(oenv, ftp2fn(efXVG, NFILE, fnm), NULL); return 0; }
void calc_order(const char *fn, atom_id *index, atom_id *a, rvec **order, real ***slOrder, real *slWidth, int nslices, gmx_bool bSliced, gmx_bool bUnsat, t_topology *top, int ePBC, int ngrps, int axis, gmx_bool permolecule, gmx_bool radial, gmx_bool distcalc, const char *radfn, real ***distvals, const output_env_t oenv) { /* if permolecule = TRUE, order parameters will be calculed per molecule * and stored in slOrder with #slices = # molecules */ rvec *x0, /* coordinates with pbc */ *x1, /* coordinates without pbc */ dist; /* vector between two atoms */ matrix box; /* box (3x3) */ t_trxstatus *status; rvec cossum, /* sum of vector angles for three axes */ Sx, Sy, Sz, /* the three molecular axes */ tmp1, tmp2, /* temp. rvecs for calculating dot products */ frameorder; /* order parameters for one frame */ real *slFrameorder; /* order parameter for one frame, per slice */ real length, /* total distance between two atoms */ t, /* time from trajectory */ z_ave, z1, z2; /* average z, used to det. which slice atom is in */ int natoms, /* nr. atoms in trj */ nr_tails, /* nr tails, to check if index file is correct */ size = 0, /* nr. of atoms in group. same as nr_tails */ i, j, m, k, l, teller = 0, slice, /* current slice number */ nr_frames = 0; int *slCount; /* nr. of atoms in one slice */ real dbangle = 0, /* angle between double bond and axis */ sdbangle = 0; /* sum of these angles */ gmx_bool use_unitvector = FALSE; /* use a specified unit vector instead of axis to specify unit normal*/ rvec direction, com, dref, dvec; int comsize, distsize; atom_id *comidx = NULL, *distidx = NULL; char *grpname = NULL; t_pbc pbc; real arcdist, tmpdist; gmx_rmpbc_t gpbc = NULL; /* PBC added for center-of-mass vector*/ /* Initiate the pbc structure */ memset(&pbc, 0, sizeof(pbc)); if ((natoms = read_first_x(oenv, &status, fn, &t, &x0, box)) == 0) { gmx_fatal(FARGS, "Could not read coordinates from statusfile\n"); } nr_tails = index[1] - index[0]; fprintf(stderr, "Number of elements in first group: %d\n", nr_tails); /* take first group as standard. Not rocksolid, but might catch error in index*/ if (permolecule) { nslices = nr_tails; bSliced = FALSE; /*force slices off */ fprintf(stderr, "Calculating order parameters for each of %d molecules\n", nslices); } if (radial) { use_unitvector = TRUE; fprintf(stderr, "Select an index group to calculate the radial membrane normal\n"); get_index(&top->atoms, radfn, 1, &comsize, &comidx, &grpname); } if (distcalc) { if (grpname != NULL) { sfree(grpname); } fprintf(stderr, "Select an index group to use as distance reference\n"); get_index(&top->atoms, radfn, 1, &distsize, &distidx, &grpname); bSliced = FALSE; /*force slices off*/ } if (use_unitvector && bSliced) { fprintf(stderr, "Warning: slicing and specified unit vectors are not currently compatible\n"); } snew(slCount, nslices); snew(*slOrder, nslices); for (i = 0; i < nslices; i++) { snew((*slOrder)[i], ngrps); } if (distcalc) { snew(*distvals, nslices); for (i = 0; i < nslices; i++) { snew((*distvals)[i], ngrps); } } snew(*order, ngrps); snew(slFrameorder, nslices); snew(x1, natoms); if (bSliced) { *slWidth = box[axis][axis]/nslices; fprintf(stderr, "Box divided in %d slices. Initial width of slice: %f\n", nslices, *slWidth); } #if 0 nr_tails = index[1] - index[0]; fprintf(stderr, "Number of elements in first group: %d\n", nr_tails); /* take first group as standard. Not rocksolid, but might catch error in index*/ #endif teller = 0; gpbc = gmx_rmpbc_init(&top->idef, ePBC, natoms, box); /*********** Start processing trajectory ***********/ do { if (bSliced) { *slWidth = box[axis][axis]/nslices; } teller++; set_pbc(&pbc, ePBC, box); gmx_rmpbc_copy(gpbc, natoms, box, x0, x1); /* Now loop over all groups. There are ngrps groups, the order parameter can be calculated for grp 1 to grp ngrps - 1. For each group, loop over all atoms in group, which is index[i] to (index[i+1] - 1) See block.h. Of course, in this case index[i+1] -index[i] has to be the same for all groups, namely the number of tails. i just runs over all atoms in a tail, so for DPPC ngrps = 16 and i runs from 1 to 14, including 14 */ if (radial) { /*center-of-mass determination*/ com[XX] = 0.0; com[YY] = 0.0; com[ZZ] = 0.0; for (j = 0; j < comsize; j++) { rvec_inc(com, x1[comidx[j]]); } svmul(1.0/comsize, com, com); } if (distcalc) { dref[XX] = 0.0; dref[YY] = 0.0; dref[ZZ] = 0.0; for (j = 0; j < distsize; j++) { rvec_inc(dist, x1[distidx[j]]); } svmul(1.0/distsize, dref, dref); if (radial) { pbc_dx(&pbc, dref, com, dvec); unitv(dvec, dvec); } } for (i = 1; i < ngrps - 1; i++) { clear_rvec(frameorder); size = index[i+1] - index[i]; if (size != nr_tails) { gmx_fatal(FARGS, "grp %d does not have same number of" " elements as grp 1\n", i); } for (j = 0; j < size; j++) { if (radial) /*create unit vector*/ { pbc_dx(&pbc, x1[a[index[i]+j]], com, direction); unitv(direction, direction); /*DEBUG*/ /*if (j==0) fprintf(stderr,"X %f %f %f\tcom %f %f %f\tdirection %f %f %f\n",x1[a[index[i]+j]][0],x1[a[index[i]+j]][1],x1[a[index[i]+j]][2],com[0],com[1],com[2], direction[0],direction[1],direction[2]);*/ } if (bUnsat) { /* Using convention for unsaturated carbons */ /* first get Sz, the vector from Cn to Cn+1 */ rvec_sub(x1[a[index[i+1]+j]], x1[a[index[i]+j]], dist); length = norm(dist); check_length(length, a[index[i]+j], a[index[i+1]+j]); svmul(1/length, dist, Sz); /* this is actually the cosine of the angle between the double bond and axis, because Sz is normalized and the two other components of the axis on the bilayer are zero */ if (use_unitvector) { sdbangle += gmx_angle(direction, Sz); /*this can probably be optimized*/ } else { sdbangle += acos(Sz[axis]); } } else { /* get vector dist(Cn-1,Cn+1) for tail atoms */ rvec_sub(x1[a[index[i+1]+j]], x1[a[index[i-1]+j]], dist); length = norm(dist); /* determine distance between two atoms */ check_length(length, a[index[i-1]+j], a[index[i+1]+j]); svmul(1/length, dist, Sz); /* Sz is now the molecular axis Sz, normalized and all that */ } /* now get Sx. Sx is normal to the plane of Cn-1, Cn and Cn+1 so we can use the outer product of Cn-1->Cn and Cn+1->Cn, I hope */ rvec_sub(x1[a[index[i+1]+j]], x1[a[index[i]+j]], tmp1); rvec_sub(x1[a[index[i-1]+j]], x1[a[index[i]+j]], tmp2); cprod(tmp1, tmp2, Sx); svmul(1/norm(Sx), Sx, Sx); /* now we can get Sy from the outer product of Sx and Sz */ cprod(Sz, Sx, Sy); svmul(1/norm(Sy), Sy, Sy); /* the square of cosine of the angle between dist and the axis. Using the innerproduct, but two of the three elements are zero Determine the sum of the orderparameter of all atoms in group */ if (use_unitvector) { cossum[XX] = sqr(iprod(Sx, direction)); /* this is allowed, since Sa is normalized */ cossum[YY] = sqr(iprod(Sy, direction)); cossum[ZZ] = sqr(iprod(Sz, direction)); } else { cossum[XX] = sqr(Sx[axis]); /* this is allowed, since Sa is normalized */ cossum[YY] = sqr(Sy[axis]); cossum[ZZ] = sqr(Sz[axis]); } for (m = 0; m < DIM; m++) { frameorder[m] += 0.5 * (3 * cossum[m] - 1); } if (bSliced) { /* get average coordinate in box length for slicing, determine which slice atom is in, increase count for that slice. slFrameorder and slOrder are reals, not rvecs. Only the component [axis] of the order tensor is kept, until I find it necessary to know the others too */ z1 = x1[a[index[i-1]+j]][axis]; z2 = x1[a[index[i+1]+j]][axis]; z_ave = 0.5 * (z1 + z2); if (z_ave < 0) { z_ave += box[axis][axis]; } if (z_ave > box[axis][axis]) { z_ave -= box[axis][axis]; } slice = (int)(0.5 + (z_ave / (*slWidth))) - 1; slCount[slice]++; /* determine slice, increase count */ slFrameorder[slice] += 0.5 * (3 * cossum[axis] - 1); } else if (permolecule) { /* store per-molecule order parameter * To just track single-axis order: (*slOrder)[j][i] += 0.5 * (3 * iprod(cossum,direction) - 1); * following is for Scd order: */ (*slOrder)[j][i] += -1* (0.3333 * (3 * cossum[XX] - 1) + 0.3333 * 0.5 * (3 * cossum[YY] - 1)); } if (distcalc) { if (radial) { /* bin order parameter by arc distance from reference group*/ arcdist = gmx_angle(dvec, direction); (*distvals)[j][i] += arcdist; } else if (i == 1) { /* Want minimum lateral distance to first group calculated */ tmpdist = trace(box); /* should be max value */ for (k = 0; k < distsize; k++) { pbc_dx(&pbc, x1[distidx[k]], x1[a[index[i]+j]], dvec); /* at the moment, just remove dvec[axis] */ dvec[axis] = 0; tmpdist = min(tmpdist, norm2(dvec)); } //fprintf(stderr, "Min dist %f; trace %f\n", tmpdist, trace(box)); (*distvals)[j][i] += sqrt(tmpdist); } } } /* end loop j, over all atoms in group */ for (m = 0; m < DIM; m++) { (*order)[i][m] += (frameorder[m]/size); } if (!permolecule) { /*Skip following if doing per-molecule*/ for (k = 0; k < nslices; k++) { if (slCount[k]) /* if no elements, nothing has to be added */ { (*slOrder)[k][i] += slFrameorder[k]/slCount[k]; slFrameorder[k] = 0; slCount[k] = 0; } } } /* end loop i, over all groups in indexfile */ } nr_frames++; } while (read_next_x(oenv, status, &t, natoms, x0, box)); /*********** done with status file **********/ fprintf(stderr, "\nRead trajectory. Printing parameters to file\n"); gmx_rmpbc_done(gpbc); /* average over frames */ for (i = 1; i < ngrps - 1; i++) { svmul(1.0/nr_frames, (*order)[i], (*order)[i]); fprintf(stderr, "Atom %d Tensor: x=%g , y=%g, z=%g\n", i, (*order)[i][XX], (*order)[i][YY], (*order)[i][ZZ]); if (bSliced || permolecule) { for (k = 0; k < nslices; k++) { (*slOrder)[k][i] /= nr_frames; } } if (distcalc) { for (k = 0; k < nslices; k++) { (*distvals)[k][i] /= nr_frames; } } } if (bUnsat) { fprintf(stderr, "Average angle between double bond and normal: %f\n", 180*sdbangle/(nr_frames * size*M_PI)); } sfree(x0); /* free memory used by coordinate arrays */ sfree(x1); if (comidx != NULL) { sfree(comidx); } if (distidx != NULL) { sfree(distidx); } if (grpname != NULL) { sfree(grpname); } }