Commit 90ff2eb6 authored by Steve Plimpton's avatar Steve Plimpton
Browse files

modified versions of creating atoms on subset of lattice, ditto for set type/fraction

parent 4ea679dd
Loading
Loading
Loading
Loading
+20 −6
Original line number Diff line number Diff line
@@ -27,7 +27,7 @@ Syntax
         region-ID = create atoms within this region, use NULL for entire simulation box

* zero or more keyword/value pairs may be appended
* keyword = *mol* or *basis* or *subset* or *remap* or *var* or *set* or *units*
* keyword = *mol* or *basis* or *ratio* or *subset* or *remap* or *var* or *set* or *rotate* or *units*
  
  .. parsed-literal::
  
@@ -37,7 +37,12 @@ Syntax
       *basis* values = M itype
         M = which basis atom
         itype = atom type (1-N) to assign to this basis atom
       *subset* value = N = number of particles to add at lattice points
       *ratio* values = frac seed
         frac = fraction of lattice sites (0 to 1) to populate randomly
         seed = random # seed (positive integer)
       *subset* values = Nsubset seed
         Nsubset = # of lattice sites to populate randomly
         seed = random # seed (positive integer)
       *remap* value = *yes* or *no*
       *var* value = name = variable name to evaluate for test of atom creation
       *set* values = dim name
@@ -60,6 +65,7 @@ Examples

   create_atoms 1 box
   create_atoms 3 region regsphere basis 2 3
   create_atoms 3 region regsphere basis 2 3 ratio 0.5 74637
   create_atoms 3 single 0 0 5
   create_atoms 1 box var v set x xpos set y ypos

@@ -215,10 +221,18 @@ command for specifics on how basis atoms are defined for the unit cell
of the lattice.  By default, all created atoms are assigned the
argument *type* as their atom type.

The *subset* keyword can be used in conjunction with the *box* or
*region* styles to limit the total number of particles inserted. The
specified number of particles N are placed randomly on the available
lattice points.
The *ratio* and *subset* keywords can be used in conjunction with the
*box* or *region* styles to limit the total number of particles
inserted.  The lattice defines a set of *Nlatt* eligible sites for
inserting particles, which may be limited by the *region* style or the
*var* and *set* keywords.  For the *ratio* keyword only the specified
fraction of them (0 <= *frac* <= 1) will be assigned particles.  For
the *subset* keyword only the specified *Nsubset* of them will be
assigned particles.  In both cases the assigned lattice sites are
chosen randomly.  An iterative algorithm is used which insures the
correct number of particles are inserted, in a perfectly random
fashion.  Which lattice sites are selected will change with the number
of processors used.

The *remap* keyword only applies to the *single* style.  If it is set
to *yes*\ , then if the specified position is outside the simulation
+46 −24
Original line number Diff line number Diff line
@@ -14,7 +14,7 @@ Syntax
* style = *atom* or *type* or *mol* or *group* or *region*
* ID = atom ID range or type range or mol ID range or group ID or region ID
* one or more keyword/value pairs may be appended
* keyword = *type* or *type/fraction* or *mol* or *x* or *y* or *z* or           *charge* or *dipole* or *dipole/random* or *quat* or           *spin* or *spin/random* or *quat* or           *quat/random* or *diameter* or *shape* or           *length* or *tri* or *theta* or *theta/random* or           *angmom* or *omega* or           *mass* or *density* or *density/disc* or *volume* or *image* or           *bond* or *angle* or *dihedral* or *improper* or           *meso/e* or *meso/cv* or *meso/rho* or           *smd/contact/radius* or *smd/mass/density* or *dpd/theta* or           *edpd/temp* or *edpd/cv* or *cc* or *i\_name* or *d\_name*
* keyword = *type* or *type/fraction* or *type/ratio* or *type/subset* or *mol* or *x* or *y* or *z* or           *charge* or *dipole* or *dipole/random* or *quat* or           *spin* or *spin/random* or *quat* or           *quat/random* or *diameter* or *shape* or           *length* or *tri* or *theta* or *theta/random* or           *angmom* or *omega* or           *mass* or *density* or *density/disc* or *volume* or *image* or           *bond* or *angle* or *dihedral* or *improper* or           *meso/e* or *meso/cv* or *meso/rho* or           *smd/contact/radius* or *smd/mass/density* or *dpd/theta* or           *edpd/temp* or *edpd/cv* or *cc* or *i\_name* or *d\_name*
  
  .. parsed-literal::
  
