gmx_bool pme_load_balance(pme_load_balancing_t pme_lb, t_commrec *cr, FILE *fp_err, FILE *fp_log, t_inputrec *ir, t_state *state, double cycles, interaction_const_t *ic, struct nonbonded_verlet_t *nbv, struct gmx_pme_t ** pmedata, gmx_int64_t step) { gmx_bool OK; pme_setup_t *set; double cycles_fast; char buf[STRLEN], sbuf[22]; real rtab; gmx_bool bUsesSimpleTables = TRUE; if (pme_lb->stage == pme_lb->nstage) { return FALSE; } if (PAR(cr)) { gmx_sumd(1, &cycles, cr); cycles /= cr->nnodes; } set = &pme_lb->setup[pme_lb->cur]; set->count++; rtab = ir->rlistlong + ir->tabext; if (set->count % 2 == 1) { /* Skip the first cycle, because the first step after a switch * is much slower due to allocation and/or caching effects. */ return TRUE; } sprintf(buf, "step %4s: ", gmx_step_str(step, sbuf)); print_grid(fp_err, fp_log, buf, "timed with", set, cycles); if (set->count <= 2) { set->cycles = cycles; } else { if (cycles*PME_LB_ACCEL_TOL < set->cycles && pme_lb->stage == pme_lb->nstage - 1) { /* The performance went up a lot (due to e.g. DD load balancing). * Add a stage, keep the minima, but rescan all setups. */ pme_lb->nstage++; if (debug) { fprintf(debug, "The performance for grid %d %d %d went from %.3f to %.1f M-cycles, this is more than %f\n" "Increased the number stages to %d" " and ignoring the previous performance\n", set->grid[XX], set->grid[YY], set->grid[ZZ], cycles*1e-6, set->cycles*1e-6, PME_LB_ACCEL_TOL, pme_lb->nstage); } } set->cycles = min(set->cycles, cycles); } if (set->cycles < pme_lb->setup[pme_lb->fastest].cycles) { pme_lb->fastest = pme_lb->cur; if (DOMAINDECOMP(cr)) { /* We found a new fastest setting, ensure that with subsequent * shorter cut-off's the dynamic load balancing does not make * the use of the current cut-off impossible. This solution is * a trade-off, as the PME load balancing and DD domain size * load balancing can interact in complex ways. * With the Verlet kernels, DD load imbalance will usually be * mainly due to bonded interaction imbalance, which will often * quickly push the domain boundaries beyond the limit for the * optimal, PME load balanced, cut-off. But it could be that * better overal performance can be obtained with a slightly * shorter cut-off and better DD load balancing. */ change_dd_dlb_cutoff_limit(cr); } } cycles_fast = pme_lb->setup[pme_lb->fastest].cycles; /* Check in stage 0 if we should stop scanning grids. * Stop when the time is more than SLOW_FAC longer than the fastest. */ if (pme_lb->stage == 0 && pme_lb->cur > 0 && cycles > pme_lb->setup[pme_lb->fastest].cycles*PME_LB_SLOW_FAC) { pme_lb->n = pme_lb->cur + 1; /* Done with scanning, go to stage 1 */ switch_to_stage1(pme_lb); } if (pme_lb->stage == 0) { int gridsize_start; gridsize_start = set->grid[XX]*set->grid[YY]*set->grid[ZZ]; do { if (pme_lb->cur+1 < pme_lb->n) { /* We had already generated the next setup */ OK = TRUE; } else { /* Find the next setup */ OK = pme_loadbal_increase_cutoff(pme_lb, ir->pme_order, cr->dd); if (!OK) { pme_lb->elimited = epmelblimPMEGRID; } } if (OK && ir->ePBC != epbcNONE) { OK = (sqr(pme_lb->setup[pme_lb->cur+1].rlistlong) <= max_cutoff2(ir->ePBC, state->box)); if (!OK) { pme_lb->elimited = epmelblimBOX; } } if (OK) { pme_lb->cur++; if (DOMAINDECOMP(cr)) { OK = change_dd_cutoff(cr, state, ir, pme_lb->setup[pme_lb->cur].rlistlong); if (!OK) { /* Failed: do not use this setup */ pme_lb->cur--; pme_lb->elimited = epmelblimDD; } } } if (!OK) { /* We hit the upper limit for the cut-off, * the setup should not go further than cur. */ pme_lb->n = pme_lb->cur + 1; print_loadbal_limited(fp_err, fp_log, step, pme_lb); /* Switch to the next stage */ switch_to_stage1(pme_lb); } } while (OK && !(pme_lb->setup[pme_lb->cur].grid[XX]* pme_lb->setup[pme_lb->cur].grid[YY]* pme_lb->setup[pme_lb->cur].grid[ZZ] < gridsize_start*PME_LB_GRID_SCALE_FAC && pme_lb->setup[pme_lb->cur].grid_efficiency < pme_lb->setup[pme_lb->cur-1].grid_efficiency*PME_LB_GRID_EFFICIENCY_REL_FAC)); } if (pme_lb->stage > 0 && pme_lb->end == 1) { pme_lb->cur = 0; pme_lb->stage = pme_lb->nstage; } else if (pme_lb->stage > 0 && pme_lb->end > 1) { /* If stage = nstage-1: * scan over all setups, rerunning only those setups * which are not much slower than the fastest * else: * use the next setup */ do { pme_lb->cur++; if (pme_lb->cur == pme_lb->end) { pme_lb->stage++; pme_lb->cur = pme_lb->start; } } while (pme_lb->stage == pme_lb->nstage - 1 && pme_lb->setup[pme_lb->cur].count > 0 && pme_lb->setup[pme_lb->cur].cycles > cycles_fast*PME_LB_SLOW_FAC); if (pme_lb->stage == pme_lb->nstage) { /* We are done optimizing, use the fastest setup we found */ pme_lb->cur = pme_lb->fastest; } } if (DOMAINDECOMP(cr) && pme_lb->stage > 0) { OK = change_dd_cutoff(cr, state, ir, pme_lb->setup[pme_lb->cur].rlistlong); if (!OK) { /* Failsafe solution */ if (pme_lb->cur > 1 && pme_lb->stage == pme_lb->nstage) { pme_lb->stage--; } pme_lb->fastest = 0; pme_lb->start = 0; pme_lb->end = pme_lb->cur; pme_lb->cur = pme_lb->start; pme_lb->elimited = epmelblimDD; print_loadbal_limited(fp_err, fp_log, step, pme_lb); } } /* Change the Coulomb cut-off and the PME grid */ set = &pme_lb->setup[pme_lb->cur]; ic->rcoulomb = set->rcut_coulomb; ic->rlist = set->rlist; ic->rlistlong = set->rlistlong; ir->nstcalclr = set->nstcalclr; ic->ewaldcoeff_q = set->ewaldcoeff_q; /* TODO: centralize the code that sets the potentials shifts */ if (ic->coulomb_modifier == eintmodPOTSHIFT) { ic->sh_ewald = gmx_erfc(ic->ewaldcoeff_q*ic->rcoulomb); } if (EVDW_PME(ic->vdwtype)) { /* We have PME for both Coulomb and VdW, set rvdw equal to rcoulomb */ ic->rvdw = set->rcut_coulomb; ic->ewaldcoeff_lj = set->ewaldcoeff_lj; if (ic->vdw_modifier == eintmodPOTSHIFT) { real crc2; ic->dispersion_shift.cpot = -pow(ic->rvdw, -6.0); ic->repulsion_shift.cpot = -pow(ic->rvdw, -12.0); ic->sh_invrc6 = -ic->dispersion_shift.cpot; crc2 = sqr(ic->ewaldcoeff_lj*ic->rvdw); ic->sh_lj_ewald = (exp(-crc2)*(1 + crc2 + 0.5*crc2*crc2) - 1)*pow(ic->rvdw, -6.0); } } bUsesSimpleTables = uses_simple_tables(ir->cutoff_scheme, nbv, 0); nbnxn_gpu_pme_loadbal_update_param(nbv, ic); /* With tMPI + GPUs some ranks may be sharing GPU(s) and therefore * also sharing texture references. To keep the code simple, we don't * treat texture references as shared resources, but this means that * the coulomb_tab texture ref will get updated by multiple threads. * Hence, to ensure that the non-bonded kernels don't start before all * texture binding operations are finished, we need to wait for all ranks * to arrive here before continuing. * * Note that we could omit this barrier if GPUs are not shared (or * texture objects are used), but as this is initialization code, there * is not point in complicating things. */ #ifdef GMX_THREAD_MPI if (PAR(cr) && use_GPU(nbv)) { gmx_barrier(cr); } #endif /* GMX_THREAD_MPI */ /* Usually we won't need the simple tables with GPUs. * But we do with hybrid acceleration and with free energy. * To avoid bugs, we always re-initialize the simple tables here. */ init_interaction_const_tables(NULL, ic, bUsesSimpleTables, rtab); if (cr->duty & DUTY_PME) { if (pme_lb->setup[pme_lb->cur].pmedata == NULL) { /* Generate a new PME data structure, * copying part of the old pointers. */ gmx_pme_reinit(&set->pmedata, cr, pme_lb->setup[0].pmedata, ir, set->grid); } *pmedata = set->pmedata; } else { /* Tell our PME-only node to switch grid */ gmx_pme_send_switchgrid(cr, set->grid, set->ewaldcoeff_q, set->ewaldcoeff_lj); } if (debug) { print_grid(NULL, debug, "", "switched to", set, -1); } if (pme_lb->stage == pme_lb->nstage) { print_grid(fp_err, fp_log, "", "optimal", set, -1); } return TRUE; }
gmx_bool pme_load_balance(pme_load_balancing_t pme_lb, t_commrec *cr, FILE *fp_err, FILE *fp_log, t_inputrec *ir, t_state *state, double cycles, interaction_const_t *ic, nonbonded_verlet_t *nbv, gmx_pme_t *pmedata, gmx_large_int_t step) { gmx_bool OK; pme_setup_t *set; double cycles_fast; char buf[STRLEN], sbuf[22]; real rtab; gmx_bool bUsesSimpleTables = TRUE; if (pme_lb->stage == pme_lb->nstage) { return FALSE; } if (PAR(cr)) { gmx_sumd(1, &cycles, cr); cycles /= cr->nnodes; } set = &pme_lb->setup[pme_lb->cur]; set->count++; rtab = ir->rlistlong + ir->tabext; if (set->count % 2 == 1) { /* Skip the first cycle, because the first step after a switch * is much slower due to allocation and/or caching effects. */ return TRUE; } sprintf(buf, "step %4s: ", gmx_step_str(step, sbuf)); print_grid(fp_err, fp_log, buf, "timed with", set, cycles); if (set->count <= 2) { set->cycles = cycles; } else { if (cycles*PME_LB_ACCEL_TOL < set->cycles && pme_lb->stage == pme_lb->nstage - 1) { /* The performance went up a lot (due to e.g. DD load balancing). * Add a stage, keep the minima, but rescan all setups. */ pme_lb->nstage++; if (debug) { fprintf(debug, "The performance for grid %d %d %d went from %.3f to %.1f M-cycles, this is more than %f\n" "Increased the number stages to %d" " and ignoring the previous performance\n", set->grid[XX], set->grid[YY], set->grid[ZZ], cycles*1e-6, set->cycles*1e-6, PME_LB_ACCEL_TOL, pme_lb->nstage); } } set->cycles = min(set->cycles, cycles); } if (set->cycles < pme_lb->setup[pme_lb->fastest].cycles) { pme_lb->fastest = pme_lb->cur; if (DOMAINDECOMP(cr)) { /* We found a new fastest setting, ensure that with subsequent * shorter cut-off's the dynamic load balancing does not make * the use of the current cut-off impossible. This solution is * a trade-off, as the PME load balancing and DD domain size * load balancing can interact in complex ways. * With the Verlet kernels, DD load imbalance will usually be * mainly due to bonded interaction imbalance, which will often * quickly push the domain boundaries beyond the limit for the * optimal, PME load balanced, cut-off. But it could be that * better overal performance can be obtained with a slightly * shorter cut-off and better DD load balancing. */ change_dd_dlb_cutoff_limit(cr); } } cycles_fast = pme_lb->setup[pme_lb->fastest].cycles; /* Check in stage 0 if we should stop scanning grids. * Stop when the time is more than SLOW_FAC longer than the fastest. */ if (pme_lb->stage == 0 && pme_lb->cur > 0 && cycles > pme_lb->setup[pme_lb->fastest].cycles*PME_LB_SLOW_FAC) { pme_lb->n = pme_lb->cur + 1; /* Done with scanning, go to stage 1 */ switch_to_stage1(pme_lb); } if (pme_lb->stage == 0) { int gridsize_start; gridsize_start = set->grid[XX]*set->grid[YY]*set->grid[ZZ]; do { if (pme_lb->cur+1 < pme_lb->n) { /* We had already generated the next setup */ OK = TRUE; } else { /* Find the next setup */ OK = pme_loadbal_increase_cutoff(pme_lb, ir->pme_order); } if (OK && ir->ePBC != epbcNONE) { OK = (sqr(pme_lb->setup[pme_lb->cur+1].rlistlong) <= max_cutoff2(ir->ePBC, state->box)); if (!OK) { pme_lb->elimited = epmelblimBOX; } } if (OK) { pme_lb->cur++; if (DOMAINDECOMP(cr)) { OK = change_dd_cutoff(cr, state, ir, pme_lb->setup[pme_lb->cur].rlistlong); if (!OK) { /* Failed: do not use this setup */ pme_lb->cur--; pme_lb->elimited = epmelblimDD; } } } if (!OK) { /* We hit the upper limit for the cut-off, * the setup should not go further than cur. */ pme_lb->n = pme_lb->cur + 1; print_loadbal_limited(fp_err, fp_log, step, pme_lb); /* Switch to the next stage */ switch_to_stage1(pme_lb); } } while (OK && !(pme_lb->setup[pme_lb->cur].grid[XX]* pme_lb->setup[pme_lb->cur].grid[YY]* pme_lb->setup[pme_lb->cur].grid[ZZ] < gridsize_start*PME_LB_GRID_SCALE_FAC && pme_lb->setup[pme_lb->cur].grid_efficiency < pme_lb->setup[pme_lb->cur-1].grid_efficiency*PME_LB_GRID_EFFICIENCY_REL_FAC)); } if (pme_lb->stage > 0 && pme_lb->end == 1) { pme_lb->cur = 0; pme_lb->stage = pme_lb->nstage; } else if (pme_lb->stage > 0 && pme_lb->end > 1) { /* If stage = nstage-1: * scan over all setups, rerunning only those setups * which are not much slower than the fastest * else: * use the next setup */ do { pme_lb->cur++; if (pme_lb->cur == pme_lb->end) { pme_lb->stage++; pme_lb->cur = pme_lb->start; } } while (pme_lb->stage == pme_lb->nstage - 1 && pme_lb->setup[pme_lb->cur].count > 0 && pme_lb->setup[pme_lb->cur].cycles > cycles_fast*PME_LB_SLOW_FAC); if (pme_lb->stage == pme_lb->nstage) { /* We are done optimizing, use the fastest setup we found */ pme_lb->cur = pme_lb->fastest; } } if (DOMAINDECOMP(cr) && pme_lb->stage > 0) { OK = change_dd_cutoff(cr, state, ir, pme_lb->setup[pme_lb->cur].rlistlong); if (!OK) { /* Failsafe solution */ if (pme_lb->cur > 1 && pme_lb->stage == pme_lb->nstage) { pme_lb->stage--; } pme_lb->fastest = 0; pme_lb->start = 0; pme_lb->end = pme_lb->cur; pme_lb->cur = pme_lb->start; pme_lb->elimited = epmelblimDD; print_loadbal_limited(fp_err, fp_log, step, pme_lb); } } /* Change the Coulomb cut-off and the PME grid */ set = &pme_lb->setup[pme_lb->cur]; ic->rcoulomb = set->rcut_coulomb; ic->rlist = set->rlist; ic->rlistlong = set->rlistlong; ir->nstcalclr = set->nstcalclr; ic->ewaldcoeff = set->ewaldcoeff; bUsesSimpleTables = uses_simple_tables(ir->cutoff_scheme, nbv, 0); if (pme_lb->cutoff_scheme == ecutsVERLET && nbv->grp[0].kernel_type == nbnxnk8x8x8_CUDA) { nbnxn_cuda_pme_loadbal_update_param(nbv->cu_nbv, ic); } else { init_interaction_const_tables(NULL, ic, bUsesSimpleTables, rtab); } if (pme_lb->cutoff_scheme == ecutsVERLET && nbv->ngrp > 1) { init_interaction_const_tables(NULL, ic, bUsesSimpleTables, rtab); } if (cr->duty & DUTY_PME) { if (pme_lb->setup[pme_lb->cur].pmedata == NULL) { /* Generate a new PME data structure, * copying part of the old pointers. */ gmx_pme_reinit(&set->pmedata, cr, pme_lb->setup[0].pmedata, ir, set->grid); } *pmedata = set->pmedata; } else { /* Tell our PME-only node to switch grid */ gmx_pme_send_switchgrid(cr, set->grid, set->ewaldcoeff); } if (debug) { print_grid(NULL, debug, "", "switched to", set, -1); } if (pme_lb->stage == pme_lb->nstage) { print_grid(fp_err, fp_log, "", "optimal", set, -1); } return TRUE; }
int gmx_spol(int argc, char *argv[]) { t_topology *top; t_inputrec *ir; t_atom *atom; t_trxstatus *status; int nrefat, natoms, nf, ntot; real t; rvec *x, xref, trial, dx = {0}, dip, dir; matrix box; FILE *fp; int *isize, nrefgrp; atom_id **index, *molindex; char **grpname; real rmin2, rmax2, rcut, rcut2, rdx2 = 0, rtry2, qav, q, dip2, invbw; int nbin, i, m, mol, a0, a1, a, d; double sdip, sdip2, sinp, sdinp, nmol; int *hist; t_pbc pbc; gmx_rmpbc_t gpbc = NULL; const char *desc[] = { "[THISMODULE] analyzes dipoles around a solute; it is especially useful", "for polarizable water. A group of reference atoms, or a center", "of mass reference (option [TT]-com[tt]) and a group of solvent", "atoms is required. The program splits the group of solvent atoms", "into molecules. For each solvent molecule the distance to the", "closest atom in reference group or to the COM is determined.", "A cumulative distribution of these distances is plotted.", "For each distance between [TT]-rmin[tt] and [TT]-rmax[tt]", "the inner product of the distance vector", "and the dipole of the solvent molecule is determined.", "For solvent molecules with net charge (ions), the net charge of the ion", "is subtracted evenly from all atoms in the selection of each ion.", "The average of these dipole components is printed.", "The same is done for the polarization, where the average dipole is", "subtracted from the instantaneous dipole. The magnitude of the average", "dipole is set with the option [TT]-dip[tt], the direction is defined", "by the vector from the first atom in the selected solvent group", "to the midpoint between the second and the third atom." }; output_env_t oenv; static gmx_bool bCom = FALSE; static int srefat = 1; static real rmin = 0.0, rmax = 0.32, refdip = 0, bw = 0.01; t_pargs pa[] = { { "-com", FALSE, etBOOL, {&bCom}, "Use the center of mass as the reference postion" }, { "-refat", FALSE, etINT, {&srefat}, "The reference atom of the solvent molecule" }, { "-rmin", FALSE, etREAL, {&rmin}, "Maximum distance (nm)" }, { "-rmax", FALSE, etREAL, {&rmax}, "Maximum distance (nm)" }, { "-dip", FALSE, etREAL, {&refdip}, "The average dipole (D)" }, { "-bw", FALSE, etREAL, {&bw}, "The bin width" } }; t_filenm fnm[] = { { efTRX, NULL, NULL, ffREAD }, { efTPR, NULL, NULL, ffREAD }, { efNDX, NULL, NULL, ffOPTRD }, { efXVG, NULL, "scdist", ffWRITE } }; #define NFILE asize(fnm) if (!parse_common_args(&argc, argv, PCA_CAN_TIME | PCA_CAN_VIEW, NFILE, fnm, asize(pa), pa, asize(desc), desc, 0, NULL, &oenv)) { return 0; } snew(top, 1); snew(ir, 1); read_tpx_top(ftp2fn(efTPR, NFILE, fnm), ir, box, &natoms, NULL, NULL, NULL, top); /* get index groups */ printf("Select a group of reference particles and a solvent group:\n"); snew(grpname, 2); snew(index, 2); snew(isize, 2); get_index(&top->atoms, ftp2fn_null(efNDX, NFILE, fnm), 2, isize, index, grpname); if (bCom) { nrefgrp = 1; nrefat = isize[0]; } else { nrefgrp = isize[0]; nrefat = 1; } spol_atom2molindex(&(isize[1]), index[1], &(top->mols)); srefat--; /* initialize reading trajectory: */ natoms = read_first_x(oenv, &status, ftp2fn(efTRX, NFILE, fnm), &t, &x, box); rcut = 0.99*std::sqrt(max_cutoff2(ir->ePBC, box)); if (rcut == 0) { rcut = 10*rmax; } rcut2 = sqr(rcut); invbw = 1/bw; nbin = static_cast<int>(rcut*invbw)+2; snew(hist, nbin); rmin2 = sqr(rmin); rmax2 = sqr(rmax); nf = 0; ntot = 0; sdip = 0; sdip2 = 0; sinp = 0; sdinp = 0; molindex = top->mols.index; atom = top->atoms.atom; gpbc = gmx_rmpbc_init(&top->idef, ir->ePBC, natoms); /* start analysis of trajectory */ do { /* make molecules whole again */ gmx_rmpbc(gpbc, natoms, box, x); set_pbc(&pbc, ir->ePBC, box); if (bCom) { calc_com_pbc(nrefat, top, x, &pbc, index[0], xref, ir->ePBC); } for (m = 0; m < isize[1]; m++) { mol = index[1][m]; a0 = molindex[mol]; a1 = molindex[mol+1]; for (i = 0; i < nrefgrp; i++) { pbc_dx(&pbc, x[a0+srefat], bCom ? xref : x[index[0][i]], trial); rtry2 = norm2(trial); if (i == 0 || rtry2 < rdx2) { copy_rvec(trial, dx); rdx2 = rtry2; } } if (rdx2 < rcut2) { hist[static_cast<int>(std::sqrt(rdx2)*invbw)+1]++; } if (rdx2 >= rmin2 && rdx2 < rmax2) { unitv(dx, dx); clear_rvec(dip); qav = 0; for (a = a0; a < a1; a++) { qav += atom[a].q; } qav /= (a1 - a0); for (a = a0; a < a1; a++) { q = atom[a].q - qav; for (d = 0; d < DIM; d++) { dip[d] += q*x[a][d]; } } for (d = 0; d < DIM; d++) { dir[d] = -x[a0][d]; } for (a = a0+1; a < a0+3; a++) { for (d = 0; d < DIM; d++) { dir[d] += 0.5*x[a][d]; } } unitv(dir, dir); svmul(ENM2DEBYE, dip, dip); dip2 = norm2(dip); sdip += std::sqrt(dip2); sdip2 += dip2; for (d = 0; d < DIM; d++) { sinp += dx[d]*dip[d]; sdinp += dx[d]*(dip[d] - refdip*dir[d]); } ntot++; } } nf++; } while (read_next_x(oenv, status, &t, x, box)); gmx_rmpbc_done(gpbc); /* clean up */ sfree(x); close_trj(status); fprintf(stderr, "Average number of molecules within %g nm is %.1f\n", rmax, static_cast<real>(ntot)/nf); if (ntot > 0) { sdip /= ntot; sdip2 /= ntot; sinp /= ntot; sdinp /= ntot; fprintf(stderr, "Average dipole: %f (D), std.dev. %f\n", sdip, std::sqrt(sdip2-sqr(sdip))); fprintf(stderr, "Average radial component of the dipole: %f (D)\n", sinp); fprintf(stderr, "Average radial component of the polarization: %f (D)\n", sdinp); } fp = xvgropen(opt2fn("-o", NFILE, fnm), "Cumulative solvent distribution", "r (nm)", "molecules", oenv); nmol = 0; for (i = 0; i <= nbin; i++) { nmol += hist[i]; fprintf(fp, "%g %g\n", i*bw, nmol/nf); } xvgrclose(fp); do_view(oenv, opt2fn("-o", NFILE, fnm), NULL); return 0; }
static void do_rdf(char *fnNDX,char *fnTPS,char *fnTRX, char *fnRDF,char *fnCNRDF, char *fnHQ, bool bCM,char **rdft,bool bXY,bool bPBC,bool bNormalize, real cutoff,real binwidth,real fade,int ng) { FILE *fp; int status; char outf1[STRLEN],outf2[STRLEN]; char title[STRLEN],gtitle[STRLEN]; int g,natoms,i,j,k,nbin,j0,j1,n,nframes; int **count; char **grpname; int *isize,isize_cm=0,nrdf=0,max_i,isize0,isize_g; atom_id **index,*index_cm=NULL; #if (defined SIZEOF_LONG_LONG_INT) && (SIZEOF_LONG_LONG_INT >= 8) long long int *sum; #else double *sum; #endif real t,rmax2,cut2,r,r2,invhbinw,normfac; real segvol,spherevol,prev_spherevol,**rdf; rvec *x,dx,*x0=NULL,*x_i1,xi; real *inv_segvol,invvol,invvol_sum,rho; bool *bExcl,bTop,bNonSelfExcl; matrix box,box_pbc; int **npairs; atom_id ix,jx,***pairs; t_topology *top=NULL; int ePBC=-1; t_block *mols=NULL; t_blocka *excl; t_atom *atom=NULL; t_pbc pbc; int *is=NULL,**coi=NULL,cur,mol,i1,res,a; excl=NULL; if (fnTPS) { snew(top,1); bTop=read_tps_conf(fnTPS,title,top,&ePBC,&x,NULL,box,TRUE); if (bTop && !bCM) /* get exclusions from topology */ excl = &(top->excls); } snew(grpname,ng+1); snew(isize,ng+1); snew(index,ng+1); fprintf(stderr,"\nSelect a reference group and %d group%s\n", ng,ng==1?"":"s"); if (fnTPS) { get_index(&(top->atoms),fnNDX,ng+1,isize,index,grpname); atom = top->atoms.atom; } else { rd_index(fnNDX,ng+1,isize,index,grpname); } if (rdft[0][0] != 'a') { /* Split up all the groups in molecules or residues */ switch (rdft[0][0]) { case 'm': mols = &top->mols; break; case 'r': atom = top->atoms.atom; break; default: gmx_fatal(FARGS,"Unknown rdf option '%s'",rdft[0]); } snew(is,ng+1); snew(coi,ng+1); for(g=(bCM ? 1 : 0); g<ng+1; g++) { snew(coi[g],isize[g]+1); is[g] = 0; cur = -1; mol = 0; for(i=0; i<isize[g]; i++) { a = index[g][i]; if (rdft[0][0] == 'm') { /* Check if the molecule number has changed */ i1 = mols->index[mol+1]; while(a >= i1) { mol++; i1 = mols->index[mol+1]; } if (mol != cur) { coi[g][is[g]++] = i; cur = mol; } } else if (rdft[0][0] == 'r') { /* Check if the residue number has changed */ res = atom[a].resnr; if (res != cur) { coi[g][is[g]++] = i; cur = res; } } } coi[g][is[g]] = i; srenew(coi[g],is[g]+1); printf("Group '%s' of %d atoms consists of %d %s\n", grpname[g],isize[g],is[g], (rdft[0][0]=='m' ? "molecules" : "residues")); } } else if (bCM) { snew(is,1); snew(coi,1); } if (bCM) { is[0] = 1; snew(coi[0],is[0]+1); coi[0][0] = 0; coi[0][1] = isize[0]; isize0 = is[0]; snew(x0,isize0); } else if (rdft[0][0] != 'a') { isize0 = is[0]; snew(x0,isize0); } else { isize0 = isize[0]; } natoms=read_first_x(&status,fnTRX,&t,&x,box); if ( !natoms ) gmx_fatal(FARGS,"Could not read coordinates from statusfile\n"); if (fnTPS) /* check with topology */ if ( natoms > top->atoms.nr ) gmx_fatal(FARGS,"Trajectory (%d atoms) does not match topology (%d atoms)", natoms,top->atoms.nr); /* check with index groups */ for (i=0; i<ng+1; i++) for (j=0; j<isize[i]; j++) if ( index[i][j] >= natoms ) gmx_fatal(FARGS,"Atom index (%d) in index group %s (%d atoms) larger " "than number of atoms in trajectory (%d atoms)", index[i][j],grpname[i],isize[i],natoms); /* initialize some handy things */ copy_mat(box,box_pbc); if (bXY) { check_box_c(box); /* Make sure the z-height does not influence the cut-off */ box_pbc[ZZ][ZZ] = 2*max(box[XX][XX],box[YY][YY]); } if (bPBC) rmax2 = 0.99*0.99*max_cutoff2(bXY ? epbcXY : epbcXYZ,box_pbc); else rmax2 = sqr(3*max(box[XX][XX],max(box[YY][YY],box[ZZ][ZZ]))); if (debug) fprintf(debug,"rmax2 = %g\n",rmax2); /* We use the double amount of bins, so we can correctly * write the rdf and rdf_cn output at i*binwidth values. */ nbin = (int)(sqrt(rmax2) * 2 / binwidth); invhbinw = 2.0 / binwidth; cut2 = sqr(cutoff); snew(count,ng); snew(pairs,ng); snew(npairs,ng); snew(bExcl,natoms); max_i = 0; for(g=0; g<ng; g++) { if (isize[g+1] > max_i) max_i = isize[g+1]; /* this is THE array */ snew(count[g],nbin+1); /* make pairlist array for groups and exclusions */ snew(pairs[g],isize[0]); snew(npairs[g],isize[0]); for(i=0; i<isize[0]; i++) { /* We can only have exclusions with atomic rdfs */ if (!(bCM || rdft[0][0] != 'a')) { ix = index[0][i]; for(j=0; j < natoms; j++) bExcl[j] = FALSE; /* exclusions? */ if (excl) for( j = excl->index[ix]; j < excl->index[ix+1]; j++) bExcl[excl->a[j]]=TRUE; k = 0; snew(pairs[g][i], isize[g+1]); bNonSelfExcl = FALSE; for(j=0; j<isize[g+1]; j++) { jx = index[g+1][j]; if (!bExcl[jx]) pairs[g][i][k++]=jx; else if (ix != jx) /* Check if we have exclusions other than self exclusions */ bNonSelfExcl = TRUE; } if (bNonSelfExcl) { npairs[g][i]=k; srenew(pairs[g][i],npairs[g][i]); } else { /* Save a LOT of memory and some cpu cycles */ npairs[g][i]=-1; sfree(pairs[g][i]); } } else { npairs[g][i]=-1; } } } sfree(bExcl); snew(x_i1,max_i); nframes = 0; invvol_sum = 0; do { /* Must init pbc every step because of pressure coupling */ copy_mat(box,box_pbc); if (bPBC) { if (top != NULL) rm_pbc(&top->idef,ePBC,natoms,box,x,x); if (bXY) { check_box_c(box); clear_rvec(box_pbc[ZZ]); } set_pbc(&pbc,ePBC,box_pbc); if (bXY) /* Set z-size to 1 so we get the surface iso the volume */ box_pbc[ZZ][ZZ] = 1; } invvol = 1/det(box_pbc); invvol_sum += invvol; if (bCM) { /* Calculate center of mass of the whole group */ calc_comg(is[0],coi[0],index[0],TRUE ,atom,x,x0); } else if (rdft[0][0] != 'a') { calc_comg(is[0],coi[0],index[0],rdft[0][6]=='m',atom,x,x0); } for(g=0; g<ng; g++) { if (rdft[0][0] == 'a') { /* Copy the indexed coordinates to a continuous array */ for(i=0; i<isize[g+1]; i++) copy_rvec(x[index[g+1][i]],x_i1[i]); } else { /* Calculate the COMs/COGs and store in x_i1 */ calc_comg(is[g+1],coi[g+1],index[g+1],rdft[0][6]=='m',atom,x,x_i1); } for(i=0; i<isize0; i++) { if (bCM || rdft[0][0] != 'a') { copy_rvec(x0[i],xi); } else { copy_rvec(x[index[0][i]],xi); } if (rdft[0][0] == 'a' && npairs[g][i] >= 0) { /* Expensive loop, because of indexing */ for(j=0; j<npairs[g][i]; j++) { jx=pairs[g][i][j]; if (bPBC) pbc_dx(&pbc,xi,x[jx],dx); else rvec_sub(xi,x[jx],dx); if (bXY) r2 = dx[XX]*dx[XX] + dx[YY]*dx[YY]; else r2=iprod(dx,dx); if (r2>cut2 && r2<=rmax2) count[g][(int)(sqrt(r2)*invhbinw)]++; } } else { /* Cheaper loop, no exclusions */ if (rdft[0][0] == 'a') isize_g = isize[g+1]; else isize_g = is[g+1]; for(j=0; j<isize_g; j++) { if (bPBC) pbc_dx(&pbc,xi,x_i1[j],dx); else rvec_sub(xi,x_i1[j],dx); if (bXY) r2 = dx[XX]*dx[XX] + dx[YY]*dx[YY]; else r2=iprod(dx,dx); if (r2>cut2 && r2<=rmax2) count[g][(int)(sqrt(r2)*invhbinw)]++; } } } } nframes++; } while (read_next_x(status,&t,natoms,x,box)); fprintf(stderr,"\n"); close_trj(status); sfree(x); /* Average volume */ invvol = invvol_sum/nframes; /* Calculate volume of sphere segments or length of circle segments */ snew(inv_segvol,(nbin+1)/2); prev_spherevol=0; for(i=0; (i<(nbin+1)/2); i++) { r = (i + 0.5)*binwidth; if (bXY) { spherevol=M_PI*r*r; } else { spherevol=(4.0/3.0)*M_PI*r*r*r; } segvol=spherevol-prev_spherevol; inv_segvol[i]=1.0/segvol; prev_spherevol=spherevol; } snew(rdf,ng); for(g=0; g<ng; g++) { /* We have to normalize by dividing by the number of frames */ if (rdft[0][0] == 'a') normfac = 1.0/(nframes*invvol*isize0*isize[g+1]); else normfac = 1.0/(nframes*invvol*isize0*is[g+1]); /* Do the normalization */ nrdf = max((nbin+1)/2,1+2*fade/binwidth); snew(rdf[g],nrdf); for(i=0; i<(nbin+1)/2; i++) { r = i*binwidth; if (i == 0) j = count[g][0]; else j = count[g][i*2-1] + count[g][i*2]; if ((fade > 0) && (r >= fade)) rdf[g][i] = 1 + (j*inv_segvol[i]*normfac-1)*exp(-16*sqr(r/fade-1)); else { if (bNormalize) rdf[g][i] = j*inv_segvol[i]*normfac; else rdf[g][i] = j/(binwidth*isize0*nframes); } } for( ; (i<nrdf); i++) rdf[g][i] = 1.0; } if (rdft[0][0] == 'a') { sprintf(gtitle,"Radial distribution"); } else { sprintf(gtitle,"Radial distribution of %s %s", rdft[0][0]=='m' ? "molecule" : "residue", rdft[0][6]=='m' ? "COM" : "COG"); } fp=xvgropen(fnRDF,gtitle,"r",""); if (ng==1) { if (bPrintXvgrCodes()) fprintf(fp,"@ subtitle \"%s%s - %s\"\n", grpname[0],bCM ? " COM" : "",grpname[1]); } else { if (bPrintXvgrCodes()) fprintf(fp,"@ subtitle \"reference %s%s\"\n", grpname[0],bCM ? " COM" : ""); xvgr_legend(fp,ng,grpname+1); } for(i=0; (i<nrdf); i++) { fprintf(fp,"%10g",i*binwidth); for(g=0; g<ng; g++) fprintf(fp," %10g",rdf[g][i]); fprintf(fp,"\n"); } ffclose(fp); do_view(fnRDF,NULL); /* h(Q) function: fourier transform of rdf */ if (fnHQ) { int nhq = 401; real *hq,*integrand,Q; /* Get a better number density later! */ rho = isize[1]*invvol; snew(hq,nhq); snew(integrand,nrdf); for(i=0; (i<nhq); i++) { Q = i*0.5; integrand[0] = 0; for(j=1; (j<nrdf); j++) { r = j*binwidth; integrand[j] = (Q == 0) ? 1.0 : sin(Q*r)/(Q*r); integrand[j] *= 4.0*M_PI*rho*r*r*(rdf[0][j]-1.0); } hq[i] = print_and_integrate(debug,nrdf,binwidth,integrand,NULL,0); } fp=xvgropen(fnHQ,"h(Q)","Q(/nm)","h(Q)"); for(i=0; (i<nhq); i++) fprintf(fp,"%10g %10g\n",i*0.5,hq[i]); ffclose(fp); do_view(fnHQ,NULL); sfree(hq); sfree(integrand); } if (fnCNRDF) { normfac = 1.0/(isize0*nframes); fp=xvgropen(fnCNRDF,"Cumulative Number RDF","r","number"); if (ng==1) { if (bPrintXvgrCodes()) fprintf(fp,"@ subtitle \"%s-%s\"\n",grpname[0],grpname[1]); } else { if (bPrintXvgrCodes()) fprintf(fp,"@ subtitle \"reference %s\"\n",grpname[0]); xvgr_legend(fp,ng,grpname+1); } snew(sum,ng); for(i=0; (i<=nbin/2); i++) { fprintf(fp,"%10g",i*binwidth); for(g=0; g<ng; g++) { fprintf(fp," %10g",(real)((double)sum[g]*normfac)); if (i*2+1 < nbin) sum[g] += count[g][i*2] + count[g][i*2+1]; } fprintf(fp,"\n"); } ffclose(fp); sfree(sum); do_view(fnCNRDF,NULL); } for(g=0; g<ng; g++) sfree(rdf[g]); sfree(rdf); }
int gmx_sorient(int argc, char *argv[]) { t_topology top; int ePBC = -1; t_trxstatus *status; int natoms; real t; rvec *xtop, *x; matrix box; FILE *fp; int i, p, sa0, sa1, sa2, n, ntot, nf, m, *hist1, *hist2, *histn, nbin1, nbin2, nrbin; real *histi1, *histi2, invbw, invrbw; double sum1, sum2; int *isize, nrefgrp, nrefat; int **index; char **grpname; real inp, outp, nav, normfac, rmin2, rmax2, rcut, rcut2, r2, r; real c1, c2; char str[STRLEN]; gmx_bool bTPS; rvec xref, dx, dxh1, dxh2, outer; gmx_rmpbc_t gpbc = NULL; t_pbc pbc; const char *legr[] = { "<cos(\\8q\\4\\s1\\N)>", "<3cos\\S2\\N(\\8q\\4\\s2\\N)-1>" }; const char *legc[] = { "cos(\\8q\\4\\s1\\N)", "3cos\\S2\\N(\\8q\\4\\s2\\N)-1" }; const char *desc[] = { "[THISMODULE] analyzes solvent orientation around solutes.", "It calculates two angles between the vector from one or more", "reference positions to the first atom of each solvent molecule:", "", " * [GRK]theta[grk][SUB]1[sub]: the angle with the vector from the first atom of the solvent", " molecule to the midpoint between atoms 2 and 3.", " * [GRK]theta[grk][SUB]2[sub]: the angle with the normal of the solvent plane, defined by the", " same three atoms, or, when the option [TT]-v23[tt] is set, ", " the angle with the vector between atoms 2 and 3.", "", "The reference can be a set of atoms or", "the center of mass of a set of atoms. The group of solvent atoms should", "consist of 3 atoms per solvent molecule.", "Only solvent molecules between [TT]-rmin[tt] and [TT]-rmax[tt] are", "considered for [TT]-o[tt] and [TT]-no[tt] each frame.[PAR]", "[TT]-o[tt]: distribtion of [MATH][COS][GRK]theta[grk][SUB]1[sub][cos][math] for rmin<=r<=rmax.[PAR]", "[TT]-no[tt]: distribution of [MATH][COS][GRK]theta[grk][SUB]2[sub][cos][math] for rmin<=r<=rmax.[PAR]", "[TT]-ro[tt]: [MATH][CHEVRON][COS][GRK]theta[grk][SUB]1[sub][cos][chevron][math] and [MATH][CHEVRON]3[COS]^2[GRK]theta[grk][SUB]2[sub][cos]-1[chevron][math] as a function of the", "distance.[PAR]", "[TT]-co[tt]: the sum over all solvent molecules within distance r", "of [MATH][COS][GRK]theta[grk][SUB]1[sub][cos][math] and [MATH]3[COS]^2([GRK]theta[grk][SUB]2[sub])-1[cos][math] as a function of r.[PAR]", "[TT]-rc[tt]: the distribution of the solvent molecules as a function of r" }; gmx_output_env_t *oenv; static gmx_bool bCom = FALSE, bVec23 = FALSE, bPBC = FALSE; static real rmin = 0.0, rmax = 0.5, binwidth = 0.02, rbinw = 0.02; t_pargs pa[] = { { "-com", FALSE, etBOOL, {&bCom}, "Use the center of mass as the reference postion" }, { "-v23", FALSE, etBOOL, {&bVec23}, "Use the vector between atoms 2 and 3" }, { "-rmin", FALSE, etREAL, {&rmin}, "Minimum distance (nm)" }, { "-rmax", FALSE, etREAL, {&rmax}, "Maximum distance (nm)" }, { "-cbin", FALSE, etREAL, {&binwidth}, "Binwidth for the cosine" }, { "-rbin", FALSE, etREAL, {&rbinw}, "Binwidth for r (nm)" }, { "-pbc", FALSE, etBOOL, {&bPBC}, "Check PBC for the center of mass calculation. Only necessary when your reference group consists of several molecules." } }; t_filenm fnm[] = { { efTRX, NULL, NULL, ffREAD }, { efTPS, NULL, NULL, ffREAD }, { efNDX, NULL, NULL, ffOPTRD }, { efXVG, NULL, "sori", ffWRITE }, { efXVG, "-no", "snor", ffWRITE }, { efXVG, "-ro", "sord", ffWRITE }, { efXVG, "-co", "scum", ffWRITE }, { efXVG, "-rc", "scount", ffWRITE } }; #define NFILE asize(fnm) if (!parse_common_args(&argc, argv, PCA_CAN_TIME | PCA_CAN_VIEW, NFILE, fnm, asize(pa), pa, asize(desc), desc, 0, NULL, &oenv)) { return 0; } bTPS = (opt2bSet("-s", NFILE, fnm) || !opt2bSet("-n", NFILE, fnm) || bCom); if (bTPS) { read_tps_conf(ftp2fn(efTPS, NFILE, fnm), &top, &ePBC, &xtop, NULL, box, bCom); } /* get index groups */ printf("Select a group of reference particles and a solvent group:\n"); snew(grpname, 2); snew(index, 2); snew(isize, 2); if (bTPS) { get_index(&top.atoms, ftp2fn_null(efNDX, NFILE, fnm), 2, isize, index, grpname); } else { get_index(NULL, ftp2fn(efNDX, NFILE, fnm), 2, isize, index, grpname); } if (bCom) { nrefgrp = 1; nrefat = isize[0]; } else { nrefgrp = isize[0]; nrefat = 1; } if (isize[1] % 3) { gmx_fatal(FARGS, "The number of solvent atoms (%d) is not a multiple of 3", isize[1]); } /* initialize reading trajectory: */ natoms = read_first_x(oenv, &status, ftp2fn(efTRX, NFILE, fnm), &t, &x, box); rmin2 = sqr(rmin); rmax2 = sqr(rmax); rcut = 0.99*std::sqrt(max_cutoff2(guess_ePBC(box), box)); if (rcut == 0) { rcut = 10*rmax; } rcut2 = sqr(rcut); invbw = 1/binwidth; nbin1 = 1+static_cast<int>(2*invbw + 0.5); nbin2 = 1+static_cast<int>(invbw + 0.5); invrbw = 1/rbinw; snew(hist1, nbin1); snew(hist2, nbin2); nrbin = 1+static_cast<int>(rcut/rbinw); if (nrbin == 0) { nrbin = 1; } snew(histi1, nrbin); snew(histi2, nrbin); snew(histn, nrbin); ntot = 0; nf = 0; sum1 = 0; sum2 = 0; if (bTPS) { /* make molecules whole again */ gpbc = gmx_rmpbc_init(&top.idef, ePBC, natoms); } /* start analysis of trajectory */ do { if (bTPS) { /* make molecules whole again */ gmx_rmpbc(gpbc, natoms, box, x); } set_pbc(&pbc, ePBC, box); n = 0; inp = 0; for (p = 0; (p < nrefgrp); p++) { if (bCom) { calc_com_pbc(nrefat, &top, x, &pbc, index[0], xref, bPBC); } else { copy_rvec(x[index[0][p]], xref); } for (m = 0; m < isize[1]; m += 3) { sa0 = index[1][m]; sa1 = index[1][m+1]; sa2 = index[1][m+2]; range_check(sa0, 0, natoms); range_check(sa1, 0, natoms); range_check(sa2, 0, natoms); pbc_dx(&pbc, x[sa0], xref, dx); r2 = norm2(dx); if (r2 < rcut2) { r = std::sqrt(r2); if (!bVec23) { /* Determine the normal to the plain */ rvec_sub(x[sa1], x[sa0], dxh1); rvec_sub(x[sa2], x[sa0], dxh2); rvec_inc(dxh1, dxh2); svmul(1/r, dx, dx); unitv(dxh1, dxh1); inp = iprod(dx, dxh1); cprod(dxh1, dxh2, outer); unitv(outer, outer); outp = iprod(dx, outer); } else { /* Use the vector between the 2nd and 3rd atom */ rvec_sub(x[sa2], x[sa1], dxh2); unitv(dxh2, dxh2); outp = iprod(dx, dxh2)/r; } { int ii = static_cast<int>(invrbw*r); range_check(ii, 0, nrbin); histi1[ii] += inp; histi2[ii] += 3*sqr(outp) - 1; histn[ii]++; } if ((r2 >= rmin2) && (r2 < rmax2)) { int ii1 = static_cast<int>(invbw*(inp + 1)); int ii2 = static_cast<int>(invbw*std::abs(outp)); range_check(ii1, 0, nbin1); range_check(ii2, 0, nbin2); hist1[ii1]++; hist2[ii2]++; sum1 += inp; sum2 += outp; n++; } } } } ntot += n; nf++; } while (read_next_x(oenv, status, &t, x, box)); /* clean up */ sfree(x); close_trj(status); gmx_rmpbc_done(gpbc); /* Add the bin for the exact maximum to the previous bin */ hist1[nbin1-1] += hist1[nbin1]; hist2[nbin2-1] += hist2[nbin2]; nav = static_cast<real>(ntot)/(nrefgrp*nf); normfac = invbw/ntot; fprintf(stderr, "Average nr of molecules between %g and %g nm: %.1f\n", rmin, rmax, nav); if (ntot > 0) { sum1 /= ntot; sum2 /= ntot; fprintf(stderr, "Average cos(theta1) between %g and %g nm: %6.3f\n", rmin, rmax, sum1); fprintf(stderr, "Average 3cos2(theta2)-1 between %g and %g nm: %6.3f\n", rmin, rmax, sum2); } sprintf(str, "Solvent orientation between %g and %g nm", rmin, rmax); fp = xvgropen(opt2fn("-o", NFILE, fnm), str, "cos(\\8q\\4\\s1\\N)", "", oenv); if (output_env_get_print_xvgr_codes(oenv)) { fprintf(fp, "@ subtitle \"average shell size %.1f molecules\"\n", nav); } for (i = 0; i < nbin1; i++) { fprintf(fp, "%g %g\n", (i+0.5)*binwidth-1, 2*normfac*hist1[i]); } xvgrclose(fp); sprintf(str, "Solvent normal orientation between %g and %g nm", rmin, rmax); fp = xvgropen(opt2fn("-no", NFILE, fnm), str, "cos(\\8q\\4\\s2\\N)", "", oenv); if (output_env_get_print_xvgr_codes(oenv)) { fprintf(fp, "@ subtitle \"average shell size %.1f molecules\"\n", nav); } for (i = 0; i < nbin2; i++) { fprintf(fp, "%g %g\n", (i+0.5)*binwidth, normfac*hist2[i]); } xvgrclose(fp); sprintf(str, "Solvent orientation"); fp = xvgropen(opt2fn("-ro", NFILE, fnm), str, "r (nm)", "", oenv); if (output_env_get_print_xvgr_codes(oenv)) { fprintf(fp, "@ subtitle \"as a function of distance\"\n"); } xvgr_legend(fp, 2, legr, oenv); for (i = 0; i < nrbin; i++) { fprintf(fp, "%g %g %g\n", (i+0.5)*rbinw, histn[i] ? histi1[i]/histn[i] : 0, histn[i] ? histi2[i]/histn[i] : 0); } xvgrclose(fp); sprintf(str, "Cumulative solvent orientation"); fp = xvgropen(opt2fn("-co", NFILE, fnm), str, "r (nm)", "", oenv); if (output_env_get_print_xvgr_codes(oenv)) { fprintf(fp, "@ subtitle \"as a function of distance\"\n"); } xvgr_legend(fp, 2, legc, oenv); normfac = 1.0/(nrefgrp*nf); c1 = 0; c2 = 0; fprintf(fp, "%g %g %g\n", 0.0, c1, c2); for (i = 0; i < nrbin; i++) { c1 += histi1[i]*normfac; c2 += histi2[i]*normfac; fprintf(fp, "%g %g %g\n", (i+1)*rbinw, c1, c2); } xvgrclose(fp); sprintf(str, "Solvent distribution"); fp = xvgropen(opt2fn("-rc", NFILE, fnm), str, "r (nm)", "molecules/nm", oenv); if (output_env_get_print_xvgr_codes(oenv)) { fprintf(fp, "@ subtitle \"as a function of distance\"\n"); } normfac = 1.0/(rbinw*nf); for (i = 0; i < nrbin; i++) { fprintf(fp, "%g %g\n", (i+0.5)*rbinw, histn[i]*normfac); } xvgrclose(fp); do_view(oenv, opt2fn("-o", NFILE, fnm), NULL); do_view(oenv, opt2fn("-no", NFILE, fnm), NULL); do_view(oenv, opt2fn("-ro", NFILE, fnm), "-nxy"); do_view(oenv, opt2fn("-co", NFILE, fnm), "-nxy"); return 0; }
static void low_set_pbc(t_pbc *pbc, int ePBC, ivec *dd_nc, matrix box) { int order[5] = {0, -1, 1, -2, 2}; int ii, jj, kk, i, j, k, d, dd, jc, kc, npbcdim, shift; ivec bPBC; real d2old, d2new, d2new_c; rvec trial, pos; gmx_bool bXY, bUse; const char *ptr; pbc->ndim_ePBC = ePBC2npbcdim(ePBC); copy_mat(box, pbc->box); pbc->bLimitDistance = FALSE; pbc->max_cutoff2 = 0; pbc->dim = -1; for (i = 0; (i < DIM); i++) { pbc->fbox_diag[i] = box[i][i]; pbc->hbox_diag[i] = pbc->fbox_diag[i]*0.5; pbc->mhbox_diag[i] = -pbc->hbox_diag[i]; } ptr = check_box(ePBC, box); if (ePBC == epbcNONE) { pbc->ePBCDX = epbcdxNOPBC; } else if (ptr) { fprintf(stderr, "Warning: %s\n", ptr); pr_rvecs(stderr, 0, " Box", box, DIM); fprintf(stderr, " Can not fix pbc.\n"); pbc->ePBCDX = epbcdxUNSUPPORTED; pbc->bLimitDistance = TRUE; pbc->limit_distance2 = 0; } else { if (ePBC == epbcSCREW && dd_nc) { /* This combinated should never appear here */ gmx_incons("low_set_pbc called with screw pbc and dd_nc != NULL"); } npbcdim = 0; for (i = 0; i < DIM; i++) { if ((dd_nc && (*dd_nc)[i] > 1) || (ePBC == epbcXY && i == ZZ)) { bPBC[i] = 0; } else { bPBC[i] = 1; npbcdim++; } } switch (npbcdim) { case 1: /* 1D pbc is not an mdp option and it is therefore only used * with single shifts. */ pbc->ePBCDX = epbcdx1D_RECT; for (i = 0; i < DIM; i++) { if (bPBC[i]) { pbc->dim = i; } } for (i = 0; i < pbc->dim; i++) { if (pbc->box[pbc->dim][i] != 0) { pbc->ePBCDX = epbcdx1D_TRIC; } } break; case 2: pbc->ePBCDX = epbcdx2D_RECT; for (i = 0; i < DIM; i++) { if (!bPBC[i]) { pbc->dim = i; } } for (i = 0; i < DIM; i++) { if (bPBC[i]) { for (j = 0; j < i; j++) { if (pbc->box[i][j] != 0) { pbc->ePBCDX = epbcdx2D_TRIC; } } } } break; case 3: if (ePBC != epbcSCREW) { if (TRICLINIC(box)) { pbc->ePBCDX = epbcdxTRICLINIC; } else { pbc->ePBCDX = epbcdxRECTANGULAR; } } else { pbc->ePBCDX = (box[ZZ][YY] == 0 ? epbcdxSCREW_RECT : epbcdxSCREW_TRIC); if (pbc->ePBCDX == epbcdxSCREW_TRIC) { fprintf(stderr, "Screw pbc is not yet implemented for triclinic boxes.\n" "Can not fix pbc.\n"); pbc->ePBCDX = epbcdxUNSUPPORTED; } } break; default: gmx_fatal(FARGS, "Incorrect number of pbc dimensions with DD: %d", npbcdim); } pbc->max_cutoff2 = max_cutoff2(ePBC, box); if (pbc->ePBCDX == epbcdxTRICLINIC || pbc->ePBCDX == epbcdx2D_TRIC || pbc->ePBCDX == epbcdxSCREW_TRIC) { if (debug) { pr_rvecs(debug, 0, "Box", box, DIM); fprintf(debug, "max cutoff %.3f\n", sqrt(pbc->max_cutoff2)); } pbc->ntric_vec = 0; /* We will only use single shifts, but we will check a few * more shifts to see if there is a limiting distance * above which we can not be sure of the correct distance. */ for (kk = 0; kk < 5; kk++) { k = order[kk]; if (!bPBC[ZZ] && k != 0) { continue; } for (jj = 0; jj < 5; jj++) { j = order[jj]; if (!bPBC[YY] && j != 0) { continue; } for (ii = 0; ii < 3; ii++) { i = order[ii]; if (!bPBC[XX] && i != 0) { continue; } /* A shift is only useful when it is trilinic */ if (j != 0 || k != 0) { d2old = 0; d2new = 0; for (d = 0; d < DIM; d++) { trial[d] = i*box[XX][d] + j*box[YY][d] + k*box[ZZ][d]; /* Choose the vector within the brick around 0,0,0 that * will become the shortest due to shift try. */ if (d == pbc->dim) { trial[d] = 0; pos[d] = 0; } else { if (trial[d] < 0) { pos[d] = min( pbc->hbox_diag[d], -trial[d]); } else { pos[d] = max(-pbc->hbox_diag[d], -trial[d]); } } d2old += sqr(pos[d]); d2new += sqr(pos[d] + trial[d]); } if (BOX_MARGIN*d2new < d2old) { if (j < -1 || j > 1 || k < -1 || k > 1) { /* Check if there is a single shift vector * that decreases this distance even more. */ jc = 0; kc = 0; if (j < -1 || j > 1) { jc = j/2; } if (k < -1 || k > 1) { kc = k/2; } d2new_c = 0; for (d = 0; d < DIM; d++) { d2new_c += sqr(pos[d] + trial[d] - jc*box[YY][d] - kc*box[ZZ][d]); } if (d2new_c > BOX_MARGIN*d2new) { /* Reject this shift vector, as there is no a priori limit * to the number of shifts that decrease distances. */ if (!pbc->bLimitDistance || d2new < pbc->limit_distance2) { pbc->limit_distance2 = d2new; } pbc->bLimitDistance = TRUE; } } else { /* Check if shifts with one box vector less do better */ bUse = TRUE; for (dd = 0; dd < DIM; dd++) { shift = (dd == 0 ? i : (dd == 1 ? j : k)); if (shift) { d2new_c = 0; for (d = 0; d < DIM; d++) { d2new_c += sqr(pos[d] + trial[d] - shift*box[dd][d]); } if (d2new_c <= BOX_MARGIN*d2new) { bUse = FALSE; } } } if (bUse) { /* Accept this shift vector. */ if (pbc->ntric_vec >= MAX_NTRICVEC) { fprintf(stderr, "\nWARNING: Found more than %d triclinic correction vectors, ignoring some.\n" " There is probably something wrong with your box.\n", MAX_NTRICVEC); pr_rvecs(stderr, 0, " Box", box, DIM); } else { copy_rvec(trial, pbc->tric_vec[pbc->ntric_vec]); pbc->tric_shift[pbc->ntric_vec][XX] = i; pbc->tric_shift[pbc->ntric_vec][YY] = j; pbc->tric_shift[pbc->ntric_vec][ZZ] = k; pbc->ntric_vec++; } } } if (debug) { fprintf(debug, " tricvec %2d = %2d %2d %2d %5.2f %5.2f %5.2f %5.2f %5.2f %5.2f %5.2f %5.2f\n", pbc->ntric_vec, i, j, k, sqrt(d2old), sqrt(d2new), trial[XX], trial[YY], trial[ZZ], pos[XX], pos[YY], pos[ZZ]); } } } } } } } } }
//! Do the real arithmetic for filling the pbc struct static void low_set_pbc(t_pbc *pbc, int ePBC, const ivec dd_pbc, const matrix box) { int order[3] = { 0, -1, 1 }; ivec bPBC; const char *ptr; pbc->ePBC = ePBC; pbc->ndim_ePBC = ePBC2npbcdim(ePBC); copy_mat(box, pbc->box); pbc->max_cutoff2 = 0; pbc->dim = -1; pbc->ntric_vec = 0; for (int i = 0; (i < DIM); i++) { pbc->fbox_diag[i] = box[i][i]; pbc->hbox_diag[i] = pbc->fbox_diag[i]*0.5; pbc->mhbox_diag[i] = -pbc->hbox_diag[i]; } ptr = check_box(ePBC, box); if (ePBC == epbcNONE) { pbc->ePBCDX = epbcdxNOPBC; } else if (ptr) { fprintf(stderr, "Warning: %s\n", ptr); pr_rvecs(stderr, 0, " Box", box, DIM); fprintf(stderr, " Can not fix pbc.\n\n"); pbc->ePBCDX = epbcdxUNSUPPORTED; } else { if (ePBC == epbcSCREW && NULL != dd_pbc) { /* This combinated should never appear here */ gmx_incons("low_set_pbc called with screw pbc and dd_nc != NULL"); } int npbcdim = 0; for (int i = 0; i < DIM; i++) { if ((dd_pbc && dd_pbc[i] == 0) || (ePBC == epbcXY && i == ZZ)) { bPBC[i] = 0; } else { bPBC[i] = 1; npbcdim++; } } switch (npbcdim) { case 1: /* 1D pbc is not an mdp option and it is therefore only used * with single shifts. */ pbc->ePBCDX = epbcdx1D_RECT; for (int i = 0; i < DIM; i++) { if (bPBC[i]) { pbc->dim = i; } } GMX_ASSERT(pbc->dim < DIM, "Dimension for PBC incorrect"); for (int i = 0; i < pbc->dim; i++) { if (pbc->box[pbc->dim][i] != 0) { pbc->ePBCDX = epbcdx1D_TRIC; } } break; case 2: pbc->ePBCDX = epbcdx2D_RECT; for (int i = 0; i < DIM; i++) { if (!bPBC[i]) { pbc->dim = i; } } for (int i = 0; i < DIM; i++) { if (bPBC[i]) { for (int j = 0; j < i; j++) { if (pbc->box[i][j] != 0) { pbc->ePBCDX = epbcdx2D_TRIC; } } } } break; case 3: if (ePBC != epbcSCREW) { if (TRICLINIC(box)) { pbc->ePBCDX = epbcdxTRICLINIC; } else { pbc->ePBCDX = epbcdxRECTANGULAR; } } else { pbc->ePBCDX = (box[ZZ][YY] == 0 ? epbcdxSCREW_RECT : epbcdxSCREW_TRIC); if (pbc->ePBCDX == epbcdxSCREW_TRIC) { fprintf(stderr, "Screw pbc is not yet implemented for triclinic boxes.\n" "Can not fix pbc.\n"); pbc->ePBCDX = epbcdxUNSUPPORTED; } } break; default: gmx_fatal(FARGS, "Incorrect number of pbc dimensions with DD: %d", npbcdim); } pbc->max_cutoff2 = max_cutoff2(ePBC, box); if (pbc->ePBCDX == epbcdxTRICLINIC || pbc->ePBCDX == epbcdx2D_TRIC || pbc->ePBCDX == epbcdxSCREW_TRIC) { if (debug) { pr_rvecs(debug, 0, "Box", box, DIM); fprintf(debug, "max cutoff %.3f\n", sqrt(pbc->max_cutoff2)); } /* We will only need single shifts here */ for (int kk = 0; kk < 3; kk++) { int k = order[kk]; if (!bPBC[ZZ] && k != 0) { continue; } for (int jj = 0; jj < 3; jj++) { int j = order[jj]; if (!bPBC[YY] && j != 0) { continue; } for (int ii = 0; ii < 3; ii++) { int i = order[ii]; if (!bPBC[XX] && i != 0) { continue; } /* A shift is only useful when it is trilinic */ if (j != 0 || k != 0) { rvec trial; rvec pos; real d2old = 0; real d2new = 0; for (int d = 0; d < DIM; d++) { trial[d] = i*box[XX][d] + j*box[YY][d] + k*box[ZZ][d]; /* Choose the vector within the brick around 0,0,0 that * will become the shortest due to shift try. */ if (d == pbc->dim) { trial[d] = 0; pos[d] = 0; } else { if (trial[d] < 0) { pos[d] = std::min( pbc->hbox_diag[d], -trial[d]); } else { pos[d] = std::max(-pbc->hbox_diag[d], -trial[d]); } } d2old += gmx::square(pos[d]); d2new += gmx::square(pos[d] + trial[d]); } if (BOX_MARGIN*d2new < d2old) { /* Check if shifts with one box vector less do better */ gmx_bool bUse = TRUE; for (int dd = 0; dd < DIM; dd++) { int shift = (dd == 0 ? i : (dd == 1 ? j : k)); if (shift) { real d2new_c = 0; for (int d = 0; d < DIM; d++) { d2new_c += gmx::square(pos[d] + trial[d] - shift*box[dd][d]); } if (d2new_c <= BOX_MARGIN*d2new) { bUse = FALSE; } } } if (bUse) { /* Accept this shift vector. */ if (pbc->ntric_vec >= MAX_NTRICVEC) { fprintf(stderr, "\nWARNING: Found more than %d triclinic correction vectors, ignoring some.\n" " There is probably something wrong with your box.\n", MAX_NTRICVEC); pr_rvecs(stderr, 0, " Box", box, DIM); } else { copy_rvec(trial, pbc->tric_vec[pbc->ntric_vec]); pbc->tric_shift[pbc->ntric_vec][XX] = i; pbc->tric_shift[pbc->ntric_vec][YY] = j; pbc->tric_shift[pbc->ntric_vec][ZZ] = k; pbc->ntric_vec++; if (debug) { fprintf(debug, " tricvec %2d = %2d %2d %2d %5.2f %5.2f %5.2f %5.2f %5.2f %5.2f %5.2f %5.2f\n", pbc->ntric_vec, i, j, k, sqrt(d2old), sqrt(d2new), trial[XX], trial[YY], trial[ZZ], pos[XX], pos[YY], pos[ZZ]); } } } } } } } } } } }
/* Try to increase nstlist when using the Verlet cut-off scheme */ static void increase_nstlist(FILE *fp, t_commrec *cr, t_inputrec *ir, int nstlist_cmdline, const gmx_mtop_t *mtop, matrix box, gmx_bool bGPU) { float listfac_ok, listfac_max; int nstlist_orig, nstlist_prev; verletbuf_list_setup_t ls; real rlistWithReferenceNstlist, rlist_inc, rlist_ok, rlist_max; real rlist_new, rlist_prev; size_t nstlist_ind = 0; t_state state_tmp; gmx_bool bBox, bDD, bCont; const char *nstl_gpu = "\nFor optimal performance with a GPU nstlist (now %d) should be larger.\nThe optimum depends on your CPU and GPU resources.\nYou might want to try several nstlist values.\n"; const char *nve_err = "Can not increase nstlist because an NVE ensemble is used"; const char *vbd_err = "Can not increase nstlist because verlet-buffer-tolerance is not set or used"; const char *box_err = "Can not increase nstlist because the box is too small"; const char *dd_err = "Can not increase nstlist because of domain decomposition limitations"; char buf[STRLEN]; const float oneThird = 1.0f / 3.0f; if (nstlist_cmdline <= 0) { if (ir->nstlist == 1) { /* The user probably set nstlist=1 for a reason, * don't mess with the settings. */ return; } if (fp != NULL && bGPU && ir->nstlist < nstlist_try[0]) { fprintf(fp, nstl_gpu, ir->nstlist); } nstlist_ind = 0; while (nstlist_ind < NNSTL && ir->nstlist >= nstlist_try[nstlist_ind]) { nstlist_ind++; } if (nstlist_ind == NNSTL) { /* There are no larger nstlist value to try */ return; } } if (EI_MD(ir->eI) && ir->etc == etcNO) { if (MASTER(cr)) { fprintf(stderr, "%s\n", nve_err); } if (fp != NULL) { fprintf(fp, "%s\n", nve_err); } return; } if (ir->verletbuf_tol == 0 && bGPU) { gmx_fatal(FARGS, "You are using an old tpr file with a GPU, please generate a new tpr file with an up to date version of grompp"); } if (ir->verletbuf_tol < 0) { if (MASTER(cr)) { fprintf(stderr, "%s\n", vbd_err); } if (fp != NULL) { fprintf(fp, "%s\n", vbd_err); } return; } if (bGPU) { listfac_ok = nbnxn_gpu_listfac_ok; listfac_max = nbnxn_gpu_listfac_max; } else { listfac_ok = nbnxn_cpu_listfac_ok; listfac_max = nbnxn_cpu_listfac_max; } nstlist_orig = ir->nstlist; if (nstlist_cmdline > 0) { if (fp) { sprintf(buf, "Getting nstlist=%d from command line option", nstlist_cmdline); } ir->nstlist = nstlist_cmdline; } verletbuf_get_list_setup(TRUE, bGPU, &ls); /* Allow rlist to make the list a given factor larger than the list * would be with the reference value for nstlist (10). */ nstlist_prev = ir->nstlist; ir->nstlist = nbnxnReferenceNstlist; calc_verlet_buffer_size(mtop, det(box), ir, -1, &ls, NULL, &rlistWithReferenceNstlist); ir->nstlist = nstlist_prev; /* Determine the pair list size increase due to zero interactions */ rlist_inc = nbnxn_get_rlist_effective_inc(ls.cluster_size_j, mtop->natoms/det(box)); rlist_ok = (rlistWithReferenceNstlist + rlist_inc)*pow(listfac_ok, oneThird) - rlist_inc; rlist_max = (rlistWithReferenceNstlist + rlist_inc)*pow(listfac_max, oneThird) - rlist_inc; if (debug) { fprintf(debug, "nstlist tuning: rlist_inc %.3f rlist_ok %.3f rlist_max %.3f\n", rlist_inc, rlist_ok, rlist_max); } nstlist_prev = nstlist_orig; rlist_prev = ir->rlist; do { if (nstlist_cmdline <= 0) { ir->nstlist = nstlist_try[nstlist_ind]; } /* Set the pair-list buffer size in ir */ calc_verlet_buffer_size(mtop, det(box), ir, -1, &ls, NULL, &rlist_new); /* Does rlist fit in the box? */ bBox = (sqr(rlist_new) < max_cutoff2(ir->ePBC, box)); bDD = TRUE; if (bBox && DOMAINDECOMP(cr)) { /* Check if rlist fits in the domain decomposition */ if (inputrec2nboundeddim(ir) < DIM) { gmx_incons("Changing nstlist with domain decomposition and unbounded dimensions is not implemented yet"); } copy_mat(box, state_tmp.box); bDD = change_dd_cutoff(cr, &state_tmp, ir, rlist_new); } if (debug) { fprintf(debug, "nstlist %d rlist %.3f bBox %d bDD %d\n", ir->nstlist, rlist_new, bBox, bDD); } bCont = FALSE; if (nstlist_cmdline <= 0) { if (bBox && bDD && rlist_new <= rlist_max) { /* Increase nstlist */ nstlist_prev = ir->nstlist; rlist_prev = rlist_new; bCont = (nstlist_ind+1 < NNSTL && rlist_new < rlist_ok); } else { /* Stick with the previous nstlist */ ir->nstlist = nstlist_prev; rlist_new = rlist_prev; bBox = TRUE; bDD = TRUE; } } nstlist_ind++; } while (bCont); if (!bBox || !bDD) { gmx_warning(!bBox ? box_err : dd_err); if (fp != NULL) { fprintf(fp, "\n%s\n", bBox ? box_err : dd_err); } ir->nstlist = nstlist_orig; } else if (ir->nstlist != nstlist_orig || rlist_new != ir->rlist) { sprintf(buf, "Changing nstlist from %d to %d, rlist from %g to %g", nstlist_orig, ir->nstlist, ir->rlist, rlist_new); if (MASTER(cr)) { fprintf(stderr, "%s\n\n", buf); } if (fp != NULL) { fprintf(fp, "%s\n\n", buf); } ir->rlist = rlist_new; ir->rlistlong = rlist_new; } }