Skip to content
Snippets Groups Projects
user avatar
Behzad Safaei authored
30c5a0ea

P4IRS - Parallel and Performance-Portable Particles Intermediate Representation and Simulator

P4IRS is an open-source, stand-alone compiler and domain-specific language for particle simulations which aims at generating optimized code for different target hardwares. It is released as a Python package and allows users to define kernels, integrators and other particle routines in a high-level and straightforward fashion without the need to implement any backend code.

Usage

To use P4IRS, it is necessary to install it as a Python package and import it with:

import pairs

Particle interactions and specific routines to update each particle individually are defined through Python methods. These can make use of defined properties in the simulation, parameters passed in the compute method and intrinsic methods from P4IRS:

def lennard_jones(i, j):
    sr2 = 1.0 / squared_distance(i, j)
    sr6 = sr2 * sr2 * sr2 * sigma6[i, j]
    apply(force, delta(i, j) * (48.0 * sr6 * (sr6 - 0.5) * sr2 * epsilon[i, j]))


def initial_integrate(i):
    linear_velocity[i] += (dt * 0.5) * force[i] / mass[i]
    position[i] += dt * linear_velocity[i]


def final_integrate(i):
    linear_velocity[i] += (dt * 0.5) * force[i] / mass[i]

After defining the methods, it is necessary to setup the P4IRS simulations:

dt = 0.005
cutoff_radius = 2.5
skin = 0.3
ntypes = 4
sigma = 1.0
epsilon = 1.0
sigma6 = sigma ** 6
nx = 32
ny = 32
nz = 32
rho = 0.8442
temp = 1.44

# Simulation setup
psim = pairs.simulation(
  "md", # Simulation identifier
  [pairs.point_mass()], # List of shapes
  timesteps=200, # Number of time-steps
  double_prec=True) # Use double-precision

# Particle properties
psim.add_position('position')
psim.add_property('mass', pairs.real(), 1.0)
psim.add_property('velocity', pairs.vector())
psim.add_property('force', pairs.vector(), volatile=True)

# Features and their properties
psim.add_feature('type', ntypes)
psim.add_feature_property('type', 'epsilon', pairs.real(), [epsilon for i in range(ntypes * ntypes)])
psim.add_feature_property('type', 'sigma6', pairs.real(), [sigma6 for i in range(ntypes * ntypes)])

# Simulation domain and initial state
psim.copper_fcc_lattice(nx, ny, nz, rho, temp, ntypes)
psim.set_domain_partitioner(pairs.regular_domain_partitioner())
psim.compute_thermo(100)

Then, define the optimization strategies and visualization settings to use:

# Optimization settings
psim.reneighbor_every(20)
psim.compute_half()
psim.build_neighbor_lists(cutoff_radius + skin)
psim.vtk_output("output/md", every=20)

Then, all defined particle routines defined must be scheduled for computation:

# Kernels to compute
psim.compute(lennard_jones, cutoff_radius)
psim.compute(euler, symbols={'dt': dt})

And finally, it is necessary to define the target and trigger the code generator:

# Target hardware
if target == 'gpu':
  psim.target(pairs.target_gpu())
else:
  psim.target(pairs.target_cpu())

psim.generate()

Build instructions

P4IRS can be built in 3 different modes using the CMake build system. Before we demostrate each mode, ensure you have CMake, MPI and CUDA (if targeting GPU execution) available in your environment.

In the following, we assume we have created and navigated to a build directory: mkdir build; cd build

General CMake flags (applicable to all 3 modes):

  • Pass your input script to CMake using -DINPUT_SCRIPT=path/to/script.py
  • Enable CUDA with -DCOMPILE_CUDA=ON
  • Enable support for BlockForest domain partitioning and dynamic load balancing by providing the path to waLBerla source directory -DWALBERLA_DIR=path/to/walberla (TODO: waLBerla as a submodule)

1. Whole-program generation:


In this mode, everything including the main function is generated by P4IRS.

  1. Set generate_whole_program=True in the input script
  2. Set the CMake flag -DGENERATE_WHOLE_PROGRAM=ON

Example: Build md.py

cmake -DINPUT_SCRIPT=../examples/whole-program-generation/md.py -DGENERATE_WHOLE_PROGRAM=ON ..

Now call make and an executable is built.

2. Modular stand-alone app


You can build a stand-alone C++ app which uses the P4IRS modular interface.

  1. Set generate_whole_program=False in the input script
  2. Set the CMake flag -DBUILD_APP=ON
  3. Provide the list of your source files to CMake (semicolon-seperated):-DUSER_SOURCE_FILES=path/to/main.cpp;path/to/helper.cpp

Example: Build the application sd_1.cpp with spring_dashpot.py
Note: In this example we assume waLBerla has been already cloned next to the P4IRS directory.

cmake -DINPUT_SCRIPT=../examples/modular/spring_dashpot.py -DBUILD_APP=ON -DUSER_SOURCE_FILES=../examples/modular/sd_1.cpp -DWALBERLA_DIR=../../walberla ..

Now call make and an executable is built.

3. P4IRS as a library


In this mode, P4IRS is compiled as a library that can be integrated into other projects.

  1. Set generate_whole_program=False in the input script
  2. Ensure both BUILD_APP and GENERATE_WHOLE_PROGRAM are OFF (they are OFF by default)

Configure CMake and call make as usual, and a static library is built. You can then include P4IRS and its dependencies in your build system as follows:

find_package(pairs REQUIRED HINTS "path/to/pairs/build" NO_DEFAULT_PATH)
target_include_directories(my_app PUBLIC ${PAIRS_INCLUDE_DIRS})
target_link_directories(my_app PUBLIC ${PAIRS_LINK_DIRS})
target_link_libraries(my_app PUBLIC ${PAIRS_LINK_LIBRARIES})

Citations

TBD

Credits

P4IRS is developed by the Erlangen National High Performance Computing Center (NHR@FAU) at the University of Erlangen-Nürnberg.

License

MIT