@@ -22,7 +22,15 @@ Syntax
         value can be an atom-style variable (see below)
       *type/fraction* values = type fraction seed
         type = new atom type
         fraction = fraction of selected atoms to set to new atom type
         fraction = approximate fraction of selected atoms to set to new atom type
         seed = random # seed (positive integer)
       *type/ratio* values = type fraction seed
         type = new atom type
         fraction = exact fraction of selected atoms to set to new atom type
         seed = random # seed (positive integer)
       *type/subset* values = type Nsubset seed
         type = new atom type
         Nsubset = exact number of selected atoms to set to new atom type
         seed = random # seed (positive integer)
       *mol* value = molecule ID
         value can be an atom-style variable (see below)
@@ -184,15 +192,16 @@ This section describes the keyword options for which properties to
change, for the selected atoms.

Note that except where explicitly prohibited below, all of the
keywords allow an :doc:`atom-style or atomfile-style variable <variable>` to be used as the specified value(s).  If the
value is a variable, it should be specified as v\_name, where name is
the variable name.  In this case, the variable will be evaluated, and
its resulting per-atom value used to determine the value assigned to
each selected atom.  Note that the per-atom value from the variable
will be ignored for atoms that are not selected via the *style* and
*ID* settings explained above.  A simple way to use per-atom values
from the variable to reset a property for all atoms is to use style
*atom* with *ID* = "\*"; this selects all atom IDs.
keywords allow an :doc:`atom-style or atomfile-style variable
<variable>` to be used as the specified value(s).  If the value is a
variable, it should be specified as v\_name, where name is the
variable name.  In this case, the variable will be evaluated, and its
resulting per-atom value used to determine the value assigned to each
selected atom.  Note that the per-atom value from the variable will be
ignored for atoms that are not selected via the *style* and *ID*
settings explained above.  A simple way to use per-atom values from
the variable to reset a property for all atoms is to use style *atom*
with *ID* = "\*"; this selects all atom IDs.

Atom-style variables can specify formulas with various mathematical
functions, and include :doc:`thermo\_style <thermo_style>` command
@@ -220,23 +229,36 @@ command.

Keyword *type/fraction* sets the atom type for a fraction of the
selected atoms.  The actual number of atoms changed is not guaranteed
to be exactly the requested fraction, but should be statistically
close.  Random numbers are used in such a way that a particular atom
is changed or not changed, regardless of how many processors are being
used.  This keyword does not allow use of an atom-style variable.

Keyword *mol* sets the molecule ID for all selected atoms.  The :doc:`atom style <atom_style>` being used must support the use of molecule
IDs.
to be exactly the specified fraction (0 <= *fraction* <= 1), but
should be statistically close.  Random numbers are used in such a way
that a particular atom is changed or not changed, regardless of how
many processors are being used.  This keyword does not allow use of an
atom-style variable.

Keywords *x*\ , *y*\ , *z*\ , and *charge* set the coordinates or charge of
all selected atoms.  For *charge*\ , the :doc:`atom style <atom_style>`
being used must support the use of atomic charge. Keywords *vx*\ , *vy*\ ,
and *vz* set the velocities of all selected atoms.
Keywords *type/ratio* and *type/subset" also set the atom type for a
fraction of the selected atoms.  The actual number of atoms changed
will be exactly the requested number.  For *type/ratio* the specified
fraction (0 <= *fraction* <= 1) determines the number.  For
*type/subset*, the specified *Nsubset* is the number.  An iterative
algorithm is used which insures the correct number of atoms are
selected, in a perfectly random fashion.  Which atoms are selected
will change with the number of processors used.  These keywords do not
allow use of an atom-style variable.

