gmx_shellfc_t init_shell_flexcon(FILE *fplog, gmx_bool bCutoffSchemeIsVerlet, gmx_mtop_t *mtop, int nflexcon, rvec *x) { struct gmx_shellfc *shfc; t_shell *shell; int *shell_index = NULL, *at2cg; t_atom *atom; int n[eptNR], ns, nshell, nsi; int i, j, nmol, type, mb, mt, a_offset, cg, mol, ftype, nra; real qS, alpha; int aS, aN = 0; /* Shell and nucleus */ int bondtypes[] = { F_BONDS, F_HARMONIC, F_CUBICBONDS, F_POLARIZATION, F_ANHARM_POL, F_WATER_POL }; #define NBT asize(bondtypes) t_iatom *ia; gmx_mtop_atomloop_block_t aloopb; gmx_mtop_atomloop_all_t aloop; gmx_ffparams_t *ffparams; gmx_molblock_t *molb; gmx_moltype_t *molt; t_block *cgs; /* Count number of shells, and find their indices */ for (i = 0; (i < eptNR); i++) { n[i] = 0; } aloopb = gmx_mtop_atomloop_block_init(mtop); while (gmx_mtop_atomloop_block_next(aloopb, &atom, &nmol)) { n[atom->ptype] += nmol; } if (fplog) { /* Print the number of each particle type */ for (i = 0; (i < eptNR); i++) { if (n[i] != 0) { fprintf(fplog, "There are: %d %ss\n", n[i], ptype_str[i]); } } } nshell = n[eptShell]; if (nshell == 0 && nflexcon == 0) { /* We're not doing shells or flexible constraints */ return NULL; } if (bCutoffSchemeIsVerlet) { gmx_fatal(FARGS, "The shell code does not work with the Verlet cut-off scheme.\n"); } snew(shfc, 1); shfc->nflexcon = nflexcon; if (nshell == 0) { return shfc; } /* We have shells: fill the shell data structure */ /* Global system sized array, this should be avoided */ snew(shell_index, mtop->natoms); aloop = gmx_mtop_atomloop_all_init(mtop); nshell = 0; while (gmx_mtop_atomloop_all_next(aloop, &i, &atom)) { if (atom->ptype == eptShell) { shell_index[i] = nshell++; } } snew(shell, nshell); /* Initiate the shell structures */ for (i = 0; (i < nshell); i++) { shell[i].shell = NO_ATID; shell[i].nnucl = 0; shell[i].nucl1 = NO_ATID; shell[i].nucl2 = NO_ATID; shell[i].nucl3 = NO_ATID; /* shell[i].bInterCG=FALSE; */ shell[i].k_1 = 0; shell[i].k = 0; } ffparams = &mtop->ffparams; /* Now fill the structures */ shfc->bInterCG = FALSE; ns = 0; a_offset = 0; for (mb = 0; mb < mtop->nmolblock; mb++) { molb = &mtop->molblock[mb]; molt = &mtop->moltype[molb->type]; cgs = &molt->cgs; snew(at2cg, molt->atoms.nr); for (cg = 0; cg < cgs->nr; cg++) { for (i = cgs->index[cg]; i < cgs->index[cg+1]; i++) { at2cg[i] = cg; } } atom = molt->atoms.atom; for (mol = 0; mol < molb->nmol; mol++) { for (j = 0; (j < NBT); j++) { ia = molt->ilist[bondtypes[j]].iatoms; for (i = 0; (i < molt->ilist[bondtypes[j]].nr); ) { type = ia[0]; ftype = ffparams->functype[type]; nra = interaction_function[ftype].nratoms; /* Check whether we have a bond with a shell */ aS = NO_ATID; switch (bondtypes[j]) { case F_BONDS: case F_HARMONIC: case F_CUBICBONDS: case F_POLARIZATION: case F_ANHARM_POL: if (atom[ia[1]].ptype == eptShell) { aS = ia[1]; aN = ia[2]; } else if (atom[ia[2]].ptype == eptShell) { aS = ia[2]; aN = ia[1]; } break; case F_WATER_POL: aN = ia[4]; /* Dummy */ aS = ia[5]; /* Shell */ break; default: gmx_fatal(FARGS, "Death Horror: %s, %d", __FILE__, __LINE__); } if (aS != NO_ATID) { qS = atom[aS].q; /* Check whether one of the particles is a shell... */ nsi = shell_index[a_offset+aS]; if ((nsi < 0) || (nsi >= nshell)) { gmx_fatal(FARGS, "nsi is %d should be within 0 - %d. aS = %d", nsi, nshell, aS); } if (shell[nsi].shell == NO_ATID) { shell[nsi].shell = a_offset + aS; ns++; } else if (shell[nsi].shell != a_offset+aS) { gmx_fatal(FARGS, "Weird stuff in %s, %d", __FILE__, __LINE__); } if (shell[nsi].nucl1 == NO_ATID) { shell[nsi].nucl1 = a_offset + aN; } else if (shell[nsi].nucl2 == NO_ATID) { shell[nsi].nucl2 = a_offset + aN; } else if (shell[nsi].nucl3 == NO_ATID) { shell[nsi].nucl3 = a_offset + aN; } else { if (fplog) { pr_shell(fplog, ns, shell); } gmx_fatal(FARGS, "Can not handle more than three bonds per shell\n"); } if (at2cg[aS] != at2cg[aN]) { /* shell[nsi].bInterCG = TRUE; */ shfc->bInterCG = TRUE; } switch (bondtypes[j]) { case F_BONDS: case F_HARMONIC: shell[nsi].k += ffparams->iparams[type].harmonic.krA; break; case F_CUBICBONDS: shell[nsi].k += ffparams->iparams[type].cubic.kb; break; case F_POLARIZATION: case F_ANHARM_POL: if (!gmx_within_tol(qS, atom[aS].qB, GMX_REAL_EPS*10)) { gmx_fatal(FARGS, "polarize can not be used with qA(%e) != qB(%e) for atom %d of molecule block %d", qS, atom[aS].qB, aS+1, mb+1); } shell[nsi].k += sqr(qS)*ONE_4PI_EPS0/ ffparams->iparams[type].polarize.alpha; break; case F_WATER_POL: if (!gmx_within_tol(qS, atom[aS].qB, GMX_REAL_EPS*10)) { gmx_fatal(FARGS, "water_pol can not be used with qA(%e) != qB(%e) for atom %d of molecule block %d", qS, atom[aS].qB, aS+1, mb+1); } alpha = (ffparams->iparams[type].wpol.al_x+ ffparams->iparams[type].wpol.al_y+ ffparams->iparams[type].wpol.al_z)/3.0; shell[nsi].k += sqr(qS)*ONE_4PI_EPS0/alpha; break; default: gmx_fatal(FARGS, "Death Horror: %s, %d", __FILE__, __LINE__); } shell[nsi].nnucl++; } ia += nra+1; i += nra+1; } } a_offset += molt->atoms.nr; } /* Done with this molecule type */ sfree(at2cg); } /* Verify whether it's all correct */ if (ns != nshell) { gmx_fatal(FARGS, "Something weird with shells. They may not be bonded to something"); } for (i = 0; (i < ns); i++) { shell[i].k_1 = 1.0/shell[i].k; } if (debug) { pr_shell(debug, ns, shell); } shfc->nshell_gl = ns; shfc->shell_gl = shell; shfc->shell_index_gl = shell_index; shfc->bPredict = (getenv("GMX_NOPREDICT") == NULL); shfc->bRequireInit = FALSE; if (!shfc->bPredict) { if (fplog) { fprintf(fplog, "\nWill never predict shell positions\n"); } } else { shfc->bRequireInit = (getenv("GMX_REQUIRE_SHELL_INIT") != NULL); if (shfc->bRequireInit && fplog) { fprintf(fplog, "\nWill always initiate shell positions\n"); } } if (shfc->bPredict) { if (x) { predict_shells(fplog, x, NULL, 0, shfc->nshell_gl, shfc->shell_gl, NULL, mtop, TRUE); } if (shfc->bInterCG) { if (fplog) { fprintf(fplog, "\nNOTE: there all shells that are connected to particles outside thier own charge group, will not predict shells positions during the run\n\n"); } shfc->bPredict = FALSE; } } return shfc; }
gmx_shellfc_t *init_shell_flexcon(FILE *fplog, gmx_mtop_t *mtop, int nflexcon, int nstcalcenergy, bool usingDomainDecomposition) { gmx_shellfc_t *shfc; t_shell *shell; int *shell_index = NULL, *at2cg; t_atom *atom; int ns, nshell, nsi; int i, j, type, mb, a_offset, cg, mol, ftype, nra; real qS, alpha; int aS, aN = 0; /* Shell and nucleus */ int bondtypes[] = { F_BONDS, F_HARMONIC, F_CUBICBONDS, F_POLARIZATION, F_ANHARM_POL, F_WATER_POL }; #define NBT asize(bondtypes) t_iatom *ia; gmx_mtop_atomloop_all_t aloop; gmx_ffparams_t *ffparams; gmx_molblock_t *molb; gmx_moltype_t *molt; t_block *cgs; std::array<int, eptNR> n = countPtypes(fplog, mtop); nshell = n[eptShell]; if (nshell == 0 && nflexcon == 0) { /* We're not doing shells or flexible constraints */ return NULL; } snew(shfc, 1); shfc->nflexcon = nflexcon; if (nshell == 0) { /* Only flexible constraints, no shells. * Note that make_local_shells() does not need to be called. */ shfc->nshell = 0; shfc->bPredict = FALSE; return shfc; } if (nstcalcenergy != 1) { gmx_fatal(FARGS, "You have nstcalcenergy set to a value (%d) that is different from 1.\nThis is not supported in combination with shell particles.\nPlease make a new tpr file.", nstcalcenergy); } if (usingDomainDecomposition) { gmx_fatal(FARGS, "Shell particles are not implemented with domain decomposition, use a single rank"); } /* We have shells: fill the shell data structure */ /* Global system sized array, this should be avoided */ snew(shell_index, mtop->natoms); aloop = gmx_mtop_atomloop_all_init(mtop); nshell = 0; while (gmx_mtop_atomloop_all_next(aloop, &i, &atom)) { if (atom->ptype == eptShell) { shell_index[i] = nshell++; } } snew(shell, nshell); /* Initiate the shell structures */ for (i = 0; (i < nshell); i++) { shell[i].shell = -1; shell[i].nnucl = 0; shell[i].nucl1 = -1; shell[i].nucl2 = -1; shell[i].nucl3 = -1; /* shell[i].bInterCG=FALSE; */ shell[i].k_1 = 0; shell[i].k = 0; } ffparams = &mtop->ffparams; /* Now fill the structures */ shfc->bInterCG = FALSE; ns = 0; a_offset = 0; for (mb = 0; mb < mtop->nmolblock; mb++) { molb = &mtop->molblock[mb]; molt = &mtop->moltype[molb->type]; cgs = &molt->cgs; snew(at2cg, molt->atoms.nr); for (cg = 0; cg < cgs->nr; cg++) { for (i = cgs->index[cg]; i < cgs->index[cg+1]; i++) { at2cg[i] = cg; } } atom = molt->atoms.atom; for (mol = 0; mol < molb->nmol; mol++) { for (j = 0; (j < NBT); j++) { ia = molt->ilist[bondtypes[j]].iatoms; for (i = 0; (i < molt->ilist[bondtypes[j]].nr); ) { type = ia[0]; ftype = ffparams->functype[type]; nra = interaction_function[ftype].nratoms; /* Check whether we have a bond with a shell */ aS = -1; switch (bondtypes[j]) { case F_BONDS: case F_HARMONIC: case F_CUBICBONDS: case F_POLARIZATION: case F_ANHARM_POL: if (atom[ia[1]].ptype == eptShell) { aS = ia[1]; aN = ia[2]; } else if (atom[ia[2]].ptype == eptShell) { aS = ia[2]; aN = ia[1]; } break; case F_WATER_POL: aN = ia[4]; /* Dummy */ aS = ia[5]; /* Shell */ break; default: gmx_fatal(FARGS, "Death Horror: %s, %d", __FILE__, __LINE__); } if (aS != -1) { qS = atom[aS].q; /* Check whether one of the particles is a shell... */ nsi = shell_index[a_offset+aS]; if ((nsi < 0) || (nsi >= nshell)) { gmx_fatal(FARGS, "nsi is %d should be within 0 - %d. aS = %d", nsi, nshell, aS); } if (shell[nsi].shell == -1) { shell[nsi].shell = a_offset + aS; ns++; } else if (shell[nsi].shell != a_offset+aS) { gmx_fatal(FARGS, "Weird stuff in %s, %d", __FILE__, __LINE__); } if (shell[nsi].nucl1 == -1) { shell[nsi].nucl1 = a_offset + aN; } else if (shell[nsi].nucl2 == -1) { shell[nsi].nucl2 = a_offset + aN; } else if (shell[nsi].nucl3 == -1) { shell[nsi].nucl3 = a_offset + aN; } else { if (fplog) { pr_shell(fplog, ns, shell); } gmx_fatal(FARGS, "Can not handle more than three bonds per shell\n"); } if (at2cg[aS] != at2cg[aN]) { /* shell[nsi].bInterCG = TRUE; */ shfc->bInterCG = TRUE; } switch (bondtypes[j]) { case F_BONDS: case F_HARMONIC: shell[nsi].k += ffparams->iparams[type].harmonic.krA; break; case F_CUBICBONDS: shell[nsi].k += ffparams->iparams[type].cubic.kb; break; case F_POLARIZATION: case F_ANHARM_POL: if (!gmx_within_tol(qS, atom[aS].qB, GMX_REAL_EPS*10)) { gmx_fatal(FARGS, "polarize can not be used with qA(%e) != qB(%e) for atom %d of molecule block %d", qS, atom[aS].qB, aS+1, mb+1); } shell[nsi].k += gmx::square(qS)*ONE_4PI_EPS0/ ffparams->iparams[type].polarize.alpha; break; case F_WATER_POL: if (!gmx_within_tol(qS, atom[aS].qB, GMX_REAL_EPS*10)) { gmx_fatal(FARGS, "water_pol can not be used with qA(%e) != qB(%e) for atom %d of molecule block %d", qS, atom[aS].qB, aS+1, mb+1); } alpha = (ffparams->iparams[type].wpol.al_x+ ffparams->iparams[type].wpol.al_y+ ffparams->iparams[type].wpol.al_z)/3.0; shell[nsi].k += gmx::square(qS)*ONE_4PI_EPS0/alpha; break; default: gmx_fatal(FARGS, "Death Horror: %s, %d", __FILE__, __LINE__); } shell[nsi].nnucl++; } ia += nra+1; i += nra+1; } } a_offset += molt->atoms.nr; } /* Done with this molecule type */ sfree(at2cg); } /* Verify whether it's all correct */ if (ns != nshell) { gmx_fatal(FARGS, "Something weird with shells. They may not be bonded to something"); } for (i = 0; (i < ns); i++) { shell[i].k_1 = 1.0/shell[i].k; } if (debug) { pr_shell(debug, ns, shell); } shfc->nshell_gl = ns; shfc->shell_gl = shell; shfc->shell_index_gl = shell_index; shfc->bPredict = (getenv("GMX_NOPREDICT") == NULL); shfc->bRequireInit = FALSE; if (!shfc->bPredict) { if (fplog) { fprintf(fplog, "\nWill never predict shell positions\n"); } } else { shfc->bRequireInit = (getenv("GMX_REQUIRE_SHELL_INIT") != NULL); if (shfc->bRequireInit && fplog) { fprintf(fplog, "\nWill always initiate shell positions\n"); } } if (shfc->bPredict) { if (shfc->bInterCG) { if (fplog) { fprintf(fplog, "\nNOTE: there all shells that are connected to particles outside thier own charge group, will not predict shells positions during the run\n\n"); } /* Prediction improves performance, so we should implement either: * 1. communication for the atoms needed for prediction * 2. prediction using the velocities of shells; currently the * shell velocities are zeroed, it's a bit tricky to keep * track of the shell displacements and thus the velocity. */ shfc->bPredict = FALSE; } } return shfc; }