Particle Sub Group#
Introduction#
A “ParticleSubGroup” is a wrapper class around a ParticleGroup which selects a subset of the particles in the ParticleGroup. A particle sub group may be passed to a ParticleLoop to define the iteration set for the ParticleLoop.
Creation#
In the listing below we demonstrate the creation of a ParticleSubGroup which is defined as the subset of particles in a ParticleGroup which have an even integer in the first component of a ParticleDat called “ID”. This subset selection is specified by the user by providing a kernel lambda. The creation of the sub group is automatically performed as required by objects which accept a ParticleSubGroup as input.
This subset selection procedure is automatically performed each time an up-to-date ParticleSubGroup is required. i.e. users may update the particle properties used in the subset selection process, or move particles between cells and MPI ranks, and expect the ParticleSubGroup to be updated to reflect these changes. This automatic reevaluation process assumes that particle data access is through the methods provided by NESO-Particles. If these data access methods are circumvented to modify particle data then no guarantee is given that a ParticleSubGroup correctly represents the desired subset of particles.
inline void particle_sub_group_creation(
// Input ParticleGroup - we will select a subset of the particles in this
// group.
ParticleGroupSharedPtr particle_group
) {
// Create new sub group
auto sub_group = particle_sub_group(
// The source ParticleGroup
particle_group,
// The second argument is a lambda which follows the rules of ParticleLoop
// kernel lambdas with the exception that this lambda returns true for
// particles which are in the sub group and false for particles which are
// not. This lambda may only access particle data and may only access
// particle data with read-only access descriptors.
[=](auto ID) {
return (ID[0] % 2 == 0);
},
// The remaining arguments are the ParticleLoop compliant arguments for the
// kernel with selects particles.
Access::read(Sym<INT>("ID"))
);
// Create an additional sub group from an existing sub group.
auto sub_group1 = particle_sub_group(
sub_group,
[=](auto ID) {
return (ID[0] % 4 == 0);
},
Access::read(Sym<INT>("ID"))
);
return;
}
Particle Loop#
A ParticleSubGroup is a valid iteration set for a ParticleLoop. In the following example we execute a particle loop over all particles with an even ID.
inline void particle_sub_group_loop(
ParticleGroupSharedPtr particle_group
) {
// Create a ParticleSubGroup from even values of ID.
auto sub_group = particle_sub_group(
particle_group,
[=](auto ID) {
return (ID[0] % 2 == 0);
},
Access::read(Sym<INT>("ID"))
);
// Perform a position update style kernel on particles with even values of
// ID[0].
auto loop = particle_loop(
sub_group,
[=](auto V, auto P){
P[0] += 0.001 * V[0];
P[1] += 0.001 * V[1];
},
Access::read(Sym<REAL>("V")),
Access::write(Sym<REAL>("P"))
);
loop->execute();
return;
}
Optimisation#
The particle sub group creation function we describe above is very general and hence the implementation assumes that there is no underlying structure to the sub group which can be used to optimise the creation of the internal representation. If users can provide additional information about the structure of a sub group to NP then this additional structure information can be used to optimise the underlying creation of the sub group.
We expose these more optimal implementations through additional free functions which all create sub groups for specific structures of selection. For example there is a sub group creation function that selects all particles between two given cell indices. The use of all particle sub groups is identical with the exception of sub groups which are simple references to the entire particle group. This special case is only of relevance to developers intending to interface with NP components at a lower level that the highest level components. We make this optimisation as if a sub group simply references an entire particle group then the implementation can simply call the corresponding implementation for whole particle groups.
inline void particle_sub_group_creation_specific(
// Input ParticleGroup - we will select a subset of the particles in this
// group.
ParticleGroupSharedPtr particle_group
) {
/**
* Create a naive copy of the ParticleGroup using the base sub group
* function.
*/
auto a0 = particle_sub_group(particle_group, [=](){return true;});
/**
* Creates a copy of a0.
*/
auto a1 = particle_sub_group(a0);
/**
* Creates a lightweight reference to the whole particle group.
*/
auto a2 = particle_sub_group(particle_group);
/**
* Creates a copy of a2. As a2 is a ParticleGroup this also creates a
* lightweight reference.
*/
auto a3 = particle_sub_group(a2);
/**
* Select all particles for all cells within a cell range from a
* ParticleGroup.
*/
auto c0 = particle_sub_group(particle_group, 0, 1);
/**
* Select all particles for all cells within a cell range from a
* ParticleSubGroup.
*/
auto c1 = particle_sub_group(a0, 0, 1);
/**
* Discard n particles then select any remaining particles from a particle
* group.
*/
const int n = 4;
auto d0 = particle_sub_group_discard(particle_group, n);
/**
* Discard n particles then select any remaining particles from a particle
* sub group.
*/
auto d1 = particle_sub_group_discard(d0, n);
/**
* Select only the first, at most, n particles from each cell from the source
* ParticleGroup.
*/
auto t0 = particle_sub_group_truncate(particle_group, n);
/**
* Select only the first, at most, n particles from each cell from the source
* ParticleGroup.
*/
auto t1 = particle_sub_group_truncate(a0, n);
}