Keyword *mol* sets the molecule ID for all selected atoms.  The
:doc:`atom style <atom_style>` being used must support the use of
molecule IDs.

Keywords *x*\ , *y*\ , *z*\ , and *charge* set the coordinates or
charge of all selected atoms.  For *charge*\ , the :doc:`atom style
<atom_style>` being used must support the use of atomic
charge. Keywords *vx*\ , *vy*\ , and *vz* set the velocities of all
selected atoms.

Keyword *dipole* uses the specified x,y,z values as components of a
vector to set as the orientation of the dipole moment vectors of the
selected atoms.  The magnitude of the dipole moment is set
by the length of this orientation vector.
selected atoms.  The magnitude of the dipole moment is set by the
length of this orientation vector.

Keyword *dipole/random* randomizes the orientation of the dipole
moment vectors for the selected atoms and sets the magnitude of each
+15 −0
Original line number Diff line number Diff line
@@ -19,6 +19,8 @@
#include "error.h"
#include "utils.h"

#include "comm.h"

using namespace LAMMPS_NS;

#define DELTA 16384
@@ -108,6 +110,19 @@ int AtomVec::grow_nmax_bonus(int nmax_bonus)
  return nmax_bonus;
}

/* ----------------------------------------------------------------------
   roundup N so it is a multiple of DELTA
   error if N exceeds 32-bit int, since will be used as arg to grow()
------------------------------------------------------------------------- */

bigint AtomVec::roundup(bigint n)
{
  if (n % DELTA) n = n/DELTA * DELTA + DELTA;
  if (n > MAXSMALLINT) 
    error->one(FLERR,"Too many atoms created on one or more procs");
  return n;
}

/* ----------------------------------------------------------------------
   unpack one line from Velocities section of data file
------------------------------------------------------------------------- */
+1 −0
Original line number Diff line number Diff line
@@ -57,6 +57,7 @@ class AtomVec : protected Pointers {

  virtual void grow(int) = 0;
  virtual void grow_reset() = 0;
  bigint roundup(bigint);
  virtual void copy(int, int, int) = 0;
  virtual void clear_bonus() {}
  virtual void force_clear(int, size_t) {}
+138 −184
Original line number Diff line number Diff line
@@ -39,9 +39,12 @@ using namespace MathConst;

#define BIG 1.0e30
#define EPSILON 1.0e-6
#define LB_FACTOR 1.1

enum{BOX,REGION,SINGLE,RANDOM};
enum{ATOM,MOLECULE};
enum{COUNT,INSERT,INSERT_SELECTED};
enum{NONE,RATIO,SUBSET};

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

@@ -108,12 +111,11 @@ void CreateAtoms::command(int narg, char **arg)
  remapflag = 0;
  mode = ATOM;
  int molseed;
  int subsetseed;
  varflag = 0;
  vstr = xstr = ystr = zstr = NULL;
  quatone[0] = quatone[1] = quatone[2] = 0.0;
  nlatt = nsubset = subsetflag = 0;
  flag = NULL;
  subsetflag = NONE;
  int subsetseed;

  nbasis = domain->lattice->nbasis;
  basistype = new int[nbasis];
@@ -194,16 +196,21 @@ void CreateAtoms::command(int narg, char **arg)
      MathExtra::norm3(axisone);
      MathExtra::axisangle_to_quat(axisone,thetaone,quatone);
      iarg += 5;
    } else if (strcmp(arg[iarg],"ratio") == 0) {
      if (iarg+3 > narg) error->all(FLERR,"Illegal create_atoms command");
      subsetflag = RATIO;
      subsetfrac = force->numeric(FLERR,arg[iarg+1]);
      subsetseed = force->inumeric(FLERR,arg[iarg+2]);
      if (subsetfrac <= 0.0 || subsetfrac > 1.0 || subsetseed <= 0) 
        error->all(FLERR,"Illegal create_atoms command");
      iarg += 3;
    } else if (strcmp(arg[iarg],"subset") == 0) {
      if (iarg+3 > narg) error->all(FLERR,"Illegal create_atoms command");
      nsubset = force->inumeric(FLERR,arg[iarg+1]);
      subsetflag = SUBSET;
      nsubset = force->bnumeric(FLERR,arg[iarg+1]);
      subsetseed = force->inumeric(FLERR,arg[iarg+2]);
      if (nsubset > 0) subsetflag = 1;
      else {
        if (me == 0) error->warning(FLERR,"Specifying an 'subset' value of "
                                 "'0' is equivalent to no 'subset' keyword");
        subsetflag = 0;
      }
      if (nsubset <= 0 || subsetseed <= 0) 
        error->all(FLERR,"Illegal create_atoms command");
      iarg += 3;
    } else error->all(FLERR,"Illegal create_atoms command");
  }
