/* ----------------------------------------------------------------------
    This is the

    ██╗     ██╗ ██████╗  ██████╗  ██████╗ ██╗  ██╗████████╗███████╗
    ██║     ██║██╔════╝ ██╔════╝ ██╔════╝ ██║  ██║╚══██╔══╝██╔════╝
    ██║     ██║██║  ███╗██║  ███╗██║  ███╗███████║   ██║   ███████╗
    ██║     ██║██║   ██║██║   ██║██║   ██║██╔══██║   ██║   ╚════██║
    ███████╗██║╚██████╔╝╚██████╔╝╚██████╔╝██║  ██║   ██║   ███████║
    ╚══════╝╚═╝ ╚═════╝  ╚═════╝  ╚═════╝ ╚═╝  ╚═╝   ╚═╝   ╚══════╝®

    DEM simulation engine, released by
    DCS Computing Gmbh, Linz, Austria
    http://www.dcs-computing.com, office@dcs-computing.com

    LIGGGHTS® is part of CFDEM®project:
    http://www.liggghts.com | http://www.cfdem.com

    Core developer and main author:
    Christoph Kloss, christoph.kloss@dcs-computing.com

    LIGGGHTS® is open-source, distributed under the terms of the GNU Public
    License, version 2 or later. It is distributed in the hope that it will
    be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
    of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. You should have
    received a copy of the GNU General Public License along with LIGGGHTS®.
    If not, see http://www.gnu.org/licenses . See also top-level README
    and LICENSE files.

    LIGGGHTS® and CFDEM® are registered trade marks of DCS Computing GmbH,
    the producer of the LIGGGHTS® software and the CFDEM®coupling software
    See http://www.cfdem.com/terms-trademark-policy for details.

-------------------------------------------------------------------------
    Contributing author and copyright for this file:
    This file is from LAMMPS, but has been modified. Copyright for
    modification:

    Copyright 2012-     DCS Computing GmbH, Linz
    Copyright 2009-2012 JKU Linz

    Copyright of original file:
    LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator
    http://lammps.sandia.gov, Sandia National Laboratories
    Steve Plimpton, sjplimp@sandia.gov

    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.
------------------------------------------------------------------------- */

/* ----------------------------------------------------------------------
   Contributing author (triclinic and multi-neigh) : Pieter in 't Veld (SNL)
------------------------------------------------------------------------- */

#include "lmptype.h"
#include <mpi.h>
#include <cmath>
#include <stdlib.h>
#include <string.h>
#include "neighbor.h"
#include "neigh_list.h"
#include "neigh_request.h"
#include "atom.h"
#include "atom_vec.h"
#include "comm.h"
#include "force.h"
#include "pair.h"
#include "domain.h"
#include "group.h"
#include "modify.h"
#include "fix.h"
#include "compute.h"
#include "update.h"
#include "respa.h"
#include "output.h"
#include "citeme.h"
#include "memory.h"
#include "error.h"
#include "neigh_multi_level_grid.h" 
#include "math_extra_liggghts.h"    
#include "fix_contact_history.h"
#include <assert.h>

using namespace LAMMPS_NS;

#define RQDELTA 1
#define EXDELTA 1

#define LB_FACTOR 1.5
#define SMALL 1.0e-6
#define BIG 1.0e20
#define CUT2BIN_RATIO 100

enum{NSQ,BIN,MULTI};     // also in neigh_list.cpp

static const char cite_neigh_multi[] =
  "neighbor multi command:\n\n"
  "@Article{Intveld08,\n"
  " author =  {P.{\\,}J.~in{\\,}'t~Veld 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 = {Comp.~Phys.~Comm.},\n"
  " year =    2008,\n"
  " volume =  179,\n"
  " pages =   {320--329}\n"
  "}\n\n";

//#define NEIGH_LIST_DEBUG 1

/* ---------------------------------------------------------------------- */

Neighbor::Neighbor(LAMMPS *lmp) : Pointers(lmp)
{
  MPI_Comm_rank(world,&me);
  MPI_Comm_size(world,&nprocs);

  style = BIN;
  every = 1;
  delay = 10;
  contactDistanceFactor = 1.0; 
  dist_check = 1;
  pgsize = 100000;
  oneatom = 2000;
  binsizeflag = 0;
  build_once = 0;
  cluster_check = 0;

  cutneighmax = 0;
  cutneighsq = NULL;
  cutneighghostsq = NULL;
  cuttype = NULL;
  cuttypesq = NULL;
  fixchecklist = NULL;

  // coords at last neighboring

  maxhold = 0;
  xhold = NULL;
  rhold = NULL; 

  // binning

  maxhead = 0;
  binhead = NULL;
  maxbin = 0;
  bins = NULL;
  mlg = NULL; 

  ago = -1;

  // pair exclusion list info

  includegroup = 0;

  nex_type = maxex_type = 0;
  ex1_type = ex2_type = NULL;
  ex_type = NULL;

  nex_group = maxex_group = 0;
  ex1_group = ex2_group = ex1_bit = ex2_bit = NULL;

  nex_mol = maxex_mol = 0;
  ex_mol_group = ex_mol_bit = NULL;

  // pair lists

  maxatom = 0;
  nblist = nglist = nslist = 0;

  nlist = 0;
  lists = NULL;
  pair_build = NULL;
  stencil_create = NULL;
  blist = glist = slist = NULL;
  anyghostlist = 0;

  nrequest = maxrequest = 0;
  requests = NULL;

  old_style = BIN;
  old_triclinic = 0;
  old_pgsize = pgsize;
  old_oneatom = oneatom;
  old_nrequest = 0;
  old_requests = NULL;

  // bond lists

  maxbond = 0;
  bondlist = NULL;
  bondhistlist = NULL; 
  maxangle = 0;
  anglelist = NULL;
  maxdihedral = 0;
  dihedrallist = NULL;
  maximproper = 0;
  improperlist = NULL;

  last_setup_bins_timestep = 0;
}

/* ---------------------------------------------------------------------- */

Neighbor::~Neighbor()
{
  memory->destroy(cutneighsq);
  memory->destroy(cutneighghostsq);
  delete [] cuttype;
  delete [] cuttypesq;
  delete [] fixchecklist;

  memory->destroy(xhold);
  memory->destroy(rhold); 

  memory->destroy(binhead);
  memory->destroy(bins);

  if(mlg) delete mlg; 

  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;

  for (int i = 0; i < nlist; i++) delete lists[i];
  delete [] lists;
  delete [] pair_build;
  delete [] stencil_create;
  delete [] blist;
  delete [] glist;
  delete [] slist;

  for (int i = 0; i < nrequest; i++) delete requests[i];
  memory->sfree(requests);
  for (int i = 0; i < old_nrequest; i++) delete old_requests[i];
  memory->sfree(old_requests);

  memory->destroy(bondlist);
  memory->destroy(bondhistlist); 
  memory->destroy(anglelist);
  memory->destroy(dihedrallist);
  memory->destroy(improperlist);
}

/* ---------------------------------------------------------------------- */

void Neighbor::init()
{
  int i,j,m,n;

  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 = 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 = NULL 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 == NULL) {
    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;

  if(atom->radius_flag) {
    double maxrd,minrd;
    modify->max_min_rad(maxrd,minrd);
    cutneighmin = MIN(cutneighmin,2*minrd+skin);
  }

  // check other classes that can induce reneighboring in decide()
  // don't check if build_once is set

  delete [] fixchecklist;
  fixchecklist = NULL;
  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;

  // always check whether we need to do reneighboring because we might have
  // a sudden restart request from the signal handler
  must_check = 1;
  if (build_once) must_check = 0;

  // 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
  // pairwise portion of KSpace solver uses all 1-2,1-3,1-4 neighbors

  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;

  if (force->kspace) special_flag[1] = special_flag[2] = special_flag[3] = 2;

  // maxwt = max multiplicative factor on atom indices stored in neigh list

  maxwt = 0;
  if (special_flag[1] == 2) maxwt = 2;
  if (special_flag[2] == 2) maxwt = 3;
  if (special_flag[3] == 2) maxwt = 4;

  // rRESPA cutoffs

  int respa = 0;
  if (update->whichflag == 1 && strstr(update->integrate_style,"respa")) {
    if (((Respa *) update->integrate)->level_inner >= 0) respa = 1;
    if (((Respa *) update->integrate)->level_middle >= 0) respa = 2;
  }

  if (respa) {
    double *cut_respa = ((Respa *) 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;
  }

  // ------------------------------------------------------------------
  // xhold, bins, exclusion lists

  // free xhold and bins if not needed for this run

  if (dist_check == 0) {
    memory->destroy(xhold);
    memory->destroy(rhold); 
    maxhold = 0;
    xhold = NULL;
    rhold = NULL; 
  }

  if (style == NSQ) {
    memory->destroy(bins);
    memory->destroy(binhead);
    maxbin = maxhead = 0;
    binhead = NULL;
    bins = NULL;
  }

  // 1st time allocation of xhold and bins

  if (dist_check) {
    if (maxhold == 0) {
      maxhold = atom->nmax;
      memory->create(xhold,maxhold,3,"neigh:xhold");
      memory->create(rhold,maxhold,"neigh:rhold"); 
    }
  }

  if (style != NSQ) {
    if (maxbin == 0) {
      maxbin = atom->nmax;
      memory->create(bins,maxbin,"bins");
    }
  }

  // exclusion lists for 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) {
    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) {
    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) {
    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");

  // ------------------------------------------------------------------
  // pairwise lists

  // 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
  // first archive request params for current requests
  //   before Neighbor possibly changes them below

  for (i = 0; i < nrequest; i++) requests[i]->archive();

  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;
        
      }
      
      //if (requests[i]->gran || requests[i]->granhistory) same = 0; 
    }

