Merge pull request #3256 from lammps/delete-atoms-exact

Delete atoms exactly by fraction or count
This commit is contained in:
Axel Kohlmeyer
2022-05-17 16:25:21 -04:00
committed by GitHub
4 changed files with 196 additions and 44 deletions

View File

@ -10,7 +10,7 @@ Syntax
delete_atoms style args keyword value ...
* style = *group* or *region* or *overlap* or *porosity* or *variable*
* style = *group* or *region* or *overlap* or *random* or *variable*
.. parsed-literal::
@ -20,11 +20,17 @@ Syntax
cutoff = delete one atom from pairs of atoms within the cutoff (distance units)
group1-ID = one atom in pair must be in this group
group2-ID = other atom in pair must be in this group
*porosity* args = group-ID region-ID fraction seed
*random* args = ranstyle value eflag group-ID region-ID seed
ranstyle = *fraction* or *count*
for *fraction*:
value = fraction (0.0 to 1.0) of eligible atoms to delete
eflag = *no* for fast approximate deletion, *yes* for exact deletion
for *count*:
value = number of atoms to delete
eflag = *no* for warning if count > eligible atoms, *yes* for error
group-ID = group within which to perform deletions
region-ID = region within which to perform deletions
or NULL to only impose the group criterion
fraction = delete this fraction of atoms
seed = random number seed (positive integer)
*variable* args = variable-name
@ -46,16 +52,17 @@ Examples
delete_atoms region sphere compress no
delete_atoms overlap 0.3 all all
delete_atoms overlap 0.5 solvent colloid
delete_atoms porosity all cube 0.1 482793 bond yes
delete_atoms porosity polymer cube 0.1 482793 bond yes
delete_atoms random fraction 0.1 yes all cube 482793 bond yes
delete_atoms random fraction 0.3 no polymer NULL 482793 bond yes
delete_atoms random count 500 no ions NULL 482793
detele_atoms variable checkers
Description
"""""""""""
Delete the specified atoms. This command can be used to carve out
voids from a block of material or to delete created atoms that are too
close to each other (e.g. at a grain boundary).
Delete the specified atoms. This command can be used, for example, to
carve out voids from a block of material or to delete created atoms
that are too close to each other (e.g. at a grain boundary).
For style *group*, all atoms belonging to the group are deleted.
@ -81,17 +88,33 @@ have occurred that no atom pairs within the cutoff will remain
minimum number of atoms will be deleted, or that the same atoms will
be deleted when running on different numbers of processors.
For style *porosity* a specified *fraction* of atoms are deleted which
are both in the specified group and within the specified region. The
region-ID can be specified as NULL to only impose the group criterion.
Likewise, specifying the group-ID as *all* will only impose the region
criterion.
For style *random* a subset of eligible atoms are deleted. Which
atoms to delete are chosen randomly using the specified random number
*seed*. Which atoms are deleted may vary when running on different
numbers of processors.
For example, if fraction is 0.1, then 10% of the eligible atoms will
be deleted. The atoms to delete are chosen randomly. There is no
guarantee that the exact fraction of atoms will be deleted, or that
the same atoms will be deleted when running on different numbers of
processors.
For *ranstyle* = *fraction*, the specified fractional *value* (0.0 to
1.0) of eligible atoms are deleted. If *eflag* is set to *no*, then
the number of deleted atoms will be approximate, but the operation
will be fast. If *eflag* is set to *yes*, then the number deleted
will match the requested fraction, but for large systems the selection
of deleted atoms may take additional time to determine.
For *ranstyle* = *count*, the specified integer *value* is the number
of eligible atoms are deleted. If *eflag* is set to *no*, then if the
requested number is larger then the number of eligible atoms, a
warning is issued and only the eligible atoms are deleted instead of
the requested *value*. If *eflag* is set to *yes*, an error is
triggered instead and LAMMPS will exit. For large systems the
selection of atoms to delete may take additional time to determine,
the same as for requesting an exact fraction with *pstyle* =
*fraction*.
Which atoms are eligible for deletion for style *random* is determined
by the specified *group-ID* and *region-ID*. To be eligible, an atom
must be in both the specified group and region. If *group-ID* = all,
there is effectively no group criterion. If *region-ID* is specified
as NULL, no region criterion is imposed.
For style *variable*, all atoms for which the atom-style variable with
the given name evaluates to non-zero will be deleted. Additional atoms
@ -100,6 +123,10 @@ were deleted within the region; see the *mol* keyword discussion below.
This option allows complex selections of atoms not covered by the
other options listed above.
----------
Here is the meaning of the optional keywords.
If the *compress* keyword is set to *yes*, then after atoms are
deleted, then atom IDs are re-assigned so that they run from 1 to the
number of atoms in the system. Note that this is not done for

View File

@ -40,6 +40,8 @@
using namespace LAMMPS_NS;
enum { UNKNOWN, FRACTION, COUNT };
/* ---------------------------------------------------------------------- */
DeleteAtoms::DeleteAtoms(LAMMPS *lmp) : Command(lmp) {}
@ -71,9 +73,14 @@ void DeleteAtoms::command(int narg, char **arg)
delete_region(narg, arg);
else if (strcmp(arg[0], "overlap") == 0)
delete_overlap(narg, arg);
else if (strcmp(arg[0], "porosity") == 0)
delete_porosity(narg, arg);
else if (strcmp(arg[0], "variable") == 0)
else if (strcmp(arg[0], "random") == 0)
delete_random(narg, arg);
// deprecated porosity option, now included in new partial option
else if (strcmp(arg[0], "porosity") == 0) {
error->all(FLERR,
"The delete_atoms 'porosity' keyword has been removed.\n"
"Please use: delete_atoms random fraction frac exact group-ID region-ID seed\n");
} else if (strcmp(arg[0], "variable") == 0)
delete_variable(narg, arg);
else
error->all(FLERR, "Unknown delete_atoms sub-command: {}", arg[0]);
@ -412,25 +419,46 @@ void DeleteAtoms::delete_overlap(int narg, char **arg)
}
/* ----------------------------------------------------------------------
create porosity by deleting atoms in a specified region
delete specified portion of atoms within group and/or region
------------------------------------------------------------------------- */
void DeleteAtoms::delete_porosity(int narg, char **arg)
void DeleteAtoms::delete_random(int narg, char **arg)
{
if (narg < 5) utils::missing_cmd_args(FLERR, "delete_atoms porosity", error);
if (narg < 7) utils::missing_cmd_args(FLERR, "delete_atoms random", error);
int igroup = group->find(arg[1]);
if (igroup == -1) error->all(FLERR, "Could not find delete_atoms porosity group ID {}", arg[1]);
int random_style = UNKNOWN;
bool exactflag = false;
bool errorflag = false;
bigint ncount = 0;
double fraction = 0.0;
auto iregion = domain->get_region_by_id(arg[2]);
if (!iregion && (strcmp(arg[2], "NULL") != 0))
error->all(FLERR, "Could not find delete_atoms porosity region ID {}", arg[2]);
if (strcmp(arg[1], "fraction") == 0) {
random_style = FRACTION;
fraction = utils::numeric(FLERR, arg[2], false, lmp);
exactflag = utils::logical(FLERR, arg[3], false, lmp);
if (fraction < 0.0 || fraction > 1.0)
error->all(FLERR, "Delete_atoms random fraction has invalid value: {}", fraction);
} else if (strcmp(arg[1], "count") == 0) {
random_style = COUNT;
ncount = utils::bnumeric(FLERR, arg[2], false, lmp);
errorflag = utils::logical(FLERR, arg[3], false, lmp);
if (ncount < 0) error->all(FLERR, "Delete_atoms random count has invalid value: {}", ncount);
exactflag = true;
} else {
error->all(FLERR, "Unknown delete_atoms random style: {}", arg[1]);
}
double porosity_fraction = utils::numeric(FLERR, arg[3], false, lmp);
int seed = utils::inumeric(FLERR, arg[4], false, lmp);
options(narg - 5, &arg[5]);
int igroup = group->find(arg[4]);
if (igroup == -1) error->all(FLERR, "Could not find delete_atoms random group ID {}", arg[4]);
auto random = new RanMars(lmp, seed + comm->me);
auto region = domain->get_region_by_id(arg[5]);
if (!region && (strcmp(arg[5], "NULL") != 0))
error->all(FLERR, "Could not find delete_atoms random region ID {}", arg[5]);
int seed = utils::inumeric(FLERR, arg[6], false, lmp);
options(narg - 7, &arg[7]);
auto ranmars = new RanMars(lmp, seed + comm->me);
// allocate and initialize deletion list
@ -438,21 +466,84 @@ void DeleteAtoms::delete_porosity(int narg, char **arg)
memory->create(dlist, nlocal, "delete_atoms:dlist");
for (int i = 0; i < nlocal; i++) dlist[i] = 0;
// delete fraction of atoms which are in both group and region
// setup
double **x = atom->x;
int *mask = atom->mask;
int groupbit = group->bitmask[igroup];
if (iregion) iregion->prematch();
if (region) region->prematch();
for (int i = 0; i < nlocal; i++) {
if (!(mask[i] & groupbit)) continue;
if (iregion && !iregion->match(x[i][0], x[i][1], x[i][2])) continue;
if (random->uniform() <= porosity_fraction) dlist[i] = 1;
// delete approximate fraction of atoms in both group and region
if (random_style == FRACTION && !exactflag) {
for (int i = 0; i < nlocal; i++) {
if (!(mask[i] & groupbit)) continue;
if (region && !region->match(x[i][0], x[i][1], x[i][2])) continue;
if (ranmars->uniform() <= fraction) dlist[i] = 1;
}
// delete exact fraction or count of atoms in both group and region
} else {
double **x = atom->x;
int *mask = atom->mask;
// count = number of atoms this proc owns in both group and region
int count = 0;
for (int i = 0; i < nlocal; i++) {
if (!(mask[i] & groupbit)) continue;
if (region && !region->match(x[i][0], x[i][1], x[i][2])) continue;
count++;
}
// convert specified fraction to ncount
bigint bcount = count;
bigint allcount;
MPI_Allreduce(&bcount, &allcount, 1, MPI_LMP_BIGINT, MPI_SUM, world);
if (random_style == FRACTION) {
ncount = static_cast<bigint>(fraction * allcount);
} else if (random_style == COUNT) {
if (ncount > allcount) {
auto mesg = fmt::format("Delete_atoms count of {} exceeds number of eligible atoms {}",
ncount, allcount);
ncount = allcount;
if (errorflag) {
error->all(FLERR, mesg);
} else {
if (comm->me == 0) error->warning(FLERR, mesg);
}
}
}
// make selection
int *flag = memory->create(flag, count, "delete_atoms:flag");
int *work = memory->create(work, count, "delete_atoms:work");
ranmars->select_subset(ncount, count, flag, work);
// set dlist for atom indices in flag
// flag vector from select_subset() is only for eligible atoms
int j = 0;
for (int i = 0; i < nlocal; i++) {
if (!(mask[i] & groupbit)) continue;
if (region && !region->match(x[i][0], x[i][1], x[i][2])) continue;
if (flag[j]) dlist[i] = 1;
j++;
}
memory->destroy(flag);
memory->destroy(work);
}
delete random;
// delete RN generator
delete ranmars;
}
/* ----------------------------------------------------------------------

View File

@ -38,7 +38,7 @@ class DeleteAtoms : public Command {
void delete_group(int, char **);
void delete_region(int, char **);
void delete_overlap(int, char **);
void delete_porosity(int, char **);
void delete_random(int, char **);
void delete_variable(int, char **);
void delete_bond();

View File

@ -70,8 +70,10 @@ protected:
command("region left block -2.0 -1.0 INF INF INF INF");
command("region right block 0.5 2.0 INF INF INF INF");
command("region top block INF INF -2.0 -1.0 INF INF");
command("region bottom block INF INF 0.0 4.0 INF INF");
command("set region left type 2");
command("set region right type 3");
command("group bottom region bottom");
command("group top region top");
END_HIDE_OUTPUT();
}
@ -109,19 +111,51 @@ TEST_F(DeleteAtomsTest, Simple)
ASSERT_EQ(atom->natoms, 392);
HIDE_OUTPUT([&] {
command("delete_atoms porosity all right 0.5 43252");
command("delete_atoms random fraction 0.5 yes all right 43252");
});
ASSERT_EQ(atom->natoms, 362);
ASSERT_EQ(atom->natoms, 364);
HIDE_OUTPUT([&] {
command("variable checker atom sin(4*PI*x/lx)*sin(4*PI*y/ly)*sin(4*PI*z/lz)>0");
command("delete_atoms variable checker");
});
ASSERT_EQ(atom->natoms, 177);
ASSERT_EQ(atom->natoms, 178);
HIDE_OUTPUT([&] {
command("delete_atoms random count 3 no bottom right 443252");
});
ASSERT_EQ(atom->natoms, 175);
HIDE_OUTPUT([&] {
command("delete_atoms random count 50 no all NULL 434325");
});
ASSERT_EQ(atom->natoms, 125);
HIDE_OUTPUT([&] {
command("delete_atoms random fraction 0.2 no all NULL 34325");
});
ASSERT_EQ(atom->natoms, 104);
HIDE_OUTPUT([&] {
command("delete_atoms random count 50 no bottom right 77325");
});
ASSERT_EQ(atom->natoms, 102);
TEST_FAILURE(".*ERROR: Illegal delete_atoms command: missing argument.*",
command("delete_atoms"););
TEST_FAILURE(".*ERROR: Unknown delete_atoms sub-command: xxx.*", command("delete_atoms xxx"););
TEST_FAILURE(".*ERROR: The delete_atoms 'porosity' keyword has been removed.*",
command("delete_atoms porosity 0.5 all right 4325234"););
TEST_FAILURE(".*ERROR: Illegal delete_atoms random command: missing argument.*",
command("delete_atoms random count 50 bottom right 77325"););
TEST_FAILURE(".*ERROR: Illegal delete_atoms random command: missing argument.*",
command("delete_atoms random fraction 0.4 bottom right 77325"););
TEST_FAILURE(".*ERROR: Delete_atoms random count has invalid value: -5.*",
command("delete_atoms random count -5 no bottom right 77325"););
TEST_FAILURE(".*ERROR: Delete_atoms count of 5 exceeds number of eligible atoms 0.*",
command("delete_atoms random count 5 yes bottom right 77325"););
TEST_FAILURE(".*ERROR: Delete_atoms random fraction has invalid value: -0.4.*",
command("delete_atoms random fraction -0.4 no bottom right 77325"););
}
} // namespace LAMMPS_NS