@@ -242,9 +249,8 @@ void CreateAtoms::command(int narg, char **arg)
    ranmol = new RanMars(lmp,molseed+me);
  }

  if (subsetflag) {
    ranlatt = new RanMars(lmp,subsetseed+me);
  }
  ranlatt = NULL;
  if (subsetflag != NONE) ranlatt = new RanMars(lmp,subsetseed+me);

  // error check and further setup for variable test

@@ -534,12 +540,13 @@ void CreateAtoms::command(int narg, char **arg)
  // clean up

  delete ranmol;
  delete ranlatt;

  if (domain->lattice) delete [] basistype;
  delete [] vstr;
  delete [] xstr;
  delete [] ystr;
  delete [] zstr;
  memory->destroy(flag);

  // for MOLECULE mode:
  // create special bond lists for molecular systems,
@@ -780,7 +787,6 @@ void CreateAtoms::add_lattice()
  //   which can lead to missing atoms in rare cases
  // extra decrement of lo if min < 0, since static_cast(-1.5) = -1

  int ilo,ihi,jlo,jhi,klo,khi;
  ilo = static_cast<int> (xmin) - 1;
  jlo = static_cast<int> (ymin) - 1;
  klo = static_cast<int> (zmin) - 1;
@@ -792,22 +798,18 @@ void CreateAtoms::add_lattice()
  if (ymin < 0.0) jlo--;
  if (zmin < 0.0) klo--;

  // iterate on 3d periodic lattice of unit cells using loop bounds
  // iterate on nbasis atoms in each unit cell
  // convert lattice coords to box coords
  // add atom or molecule (on each basis point) if it meets all criteria
  // rough estimate of total time used for create atoms
  // one inner loop takes about 25ns on a typical desktop CPU core in 2019
  // maxestimate = time in hours
  
  const double * const * const basis = domain->lattice->basis;
  double estimate = 2.5e-8/3600.0;
  estimate *= static_cast<double> (khi-klo+1);
  estimate *= static_cast<double> (jhi-jlo+1);
  estimate *= static_cast<double> (ihi-ilo+1);
  estimate *= static_cast<double> (nbasis);

  // rough estimate of total time used for create atoms.
  // one inner loop takes about 25ns on a typical desktop CPU core in 2019
  double testimate = 2.5e-8/3600.0; // convert seconds to hours
  testimate *= static_cast<double>(khi-klo+1);
  testimate *= static_cast<double>(jhi-jlo+1);
  testimate *= static_cast<double>(ihi-ilo+1);
  testimate *= static_cast<double>(nbasis);
  double maxestimate = 0.0;
  MPI_Reduce(&testimate,&maxestimate,1,MPI_DOUBLE,MPI_MAX,0,world);
  MPI_Reduce(&estimate,&maxestimate,1,MPI_DOUBLE,MPI_MAX,0,world);

  if ((comm->me == 0) && (maxestimate > 0.01)) {
    if (screen) fprintf(screen,"WARNING: create_atoms will take "
@@ -816,16 +818,73 @@ void CreateAtoms::add_lattice()
                         "approx. %.2f hours to complete\n",maxestimate);
  }

  // count lattice sites on each proc

  nlatt_overflow = 0;
  loop_lattice(COUNT);

  // nadd = # of atoms each proc will insert (estimated if subsetflag)

  int overflow;
  MPI_Allreduce(&nlatt_overflow,&overflow,1,MPI_INT,MPI_SUM,world);
  if (overflow) 
    error->all(FLERR,"Create_atoms lattice size overflow on 1 or more procs");

  bigint nadd;

  if (subsetflag == NONE) {
    if (nprocs == 1) nadd = nlatt;
    else nadd = static_cast<bigint> (LB_FACTOR * nlatt);
  } else {
    bigint bnlatt = nlatt;
    bigint bnlattall;
    MPI_Allreduce(&bnlatt,&bnlattall,1,MPI_LMP_BIGINT,MPI_SUM,world);
    if (subsetflag == RATIO)
      nsubset = static_cast<bigint> (subsetfrac * bnlattall);
    if (nsubset > bnlattall)
      error->all(FLERR,"Create_atoms subset size > # of lattice sites");
    if (nprocs == 1) nadd = nsubset;
    else nadd = static_cast<bigint> (LB_FACTOR * nsubset/bnlattall * nlatt);
  }
  
  // allocate atom arrays to size N, rounded up by AtomVec->DELTA

  bigint nbig = atom->avec->roundup(nadd + atom->nlocal);
  int n = static_cast<int> (nbig);
  atom->avec->grow(n);

  // add atoms or molecules
  // if no subset: add to all lattice sites
  // if subset: count lattice sites, select random subset, then add

  if (subsetflag == NONE) loop_lattice(INSERT);
  else {
    memory->create(flag,nlatt,"create_atoms:flag");
    memory->create(next,nlatt,"create_atoms:next");
    ranlatt->select_subset(nsubset,nlatt,flag,next);
    loop_lattice(INSERT_SELECTED);
    memory->destroy(flag);
    memory->destroy(next);
  }
}