#ifdef NEIGH_LIST_DEBUG
  if (comm->me == 0) printf("SAME flag %d\n",same);
#endif

  // if old and new are not the same, create new pairwise lists

  if (!same) {

    // delete old lists and create new ones

    for (i = 0; i < nlist; i++) delete lists[i];
    delete [] lists;
    delete [] pair_build;
    delete [] stencil_create;

    nlist = nrequest;
    lists = new NeighList*[nlist];
    pair_build = new PairPtr[nlist];
    stencil_create = new StencilPtr[nlist];

    // create individual lists, one per request
    // pass list ptr back to requestor (except for Command class)
    // wait to allocate initial pages until copy lists are detected

    for (i = 0; i < nlist; i++) {
      lists[i] = new NeighList(lmp);
      lists[i]->index = i;
      
      if (requests[i]->pair) {
        Pair *pair = (Pair *) requests[i]->requestor;
        pair->init_list(requests[i]->id,lists[i]);
      } else if (requests[i]->fix) {
        Fix *fix = (Fix *) requests[i]->requestor;
        fix->init_list(requests[i]->id,lists[i]);
      } else if (requests[i]->compute) {
        Compute *compute = (Compute *) requests[i]->requestor;
        compute->init_list(requests[i]->id,lists[i]);
      }
    }

    // detect lists that are connected to other lists
    // if-then-else sequence and processed flag is important
    //   since don't want to re-process skip or copy lists further down
    //   skip list needs to have granhistory or respa info added
    // copy: point this list at request->otherlist, could be a skip list
    // skip: point this list at request->otherlist, copy skip info from request
    //   also set granular and respa info if applicable
    // half_from_full: point this list at preceeding full list
    // granhistory: set preceeding list's listgranhistory to this list
    //   also set preceeding list's ptr to FixShearHistory
    // respaouter: point this list at preceeding 1/2 inner/middle lists
    // pair and half: if there is a full non-occasional non-skip list
    //   change this list to half_from_full and point at the full list
    //   parent could be copy list or pair or fix
    // fix/compute requests:
    //   whether request is occasional or not doesn't matter
    //   if request = half and non-skip pair half/respaouter exists,
    //     become copy of that list if cudable flag matches
    //   if request = full and non-skip pair full exists,
    //     become copy of that list if cudable flag matches
    //   if request = half and non-skip pair full exists,
    //     become half_from_full of that list if cudable flag matches
    //   if no matches, do nothing, fix/compute list will be built directly
    //   ok if parent is copy list

    int processed;

    for (i = 0; i < nlist; i++) {
      processed = 0;

      if (requests[i]->copy) {
        lists[i]->listcopy = lists[requests[i]->otherlist];
        processed = 1;

      } else if (requests[i]->skip) {
        lists[i]->listskip = lists[requests[i]->otherlist];
        lists[i]->copy_skip_info(requests[i]->iskip,requests[i]->ijskip);
        processed = 1;

      } else if (requests[i]->half_from_full) {
        lists[i]->listfull = lists[i-1];
        processed = 1;
      }

      if (requests[i]->granhistory) {
        lists[i-1]->listgranhistory = lists[i];
        for (int ifix = 0; ifix < modify->nfix; ifix++){
          if (strcmp(modify->fix[ifix]->style,"contacthistory") == 0) {  
            lists[i-1]->fix_history = dynamic_cast<FixContactHistory*>(modify->fix[ifix]); 
            assert(lists[i-1]->fix_history != NULL);
          }
        }
        processed = 1;
      } else if (requests[i]->respaouter) {
        if (requests[i-1]->respainner) {
          lists[i]->respamiddle = 0;
          lists[i]->listinner = lists[i-1];
        } else {
          lists[i]->respamiddle = 1;
          lists[i]->listmiddle = lists[i-1];
          lists[i]->listinner = lists[i-2];
        }
        processed = 1;
      }

      if (processed) continue;

      if (requests[i]->pair && requests[i]->half) {
        for (j = 0; j < nlist; j++)
          if (requests[j]->full && requests[j]->occasional == 0 &&
              requests[j]->skip == 0) break;
        if (j < nlist) {
          requests[i]->half = 0;
          requests[i]->half_from_full = 1;
          lists[i]->listfull = lists[j];
        }

      } else if (requests[i]->fix || requests[i]->compute) {
        for (j = 0; j < nlist; j++) {
          if (requests[i]->half && requests[j]->pair &&
              requests[j]->skip == 0 && requests[j]->half) break;
          if (requests[i]->full && requests[j]->pair &&
              requests[j]->skip == 0 && requests[j]->full) break;
          if (requests[i]->gran && requests[j]->pair &&
              requests[j]->skip == 0 && requests[j]->gran) break;
          if (requests[i]->half && requests[j]->pair &&
              requests[j]->skip == 0 && requests[j]->respaouter) break;
        }
        if (j < nlist && requests[j]->cudable != requests[i]->cudable)
          j = nlist;
        if (j < nlist) {
          requests[i]->copy = 1;
          requests[i]->otherlist = j;
          lists[i]->listcopy = lists[j];
        } else {
          for (j = 0; j < nlist; j++) {
            if (requests[i]->half && requests[j]->pair &&
                requests[j]->skip == 0 && requests[j]->full) break;
          }
          if (j < nlist && requests[j]->cudable != requests[i]->cudable)
            j = nlist;
          if (j < nlist) {
            requests[i]->half = 0;
            requests[i]->half_from_full = 1;
            lists[i]->listfull = lists[j];
          }
        }
      }
    }

    // allocate initial pages for each list, except if listcopy set

    for (i = 0; i < nlist; i++)
      if (!lists[i]->listcopy)
        lists[i]->setup_pages(pgsize,oneatom,requests[i]->dnum);

    // set ptrs to pair_build and stencil_create functions for each list
    // ptrs set to NULL if not set explicitly
    // also set cudable to 0 if any neigh list request is not cudable

    for (i = 0; i < nlist; i++) {
      choose_build(i,requests[i]);
      if (style != NSQ) choose_stencil(i,requests[i]);
      else stencil_create[i] = NULL;
      if (!requests[i]->cudable) cudable = 0;
    }

    // set each list's build/grow/stencil/ghost flags based on neigh request
    // buildflag = 1 if its pair_build() invoked every reneighbor
    // growflag = 1 if it stores atom-based arrays and pages
    // stencilflag = 1 if it stores stencil arrays
    // ghostflag = 1 if it stores neighbors of ghosts
    // anyghostlist = 1 if any non-occasional list stores neighbors of ghosts

    anyghostlist = 0;
    for (i = 0; i < nlist; i++) {
      lists[i]->buildflag = 1;
      if (pair_build[i] == NULL) lists[i]->buildflag = 0;
      if (requests[i]->occasional) lists[i]->buildflag = 0;

      lists[i]->growflag = 1;
      if (requests[i]->copy) lists[i]->growflag = 0;

      lists[i]->stencilflag = 1;
      if (style == NSQ) lists[i]->stencilflag = 0;
      if (stencil_create[i] == NULL) lists[i]->stencilflag = 0;

      lists[i]->ghostflag = 0;
      if (requests[i]->ghost) lists[i]->ghostflag = 1;
      if (requests[i]->ghost && !requests[i]->occasional) anyghostlist = 1;
    }

#ifdef NEIGH_LIST_DEBUG
    for (i = 0; i < nlist; i++) lists[i]->print_attributes();
#endif

    // allocate atom arrays for neighbor lists that store them

    maxatom = atom->nmax;
    for (i = 0; i < nlist; i++)
      if (lists[i]->growflag) lists[i]->grow(maxatom);

    // setup 3 vectors of pairwise neighbor lists
    // blist = lists whose pair_build() is invoked every reneighbor
    // glist = lists who store atom arrays which are used every reneighbor
    // slist = lists who store stencil arrays which are used every reneighbor
    // blist and glist vectors are used by neighbor::build()
    // slist vector is used by neighbor::setup_bins()

    nblist = nglist = nslist = 0;
    delete [] blist;
    delete [] glist;
    delete [] slist;
    blist = new int[nlist];
    glist = new int[nlist];
    slist = new int[nlist];

    for (i = 0; i < nlist; i++) {
      if (lists[i]->buildflag) blist[nblist++] = i;
      if (lists[i]->growflag && requests[i]->occasional == 0)
        glist[nglist++] = i;
      if (lists[i]->stencilflag && requests[i]->occasional == 0)
        slist[nslist++] = i;
    }

#ifdef NEIGH_LIST_DEBUG
    print_lists_of_lists();
#endif

    // reorder build vector if necessary
    // relevant for lists that copy/skip/half-full from parent
    // the derived list must appear in blist after the parent list
    // no occasional lists are in build vector
    // swap two lists within blist when dependency is mis-ordered
    // done when entire pass thru blist results in no swaps

    int done = 0;
    while (!done) {
      done = 1;
      for (i = 0; i < nblist; i++) {
        NeighList *ptr = NULL;
        if (lists[blist[i]]->listfull) ptr = lists[blist[i]]->listfull;
        if (lists[blist[i]]->listcopy) ptr = lists[blist[i]]->listcopy;
        if (lists[blist[i]]->listskip) ptr = lists[blist[i]]->listskip;
        if (ptr == NULL) continue;
        for (m = 0; m < nlist; m++)
          if (ptr == lists[m]) break;
        for (j = 0; j < nblist; j++)
          if (m == blist[j]) break;
        if (j < i) continue;
        int tmp = blist[i];
        blist[i] = blist[j];
        blist[j] = tmp;
        done = 0;
        break;
      }
    }

#ifdef NEIGH_LIST_DEBUG
    print_lists_of_lists();
#endif
  } else {
    for (i = 0; i < nlist; i++) {
      if (requests[i]->pair) {
        Pair *pair = (Pair *) requests[i]->requestor;
        pair->init_list(requests[i]->id,lists[i]);
      } else if (requests[i]->fix) {
        Fix *fix = (Fix *) requests[i]->requestor;
        fix->init_list(requests[i]->id,lists[i]);
      } else if (requests[i]->compute) {
        Compute *compute = (Compute *) requests[i]->requestor;
        compute->init_list(requests[i]->id,lists[i]);
      }

      // update contacthistory pointer
      if (requests[i]->granhistory) {
        for (int ifix = 0; ifix < modify->nfix; ifix++) {
          if (strcmp(modify->fix[ifix]->style,"contacthistory") == 0) {  
            lists[i-1]->fix_history = dynamic_cast<FixContactHistory*>(modify->fix[ifix]); 
            assert(lists[i-1]->fix_history != NULL);
          }
        }
      }
    }
  }

  // delete old requests
  // copy current requests and style to old for next run

  for (i = 0; i < old_nrequest; i++) delete old_requests[i];
  memory->sfree(old_requests);
  old_nrequest = nrequest;
  old_requests = requests;
  nrequest = maxrequest = 0;
  requests = NULL;
  old_style = style;
  old_triclinic = triclinic;

  // ------------------------------------------------------------------
  // topology lists

  // 1st time allocation of topology lists

  if (atom->molecular && atom->nbonds && maxbond == 0) {
    if (nprocs == 1) maxbond = atom->nbonds;
    else maxbond = static_cast<int> (LB_FACTOR * atom->nbonds / nprocs);
    memory->create(bondlist,maxbond,4,"neigh:bondlist");   
    
    if(atom->n_bondhist)
       memory->create(bondhistlist,maxbond,atom->n_bondhist,"neigh:bondhistlist");
  }

  if (atom->molecular && atom->nangles && maxangle == 0) {
    if (nprocs == 1) maxangle = atom->nangles;
    else maxangle = static_cast<int> (LB_FACTOR * atom->nangles / nprocs);
    memory->create(anglelist,maxangle,4,"neigh:anglelist");
  }

  if (atom->molecular && atom->ndihedrals && maxdihedral == 0) {
    if (nprocs == 1) maxdihedral = atom->ndihedrals;
    else maxdihedral = static_cast<int>
           (LB_FACTOR * atom->ndihedrals / nprocs);
    memory->create(dihedrallist,maxdihedral,5,"neigh:dihedrallist");
  }

  if (atom->molecular && atom->nimpropers && maximproper == 0) {
    if (nprocs == 1) maximproper = atom->nimpropers;
    else maximproper = static_cast<int>
           (LB_FACTOR * atom->nimpropers / nprocs);
    memory->create(improperlist,maximproper,5,"neigh:improperlist");
  }

  // set flags that determine which topology neighboring routines to use
  // SHAKE sets bonds and angles negative
  // bond_quartic 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 (strcmp(modify->fix[i]->style,"shake") == 0)
      bond_off = angle_off = 1;
  if (force->bond && force->bond_match("quartic")) bond_off = 1;

  if (atom->avec->bonds_allow) {
    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) {
    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) {
    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) {
    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;
    }
  }

  // sync on/off settings across all procs

  int on_or_off = bond_off;
  MPI_Allreduce(&on_or_off,&bond_off,1,MPI_INT,MPI_MAX,world);
  on_or_off = angle_off;
  MPI_Allreduce(&on_or_off,&angle_off,1,MPI_INT,MPI_MAX,world);
  on_or_off = dihedral_off;
  MPI_Allreduce(&on_or_off,&dihedral_off,1,MPI_INT,MPI_MAX,world);
  on_or_off = improper_off;
  MPI_Allreduce(&on_or_off,&improper_off,1,MPI_INT,MPI_MAX,world);

  // set ptrs to topology build functions

  if (bond_off) bond_build = &Neighbor::bond_partial;
  else bond_build = &Neighbor::bond_all;

  if (angle_off) angle_build = &Neighbor::angle_partial;
  else angle_build = &Neighbor::angle_all;

  if (dihedral_off) dihedral_build = &Neighbor::dihedral_partial;
  else dihedral_build = &Neighbor::dihedral_all;

  if (improper_off) improper_build = &Neighbor::improper_partial;
  else improper_build = &Neighbor::improper_all;

  // set topology neighbor list counts to 0
  // in case all are turned off but potential is still defined

  nbondlist = nanglelist = ndihedrallist = nimproperlist = 0;
}

