Skip to content

Coding guidelines

Raul edited this page Feb 18, 2021 · 1 revision

NOTES FOR DEVELOPERS

The procedure to implement a new module is the following:

1. Create a new class that inherits from one of the parents (Interactor, Integrator...) and overload the virtual methods. You can do whatever you want as long as the virtual methods are overloaded.   
2. Take as input shared_ptr's to a ParticleData and a System at least, use them to interface with UAMMD (ask ParticleData for properties like pos, force, torque..)
3. If the new module needs a new particle property (i.e torque) include it in ParticleData.cuh ALL_PROPERTIES_LIST macro
4. If the new module needs to communicate a new parameter change to all modules (i.e it changes the simulation box with time) include it in ParameterUpdatable.cuh  PARAMETER_LIST macro	
5. Include the new module in the source file that makes use of it

See available modules for a tutorial (i.e PairForces.cuh or VerletNVT.cuh)

Some things to take into account:

1. ParticleData can regularly update the particle order and/or the number of particles, it will communicate this changes through signals. See ParticleData.cuh for a tutorial on how to connect and handle a signal.
2. ParticleData can also change the storage location of the particle arrays, so do not store raw pointers to particle properties, always ask PD for them before using them with ParticleData::get*()
3. In the modules where it makes sense, make them be able to take a ParticleGroup (which will contain all particles by default). See PairForces.cuh for an example of a module handling ParticleGroups. Groups will handle particle reorders and particle number changes, easing working with variable number of particles. A ParticleGroup containing all particles yields no overhead and has a very small memory footprint.  
4. UAMMD usually uses the lazy initialization scheme, nothing is initialized unless it is absolutely necessary. For example, the CPU version of a particle property (and the GPU version FWIW) will not be allocated until someone explicitly asks for it with pd->get*().  
5. Using the "real" type and "make_real" type will ensure precision agnostic code, as real is an alias to either float or double depending on the precision mode.  

Some advice:

1. Make use of the existing modules and submodules when possible, inherit from them if you need an extra level of control. For example with a neighbourList.
2. Use cub/thrust when possible, it is unusual to write an actual kernel. Most times a thrust::transform of thrust::for_each will do the trick.
3. When constructing a new kind of simulation compile the modules in one file and compile another separate one for using the first (to reduce compilation time), or better yet make the code read all needed parameters from a file or script using InputFile.
4. Use the iterator concept whenever possible.  

In the creation of a new module (Interactor or Integrator) for interoperability with the already existing modules, the code expects you to use the variables from ParticleData when available, the containers storing the positions, forces, velocities... of each particle.
These containers start with zero size and are initialized by ParticleData the first time they are asked for.

Guidelines

Each module should be under the uammd namespace. And if helper functions are needed which are not available in UAMMD, they should be under another, module specific, namespace (usually called detail, they can later be introduced to the code base).
If you want to make small changes to an existing module without changing it you should create a new module that inherits it, and overload the necessary functions or just copy it.









Clone this wiki locally