/* ----------------------------------------------------------------------
   iterate on 3d periodic lattice of unit cells using loop bounds
   iterate on nbasis atoms in each unit cell
   convert lattice coords to box coords
   check if lattice point meets all criteria to be added
   perform action on atom or molecule (on each basis point) if meets all criteria
   actions = add, count, add if flagged
------------------------------------------------------------------------- */

void CreateAtoms::loop_lattice(int action)
{
  int i,j,k,m;

  // one pass for default mode, two passes for subset mode:
  // first pass: count how many particles will be inserted
  // second pass: filter to N number of particles (and insert)
  int iflag = 0;
  int npass = 1;
  if (subsetflag) npass = 2;
  for (int pass = 0; pass < npass; pass++) {
    if (pass == 1) get_subset();
  const double * const * const basis = domain->lattice->basis;

  nlatt = 0;

  for (k = klo; k <= khi; k++) {
    for (j = jlo; j <= jhi; j++) {
      for (i = ilo; i <= ihi; i++) {
@@ -861,136 +920,31 @@ void CreateAtoms::add_lattice()
              coord[1] < sublo[1] || coord[1] >= subhi[1] ||
              coord[2] < sublo[2] || coord[2] >= subhi[2]) continue;
          
            // add the atom or entire molecule to my list of atoms
            if (subsetflag && pass == 0) nlatt++;
            else {
              if (!subsetflag || flag[iflag++] == 1)
          // this proc owns the lattice site
          // perform action: add, just count, add if flagged
          // add = add an atom or entire molecule to my list of atoms

          if (action == INSERT) {
            if (mode == ATOM) atom->avec->create_atom(basistype[m],x);
            else if (quatone[0] == 0 && quatone[1] == 0 && quatone[2] == 0)
              add_molecule(x);
            else add_molecule(x,quatone);
          } else if (action == COUNT) {
            if (nlatt == MAXSMALLINT) nlatt_overflow = 1;
          } else if (action == INSERT_SELECTED && flag[nlatt]) {
            if (mode == ATOM) atom->avec->create_atom(basistype[m],x);
            else if (quatone[0] == 0 && quatone[1] == 0 && quatone[2] == 0)
              add_molecule(x);
            else add_molecule(x,quatone);
            }
          }
        }
      }
    }
  }
}