/* ---------------------------------------------------------------------- */

int Neighbor::request(void *requestor)
{
  if (nrequest == maxrequest) {
    maxrequest += RQDELTA;
    requests = (NeighRequest **)
      memory->srealloc(requests,maxrequest*sizeof(NeighRequest *),
                       "neighbor:requests");
  }

  requests[nrequest] = new NeighRequest(lmp);
  requests[nrequest]->requestor = requestor;
  nrequest++;
  return nrequest-1;
}

/* ----------------------------------------------------------------------
   determine which pair_build function each neigh list needs
   based on settings of neigh request
   copy -> copy_from function
   skip -> granular function if gran with granhistory,
           respa function if respaouter,
           skip_from function for everything else
   half_from_full, half, full, gran, respaouter ->
     choose by newton and rq->newton and tri settings
     style NSQ options = newton off, newton on
     style BIN options = newton off, newton on and not tri, newton on and tri
     stlye MULTI options = same options as BIN
   if none of these, ptr = NULL since pair_build is not invoked for this list
   use "else if" b/c skip,copy can be set in addition to half,full,etc
------------------------------------------------------------------------- */

void Neighbor::choose_build(int index, NeighRequest *rq)
{
  PairPtr pb = NULL;

  if (rq->omp == 0) {

    if (rq->copy) pb = &Neighbor::copy_from;

    else if (rq->skip) {
      if (rq->gran && lists[index]->listgranhistory)
        pb = &Neighbor::skip_from_granular;
      else if (rq->respaouter) pb = &Neighbor::skip_from_respa;
      else pb = &Neighbor::skip_from;

    } else if (rq->half_from_full) {
      if (newton_pair == 0) pb = &Neighbor::half_from_full_no_newton;
      else if (newton_pair == 1) pb = &Neighbor::half_from_full_newton;

    } else if (rq->half) {
      if (style == NSQ) {
        if (rq->newton == 0) {
          if (newton_pair == 0) {
            if (rq->ghost == 0) pb = &Neighbor::half_nsq_no_newton;
            else if (includegroup)
              error->all(FLERR,"Neighbor include group not allowed "
                         "with ghost neighbors");
            else pb = &Neighbor::half_nsq_no_newton_ghost;
          } else if (newton_pair == 1) pb = &Neighbor::half_nsq_newton;
        } else if (rq->newton == 1) {
          pb = &Neighbor::half_nsq_newton;
        } else if (rq->newton == 2) {
          if (rq->ghost == 0) pb = &Neighbor::half_nsq_no_newton;
          else if (includegroup)
            error->all(FLERR,"Neighbor include group not allowed "
                       "with ghost neighbors");
          else pb = &Neighbor::half_nsq_no_newton_ghost;
        }
      } else if (style == BIN) {
        if (rq->newton == 0) {
          if (newton_pair == 0) {
            if (rq->ghost == 0) pb = &Neighbor::half_bin_no_newton;
            else if (includegroup)
              error->all(FLERR,"Neighbor include group not allowed "
                         "with ghost neighbors");
            else pb = &Neighbor::half_bin_no_newton_ghost;
          } else if (triclinic == 0) {
            pb = &Neighbor::half_bin_newton;
          } else if (triclinic == 1)
            pb = &Neighbor::half_bin_newton_tri;
        } else if (rq->newton == 1) {
          if (triclinic == 0) pb = &Neighbor::half_bin_newton;
          else if (triclinic == 1) pb = &Neighbor::half_bin_newton_tri;
        } else if (rq->newton == 2) {
          if (rq->ghost == 0) pb = &Neighbor::half_bin_no_newton;
          else if (includegroup)
            error->all(FLERR,"Neighbor include group not allowed "
                       "with ghost neighbors");
          else pb = &Neighbor::half_bin_no_newton_ghost;
        }
      } else if (style == MULTI) {
        if (rq->ghost == 1)
          error->all(FLERR,
                     "Neighbor multi not yet enabled for ghost neighbors");
        if (rq->newton == 0) {
          if (newton_pair == 0) pb = &Neighbor::half_multi_no_newton;
          else if (triclinic == 0) pb = &Neighbor::half_multi_newton;
          else if (triclinic == 1) pb = &Neighbor::half_multi_newton_tri;
        } else if (rq->newton == 1) {
          if (triclinic == 0) pb = &Neighbor::half_multi_newton;
          else if (triclinic == 1) pb = &Neighbor::half_multi_newton_tri;
        } else if (rq->newton == 2) pb = &Neighbor::half_multi_no_newton;
      }

    } else if (rq->full) {
      if (style == NSQ) {
        if (rq->ghost == 0) pb = &Neighbor::full_nsq;
        else if (includegroup)
          error->all(FLERR,
                     "Neighbor include group not allowed with ghost neighbors");
        else pb = &Neighbor::full_nsq_ghost;
      } else if (style == BIN) {
        if (rq->ghost == 0) pb = &Neighbor::full_bin;
        else if (includegroup)
          error->all(FLERR,
                     "Neighbor include group not allowed with ghost neighbors");
        else pb = &Neighbor::full_bin_ghost;
      } else if (style == MULTI) {
        if (rq->ghost == 1)
          error->all(FLERR,
                        "Neighbor multi not yet enabled for ghost neighbors");
        pb = &Neighbor::full_multi;
      }

    } else if (rq->gran) {
      if (style == NSQ) {
        if (newton_pair == 0) pb = &Neighbor::granular_nsq_no_newton;
        else if (newton_pair == 1) pb = &Neighbor::granular_nsq_newton;
      } else if (style == BIN) {
        if (newton_pair == 0) pb = &Neighbor::granular_bin_no_newton;
        else if (triclinic == 0) pb = &Neighbor::granular_bin_newton;
        else if (triclinic == 1) pb = &Neighbor::granular_bin_newton_tri;
      } else if (style == MULTI) { 
        if (0) {}
        else if (1 == triclinic) error->all(FLERR,"Neighbor multi not yet enabled for granular with triclinic");
        else if (1 == newton_pair) error->all(FLERR,"Neighbor multi not yet enabled for granular with newton on");
        else if (0 == newton_pair) pb = &Neighbor::granular_multi_no_newton;
        else error->all(FLERR,"Neighbor multi not yet enabled for granular");
      }

    } else if (rq->respaouter) {
      if (style == NSQ) {
        if (newton_pair == 0) pb = &Neighbor::respa_nsq_no_newton;
        else if (newton_pair == 1) pb = &Neighbor::respa_nsq_newton;
      } else if (style == BIN) {
        if (newton_pair == 0) pb = &Neighbor::respa_bin_no_newton;
        else if (triclinic == 0) pb = &Neighbor::respa_bin_newton;
        else if (triclinic == 1) pb = &Neighbor::respa_bin_newton_tri;
      } else if (style == MULTI)
        error->all(FLERR,"Neighbor multi not yet enabled for rRESPA");
    }

  // OMP versions of build methods

  } else {

    if (rq->copy) pb = &Neighbor::copy_from;

    else if (rq->skip) {
      if (rq->gran && lists[index]->listgranhistory)
        pb = &Neighbor::skip_from_granular;
      else if (rq->respaouter) pb = &Neighbor::skip_from_respa;
      else pb = &Neighbor::skip_from;

    } else if (rq->half_from_full) {
      if (newton_pair == 0) pb = &Neighbor::half_from_full_no_newton_omp;
      else if (newton_pair == 1) pb = &Neighbor::half_from_full_newton_omp;

    } else if (rq->half) {
      if (style == NSQ) {
        if (rq->newton == 0) {
          if (newton_pair == 0) {
            if (rq->ghost == 0) pb = &Neighbor::half_nsq_no_newton_omp;
            else if (includegroup)
              error->all(FLERR,"Neighbor include group not allowed "
                         "with ghost neighbors");
            else pb = &Neighbor::half_nsq_no_newton_ghost_omp;
          } else if (newton_pair == 1) pb = &Neighbor::half_nsq_newton_omp;
        } else if (rq->newton == 1) {
          pb = &Neighbor::half_nsq_newton_omp;
        } else if (rq->newton == 2) {
          if (rq->ghost == 0) pb = &Neighbor::half_nsq_no_newton_omp;
          else if (includegroup)
            error->all(FLERR,"Neighbor include group not allowed "
                       "with ghost neighbors");
          else pb = &Neighbor::half_nsq_no_newton_ghost_omp;
        }
      } else if (style == BIN) {
        if (rq->newton == 0) {
          if (newton_pair == 0) {
            if (rq->ghost == 0) pb = &Neighbor::half_bin_no_newton_omp;
            else if (includegroup)
              error->all(FLERR,"Neighbor include group not allowed "
                         "with ghost neighbors");
            else pb = &Neighbor::half_bin_no_newton_ghost_omp;
          } else if (triclinic == 0) {
            pb = &Neighbor::half_bin_newton_omp;
          } else if (triclinic == 1)
            pb = &Neighbor::half_bin_newton_tri_omp;
        } else if (rq->newton == 1) {
          if (triclinic == 0) pb = &Neighbor::half_bin_newton_omp;
          else if (triclinic == 1) pb = &Neighbor::half_bin_newton_tri_omp;
        } else if (rq->newton == 2) {
          if (rq->ghost == 0) pb = &Neighbor::half_bin_no_newton_omp;
          else if (includegroup)
            error->all(FLERR,"Neighbor include group not allowed "
                       "with ghost neighbors");
          else pb = &Neighbor::half_bin_no_newton_ghost_omp;
        }
      } else if (style == MULTI) {
        if (rq->ghost == 1)
          error->all(FLERR,
                     "Neighbor multi not yet enabled for ghost neighbors");
        if (rq->newton == 0) {
          if (newton_pair == 0) pb = &Neighbor::half_multi_no_newton_omp;
          else if (triclinic == 0) pb = &Neighbor::half_multi_newton_omp;
          else if (triclinic == 1) pb = &Neighbor::half_multi_newton_tri_omp;
        } else if (rq->newton == 1) {
          if (triclinic == 0) pb = &Neighbor::half_multi_newton_omp;
          else if (triclinic == 1) pb = &Neighbor::half_multi_newton_tri_omp;
        } else if (rq->newton == 2) pb = &Neighbor::half_multi_no_newton_omp;
      }

    } else if (rq->full) {
      if (style == NSQ) {
        if (rq->ghost == 0) pb = &Neighbor::full_nsq_omp;
        else if (includegroup)
          error->all(FLERR,
                     "Neighbor include group not allowed with ghost neighbors");
        else pb = &Neighbor::full_nsq_ghost_omp;
      } else if (style == BIN) {
        if (rq->ghost == 0) pb = &Neighbor::full_bin_omp;
        else if (includegroup)
          error->all(FLERR,
                     "Neighbor include group not allowed with ghost neighbors");
        else pb = &Neighbor::full_bin_ghost_omp;
      } else if (style == MULTI) {
        if (rq->ghost == 1)
          error->all(FLERR,
                        "Neighbor multi not yet enabled for ghost neighbors");
        pb = &Neighbor::full_multi_omp;
      }

    } else if (rq->gran) {
      if (style == NSQ) {
        if (newton_pair == 0) pb = &Neighbor::granular_nsq_no_newton_omp;
        else if (newton_pair == 1) pb = &Neighbor::granular_nsq_newton_omp;
      } else if (style == BIN) {
        if (newton_pair == 0) pb = &Neighbor::granular_bin_no_newton_omp;
        else if (triclinic == 0) pb = &Neighbor::granular_bin_newton_omp;
        else if (triclinic == 1) pb = &Neighbor::granular_bin_newton_tri_omp;
      } else if (style == MULTI)
        error->all(FLERR,"Neighbor multi not yet enabled for granular");

    } else if (rq->respaouter) {
      if (style == NSQ) {
        if (newton_pair == 0) pb = &Neighbor::respa_nsq_no_newton_omp;
        else if (newton_pair == 1) pb = &Neighbor::respa_nsq_newton_omp;
      } else if (style == BIN) {
        if (newton_pair == 0) pb = &Neighbor::respa_bin_no_newton_omp;
        else if (triclinic == 0) pb = &Neighbor::respa_bin_newton_omp;
        else if (triclinic == 1) pb = &Neighbor::respa_bin_newton_tri_omp;
      } else if (style == MULTI)
        error->all(FLERR,"Neighbor multi not yet enabled for rRESPA");
    }
  }

  pair_build[index] = pb;
}

