/* ---------------------------------------------------------------------- LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator https://www.lammps.org/, Sandia National Laboratories LAMMPS development team: developers@lammps.org Copyright (2003) Sandia Corporation. Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains certain rights in this software. This software is distributed under the GNU General Public License. See the README file in the top-level LAMMPS directory. ------------------------------------------------------------------------- */ #include "pair_hybrid_scaled.h" #include "atom.h" #include "atom_vec.h" #include "comm.h" #include "error.h" #include "force.h" #include "input.h" #include "memory.h" #include "respa.h" #include "suffix.h" #include "update.h" #include "variable.h" #include using namespace LAMMPS_NS; /* ---------------------------------------------------------------------- */ PairHybridScaled::PairHybridScaled(LAMMPS *lmp) : PairHybrid(lmp), fsum(nullptr), tsum(nullptr), scaleval(nullptr), scaleidx(nullptr), atomvar(nullptr), atomscale(nullptr) { nmaxfsum = -1; // set comm size needed by this Pair (if atomscaleflag) comm_forward = 1; } /* ---------------------------------------------------------------------- */ PairHybridScaled::~PairHybridScaled() { memory->destroy(fsum); memory->destroy(tsum); delete[] scaleval; delete[] scaleidx; delete[] atomvar; memory->destroy(atomscale); } /* ---------------------------------------------------------------------- call each sub-style's compute() or compute_outer() function accumulate sub-style global/peratom energy/virial in hybrid for global vflag = VIRIAL_PAIR: each sub-style computes own virial[6] sum sub-style virial[6] to hybrid's virial[6] for global vflag = VIRIAL_FDOTR: call sub-style with adjusted vflag to prevent it calling virial_fdotr_compute() hybrid calls virial_fdotr_compute() on final accumulated f ------------------------------------------------------------------------- */ void PairHybridScaled::compute(int eflag, int vflag) { int i, j, m, n; // update scale values from variables where needed const int nvars = scalevars.size(); int atomscaleflag = 0; if (nvars > 0) { auto vals = new double[nvars]; auto vars = new int[nvars]; for (int k = 0; k < nvars; ++k) { int m = input->variable->find(scalevars[k].c_str()); if (m < 0) error->all(FLERR, "Variable '{}' not found when updating scale factors", scalevars[k]); // for equal-style, compute variable, set variable index to -1 if (input->variable->equalstyle(m)) { vals[k] = input->variable->compute_equal(m); vars[k] = -1; // for atom-style, store variable index, set variable to 0.0, set atomscaleflag } else if (input->variable->atomstyle(m)) { vals[k] = 0.0; vars[k] = m; atomscaleflag = 1; } else error->all(FLERR, "Variable '{}' has incompatible style", scalevars[k]); } for (int k = 0; k < nstyles; ++k) { if (scaleidx[k] >= 0) { scaleval[k] = vals[scaleidx[k]]; atomvar[k] = vars[scaleidx[k]]; } } delete[] vals; delete[] vars; } // check if no_virial_fdotr_compute is set and global component of // incoming vflag = VIRIAL_FDOTR // if so, reset vflag as if global component were VIRIAL_PAIR // necessary since one or more sub-styles cannot compute virial as F dot r if (no_virial_fdotr_compute && (vflag & VIRIAL_FDOTR)) vflag = VIRIAL_PAIR | (vflag & ~VIRIAL_FDOTR); ev_init(eflag, vflag); // grow fsum array if needed, and copy existing forces (usually 0.0) to it. if (atom->nmax > nmaxfsum) { memory->destroy(fsum); if (atom->torque_flag) memory->destroy(tsum); if (atomscaleflag) memory->destroy(atomscale); nmaxfsum = atom->nmax; memory->create(fsum, nmaxfsum, 3, "pair:fsum"); if (atom->torque_flag) memory->create(tsum, nmaxfsum, 3, "pair:tsum"); if (atomscaleflag) memory->create(atomscale, nmaxfsum, "pair:atomscale"); } const int nall = atom->nlocal + atom->nghost; auto f = atom->f; auto t = atom->torque; for (i = 0; i < nall; ++i) { fsum[i][0] = f[i][0]; fsum[i][1] = f[i][1]; fsum[i][2] = f[i][2]; if (atom->torque_flag) { tsum[i][0] = t[i][0]; tsum[i][1] = t[i][1]; tsum[i][2] = t[i][2]; } } // check if global component of incoming vflag = VIRIAL_FDOTR // if so, reset vflag passed to substyle so VIRIAL_FDOTR is turned off // necessary so substyle will not invoke virial_fdotr_compute() int vflag_substyle; if (vflag & VIRIAL_FDOTR) vflag_substyle = vflag & ~VIRIAL_FDOTR; else vflag_substyle = vflag; double *saved_special = save_special(); // check if we are running with r-RESPA using the hybrid keyword Respa *respa = nullptr; respaflag = 0; if (utils::strmatch(update->integrate_style, "^respa")) { respa = dynamic_cast(update->integrate); if (respa->nhybrid_styles > 0) respaflag = 1; } for (m = 0; m < nstyles; m++) { // clear forces and torques memset(&f[0][0], 0, nall * 3 * sizeof(double)); if (atom->torque_flag) memset(&t[0][0], 0, nall * 3 * sizeof(double)); set_special(m); if (!respaflag || (respaflag && respa->hybrid_compute[m])) { // invoke compute() unless compute flag is turned off or // outerflag is set and sub-style has a compute_outer() method if (styles[m]->compute_flag == 0) continue; if (outerflag && styles[m]->respa_enable) styles[m]->compute_outer(eflag, vflag_substyle); else styles[m]->compute(eflag, vflag_substyle); } // add scaled forces to global sum const double scale = scaleval[m]; // if scale factor is constant or equal-style variable if (scaleidx[m] < 0 || atomvar[m] < 0) { for (i = 0; i < nall; ++i) { fsum[i][0] += scale * f[i][0]; fsum[i][1] += scale * f[i][1]; fsum[i][2] += scale * f[i][2]; if (atom->torque_flag) { tsum[i][0] += scale * t[i][0]; tsum[i][1] += scale * t[i][1]; tsum[i][2] += scale * t[i][2]; } } // if scale factor is atom-style variable } else { const int igroupall = 0; input->variable->compute_atom(atomvar[m], igroupall, atomscale, 1, 0); comm->forward_comm(this); for (i = 0; i < nall; ++i) { const double ascale = atomscale[i]; fsum[i][0] += ascale * f[i][0]; fsum[i][1] += ascale * f[i][1]; fsum[i][2] += ascale * f[i][2]; if (atom->torque_flag) { tsum[i][0] += ascale * t[i][0]; tsum[i][1] += ascale * t[i][1]; tsum[i][2] += ascale * t[i][2]; } } } restore_special(saved_special); // jump to next sub-style if r-RESPA does not want global accumulated data if (respaflag && !respa->tally_global) continue; if (eflag_global) { eng_vdwl += scale * styles[m]->eng_vdwl; eng_coul += scale * styles[m]->eng_coul; } if (vflag_global) { for (n = 0; n < 6; n++) virial[n] += scale * styles[m]->virial[n]; } if (eflag_atom) { n = atom->nlocal; if (force->newton_pair) n += atom->nghost; double *eatom_substyle = styles[m]->eatom; for (i = 0; i < n; i++) eatom[i] += scale * eatom_substyle[i]; } if (vflag_atom) { n = atom->nlocal; if (force->newton_pair) n += atom->nghost; double **vatom_substyle = styles[m]->vatom; for (i = 0; i < n; i++) for (j = 0; j < 6; j++) vatom[i][j] += scale * vatom_substyle[i][j]; } // substyles may be CENTROID_SAME or CENTROID_AVAIL if (cvflag_atom) { n = atom->nlocal; if (force->newton_pair) n += atom->nghost; if (styles[m]->centroidstressflag == CENTROID_AVAIL) { double **cvatom_substyle = styles[m]->cvatom; for (i = 0; i < n; i++) for (j = 0; j < 9; j++) cvatom[i][j] += scale * cvatom_substyle[i][j]; } else { double **vatom_substyle = styles[m]->vatom; for (i = 0; i < n; i++) { for (j = 0; j < 6; j++) { cvatom[i][j] += scale * vatom_substyle[i][j]; } for (j = 6; j < 9; j++) { cvatom[i][j] += scale * vatom_substyle[i][j - 3]; } } } } } // copy accumulated scaled forces to original force array for (i = 0; i < nall; ++i) { f[i][0] = fsum[i][0]; f[i][1] = fsum[i][1]; f[i][2] = fsum[i][2]; if (atom->torque_flag) { t[i][0] = tsum[i][0]; t[i][1] = tsum[i][1]; t[i][2] = tsum[i][2]; } } delete[] saved_special; if (vflag_fdotr) virial_fdotr_compute(); } /* ---------------------------------------------------------------------- create one pair style for each arg in list ------------------------------------------------------------------------- */ void PairHybridScaled::settings(int narg, char **arg) { if (narg < 1) error->all(FLERR, "Illegal pair_style command"); if (lmp->kokkos && !utils::strmatch(force->pair_style, "^hybrid.*/kk$")) error->all(FLERR, "Must use pair_style {}/kk with Kokkos", force->pair_style); if (atom->avec->forceclearflag) error->all(FLERR, "Atom style is not compatible with pair_style hybrid/scaled"); // delete old lists, since cannot just change settings if (nstyles > 0) { for (int m = 0; m < nstyles; m++) { delete styles[m]; delete[] keywords[m]; delete[] special_lj[m]; delete[] special_coul[m]; } delete[] styles; delete[] cutmax_style; delete[] keywords; delete[] multiple; delete[] special_lj; delete[] special_coul; delete[] compute_tally; delete[] scaleval; delete[] scaleidx; delete[] atomvar; scalevars.clear(); } if (allocated) { memory->destroy(setflag); memory->destroy(cutsq); memory->destroy(cutghost); memory->destroy(nmap); memory->destroy(map); } allocated = 0; // allocate list of sub-styles as big as possibly needed if no extra args styles = new Pair *[narg]; cutmax_style = new double[narg]; memset(cutmax_style, 0.0, narg * sizeof(double)); keywords = new char *[narg]; multiple = new int[narg]; special_lj = new double *[narg]; special_coul = new double *[narg]; compute_tally = new int[narg]; scaleval = new double[narg]; scaleidx = new int[narg]; atomvar = new int[narg]; scalevars.reserve(narg); // allocate each sub-style // allocate uses suffix, but don't store suffix version in keywords, // else syntax in coeff() will not match // call settings() with set of args that are not pair style names // use force->pair_map to determine which args these are int iarg, jarg, dummy; iarg = 0; nstyles = 0; while (iarg < narg - 1) { // first process scale factor or variable // scaleidx[k] < 0 indicates constant value, otherwise index in variable name list // initialize atomvar[k] to -1, indicates not atom-style variable double val = 0.0; int idx = -1; if (utils::strmatch(arg[iarg], "^v_")) { for (std::size_t i = 0; i < scalevars.size(); ++i) { if (scalevars[i] == arg[iarg] + 2) { idx = i; break; } } if (idx < 0) { idx = scalevars.size(); scalevars.emplace_back(arg[iarg] + 2); } } else { val = utils::numeric(FLERR, arg[iarg], false, lmp); } scaleval[nstyles] = val; scaleidx[nstyles] = idx; atomvar[nstyles] = -1; ++iarg; if (utils::strmatch(arg[iarg], "^hybrid")) error->all(FLERR, "Pair style hybrid/scaled cannot have hybrid as an argument"); if (strcmp(arg[iarg], "none") == 0) error->all(FLERR, "Pair style hybrid/scaled cannot have none as an argument"); styles[nstyles] = force->new_pair(arg[iarg], 1, dummy); keywords[nstyles] = force->store_style(arg[iarg], 0); special_lj[nstyles] = special_coul[nstyles] = nullptr; compute_tally[nstyles] = 1; if ((styles[nstyles]->suffix_flag & (Suffix::INTEL | Suffix::GPU | Suffix::OMP)) != 0) error->all(FLERR, "Pair style hybrid/scaled does not support " "accelerator styles"); // determine list of arguments for pair style settings // by looking for the next known pair style name. jarg = iarg + 1; while ((jarg < narg) && !force->pair_map->count(arg[jarg]) && !lmp->match_style("pair", arg[jarg])) jarg++; // decrement to account for scale factor except when last argument if (jarg < narg) --jarg; styles[nstyles]->settings(jarg - iarg - 1, arg + iarg + 1); iarg = jarg; nstyles++; } // multiple[i] = 1 to M if sub-style used multiple times, else 0 for (int i = 0; i < nstyles; i++) { int count = 0; for (int j = 0; j < nstyles; j++) { if (strcmp(keywords[j], keywords[i]) == 0) count++; if (j == i) multiple[i] = count; } if (count == 1) multiple[i] = 0; } // set pair flags from sub-style flags flags(); } /* ---------------------------------------------------------------------- call sub-style to compute single interaction error if sub-style does not support single() call since overlay could have multiple sub-styles, sum results explicitly ------------------------------------------------------------------------- */ double PairHybridScaled::single(int i, int j, int itype, int jtype, double rsq, double factor_coul, double factor_lj, double &fforce) { if (nmap[itype][jtype] == 0) error->one(FLERR, "Invoked pair single on pair style none"); // update scale values from variables where needed const int nvars = scalevars.size(); if (nvars > 0) { auto vals = new double[nvars]; auto vars = new int[nvars]; for (int k = 0; k < nvars; ++k) { int m = input->variable->find(scalevars[k].c_str()); if (m < 0) error->all(FLERR, "Variable '{}' not found when updating scale factors", scalevars[k]); // for equal-style, compute variable, set variable index to -1 if (input->variable->equalstyle(m)) { vals[k] = input->variable->compute_equal(m); vars[k] = -1; // for atom-style, store variable index, set variable to 0.0, set atomscaleflag } else if (input->variable->atomstyle(m)) { vals[k] = 0.0; vars[k] = m; } else error->all(FLERR, "Variable '{}' has incompatible style", scalevars[k]); } for (int k = 0; k < nstyles; ++k) { if (scaleidx[k] >= 0) { scaleval[k] = vals[scaleidx[k]]; atomvar[k] = vars[scaleidx[k]]; } } delete[] vals; delete[] vars; } double fone; fforce = 0.0; double esum = 0.0; for (int m = 0; m < nmap[itype][jtype]; m++) { auto pstyle = styles[map[itype][jtype][m]]; if (rsq < pstyle->cutsq[itype][jtype]) { if (pstyle->single_enable == 0) error->one(FLERR, "Pair hybrid sub-style does not support single call"); if ((special_lj[map[itype][jtype][m]] != nullptr) || (special_coul[map[itype][jtype][m]] != nullptr)) error->one(FLERR, "Pair hybrid single() does not support per sub-style special_bond"); double scale = scaleval[map[itype][jtype][m]]; esum += scale * pstyle->single(i, j, itype, jtype, rsq, factor_coul, factor_lj, fone); // if scale factor is constant or equal-style variable if (scaleidx[m] < 0 || atomvar[m] < 0) { fforce += scale * fone; // if scale factor is atom-style variable, average i and j } else { const int igroupall = 0; input->variable->compute_atom(atomvar[m], igroupall, atomscale, 1, 0); comm->forward_comm(this); const double ascale = 0.5 * (atomscale[i] + atomscale[j]); fforce += ascale * fone; } } } if (single_extra) copy_svector(itype, jtype); return esum; } /* ---------------------------------------------------------------------- call sub-style to compute born matrix interaction error if sub-style does not support born_matrix call since overlay could have multiple sub-styles, sum results explicitly ------------------------------------------------------------------------- */ void PairHybridScaled::born_matrix(int i, int j, int itype, int jtype, double rsq, double factor_coul, double factor_lj, double &dupair, double &du2pair) { if (nmap[itype][jtype] == 0) error->one(FLERR, "Invoked pair born_matrix on pair style none"); // update scale values from variables where needed const int nvars = scalevars.size(); if (nvars > 0) { auto vals = new double[nvars]; auto vars = new int[nvars]; for (int k = 0; k < nvars; ++k) { int m = input->variable->find(scalevars[k].c_str()); if (m < 0) error->all(FLERR, "Variable '{}' not found when updating scale factors", scalevars[k]); // for equal-style, compute variable, set variable index to -1 if (input->variable->equalstyle(m)) { vals[k] = input->variable->compute_equal(m); vars[k] = -1; // for atom-style, store variable index, set variable to 0.0, set atomscaleflag } else if (input->variable->atomstyle(m)) { vals[k] = 0.0; vars[k] = m; } else error->all(FLERR, "Variable '{}' has incompatible style", scalevars[k]); } for (int k = 0; k < nstyles; ++k) { if (scaleidx[k] >= 0) { scaleval[k] = vals[scaleidx[k]]; atomvar[k] = vars[scaleidx[k]]; } } delete[] vals; delete[] vars; } double du, du2; dupair = du2pair = 0.0; for (int m = 0; m < nmap[itype][jtype]; m++) { auto pstyle = styles[map[itype][jtype][m]]; if (rsq < pstyle->cutsq[itype][jtype]) { if (pstyle->single_enable == 0) error->one(FLERR, "Pair hybrid sub-style does not support single call"); if ((special_lj[map[itype][jtype][m]] != nullptr) || (special_coul[map[itype][jtype][m]] != nullptr)) error->one(FLERR, "Pair hybrid single() does not support per sub-style special_bond"); du = du2 = 0.0; double scale = scaleval[map[itype][jtype][m]]; pstyle->born_matrix(i, j, itype, jtype, rsq, factor_coul, factor_lj, du, du2); // if scale factor is constant or equal-style variable if (scaleidx[m] < 0 || atomvar[m] < 0) { dupair += scale * du; du2pair += scale * du2; // if scale factor is atom-style variable, average i and j } else { const int igroupall = 0; input->variable->compute_atom(atomvar[m], igroupall, atomscale, 1, 0); comm->forward_comm(this); const double ascale = 0.5 * (atomscale[i] + atomscale[j]); dupair += ascale * du; du2pair += ascale * du2; } } } } /* ---------------------------------------------------------------------- set coeffs for one or more type pairs ------------------------------------------------------------------------- */ void PairHybridScaled::coeff(int narg, char **arg) { if (narg < 3) error->all(FLERR, "Incorrect args for pair coefficients" + utils::errorurl(21)); if (!allocated) allocate(); int ilo, ihi, jlo, jhi; utils::bounds(FLERR, arg[0], 1, atom->ntypes, ilo, ihi, error); utils::bounds(FLERR, arg[1], 1, atom->ntypes, jlo, jhi, error); // 3rd arg = pair sub-style name // 4th arg = pair sub-style index if name used multiple times // allow for "none" as valid sub-style name int multflag = 0; int m; for (m = 0; m < nstyles; m++) { multflag = 0; if (strcmp(arg[2], keywords[m]) == 0) { if (multiple[m]) { multflag = 1; if (narg < 4) error->all(FLERR, "Incorrect args for pair coefficients" + utils::errorurl(21)); int index = utils::inumeric(FLERR, arg[3], false, lmp); if (index == multiple[m]) break; else continue; } else break; } } int none = 0; if (m == nstyles) { if (strcmp(arg[2], "none") == 0) none = 1; else error->all(FLERR, "Expected hybrid sub-style instead of {} in pair_coeff command", arg[2]); } // move 1st/2nd args to 2nd/3rd args // if multflag: move 1st/2nd args to 3rd/4th args // just copy ptrs, since arg[] points into original input line arg[2 + multflag] = arg[1]; arg[1 + multflag] = arg[0]; // ensure that one_coeff flag is honored if (!none && styles[m]->one_coeff) if ((strcmp(arg[0], "*") != 0) || (strcmp(arg[1], "*") != 0)) error->all(FLERR, "Incorrect args for pair coefficients" + utils::errorurl(21)); // invoke sub-style coeff() starting with 1st remaining arg if (!none) styles[m]->coeff(narg - 1 - multflag, &arg[1 + multflag]); // set setflag and which type pairs map to which sub-style // if sub-style is none: set hybrid subflag, wipe out map // else: set hybrid setflag & map only if substyle setflag is set // if sub-style is new for type pair, add as multiple mapping // if sub-style exists for type pair, don't add, just update coeffs int count = 0; for (int i = ilo; i <= ihi; i++) { for (int j = MAX(jlo, i); j <= jhi; j++) { if (none) { setflag[i][j] = 1; nmap[i][j] = 0; count++; } else if (styles[m]->setflag[i][j]) { int k; for (k = 0; k < nmap[i][j]; k++) if (map[i][j][k] == m) break; if (k == nmap[i][j]) map[i][j][nmap[i][j]++] = m; setflag[i][j] = 1; count++; } } } if (count == 0) error->all(FLERR, "Incorrect args for pair coefficients" + utils::errorurl(21)); } /* ---------------------------------------------------------------------- proc 0 writes to restart file ------------------------------------------------------------------------- */ void PairHybridScaled::write_restart(FILE *fp) { PairHybrid::write_restart(fp); fwrite(scaleval, sizeof(double), nstyles, fp); fwrite(scaleidx, sizeof(int), nstyles, fp); fwrite(atomvar, sizeof(int), nstyles, fp); int n = scalevars.size(); fwrite(&n, sizeof(int), 1, fp); for (auto &var : scalevars) { n = var.size() + 1; fwrite(&n, sizeof(int), 1, fp); fwrite(var.c_str(), sizeof(char), n, fp); } } /* ---------------------------------------------------------------------- proc 0 reads from restart file, bcasts ------------------------------------------------------------------------- */ void PairHybridScaled::read_restart(FILE *fp) { PairHybrid::read_restart(fp); delete[] scaleval; delete[] scaleidx; delete[] atomvar; scalevars.clear(); scaleval = new double[nstyles]; scaleidx = new int[nstyles]; atomvar = new int[nstyles]; int n, me = comm->me; if (me == 0) { utils::sfread(FLERR, scaleval, sizeof(double), nstyles, fp, nullptr, error); utils::sfread(FLERR, scaleidx, sizeof(int), nstyles, fp, nullptr, error); utils::sfread(FLERR, atomvar, sizeof(int), nstyles, fp, nullptr, error); } MPI_Bcast(scaleval, nstyles, MPI_DOUBLE, 0, world); MPI_Bcast(scaleidx, nstyles, MPI_INT, 0, world); MPI_Bcast(atomvar, nstyles, MPI_INT, 0, world); char *tmp; if (me == 0) utils::sfread(FLERR, &n, sizeof(int), 1, fp, nullptr, error); MPI_Bcast(&n, 1, MPI_INT, 0, world); scalevars.resize(n); for (auto &scale : scalevars) { if (me == 0) utils::sfread(FLERR, &n, sizeof(int), 1, fp, nullptr, error); MPI_Bcast(&n, 1, MPI_INT, 0, world); tmp = new char[n]; if (me == 0) utils::sfread(FLERR, tmp, sizeof(char), n, fp, nullptr, error); MPI_Bcast(tmp, n, MPI_CHAR, 0, world); scale = tmp; delete[] tmp; } } /* ---------------------------------------------------------------------- we need to handle Pair::svector special for hybrid/scaled ------------------------------------------------------------------------- */ void PairHybridScaled::init_svector() { // single_extra = list all sub-style single_extra // allocate svector single_extra = 0; for (int m = 0; m < nstyles; m++) single_extra += styles[m]->single_extra; if (single_extra) { delete[] svector; svector = new double[single_extra]; } } /* ---------------------------------------------------------------------- we need to handle Pair::svector special for hybrid/scaled ------------------------------------------------------------------------- */ void PairHybridScaled::copy_svector(int itype, int jtype) { int n = 0; Pair *this_style = nullptr; // fill svector array. // copy data from active styles and use 0.0 for inactive ones for (int m = 0; m < nstyles; m++) { for (int k = 0; k < nmap[itype][jtype]; ++k) { if (m == map[itype][jtype][k]) { this_style = styles[m]; } else { this_style = nullptr; } } for (int l = 0; l < styles[m]->single_extra; ++l) { if (this_style) { svector[n++] = this_style->svector[l]; } else { svector[n++] = 0.0; } } } } /* ---------------------------------------------------------------------- */ int PairHybridScaled::pack_forward_comm(int n, int *list, double *buf, int /*pbc_flag*/, int * /*pbc*/) { int i,j,m; m = 0; for (i = 0; i < n; i++) { j = list[i]; buf[m++] = atomscale[j]; } return m; } /* ---------------------------------------------------------------------- */ void PairHybridScaled::unpack_forward_comm(int n, int first, double *buf) { int i,m,last; m = 0; last = first + n; for (i = first; i < last; i++) atomscale[i] = buf[m++]; }