/* ----------------------------------------------------------------------
   define a random mask to insert only N particles on lattice
------------------------------------------------------------------------- */

void CreateAtoms::get_subset()
{
  enum{ATOMS,HOLES};
  int i,j,temp,irand,offlag,npicks,pickmode,mynpicks,mysubset;
  double myrand;
  int *allnlatts;
  int *allsubsets;
  int *localpicks;
  double *proc_sects;

  memory->create(allnlatts,nprocs,"create_atoms:allnlatts");
  memory->create(allsubsets,nprocs,"create_atoms:allsubsets");
  memory->create(localpicks,nprocs,"create_atoms:localpicks");
  memory->create(proc_sects,nprocs,"create_atoms:proc_sects");

  MPI_Allgather(&nlatt, 1, MPI_INT, &allnlatts[0], 1, MPI_INT, world);

  int ntotal = 0;
  for (i = 0; i < nprocs; i++)
    ntotal += allnlatts[i];

  if (nsubset > ntotal)
     error->all(FLERR,"Attempting to insert more particles than available lattice points");

  // define regions of unity based on a proc's fraction of total lattice points
  proc_sects[0] = (double) allnlatts[0] / (double) ntotal;
  for (i = 1; i < nprocs; i++)
    proc_sects[i] = proc_sects[i-1] + (double) allnlatts[i] / (double) ntotal;

  if (nsubset > ntotal/2) {
    pickmode = HOLES;
    npicks = ntotal - nsubset;
  } else {
    pickmode = ATOMS;
    npicks = nsubset;
  }

  mynpicks = npicks/nprocs;
  if (me == 0) mynpicks = npicks - (nprocs-1)*(mynpicks);

  for (i = 0; i < nprocs; i++)
    localpicks[i] = 0;

  for (i = 0; i < mynpicks; i++) {
    myrand = ranlatt->uniform();
    for (j = 0; j < nprocs; j++)
      if (myrand < proc_sects[j]) {
        localpicks[j]++;
        break;
      }
  }

  MPI_Allreduce(&localpicks[0],&allsubsets[0],nprocs,MPI_INT,MPI_SUM,world);

  if (pickmode == HOLES)
    for (i = 0; i < nprocs; i++)
      allsubsets[i] = allnlatts[i] - allsubsets[i];

  mysubset = allsubsets[me];

  // it's possible, but statistically unlikely, that a proc was assigned too many
  // proc 0 will fix this
  offlag = 0;
  npicks = 0;
  for (i = 0; i < nprocs; i++) {
    if (allsubsets[i] > allnlatts[i]) {
      offlag = 1;
      npicks += allsubsets[i] - allnlatts[i];
    }
          }

  if (offlag == 1) {
    if (me == 0) {
      while (npicks > 0) { // while loop
        myrand = ranlatt->uniform();
        for (j = 0; j < nprocs; j++)
          if (myrand < proc_sects[j]) break;
        if (allsubsets[j] < allnlatts[j]) {
          allsubsets[j]++;
          npicks--;
          nlatt++;
        }
      }
    }
    MPI_Scatter(&allsubsets[0], 1, MPI_INT, &mysubset, 1, MPI_INT, 0, world);
  }

  // each processor chooses its random lattice points
  memory->create(flag,nlatt,"create_atoms:flag");

  for (i = 0; i < nlatt; i++)
    if (i < mysubset)
      flag[i] = 1;
    else
      flag[i] = 0;

  // shuffle filled lattice points
  for (i = nlatt-1; i > 0; --i) {
    irand = floor(ranlatt->uniform()*(i+1));
    temp = flag[i];
    flag[i] = flag[irand];
    flag[irand] = temp;
  }

  memory->destroy(allnlatts);
  memory->destroy(allsubsets);
  memory->destroy(localpicks);
  memory->destroy(proc_sects);
}


/* ----------------------------------------------------------------------
   add a randomly rotated molecule with its center at center
   if quat_user set, perform requested rotation
Loading