/* ----------------------------------------------------------------------
   determine which stencil_create function each neigh list needs
   based on settings of neigh request, only called if style != NSQ
   skip or copy or half_from_full -> no stencil
   half, gran, respaouter, full -> choose by newton and tri and dimension
   if none of these, ptr = NULL since this list needs no stencils
   use "else if" b/c skip,copy can be set in addition to half,full,etc
------------------------------------------------------------------------- */

void Neighbor::choose_stencil(int index, NeighRequest *rq)
{
  StencilPtr sc = NULL;

  if (rq->skip || rq->copy || rq->half_from_full) sc = NULL;

  else if (rq->half || rq->gran || rq->respaouter) {
    if (style == BIN) {
      if (rq->newton == 0) {
        if (newton_pair == 0) {
          if (dimension == 2) {
            if (rq->ghost) sc = &Neighbor::stencil_half_ghost_bin_2d_no_newton;
            else sc = &Neighbor::stencil_half_bin_2d_no_newton;
          } else if (dimension == 3) {
            if (rq->ghost) sc = &Neighbor::stencil_half_ghost_bin_3d_no_newton;
            else sc = &Neighbor::stencil_half_bin_3d_no_newton;
          }
        } else if (triclinic == 0) {
          if (dimension == 2)
            sc = &Neighbor::stencil_half_bin_2d_newton;
          else if (dimension == 3)
            sc = &Neighbor::stencil_half_bin_3d_newton;
        } else if (triclinic == 1) {
          if (dimension == 2)
            sc = &Neighbor::stencil_half_bin_2d_newton_tri;
          else if (dimension == 3)
            sc = &Neighbor::stencil_half_bin_3d_newton_tri;
        }
      } else if (rq->newton == 1) {
        if (triclinic == 0) {
          if (dimension == 2)
            sc = &Neighbor::stencil_half_bin_2d_newton;
          else if (dimension == 3)
            sc = &Neighbor::stencil_half_bin_3d_newton;
        } else if (triclinic == 1) {
          if (dimension == 2)
            sc = &Neighbor::stencil_half_bin_2d_newton_tri;
          else if (dimension == 3)
            sc = &Neighbor::stencil_half_bin_3d_newton_tri;
        }
      } else if (rq->newton == 2) {
        if (dimension == 2)
          if (rq->ghost) sc = &Neighbor::stencil_half_ghost_bin_2d_no_newton;
          else sc = &Neighbor::stencil_half_bin_2d_no_newton;
        else if (dimension == 3) {
          if (rq->ghost) sc = &Neighbor::stencil_half_ghost_bin_3d_no_newton;
          else sc = &Neighbor::stencil_half_bin_3d_no_newton;
        }
      }

    } else if (style == MULTI) {
      
      if(rq->gran) {
          if(rq->newton == 1 || (rq->newton == 0 && newton_pair == 1))
            error->all(FLERR,"Neigh multi with gran requires newton off");
          if(triclinic == 1)
            error->all(FLERR,"Neigh multi with gran requires triclinic off");
          if(dimension == 2)
            error->all(FLERR,"Neigh multi with gran requires dimension 3");
          sc = &Neighbor::stencil_gran_multi_3d_no_newton;
      }

      else if (rq->newton == 0) {  
        if (newton_pair == 0) {
          if (dimension == 2)
            sc = &Neighbor::stencil_half_multi_2d_no_newton;
          else if (dimension == 3)
            sc = &Neighbor::stencil_half_multi_3d_no_newton;
        } else if (triclinic == 0) {
          if (dimension == 2)
            sc = &Neighbor::stencil_half_multi_2d_newton;
          else if (dimension == 3)
            sc = &Neighbor::stencil_half_multi_3d_newton;
        } else if (triclinic == 1) {
          if (dimension == 2)
            sc = &Neighbor::stencil_half_multi_2d_newton_tri;
          else if (dimension == 3)
            sc = &Neighbor::stencil_half_multi_3d_newton_tri;
        }
      } else if (rq->newton == 1) {
        if (triclinic == 0) {
          if (dimension == 2)
            sc = &Neighbor::stencil_half_multi_2d_newton;
          else if (dimension == 3)
            sc = &Neighbor::stencil_half_multi_3d_newton;
        } else if (triclinic == 1) {
          if (dimension == 2)
            sc = &Neighbor::stencil_half_multi_2d_newton_tri;
          else if (dimension == 3)
            sc = &Neighbor::stencil_half_multi_3d_newton_tri;
        }
      } else if (rq->newton == 2) {
        if (dimension == 2)
          sc = &Neighbor::stencil_half_multi_2d_no_newton;
        else if (dimension == 3)
          sc = &Neighbor::stencil_half_multi_3d_no_newton;
      }
    }

  } else if (rq->full) {
    if (style == BIN) {
      if (dimension == 2) {
        if (rq->ghost) sc = &Neighbor::stencil_full_ghost_bin_2d;
        else sc = &Neighbor::stencil_full_bin_2d;
      }
      else if (dimension == 3) {
        if (rq->ghost) sc = &Neighbor::stencil_full_ghost_bin_3d;
        else sc = &Neighbor::stencil_full_bin_3d;
      }
    } else if (style == MULTI) {
      if (dimension == 2) sc = &Neighbor::stencil_full_multi_2d;
      else if (dimension == 3) sc = &Neighbor::stencil_full_multi_3d;
    }
  }

  stencil_create[index] = sc;
}

