-
Notifications
You must be signed in to change notification settings - Fork 188
Development
Once you have installed the compiler toolchain and the python packages outlined in Tooling, you can start with your first contribution. This page will guide you through the different folders in the ESPResSo source code and explain where to add new features, how to run the tests and how to debug the code.
The source tree has the following structure:
-
src: the actual source code
- utils: C++ utility functions
-
core: C++ source code of the simulation core
- unit_tests: unit tests
- python/espressomd: espressomd Python module and its submodules
- script_interface: C++ source code which links Python classes to functionality in the simulation core
- doc: documentation
- testsuite: integration tests
- samples: Python samples
- libs: external dependencies
-
maintainer: files used by the maintainers
- configs: collection of myconfig.hpp files which activate different sets of features for testing
- benchmarks: collection of benchmarking Python scripts
- CI: support files for the continuous integration testing run on GitLab-CI
The build system of ESPResSo is based on CMake.
The central source files of the build system are the following:
- CMakeLists.txt
- contents of the cmake directory
- the CMakeLists.txt files in the src/, doc/, and testsuite/ directories and their sub-directories
- adding new source files
- adding new external dependencies
To add new files to ESPResSo (like C++ source files or header files) you need to look at the CMakeLists.txt in the directory where the file is located. Please note that .hpp header files usually do not have to be added to CMakeLists.txt. When contributing significant changes, please add integration tests as detailed in Testing.
Make sure to follow the instructions in Software design when developing new features.
To apply the formatting enforced in the CI on your local tree, you can run
pre-commit
You will need the python packages autopep8, pylint, and pycodestyle with the versions given in requirements.txt. These versions are updated every two years to match the versions available in the Ubuntu LTS releases. If you don't have these versions installed, you can install them locally with:
pip3 install --user -c requirements.txt autopep8 pylint pycodestyle
Run the unit tests and the integration tests to make sure you don't accidentally introduce regressions:
make check_unit_tests
make check_python_skip_long
To run these tests in parallel, you can run once the following command to set the maximal number of cores:
cmake . -D ESPRESSO_CTEST_ARGS="-j$(nproc)"
For more details on how to run different types of tests, see the relevant "Running tests" subsections in Testing.
The pypresso script has options to use common debugging tools with ESPResSo. Currently supported are gdb, lldb, valgrind, cuda-gdb, cuda-memcheck. They can be used like
pypresso --TOOL OTHER_ARGS
e.g. to use gdb to debug ESPResSo running my_script.py, you can issue
pypresso --gdb my_script.py
To pass additional arguments, you can do
pypresso --TOOL=ADDITIONAL_ARGUMENTS OTHER_ARGUMENTS
e.g. to use gdb and directly run the program until main, you can use
pypresso --gdb="--exec=start" my_script.py
The folder maintainer/benchmarks/ contains Python scripts for common simulation techniques (P3M algorithm, LJ liquid) and bash scripts to run them for various configurations and commits.
All benchmarks rely on the argparse module to tweak their simulation parameters. Use pypresso --help lj.py to show the list of command line options. Several benchmarks provide a --visualizer option to visualize the simulation box.
The benchmark suite is based on the CTest framework. By default, each simulation will run with MPI using 1, 2, 4, 8 and 16 threads. The timing statistics will be stored as comma-separated values.
Procedure:
- customize the benchmark simulation parameters by editing the python_benchmark() calls in CMakeLists.txt
- enable benchmarks with cmake . -D ESPRESSO_BUILD_BENCHMARKS=ON -D ESPRESSO_TEST_TIMEOUT=3600 -D CMAKE_BUILD_TYPE=Release
- remove slow features from the myconfig.hpp files, such as ADDITIONAL_CHECKS
- run make benchmark
To automatize that task for multiple myconfig files and/or multiple commits, see the next section.
The benchmarks can be executed in a loop over multiple commits and myconfig files. The loop is designed to halt and run a cleanup action at the first error using set -e.
Procedure:
- provide custom CMake options and paths to myconfig files in runner.sh, in particular the test timeout must be large enough for your slowest benchmark
- provide a whitespace-separated list of commits to compare in suite.sh
- customize the benchmark simulation parameters by editing the python_benchmark() calls in CMakeLists.txt
- run bash suite.sh
The timing statistics are written to a CSV file with the following structure:
commit | config | script | arguments | cores | MPI | mean | ci | nsteps | duration | label |
---|---|---|---|---|---|---|---|---|---|---|
42866e9e | minimal.hpp | lj.py | --particles_per_core=1000 --volume_fraction=0.50 | 2 | True | 5.77e-4 | 1.04e-6 | 5000 | 57.7 | |
42866e9e | minimal.hpp | p3m.py | --particles_per_core=1000 --prefactor=4 | 2 | True | 1.23e-2 | 4.81e-5 | 500 | 122.7 | |
42866e9e | default.hpp | lj.py | --particles_per_core=1000 --volume_fraction=0.50 | 2 | True | 5.82e-4 | 3.11e-6 | 5000 | 58.2 | |
42866e9e | default.hpp | p3m.py | --particles_per_core=1000 --prefactor=4 | 2 | True | 1.21e-2 | 2.08e-5 | 500 | 120.6 | |
d810014d | minimal.hpp | lj.py | --particles_per_core=1000 --volume_fraction=0.50 | 2 | True | 3.67e-4 | 2.37e-7 | 5000 | 36.7 | |
d810014d | minimal.hpp | p3m.py | --particles_per_core=1000 --prefactor=4 | 2 | True | 1.02e-2 | 1.15e-5 | 500 | 102.1 | |
d810014d | default.hpp | lj.py | --particles_per_core=1000 --volume_fraction=0.50 | 2 | True | 6.30e-4 | 7.67e-6 | 5000 | 63.0 | |
d810014d | default.hpp | p3m.py | --particles_per_core=1000 --prefactor=4 | 2 | True | 1.19e-2 | 3.19e-5 | 500 | 119.2 | |
0ec5b89c | maxset.hpp | mc.py | --particles_per_core=500 | 1 | False | 2.43e-4 | 2.69e-6 | 1000 | 48.6 | MC |
0ec5b89c | maxset.hpp | mc.py | --particles_per_core=500 | 1 | False | 7.53e-5 | 5.70e-6 | 100 | 15.1 | MD |
where mean is the average time for 30 integration loops of nsteps integration steps per loop, ci is the 95% confidence interval of the mean, duration is the total running time, label is an identifier for benchmarks that generate multiple rows. This data can be processed in a spreadsheet software to determine speed-up and slow-downs of commits compared to a reference commit.
When running a benchmark on ICP machines, the machine must be put on drain for the duration of the benchmarks to prevent interference from distributed jobs allocated by the HTCondor scheduler. This is best achieved by running the following bash script:
condor_drain ${HOSTNAME}
sleep 30 # delay: the drain request takes time
bash suite.sh
condor_drain -c ${HOSTNAME}
To run code coverage analysis, create a new build folder or update the current one with:
cmake . -D CMAKE_BUILD_TYPE=Coverage -D ESPRESSO_BUILD_WITH_COVERAGE=ON -D ESPRESSO_CTEST_ARGS="-j$(nproc)"
make -j$(nproc)
Run the C++ unit tests to get minimal code coverage, and then the Python tests for the feature of interest (or the full Python test suite):
rm -f $(find src _deps -type f -name "*.gcda") # remove old code coverage data
make -j$(nproc) check_unit_tests # get minimal code coverage
mpiexec -n 2 ./pypresso ../testsuite/python/collision_detection_interface.py
mpiexec -n 4 ./pypresso ../testsuite/python/collision_detection.py
mpiexec -n 4 ./pypresso ../testsuite/python/save_checkpoint.py Test__lj
mpiexec -n 4 ./pypresso ../testsuite/python/test_checkpoint.py Test__lj
# alternative: run the full Python test suite with `make -j$(nproc) check_python_skip_long`
Now create the code coverage report, and remove superfluous information, such as the STL library, Boost libraries, external dependencies and generated waLBerla code. If multiple versions of the C++ compiler are installed on the computer, and the wrong version of gcov is picked up by lcov, warnings will appear in the terminal.
if [ -d "src/walberla_bridge" ]; then
rm -f $(find src/walberla_bridge/ -type f -name "*.gcda" | grep --color=never generated_kernels)
fi
lcov --gcov-tool "${GCOV:-gcov}" -q --directory . --ignore-errors graph --capture --output-file coverage.info # capture coverage info
lcov --gcov-tool "${GCOV:-gcov}" -q --remove "coverage.info" '/usr/*' --output-file "coverage.info" # filter out system
lcov --gcov-tool "${GCOV:-gcov}" -q --remove "coverage.info" '*/doc/*' --output-file "coverage.info" # filter out docs
lcov --gcov-tool "${GCOV:-gcov}" -q --remove "coverage.info" "$(realpath ..)/libs/*" --output-file "coverage.info" # filter out libs
lcov --gcov-tool "${GCOV:-gcov}" -q --remove "coverage.info" "$(realpath .)/src/python/espressomd/*" --output-file "coverage.info" # filter out cython
lcov --gcov-tool "${GCOV:-gcov}" -q --remove "coverage.info" "$(realpath .)/src/walberla_bridge/src/lattice_boltzmann/generated_kernels/*" --output-file "coverage.info"
lcov --gcov-tool "${GCOV:-gcov}" -q --remove "coverage.info" "$(realpath .)/src/walberla_bridge/src/electrokinetics/generated_kernels/*" --output-file "coverage.info"
lcov --gcov-tool "${GCOV:-gcov}" -q --remove "coverage.info" "$(realpath .)/src/walberla_bridge/src/electrokinetics/reactions/generated_kernels/*" --output-file "coverage.info"
if [ -d "_deps/" ]; then
lcov --gcov-tool "${GCOV:-gcov}" -q --remove "coverage.info" "$(realpath .)/_deps/*" --output-file "coverage.info" # filter out dependencies
fi
Finally, create the code coverage report in HTML format:
output="coverage_report"
cp "coverage.info" "${output}.info"
rm -rf "${output}_html"
genhtml "${output}.info" --demangle-cpp --quiet --output-directory "${output}_html"
echo 'center table tbody tr:hover td { box-shadow:inset 0px 3px 2px -2px #AAA, 0px 4px 2px -2px #AAA; }' >> "${output}_html/gcov.css"
xdg-open "${output}_html/index.html"