static void write_xplor(const char *file,real *data,int *ibox,real dmin[],real dmax[]) { XplorMap *xm; int i,j,k,n; snew(xm,1); xm->Nx = ibox[XX]; xm->Ny = ibox[YY]; xm->Nz = ibox[ZZ]; snew(xm->ed,xm->Nx*xm->Ny*xm->Nz); n=0; for(k=0; (k<xm->Nz); k++) for(j=0; (j<xm->Ny); j++) for(i=0; (i<xm->Nx); i++) xm->ed[n++] = data[index3(ibox,i,j,k)]; xm->cell[0] = dmax[XX]-dmin[XX]; xm->cell[1] = dmax[YY]-dmin[YY]; xm->cell[2] = dmax[ZZ]-dmin[ZZ]; xm->cell[3] = xm->cell[4] = xm->cell[5] = 90; clear_ivec(xm->dmin); xm->dmax[XX] = ibox[XX]-1; xm->dmax[YY] = ibox[YY]-1; xm->dmax[ZZ] = ibox[ZZ]-1; lo_write_xplor(xm,file); sfree(xm->ed); sfree(xm); }
void done_grid(t_grid *grid) { if (grid == nullptr) { return; } grid->nr = 0; clear_ivec(grid->n); grid->ncells = 0; sfree(grid->cell_index); sfree(grid->a); sfree(grid->index); sfree(grid->nra); grid->cells_nalloc = 0; sfree(grid->dcx2); sfree(grid->dcy2); sfree(grid->dcz2); grid->dc_nalloc = 0; if (debug) { fprintf(debug, "Successfully freed memory for grid pointers."); } sfree(grid); }
static void get_shifts_group( int npbcdim, const matrix box, rvec *xcoll, /* IN: Collective set of positions [0..nr] */ int nr, /* IN: Total number of atoms in the group */ rvec *xcoll_old, /* IN: Positions from the last time step [0...nr] */ ivec *shifts) /* OUT: Shifts for xcoll */ { int i, m, d; rvec dx; /* Get the shifts such that each atom is within closest * distance to its position at the last NS time step after shifting. * If we start with a whole group, and always keep track of * shift changes, the group will stay whole this way */ for (i = 0; i < nr; i++) { clear_ivec(shifts[i]); } for (i = 0; i < nr; i++) { /* The distance this atom moved since the last time step */ /* If this is more than just a bit, it has changed its home pbc box */ rvec_sub(xcoll[i], xcoll_old[i], dx); for (m = npbcdim-1; m >= 0; m--) { while (dx[m] < -0.5*box[m][m]) { for (d = 0; d < DIM; d++) { dx[d] += box[m][d]; } shifts[i][m]++; } while (dx[m] >= 0.5*box[m][m]) { for (d = 0; d < DIM; d++) { dx[d] -= box[m][d]; } shifts[i][m]--; } } } }
/*! \brief Determine the optimal distribution of DD cells for the simulation system and number of MPI ranks */ static real optimize_ncells(FILE *fplog, int nnodes_tot, int npme_only, gmx_bool bDynLoadBal, real dlb_scale, gmx_mtop_t *mtop, matrix box, gmx_ddbox_t *ddbox, t_inputrec *ir, gmx_domdec_t *dd, real cellsize_limit, real cutoff, gmx_bool bInterCGBondeds, ivec nc) { int npp, npme, ndiv, *div, *mdiv, d, nmax; double pbcdxr; real limit; ivec itry; limit = cellsize_limit; dd->nc[XX] = 1; dd->nc[YY] = 1; dd->nc[ZZ] = 1; npp = nnodes_tot - npme_only; if (EEL_PME(ir->coulombtype)) { npme = (npme_only > 0 ? npme_only : npp); } else { npme = 0; } if (bInterCGBondeds) { /* If we can skip PBC for distance calculations in plain-C bondeds, * we can save some time (e.g. 3D DD with pbc=xyz). * Here we ignore SIMD bondeds as they always do (fast) PBC. */ count_bonded_distances(mtop, ir, &pbcdxr, NULL); pbcdxr /= (double)mtop->natoms; } else { /* Every molecule is a single charge group: no pbc required */ pbcdxr = 0; } /* Add a margin for DLB and/or pressure scaling */ if (bDynLoadBal) { if (dlb_scale >= 1.0) { gmx_fatal(FARGS, "The value for option -dds should be smaller than 1"); } if (fplog) { fprintf(fplog, "Scaling the initial minimum size with 1/%g (option -dds) = %g\n", dlb_scale, 1/dlb_scale); } limit /= dlb_scale; } else if (ir->epc != epcNO) { if (fplog) { fprintf(fplog, "To account for pressure scaling, scaling the initial minimum size with %g\n", DD_GRID_MARGIN_PRES_SCALE); limit *= DD_GRID_MARGIN_PRES_SCALE; } } if (fplog) { fprintf(fplog, "Optimizing the DD grid for %d cells with a minimum initial size of %.3f nm\n", npp, limit); if (inhomogeneous_z(ir)) { fprintf(fplog, "Ewald_geometry=%s: assuming inhomogeneous particle distribution in z, will not decompose in z.\n", eewg_names[ir->ewald_geometry]); } if (limit > 0) { fprintf(fplog, "The maximum allowed number of cells is:"); for (d = 0; d < DIM; d++) { nmax = (int)(ddbox->box_size[d]*ddbox->skew_fac[d]/limit); if (d >= ddbox->npbcdim && nmax < 2) { nmax = 2; } if (d == ZZ && inhomogeneous_z(ir)) { nmax = 1; } fprintf(fplog, " %c %d", 'X' + d, nmax); } fprintf(fplog, "\n"); } } if (debug) { fprintf(debug, "Average nr of pbc_dx calls per atom %.2f\n", pbcdxr); } /* Decompose npp in factors */ ndiv = factorize(npp, &div, &mdiv); itry[XX] = 1; itry[YY] = 1; itry[ZZ] = 1; clear_ivec(nc); assign_factors(dd, limit, cutoff, box, ddbox, mtop->natoms, ir, pbcdxr, npme, ndiv, div, mdiv, itry, nc); sfree(div); sfree(mdiv); return limit; }
static gmx_bool pme_loadbal_increase_cutoff(pme_load_balancing_t pme_lb, int pme_order, const gmx_domdec_t *dd) { pme_setup_t *set; int npmenodes_x, npmenodes_y; real fac, sp; real tmpr_coulomb, tmpr_vdw; int d; gmx_bool grid_ok; /* Try to add a new setup with next larger cut-off to the list */ pme_lb->n++; srenew(pme_lb->setup, pme_lb->n); set = &pme_lb->setup[pme_lb->n-1]; set->pmedata = NULL; get_pme_nnodes(dd, &npmenodes_x, &npmenodes_y); fac = 1; do { /* Avoid infinite while loop, which can occur at the minimum grid size. * Note that in practice load balancing will stop before this point. * The factor 2.1 allows for the extreme case in which only grids * of powers of 2 are allowed (the current code supports more grids). */ if (fac > 2.1) { pme_lb->n--; return FALSE; } fac *= 1.01; clear_ivec(set->grid); sp = calc_grid(NULL, pme_lb->box_start, fac*pme_lb->setup[pme_lb->cur].spacing, &set->grid[XX], &set->grid[YY], &set->grid[ZZ]); /* As here we can't easily check if one of the PME nodes * uses threading, we do a conservative grid check. * This means we can't use pme_order or less grid lines * per PME node along x, which is not a strong restriction. */ gmx_pme_check_restrictions(pme_order, set->grid[XX], set->grid[YY], set->grid[ZZ], npmenodes_x, npmenodes_y, TRUE, FALSE, &grid_ok); } while (sp <= 1.001*pme_lb->setup[pme_lb->cur].spacing || !grid_ok); set->rcut_coulomb = pme_lb->cut_spacing*sp; if (set->rcut_coulomb < pme_lb->rcut_coulomb_start) { /* This is unlikely, but can happen when e.g. continuing from * a checkpoint after equilibration where the box shrank a lot. * We want to avoid rcoulomb getting smaller than rvdw * and there might be more issues with decreasing rcoulomb. */ set->rcut_coulomb = pme_lb->rcut_coulomb_start; } if (pme_lb->cutoff_scheme == ecutsVERLET) { set->rlist = set->rcut_coulomb + pme_lb->rbuf_coulomb; /* We dont use LR lists with Verlet, but this avoids if-statements in further checks */ set->rlistlong = set->rlist; } else { tmpr_coulomb = set->rcut_coulomb + pme_lb->rbuf_coulomb; tmpr_vdw = pme_lb->rcut_vdw + pme_lb->rbuf_vdw; set->rlist = min(tmpr_coulomb, tmpr_vdw); set->rlistlong = max(tmpr_coulomb, tmpr_vdw); /* Set the long-range update frequency */ if (set->rlist == set->rlistlong) { /* No long-range interactions if the short-/long-range cutoffs are identical */ set->nstcalclr = 0; } else if (pme_lb->nstcalclr_start == 0 || pme_lb->nstcalclr_start == 1) { /* We were not doing long-range before, but now we are since rlist!=rlistlong */ set->nstcalclr = 1; } else { /* We were already doing long-range interactions from the start */ if (pme_lb->rcut_vdw > pme_lb->rcut_coulomb_start) { /* We were originally doing long-range VdW-only interactions. * If rvdw is still longer than rcoulomb we keep the original nstcalclr, * but if the coulomb cutoff has become longer we should update the long-range * part every step. */ set->nstcalclr = (tmpr_vdw > tmpr_coulomb) ? pme_lb->nstcalclr_start : 1; } else { /* We were not doing any long-range interaction from the start, * since it is not possible to do twin-range coulomb for the PME interaction. */ set->nstcalclr = 1; } } } set->spacing = sp; /* The grid efficiency is the size wrt a grid with uniform x/y/z spacing */ set->grid_efficiency = 1; for (d = 0; d < DIM; d++) { set->grid_efficiency *= (set->grid[d]*sp)/norm(pme_lb->box_start[d]); } /* The Ewald coefficient is inversly proportional to the cut-off */ set->ewaldcoeff_q = pme_lb->setup[0].ewaldcoeff_q*pme_lb->setup[0].rcut_coulomb/set->rcut_coulomb; /* We set ewaldcoeff_lj in set, even when LJ-PME is not used */ set->ewaldcoeff_lj = pme_lb->setup[0].ewaldcoeff_lj*pme_lb->setup[0].rcut_coulomb/set->rcut_coulomb; set->count = 0; set->cycles = 0; if (debug) { fprintf(debug, "PME loadbal: grid %d %d %d, coulomb cutoff %f\n", set->grid[XX], set->grid[YY], set->grid[ZZ], set->rcut_coulomb); } return TRUE; }
void dd_move_f_specat(gmx_domdec_t *dd, gmx_domdec_specat_comm_t *spac, rvec *f, rvec *fshift) { gmx_specatsend_t *spas; rvec *vbuf; int n, n0, n1, d, dim, dir, i; ivec vis; int is; gmx_bool bPBC, bScrew; n = spac->at_end; for (d = dd->ndim-1; d >= 0; d--) { dim = dd->dim[d]; if (dd->nc[dim] > 2) { /* Pulse the grid forward and backward */ spas = spac->spas[d]; n0 = spas[0].nrecv; n1 = spas[1].nrecv; n -= n1 + n0; vbuf = spac->vbuf; /* Send and receive the coordinates */ dd_sendrecv2_rvec(dd, d, f+n+n1, n0, vbuf, spas[0].nsend, f+n, n1, vbuf+spas[0].nsend, spas[1].nsend); for (dir = 0; dir < 2; dir++) { bPBC = ((dir == 0 && dd->ci[dim] == 0) || (dir == 1 && dd->ci[dim] == dd->nc[dim]-1)); bScrew = (bPBC && dd->bScrewPBC && dim == XX); spas = &spac->spas[d][dir]; /* Sum the buffer into the required forces */ if (!bPBC || (!bScrew && fshift == NULL)) { for (i = 0; i < spas->nsend; i++) { rvec_inc(f[spas->a[i]], *vbuf); vbuf++; } } else { clear_ivec(vis); vis[dim] = (dir == 0 ? 1 : -1); is = IVEC2IS(vis); if (!bScrew) { /* Sum and add to shift forces */ for (i = 0; i < spas->nsend; i++) { rvec_inc(f[spas->a[i]], *vbuf); rvec_inc(fshift[is], *vbuf); vbuf++; } } else { /* Rotate the forces */ for (i = 0; i < spas->nsend; i++) { f[spas->a[i]][XX] += (*vbuf)[XX]; f[spas->a[i]][YY] -= (*vbuf)[YY]; f[spas->a[i]][ZZ] -= (*vbuf)[ZZ]; if (fshift) { rvec_inc(fshift[is], *vbuf); } vbuf++; } } } } } else { /* Two cells, so we only need to communicate one way */ spas = &spac->spas[d][0]; n -= spas->nrecv; /* Send and receive the coordinates */ dd_sendrecv_rvec(dd, d, dddirForward, f+n, spas->nrecv, spac->vbuf, spas->nsend); /* Sum the buffer into the required forces */ if (dd->bScrewPBC && dim == XX && (dd->ci[dim] == 0 || dd->ci[dim] == dd->nc[dim]-1)) { for (i = 0; i < spas->nsend; i++) { /* Rotate the force */ f[spas->a[i]][XX] += spac->vbuf[i][XX]; f[spas->a[i]][YY] -= spac->vbuf[i][YY]; f[spas->a[i]][ZZ] -= spac->vbuf[i][ZZ]; } } else { for (i = 0; i < spas->nsend; i++) { rvec_inc(f[spas->a[i]], spac->vbuf[i]); } } } } }
static real optimize_ncells(FILE *fplog, int nnodes_tot,int npme_only, bool bDynLoadBal,real dlb_scale, gmx_mtop_t *mtop,matrix box,gmx_ddbox_t *ddbox, t_inputrec *ir, gmx_domdec_t *dd, real cellsize_limit,real cutoff, bool bInterCGBondeds,bool bInterCGMultiBody, ivec nc) { int npp,npme,ndiv,*div,*mdiv,d,nmax; bool bExcl_pbcdx; float pbcdxr; real limit; ivec itry; limit = cellsize_limit; dd->nc[XX] = 1; dd->nc[YY] = 1; dd->nc[ZZ] = 1; npp = nnodes_tot - npme_only; if (EEL_PME(ir->coulombtype)) { npme = (npme_only > 0 ? npme_only : npp); } else { npme = 0; } if (bInterCGBondeds) { /* For Ewald exclusions pbc_dx is not called */ bExcl_pbcdx = (EEL_EXCL_FORCES(ir->coulombtype) && !EEL_FULL(ir->coulombtype)); pbcdxr = (double)n_bonded_dx(mtop,bExcl_pbcdx)/(double)mtop->natoms; } else { /* Every molecule is a single charge group: no pbc required */ pbcdxr = 0; } /* Add a margin for DLB and/or pressure scaling */ if (bDynLoadBal) { if (dlb_scale >= 1.0) { gmx_fatal(FARGS,"The value for option -dds should be smaller than 1"); } if (fplog) { fprintf(fplog,"Scaling the initial minimum size with 1/%g (option -dds) = %g\n",dlb_scale,1/dlb_scale); } limit /= dlb_scale; } else if (ir->epc != epcNO) { if (fplog) { fprintf(fplog,"To account for pressure scaling, scaling the initial minimum size with %g\n",DD_GRID_MARGIN_PRES_SCALE); limit *= DD_GRID_MARGIN_PRES_SCALE; } } if (fplog) { fprintf(fplog,"Optimizing the DD grid for %d cells with a minimum initial size of %.3f nm\n",npp,limit); if (limit > 0) { fprintf(fplog,"The maximum allowed number of cells is:"); for(d=0; d<DIM; d++) { nmax = (int)(ddbox->box_size[d]*ddbox->skew_fac[d]/limit); if (d >= ddbox->npbcdim && nmax < 2) { nmax = 2; } fprintf(fplog," %c %d",'X' + d,nmax); } fprintf(fplog,"\n"); } } if (debug) { fprintf(debug,"Average nr of pbc_dx calls per atom %.2f\n",pbcdxr); } /* Decompose npp in factors */ ndiv = factorize(npp,&div,&mdiv); itry[XX] = 1; itry[YY] = 1; itry[ZZ] = 1; clear_ivec(nc); assign_factors(dd,limit,cutoff,box,ddbox,ir,pbcdxr, npme,ndiv,div,mdiv,itry,nc); sfree(div); sfree(mdiv); return limit; }
static gmx_bool pme_loadbal_increase_cutoff(pme_load_balancing_t pme_lb, int pme_order) { pme_setup_t *set; real fac, sp; real tmpr_coulomb, tmpr_vdw; int d; /* Try to add a new setup with next larger cut-off to the list */ pme_lb->n++; srenew(pme_lb->setup, pme_lb->n); set = &pme_lb->setup[pme_lb->n-1]; set->pmedata = NULL; fac = 1; do { fac *= 1.01; clear_ivec(set->grid); sp = calc_grid(NULL, pme_lb->box_start, fac*pme_lb->setup[pme_lb->cur].spacing, &set->grid[XX], &set->grid[YY], &set->grid[ZZ]); /* In parallel we can't have grids smaller than 2*pme_order, * and we would anyhow not gain much speed at these grid sizes. */ for (d = 0; d < DIM; d++) { if (set->grid[d] <= 2*pme_order) { pme_lb->n--; return FALSE; } } } while (sp <= 1.001*pme_lb->setup[pme_lb->cur].spacing); set->rcut_coulomb = pme_lb->cut_spacing*sp; if (pme_lb->cutoff_scheme == ecutsVERLET) { set->rlist = set->rcut_coulomb + pme_lb->rbuf_coulomb; /* We dont use LR lists with Verlet, but this avoids if-statements in further checks */ set->rlistlong = set->rlist; } else { tmpr_coulomb = set->rcut_coulomb + pme_lb->rbuf_coulomb; tmpr_vdw = pme_lb->rcut_vdw + pme_lb->rbuf_vdw; set->rlist = min(tmpr_coulomb, tmpr_vdw); set->rlistlong = max(tmpr_coulomb, tmpr_vdw); /* Set the long-range update frequency */ if (set->rlist == set->rlistlong) { /* No long-range interactions if the short-/long-range cutoffs are identical */ set->nstcalclr = 0; } else if (pme_lb->nstcalclr_start == 0 || pme_lb->nstcalclr_start == 1) { /* We were not doing long-range before, but now we are since rlist!=rlistlong */ set->nstcalclr = 1; } else { /* We were already doing long-range interactions from the start */ if (pme_lb->rcut_vdw > pme_lb->rcut_coulomb_start) { /* We were originally doing long-range VdW-only interactions. * If rvdw is still longer than rcoulomb we keep the original nstcalclr, * but if the coulomb cutoff has become longer we should update the long-range * part every step. */ set->nstcalclr = (tmpr_vdw > tmpr_coulomb) ? pme_lb->nstcalclr_start : 1; } else { /* We were not doing any long-range interaction from the start, * since it is not possible to do twin-range coulomb for the PME interaction. */ set->nstcalclr = 1; } } } set->spacing = sp; /* The grid efficiency is the size wrt a grid with uniform x/y/z spacing */ set->grid_efficiency = 1; for (d = 0; d < DIM; d++) { set->grid_efficiency *= (set->grid[d]*sp)/norm(pme_lb->box_start[d]); } /* The Ewald coefficient is inversly proportional to the cut-off */ set->ewaldcoeff = pme_lb->setup[0].ewaldcoeff*pme_lb->setup[0].rcut_coulomb/set->rcut_coulomb; set->count = 0; set->cycles = 0; if (debug) { fprintf(debug, "PME loadbal: grid %d %d %d, coulomb cutoff %f\n", set->grid[XX], set->grid[YY], set->grid[ZZ], set->rcut_coulomb); } return TRUE; }
void update_QMMMrec(t_commrec *cr, t_forcerec *fr, rvec x[], t_mdatoms *md, matrix box, gmx_localtop_t *top) { /* updates the coordinates of both QM atoms and MM atoms and stores * them in the QMMMrec. * * NOTE: is NOT yet working if there are no PBC. Also in ns.c, simple * ns needs to be fixed! */ int mm_max = 0, mm_nr = 0, mm_nr_new, i, j, is, k, shift; t_j_particle *mm_j_particles = NULL, *qm_i_particles = NULL; t_QMMMrec *qr; t_nblist *QMMMlist; rvec dx, crd; t_QMrec *qm; t_MMrec *mm; t_pbc pbc; int *parallelMMarray = NULL; real c12au, c6au; c6au = (HARTREE2KJ*AVOGADRO*gmx::power6(BOHR2NM)); c12au = (HARTREE2KJ*AVOGADRO*gmx::power12(BOHR2NM)); /* every cpu has this array. On every processor we fill this array * with 1's and 0's. 1's indicate the atoms is a QM atom on the * current cpu in a later stage these arrays are all summed. indexes * > 0 indicate the atom is a QM atom. Every node therefore knows * whcih atoms are part of the QM subsystem. */ /* copy some pointers */ qr = fr->qr; mm = qr->mm; QMMMlist = fr->QMMMlist; /* init_pbc(box); needs to be called first, see pbc.h */ ivec null_ivec; clear_ivec(null_ivec); set_pbc_dd(&pbc, fr->ePBC, DOMAINDECOMP(cr) ? cr->dd->nc : null_ivec, FALSE, box); /* only in standard (normal) QMMM we need the neighbouring MM * particles to provide a electric field of point charges for the QM * atoms. */ if (qr->QMMMscheme == eQMMMschemenormal) /* also implies 1 QM-layer */ { /* we NOW create/update a number of QMMMrec entries: * * 1) the shiftQM, containing the shifts of the QM atoms * * 2) the indexMM array, containing the index of the MM atoms * * 3) the shiftMM, containing the shifts of the MM atoms * * 4) the shifted coordinates of the MM atoms * * the shifts are used for computing virial of the QM/MM particles. */ qm = qr->qm[0]; /* in case of normal QMMM, there is only one group */ snew(qm_i_particles, QMMMlist->nri); if (QMMMlist->nri) { qm_i_particles[0].shift = XYZ2IS(0, 0, 0); for (i = 0; i < QMMMlist->nri; i++) { qm_i_particles[i].j = QMMMlist->iinr[i]; if (i) { qm_i_particles[i].shift = pbc_dx_aiuc(&pbc, x[QMMMlist->iinr[0]], x[QMMMlist->iinr[i]], dx); } /* However, since nri >= nrQMatoms, we do a quicksort, and throw * out double, triple, etc. entries later, as we do for the MM * list too. */ /* compute the shift for the MM j-particles with respect to * the QM i-particle and store them. */ crd[0] = IS2X(QMMMlist->shift[i]) + IS2X(qm_i_particles[i].shift); crd[1] = IS2Y(QMMMlist->shift[i]) + IS2Y(qm_i_particles[i].shift); crd[2] = IS2Z(QMMMlist->shift[i]) + IS2Z(qm_i_particles[i].shift); is = static_cast<int>(XYZ2IS(crd[0], crd[1], crd[2])); for (j = QMMMlist->jindex[i]; j < QMMMlist->jindex[i+1]; j++) { if (mm_nr >= mm_max) { mm_max += 1000; srenew(mm_j_particles, mm_max); } mm_j_particles[mm_nr].j = QMMMlist->jjnr[j]; mm_j_particles[mm_nr].shift = is; mm_nr++; } } /* quicksort QM and MM shift arrays and throw away multiple entries */ qsort(qm_i_particles, QMMMlist->nri, (size_t)sizeof(qm_i_particles[0]), struct_comp); /* The mm_j_particles argument to qsort is not allowed to be NULL */ if (mm_nr > 0) { qsort(mm_j_particles, mm_nr, (size_t)sizeof(mm_j_particles[0]), struct_comp); } /* remove multiples in the QM shift array, since in init_QMMM() we * went through the atom numbers from 0 to md.nr, the order sorted * here matches the one of QMindex already. */ j = 0; for (i = 0; i < QMMMlist->nri; i++) { if (i == 0 || qm_i_particles[i].j != qm_i_particles[i-1].j) { qm_i_particles[j++] = qm_i_particles[i]; } } mm_nr_new = 0; if (qm->bTS || qm->bOPT) { /* only remove double entries for the MM array */ for (i = 0; i < mm_nr; i++) { if ((i == 0 || mm_j_particles[i].j != mm_j_particles[i-1].j) && !md->bQM[mm_j_particles[i].j]) { mm_j_particles[mm_nr_new++] = mm_j_particles[i]; } } } /* we also remove mm atoms that have no charges! * actually this is already done in the ns.c */ else { for (i = 0; i < mm_nr; i++) { if ((i == 0 || mm_j_particles[i].j != mm_j_particles[i-1].j) && !md->bQM[mm_j_particles[i].j] && (md->chargeA[mm_j_particles[i].j] || (md->chargeB && md->chargeB[mm_j_particles[i].j]))) { mm_j_particles[mm_nr_new++] = mm_j_particles[i]; } } } mm_nr = mm_nr_new; /* store the data retrieved above into the QMMMrec */ k = 0; /* Keep the compiler happy, * shift will always be set in the loop for i=0 */ shift = 0; for (i = 0; i < qm->nrQMatoms; i++) { /* not all qm particles might have appeared as i * particles. They might have been part of the same charge * group for instance. */ if (qm->indexQM[i] == qm_i_particles[k].j) { shift = qm_i_particles[k++].shift; } /* use previous shift, assuming they belong the same charge * group anyway, */ qm->shiftQM[i] = shift; } } /* parallel excecution */ if (PAR(cr)) { snew(parallelMMarray, 2*(md->nr)); /* only MM particles have a 1 at their atomnumber. The second part * of the array contains the shifts. Thus: * p[i]=1/0 depending on wether atomnumber i is a MM particle in the QM * step or not. p[i+md->nr] is the shift of atomnumber i. */ for (i = 0; i < 2*(md->nr); i++) { parallelMMarray[i] = 0; } for (i = 0; i < mm_nr; i++) { parallelMMarray[mm_j_particles[i].j] = 1; parallelMMarray[mm_j_particles[i].j+(md->nr)] = mm_j_particles[i].shift; } gmx_sumi(md->nr, parallelMMarray, cr); mm_nr = 0; mm_max = 0; for (i = 0; i < md->nr; i++) { if (parallelMMarray[i]) { if (mm_nr >= mm_max) { mm_max += 1000; srenew(mm->indexMM, mm_max); srenew(mm->shiftMM, mm_max); } mm->indexMM[mm_nr] = i; mm->shiftMM[mm_nr++] = parallelMMarray[i+md->nr]/parallelMMarray[i]; } } mm->nrMMatoms = mm_nr; free(parallelMMarray); } /* serial execution */ else { mm->nrMMatoms = mm_nr; srenew(mm->shiftMM, mm_nr); srenew(mm->indexMM, mm_nr); for (i = 0; i < mm_nr; i++) { mm->indexMM[i] = mm_j_particles[i].j; mm->shiftMM[i] = mm_j_particles[i].shift; } } /* (re) allocate memory for the MM coordiate array. The QM * coordinate array was already allocated in init_QMMM, and is * only (re)filled in the update_QMMM_coordinates routine */ srenew(mm->xMM, mm->nrMMatoms); /* now we (re) fill the array that contains the MM charges with * the forcefield charges. If requested, these charges will be * scaled by a factor */ srenew(mm->MMcharges, mm->nrMMatoms); for (i = 0; i < mm->nrMMatoms; i++) /* no free energy yet */ { mm->MMcharges[i] = md->chargeA[mm->indexMM[i]]*mm->scalefactor; } if (qm->bTS || qm->bOPT) { /* store (copy) the c6 and c12 parameters into the MMrec struct */ srenew(mm->c6, mm->nrMMatoms); srenew(mm->c12, mm->nrMMatoms); for (i = 0; i < mm->nrMMatoms; i++) { /* nbfp now includes the 6.0/12.0 derivative prefactors */ mm->c6[i] = C6(fr->nbfp, top->idef.atnr, md->typeA[mm->indexMM[i]], md->typeA[mm->indexMM[i]])/c6au/6.0; mm->c12[i] = C12(fr->nbfp, top->idef.atnr, md->typeA[mm->indexMM[i]], md->typeA[mm->indexMM[i]])/c12au/12.0; } punch_QMMM_excl(qr->qm[0], mm, &(top->excls)); } /* the next routine fills the coordinate fields in the QMMM rec of * both the qunatum atoms and the MM atoms, using the shifts * calculated above. */ update_QMMM_coord(x, fr, qr->qm[0], qr->mm); free(qm_i_particles); free(mm_j_particles); } else /* ONIOM */ /* ????? */ { mm->nrMMatoms = 0; /* do for each layer */ for (j = 0; j < qr->nrQMlayers; j++) { qm = qr->qm[j]; qm->shiftQM[0] = XYZ2IS(0, 0, 0); for (i = 1; i < qm->nrQMatoms; i++) { qm->shiftQM[i] = pbc_dx_aiuc(&pbc, x[qm->indexQM[0]], x[qm->indexQM[i]], dx); } update_QMMM_coord(x, fr, qm, mm); } } } /* update_QMMM_rec */
int pbc_dx_aiuc(const t_pbc *pbc, const rvec x1, const rvec x2, rvec dx) { int i, j, is; rvec dx_start, trial; real d2min, d2trial; ivec ishift, ishift_start; rvec_sub(x1, x2, dx); clear_ivec(ishift); switch (pbc->ePBCDX) { case epbcdxRECTANGULAR: for (i = 0; i < DIM; i++) { if (dx[i] > pbc->hbox_diag[i]) { dx[i] -= pbc->fbox_diag[i]; ishift[i]--; } else if (dx[i] <= pbc->mhbox_diag[i]) { dx[i] += pbc->fbox_diag[i]; ishift[i]++; } } break; case epbcdxTRICLINIC: /* For triclinic boxes the performance difference between * if/else and two while loops is negligible. * However, the while version can cause extreme delays * before a simulation crashes due to large forces which * can cause unlimited displacements. * Also allowing multiple shifts would index fshift beyond bounds. */ for (i = DIM-1; i >= 1; i--) { if (dx[i] > pbc->hbox_diag[i]) { for (j = i; j >= 0; j--) { dx[j] -= pbc->box[i][j]; } ishift[i]--; } else if (dx[i] <= pbc->mhbox_diag[i]) { for (j = i; j >= 0; j--) { dx[j] += pbc->box[i][j]; } ishift[i]++; } } /* Allow 2 shifts in x */ if (dx[XX] > pbc->hbox_diag[XX]) { dx[XX] -= pbc->fbox_diag[XX]; ishift[XX]--; if (dx[XX] > pbc->hbox_diag[XX]) { dx[XX] -= pbc->fbox_diag[XX]; ishift[XX]--; } } else if (dx[XX] <= pbc->mhbox_diag[XX]) { dx[XX] += pbc->fbox_diag[XX]; ishift[XX]++; if (dx[XX] <= pbc->mhbox_diag[XX]) { dx[XX] += pbc->fbox_diag[XX]; ishift[XX]++; } } /* dx is the distance in a rectangular box */ d2min = norm2(dx); if (d2min > pbc->max_cutoff2) { copy_rvec(dx, dx_start); copy_ivec(ishift, ishift_start); d2min = norm2(dx); /* Now try all possible shifts, when the distance is within max_cutoff * it must be the shortest possible distance. */ i = 0; while ((d2min > pbc->max_cutoff2) && (i < pbc->ntric_vec)) { rvec_add(dx_start, pbc->tric_vec[i], trial); d2trial = norm2(trial); if (d2trial < d2min) { copy_rvec(trial, dx); ivec_add(ishift_start, pbc->tric_shift[i], ishift); d2min = d2trial; } i++; } } break; case epbcdx2D_RECT: for (i = 0; i < DIM; i++) { if (i != pbc->dim) { if (dx[i] > pbc->hbox_diag[i]) { dx[i] -= pbc->fbox_diag[i]; ishift[i]--; } else if (dx[i] <= pbc->mhbox_diag[i]) { dx[i] += pbc->fbox_diag[i]; ishift[i]++; } } } break; case epbcdx2D_TRIC: d2min = 0; for (i = DIM-1; i >= 1; i--) { if (i != pbc->dim) { if (dx[i] > pbc->hbox_diag[i]) { for (j = i; j >= 0; j--) { dx[j] -= pbc->box[i][j]; } ishift[i]--; } else if (dx[i] <= pbc->mhbox_diag[i]) { for (j = i; j >= 0; j--) { dx[j] += pbc->box[i][j]; } ishift[i]++; } d2min += dx[i]*dx[i]; } } if (pbc->dim != XX) { /* Allow 2 shifts in x */ if (dx[XX] > pbc->hbox_diag[XX]) { dx[XX] -= pbc->fbox_diag[XX]; ishift[XX]--; if (dx[XX] > pbc->hbox_diag[XX]) { dx[XX] -= pbc->fbox_diag[XX]; ishift[XX]--; } } else if (dx[XX] <= pbc->mhbox_diag[XX]) { dx[XX] += pbc->fbox_diag[XX]; ishift[XX]++; if (dx[XX] <= pbc->mhbox_diag[XX]) { dx[XX] += pbc->fbox_diag[XX]; ishift[XX]++; } } d2min += dx[XX]*dx[XX]; } if (d2min > pbc->max_cutoff2) { copy_rvec(dx, dx_start); copy_ivec(ishift, ishift_start); /* Now try all possible shifts, when the distance is within max_cutoff * it must be the shortest possible distance. */ i = 0; while ((d2min > pbc->max_cutoff2) && (i < pbc->ntric_vec)) { rvec_add(dx_start, pbc->tric_vec[i], trial); d2trial = 0; for (j = 0; j < DIM; j++) { if (j != pbc->dim) { d2trial += trial[j]*trial[j]; } } if (d2trial < d2min) { copy_rvec(trial, dx); ivec_add(ishift_start, pbc->tric_shift[i], ishift); d2min = d2trial; } i++; } } break; case epbcdx1D_RECT: i = pbc->dim; if (dx[i] > pbc->hbox_diag[i]) { dx[i] -= pbc->fbox_diag[i]; ishift[i]--; } else if (dx[i] <= pbc->mhbox_diag[i]) { dx[i] += pbc->fbox_diag[i]; ishift[i]++; } break; case epbcdx1D_TRIC: i = pbc->dim; if (dx[i] > pbc->hbox_diag[i]) { rvec_dec(dx, pbc->box[i]); ishift[i]--; } else if (dx[i] <= pbc->mhbox_diag[i]) { rvec_inc(dx, pbc->box[i]); ishift[i]++; } break; case epbcdxSCREW_RECT: /* The shift definition requires x first */ if (dx[XX] > pbc->hbox_diag[XX]) { dx[XX] -= pbc->fbox_diag[XX]; ishift[XX]--; } else if (dx[XX] <= pbc->mhbox_diag[XX]) { dx[XX] += pbc->fbox_diag[XX]; ishift[XX]++; } if (ishift[XX] == 1 || ishift[XX] == -1) { /* Rotate around the x-axis in the middle of the box */ dx[YY] = pbc->box[YY][YY] - x1[YY] - x2[YY]; dx[ZZ] = pbc->box[ZZ][ZZ] - x1[ZZ] - x2[ZZ]; } /* Normal pbc for y and z */ for (i = YY; i <= ZZ; i++) { if (dx[i] > pbc->hbox_diag[i]) { dx[i] -= pbc->fbox_diag[i]; ishift[i]--; } else if (dx[i] <= pbc->mhbox_diag[i]) { dx[i] += pbc->fbox_diag[i]; ishift[i]++; } } break; case epbcdxNOPBC: case epbcdxUNSUPPORTED: break; default: gmx_fatal(FARGS, "Internal error in pbc_dx_aiuc, set_pbc_dd or set_pbc has not been called"); break; } is = IVEC2IS(ishift); if (debug) { range_check_mesg(is, 0, SHIFTS, "PBC shift vector index range check."); } return is; }