/* ---------------------------------------------------------------------- */

void Neighbor::print_lists_of_lists()
{
  if (comm->me == 0) {
    printf("Build lists = %d: ",nblist);
    for (int i = 0; i < nblist; i++) printf("%d ",blist[i]);
    printf("\n");
    printf("Grow lists = %d: ",nglist);
    for (int i = 0; i < nglist; i++) printf("%d ",glist[i]);
    printf("\n");
    printf("Stencil lists = %d: ",nslist);
    for (int i = 0; i < nslist; i++) printf("%d ",slist[i]);
    printf("\n");
  }
}

/* ---------------------------------------------------------------------- */

int Neighbor::decide()
{
  if (must_check) {
    const bigint n = update->ntimestep;
    if (output->restart_requested(update->ntimestep)) 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
     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,delr,delrsq; 

  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));
      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));
      deltasq = delta*delta;
    }
  } else { 
    deltasq = triggersq;
    delta = sqrt(deltasq);
  }

  double **x = atom->x;
  double *radius = atom->radius; 
  int nlocal = atom->nlocal;
  int radvary_flag = atom->radvary_flag; 
  if (includegroup) nlocal = atom->nfirst;

  int flag = 0;
  
  if(radvary_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;
        
      }
  }
  
  else 
  {
      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];
        delr = radius[i] - rhold[i];
        rsq = delx*delx + dely*dely + delz*delz;
        delrsq = delr*delr;
        if (delrsq > deltasq || rsq > deltasq - 2.*delr*delta + delrsq)
            flag = 1;
        
      }
      
  }

  int flagall;
  MPI_Allreduce(&flag,&flagall,1,MPI_INT,MPI_MAX,world);
  if (flagall && ago == MAX(every,delay)) ndanger++;
  return flagall;
}

/* ----------------------------------------------------------------------
   build all perpetual neighbor lists every few timesteps
   pairwise & topology lists are created as needed
   topology lists only built if topoflag = 1
------------------------------------------------------------------------- */

void Neighbor::build(int topoflag)
{
  int i;

  ago = 0;
  ncalls++;
  lastcall = update->ntimestep;

  // store current atom positions and box size if needed

  if (dist_check) {
    double **x = atom->x;
    double *radius = atom->radius; 
    int nlocal = atom->nlocal;
    if (includegroup) nlocal = atom->nfirst;
    if (nlocal > maxhold) {
      maxhold = atom->nmax;
      memory->destroy(xhold);
      memory->create(xhold,maxhold,3,"neigh:xhold");
      memory->destroy(rhold); 
      memory->create(rhold,maxhold,"neigh:rhold");  
    }

    if(atom->radvary_flag == 0)
    {
        for (i = 0; i < nlocal; i++) {
          xhold[i][0] = x[i][0];
          xhold[i][1] = x[i][1];
          xhold[i][2] = x[i][2];
        }
    }
    else
    {
        for (i = 0; i < nlocal; i++) {
          xhold[i][0] = x[i][0];
          xhold[i][1] = x[i][1];
          xhold[i][2] = x[i][2];
          rhold[i] = radius[i];
        }
    }
    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];
        }
      }
    }
  }

  // if any lists store neighbors of ghosts:
  // invoke grow() if nlocal+nghost exceeds previous list size
  // else only invoke grow() if nlocal exceeds previous list size
  // only done for lists with growflag set and which are perpetual

  if (anyghostlist && atom->nlocal+atom->nghost > maxatom) {
    maxatom = atom->nmax;
    for (i = 0; i < nglist; i++) lists[glist[i]]->grow(maxatom);
  } else if (atom->nlocal > maxatom) {
    maxatom = atom->nmax;
    for (i = 0; i < nglist; i++) lists[glist[i]]->grow(maxatom);
  }

  // extend atom bin list if necessary

  if (style != NSQ && atom->nmax > maxbin) {
    maxbin = atom->nmax;
    memory->destroy(bins);
    memory->create(bins,maxbin,"bins");
  }

  // check that neighbor list with special bond flags will not overflow

  if (atom->nlocal+atom->nghost > NEIGHMASK)
    error->one(FLERR,"Too many local+ghost atoms for neighbor list");

  // invoke building of pair and molecular neighbor lists
  // only for pairwise lists with buildflag set

  for (i = 0; i < nblist; i++)
    (this->*pair_build[blist[i]])(lists[blist[i]]);

  if (atom->molecular && topoflag) build_topology();
}

