ParticleGroup
Most UAMMD modules can be instructed to act only on a subset of particles, this is handled by creating a ParticleGroup. A group can contain all particles, no particles or anything in between.
-
class ParticleGroup
Keeps track of a subset of particles which properties reside in
ParticleData.-
std::shared_ptr<ParticleData> getParticleData()
Get the instance of
ParticleDatatheParticleGroupworks on.
-
void clear();
Remove all particles from the group.
-
void addParticlesById(access::location loc, const int *ids, int N);
Add particles to the group via an array (with memory residing according to
loc) of particle ids.
-
void addParticlesByCurrentIndex(access::location loc, const int *indices, int N);
Add particles to the group via an array with the current indices of the particles in ParticleData (this is faster than
addParticlesById()).
-
const int *getIndicesRawPtr(access::location loc);
Get a raw memory pointer to a list with the indices of the particles in this group. Returns a null pointer if all or none particles are in the group.
-
IndexIterator getIndexIterator(access::location loc);
Get an iterator with the indices of particles in this group.
-
const int *getGroupIndexMaskRawPtr(access::location loc);
Get an int pointer of size pd->getNumParticles() returning 1 if the particle in the particle currently in that index index is part of the group and 0 otherwise. loc specifies the memory location of the underlying array. Returns a null pointer if all or none particles are in the group.
-
MaskIterator getGroupIndexMask(access::location loc);
Same as
getGroupIndexMaskbut will also work with groups containing all particles, returning a constant iterator with a value of 1.
-
template<class Iterator>
accessIterator<Iterator> getPropertyIterator(Iterator property, access::location loc); Returns an iterator that will have size pg->getNumberParticles() and will iterate over the particles in the group.For example, If a group contains only the particle with
id=10, passingpd->getPos(...).begin()to this function will return an iterator so thatiterator[0] = pos[10];and it will take into account any possible reordering of the pos array. The location does not have to be specified if the property argument is aproperty_ptrprovided byParticleData.
-
int getNumberParticles();
Returns the number of particles currently in the group.
-
std::string getName();
Returns the given name of the group.
-
std::shared_ptr<ParticleData> getParticleData()
Hint
ParticleGroup does not always create an actual list of particles. The iterator returned by getPropertyIterator takes advantage of this
Creation
ParticleGroup exposes several constructors:
This constructor creates a
ParticleGroupcontaining all particles in the providedParticleDatainstance.
Fills the group with the particles according to the provided
ParticleSelector.
Fills the group with the particles ids provided in the iterator range begin:end.
Example
//By default a ParticleGroup will contain all particles
auto allParticlesGroup = make_shared<ParticleGroup>(pd, sys, "AGroupWithAllParticles");
//Different selectors offer different criteria
//In this case, it will result in a group with particles whose ID lies between 4 and 8
particle_selector::IDRange selector(4,8);
auto aGroupWithSomeIDs = make_shared<ParticleGroup>(selector, pd, sys, "SomeName");
//Equivalently a list of particle IDs can be provided directly
auto idrange = std::vector<int>(4); std::iota(idrange.begin(), idrange.end(), 4);
auto anEquivalentGroup = make_shared<ParticleGroup>(idrange.begin(), idrange.end(), pd, sys, "SomeOtherName");
//A group containing all particles of a certain type (or types) (type being the value of pos.w)
auto groupOfParticlesWithType0 = make_shared<ParticleGroup>(particle_selector::Type(0), pd, sys, "Type 0 particles");
auto groupOfParticlesWithType0And3 = make_shared<ParticleGroup>(particle_selector::Type({0,3}), pd, sys, "Type 0 and type 3 particles");
//A group of 10 random particles
std::vector<int> randomlyOrderedIds(numberParticles);
std::iota(randomlyOrderedIds.begin(), randomlyOrderedIds.end(), 0);
std::shuffle(randomlyOrderedIds.begin(), randomlyOrderedIds.end(), std::mt19937{std::random_device{}()});
randomlyOrderedIds.resize(10);
auto groupOf10RandomParticles = make_shared<ParticleGroup>(randomlyOrderedIds.begin(), randomlyOrderedIds.end(), pd, sys, "10 Random Particles");
Instructions on how to create a selector are located in ParticleGroup.cuh but the easiest way to create a group with a custom criteria is to just pass a list of particle ids as in the examples.
Particle selectors
Selectors are small functors providing a member that checks if a given particle should be in a group or not.
-
class ParticleSelector
This is a concept, not a virtual class that must be inherited. Any class defining a member with the signature below will act as a valid selector for
ParticleGroupThis function should use the provided
ParticleDatainstance to decide if the particle with indexparticleIndexshould be included in the group or not.
Important
Selectors are only used for particle inclusion into a group when the group is created. ParticleGroup will not track the changes in the inclusion conditions.
Example
A selector that returns true for every particle.
class All{
public:
All(){}
bool isSelected(int particleIndex, std::shared_ptr<ParticleData> pd){
return true;
}
};
Available particle selector
Creating a group by providing the ids of the relevant particles can be in many cases the most acceptable way of creating a group. However, several selectors are available for convenience under the particle_selector namespace.
-
class particle_selector::All;
Selects all the particles.
-
class particle_selector::None;
Results in an empty group.
-
class particle_selector::IDRange
Select particles with ID in a certain range
-
class particle_selector::Domain
Select particles inside a certain rectangular region of the simulation box.
Usage with UAMMD modules
When it makes sense, UAMMD modules will have an optional ParticleGroup argument at creation. See for example Pair Forces.
General usage
ParticleGroup will keep track of its particles and will always provide their up to date global indices.
//You can request an iterator with the current indices of the particles in a group with:
auto indicesOfParticlesInGroup = pg->getIndexIterator(access::location::gpu);
//Or get a plain array with the indices directly, if it exists.
auto rawMemoryPtrOfIndices = pg->getIndicesRawPtr(access::location::gpu); //or cpu, it will be nullptr if all (or none) particles are in the group
//You can also request an iterator that will read a ParticleData array using the group indices directly.
//This allows to write generic code that will work both with a group or with a ParticleData array.
auto allPositions = pd->getPos(access::location::gpu, access::mode::read);
auto IteratorWithPositionsInGroup = pg->getPropertyIterator(allPositions);
...
//In device code
real4 positionOfFirstParticleInGroup = IteratorWithPositionsInGroup[0];
Hint
As a general rule, when writing UAMMD code, it is wise to access particle properties using ParticleGroups instead of ParticleData directly.
Note
A default group contains all particles, it is a special case and incurs no overhead (besides maybe a couple of registers) when created or used.