ParticleData
One of the basic assumptions in UAMMD is that simulations are based on the state of “particles”. ParticleData is the class in UAMMD that stores the properties of all the particles in the system.
ParticleData exposes the following API:
Warning
doxygenclass: Cannot find class “uammd::ParticleData” in doxygen xml output for project “uammd” from directory: doxygen/xml
Example: creating a ParticleData instance
auto pd = make_shared<ParticleData>(numberParticles);
Getting a handle get a certain property
You can access a property via ParticleData in GPU or CPU memory. You must specify the kind of access (read, write, readwrite).
-
struct access
Access modes for memory resources.
This struct defines the different access modes for memory resources in UAMMD. It includes locations (CPU, GPU, managed) and modes (read, write, read/write).
Note
The enumerators access::location and access::mode can be used without the second scope. In other words, you can write access::gpu instead of access::location::gpu.
The type returned by ParticleData::getProperty is a lightweight standard-library-like pseudo-container defined as
-
template<class T>
class property_ptr : public thrust::iterator_adaptor<property_ptr<T>, T*> A random-access iterator over a property array.
This class is a thin wrapper around a raw pointer to an array of elements of type
T. It behaves like a Thrust-compatible iterator, enabling use in both host and device code. Additionally, it tracks ownership and access metadata to help coordinate memory operations, especially in CUDA environments.- Template Parameters:
T – The type of the elements the pointer refers to.
Public Functions
-
__host__ __device__ inline T *raw() const
Get the raw pointer to the underlying data.
- Returns:
A raw pointer to the data.
-
__host__ __device__ inline Iterator end() const
Returns an iterator to the end of the data.
- Returns:
Iterator pointing past the last element.
-
__host__ __device__ inline Iterator begin() const
Returns an iterator to the beginning of the data.
- Returns:
Iterator pointing to the first element.
-
__host__ __device__ inline size_t size() const
Get the number of elements in the property.
- Returns:
The number of elements.
-
inline access::location location() const
Get the location of the data (host or device).
- Returns:
The access::location value.
Example
auto pd = std::make_shared<ParticleData>(numberParticles);
auto radius = pd->getRadius(access::gpu, access::write);
thrust::fill(thrust::cuda::par, radius.begin(), radius.end(), 1.0);
auto force = pd->getForce(access::cpu, access::write);
std::fill(force.begin(), force.end(), real4());
auto id = pd->getId(access::cpu, access::read); //It is not legal to write to ID, one can only read from it.
int* raw_id_property_pointer = id.raw();
If the mode is set to write, the handle will gain exclusivity and no one else will be able to access it until it is released (the handle is deleted).
Note
There is no real difference between access::write and access::readwrite (at the moment) beyond informing the reader of the intention of modifying the contents (readwrite) vs ignoring the current contents and overwriting (write).
Warning
UAMMD cannot write to a property that is currently being read and cannot read from a property that is currently being written to. It is important to control the scope of the property handles, deleting them as soon as possible and never storing them (store a pointer to ParticleData instead).
Handles are compatible with std and thrust algorithms and can be considered c++ iterators for all intents and purposes.
List of available properties
The beginning of ParticleData.cuh contains a list of available per particle properties (such as positons, velocities, forces…).
You can see a list of all the available ones and add more properties by appending to the macro ALL_PROPERTIES_LIST.
A family of access functions will be autogenerated for each property inside this macro (such as get[Name] (), [Name]WrittenSignal(), …).
For instance, ParticleData holds the positions of the particles in real4 variables in an array named “pos”. Thus, the function property_ptr<real4> ParticleData::getPos() is available.
- Basic properties include (type name):
real4 posreal3 velreal4 forcereal energyreal virialreal massreal chargeAnd more defined in ParticleData.cuh
-
ALL_PROPERTIES_LIST
List of all properties available in ParticleData.
This macro is used to generate the list of all properties available in ParticleData. It is used to generate getter functions and other related functionality.
You can add your own properties by adding them to the EXTRA_PARTICLE_PROPERTIES macro.
The format is:
((PropertyName, propertyName, TYPE))where TYPE is the type of the property (e.g. real4 , int , real , etc.). The property name will be used to generate the getter functions, so it must be a valid C++ type.
Particle id assignation
When added each particle is assigned an unique id or name (which corresponds to its position in the underlying container just after ParticleData creation). At this moment one can access the position of particle with id=i at pos[i]. A reordering or some other internal processes may alter this fact, making the index of the particle with id=i not correspond to i anymore. While the location of each particle in the internal containers might change, the particles ids (names) will never change. The current ids are available through the property “Id” in ParticleData:
auto index2id = pd->getId(access::cpu, access::read);
int someIndex=0;
int nameOfParticleAtSomeIndex = index2id[someIndex];
The opposite indirection is also accessible through ParticleData. That is finding the current index of a certain particle through its id (name):
-
const int *ParticleData::getIdOrderedIndices(access::location loc);
Returns an array with memory residing at the given location with the current indices of the particles given their id.
auto id2index = pd->getIdOrderedIndices(access::cpu);
int someId=0;
int indexOfParticleWithSomeId = id2index[someId];
Thrust offers a permutation_iterator that can be used to mask this behavior to access a certain property by either id or index:
auto positionWithArbitraryOrder = pd->getPos(access::cpu, access::read);
//Accessing particles when order is not important
int someIndex = 0;
real4 positionOfParticleAtSomeIndex = pos[someIndex];
auto index2id = pd->getId(access::cpu, access::read);
int idOfParticleAtSomeIndex = index2id[someIndex];
//Accessing particles so index = name
int someId = 0;
auto id2Index = pd->getIdOrderedIndices(access::cpu);
//Using a simple indirection
real4 positionOfParticleWithSomeId = positionWithArbitraryOrder[id2index[someId]];
//Using a permutation iterator
auto positionOrderedById = thrust::make_permutation_iterator(positionWithArbitraryOrder, id2index);
real4 positionOfParticleWithSomeId = positionOrderedById[someId];
Advanced usage
Triggering a sorting
One can trigger a global sorting of the particles in ParticleData by calling ParticleData::sortParticles(). This will reorder the particles in the internal containers and update the indices accordingly.
Signals
ParticleData broadcasts a signal every time some internal processes undergo, such as a particle reordering or a resize. One can subscribe to these signals like this:
class User{
connection reorderConnection, numParticlesChangedConnection;
public:
User(std::shared_ptr<ParticleData> pd){
reorderConnection = pd->getReorderSignal()-> connect([this](){this->handle_reorder();});
numParticlesChangedConnection = pd->getNumParticlesChangedSignal()->connect([this](int Nnew){this->handle_numChanged(Nnew);});
}
~User(){
reorderConnection.disconnect();
numParticlesChangedConnection.disconnect();
}
void handle_reorder(){
std::cout<<"A reorder occured!!"<std::endl;
}
void handle_numChanged(int Nnew){
std::cout<<"Particle number changed, now it is: "<<Nnew<<std::endl;
}
};
Note that it is possible that a module does not need to track the specific order of the particles or do anything special when the number of them changes. See for example NbodyForces or PairForces. Actually, most of the time you will get away without needing to connect to the signals.
UAMMD uses the signal/connection classes from fr00b0/nod.
-
template<class Function>
using signal = nod::unsafe_signal<Function> UAMMD’s signal class. Must be specialized with a function signature, for instance
using non_broadcasting_signal = signal<void()>.
-
using connection = nod::connection
Keeps track of an open signal connection. Its main use is to be able to safely detach from a signal via
connection::disconnect()
A list of available signals is available in the ParticleData class documentation.
Saving and restoring from a file
Writes all allocated properties in
pdto the file (overwrites if existing);
Loads the
ParticleDatainstance stored bysaveParticleDatain the filefileName.