/* ----------------------------------------------------------------------
   build all topology neighbor lists every few timesteps
   normally built with pair lists, but USER-CUDA separates them
------------------------------------------------------------------------- */

void Neighbor::build_topology()
{
    if (force->bond) (this->*bond_build)();
    if (force->angle) (this->*angle_build)();
    if (force->dihedral) (this->*dihedral_build)();
    if (force->improper) (this->*improper_build)();
}

/* ----------------------------------------------------------------------
   build a single occasional pairwise neighbor list indexed by I
   called by other classes
------------------------------------------------------------------------- */

void Neighbor::build_one(int i)
{
  // update stencils and grow atom arrays and bins as needed
  // only for relevant settings of stencilflag and growflag
  // grow atom array for this list to current size of perpetual lists

  if (lists[i]->stencilflag) {
    lists[i]->stencil_allocate(smax,style);
    (this->*stencil_create[i])(lists[i],sx,sy,sz);
  }

  if (lists[i]->growflag) lists[i]->grow(maxatom);

  if (style != NSQ && atom->nmax > maxbin) {
    maxbin = atom->nmax;
    memory->destroy(bins);
    memory->create(bins,maxbin,"bins");
  }

  // check that neighbor list with special bond flags will not overflow

  if (atom->nlocal+atom->nghost > NEIGHMASK)
    error->one(FLERR,"Too many local+ghost atoms for neighbor list");

  // when occasional list built, LAMMPS can crash if atoms have moved too far
  // why is this?, give warning if this is the case
  // no easy workaround b/c all neighbor lists really need to be rebuilt
  // solution is for input script to check more often for rebuild
  // only check_distance if running a simulation, not between simulations

  int flag = 0;
  if (dist_check && update->whichflag) flag = check_distance();
  if (flag && me == 0)
    error->warning(FLERR,"Building an occasional neighobr list when "
                   "atoms may have moved too far");

  (this->*pair_build[i])(lists[i]);
}

/* ----------------------------------------------------------------------
   setup neighbor binning parameters
   bin numbering in each dimension is global:
     0 = 0.0 to binsize, 1 = binsize to 2*binsize, etc
     nbin-1,nbin,etc = bbox-binsize to bbox, bbox to bbox+binsize, etc
     -1,-2,etc = -binsize to 0.0, -2*binsize to -binsize, etc
   code will work for any binsize
     since next(xyz) and stencil extend as far as necessary
     binsize = 1/2 of cutoff is roughly optimal
   for orthogonal boxes:
     a dim must be filled exactly by integer # of bins
     in periodic, procs on both sides of PBC must see same bin boundary
     in non-periodic, coord2bin() still assumes this by use of nbin xyz
   for triclinic boxes:
     tilted simulation box cannot contain integer # of bins
     stencil & neigh list built differently to account for this
   mbinlo = lowest global bin any of my ghost atoms could fall into
   mbinhi = highest global bin any of my ghost atoms could fall into
   mbin = number of bins I need in a dimension
------------------------------------------------------------------------- */

void Neighbor::setup_bins()
{
  // bbox = size of bbox of entire domain
  // bsubbox lo/hi = bounding box of my subdomain extended by comm->cutghost
  // for triclinic:
  //   bbox bounds all 8 corners of tilted box
  //   subdomain is in lamda coords
  //   include dimension-dependent extension via comm->cutghost
  //   domain->bbox() converts lamda extent to box coords and computes bbox

  double bbox[3],bsubboxlo[3],bsubboxhi[3];
  double *cutghost = comm->cutghost;

  if (triclinic == 0) {
    bsubboxlo[0] = domain->sublo[0] - cutghost[0];
    bsubboxlo[1] = domain->sublo[1] - cutghost[1];
    bsubboxlo[2] = domain->sublo[2] - cutghost[2];
    bsubboxhi[0] = domain->subhi[0] + cutghost[0];
    bsubboxhi[1] = domain->subhi[1] + cutghost[1];
    bsubboxhi[2] = domain->subhi[2] + cutghost[2];
  } else {
    double lo[3],hi[3];
    lo[0] = domain->sublo_lamda[0] - cutghost[0];
    lo[1] = domain->sublo_lamda[1] - cutghost[1];
    lo[2] = domain->sublo_lamda[2] - cutghost[2];
    hi[0] = domain->subhi_lamda[0] + cutghost[0];
    hi[1] = domain->subhi_lamda[1] + cutghost[1];
    hi[2] = domain->subhi_lamda[2] + cutghost[2];
    domain->bbox(lo,hi,bsubboxlo,bsubboxhi);
  }

  bbox[0] = bboxhi[0] - bboxlo[0];
  bbox[1] = bboxhi[1] - bboxlo[1];
  bbox[2] = bboxhi[2] - bboxlo[2];

  // optimal bin size is roughly 1/2 the cutoff
  // for BIN style, binsize = 1/2 of max neighbor cutoff
  // for MULTI style, binsize = 1/2 of min neighbor cutoff
  // special case of all cutoffs = 0.0, binsize = box size

  double binsize_optimal;
  if (binsizeflag) binsize_optimal = binsize_user;
  else if (style == BIN) binsize_optimal = 0.5*cutneighmax;
  else binsize_optimal = 0.5*cutneighmin;
  if (binsize_optimal == 0.0) binsize_optimal = bbox[0];
  double binsizeinv = 1.0/binsize_optimal;

  // test for too many global bins in any dimension due to huge global domain

  if (bbox[0]*binsizeinv > MAXSMALLINT || bbox[1]*binsizeinv > MAXSMALLINT ||
      bbox[2]*binsizeinv > MAXSMALLINT)
    error->all(FLERR,"Domain too large for neighbor bins");

  // create actual bins
  // always have one bin even if cutoff > bbox
  // for 2d, nbinz = 1

  nbinx = static_cast<int> (bbox[0]*binsizeinv);
  nbiny = static_cast<int> (bbox[1]*binsizeinv);
  if (dimension == 3) nbinz = static_cast<int> (bbox[2]*binsizeinv);
  else nbinz = 1;

  if (nbinx == 0) nbinx = 1;
  if (nbiny == 0) nbiny = 1;
  if (nbinz == 0) nbinz = 1;

  // compute actual bin size for nbins to fit into box exactly
  // error if actual bin size << cutoff, since will create a zillion bins
  // this happens when nbin = 1 and box size << cutoff
  // typically due to non-periodic, flat system in a particular dim
  // in that extreme case, should use NSQ not BIN neighbor style

  binsizex = bbox[0]/nbinx;
  binsizey = bbox[1]/nbiny;
  binsizez = bbox[2]/nbinz;

  bininvx = 1.0 / binsizex;
  bininvy = 1.0 / binsizey;
  bininvz = 1.0 / binsizez;

  if (binsize_optimal*bininvx > CUT2BIN_RATIO ||
      binsize_optimal*bininvy > CUT2BIN_RATIO ||
      binsize_optimal*bininvz > CUT2BIN_RATIO)
    error->all(FLERR,"Cannot use neighbor bins - box size << cutoff");

  // mbinlo/hi = lowest and highest global bins my ghost atoms could be in
  // coord = lowest and highest values of coords for my ghost atoms
  // static_cast(-1.5) = -1, so subract additional -1
  // add in SMALL for round-off safety

  int mbinxhi,mbinyhi,mbinzhi;
  double coord;

  coord = bsubboxlo[0] - SMALL*bbox[0];
  mbinxlo = static_cast<int> ((coord-bboxlo[0])*bininvx);
  if (coord < bboxlo[0]) mbinxlo = mbinxlo - 1;
  coord = bsubboxhi[0] + SMALL*bbox[0];
  mbinxhi = static_cast<int> ((coord-bboxlo[0])*bininvx);

  coord = bsubboxlo[1] - SMALL*bbox[1];
  mbinylo = static_cast<int> ((coord-bboxlo[1])*bininvy);
  if (coord < bboxlo[1]) mbinylo = mbinylo - 1;
  coord = bsubboxhi[1] + SMALL*bbox[1];
  mbinyhi = static_cast<int> ((coord-bboxlo[1])*bininvy);

  if (dimension == 3) {
    coord = bsubboxlo[2] - SMALL*bbox[2];
    mbinzlo = static_cast<int> ((coord-bboxlo[2])*bininvz);
    if (coord < bboxlo[2]) mbinzlo = mbinzlo - 1;
    coord = bsubboxhi[2] + SMALL*bbox[2];
    mbinzhi = static_cast<int> ((coord-bboxlo[2])*bininvz);
  }

  // extend bins by 1 to insure stencil extent is included
  // if 2d, only 1 bin in z

  mbinxlo = mbinxlo - 1;
  mbinxhi = mbinxhi + 1;
  mbinx = mbinxhi - mbinxlo + 1;

  mbinylo = mbinylo - 1;
  mbinyhi = mbinyhi + 1;
  mbiny = mbinyhi - mbinylo + 1;

  if (dimension == 3) {
    mbinzlo = mbinzlo - 1;
    mbinzhi = mbinzhi + 1;
  } else mbinzlo = mbinzhi = 0;
  mbinz = mbinzhi - mbinzlo + 1;

  // memory for bin ptrs

  bigint bbin = ((bigint) mbinx) * ((bigint) mbiny) * ((bigint) mbinz);
  
  if (bbin > MAXSMALLINT) error->one(FLERR,"Too many neighbor bins");
  mbins = bbin;
  if (mbins > maxhead) {
    maxhead = mbins;
    memory->destroy(binhead);
    memory->create(binhead,maxhead,"neigh:binhead");
  }

  // create stencil of bins to search over in neighbor list construction
  // sx,sy,sz = max range of stencil in each dim
  // smax = max possible size of entire 3d stencil
  // stencil is empty if cutneighmax = 0.0

  sx = static_cast<int> (cutneighmax*bininvx);
  if (sx*binsizex < cutneighmax) sx++;
  sy = static_cast<int> (cutneighmax*bininvy);
  if (sy*binsizey < cutneighmax) sy++;
  sz = static_cast<int> (cutneighmax*bininvz);
  if (sz*binsizez < cutneighmax) sz++;
  if (dimension == 2) sz = 0;
  smax = (2*sx+1) * (2*sy+1) * (2*sz+1);

  // create stencils for pairwise neighbor lists
  // only done for lists with stencilflag and buildflag set

  for (int i = 0; i < nslist; i++) {
    lists[slist[i]]->stencil_allocate(smax,style);
    (this->*stencil_create[slist[i]])(lists[slist[i]],sx,sy,sz);
  }

  last_setup_bins_timestep = update->ntimestep;
}

