// clang-format off /* ---------------------------------------------------------------------- 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. ------------------------------------------------------------------------- */ /* ---------------------------------------------------------------------- Contributing author (triclinic and multi-neigh) : Pieter in 't Veld (SNL) Contributing author (improved multi-neigh) : Joel Clemmer (SNL) ------------------------------------------------------------------------- */ #include "neighbor.h" #include "accelerator_kokkos.h" #include "atom.h" #include "atom_vec.h" #include "bond.h" #include "citeme.h" #include "comm.h" #include "compute.h" #include "domain.h" #include "error.h" #include "fix.h" #include "force.h" #include "group.h" #include "memory.h" #include "modify.h" #include "nbin.h" #include "neigh_list.h" #include "neigh_request.h" #include "npair.h" #include "nstencil.h" #include "ntopo.h" #include "output.h" #include "pair.h" #include "pair_hybrid.h" #include "respa.h" #include "style_nbin.h" // IWYU pragma: keep #include "style_npair.h" // IWYU pragma: keep #include "style_nstencil.h" // IWYU pragma: keep #include "style_ntopo.h" // IWYU pragma: keep #include "suffix.h" #include "tokenizer.h" #include "update.h" #include #include using namespace LAMMPS_NS; using namespace NeighConst; #define RQDELTA 1 #define EXDELTA 1 #define DELTA_PERATOM 64 #define BIG 1.0e20 enum{NONE,ALL,PARTIAL,TEMPLATE}; static const char cite_neigh_multi_old[] = "neighbor multi/old command: doi:10.1016/j.cpc.2008.03.005\n\n" "@Article{Intveld08,\n" " author = {in 't Veld, P. J. and S. J. Plimpton and G. S. Grest},\n" " title = {Accurate and Efficient Methods for Modeling Colloidal\n" " Mixtures in an Explicit Solvent using Molecular Dynamics},\n" " journal = {Comput.\\ Phys.\\ Commun.},\n" " year = 2008,\n" " volume = 179,\n" " number = 5,\n" " pages = {320--329}\n" "}\n\n"; static const char cite_neigh_multi[] = "neighbor multi command: doi:10.1016/j.cpc.2008.03.005, doi:10.1007/s40571-020-00361-2\n\n" "@Article{Intveld08,\n" " author = {in 't Veld, P. J. and S. J.~Plimpton and G. S. Grest},\n" " title = {Accurate and Efficient Methods for Modeling Colloidal\n" " Mixtures in an Explicit Solvent using Molecular Dynamics},\n" " journal = {Comput.\\ Phys.\\ Commut.},\n" " year = 2008,\n" " volume = 179,\n" " pages = {320--329}\n" "}\n\n" "@article{Shire2020,\n" " author = {Shire, Tom and Hanley, Kevin J. and Stratford, Kevin},\n" " title = {{DEM} Simulations of Polydisperse Media: Efficient Contact\n" " Detection Applied to Investigate the Quasi-Static Limit},\n" " journal = {Computational Particle Mechanics},\n" " year = {2020}\n" "@article{Monti2022,\n" " author = {Monti, Joseph M. and Clemmer, Joel T. and Srivastava, \n" " Ishan and Silbert, Leonardo E. and Grest, Gary S. \n" " and Lechman, Jeremy B.},\n" " title = {Large-scale frictionless jamming with power-law particle \n" " size distributions},\n" " journal = {Phys. Rev. E},\n" " volume = {106}\n" " issue = {3}\n" " year = {2022}\n" "}\n\n"; // template for factory functions: // there will be one instance for each style keyword in the respective style_xxx.h files template static S *style_creator(LAMMPS *lmp) { return new T(lmp); } //#define NEIGH_LIST_DEBUG 1 /* ---------------------------------------------------------------------- */ Neighbor::Neighbor(LAMMPS *lmp) : Pointers(lmp), pairclass(nullptr), pairnames(nullptr), pairmasks(nullptr) { MPI_Comm_rank(world,&me); MPI_Comm_size(world,&nprocs); firsttime = 1; style = Neighbor::BIN; every = 1; delay = 0; dist_check = 1; pgsize = 100000; oneatom = 2000; binsizeflag = 0; build_once = 0; cluster_check = 0; ago = -1; cutneighmax = 0.0; cutneighsq = nullptr; cutneighghostsq = nullptr; cuttype = nullptr; cuttypesq = nullptr; fixchecklist = nullptr; // pairwise neighbor lists and associated data structs nlist = 0; lists = nullptr; nbin = 0; neigh_bin = nullptr; nstencil = 0; neigh_stencil = nullptr; neigh_pair = nullptr; nstencil_perpetual = 0; slist = nullptr; npair_perpetual = 0; plist = nullptr; nrequest = maxrequest = 0; requests = nullptr; j_sorted = nullptr; old_nrequest = 0; old_requests = nullptr; old_style = style; old_triclinic = 0; old_pgsize = pgsize; old_oneatom = oneatom; binclass = nullptr; binnames = nullptr; binmasks = nullptr; stencilclass = nullptr; stencilnames = nullptr; stencilmasks = nullptr; // topology lists bondwhich = anglewhich = dihedralwhich = improperwhich = NONE; neigh_bond = nullptr; neigh_angle = nullptr; neigh_dihedral = nullptr; neigh_improper = nullptr; // coords at last neighboring maxhold = 0; xhold = nullptr; lastcall = -1; last_setup_bins = -1; // pair exclusion list info includegroup = 0; nex_type = maxex_type = 0; ex1_type = ex2_type = nullptr; ex_type = nullptr; nex_group = maxex_group = 0; ex1_group = ex2_group = ex1_bit = ex2_bit = nullptr; nex_mol = maxex_mol = 0; ex_mol_group = ex_mol_bit = ex_mol_intra = nullptr; // Multi data type2collection = nullptr; collection2cut = nullptr; collection = nullptr; cutcollectionsq = nullptr; custom_collection_flag = 0; interval_collection_flag = 0; nmax_collection = 0; // Kokkos setting copymode = 0; // GPU setting overlap_topo = 0; } /* ---------------------------------------------------------------------- */ Neighbor::~Neighbor() { if (copymode) return; memory->destroy(cutneighsq); memory->destroy(cutneighghostsq); delete[] cuttype; delete[] cuttypesq; delete[] fixchecklist; for (int i = 0; i < nlist; i++) delete lists[i]; for (int i = 0; i < nbin; i++) delete neigh_bin[i]; for (int i = 0; i < nstencil; i++) delete neigh_stencil[i]; for (int i = 0; i < nlist; i++) delete neigh_pair[i]; delete[] lists; delete[] neigh_bin; delete[] neigh_stencil; delete[] neigh_pair; delete[] slist; delete[] plist; for (int i = 0; i < nrequest; i++) if (requests[i]) delete requests[i]; memory->sfree(requests); for (int i = 0; i < old_nrequest; i++) if (old_requests[i]) delete old_requests[i]; memory->sfree(old_requests); delete[] j_sorted; delete[] binclass; delete[] binnames; delete[] binmasks; delete[] stencilclass; delete[] stencilnames; delete[] stencilmasks; delete[] pairclass; delete[] pairnames; delete[] pairmasks; delete neigh_bond; delete neigh_angle; delete neigh_dihedral; delete neigh_improper; memory->destroy(xhold); memory->destroy(ex1_type); memory->destroy(ex2_type); memory->destroy(ex_type); memory->destroy(ex1_group); memory->destroy(ex2_group); delete[] ex1_bit; delete[] ex2_bit; memory->destroy(ex_mol_group); delete[] ex_mol_bit; memory->destroy(ex_mol_intra); memory->destroy(type2collection); memory->destroy(collection2cut); memory->destroy(collection); memory->destroy(cutcollectionsq); } /* ---------------------------------------------------------------------- */ void Neighbor::init() { int i,j,n; overlap_topo = 0; ncalls = ndanger = 0; dimension = domain->dimension; triclinic = domain->triclinic; newton_pair = force->newton_pair; // error check if (delay > 0 && (delay % every) != 0) error->all(FLERR,"Neighbor delay must be 0 or multiple of every setting"); if (pgsize < 10*oneatom) error->all(FLERR,"Neighbor page size must be >= 10x the one atom setting"); // ------------------------------------------------------------------ // settings // bbox lo/hi ptrs = bounding box of entire domain, stored by Domain if (triclinic == 0) { bboxlo = domain->boxlo; bboxhi = domain->boxhi; } else { bboxlo = domain->boxlo_bound; bboxhi = domain->boxhi_bound; } // set neighbor cutoffs (force cutoff + skin) // trigger determines when atoms migrate and neighbor lists are rebuilt // needs to be non-zero for migration distance check // even if pair = nullptr and no neighbor lists are used // cutneigh = force cutoff + skin if cutforce > 0, else cutneigh = 0 // cutneighghost = pair cutghost if it requests it, else same as cutneigh triggersq = 0.25*skin*skin; boxcheck = 0; if (domain->box_change && (domain->xperiodic || domain->yperiodic || (dimension == 3 && domain->zperiodic))) boxcheck = 1; n = atom->ntypes; if (cutneighsq == nullptr) { if (lmp->kokkos) init_cutneighsq_kokkos(n); else memory->create(cutneighsq,n+1,n+1,"neigh:cutneighsq"); memory->create(cutneighghostsq,n+1,n+1,"neigh:cutneighghostsq"); cuttype = new double[n+1]; cuttypesq = new double[n+1]; } double cutoff,delta,cut; cutneighmin = BIG; cutneighmax = 0.0; for (i = 1; i <= n; i++) { cuttype[i] = cuttypesq[i] = 0.0; for (j = 1; j <= n; j++) { if (force->pair) cutoff = sqrt(force->pair->cutsq[i][j]); else cutoff = 0.0; if (cutoff > 0.0) delta = skin; else delta = 0.0; cut = cutoff + delta; cutneighsq[i][j] = cut*cut; cuttype[i] = MAX(cuttype[i],cut); cuttypesq[i] = MAX(cuttypesq[i],cut*cut); cutneighmin = MIN(cutneighmin,cut); cutneighmax = MAX(cutneighmax,cut); if (force->pair && force->pair->ghostneigh) { cut = force->pair->cutghost[i][j] + skin; cutneighghostsq[i][j] = cut*cut; } else cutneighghostsq[i][j] = cut*cut; } } cutneighmaxsq = cutneighmax * cutneighmax; // Define cutoffs for multi if (style == Neighbor::MULTI) { int icollection, jcollection; // If collections not yet defined, create default map using types if (!custom_collection_flag) { ncollections = n; interval_collection_flag = 0; if (!type2collection) memory->create(type2collection,n+1,"neigh:type2collection"); for (i = 1; i <= n; i++) type2collection[i] = i-1; } memory->grow(cutcollectionsq, ncollections, ncollections, "neigh:cutcollectionsq"); // 3 possible ways of defining collections // 1) Types are used to define collections // Each collection loops through its owned types, and uses cutneighsq to calculate its cutoff // 2) Collections are defined by intervals, point particles // Types are first sorted into collections based on cutneighsq[i][i] // Each collection loops through its owned types, and uses cutneighsq to calculate its cutoff // 3) Collections are defined by intervals, finite particles // // Define collection cutoffs for (i = 0; i < ncollections; i++) for (j = 0; j < ncollections; j++) cutcollectionsq[i][j] = 0.0; if (!interval_collection_flag) { finite_cut_flag = 0; for (i = 1; i <= n; i++){ icollection = type2collection[i]; for (j = 1; j <= n; j++){ jcollection = type2collection[j]; if (cutneighsq[i][j] > cutcollectionsq[icollection][jcollection]) { cutcollectionsq[icollection][jcollection] = cutneighsq[i][j]; cutcollectionsq[jcollection][icollection] = cutneighsq[i][j]; } } } } else { if (force->pair->finitecutflag) { finite_cut_flag = 1; // If cutoffs depend on finite atom sizes, use radii of intervals to find cutoffs double ri, rj, tmp; for (i = 0; i < ncollections; i++){ ri = collection2cut[i]*0.5; for (j = 0; j < ncollections; j++){ rj = collection2cut[j]*0.5; tmp = force->pair->radii2cut(ri, rj) + skin; cutcollectionsq[i][j] = tmp*tmp; } } } else { finite_cut_flag = 0; // Map types to collections if (!type2collection) memory->create(type2collection,n+1,"neigh:type2collection"); for (i = 1; i <= n; i++) type2collection[i] = -1; double cuttmp; for (i = 1; i <= n; i++){ // Remove skin added to cutneighsq cuttmp = sqrt(cutneighsq[i][i]) - skin; for (icollection = 0; icollection < ncollections; icollection ++){ if (collection2cut[icollection] >= cuttmp) { type2collection[i] = icollection; break; } } if (type2collection[i] == -1) error->all(FLERR, "Pair cutoff exceeds interval cutoffs for multi"); } // Define cutoffs for (i = 1; i <= n; i++){ icollection = type2collection[i]; for (j = 1; j <= n; j++){ jcollection = type2collection[j]; if (cutneighsq[i][j] > cutcollectionsq[icollection][jcollection]) { cutcollectionsq[icollection][jcollection] = cutneighsq[i][j]; cutcollectionsq[jcollection][icollection] = cutneighsq[i][j]; } } } } } } // rRESPA cutoffs int respa = 0; if (update->whichflag == 1 && utils::strmatch(update->integrate_style,"^respa")) { if ((dynamic_cast(update->integrate))->level_inner >= 0) respa = 1; if ((dynamic_cast(update->integrate))->level_middle >= 0) respa = 2; } if (respa) { double *cut_respa = (dynamic_cast(update->integrate))->cutoff; cut_inner_sq = (cut_respa[1] + skin) * (cut_respa[1] + skin); cut_middle_sq = (cut_respa[3] + skin) * (cut_respa[3] + skin); cut_middle_inside_sq = (cut_respa[0] - skin) * (cut_respa[0] - skin); if (cut_respa[0]-skin < 0) cut_middle_inside_sq = 0.0; } // fixchecklist = other classes that can induce reneighboring in decide() restart_check = 0; if (output->restart_flag) restart_check = 1; delete[] fixchecklist; fixchecklist = nullptr; fixchecklist = new int[modify->nfix]; fix_check = 0; for (i = 0; i < modify->nfix; i++) if (modify->fix[i]->force_reneighbor) fixchecklist[fix_check++] = i; must_check = 0; if (restart_check || fix_check) must_check = 1; // set special_flag for 1-2, 1-3, 1-4 neighbors // flag[0] is not used, flag[1] = 1-2, flag[2] = 1-3, flag[3] = 1-4 // flag = 0 if both LJ/Coulomb special values are 0.0 // flag = 1 if both LJ/Coulomb special values are 1.0 // flag = 2 otherwise or if KSpace solver is enabled // b/c pairwise portion of KSpace solver uses all 1-2,1-3,1-4 neighbors // some Coulomb-approximation pair styles also require it (below) if (force->special_lj[1] == 0.0 && force->special_coul[1] == 0.0) special_flag[1] = 0; else if (force->special_lj[1] == 1.0 && force->special_coul[1] == 1.0) special_flag[1] = 1; else special_flag[1] = 2; if (force->special_lj[2] == 0.0 && force->special_coul[2] == 0.0) special_flag[2] = 0; else if (force->special_lj[2] == 1.0 && force->special_coul[2] == 1.0) special_flag[2] = 1; else special_flag[2] = 2; if (force->special_lj[3] == 0.0 && force->special_coul[3] == 0.0) special_flag[3] = 0; else if (force->special_lj[3] == 1.0 && force->special_coul[3] == 1.0) special_flag[3] = 1; else special_flag[3] = 2; // cannot remove special neighbors with kspace or kspace-like pair styles // b/c exclusion needs to remove the full coulomb and not the damped interaction // special treatment required for hybrid pair styles since Force::pair_match() // will only return a non-NULL pointer if there is only one substyle of the kind if (force->kspace) { special_flag[1] = special_flag[2] = special_flag[3] = 2; } else { PairHybrid *ph = reinterpret_cast(force->pair_match("^hybrid",0)); if (ph) { int flag=0; for (int isub=0; isub < ph->nstyles; ++isub) { if (force->pair_match("amoeba",0,isub) || force->pair_match("hippo",0,isub) || force->pair_match("coul/wolf",0,isub) || force->pair_match("coul/dsf",0,isub) || force->pair_match("coul/exclude",0) || force->pair_match("thole",0,isub)) ++flag; } if (flag) special_flag[1] = special_flag[2] = special_flag[3] = 2; } else { if (force->pair_match("amoeba",0) || force->pair_match("hippo",0) || force->pair_match("coul/wolf",0) || force->pair_match("coul/dsf",0) || force->pair_match("coul/exclude",0) || force->pair_match("thole",0)) special_flag[1] = special_flag[2] = special_flag[3] = 2; } } // ------------------------------------------------------------------ // xhold array // free if not needed for this run if (dist_check == 0) { memory->destroy(xhold); maxhold = 0; xhold = nullptr; } // first time allocation if (dist_check) { if (maxhold == 0) { maxhold = atom->nmax; memory->create(xhold,maxhold,3,"neigh:xhold"); } } // ------------------------------------------------------------------ // exclusion lists // depend on type, group, molecule settings from neigh_modify // warn if exclusions used with KSpace solver n = atom->ntypes; if (nex_type == 0 && nex_group == 0 && nex_mol == 0) exclude = 0; else exclude = 1; if (nex_type) { if (lmp->kokkos) init_ex_type_kokkos(n); else { memory->destroy(ex_type); memory->create(ex_type,n+1,n+1,"neigh:ex_type"); } for (i = 1; i <= n; i++) for (j = 1; j <= n; j++) ex_type[i][j] = 0; for (i = 0; i < nex_type; i++) { if (ex1_type[i] <= 0 || ex1_type[i] > n || ex2_type[i] <= 0 || ex2_type[i] > n) error->all(FLERR,"Invalid atom type in neighbor exclusion list"); ex_type[ex1_type[i]][ex2_type[i]] = 1; ex_type[ex2_type[i]][ex1_type[i]] = 1; } } if (nex_group) { if (lmp->kokkos) init_ex_bit_kokkos(); else { delete[] ex1_bit; delete[] ex2_bit; ex1_bit = new int[nex_group]; ex2_bit = new int[nex_group]; } for (i = 0; i < nex_group; i++) { ex1_bit[i] = group->bitmask[ex1_group[i]]; ex2_bit[i] = group->bitmask[ex2_group[i]]; } } if (nex_mol) { if (lmp->kokkos) init_ex_mol_bit_kokkos(); else { delete[] ex_mol_bit; ex_mol_bit = new int[nex_mol]; } for (i = 0; i < nex_mol; i++) ex_mol_bit[i] = group->bitmask[ex_mol_group[i]]; } if (exclude && force->kspace && me == 0) error->warning(FLERR,"Neighbor exclusions used with KSpace solver " "may give inconsistent Coulombic energies"); if (lmp->kokkos) set_binsize_kokkos(); // ------------------------------------------------------------------ // create pairwise lists // one-time call to init_styles() to scan style files and setup // init_pair() creates auxiliary classes: NBin, NStencil, NPair if (firsttime) init_styles(); firsttime = 0; int same = init_pair(); // invoke copy_neighbor_info() in Bin,Stencil,Pair classes // copied once per run in case any cutoff, exclusion, special info changed for (i = 0; i < nbin; i++) neigh_bin[i]->copy_neighbor_info(); for (i = 0; i < nstencil; i++) neigh_stencil[i]->copy_neighbor_info(); for (i = 0; i < nlist; i++) if (neigh_pair[i]) neigh_pair[i]->copy_neighbor_info(); if (!same && (nrequest > 0) && (comm->me == 0)) print_pairwise_info(); // can now delete requests so next run can make new ones // print_pairwise_info() made use of requests // set of NeighLists now stores all needed info for (i = 0; i < nrequest; i++) { delete requests[i]; requests[i] = nullptr; } nrequest = 0; // ------------------------------------------------------------------ // create topology lists // instantiated topo styles can change from run to run init_topology(); } /* ---------------------------------------------------------------------- create and initialize lists of Nbin, Nstencil, NPair classes lists have info on all classes in 3 style*.h files cannot do this in constructor, b/c too early to instantiate classes ------------------------------------------------------------------------- */ void Neighbor::init_styles() { // extract info from NBin classes listed in style_nbin.h nbclass = 0; #define NBIN_CLASS #define NBinStyle(key,Class,bitmasks) nbclass++; #include "style_nbin.h" // IWYU pragma: keep #undef NBinStyle #undef NBIN_CLASS binclass = new BinCreator[nbclass]; binnames = new char*[nbclass]; binmasks = new int[nbclass]; nbclass = 0; #define NBIN_CLASS #define NBinStyle(key,Class,bitmasks) \ binnames[nbclass] = (char *) #key; \ binclass[nbclass] = &style_creator; \ binmasks[nbclass++] = bitmasks; #include "style_nbin.h" // IWYU pragma: keep #undef NBinStyle #undef NBIN_CLASS // extract info from NStencil classes listed in style_nstencil.h nsclass = 0; #define NSTENCIL_CLASS #define NStencilStyle(key,Class,bitmasks) nsclass++; #include "style_nstencil.h" // IWYU pragma: keep #undef NStencilStyle #undef NSTENCIL_CLASS stencilclass = new StencilCreator[nsclass]; stencilnames = new char*[nsclass]; stencilmasks = new int[nsclass]; nsclass = 0; #define NSTENCIL_CLASS #define NStencilStyle(key,Class,bitmasks) \ stencilnames[nsclass] = (char *) #key; \ stencilclass[nsclass] = &style_creator; \ stencilmasks[nsclass++] = bitmasks; #include "style_nstencil.h" // IWYU pragma: keep #undef NStencilStyle #undef NSTENCIL_CLASS // extract info from NPair classes listed in style_npair.h npclass = 0; #define NPAIR_CLASS #define NPairStyle(key,Class,bitmasks) npclass++; #include "style_npair.h" // IWYU pragma: keep #undef NPairStyle #undef NPAIR_CLASS pairclass = new PairCreator[npclass]; pairnames = new char*[npclass]; pairmasks = new int[npclass]; npclass = 0; #define NPAIR_CLASS #define NPairStyle(key,Class,bitmasks) \ pairnames[npclass] = (char *) #key; \ pairclass[npclass] = &style_creator; \ pairmasks[npclass++] = bitmasks; #include "style_npair.h" // IWYU pragma: keep #undef NPairStyle #undef NPAIR_CLASS } /* ---------------------------------------------------------------------- create and initialize NPair classes ------------------------------------------------------------------------- */ int Neighbor::init_pair() { int i,j,k,m; // test if pairwise lists need to be re-created // no need to re-create if: // neigh style, triclinic, pgsize, oneatom have not changed // current requests = old requests // so just return: // delete requests so next run can make new ones // current set of NeighLists already stores all needed info // requests are compared via identical() before: // any requests are morphed using logic below // any requests are added below, e.g. as parents of pair hybrid skip lists // copy them via requests_new2old() BEFORE any changes made to requests // necessary b/c morphs can change requestor settings (see comment below) int same = 1; if (style != old_style) same = 0; if (triclinic != old_triclinic) same = 0; if (pgsize != old_pgsize) same = 0; if (oneatom != old_oneatom) same = 0; if (nrequest != old_nrequest) same = 0; else for (i = 0; i < nrequest; i++) if (requests[i]->identical(old_requests[i]) == 0) same = 0; #ifdef NEIGH_LIST_DEBUG if (comm->me == 0) printf("SAME flag %d\n",same); #endif if (same) return same; requests_new2old(); // delete old lists since creating new ones for (i = 0; i < nlist; i++) delete lists[i]; for (i = 0; i < nbin; i++) delete neigh_bin[i]; for (i = 0; i < nstencil; i++) delete neigh_stencil[i]; for (i = 0; i < nlist; i++) delete neigh_pair[i]; delete[] lists; delete[] neigh_bin; delete[] neigh_stencil; delete[] neigh_pair; // error check on requests // do not allow occasional, ghost, bin list // b/c it still uses variant of coord2bin() in NPair() method // instead of atom2bin, this could cause error b/c stoms have // moved out of proc domain by time occasional list is built // solution would be to use a different NBin variant // that used Npair::coord2bin(x,ix,iy,iz) (then delete it from NPair) // and stored the ix,iy,iz values for all atoms (including ghosts) // at time of binning when neighbor lists are rebuilt, // similar to what vanilla Nbin::coord2atom() does now in atom2bin if (style == Neighbor::BIN) { for (i = 0; i < nrequest; i++) if (requests[i]->occasional && requests[i]->ghost) error->all(FLERR,"Cannot request an occasional binned neighbor list " "with ghost info"); } // morph requests in various ways // purpose is to avoid duplicate or inefficient builds // may add new requests if a needed request to derive from does not exist // methods: // (1) unique = create unique lists if cutoff is explicitly set // (2) skip = create any new non-skip lists needed by pair hybrid skip lists // (3) granular = adjust parent and skip lists for granular onesided usage // (4) h/f = pair up any matching half/full lists // (5) copy = convert as many lists as possible to copy lists // order of morph methods matters: // (3) after (2), b/c it adjusts lists created by (2) // (4) after (2) and (3), // b/c (2) may create new full lists, (3) may change them // (5) last, after all lists are finalized, so all possible copies/trims found int nrequest_original = nrequest; morph_unique(); morph_skip(); morph_granular(); // this method can change flags set by requestor // sort requests by cutoff distance for trimming, used by // morph_halffull and morph_copy_trim. Must come after // morph_skip() which change the number of requests sort_requests(); morph_halffull(); morph_copy_trim(); // create new lists, one per request including added requests // wait to allocate initial pages until copy lists are detected // NOTE: can I allocate now, instead of down below? nlist = nrequest; lists = new NeighList*[nrequest]; neigh_bin = new NBin*[nrequest]; neigh_stencil = new NStencil*[nrequest]; neigh_pair = new NPair*[nrequest]; // allocate new lists // pass list ptr back to requestor (except for Command class) // only for original requests, not ones added by Neighbor class for (i = 0; i < nrequest; i++) { if (requests[i]->kokkos_host || requests[i]->kokkos_device) create_kokkos_list(i); else lists[i] = new NeighList(lmp); lists[i]->index = i; lists[i]->requestor = requests[i]->requestor; if (requests[i]->pair) { lists[i]->requestor_type = NeighList::PAIR; } else if (requests[i]->fix) { lists[i]->requestor_type = NeighList::FIX; } else if (requests[i]->compute) { lists[i]->requestor_type = NeighList::COMPUTE; } if (requests[i]->pair && i < nrequest_original) { auto pair = (Pair *) requests[i]->requestor; pair->init_list(requests[i]->id,lists[i]); } else if (requests[i]->fix && i < nrequest_original) { Fix *fix = (Fix *) requests[i]->requestor; fix->init_list(requests[i]->id,lists[i]); } else if (requests[i]->compute && i < nrequest_original) { auto compute = (Compute *) requests[i]->requestor; compute->init_list(requests[i]->id,lists[i]); } } // invoke post_constructor() for all lists // copies info from requests to lists, sets ptrs to related lists for (i = 0; i < nrequest; i++) lists[i]->post_constructor(requests[i]); // assign Bin,Stencil,Pair style to each list int flag; for (i = 0; i < nrequest; i++) { flag = choose_bin(requests[i]); lists[i]->bin_method = flag; if (flag < 0) error->all(FLERR,"Requested neighbor bin option does not exist"); flag = choose_stencil(requests[i]); lists[i]->stencil_method = flag; if (flag < 0) error->all(FLERR,"Requested neighbor stencil method does not exist"); flag = choose_pair(requests[i]); lists[i]->pair_method = flag; if (flag < 0) error->all(FLERR,"Requested neighbor pair method does not exist"); } // instantiate unique Bin,Stencil classes in neigh_bin & neigh_stencil vecs // unique = only one of its style, or request unique flag set (custom cutoff) nbin = 0; for (i = 0; i < nrequest; i++) { requests[i]->index_bin = -1; flag = lists[i]->bin_method; if (flag == 0) continue; if (!requests[i]->unique) { for (j = 0; j < nbin; j++) if (neigh_bin[j]->istyle == flag && neigh_bin[j]->cutoff_custom == 0.0) break; if (j < nbin) { requests[i]->index_bin = j; continue; } } BinCreator &bin_creator = binclass[flag-1]; neigh_bin[nbin] = bin_creator(lmp); neigh_bin[nbin]->post_constructor(requests[i]); neigh_bin[nbin]->istyle = flag; requests[i]->index_bin = nbin; nbin++; } nstencil = 0; for (i = 0; i < nrequest; i++) { requests[i]->index_stencil = -1; flag = lists[i]->stencil_method; if (flag == 0) continue; if (!requests[i]->unique) { for (j = 0; j < nstencil; j++) if (neigh_stencil[j]->istyle == flag && neigh_stencil[j]->cutoff_custom == 0.0) break; if (j < nstencil) { requests[i]->index_stencil = j; continue; } } StencilCreator &stencil_creator = stencilclass[flag-1]; neigh_stencil[nstencil] = stencil_creator(lmp); neigh_stencil[nstencil]->post_constructor(requests[i]); neigh_stencil[nstencil]->istyle = flag; if (lists[i]->bin_method > 0) { neigh_stencil[nstencil]->nb = neigh_bin[requests[i]->index_bin]; if (neigh_stencil[nstencil]->nb == nullptr) error->all(FLERR,"Could not assign bin method to neighbor stencil"); } requests[i]->index_stencil = nstencil; nstencil++; } // instantiate one Pair class per list in neigh_pair vec for (i = 0; i < nrequest; i++) { requests[i]->index_pair = -1; flag = lists[i]->pair_method; if (flag == 0) { neigh_pair[i] = nullptr; continue; } PairCreator &pair_creator = pairclass[flag-1]; lists[i]->np = neigh_pair[i] = pair_creator(lmp); neigh_pair[i]->post_constructor(requests[i]); neigh_pair[i]->istyle = flag; if (lists[i]->bin_method > 0) { neigh_pair[i]->nb = neigh_bin[requests[i]->index_bin]; if (neigh_pair[i]->nb == nullptr) error->all(FLERR,"Could not assign bin method to neighbor pair"); } if (lists[i]->stencil_method > 0) { neigh_pair[i]->ns = neigh_stencil[requests[i]->index_stencil]; if (neigh_pair[i]->ns == nullptr) error->all(FLERR,"Could not assign stencil method to neighbor pair"); } requests[i]->index_pair = i; } // allocate initial pages for each list, except if copy flag set for (i = 0; i < nlist; i++) { if (lists[i]->copy && !lists[i]->trim && !lists[i]->kk2cpu) continue; lists[i]->setup_pages(pgsize,oneatom); } // first-time allocation of per-atom data for lists that are built and store // lists that do not store: copy // use atom->nmax for both grow() args // i.e. grow first time to expanded size to avoid future reallocs // also Kokkos list initialization int maxatom = atom->nmax; for (i = 0; i < nlist; i++) { if (neigh_pair[i] && (!lists[i]->copy || lists[i]->trim || lists[i]->kk2cpu)) lists[i]->grow(maxatom,maxatom); } // plist = indices of perpetual NPair classes // perpetual = non-occasional, re-built at every reneighboring // slist = indices of perpetual NStencil classes // perpetual = used by any perpetual NPair class delete[] slist; delete[] plist; nstencil_perpetual = npair_perpetual = 0; slist = new int[nstencil]; plist = new int[nlist]; for (i = 0; i < nlist; i++) { if (lists[i]->occasional == 0 && lists[i]->pair_method) plist[npair_perpetual++] = i; } for (i = 0; i < nstencil; i++) { flag = 0; for (j = 0; j < npair_perpetual; j++) if (lists[plist[j]]->stencil_method == neigh_stencil[i]->istyle) flag = 1; if (flag) slist[nstencil_perpetual++] = i; } // reorder plist vector if necessary // relevant for lists that are derived from a parent list: // half-full,copy,skip // the child index must appear in plist after the parent index // swap two indices within plist when dependency is mis-ordered // start double loop check again whenever a swap is made // done when entire double loop test results in no swaps NeighList *ptr; int done = 0; while (!done) { done = 1; for (i = 0; i < npair_perpetual; i++) { for (k = 0; k < 3; k++) { ptr = nullptr; if (k == 0) ptr = lists[plist[i]]->listcopy; if (k == 1) ptr = lists[plist[i]]->listskip; if (k == 2) ptr = lists[plist[i]]->listfull; if (ptr == nullptr) continue; for (m = 0; m < nrequest; m++) if (ptr == lists[m]) break; for (j = 0; j < npair_perpetual; j++) if (m == plist[j]) break; if (j < i) continue; int tmp = plist[i]; // swap I,J indices plist[i] = plist[j]; plist[j] = tmp; done = 0; break; } if (!done) break; } } // debug output #ifdef NEIGH_LIST_DEBUG for (i = 0; i < nrequest; i++) lists[i]->print_attributes(); #endif return same; } /* ---------------------------------------------------------------------- sort NeighRequests by cutoff distance to find smallest list for trimming ------------------------------------------------------------------------- */ void Neighbor::sort_requests() { NeighRequest *jrq; int i,j,jmin; double jcut; delete[] j_sorted; j_sorted = new int[nrequest]; for (i = 0; i < nrequest; i++) j_sorted[i] = i; for (i = 0; i < nrequest; i++) { double cutoff_min = cutneighmax; jmin = i; for (j = i; j < nrequest-1; j++) { jrq = requests[j_sorted[j]]; if (jrq->cut) jcut = jrq->cutoff; else jcut = cutneighmax; if (jcut <= cutoff_min) { cutoff_min = jcut; jmin = j; } } int tmp = j_sorted[i]; j_sorted[i] = j_sorted[jmin]; j_sorted[jmin] = tmp; } } /* ---------------------------------------------------------------------- scan NeighRequests to set additional flags: custom cutoff lists and accelerator lists ------------------------------------------------------------------------- */ void Neighbor::morph_unique() { NeighRequest *irq; for (int i = 0; i < nrequest; i++) { irq = requests[i]; // if cut flag set by requestor and cutoff is different than default, // set unique flag, otherwise unset cut flag // this forces Pair,Stencil,Bin styles to be instantiated separately // also add skin to cutoff of perpetual lists if (irq->cut) { if (!irq->occasional) irq->cutoff += skin; if (irq->cutoff != cutneighmax) { irq->unique = 1; } else { irq->cut = 0; irq->cutoff = 0.0; } } // avoid flagging a neighbor list as both INTEL and OPENMP if (irq->intel) irq->omp = 0; // avoid flagging a neighbor list as both KOKKOS and INTEL or OPENMP if (irq->kokkos_host || irq->kokkos_device) irq->omp = irq->intel = 0; } } /* ---------------------------------------------------------------------- scan NeighRequests to process all skip lists look for a matching non-skip list if one exists, point at it via skiplist else make new parent via copy_request() and point at it ------------------------------------------------------------------------- */ void Neighbor::morph_skip() { int i,j,inewton,jnewton; NeighRequest *irq,*jrq,*nrq; for (i = 0; i < nrequest; i++) { irq = requests[i]; // only processing skip lists if (!irq->skip) continue; // these lists are created other ways, no need for skipping // halffull list and its full parent may both skip, // but are checked to ensure matching skip info if (irq->halffull) continue; if (irq->copy) continue; // check all other lists for (j = 0; j < nrequest; j++) { if (i == j) continue; jrq = requests[j]; // can only skip from a perpetual non-skip list if (jrq->occasional) continue; if (jrq->skip) continue; // both lists must be half, or both full if (irq->half != jrq->half) continue; if (irq->full != jrq->full) continue; // both lists must be newton on, or both newton off // IJ newton = 1 for newton on, 2 for newton off inewton = irq->newton; if (inewton == 0) inewton = force->newton_pair ? 1 : 2; jnewton = jrq->newton; if (jnewton == 0) jnewton = force->newton_pair ? 1 : 2; if (inewton != jnewton) continue; // these flags must be same, // else 2 lists do not store same pairs // or their data structures are different // this includes custom cutoff set by requestor // NOTE: need check for 2 Kokkos flags? if (irq->ghost != jrq->ghost) continue; if (irq->size != jrq->size) continue; if (irq->history != jrq->history) continue; if (irq->bond != jrq->bond) continue; if (irq->omp != jrq->omp) continue; if (irq->intel != jrq->intel) continue; if (irq->kokkos_host != jrq->kokkos_host) continue; if (irq->kokkos_device != jrq->kokkos_device) continue; if (irq->ssa != jrq->ssa) continue; if (irq->cut != jrq->cut) continue; if (irq->cutoff != jrq->cutoff) continue; // 2 lists are a match break; } // if matching list exists, point to it // else create a new identical list except non-skip // for new list, set neigh = 1, skip = 0, no skip vec/array, // copy unique flag (since copy_request() will not do it) // note: parents of skip lists do not have associated history // b/c child skip lists have the associated history if (j < nrequest) irq->skiplist = j; else { int newrequest = request(this,-1); irq->skiplist = newrequest; nrq = requests[newrequest]; nrq->copy_request(irq,0); nrq->pair = nrq->fix = nrq->compute = nrq->command = 0; nrq->neigh = 1; nrq->skip = 0; if (irq->unique) nrq->unique = 1; } } } /* ---------------------------------------------------------------------- scan NeighRequests just added by morph_skip for hybrid granular adjust newton/oneside parent settings if children require onesided skipping also set children off2on flag if parent becomes a newton off list this is needed because line/gran and tri/gran pair styles require onesided neigh lists and system newton on, but parent list must be newton off to enable the onesided skipping ------------------------------------------------------------------------- */ void Neighbor::morph_granular() { int i,j; NeighRequest *irq,*jrq; for (i = 0; i < nrequest; i++) { irq = requests[i]; // only examine NeighRequests added by morph_skip() // only those with size attribute for granular systems if (!irq->neigh) continue; if (!irq->size) continue; // check children of this list int onesided = -1; for (j = 0; j < nrequest; j++) { jrq = requests[j]; // only consider JRQ pair, size lists that skip from Irq list if (!jrq->pair) continue; if (!jrq->size) continue; if (!jrq->skip || jrq->skiplist != i) continue; // onesided = -1 if no children // onesided = 0/1 = child granonesided value if same for all children // onesided = 2 if children have different granonesided values if (onesided < 0) onesided = jrq->granonesided; else if (onesided != jrq->granonesided) onesided = 2; if (onesided == 2) break; } // if onesided = 2, parent has children with both granonesided = 0/1 // force parent newton off (newton = 2) to enable onesided skip by child // set parent granonesided = 0, so it stores all neighs in usual manner // set off2on = 1 for all children, since they expect newton on lists // this is b/c granonesided only set by line/gran and tri/gran which // both require system newton on if (onesided == 2) { irq->newton = 2; irq->granonesided = 0; for (j = 0; j < nrequest; j++) { jrq = requests[j]; // only consider JRQ pair, size lists that skip from Irq list if (!jrq->pair) continue; if (!jrq->size) continue; if (!jrq->skip || jrq->skiplist != i) continue; jrq->off2on = 1; } } } } /* ---------------------------------------------------------------------- scan NeighRequests for possible half lists to derive from full lists if 2 requests match, set half list to derive from full list ------------------------------------------------------------------------- */ void Neighbor::morph_halffull() { int i,j,jj; NeighRequest *irq,*jrq; double icut,jcut; for (i = 0; i < nrequest; i++) { irq = requests[i]; int trim_flag = irq->trim; // only processing half lists if (!irq->half) continue; // these lists are created other ways, no need for halffull // do want to process skip lists if (irq->copy) continue; // check all other lists for (jj = 0; jj < nrequest; jj++) { if (irq->cut) j = j_sorted[jj]; else j = jj; jrq = requests[j]; // can only derive from a perpetual full list // newton setting of derived list does not matter if (jrq->occasional) continue; if (!jrq->full) continue; // trim a list with longer cutoff if (irq->cut) icut = irq->cutoff; else icut = cutneighmax; if (jrq->cut) jcut = jrq->cutoff; else jcut = cutneighmax; if (icut > jcut) continue; else if (icut != jcut) trim_flag = 1; // these flags must be same, // else 2 lists do not store same pairs // or their data structures are different if (irq->ghost != jrq->ghost) continue; if (irq->size != jrq->size) continue; if (irq->history != jrq->history) continue; if (irq->bond != jrq->bond) continue; if (irq->omp != jrq->omp) continue; if (irq->intel != jrq->intel) continue; if (irq->kokkos_host != jrq->kokkos_host) continue; if (irq->kokkos_device != jrq->kokkos_device) continue; if (irq->ssa != jrq->ssa) continue; // skip flag must be same // if both are skip lists, skip info must match if (irq->skip != jrq->skip) continue; if (irq->skip && irq->same_skip(jrq) == 0) continue; // 2 lists are a match break; } // if matching list exists, point to it if (jj < nrequest) { irq->halffull = 1; irq->halffulllist = j; irq->trim = trim_flag; } } } /* ---------------------------------------------------------------------- scan NeighRequests for possible copies or trims if 2 requests match, turn one into a copy or trim of the other ------------------------------------------------------------------------- */ void Neighbor::morph_copy_trim() { int i,j,jj,inewton,jnewton; NeighRequest *irq,*jrq; double icut,jcut; for (i = 0; i < nrequest; i++) { irq = requests[i]; int trim_flag = irq->trim; // this list is already a copy list due to another morph method if (irq->copy) continue; // check all other lists for (jj = 0; jj < nrequest; jj++) { if (irq->cut) j = j_sorted[jj]; else j = jj; if (i == j) continue; jrq = requests[j]; // other list is already copied from this one if (jrq->copy && jrq->copylist == i) continue; // trim a list with longer cutoff if (irq->cut) icut = irq->cutoff; else icut = cutneighmax; if (jrq->cut) jcut = jrq->cutoff; else jcut = cutneighmax; if (icut > jcut) continue; else if (icut != jcut) trim_flag = 1; // other list (jrq) to copy from must be perpetual // list that becomes a copy list (irq) can be perpetual or occasional // if both lists are perpetual, require j < i // to prevent circular dependence with 3 or more copies of a list if (jrq->occasional) continue; if (!irq->occasional && !irq->cut && j > i) continue; // both lists must be half, or both full if (irq->half != jrq->half) continue; if (irq->full != jrq->full) continue; // both lists must be newton on, or both newton off // IJ newton = 1 for newton on, 2 for newton off inewton = irq->newton; if (inewton == 0) inewton = force->newton_pair ? 1 : 2; jnewton = jrq->newton; if (jnewton == 0) jnewton = force->newton_pair ? 1 : 2; if (inewton != jnewton) continue; // ok for non-ghost list to copy from ghost list, but not vice versa if (irq->ghost && !jrq->ghost) continue; // do not copy from a list with respa middle/inner // b/c its outer list will not be complete if (jrq->respamiddle) continue; if (jrq->respainner) continue; // these flags must be same, // else 2 lists do not store same pairs // or their data structures are different // no need to check omp b/c it stores same pairs // NOTE: need check for 2 Kokkos flags? if (irq->size != jrq->size) continue; if (irq->history != jrq->history) continue; if (irq->bond != jrq->bond) continue; if (irq->intel != jrq->intel) continue; if (irq->kokkos_host && !jrq->kokkos_host) continue; if (irq->kokkos_device && !jrq->kokkos_device) continue; if (irq->ssa != jrq->ssa) continue; // skip flag must be same // if both are skip lists, skip info must match if (irq->skip != jrq->skip) continue; if (irq->skip && irq->same_skip(jrq) == 0) continue; // 2 lists are a match break; } // turn list I into a copy of list J // do not copy a list from another copy list, but from its parent list if (jj < nrequest) { irq->copy = 1; irq->trim = trim_flag; if (jrq->copy && irq->cutoff == requests[jrq->copylist]->cutoff) irq->copylist = jrq->copylist; else irq->copylist = j; } } } /* ---------------------------------------------------------------------- create and initialize NTopo classes ------------------------------------------------------------------------- */ void Neighbor::init_topology() { int i,m; if (atom->molecular == Atom::ATOMIC) return; // set flags that determine which topology neighbor classes to use // these settings could change from run to run, depending on fixes defined // bonds,etc can only be broken for atom->molecular = Atom::MOLECULAR, not Atom::TEMPLATE // SHAKE sets bonds and angles negative // gcmc sets all bonds, angles, etc negative // partial_flag sets bonds to 0 // delete_bonds sets all interactions negative int bond_off = 0; int angle_off = 0; for (i = 0; i < modify->nfix; i++) if (utils::strmatch(modify->fix[i]->style,"^shake") || utils::strmatch(modify->fix[i]->style,"^rattle")) bond_off = angle_off = 1; if (force->bond) if (force->bond->partial_flag) bond_off = 1; if (atom->avec->bonds_allow && atom->molecular == Atom::MOLECULAR) { for (i = 0; i < atom->nlocal; i++) { if (bond_off) break; for (m = 0; m < atom->num_bond[i]; m++) if (atom->bond_type[i][m] <= 0) bond_off = 1; } } if (atom->avec->angles_allow && atom->molecular == Atom::MOLECULAR) { for (i = 0; i < atom->nlocal; i++) { if (angle_off) break; for (m = 0; m < atom->num_angle[i]; m++) if (atom->angle_type[i][m] <= 0) angle_off = 1; } } int dihedral_off = 0; if (atom->avec->dihedrals_allow && atom->molecular == Atom::MOLECULAR) { for (i = 0; i < atom->nlocal; i++) { if (dihedral_off) break; for (m = 0; m < atom->num_dihedral[i]; m++) if (atom->dihedral_type[i][m] <= 0) dihedral_off = 1; } } int improper_off = 0; if (atom->avec->impropers_allow && atom->molecular == Atom::MOLECULAR) { for (i = 0; i < atom->nlocal; i++) { if (improper_off) break; for (m = 0; m < atom->num_improper[i]; m++) if (atom->improper_type[i][m] <= 0) improper_off = 1; } } for (i = 0; i < modify->nfix; i++) if ((strcmp(modify->fix[i]->style,"gcmc") == 0)) bond_off = angle_off = dihedral_off = improper_off = 1; // sync on/off settings across all procs int onoff = bond_off; MPI_Allreduce(&onoff,&bond_off,1,MPI_INT,MPI_MAX,world); onoff = angle_off; MPI_Allreduce(&onoff,&angle_off,1,MPI_INT,MPI_MAX,world); onoff = dihedral_off; MPI_Allreduce(&onoff,&dihedral_off,1,MPI_INT,MPI_MAX,world); onoff = improper_off; MPI_Allreduce(&onoff,&improper_off,1,MPI_INT,MPI_MAX,world); // instantiate NTopo classes if (atom->avec->bonds_allow) { int old_bondwhich = bondwhich; if (atom->molecular == Atom::TEMPLATE) bondwhich = TEMPLATE; else if (bond_off) bondwhich = PARTIAL; else bondwhich = ALL; if (!neigh_bond || bondwhich != old_bondwhich) { delete neigh_bond; if (bondwhich == ALL) neigh_bond = new NTopoBondAll(lmp); else if (bondwhich == PARTIAL) neigh_bond = new NTopoBondPartial(lmp); else if (bondwhich == TEMPLATE) neigh_bond = new NTopoBondTemplate(lmp); } } if (atom->avec->angles_allow) { int old_anglewhich = anglewhich; if (atom->molecular == Atom::TEMPLATE) anglewhich = TEMPLATE; else if (angle_off) anglewhich = PARTIAL; else anglewhich = ALL; if (!neigh_angle || anglewhich != old_anglewhich) { delete neigh_angle; if (anglewhich == ALL) neigh_angle = new NTopoAngleAll(lmp); else if (anglewhich == PARTIAL) neigh_angle = new NTopoAnglePartial(lmp); else if (anglewhich == TEMPLATE) neigh_angle = new NTopoAngleTemplate(lmp); } } if (atom->avec->dihedrals_allow) { int old_dihedralwhich = dihedralwhich; if (atom->molecular == Atom::TEMPLATE) dihedralwhich = TEMPLATE; else if (dihedral_off) dihedralwhich = PARTIAL; else dihedralwhich = ALL; if (!neigh_dihedral || dihedralwhich != old_dihedralwhich) { delete neigh_dihedral; if (dihedralwhich == ALL) neigh_dihedral = new NTopoDihedralAll(lmp); else if (dihedralwhich == PARTIAL) neigh_dihedral = new NTopoDihedralPartial(lmp); else if (dihedralwhich == TEMPLATE) neigh_dihedral = new NTopoDihedralTemplate(lmp); } } if (atom->avec->impropers_allow) { int old_improperwhich = improperwhich; if (atom->molecular == Atom::TEMPLATE) improperwhich = TEMPLATE; else if (improper_off) improperwhich = PARTIAL; else improperwhich = ALL; if (!neigh_improper || improperwhich != old_improperwhich) { delete neigh_improper; if (improperwhich == ALL) neigh_improper = new NTopoImproperAll(lmp); else if (improperwhich == PARTIAL) neigh_improper = new NTopoImproperPartial(lmp); else if (improperwhich == TEMPLATE) neigh_improper = new NTopoImproperTemplate(lmp); } } } /* ---------------------------------------------------------------------- output summary of pairwise neighbor list info only called by proc 0 ------------------------------------------------------------------------- */ void Neighbor::print_pairwise_info() { int i; NeighRequest *rq; const double cutghost = MAX(cutneighmax,comm->cutghostuser); double binsize, bbox[3]; bbox[0] = bboxhi[0]-bboxlo[0]; bbox[1] = bboxhi[1]-bboxlo[1]; bbox[2] = bboxhi[2]-bboxlo[2]; if (binsizeflag) binsize = binsize_user; else if (style == Neighbor::BIN) binsize = 0.5*cutneighmax; else binsize = 0.5*cutneighmin; if (binsize == 0.0) binsize = bbox[0]; int nperpetual = 0; int noccasional = 0; int nextra = 0; for (i = 0; i < nlist; i++) { if (lists[i]->pair_method == 0) nextra++; else if (lists[i]->occasional) noccasional++; else nperpetual++; } std::string out = "Neighbor list info ...\n"; out += fmt::format(" update: every = {} steps, delay = {} steps, check = {}\n", every,delay,dist_check ? "yes" : "no"); out += fmt::format(" max neighbors/atom: {}, page size: {}\n", oneatom, pgsize); out += fmt::format(" master list distance cutoff = {:.8g}\n",cutneighmax); out += fmt::format(" ghost atom cutoff = {:.8g}\n",cutghost); if (style != Neighbor::NSQ) out += fmt::format(" binsize = {:.8g}, bins = {:g} {:g} {:g}\n",binsize, ceil(bbox[0]/binsize), ceil(bbox[1]/binsize), ceil(bbox[2]/binsize)); out += fmt::format(" {} neighbor lists, perpetual/occasional/extra = {} {} {}\n", nlist,nperpetual,noccasional,nextra); for (i = 0; i < nlist; i++) { rq = requests[i]; if (rq->pair) { char *pname = force->pair_match_ptr((Pair *) rq->requestor); if (pname) out += fmt::format(" ({}) pair {}",i+1,pname); else out += fmt::format(" ({}) pair (none)",i+1); } else if (rq->fix) { out += fmt::format(" ({}) fix {}",i+1,((Fix *) rq->requestor)->style); } else if (rq->compute) { out += fmt::format(" ({}) compute {}",i+1,((Compute *) rq->requestor)->style); } else if (rq->command) { out += fmt::format(" ({}) command {}",i+1,rq->command_style); } else if (rq->neigh) { out += fmt::format(" ({}) neighbor class addition",i+1); } if (rq->occasional) out += ", occasional"; else out += ", perpetual"; // order these to get single output of most relevant if (rq->copy) { if (rq->trim) out += fmt::format(", trim from ({})",rq->copylist+1); else out += fmt::format(", copy from ({})",rq->copylist+1); } else if (rq->halffull) if (rq->trim) out += fmt::format(", half/full trim from ({})",rq->halffulllist+1); else out += fmt::format(", half/full from ({})",rq->halffulllist+1); else if (rq->skip) out += fmt::format(", skip from ({})",rq->skiplist+1); out += "\n"; // list of neigh list attributes out += " attributes: "; if (rq->half) out += "half"; else if (rq->full) out += "full"; if (rq->newton == 0) { if (force->newton_pair) out += ", newton on"; else out += ", newton off"; } else if (rq->newton == 1) out += ", newton on"; else if (rq->newton == 2) out += ", newton off"; if (rq->ghost) out += ", ghost"; if (rq->size) out += ", size"; if (rq->history) out += ", history"; if (rq->granonesided) out += ", onesided"; if (rq->respamiddle) out += ", respa outer/middle/inner"; else if (rq->respainner) out += ", respa outer/inner"; if (rq->bond) out += ", bond"; if (rq->omp) out += ", omp"; if (rq->intel) out += ", intel"; if (rq->kokkos_device) out += ", kokkos_device"; if (rq->kokkos_host) out += ", kokkos_host"; if (rq->ssa) out += ", ssa"; if (rq->cut) out += fmt::format(", cut {}",rq->cutoff); if (rq->off2on) out += ", off2on"; out += "\n"; out += " "; if (lists[i]->pair_method == 0) out += "pair build: none\n"; else out += fmt::format("pair build: {}\n",pairnames[lists[i]->pair_method-1]); out += " "; if (lists[i]->stencil_method == 0) out += "stencil: none\n"; else out += fmt::format("stencil: {}\n",stencilnames[lists[i]->stencil_method-1]); out += " "; if (lists[i]->bin_method == 0) out += "bin: none\n"; else out += fmt::format("bin: {}\n",binnames[lists[i]->bin_method-1]); } utils::logmesg(lmp,out); } /* ---------------------------------------------------------------------- make copy of current requests and Neighbor params used to compare to when next run occurs ------------------------------------------------------------------------- */ void Neighbor::requests_new2old() { for (int i = 0; i < old_nrequest; i++) delete old_requests[i]; memory->sfree(old_requests); old_nrequest = nrequest; old_requests = (NeighRequest **) memory->smalloc(old_nrequest*sizeof(NeighRequest *),"neighbor:old_requests"); for (int i = 0; i < old_nrequest; i++) old_requests[i] = new NeighRequest(requests[i]); old_style = style; old_triclinic = triclinic; old_pgsize = pgsize; old_oneatom = oneatom; } /* ---------------------------------------------------------------------- find and return request made by classptr if not found or classptr = nullptr, return nullptr id is optional and defaults to 0, which is the request id value unless set explicitly ------------------------------------------------------------------------- */ NeighRequest *Neighbor::find_request(void *classptr, const int id) const { if (classptr == nullptr) return nullptr; for (int i = 0; i < nrequest; i++) if ((requests[i]->requestor == classptr) && (requests[i]->id == id)) return requests[i]; return nullptr; } /* ---------------------------------------------------------------------- return vector with neighbor list requests from pair styles ------------------------------------------------------------------------- */ const std::vector Neighbor::get_pair_requests() const { std::vector matches; for (int i=0; i < nrequest; ++i) if (requests[i]->pair) matches.push_back(requests[i]); return matches; } /* ---------------------------------------------------------------------- find and return list requested by classptr if not found or classptr = nullptr, return nullptr id is optional and defaults to 0, which is the request id value unless set explicitly ------------------------------------------------------------------------- */ NeighList *Neighbor::find_list(void *classptr, const int id) const { if (classptr == nullptr) return nullptr; for (int i = 0; i < nlist; i++) if ((lists[i]->requestor == classptr) && (lists[i]->id == id)) return lists[i]; return nullptr; } /* ---------------------------------------------------------------------- assign NBin class to a NeighList use neigh request settings to build mask match mask to list of masks of known Nbin classes return index+1 of match in list of masks return 0 for no binning return -1 if no match ------------------------------------------------------------------------- */ int Neighbor::choose_bin(NeighRequest *rq) { // no binning needed if (style == Neighbor::NSQ) return 0; if (rq->skip || rq->copy || rq->halffull) return 0; // use request settings to match exactly one NBin class mask // checks are bitwise using NeighConst bit masks int mask; for (int i = 0; i < nbclass; i++) { mask = binmasks[i]; // require match of these request flags and mask bits // (!A != !B) is effectively a logical xor if (!rq->intel != !(mask & NB_INTEL)) continue; if (!rq->ssa != !(mask & NB_SSA)) continue; if (!rq->kokkos_device != !(mask & NB_KOKKOS_DEVICE)) continue; if (!rq->kokkos_host != !(mask & NB_KOKKOS_HOST)) continue; // multi neighbor style require multi bin style if (style == Neighbor::MULTI) { if (!(mask & NB_MULTI)) continue; } else { if (!(mask & NB_STANDARD)) continue; } return i+1; } // error return if matched none return -1; } /* ---------------------------------------------------------------------- assign NStencil class to a NeighList use neigh request settings to build mask match mask to list of masks of known NStencil classes return index+1 of match in list of masks return 0 for no binning return -1 if no match ------------------------------------------------------------------------- */ int Neighbor::choose_stencil(NeighRequest *rq) { // no stencil creation needed if (style == Neighbor::NSQ) return 0; if (rq->skip || rq->copy || rq->halffull) return 0; // convert newton request to newtflag = on or off int newtflag = 1; if (rq->newton == 0 && newton_pair) newtflag = 1; else if (rq->newton == 0 && !newton_pair) newtflag = 0; else if (rq->newton == 1) newtflag = 1; else if (rq->newton == 2) newtflag = 0; // request a full stencil if building full neighbor list or newton is off int fullflag = 0; if (rq->full) fullflag = 1; if (!newtflag) fullflag = 1; //printf("STENCIL RQ FLAGS: hff %d %d n %d g %d s %d newtflag %d fullflag %d\n", // rq->half,rq->full,rq->newton,rq->ghost,rq->ssa, // newtflag, fullflag); // use request and system settings to match exactly one NStencil class mask // checks are bitwise using NeighConst bit masks int mask; for (int i = 0; i < nsclass; i++) { mask = stencilmasks[i]; //printf("III %d: half %d full %d ghost %d ssa %d\n", // i,mask & NS_HALF,mask & NS_FULL,mask & NS_GHOST,mask & NS_SSA); // exactly one of half or full is set and must match if (fullflag) { if (!(mask & NS_FULL)) continue; } else { if (!(mask & NS_HALF)) continue; } // require match of these request flags and mask bits // (!A != !B) is effectively a logical xor if (!rq->ghost != !(mask & NS_GHOST)) continue; if (!rq->ssa != !(mask & NS_SSA)) continue; // neighbor style is one of BIN, MULTI_OLD, or MULTI and must match if (style == Neighbor::BIN) { if (!(mask & NS_BIN)) continue; } else if (style == Neighbor::MULTI_OLD) { if (!(mask & NS_MULTI_OLD)) continue; } else if (style == Neighbor::MULTI) { if (!(mask & NS_MULTI)) continue; } // dimension is 2 or 3 and must match if (dimension == 2) { if (!(mask & NS_2D)) continue; } else if (dimension == 3) { if (!(mask & NS_3D)) continue; } // domain triclinic flag is on or off and must match if (triclinic) { if (!(mask & NS_TRI)) continue; } else if (!triclinic) { if (!(mask & NS_ORTHO)) continue; } return i+1; } // error return if matched none return -1; } /* ---------------------------------------------------------------------- assign NPair class to a NeighList use neigh request settings to build mask match mask to list of masks of known NPair classes return index+1 of match in list of masks return 0 for no binning return -1 if no match ------------------------------------------------------------------------- */ int Neighbor::choose_pair(NeighRequest *rq) { // error check for includegroup with ghost neighbor request if (includegroup && rq->ghost) error->all(FLERR,"Neighbor include group not allowed with ghost neighbors"); // convert newton request to newtflag = on or off bool newtflag; if (rq->newton == 0 && newton_pair) newtflag = true; else if (rq->newton == 0 && !newton_pair) newtflag = false; else if (rq->newton == 1) newtflag = true; else if (rq->newton == 2) newtflag = false; else error->all(FLERR,"Illegal 'newton' flag in neighbor list request"); int molecular = atom->molecular; //printf("PAIR RQ FLAGS: hf %d %d n %d g %d sz %d gos %d r %d b %d o %d i %d " // "kk %d %d ss %d dn %d sk %d cp %d hf %d oo %d\n", // rq->half,rq->full,rq->newton,rq->ghost,rq->size, // rq->granonesided,rq->respaouter,rq->bond,rq->omp,rq->intel, // rq->kokkos_host,rq->kokkos_device,rq->ssa,rq->dnum, // rq->skip,rq->copy,rq->halffull,rq->off2on); // use request and system settings to match exactly one NPair class mask // checks are bitwise using NeighConst bit masks int mask; for (int i = 0; i < npclass; i++) { mask = pairmasks[i]; //printf(" PAIR NAMES i %d %d name %s mask %d\n",i,nrequest, // pairnames[i],pairmasks[i]); // if copy request, no further checks needed, just return or continue // trim and Kokkos device/host flags must also match in order to copy // intel and omp flags must match to trim if (rq->copy) { if (!(mask & NP_COPY)) continue; if (rq->trim) { if (!rq->trim != !(mask & NP_TRIM)) continue; if (!rq->omp != !(mask & NP_OMP)) continue; if (!rq->intel != !(mask & NP_INTEL)) continue; } if (rq->kokkos_device || rq->kokkos_host) { if (!rq->kokkos_device != !(mask & NP_KOKKOS_DEVICE)) continue; if (!rq->kokkos_host != !(mask & NP_KOKKOS_HOST)) continue; } if (!requests[rq->copylist]->kokkos_device != !(mask & NP_KOKKOS_DEVICE)) continue; if (!requests[rq->copylist]->kokkos_host != !(mask & NP_KOKKOS_HOST)) continue; return i+1; } // exactly one of half or full is set and must match if (rq->half) { if (!(mask & NP_HALF)) continue; } else if (rq->full) { if (!(mask & NP_FULL)) continue; } // newtflag is on or off and must match if (newtflag) { if (!(mask & NP_NEWTON)) continue; } else if (!newtflag) { if (!(mask & NP_NEWTOFF)) continue; } // if molecular on, do not match ATOMONLY (b/c a MOLONLY Npair exists) // if molecular off, do not match MOLONLY (b/c an ATOMONLY Npair exists) if (molecular != Atom::ATOMIC) { if (mask & NP_ATOMONLY) continue; } else if (molecular == Atom::ATOMIC) { if (mask & NP_MOLONLY) continue; } // require match of these request flags and mask bits // (!A != !B) is effectively a logical xor if (!rq->ghost != !(mask & NP_GHOST)) continue; if (!rq->size != !(mask & NP_SIZE)) continue; if (!rq->respaouter != !(mask & NP_RESPA)) continue; if (!rq->granonesided != !(mask & NP_ONESIDE)) continue; if (!rq->respaouter != !(mask & NP_RESPA)) continue; if (!rq->bond != !(mask & NP_BOND)) continue; if (!rq->omp != !(mask & NP_OMP)) continue; if (!rq->intel != !(mask & NP_INTEL)) continue; if (!rq->kokkos_device != !(mask & NP_KOKKOS_DEVICE)) continue; if (!rq->kokkos_host != !(mask & NP_KOKKOS_HOST)) continue; if (!rq->ssa != !(mask & NP_SSA)) continue; if (!rq->skip != !(mask & NP_SKIP)) continue; if (!rq->trim != !(mask & NP_TRIM)) continue; if (!rq->halffull != !(mask & NP_HALF_FULL)) continue; if (!rq->off2on != !(mask & NP_OFF2ON)) continue; // neighbor style is one of NSQ, BIN, MULTI_OLD, or MULTI and must match if (style == Neighbor::NSQ) { if (!(mask & NP_NSQ)) continue; } else if (style == Neighbor::BIN) { if (!(mask & NP_BIN)) continue; } else if (style == Neighbor::MULTI_OLD) { if (!(mask & NP_MULTI_OLD)) continue; } else if (style == Neighbor::MULTI) { if (!(mask & NP_MULTI)) continue; } // domain triclinic flag is on or off and must match if (triclinic) { if (!(mask & NP_TRI)) continue; } else if (!triclinic) { if (!(mask & NP_ORTHO)) continue; } return i+1; } // error return if matched none return -1; } /* ---------------------------------------------------------------------- called internally to request a pairwise neighbor list ------------------------------------------------------------------------- */ int Neighbor::request(void *requestor, int instance) { if (nrequest == maxrequest) { maxrequest += RQDELTA; requests = (NeighRequest **) memory->srealloc(requests,maxrequest*sizeof(NeighRequest *), "neighbor:requests"); } requests[nrequest] = new NeighRequest(lmp, requestor, instance); nrequest++; return nrequest-1; } /* ---------------------------------------------------------------------- add_request() is called by other classes to request a pairwise neighbor list ------------------------------------------------------------------------- */ NeighRequest *Neighbor::add_request(Pair *requestor, int flags) { int irequest = request(requestor, requestor->instance_me); auto req = requests[irequest]; req->apply_flags(flags); // apply intel flag. omp flag is set globally via set_omp_neighbor() if (requestor->suffix_flag & Suffix::INTEL) { req->intel = 1; req->omp = 0; } return req; } NeighRequest *Neighbor::add_request(Fix *requestor, int flags) { int irequest = request(requestor, requestor->instance_me); auto req = requests[irequest]; req->pair = 0; req->fix = 1; req->apply_flags(flags); return req; } NeighRequest *Neighbor::add_request(Compute *requestor, int flags) { int irequest = request(requestor, requestor->instance_me); auto req = requests[irequest]; req->pair = 0; req->compute = 1; req->apply_flags(flags); return req; } NeighRequest *Neighbor::add_request(Command *requestor, const char *style, int flags) { int irequest = request(requestor, 0); auto req = requests[irequest]; req->pair = 0; req->command = 1; req->occasional = 1; req->command_style = style; req->apply_flags(flags); return req; } // set neighbor list request OpenMP flag void Neighbor::set_omp_neighbor(int flag) { // flag *all* neighbor list requests as OPENMP threaded, // but skip lists already flagged as INTEL threaded for (int i = 0; i < nrequest; ++i) if (!requests[i]->intel) requests[i]->omp = flag; } /* report if there is a neighbor list with the intel flag set */ bool Neighbor::has_intel_request() const { return (((nrequest > 0) && (requests[0]->intel > 0)) || ((old_nrequest > 0) && (old_requests[0]->intel > 0))); } /* ---------------------------------------------------------------------- setup neighbor binning and neighbor stencils called before run and every reneighbor if box size/shape changes only operates on perpetual lists build_one() operates on occasional lists ------------------------------------------------------------------------- */ void Neighbor::setup_bins() { // invoke setup_bins() for all NBin // actual binning is performed in build() for (int i = 0; i < nbin; i++) neigh_bin[i]->setup_bins(style); // invoke create_setup() and create() for all perpetual NStencil // same ops performed for occasional lists in build_one() for (int i = 0; i < nstencil_perpetual; i++) { neigh_stencil[slist[i]]->create_setup(); neigh_stencil[slist[i]]->create(); } last_setup_bins = update->ntimestep; } /* ---------------------------------------------------------------------- */ int Neighbor::decide() { if (must_check) { bigint n = update->ntimestep; if (restart_check && n == output->next_restart) return 1; for (int i = 0; i < fix_check; i++) if (n == modify->fix[fixchecklist[i]]->next_reneighbor) return 1; } ago++; if (ago >= delay && ago % every == 0) { if (build_once) return 0; if (dist_check == 0) return 1; return check_distance(); } else return 0; } /* ---------------------------------------------------------------------- if any atom moved trigger distance (half of neighbor skin) return 1 shrink trigger distance if box size has changed conservative shrink procedure: compute distance each of 8 corners of box has moved since last reneighbor reduce skin distance by sum of 2 largest of the 8 values if reduced skin distance is negative, set to zero new trigger = 1/2 of reduced skin distance for orthogonal box, only need 2 lo/hi corners for triclinic, need all 8 corners since deformations can displace all 8 ------------------------------------------------------------------------- */ int Neighbor::check_distance() { double delx,dely,delz,rsq; double delta,deltasq,delta1,delta2; if (boxcheck) { if (triclinic == 0) { delx = bboxlo[0] - boxlo_hold[0]; dely = bboxlo[1] - boxlo_hold[1]; delz = bboxlo[2] - boxlo_hold[2]; delta1 = sqrt(delx*delx + dely*dely + delz*delz); delx = bboxhi[0] - boxhi_hold[0]; dely = bboxhi[1] - boxhi_hold[1]; delz = bboxhi[2] - boxhi_hold[2]; delta2 = sqrt(delx*delx + dely*dely + delz*delz); delta = 0.5 * (skin - (delta1+delta2)); if (delta < 0.0) delta = 0.0; deltasq = delta*delta; } else { domain->box_corners(); delta1 = delta2 = 0.0; for (int i = 0; i < 8; i++) { delx = corners[i][0] - corners_hold[i][0]; dely = corners[i][1] - corners_hold[i][1]; delz = corners[i][2] - corners_hold[i][2]; delta = sqrt(delx*delx + dely*dely + delz*delz); if (delta > delta1) delta1 = delta; else if (delta > delta2) delta2 = delta; } delta = 0.5 * (skin - (delta1+delta2)); if (delta < 0.0) delta = 0.0; deltasq = delta*delta; } } else deltasq = triggersq; double **x = atom->x; int nlocal = atom->nlocal; if (includegroup) nlocal = atom->nfirst; int flag = 0; for (int i = 0; i < nlocal; i++) { delx = x[i][0] - xhold[i][0]; dely = x[i][1] - xhold[i][1]; delz = x[i][2] - xhold[i][2]; rsq = delx*delx + dely*dely + delz*delz; if (rsq > deltasq) flag = 1; } int flagall; MPI_Allreduce(&flag,&flagall,1,MPI_INT,MPI_MAX,world); if (flagall && ago == MAX(every,delay)) ndanger++; return flagall; } /* ---------------------------------------------------------------------- build perpetual neighbor lists called at setup and every few timesteps during run or minimization topology lists also built if topoflag = 1 (Kokkos calls with topoflag=0) ------------------------------------------------------------------------- */ void Neighbor::build(int topoflag) { int i,m; ago = 0; ncalls++; lastcall = update->ntimestep; int nlocal = atom->nlocal; int nall = nlocal + atom->nghost; // rebuild collection array from scratch if (style == Neighbor::MULTI) build_collection(0); // check that using special bond flags will not overflow neigh lists if (nall > NEIGHMASK) error->one(FLERR,"Too many local+ghost atoms for neighbor list"); // store current atom positions and box size if needed if (dist_check) { double **x = atom->x; if (includegroup) nlocal = atom->nfirst; if (atom->nmax > maxhold) { maxhold = atom->nmax; memory->destroy(xhold); memory->create(xhold,maxhold,3,"neigh:xhold"); } for (i = 0; i < nlocal; i++) { xhold[i][0] = x[i][0]; xhold[i][1] = x[i][1]; xhold[i][2] = x[i][2]; } if (boxcheck) { if (triclinic == 0) { boxlo_hold[0] = bboxlo[0]; boxlo_hold[1] = bboxlo[1]; boxlo_hold[2] = bboxlo[2]; boxhi_hold[0] = bboxhi[0]; boxhi_hold[1] = bboxhi[1]; boxhi_hold[2] = bboxhi[2]; } else { domain->box_corners(); corners = domain->corners; for (i = 0; i < 8; i++) { corners_hold[i][0] = corners[i][0]; corners_hold[i][1] = corners[i][1]; corners_hold[i][2] = corners[i][2]; } } } } // bin atoms for all NBin instances // not just NBin associated with perpetual lists, also occasional lists // b/c cannot wait to bin occasional lists in build_one() call // if bin then, atoms may have moved outside of proc domain & bin extent, // leading to errors or even a crash if (style != Neighbor::NSQ) { if (last_setup_bins < 0) setup_bins(); for (i = 0; i < nbin; i++) { neigh_bin[i]->bin_atoms_setup(nall); neigh_bin[i]->bin_atoms(); } } // build pairwise lists for all perpetual NPair/NeighList // grow() with nlocal/nall args so that only realloc if have to for (i = 0; i < npair_perpetual; i++) { m = plist[i]; if (!lists[m]->copy || lists[m]->trim || lists[m]->kk2cpu) lists[m]->grow(nlocal,nall); neigh_pair[m]->build_setup(); neigh_pair[m]->build(lists[m]); } // build topology lists for bonds/angles/etc // skip if GPU package styles will call it explicitly to overlap with GPU computation. if ((atom->molecular != Atom::ATOMIC) && topoflag && !overlap_topo) build_topology(); } /* ---------------------------------------------------------------------- build topology neighbor lists: bond, angle, dihedral, improper copy their list info back to Neighbor for access by bond/angle/etc classes ------------------------------------------------------------------------- */ void Neighbor::build_topology() { if (force->bond) { neigh_bond->build(); nbondlist = neigh_bond->nbondlist; bondlist = neigh_bond->bondlist; } if (force->angle) { neigh_angle->build(); nanglelist = neigh_angle->nanglelist; anglelist = neigh_angle->anglelist; } if (force->dihedral) { neigh_dihedral->build(); ndihedrallist = neigh_dihedral->ndihedrallist; dihedrallist = neigh_dihedral->dihedrallist; } if (force->improper) { neigh_improper->build(); nimproperlist = neigh_improper->nimproperlist; improperlist = neigh_improper->improperlist; } } /* ---------------------------------------------------------------------- build a single occasional pairwise neighbor list indexed by I called by other classes ------------------------------------------------------------------------- */ void Neighbor::build_one(class NeighList *mylist, int preflag) { // check if list structure is initialized if (mylist == nullptr) error->all(FLERR,"Trying to build an occasional neighbor list before initialization complete"); // build_one() should never be invoked on a perpetual list if (!mylist->occasional) error->all(FLERR,"Neighbor::build_one() invoked on perpetual list"); // no need to build if already built since last re-neighbor // preflag is set by fix bond/create and fix bond/swap // b/c they invoke build_one() on same step neigh list is re-built, // but before re-build, so need to use ">" instead of ">=" NPair *np = neigh_pair[mylist->index]; if (preflag) { if (np->last_build > lastcall) return; } else { if (np->last_build >= lastcall) return; } // if this is copy list and parent is occasional list, // or this is halffull and parent is occasional list, // or this is skip list and parent is occasional list, // ensure parent is current if (mylist->listcopy && mylist->listcopy->occasional) build_one(mylist->listcopy,preflag); if (mylist->listfull && mylist->listfull->occasional) build_one(mylist->listfull,preflag); if (mylist->listskip && mylist->listskip->occasional) build_one(mylist->listskip,preflag); // create stencil if hasn't been created since last setup_bins() call NStencil *ns = np->ns; if (ns && ns->last_stencil < last_setup_bins) { ns->create_setup(); ns->create(); } // build the list if (!mylist->copy || mylist->trim || mylist->kk2cpu) mylist->grow(atom->nlocal,atom->nlocal+atom->nghost); np->build_setup(); np->build(mylist); } /* ---------------------------------------------------------------------- set neighbor style and skin distance ------------------------------------------------------------------------- */ void Neighbor::set(int narg, char **arg) { if (narg != 2) error->all(FLERR,"Illegal neighbor command: expected 2 arguments but found {}", narg); skin = utils::numeric(FLERR,arg[0],false,lmp); if (skin < 0.0) error->all(FLERR, "Invalid neighbor argument: {}", arg[0]); if (strcmp(arg[1],"nsq") == 0) style = Neighbor::NSQ; else if (strcmp(arg[1],"bin") == 0) style = Neighbor::BIN; else if (strcmp(arg[1],"multi") == 0) { style = Neighbor::MULTI; ncollections = atom->ntypes; } else if (strcmp(arg[1],"multi/old") == 0) style = Neighbor::MULTI_OLD; else error->all(FLERR,"Unknown neighbor {} argument: {}", arg[0], arg[1]); if (style == Neighbor::MULTI_OLD && lmp->citeme) lmp->citeme->add(cite_neigh_multi_old); if (style == Neighbor::MULTI && lmp->citeme) lmp->citeme->add(cite_neigh_multi); } /* ---------------------------------------------------------------------- reset timestamps in all NeignBin, NStencil, NPair classes so that neighbor lists will rebuild properly with timestep change ditto for lastcall and last_setup_bins ------------------------------------------------------------------------- */ void Neighbor::reset_timestep(bigint /*ntimestep*/) { for (int i = 0; i < nbin; i++) neigh_bin[i]->last_bin = -1; for (int i = 0; i < nstencil; i++) neigh_stencil[i]->last_stencil = -1; for (int i = 0; i < nlist; i++) { if (!neigh_pair[i]) continue; neigh_pair[i]->last_build = -1; } lastcall = -1; last_setup_bins = -1; } /* ---------------------------------------------------------------------- modify parameters of the pair-wise neighbor build ------------------------------------------------------------------------- */ void Neighbor::modify_params(int narg, char **arg) { int iarg = 0; while (iarg < narg) { if (strcmp(arg[iarg],"every") == 0) { if (iarg+2 > narg) utils::missing_cmd_args(FLERR, "neigh_modify every", error); every = utils::inumeric(FLERR,arg[iarg+1],false,lmp); if (every <= 0) error->all(FLERR, "Invalid neigh_modify every argument: {}", every); iarg += 2; } else if (strcmp(arg[iarg],"delay") == 0) { if (iarg+2 > narg) utils::missing_cmd_args(FLERR, "neigh_modify delay", error); delay = utils::inumeric(FLERR,arg[iarg+1],false,lmp); if (delay < 0) error->all(FLERR, "Invalid neigh_modify delay argument: {}", delay); iarg += 2; } else if (strcmp(arg[iarg],"check") == 0) { if (iarg+2 > narg) utils::missing_cmd_args(FLERR, "neigh_modify check", error); dist_check = utils::logical(FLERR,arg[iarg+1],false,lmp); iarg += 2; } else if (strcmp(arg[iarg],"once") == 0) { if (iarg+2 > narg) utils::missing_cmd_args(FLERR, "neigh_modify once", error); build_once = utils::logical(FLERR,arg[iarg+1],false,lmp); iarg += 2; } else if (strcmp(arg[iarg],"page") == 0) { if (iarg+2 > narg) utils::missing_cmd_args(FLERR, "neigh_modify page", error); old_pgsize = pgsize; pgsize = utils::inumeric(FLERR,arg[iarg+1],false,lmp); iarg += 2; } else if (strcmp(arg[iarg],"one") == 0) { if (iarg+2 > narg) utils::missing_cmd_args(FLERR, "neigh_modify one", error); old_oneatom = oneatom; oneatom = utils::inumeric(FLERR,arg[iarg+1],false,lmp); iarg += 2; } else if (strcmp(arg[iarg],"binsize") == 0) { if (iarg+2 > narg) utils::missing_cmd_args(FLERR, "neigh_modify binsize", error); binsize_user = utils::numeric(FLERR,arg[iarg+1],false,lmp); if (binsize_user <= 0.0) binsizeflag = 0; else binsizeflag = 1; iarg += 2; } else if (strcmp(arg[iarg],"cluster") == 0) { if (iarg+2 > narg) utils::missing_cmd_args(FLERR, "neigh_modify cluster", error); cluster_check = utils::logical(FLERR,arg[iarg+1],false,lmp); iarg += 2; } else if (strcmp(arg[iarg],"include") == 0) { if (iarg+2 > narg) utils::missing_cmd_args(FLERR, "neigh_modify include", error); includegroup = group->find(arg[iarg+1]); if (includegroup < 0) error->all(FLERR, "Invalid include keyword: group {} not found", arg[iarg+1]); if (atom->firstgroupname == nullptr) error->all(FLERR, "Invalid include keyword: atom_modify first command must be used"); if (strcmp(arg[iarg+1],atom->firstgroupname) != 0) error->all(FLERR, "Neigh_modify include group != atom_modify first group: {}", atom->firstgroupname); iarg += 2; } else if (strcmp(arg[iarg],"exclude") == 0) { if (iarg+2 > narg) utils::missing_cmd_args(FLERR, "neigh_modify exclude", error); if (strcmp(arg[iarg+1],"type") == 0) { if (iarg+4 > narg) utils::missing_cmd_args(FLERR, "neigh_modify exclude type", error); if (nex_type == maxex_type) { maxex_type += EXDELTA; memory->grow(ex1_type,maxex_type,"neigh:ex1_type"); memory->grow(ex2_type,maxex_type,"neigh:ex2_type"); } ex1_type[nex_type] = utils::inumeric(FLERR,arg[iarg+2],false,lmp); ex2_type[nex_type] = utils::inumeric(FLERR,arg[iarg+3],false,lmp); nex_type++; iarg += 4; } else if (strcmp(arg[iarg+1],"group") == 0) { if (iarg+4 > narg) utils::missing_cmd_args(FLERR, "neigh_modify exclude group", error); if (nex_group == maxex_group) { maxex_group += EXDELTA; memory->grow(ex1_group,maxex_group,"neigh:ex1_group"); memory->grow(ex2_group,maxex_group,"neigh:ex2_group"); } ex1_group[nex_group] = group->find(arg[iarg+2]); ex2_group[nex_group] = group->find(arg[iarg+3]); if (ex1_group[nex_group] == -1) error->all(FLERR, "Invalid exclude group keyword: group {} not found", arg[iarg+2]); if (ex2_group[nex_group] == -1) error->all(FLERR, "Invalid exclude group keyword: group {} not found", arg[iarg+3]); nex_group++; iarg += 4; } else if (strcmp(arg[iarg+1],"molecule/inter") == 0 || strcmp(arg[iarg+1],"molecule/intra") == 0) { if (iarg+3 > narg) utils::missing_cmd_args(FLERR, "neigh_modify exclude molecule", error); if (atom->molecule_flag == 0) error->all(FLERR,"Neigh_modify exclude molecule " "requires atom attribute molecule"); if (nex_mol == maxex_mol) { maxex_mol += EXDELTA; memory->grow(ex_mol_group,maxex_mol,"neigh:ex_mol_group"); if (lmp->kokkos) grow_ex_mol_intra_kokkos(); else memory->grow(ex_mol_intra,maxex_mol,"neigh:ex_mol_intra"); } ex_mol_group[nex_mol] = group->find(arg[iarg+2]); if (ex_mol_group[nex_mol] == -1) error->all(FLERR, "Invalid exclude keyword:group {} not found", arg[iarg+2]); if (strcmp(arg[iarg+1],"molecule/intra") == 0) ex_mol_intra[nex_mol] = 1; else ex_mol_intra[nex_mol] = 0; nex_mol++; iarg += 3; } else if (strcmp(arg[iarg+1],"none") == 0) { nex_type = nex_group = nex_mol = 0; iarg += 2; } else error->all(FLERR,"Unknown neigh_modify exclude keyword: {}", arg[iarg+1]); } else if (strcmp(arg[iarg],"collection/interval") == 0) { if (style != Neighbor::MULTI) error->all(FLERR,"Cannot use collection/interval command without multi setting"); if (iarg+2 > narg) utils::missing_cmd_args(FLERR, "neigh_modify collection/interval", error); ncollections = utils::inumeric(FLERR,arg[iarg+1],false,lmp); if (ncollections < 1) error->all(FLERR, "Invalid collection/interval keyword: illegal number of custom collections: {}", ncollections); if (iarg+2+ncollections > narg) error->all(FLERR, "Invalid collection/interval keyword: expected {} separate lists of types", ncollections); int i; // Invalidate old user cutoffs comm->ncollections_cutoff = 0; interval_collection_flag = 1; custom_collection_flag = 1; memory->grow(collection2cut,ncollections,"neigh:collection2cut"); // Set upper cutoff for each collection double cut_interval; for (i = 0; i < ncollections; i++){ cut_interval = utils::numeric(FLERR,arg[iarg+2+i],false,lmp); collection2cut[i] = cut_interval; if (i != 0) if (collection2cut[i-1] >= collection2cut[i]) error->all(FLERR,"Nonsequential interval cutoffs in collection/interval setting"); } iarg += 2 + ncollections; } else if (strcmp(arg[iarg],"collection/type") == 0) { if (style != Neighbor::MULTI) error->all(FLERR,"Cannot use collection/type command without multi setting"); if (iarg+2 > narg) utils::missing_cmd_args(FLERR, "neigh_modify collection/type", error); ncollections = utils::inumeric(FLERR,arg[iarg+1],false,lmp); if (ncollections < 1) error->all(FLERR, "Invalid collection/type keyword: illegal number of custom collections: {}", ncollections); if (iarg+2+ncollections > narg) error->all(FLERR, "Invalid collection/type keyword: expected {} separate lists of types", ncollections); int ntypes = atom->ntypes; int nlo, nhi, i, k; // Invalidate old user cutoffs comm->ncollections_cutoff = 0; interval_collection_flag = 0; custom_collection_flag = 1; if (!type2collection) memory->create(type2collection,ntypes+1,"neigh:type2collection"); // Erase previous mapping for (i = 1; i <= ntypes; i++) type2collection[i] = -1; // For each custom range, define mapping for types in interval for (i = 0; i < ncollections; i++){ std::vector words = Tokenizer(arg[iarg+2+i], ",").as_vector(); for (const auto &word : words) { utils::bounds(FLERR,word,1,ntypes,nlo,nhi,error); for (k = nlo; k <= nhi; k++) { if (type2collection[k] != -1) error->all(FLERR,"Type specified more than once in collection/type commnd"); type2collection[k] = i; } } } // Check for undefined atom type for (i = 1; i <= ntypes; i++){ if (type2collection[i] == -1) { error->all(FLERR,"Type missing in collection/type commnd"); } } iarg += 2 + ncollections; } else error->all(FLERR,"Unknown neigh_modify keyword: {}", arg[iarg]); } } /* ---------------------------------------------------------------------- convenience function to allow modifying parameters from a single string ------------------------------------------------------------------------- */ void Neighbor::modify_params(const std::string &modcmd) { auto args = utils::split_words(modcmd); auto newarg = new char*[args.size()]; int i=0; for (const auto &arg : args) { newarg[i++] = (char *)arg.c_str(); } modify_params(args.size(),newarg); delete[] newarg; } /* ---------------------------------------------------------------------- remove the first group-group exclusion matching group1, group2 ------------------------------------------------------------------------- */ void Neighbor::exclusion_group_group_delete(int group1, int group2) { int m, mlast; for (m = 0; m < nex_group; m++) if (ex1_group[m] == group1 && ex2_group[m] == group2 ) break; mlast = m; if (mlast == nex_group) error->all(FLERR,"Unable to find group-group exclusion"); for (m = mlast+1; m < nex_group; m++) { ex1_group[m-1] = ex1_group[m]; ex2_group[m-1] = ex2_group[m]; ex1_bit[m-1] = ex1_bit[m]; ex2_bit[m-1] = ex2_bit[m]; } nex_group--; } /* ---------------------------------------------------------------------- return the value of exclude - used to check compatibility with GPU ------------------------------------------------------------------------- */ int Neighbor::exclude_setting() { return exclude; } /* ---------------------------------------------------------------------- If nonzero, call build_topology from GPU styles instead to overlap comp ------------------------------------------------------------------------- */ void Neighbor::set_overlap_topo(int s) { overlap_topo = s; } /* ---------------------------------------------------------------------- check if any of the old requested neighbor lists are full ------------------------------------------------------------------------- */ int Neighbor::any_full() { int any_full = 0; for (int i = 0; i < old_nrequest; i++) { if (old_requests[i]->full) any_full = 1; } return any_full; } /* ---------------------------------------------------------------------- populate collection array for multi starting at the index istart ------------------------------------------------------------------------- */ void Neighbor::build_collection(int istart) { if (style != Neighbor::MULTI) error->all(FLERR, "Cannot define atom collections without neighbor style multi"); int nmax = atom->nlocal+atom->nghost; if (nmax > nmax_collection) { nmax_collection = nmax+DELTA_PERATOM; memory->grow(collection, nmax_collection, "neigh:collection"); } if (finite_cut_flag) { double cut; int icollection; for (int i = istart; i < nmax; i++){ cut = force->pair->atom2cut(i); collection[i] = -1; for (icollection = 0; icollection < ncollections; icollection++){ if (collection2cut[icollection] >= cut) { collection[i] = icollection; break; } } if (collection[i] == -1) error->one(FLERR, "Atom cutoff exceeds interval cutoffs for multi"); } } else { int *type = atom->type; for (int i = istart; i < nmax; i++){ collection[i] = type2collection[type[i]]; } } } /* ---------------------------------------------------------------------- for neighbor list statistics in Finish class ------------------------------------------------------------------------- */ bigint Neighbor::get_nneigh_full() { // find a non-skip neighbor list containing full pairwise interactions // count neighbors in that list for stats purposes // allow it to be Kokkos neigh list as well int m; for (m = 0; m < old_nrequest; m++) if (old_requests[m]->full && !old_requests[m]->skip) break; bigint nneighfull = -1; if (m < old_nrequest) { nneighfull = 0; if (!lists[m]->kokkos && lists[m]->numneigh) { int inum = neighbor->lists[m]->inum; int *ilist = neighbor->lists[m]->ilist; int *numneigh = neighbor->lists[m]->numneigh; for (int i = 0; i < inum; i++) nneighfull += numneigh[ilist[i]]; } else if (lmp->kokkos) nneighfull = lmp->kokkos->neigh_count(m); } return nneighfull; } bigint Neighbor::get_nneigh_half() { // find a non-skip neighbor list containing half pairwise interactions // count neighbors in that list for stats purposes // allow it to be Kokkos neigh list as well int m; for (m = 0; m < old_nrequest; m++) if (old_requests[m]->half && !old_requests[m]->skip && lists[m] && lists[m]->numneigh) break; bigint nneighhalf = -1; if (m < old_nrequest) { nneighhalf = 0; if (!lists[m]->kokkos) { int inum = neighbor->lists[m]->inum; int *ilist = neighbor->lists[m]->ilist; int *numneigh = neighbor->lists[m]->numneigh; for (int i = 0; i < inum; i++) nneighhalf += numneigh[ilist[i]]; } else if (lmp->kokkos) nneighhalf = lmp->kokkos->neigh_count(m); } return nneighhalf; } /* ---------------------------------------------------------------------- add pair of atoms to bondlist array will only persist until the next neighbor build ------------------------------------------------------------------------- */ void Neighbor::add_temporary_bond(int i1, int i2, int btype) { neigh_bond->add_temporary_bond(i1, i2, btype); } /* ---------------------------------------------------------------------- return # of bytes of allocated memory ------------------------------------------------------------------------- */ double Neighbor::memory_usage() { double bytes = 0; bytes += memory->usage(xhold,maxhold,3); for (int i = 0; i < nlist; i++) if (lists[i]) bytes += lists[i]->memory_usage(); for (int i = 0; i < nstencil; i++) bytes += neigh_stencil[i]->memory_usage(); for (int i = 0; i < nbin; i++) bytes += neigh_bin[i]->memory_usage(); if (neigh_bond) bytes += neigh_bond->memory_usage(); if (neigh_angle) bytes += neigh_angle->memory_usage(); if (neigh_dihedral) bytes += neigh_dihedral->memory_usage(); if (neigh_improper) bytes += neigh_improper->memory_usage(); return bytes; }