static void predict_shells(FILE *fplog, rvec x[], rvec v[], real dt, int ns, t_shell s[], real mass[], gmx_mtop_t *mtop, gmx_bool bInit) { int i, m, s1, n1, n2, n3; real dt_1, dt_2, dt_3, fudge, tm, m1, m2, m3; rvec *ptr; gmx_mtop_atomlookup_t alook = NULL; t_atom *atom; if (mass == NULL) { alook = gmx_mtop_atomlookup_init(mtop); } /* We introduce a fudge factor for performance reasons: with this choice * the initial force on the shells is about a factor of two lower than * without */ fudge = 1.0; if (bInit) { if (fplog) { fprintf(fplog, "RELAX: Using prediction for initial shell placement\n"); } ptr = x; dt_1 = 1; } else { ptr = v; dt_1 = fudge*dt; } for (i = 0; (i < ns); i++) { s1 = s[i].shell; if (bInit) { clear_rvec(x[s1]); } switch (s[i].nnucl) { case 1: n1 = s[i].nucl1; for (m = 0; (m < DIM); m++) { x[s1][m] += ptr[n1][m]*dt_1; } break; case 2: n1 = s[i].nucl1; n2 = s[i].nucl2; if (mass) { m1 = mass[n1]; m2 = mass[n2]; } else { /* Not the correct masses with FE, but it is just a prediction... */ m1 = atom[n1].m; m2 = atom[n2].m; } tm = dt_1/(m1+m2); for (m = 0; (m < DIM); m++) { x[s1][m] += (m1*ptr[n1][m]+m2*ptr[n2][m])*tm; } break; case 3: n1 = s[i].nucl1; n2 = s[i].nucl2; n3 = s[i].nucl3; if (mass) { m1 = mass[n1]; m2 = mass[n2]; m3 = mass[n3]; } else { /* Not the correct masses with FE, but it is just a prediction... */ gmx_mtop_atomnr_to_atom(alook, n1, &atom); m1 = atom->m; gmx_mtop_atomnr_to_atom(alook, n2, &atom); m2 = atom->m; gmx_mtop_atomnr_to_atom(alook, n3, &atom); m3 = atom->m; } tm = dt_1/(m1+m2+m3); for (m = 0; (m < DIM); m++) { x[s1][m] += (m1*ptr[n1][m]+m2*ptr[n2][m]+m3*ptr[n3][m])*tm; } break; default: gmx_fatal(FARGS, "Shell %d has %d nuclei!", i, s[i].nnucl); } } if (mass == NULL) { gmx_mtop_atomlookup_destroy(alook); } }
void atoms2md(const gmx_mtop_t *mtop, const t_inputrec *ir, int nindex, const int *index, int homenr, t_mdatoms *md) { gmx_bool bLJPME; gmx_mtop_atomlookup_t alook; int i; const t_grpopts *opts; const gmx_groups_t *groups; int nthreads gmx_unused; const real oneOverSix = 1.0 / 6.0; bLJPME = EVDW_PME(ir->vdwtype); opts = &ir->opts; groups = &mtop->groups; /* Index==NULL indicates no DD (unless we have a DD node with no * atoms), so also check for homenr. This should be * signaled properly with an extra parameter or nindex==-1. */ if (index == NULL && (homenr > 0)) { md->nr = mtop->natoms; } else { md->nr = nindex; } if (md->nr > md->nalloc) { md->nalloc = over_alloc_dd(md->nr); if (md->nMassPerturbed) { srenew(md->massA, md->nalloc); srenew(md->massB, md->nalloc); } srenew(md->massT, md->nalloc); srenew(md->invmass, md->nalloc); srenew(md->chargeA, md->nalloc); srenew(md->typeA, md->nalloc); if (md->nPerturbed) { srenew(md->chargeB, md->nalloc); srenew(md->typeB, md->nalloc); } if (bLJPME) { srenew(md->sqrt_c6A, md->nalloc); srenew(md->sigmaA, md->nalloc); srenew(md->sigma3A, md->nalloc); if (md->nPerturbed) { srenew(md->sqrt_c6B, md->nalloc); srenew(md->sigmaB, md->nalloc); srenew(md->sigma3B, md->nalloc); } } srenew(md->ptype, md->nalloc); if (opts->ngtc > 1) { srenew(md->cTC, md->nalloc); /* We always copy cTC with domain decomposition */ } srenew(md->cENER, md->nalloc); if (opts->ngacc > 1) { srenew(md->cACC, md->nalloc); } if (opts->nFreeze && (opts->ngfrz > 1 || opts->nFreeze[0][XX] || opts->nFreeze[0][YY] || opts->nFreeze[0][ZZ])) { srenew(md->cFREEZE, md->nalloc); } if (md->bVCMgrps) { srenew(md->cVCM, md->nalloc); } if (md->bOrires) { srenew(md->cORF, md->nalloc); } if (md->nPerturbed) { srenew(md->bPerturbed, md->nalloc); } /* Note that these user t_mdatoms array pointers are NULL * when there is only one group present. * Therefore, when adding code, the user should use something like: * gprnrU1 = (md->cU1==NULL ? 0 : md->cU1[localatindex]) */ if (mtop->groups.grpnr[egcUser1] != NULL) { srenew(md->cU1, md->nalloc); } if (mtop->groups.grpnr[egcUser2] != NULL) { srenew(md->cU2, md->nalloc); } if (ir->bQMMM) { srenew(md->bQM, md->nalloc); } if (ir->bAdress) { srenew(md->wf, md->nalloc); srenew(md->tf_table_index, md->nalloc); } } alook = gmx_mtop_atomlookup_init(mtop); // cppcheck-suppress unreadVariable nthreads = gmx_omp_nthreads_get(emntDefault); #pragma omp parallel for num_threads(nthreads) schedule(static) for (i = 0; i < md->nr; i++) { try { int g, ag; real mA, mB, fac; real c6, c12; t_atom *atom; if (index == NULL) { ag = i; } else { ag = index[i]; } gmx_mtop_atomnr_to_atom(alook, ag, &atom); if (md->cFREEZE) { md->cFREEZE[i] = ggrpnr(groups, egcFREEZE, ag); } if (EI_ENERGY_MINIMIZATION(ir->eI)) { /* Displacement is proportional to F, masses used for constraints */ mA = 1.0; mB = 1.0; } else if (ir->eI == eiBD) { /* With BD the physical masses are irrelevant. * To keep the code simple we use most of the normal MD code path * for BD. Thus for constraining the masses should be proportional * to the friction coefficient. We set the absolute value such that * m/2<(dx/dt)^2> = m/2*2kT/fric*dt = kT/2 => m=fric*dt/2 * Then if we set the (meaningless) velocity to v=dx/dt, we get the * correct kinetic energy and temperature using the usual code path. * Thus with BD v*dt will give the displacement and the reported * temperature can signal bad integration (too large time step). */ if (ir->bd_fric > 0) { mA = 0.5*ir->bd_fric*ir->delta_t; mB = 0.5*ir->bd_fric*ir->delta_t; } else { /* The friction coefficient is mass/tau_t */ fac = ir->delta_t/opts->tau_t[md->cTC ? groups->grpnr[egcTC][ag] : 0]; mA = 0.5*atom->m*fac; mB = 0.5*atom->mB*fac; } } else { mA = atom->m; mB = atom->mB; } if (md->nMassPerturbed) { md->massA[i] = mA; md->massB[i] = mB; } md->massT[i] = mA; if (mA == 0.0) { md->invmass[i] = 0; } else if (md->cFREEZE) { g = md->cFREEZE[i]; if (opts->nFreeze[g][XX] && opts->nFreeze[g][YY] && opts->nFreeze[g][ZZ]) { /* Set the mass of completely frozen particles to ALMOST_ZERO iso 0 * to avoid div by zero in lincs or shake. * Note that constraints can still move a partially frozen particle. */ md->invmass[i] = ALMOST_ZERO; } else { md->invmass[i] = 1.0/mA; } } else { md->invmass[i] = 1.0/mA; } md->chargeA[i] = atom->q; md->typeA[i] = atom->type; if (bLJPME) { c6 = mtop->ffparams.iparams[atom->type*(mtop->ffparams.atnr+1)].lj.c6; c12 = mtop->ffparams.iparams[atom->type*(mtop->ffparams.atnr+1)].lj.c12; md->sqrt_c6A[i] = sqrt(c6); if (c6 == 0.0 || c12 == 0) { md->sigmaA[i] = 1.0; } else { md->sigmaA[i] = pow(c12/c6, oneOverSix); } md->sigma3A[i] = 1/(md->sigmaA[i]*md->sigmaA[i]*md->sigmaA[i]); } if (md->nPerturbed) { md->bPerturbed[i] = PERTURBED(*atom); md->chargeB[i] = atom->qB; md->typeB[i] = atom->typeB; if (bLJPME) { c6 = mtop->ffparams.iparams[atom->typeB*(mtop->ffparams.atnr+1)].lj.c6; c12 = mtop->ffparams.iparams[atom->typeB*(mtop->ffparams.atnr+1)].lj.c12; md->sqrt_c6B[i] = sqrt(c6); if (c6 == 0.0 || c12 == 0) { md->sigmaB[i] = 1.0; } else { md->sigmaB[i] = pow(c12/c6, oneOverSix); } md->sigma3B[i] = 1/(md->sigmaB[i]*md->sigmaB[i]*md->sigmaB[i]); } } md->ptype[i] = atom->ptype; if (md->cTC) { md->cTC[i] = groups->grpnr[egcTC][ag]; } md->cENER[i] = (groups->grpnr[egcENER] ? groups->grpnr[egcENER][ag] : 0); if (md->cACC) { md->cACC[i] = groups->grpnr[egcACC][ag]; } if (md->cVCM) { md->cVCM[i] = groups->grpnr[egcVCM][ag]; } if (md->cORF) { md->cORF[i] = groups->grpnr[egcORFIT][ag]; } if (md->cU1) { md->cU1[i] = groups->grpnr[egcUser1][ag]; } if (md->cU2) { md->cU2[i] = groups->grpnr[egcUser2][ag]; } if (ir->bQMMM) { if (groups->grpnr[egcQMMM] == 0 || groups->grpnr[egcQMMM][ag] < groups->grps[egcQMMM].nr-1) { md->bQM[i] = TRUE; } else { md->bQM[i] = FALSE; } } /* Initialize AdResS weighting functions to adressw */ if (ir->bAdress) { md->wf[i] = 1.0; /* if no tf table groups specified, use default table */ md->tf_table_index[i] = DEFAULT_TF_TABLE; if (ir->adress->n_tf_grps > 0) { /* if tf table groups specified, tf is only applied to thoose energy groups*/ md->tf_table_index[i] = NO_TF_TABLE; /* check wether atom is in one of the relevant energy groups and assign a table index */ for (g = 0; g < ir->adress->n_tf_grps; g++) { if (md->cENER[i] == ir->adress->tf_table_index[g]) { md->tf_table_index[i] = g; } } } } } GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR; } gmx_mtop_atomlookup_destroy(alook); md->homenr = homenr; md->lambda = 0; }
static void clust_size(const char *ndx, const char *trx, const char *xpm, const char *xpmw, const char *ncl, const char *acl, const char *mcl, const char *histo, const char *tempf, const char *mcn, gmx_bool bMol, gmx_bool bPBC, const char *tpr, real cut, int nskip, int nlevels, t_rgb rmid, t_rgb rhi, int ndf, const output_env_t oenv) { FILE *fp, *gp, *hp, *tp; atom_id *index = NULL; int nindex, natoms; t_trxstatus *status; rvec *x = NULL, *v = NULL, dx; t_pbc pbc; char *gname; char timebuf[32]; gmx_bool bSame, bTPRwarn = TRUE; /* Topology stuff */ t_trxframe fr; t_tpxheader tpxh; gmx_mtop_t *mtop = NULL; int ePBC = -1; t_block *mols = NULL; gmx_mtop_atomlookup_t alook; t_atom *atom; int version, generation, ii, jj; real temp, tfac; /* Cluster size distribution (matrix) */ real **cs_dist = NULL; real tf, dx2, cut2, *t_x = NULL, *t_y, cmid, cmax, cav, ekin; int i, j, k, ai, aj, ci, cj, nframe, nclust, n_x, max_size = 0; int *clust_index, *clust_size, max_clust_size, max_clust_ind, nav, nhisto; t_rgb rlo = { 1.0, 1.0, 1.0 }; clear_trxframe(&fr, TRUE); sprintf(timebuf, "Time (%s)", output_env_get_time_unit(oenv)); tf = output_env_get_time_factor(oenv); fp = xvgropen(ncl, "Number of clusters", timebuf, "N", oenv); gp = xvgropen(acl, "Average cluster size", timebuf, "#molecules", oenv); hp = xvgropen(mcl, "Max cluster size", timebuf, "#molecules", oenv); tp = xvgropen(tempf, "Temperature of largest cluster", timebuf, "T (K)", oenv); if (!read_first_frame(oenv, &status, trx, &fr, TRX_NEED_X | TRX_READ_V)) { gmx_file(trx); } natoms = fr.natoms; x = fr.x; if (tpr) { snew(mtop, 1); read_tpxheader(tpr, &tpxh, TRUE, &version, &generation); if (tpxh.natoms != natoms) { gmx_fatal(FARGS, "tpr (%d atoms) and trajectory (%d atoms) do not match!", tpxh.natoms, natoms); } ePBC = read_tpx(tpr, NULL, NULL, &natoms, NULL, NULL, mtop); } if (ndf <= -1) { tfac = 1; } else { tfac = ndf/(3.0*natoms); } if (bMol) { if (ndx) { printf("Using molecules rather than atoms. Not reading index file %s\n", ndx); } GMX_RELEASE_ASSERT(mtop != NULL, "Trying to access mtop->mols from NULL mtop pointer"); mols = &(mtop->mols); /* Make dummy index */ nindex = mols->nr; snew(index, nindex); for (i = 0; (i < nindex); i++) { index[i] = i; } gname = gmx_strdup("mols"); } else { rd_index(ndx, 1, &nindex, &index, &gname); } alook = gmx_mtop_atomlookup_init(mtop); snew(clust_index, nindex); snew(clust_size, nindex); cut2 = cut*cut; nframe = 0; n_x = 0; snew(t_y, nindex); for (i = 0; (i < nindex); i++) { t_y[i] = i+1; } max_clust_size = 1; max_clust_ind = -1; do { if ((nskip == 0) || ((nskip > 0) && ((nframe % nskip) == 0))) { if (bPBC) { set_pbc(&pbc, ePBC, fr.box); } max_clust_size = 1; max_clust_ind = -1; /* Put all atoms/molecules in their own cluster, with size 1 */ for (i = 0; (i < nindex); i++) { /* Cluster index is indexed with atom index number */ clust_index[i] = i; /* Cluster size is indexed with cluster number */ clust_size[i] = 1; } /* Loop over atoms */ for (i = 0; (i < nindex); i++) { ai = index[i]; ci = clust_index[i]; /* Loop over atoms (only half a matrix) */ for (j = i+1; (j < nindex); j++) { cj = clust_index[j]; /* If they are not in the same cluster already */ if (ci != cj) { aj = index[j]; /* Compute distance */ if (bMol) { GMX_RELEASE_ASSERT(mols != NULL, "Cannot access index[] from NULL mols pointer"); bSame = FALSE; for (ii = mols->index[ai]; !bSame && (ii < mols->index[ai+1]); ii++) { for (jj = mols->index[aj]; !bSame && (jj < mols->index[aj+1]); jj++) { if (bPBC) { pbc_dx(&pbc, x[ii], x[jj], dx); } else { rvec_sub(x[ii], x[jj], dx); } dx2 = iprod(dx, dx); bSame = (dx2 < cut2); } } } else { if (bPBC) { pbc_dx(&pbc, x[ai], x[aj], dx); } else { rvec_sub(x[ai], x[aj], dx); } dx2 = iprod(dx, dx); bSame = (dx2 < cut2); } /* If distance less than cut-off */ if (bSame) { /* Merge clusters: check for all atoms whether they are in * cluster cj and if so, put them in ci */ for (k = 0; (k < nindex); k++) { if (clust_index[k] == cj) { if (clust_size[cj] <= 0) { gmx_fatal(FARGS, "negative cluster size %d for element %d", clust_size[cj], cj); } clust_size[cj]--; clust_index[k] = ci; clust_size[ci]++; } } } } } } n_x++; srenew(t_x, n_x); t_x[n_x-1] = fr.time*tf; srenew(cs_dist, n_x); snew(cs_dist[n_x-1], nindex); nclust = 0; cav = 0; nav = 0; for (i = 0; (i < nindex); i++) { ci = clust_size[i]; if (ci > max_clust_size) { max_clust_size = ci; max_clust_ind = i; } if (ci > 0) { nclust++; cs_dist[n_x-1][ci-1] += 1.0; max_size = std::max(max_size, ci); if (ci > 1) { cav += ci; nav++; } } } fprintf(fp, "%14.6e %10d\n", fr.time, nclust); if (nav > 0) { fprintf(gp, "%14.6e %10.3f\n", fr.time, cav/nav); } fprintf(hp, "%14.6e %10d\n", fr.time, max_clust_size); } /* Analyse velocities, if present */ if (fr.bV) { if (!tpr) { if (bTPRwarn) { printf("You need a [REF].tpr[ref] file to analyse temperatures\n"); bTPRwarn = FALSE; } } else { v = fr.v; /* Loop over clusters and for each cluster compute 1/2 m v^2 */ if (max_clust_ind >= 0) { ekin = 0; for (i = 0; (i < nindex); i++) { if (clust_index[i] == max_clust_ind) { ai = index[i]; gmx_mtop_atomnr_to_atom(alook, ai, &atom); ekin += 0.5*atom->m*iprod(v[ai], v[ai]); } } temp = (ekin*2.0)/(3.0*tfac*max_clust_size*BOLTZ); fprintf(tp, "%10.3f %10.3f\n", fr.time, temp); } } } nframe++; } while (read_next_frame(oenv, status, &fr)); close_trx(status); xvgrclose(fp); xvgrclose(gp); xvgrclose(hp); xvgrclose(tp); gmx_mtop_atomlookup_destroy(alook); if (max_clust_ind >= 0) { fp = gmx_ffopen(mcn, "w"); fprintf(fp, "[ max_clust ]\n"); for (i = 0; (i < nindex); i++) { if (clust_index[i] == max_clust_ind) { if (bMol) { GMX_RELEASE_ASSERT(mols != NULL, "Cannot access index[] from NULL mols pointer"); for (j = mols->index[i]; (j < mols->index[i+1]); j++) { fprintf(fp, "%d\n", j+1); } } else { fprintf(fp, "%d\n", index[i]+1); } } } gmx_ffclose(fp); } /* Print the real distribution cluster-size/numer, averaged over the trajectory. */ fp = xvgropen(histo, "Cluster size distribution", "Cluster size", "()", oenv); nhisto = 0; fprintf(fp, "%5d %8.3f\n", 0, 0.0); for (j = 0; (j < max_size); j++) { real nelem = 0; for (i = 0; (i < n_x); i++) { nelem += cs_dist[i][j]; } fprintf(fp, "%5d %8.3f\n", j+1, nelem/n_x); nhisto += static_cast<int>((j+1)*nelem/n_x); } fprintf(fp, "%5d %8.3f\n", j+1, 0.0); xvgrclose(fp); fprintf(stderr, "Total number of atoms in clusters = %d\n", nhisto); /* Look for the smallest entry that is not zero * This will make that zero is white, and not zero is coloured. */ cmid = 100.0; cmax = 0.0; for (i = 0; (i < n_x); i++) { for (j = 0; (j < max_size); j++) { if ((cs_dist[i][j] > 0) && (cs_dist[i][j] < cmid)) { cmid = cs_dist[i][j]; } cmax = std::max(cs_dist[i][j], cmax); } } fprintf(stderr, "cmid: %g, cmax: %g, max_size: %d\n", cmid, cmax, max_size); cmid = 1; fp = gmx_ffopen(xpm, "w"); write_xpm3(fp, 0, "Cluster size distribution", "# clusters", timebuf, "Size", n_x, max_size, t_x, t_y, cs_dist, 0, cmid, cmax, rlo, rmid, rhi, &nlevels); gmx_ffclose(fp); cmid = 100.0; cmax = 0.0; for (i = 0; (i < n_x); i++) { for (j = 0; (j < max_size); j++) { cs_dist[i][j] *= (j+1); if ((cs_dist[i][j] > 0) && (cs_dist[i][j] < cmid)) { cmid = cs_dist[i][j]; } cmax = std::max(cs_dist[i][j], cmax); } } fprintf(stderr, "cmid: %g, cmax: %g, max_size: %d\n", cmid, cmax, max_size); fp = gmx_ffopen(xpmw, "w"); write_xpm3(fp, 0, "Weighted cluster size distribution", "Fraction", timebuf, "Size", n_x, max_size, t_x, t_y, cs_dist, 0, cmid, cmax, rlo, rmid, rhi, &nlevels); gmx_ffclose(fp); sfree(clust_index); sfree(clust_size); sfree(index); }
/*! \brief Looks up constraint for the local atoms */ static void atoms_to_constraints(gmx_domdec_t *dd, const gmx_mtop_t *mtop, const int *cginfo, const t_blocka *at2con_mt, int nrec, t_ilist *ilc_local, ind_req_t *ireq) { const t_blocka *at2con; gmx_ga2la_t *ga2la; gmx_mtop_atomlookup_t alook; int ncon1; gmx_molblock_t *molb; t_iatom *ia1, *ia2, *iap; int nhome, cg, a, a_gl, a_mol, a_loc, b_lo, offset, mb, molnr, b_mol, i, con, con_offset; gmx_domdec_constraints_t *dc; gmx_domdec_specat_comm_t *dcc; dc = dd->constraints; dcc = dd->constraint_comm; ga2la = dd->ga2la; alook = gmx_mtop_atomlookup_init(mtop); nhome = 0; for (cg = 0; cg < dd->ncg_home; cg++) { if (GET_CGINFO_CONSTR(cginfo[cg])) { for (a = dd->cgindex[cg]; a < dd->cgindex[cg+1]; a++) { a_gl = dd->gatindex[a]; gmx_mtop_atomnr_to_molblock_ind(alook, a_gl, &mb, &molnr, &a_mol); molb = &mtop->molblock[mb]; ncon1 = mtop->moltype[molb->type].ilist[F_CONSTR].nr/NRAL(F_SETTLE); ia1 = mtop->moltype[molb->type].ilist[F_CONSTR].iatoms; ia2 = mtop->moltype[molb->type].ilist[F_CONSTRNC].iatoms; /* Calculate the global constraint number offset for the molecule. * This is only required for the global index to make sure * that we use each constraint only once. */ con_offset = dc->molb_con_offset[mb] + molnr*dc->molb_ncon_mol[mb]; /* The global atom number offset for this molecule */ offset = a_gl - a_mol; at2con = &at2con_mt[molb->type]; for (i = at2con->index[a_mol]; i < at2con->index[a_mol+1]; i++) { con = at2con->a[i]; iap = constr_iatomptr(ncon1, ia1, ia2, con); if (a_mol == iap[1]) { b_mol = iap[2]; } else { b_mol = iap[1]; } if (ga2la_get_home(ga2la, offset+b_mol, &a_loc)) { /* Add this fully home constraint at the first atom */ if (a_mol < b_mol) { if (dc->ncon+1 > dc->con_nalloc) { dc->con_nalloc = over_alloc_large(dc->ncon+1); srenew(dc->con_gl, dc->con_nalloc); srenew(dc->con_nlocat, dc->con_nalloc); } dc->con_gl[dc->ncon] = con_offset + con; dc->con_nlocat[dc->ncon] = 2; if (ilc_local->nr + 3 > ilc_local->nalloc) { ilc_local->nalloc = over_alloc_dd(ilc_local->nr + 3); srenew(ilc_local->iatoms, ilc_local->nalloc); } b_lo = a_loc; ilc_local->iatoms[ilc_local->nr++] = iap[0]; ilc_local->iatoms[ilc_local->nr++] = (a_gl == iap[1] ? a : b_lo); ilc_local->iatoms[ilc_local->nr++] = (a_gl == iap[1] ? b_lo : a ); dc->ncon++; nhome++; } } else { /* We need the nrec constraints coupled to this constraint, * so we need to walk out of the home cell by nrec+1 atoms, * since already atom bg is not locally present. * Therefore we call walk_out with nrec recursions to go * after this first call. */ walk_out(con, con_offset, b_mol, offset, nrec, ncon1, ia1, ia2, at2con, dd->ga2la, TRUE, dc, dcc, ilc_local, ireq); } } } } } gmx_mtop_atomlookup_destroy(alook); if (debug) { fprintf(debug, "Constraints: home %3d border %3d atoms: %3d\n", nhome, dc->ncon-nhome, dd->constraint_comm ? ireq->n : 0); } }
/*! \brief Looks up SETTLE constraints for a range of charge-groups */ static void atoms_to_settles(gmx_domdec_t *dd, const gmx_mtop_t *mtop, const int *cginfo, const int **at2settle_mt, int cg_start, int cg_end, t_ilist *ils_local, ind_req_t *ireq) { gmx_ga2la_t *ga2la; gmx_mtop_atomlookup_t alook; int settle; int nral, sa; int cg, a, a_gl, a_glsa, a_gls[3], a_locs[3]; int mb, molnr, a_mol, offset; const gmx_molblock_t *molb; const t_iatom *ia1; gmx_bool a_home[3]; int nlocal; gmx_bool bAssign; ga2la = dd->ga2la; alook = gmx_mtop_atomlookup_settle_init(mtop); nral = NRAL(F_SETTLE); for (cg = cg_start; cg < cg_end; cg++) { if (GET_CGINFO_SETTLE(cginfo[cg])) { for (a = dd->cgindex[cg]; a < dd->cgindex[cg+1]; a++) { a_gl = dd->gatindex[a]; gmx_mtop_atomnr_to_molblock_ind(alook, a_gl, &mb, &molnr, &a_mol); molb = &mtop->molblock[mb]; settle = at2settle_mt[molb->type][a_mol]; if (settle >= 0) { offset = a_gl - a_mol; ia1 = mtop->moltype[molb->type].ilist[F_SETTLE].iatoms; bAssign = FALSE; nlocal = 0; for (sa = 0; sa < nral; sa++) { a_glsa = offset + ia1[settle*(1+nral)+1+sa]; a_gls[sa] = a_glsa; a_home[sa] = ga2la_get_home(ga2la, a_glsa, &a_locs[sa]); if (a_home[sa]) { if (nlocal == 0 && a_gl == a_glsa) { bAssign = TRUE; } nlocal++; } } if (bAssign) { if (ils_local->nr+1+nral > ils_local->nalloc) { ils_local->nalloc = over_alloc_dd(ils_local->nr+1+nral); srenew(ils_local->iatoms, ils_local->nalloc); } ils_local->iatoms[ils_local->nr++] = ia1[settle*4]; for (sa = 0; sa < nral; sa++) { if (ga2la_get_home(ga2la, a_gls[sa], &a_locs[sa])) { ils_local->iatoms[ils_local->nr++] = a_locs[sa]; } else { ils_local->iatoms[ils_local->nr++] = -a_gls[sa] - 1; /* Add this non-home atom to the list */ if (ireq->n+1 > ireq->nalloc) { ireq->nalloc = over_alloc_large(ireq->n+1); srenew(ireq->ind, ireq->nalloc); } ireq->ind[ireq->n++] = a_gls[sa]; /* A check on double atom requests is * not required for settle. */ } } } } } } } gmx_mtop_atomlookup_destroy(alook); }
static void init_pull_group_index(FILE *fplog, t_commrec *cr, int start, int end, int g, t_pull_group *pg, ivec pulldims, gmx_mtop_t *mtop, t_inputrec *ir, real lambda) { int i, ii, d, nfrozen, ndim; real m, w, mbd; double tmass, wmass, wwmass; gmx_bool bDomDec; gmx_ga2la_t ga2la = NULL; gmx_groups_t *groups; gmx_mtop_atomlookup_t alook; t_atom *atom; bDomDec = (cr && DOMAINDECOMP(cr)); if (bDomDec) { ga2la = cr->dd->ga2la; } if (EI_ENERGY_MINIMIZATION(ir->eI) || ir->eI == eiBD) { /* There are no masses in the integrator. * But we still want to have the correct mass-weighted COMs. * So we store the real masses in the weights. * We do not set nweight, so these weights do not end up in the tpx file. */ if (pg->nweight == 0) { snew(pg->weight, pg->nat); } } if (cr && PAR(cr)) { pg->nat_loc = 0; pg->nalloc_loc = 0; pg->ind_loc = NULL; pg->weight_loc = NULL; } else { pg->nat_loc = pg->nat; pg->ind_loc = pg->ind; if (pg->epgrppbc == epgrppbcCOS) { snew(pg->weight_loc, pg->nat); } else { pg->weight_loc = pg->weight; } } groups = &mtop->groups; alook = gmx_mtop_atomlookup_init(mtop); nfrozen = 0; tmass = 0; wmass = 0; wwmass = 0; for (i = 0; i < pg->nat; i++) { ii = pg->ind[i]; gmx_mtop_atomnr_to_atom(alook, ii, &atom); if (cr && PAR(cr) && !bDomDec && ii >= start && ii < end) { pg->ind_loc[pg->nat_loc++] = ii; } if (ir->opts.nFreeze) { for (d = 0; d < DIM; d++) { if (pulldims[d] && ir->opts.nFreeze[ggrpnr(groups, egcFREEZE, ii)][d]) { nfrozen++; } } } if (ir->efep == efepNO) { m = atom->m; } else { m = (1 - lambda)*atom->m + lambda*atom->mB; } if (pg->nweight > 0) { w = pg->weight[i]; } else { w = 1; } if (EI_ENERGY_MINIMIZATION(ir->eI)) { /* Move the mass to the weight */ w *= m; m = 1; pg->weight[i] = w; } else if (ir->eI == eiBD) { if (ir->bd_fric) { mbd = ir->bd_fric*ir->delta_t; } else { if (groups->grpnr[egcTC] == NULL) { mbd = ir->delta_t/ir->opts.tau_t[0]; } else { mbd = ir->delta_t/ir->opts.tau_t[groups->grpnr[egcTC][ii]]; } } w *= m/mbd; m = mbd; pg->weight[i] = w; } tmass += m; wmass += m*w; wwmass += m*w*w; } gmx_mtop_atomlookup_destroy(alook); if (wmass == 0) { gmx_fatal(FARGS, "The total%s mass of pull group %d is zero", pg->weight ? " weighted" : "", g); } if (fplog) { fprintf(fplog, "Pull group %d: %5d atoms, mass %9.3f", g, pg->nat, tmass); if (pg->weight || EI_ENERGY_MINIMIZATION(ir->eI) || ir->eI == eiBD) { fprintf(fplog, ", weighted mass %9.3f", wmass*wmass/wwmass); } if (pg->epgrppbc == epgrppbcCOS) { fprintf(fplog, ", cosine weighting will be used"); } fprintf(fplog, "\n"); } if (nfrozen == 0) { /* A value > 0 signals not frozen, it is updated later */ pg->invtm = 1.0; } else { ndim = 0; for (d = 0; d < DIM; d++) { ndim += pulldims[d]*pg->nat; } if (fplog && nfrozen > 0 && nfrozen < ndim) { fprintf(fplog, "\nWARNING: In pull group %d some, but not all of the degrees of freedom\n" " that are subject to pulling are frozen.\n" " For pulling the whole group will be frozen.\n\n", g); } pg->invtm = 0.0; pg->wscale = 1.0; } }
void init_QMMMrec(t_commrec *cr, gmx_mtop_t *mtop, t_inputrec *ir, t_forcerec *fr) { /* we put the atomsnumbers of atoms that belong to the QMMM group in * an array that will be copied later to QMMMrec->indexQM[..]. Also * it will be used to create an QMMMrec->bQMMM index array that * simply contains true/false for QM and MM (the other) atoms. */ gmx_groups_t *groups; int *qm_arr = NULL, vsite, ai, aj; int qm_max = 0, qm_nr = 0, i, j, jmax, k, l, nrvsite2 = 0; t_QMMMrec *qr; t_MMrec *mm; t_iatom *iatoms; real c12au, c6au; gmx_mtop_atomloop_all_t aloop; t_atom *atom; gmx_mtop_ilistloop_all_t iloop; int a_offset; t_ilist *ilist_mol; gmx_mtop_atomlookup_t alook; c6au = (HARTREE2KJ*AVOGADRO*gmx::power6(BOHR2NM)); c12au = (HARTREE2KJ*AVOGADRO*gmx::power12(BOHR2NM)); /* issue a fatal if the user wants to run with more than one node */ if (PAR(cr)) { gmx_fatal(FARGS, "QM/MM does not work in parallel, use a single rank instead\n"); } /* Make a local copy of the QMMMrec */ qr = fr->qr; /* bQMMM[..] is an array containing TRUE/FALSE for atoms that are * QM/not QM. We first set all elemenst at false. Afterwards we use * the qm_arr (=MMrec->indexQM) to changes the elements * corresponding to the QM atoms at TRUE. */ qr->QMMMscheme = ir->QMMMscheme; /* we take the possibility into account that a user has * defined more than one QM group: */ /* an ugly work-around in case there is only one group In this case * the whole system is treated as QM. Otherwise the second group is * always the rest of the total system and is treated as MM. */ /* small problem if there is only QM.... so no MM */ jmax = ir->opts.ngQM; if (qr->QMMMscheme == eQMMMschemeoniom) { qr->nrQMlayers = jmax; } else { qr->nrQMlayers = 1; } groups = &mtop->groups; /* there are jmax groups of QM atoms. In case of multiple QM groups * I assume that the users wants to do ONIOM. However, maybe it * should also be possible to define more than one QM subsystem with * independent neighbourlists. I have to think about * that.. 11-11-2003 */ snew(qr->qm, jmax); for (j = 0; j < jmax; j++) { /* new layer */ aloop = gmx_mtop_atomloop_all_init(mtop); while (gmx_mtop_atomloop_all_next(aloop, &i, &atom)) { if (qm_nr >= qm_max) { qm_max += 1000; srenew(qm_arr, qm_max); } if (ggrpnr(groups, egcQMMM, i) == j) { /* hack for tip4p */ qm_arr[qm_nr++] = i; } } if (qr->QMMMscheme == eQMMMschemeoniom) { /* add the atoms to the bQMMM array */ /* I assume that users specify the QM groups from small to * big(ger) in the mdp file */ qr->qm[j] = mk_QMrec(); /* we need to throw out link atoms that in the previous layer * existed to separate this QMlayer from the previous * QMlayer. We use the iatoms array in the idef for that * purpose. If all atoms defining the current Link Atom (Dummy2) * are part of the current QM layer it needs to be removed from * qm_arr[]. */ iloop = gmx_mtop_ilistloop_all_init(mtop); while (gmx_mtop_ilistloop_all_next(iloop, &ilist_mol, &a_offset)) { nrvsite2 = ilist_mol[F_VSITE2].nr; iatoms = ilist_mol[F_VSITE2].iatoms; for (k = 0; k < nrvsite2; k += 4) { vsite = a_offset + iatoms[k+1]; /* the vsite */ ai = a_offset + iatoms[k+2]; /* constructing atom */ aj = a_offset + iatoms[k+3]; /* constructing atom */ if (ggrpnr(groups, egcQMMM, vsite) == ggrpnr(groups, egcQMMM, ai) && ggrpnr(groups, egcQMMM, vsite) == ggrpnr(groups, egcQMMM, aj)) { /* this dummy link atom needs to be removed from the qm_arr * before making the QMrec of this layer! */ for (i = 0; i < qm_nr; i++) { if (qm_arr[i] == vsite) { /* drop the element */ for (l = i; l < qm_nr; l++) { qm_arr[l] = qm_arr[l+1]; } qm_nr--; } } } } } /* store QM atoms in this layer in the QMrec and initialise layer */ init_QMrec(j, qr->qm[j], qm_nr, qm_arr, mtop, ir); /* we now store the LJ C6 and C12 parameters in QM rec in case * we need to do an optimization */ if (qr->qm[j]->bOPT || qr->qm[j]->bTS) { for (i = 0; i < qm_nr; i++) { /* nbfp now includes the 6.0/12.0 derivative prefactors */ qr->qm[j]->c6[i] = C6(fr->nbfp, mtop->ffparams.atnr, atom->type, atom->type)/c6au/6.0; qr->qm[j]->c12[i] = C12(fr->nbfp, mtop->ffparams.atnr, atom->type, atom->type)/c12au/12.0; } } /* now we check for frontier QM atoms. These occur in pairs that * construct the vsite */ iloop = gmx_mtop_ilistloop_all_init(mtop); while (gmx_mtop_ilistloop_all_next(iloop, &ilist_mol, &a_offset)) { nrvsite2 = ilist_mol[F_VSITE2].nr; iatoms = ilist_mol[F_VSITE2].iatoms; for (k = 0; k < nrvsite2; k += 4) { vsite = a_offset + iatoms[k+1]; /* the vsite */ ai = a_offset + iatoms[k+2]; /* constructing atom */ aj = a_offset + iatoms[k+3]; /* constructing atom */ if (ggrpnr(groups, egcQMMM, ai) < (groups->grps[egcQMMM].nr-1) && (ggrpnr(groups, egcQMMM, aj) >= (groups->grps[egcQMMM].nr-1))) { /* mark ai as frontier atom */ for (i = 0; i < qm_nr; i++) { if ( (qm_arr[i] == ai) || (qm_arr[i] == vsite) ) { qr->qm[j]->frontatoms[i] = TRUE; } } } else if (ggrpnr(groups, egcQMMM, aj) < (groups->grps[egcQMMM].nr-1) && (ggrpnr(groups, egcQMMM, ai) >= (groups->grps[egcQMMM].nr-1))) { /* mark aj as frontier atom */ for (i = 0; i < qm_nr; i++) { if ( (qm_arr[i] == aj) || (qm_arr[i] == vsite)) { qr->qm[j]->frontatoms[i] = TRUE; } } } } } } } if (qr->QMMMscheme != eQMMMschemeoniom) { /* standard QMMM, all layers are merged together so there is one QM * subsystem and one MM subsystem. * Also we set the charges to zero in the md->charge arrays to prevent * the innerloops from doubly counting the electostatic QM MM interaction */ alook = gmx_mtop_atomlookup_init(mtop); for (k = 0; k < qm_nr; k++) { gmx_mtop_atomnr_to_atom(alook, qm_arr[k], &atom); atom->q = 0.0; atom->qB = 0.0; } qr->qm[0] = mk_QMrec(); /* store QM atoms in the QMrec and initialise */ init_QMrec(0, qr->qm[0], qm_nr, qm_arr, mtop, ir); if (qr->qm[0]->bOPT || qr->qm[0]->bTS) { for (i = 0; i < qm_nr; i++) { gmx_mtop_atomnr_to_atom(alook, qm_arr[i], &atom); /* nbfp now includes the 6.0/12.0 derivative prefactors */ qr->qm[0]->c6[i] = C6(fr->nbfp, mtop->ffparams.atnr, atom->type, atom->type)/c6au/6.0; qr->qm[0]->c12[i] = C12(fr->nbfp, mtop->ffparams.atnr, atom->type, atom->type)/c12au/12.0; } } /* find frontier atoms and mark them true in the frontieratoms array. */ for (i = 0; i < qm_nr; i++) { gmx_mtop_atomnr_to_ilist(alook, qm_arr[i], &ilist_mol, &a_offset); nrvsite2 = ilist_mol[F_VSITE2].nr; iatoms = ilist_mol[F_VSITE2].iatoms; for (k = 0; k < nrvsite2; k += 4) { vsite = a_offset + iatoms[k+1]; /* the vsite */ ai = a_offset + iatoms[k+2]; /* constructing atom */ aj = a_offset + iatoms[k+3]; /* constructing atom */ if (ggrpnr(groups, egcQMMM, ai) < (groups->grps[egcQMMM].nr-1) && (ggrpnr(groups, egcQMMM, aj) >= (groups->grps[egcQMMM].nr-1))) { /* mark ai as frontier atom */ if ( (qm_arr[i] == ai) || (qm_arr[i] == vsite) ) { qr->qm[0]->frontatoms[i] = TRUE; } } else if (ggrpnr(groups, egcQMMM, aj) < (groups->grps[egcQMMM].nr-1) && (ggrpnr(groups, egcQMMM, ai) >= (groups->grps[egcQMMM].nr-1))) { /* mark aj as frontier atom */ if ( (qm_arr[i] == aj) || (qm_arr[i] == vsite) ) { qr->qm[0]->frontatoms[i] = TRUE; } } } } gmx_mtop_atomlookup_destroy(alook); /* MM rec creation */ mm = mk_MMrec(); mm->scalefactor = ir->scalefactor; mm->nrMMatoms = (mtop->natoms)-(qr->qm[0]->nrQMatoms); /* rest of the atoms */ qr->mm = mm; } else /* ONIOM */ { /* MM rec creation */ mm = mk_MMrec(); mm->scalefactor = ir->scalefactor; mm->nrMMatoms = 0; qr->mm = mm; } /* these variables get updated in the update QMMMrec */ if (qr->nrQMlayers == 1) { /* with only one layer there is only one initialisation * needed. Multilayer is a bit more complicated as it requires * re-initialisation at every step of the simulation. This is due * to the use of COMMON blocks in the fortran QM subroutines. */ if (qr->qm[0]->QMmethod < eQMmethodRHF) { #if GMX_QMMM_MOPAC /* semi-empiprical 1-layer ONIOM calculation requested (mopac93) */ init_mopac(qr->qm[0]); #else gmx_fatal(FARGS, "Semi-empirical QM only supported with Mopac."); #endif } else { /* ab initio calculation requested (gamess/gaussian/ORCA) */ #if GMX_QMMM_GAMESS init_gamess(cr, qr->qm[0], qr->mm); #elif GMX_QMMM_GAUSSIAN init_gaussian(qr->qm[0]); #elif GMX_QMMM_ORCA init_orca(qr->qm[0]); #else gmx_fatal(FARGS, "Ab-initio calculation only supported with Gamess, Gaussian or ORCA."); #endif } } } /* init_QMMMrec */
static void init_QMrec(int grpnr, t_QMrec *qm, int nr, int *atomarray, gmx_mtop_t *mtop, t_inputrec *ir) { /* fills the t_QMrec struct of QM group grpnr */ int i; gmx_mtop_atomlookup_t alook; t_atom *atom; qm->nrQMatoms = nr; snew(qm->xQM, nr); snew(qm->indexQM, nr); snew(qm->shiftQM, nr); /* the shifts */ for (i = 0; i < nr; i++) { qm->indexQM[i] = atomarray[i]; } alook = gmx_mtop_atomlookup_init(mtop); snew(qm->atomicnumberQM, nr); for (i = 0; i < qm->nrQMatoms; i++) { gmx_mtop_atomnr_to_atom(alook, qm->indexQM[i], &atom); qm->nelectrons += mtop->atomtypes.atomnumber[atom->type]; qm->atomicnumberQM[i] = mtop->atomtypes.atomnumber[atom->type]; } gmx_mtop_atomlookup_destroy(alook); qm->QMcharge = ir->opts.QMcharge[grpnr]; qm->multiplicity = ir->opts.QMmult[grpnr]; qm->nelectrons -= ir->opts.QMcharge[grpnr]; qm->QMmethod = ir->opts.QMmethod[grpnr]; qm->QMbasis = ir->opts.QMbasis[grpnr]; /* trajectory surface hopping setup (Gaussian only) */ qm->bSH = ir->opts.bSH[grpnr]; qm->CASorbitals = ir->opts.CASorbitals[grpnr]; qm->CASelectrons = ir->opts.CASelectrons[grpnr]; qm->SAsteps = ir->opts.SAsteps[grpnr]; qm->SAon = ir->opts.SAon[grpnr]; qm->SAoff = ir->opts.SAoff[grpnr]; /* hack to prevent gaussian from reinitializing all the time */ qm->nQMcpus = 0; /* number of CPU's to be used by g01, is set * upon initializing gaussian * (init_gaussian() */ /* print the current layer to allow users to check their input */ fprintf(stderr, "Layer %d\nnr of QM atoms %d\n", grpnr, nr); fprintf(stderr, "QMlevel: %s/%s\n\n", eQMmethod_names[qm->QMmethod], eQMbasis_names[qm->QMbasis]); /* frontier atoms */ snew(qm->frontatoms, nr); /* Lennard-Jones coefficients */ snew(qm->c6, nr); snew(qm->c12, nr); /* do we optimize the QM separately using the algorithms of the QM program?? */ qm->bTS = ir->opts.bTS[grpnr]; qm->bOPT = ir->opts.bOPT[grpnr]; } /* init_QMrec */