/* ----------------------------------------------------------------------
   compute closest distance between central bin (0,0,0) and bin (i,j,k)
------------------------------------------------------------------------- */

double Neighbor::bin_distance(int i, int j, int k)
{
  double delx,dely,delz;

  if (i > 0) delx = (i-1)*binsizex;
  else if (i == 0) delx = 0.0;
  else delx = (i+1)*binsizex;

  if (j > 0) dely = (j-1)*binsizey;
  else if (j == 0) dely = 0.0;
  else dely = (j+1)*binsizey;

  if (k > 0) delz = (k-1)*binsizez;
  else if (k == 0) delz = 0.0;
  else delz = (k+1)*binsizez;

  return (delx*delx + dely*dely + delz*delz);
}

/* ----------------------------------------------------------------------
   compute largest distance between central bin (0,0,0) and bin (i,j,k)
------------------------------------------------------------------------- */

double Neighbor::bin_largest_distance(int i, int j, int k)
{
  double delx,dely,delz;

  if (i > 0) delx = (i+1)*binsizex;
  else if (i == 0) delx = binsizex;
  else delx = (i-1)*binsizex;

  if (j > 0) dely = (j+1)*binsizey;
  else if (j == 0) dely = binsizey;
  else dely = (j-1)*binsizey;

  if (k > 0) delz = (k+1)*binsizez;
  else if (k == 0) delz = binsizez;
  else delz = (k-1)*binsizez;

  return (delx*delx + dely*dely + delz*delz);
}

/* ----------------------------------------------------------------------
   set neighbor style and skin distance
------------------------------------------------------------------------- */

void Neighbor::set(int narg, char **arg, bool auto_set_bin)
{
  if (narg != (auto_set_bin ? 1 : 2))
    error->all(FLERR,"Illegal neighbor command");

  skin = force->cg_max()*force->numeric(FLERR,arg[0]); 
  if (skin < 0.0) error->all(FLERR,"Illegal neighbor command");

  if (auto_set_bin)
    style = BIN;
  else
  {
    if (strcmp(arg[1],"nsq") == 0) style = NSQ;
    else if (strcmp(arg[1],"bin") == 0) style = BIN;
    else if (strcmp(arg[1],"multi") == 0) style = MULTI;
    else error->all(FLERR,"Illegal neighbor command");
  }

  if (style == MULTI && lmp->citeme) lmp->citeme->add(cite_neigh_multi);
}

/* ----------------------------------------------------------------------
   modify parameters of the pair-wise neighbor build (neigh_settings)
------------------------------------------------------------------------- */

void Neighbor::modify_params_restricted(int narg, char **arg)
{
    // set delay to 0
    delay = 0;
    // allow only one input
    if (narg > 1)
        error->all (FLERR, "neigh_settings requires at most one parameter");
    // which is to be the binsize
    binsize_user = force->cg_max()*force->numeric(FLERR,arg[0]);
    if (binsize_user <= 0.0)
        binsizeflag = 0;
    else
        binsizeflag = 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) error->all(FLERR,"Illegal neigh_modify command");
      every = force->inumeric(FLERR,arg[iarg+1]);
      if (every <= 0) error->all(FLERR,"Illegal neigh_modify command");
      iarg += 2;
    } else if (strcmp(arg[iarg],"delay") == 0) {
      if (iarg+2 > narg) error->all(FLERR,"Illegal neigh_modify command");
      delay = force->inumeric(FLERR,arg[iarg+1]);
      if (delay < 0) error->all(FLERR,"Illegal neigh_modify command");
      iarg += 2;
    } else if (strcmp(arg[iarg],"contact_distance_factor") == 0) { 
      if (iarg+2 > narg) error->all(FLERR,"Illegal neigh_modify command");
      contactDistanceFactor = atof(arg[iarg+1]);
      if (contactDistanceFactor  < 1.0) error->all(FLERR,"Illegal neigh_modify command. Please set contact_distance_factor value >=1");
      iarg +=2;
    } else if (strcmp(arg[iarg],"check") == 0) {
      if (iarg+2 > narg) error->all(FLERR,"Illegal neigh_modify command");
      if (strcmp(arg[iarg+1],"yes") == 0) dist_check = 1;
      else if (strcmp(arg[iarg+1],"no") == 0) dist_check = 0;
      else error->all(FLERR,"Illegal neigh_modify command");
      iarg += 2;
    } else if (strcmp(arg[iarg],"once") == 0) {
      if (iarg+2 > narg) error->all(FLERR,"Illegal neigh_modify command");
      if (strcmp(arg[iarg+1],"yes") == 0) build_once = 1;
      else if (strcmp(arg[iarg+1],"no") == 0) build_once = 0;
      else error->all(FLERR,"Illegal neigh_modify command");
      iarg += 2;
    } else if (strcmp(arg[iarg],"page") == 0) {
      if (iarg+2 > narg) error->all(FLERR,"Illegal neigh_modify command");
      old_pgsize = pgsize;
      pgsize = force->inumeric(FLERR,arg[iarg+1]);
      iarg += 2;
    } else if (strcmp(arg[iarg],"one") == 0) {
      if (iarg+2 > narg) error->all(FLERR,"Illegal neigh_modify command");
      old_oneatom = oneatom;
      oneatom = force->inumeric(FLERR,arg[iarg+1]);
      iarg += 2;
    } else if (strcmp(arg[iarg],"binsize") == 0) {
      if (iarg+2 > narg) error->all(FLERR,"Illegal neigh_modify command");
      binsize_user = force->cg_max()*force->numeric(FLERR,arg[iarg+1]); 
      if (binsize_user <= 0.0) binsizeflag = 0;
      else binsizeflag = 1;
      iarg += 2;
    } else if (strcmp(arg[iarg],"cluster") == 0) {
      error->all(FLERR,"neigh_modify cluster is deprecated");
      if (iarg+2 > narg) error->all(FLERR,"Illegal neigh_modify command");
      if (strcmp(arg[iarg+1],"yes") == 0) cluster_check = 1;
      else if (strcmp(arg[iarg+1],"no") == 0) cluster_check = 0;
      else error->all(FLERR,"Illegal neigh_modify command");
      iarg += 2;

    } else if (strcmp(arg[iarg],"include") == 0) {
      if (iarg+2 > narg) error->all(FLERR,"Illegal neigh_modify command");
      includegroup = group->find(arg[iarg+1]);
      if (includegroup < 0)
        error->all(FLERR,"Invalid group ID in neigh_modify command");
      if (includegroup && (atom->firstgroupname == NULL ||
                            strcmp(arg[iarg+1],atom->firstgroupname) != 0))
        error->all(FLERR,
                   "Neigh_modify include group != atom_modify first group");
      iarg += 2;

    } else if (strcmp(arg[iarg],"exclude") == 0) {
      if (iarg+2 > narg) error->all(FLERR,"Illegal neigh_modify command");

      if (strcmp(arg[iarg+1],"type") == 0) {
        if (iarg+4 > narg) error->all(FLERR,"Illegal neigh_modify command");
        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] = force->inumeric(FLERR,arg[iarg+2]);
        ex2_type[nex_type] = force->inumeric(FLERR,arg[iarg+3]);
        nex_type++;
        iarg += 4;

      } else if (strcmp(arg[iarg+1],"group") == 0) {
        if (iarg+4 > narg) error->all(FLERR,"Illegal neigh_modify command");
        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 || ex2_group[nex_group] == -1)
          error->all(FLERR,"Invalid group ID in neigh_modify command");
        nex_group++;
        iarg += 4;

      } else if (strcmp(arg[iarg+1],"molecule") == 0) {
        if (iarg+3 > narg) error->all(FLERR,"Illegal neigh_modify command");
        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");
        }
        ex_mol_group[nex_mol] = group->find(arg[iarg+2]);
        if (ex_mol_group[nex_mol] == -1)
          error->all(FLERR,"Invalid group ID in neigh_modify command");
        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,"Illegal neigh_modify command");

    } else error->all(FLERR,"Illegal neigh_modify command");
  }
}

