Merge pull request #3256 from lammps/delete-atoms-exact
Delete atoms exactly by fraction or count
This commit is contained in:
@ -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
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user