/* ----------------------------------------------------------------------
   bin owned and ghost atoms
------------------------------------------------------------------------- */

void Neighbor::bin_atoms()
{
  int i,ibin;

  for (i = 0; i < mbins; i++) binhead[i] = -1;

  // bin in reverse order so linked list will be in forward order
  // also puts ghost atoms at end of list, which is necessary

  double **x = atom->x;
  int *mask = atom->mask;
  int nlocal = atom->nlocal;
  int nall = nlocal + atom->nghost;

  if (includegroup) {
    int bitmask = group->bitmask[includegroup];
    for (i = nall-1; i >= nlocal; i--) {
      if (mask[i] & bitmask) {
        ibin = coord2bin(x[i]);
        bins[i] = binhead[ibin];
        binhead[ibin] = i;
      }
    }
    for (i = atom->nfirst-1; i >= 0; i--) {
      ibin = coord2bin(x[i]);
      bins[i] = binhead[ibin];
      binhead[ibin] = i;
    }

  } else {
    for (i = nall-1; i >= 0; i--) {
      ibin = coord2bin(x[i]);
      bins[i] = binhead[ibin];
      binhead[ibin] = i;
    }
  }
}

/* ----------------------------------------------------------------------
   convert atom coords into local bin #
   for orthogonal, only ghost atoms will have coord >= bboxhi or coord < bboxlo
     take special care to insure ghosts are in correct bins even w/ roundoff
     hi ghost atoms = nbin,nbin+1,etc
     owned atoms = 0 to nbin-1
     lo ghost atoms = -1,-2,etc
     this is necessary so that both procs on either side of PBC
       treat a pair of atoms straddling the PBC in a consistent way
   for triclinic, doesn't matter since stencil & neigh list built differently
------------------------------------------------------------------------- */

int Neighbor::coord2bin(double *x)
{
  int ix,iy,iz;

  if (x[0] >= bboxhi[0])
    ix = static_cast<int> ((x[0]-bboxhi[0])*bininvx) + nbinx;
  else if (x[0] >= bboxlo[0]) {
    ix = static_cast<int> ((x[0]-bboxlo[0])*bininvx);
    ix = MIN(ix,nbinx-1);
  } else
    ix = static_cast<int> ((x[0]-bboxlo[0])*bininvx) - 1;

  if (x[1] >= bboxhi[1])
    iy = static_cast<int> ((x[1]-bboxhi[1])*bininvy) + nbiny;
  else if (x[1] >= bboxlo[1]) {
    iy = static_cast<int> ((x[1]-bboxlo[1])*bininvy);
    iy = MIN(iy,nbiny-1);
  } else
    iy = static_cast<int> ((x[1]-bboxlo[1])*bininvy) - 1;

  if (x[2] >= bboxhi[2])
    iz = static_cast<int> ((x[2]-bboxhi[2])*bininvz) + nbinz;
  else if (x[2] >= bboxlo[2]) {
    iz = static_cast<int> ((x[2]-bboxlo[2])*bininvz);
    iz = MIN(iz,nbinz-1);
  } else
    iz = static_cast<int> ((x[2]-bboxlo[2])*bininvz) - 1;

  return (iz-mbinzlo)*mbiny*mbinx + (iy-mbinylo)*mbinx + (ix-mbinxlo);
}

/* ----------------------------------------------------------------------
   same as coord2bin, but also return ix,iy,iz offsets in each dim
------------------------------------------------------------------------- */

int Neighbor::coord2bin(double *x, int &ix, int &iy, int &iz)
{
  if (x[0] >= bboxhi[0])
    ix = static_cast<int> ((x[0]-bboxhi[0])*bininvx) + nbinx;
  else if (x[0] >= bboxlo[0]) {
    ix = static_cast<int> ((x[0]-bboxlo[0])*bininvx);
    ix = MIN(ix,nbinx-1);
  } else
    ix = static_cast<int> ((x[0]-bboxlo[0])*bininvx) - 1;

  if (x[1] >= bboxhi[1])
    iy = static_cast<int> ((x[1]-bboxhi[1])*bininvy) + nbiny;
  else if (x[1] >= bboxlo[1]) {
    iy = static_cast<int> ((x[1]-bboxlo[1])*bininvy);
    iy = MIN(iy,nbiny-1);
  } else
    iy = static_cast<int> ((x[1]-bboxlo[1])*bininvy) - 1;

  if (x[2] >= bboxhi[2])
    iz = static_cast<int> ((x[2]-bboxhi[2])*bininvz) + nbinz;
  else if (x[2] >= bboxlo[2]) {
    iz = static_cast<int> ((x[2]-bboxlo[2])*bininvz);
    iz = MIN(iz,nbinz-1);
  } else
    iz = static_cast<int> ((x[2]-bboxlo[2])*bininvz) - 1;

  ix -= mbinxlo;
  iy -= mbinylo;
  iz -= mbinzlo;
  return iz*mbiny*mbinx + iy*mbinx + ix;
}

/* ----------------------------------------------------------------------
   return center coordinate of bin (ix,iy,iz)
------------------------------------------------------------------------- */

void Neighbor::bin_center(int ix, int iy, int iz, double * center)
{
  center[0] = (mbinxlo + ix + 0.5) * binsizex + bboxlo[0];
  center[1] = (mbinylo + iy + 0.5) * binsizey + bboxlo[1];
  center[2] = (mbinzlo + iz + 0.5) * binsizez + bboxlo[2];
}

/* ----------------------------------------------------------------------
   test if atom pair i,j is excluded from neighbor list
   due to type, group, molecule settings from neigh_modify command
   return 1 if should be excluded, 0 if included
------------------------------------------------------------------------- */

int Neighbor::exclusion(int i, int j, int itype, int jtype,
                        int *mask, int *molecule) const {
  int m;
  
  if (nex_type && ex_type[itype][jtype]) return 1;

  if (nex_group) {
    for (m = 0; m < nex_group; m++) {
      if (mask[i] & ex1_bit[m] && mask[j] & ex2_bit[m]) return 1;
      if (mask[i] & ex2_bit[m] && mask[j] & ex1_bit[m]) return 1;
    }
  }

  if (nex_mol) {
    for (m = 0; m < nex_mol; m++)
      if (mask[i] & ex_mol_bit[m] && mask[j] & ex_mol_bit[m] &&
          (molecule[i] > -1 && molecule[j] > -1) &&  
          molecule[i] == molecule[j]) return 1;
  }

  return 0;
}

/* ----------------------------------------------------------------------
   return # of bytes of allocated memory
------------------------------------------------------------------------- */

bigint Neighbor::memory_usage()
{
  bigint bytes = 0;
  bytes += memory->usage(xhold,maxhold,3);
  bytes += memory->usage(rhold,maxhold); 

  if (style != NSQ) {
    bytes += memory->usage(bins,maxbin);
    bytes += memory->usage(binhead,maxhead);
  }

  for (int i = 0; i < nlist; i++) bytes += lists[i]->memory_usage();

  bytes += memory->usage(bondlist,maxbond,3);
  bytes += memory->usage(anglelist,maxangle,4);
  bytes += memory->usage(dihedrallist,maxdihedral,5);
  bytes += memory->usage(improperlist,maximproper,5);

  return bytes;
}

/* ----------------------------------------------------------------------
   return the value of exclude - used to check compatibility with GPU
------------------------------------------------------------------------- */

int Neighbor::exclude_setting()
{
  return exclude;
}

/* ----------------------------------------------------------------------
   return # neighs
------------------------------------------------------------------------- */

int Neighbor::n_neighs()
{
    // find a non-skip neighbor list containing half the pairwise interactions
    // count neighbors in that list for stats purposes

    int m;

    for (m = 0; m < neighbor->old_nrequest; m++)
      if ((neighbor->old_requests[m]->half ||
           neighbor->old_requests[m]->gran ||
           neighbor->old_requests[m]->respaouter ||
           neighbor->old_requests[m]->half_from_full) &&
          neighbor->old_requests[m]->skip == 0 &&
          neighbor->lists[m]->numneigh) break;

    int nneigh = 0;

    if (m < old_nrequest) {
      int inum = lists[m]->inum;
      
      int *ilist = lists[m]->ilist;
      int *numneigh = lists[m]->numneigh;
      for (int ii = 0; ii < inum; ii++)
      {
        
        nneigh += numneigh[ilist[ii]];
      }
    }

    return nneigh;
}
