Contributing¶
This page is the practical guide for contributing code, tests, fixtures, parameter files, documentation, and build-system changes to ImmersX.
Coding agents must also read the repository-root
AGENTS.md.
AGENTS.md contains the normative repository and
agent-behavior rules; this page focuses on the human-facing development
workflow and detailed commands.
Repository layout¶
The main source-tree areas are:
include/immersx/— public library headers;source/— compiled implementation sources;apps/— executable entry points;gtests/— GoogleTest unit and integration tests;tests/— deal.II-style regression tests;gtests/parameters/,prms/, anddata/— parameter files and input data;doc/— Sphinx/MyST documentation;scripts/— repository utilities such asscripts/indent.
Keep build directories, generated outputs, downloaded dependencies, and test scratch files out of the source tree whenever possible.
Configure and build¶
Always use an out-of-source build.
A typical Debug configuration is:
cmake -S . -B build-debug \
-DCMAKE_BUILD_TYPE=Debug \
-DDEAL_II_DIR=/path/to/deal.II \
-DENABLE_GOOGLE_TESTING=ON \
-DENABLE_DEAL_II_APP_TESTING=ON
cmake --build build-debug -j
When DEAL_II_DIR is already discoverable, it may be omitted.
For Release:
cmake -S . -B build-release \
-DCMAKE_BUILD_TYPE=Release \
-DDEAL_II_DIR=/path/to/deal.II \
-DENABLE_GOOGLE_TESTING=ON \
-DENABLE_DEAL_II_APP_TESTING=ON
cmake --build build-release -j
ImmersX also supports DebugRelease in one single-config build tree. It
requires a deal.II installation containing both variants and creates the
normal Release targets together with _debug-suffixed Debug targets:
cmake -S . -B build-debugrelease \
-DCMAKE_BUILD_TYPE=DebugRelease \
-DDEAL_II_DIR=/path/to/deal.II \
-DENABLE_GOOGLE_TESTING=ON \
-DENABLE_DEAL_II_APP_TESTING=ON
cmake --build build-debugrelease -j
This is an ImmersX single-config mode, not a CMake multi-config generator.
When both variants are enabled, CTest names include .Release or .Debug so
they can coexist in one manifest.
Depending on the deal.II installation, GoogleTest binaries are typically:
build-debug/gtests/gtests_debug
build-release/gtests/gtests
Application binaries are generated in the build tree. Debug executables may
carry the _debug suffix.
ccache¶
ccache is recommended for local development:
export CCACHE_DIR="$HOME/.ccache"
cmake -S . -B build-debug \
-DCMAKE_BUILD_TYPE=Debug \
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache
Parallel git worktrees may share the same ccache, but each worktree must use a separate CMake build directory.
Parameter and fixture preprocessing¶
The top-level CMakeLists.txt preprocesses every file ending in .in below:
gtests/
tests/
data/
The generated file is written under the build tree at the same relative path,
with only the final .in suffix removed.
Example:
source:
gtests/parameters/case.prm.in
generated:
<build>/gtests/parameters/case.prm
The same rule applies to:
.prm.in
.txt.in
.vtk.in
.vtu.in
.pvtu.in
and any other .in file.
CMake uses configure_file(... @ONLY). Templates may use:
@IMMERSX_SOURCE_DIR@
@IMMERSX_BINARY_DIR@
@TEST_DATA_DIR@
@TEST_OUTPUT_DIR@
Example:
set Reduced grid name = @TEST_DATA_DIR@/tests/one_cylinder.vtk
set Output directory = @TEST_OUTPUT_DIR@/reduced-poisson/one-cylinder
Runtime code and tests should open the generated build-tree file, not the source template.
Do not use relative-runtime assumptions such as:
../data/...
./output/...
and do not solve path problems by creating source/build symlinks.
Reconfigure CMake after adding new .in files.
GoogleTest infrastructure¶
GoogleTests live in gtests/.
The common test driver in gtests/gtest_main.cc initializes MPI and separates
serial and MPI tests according to the test name.
Serial and MPI naming¶
A serial test must not contain MPI_ in the test-name component:
TEST(MyFeature, BasicBehavior)
{
// ...
}
A test intended for MPI execution must contain MPI_:
TEST(MyFeature, MPI_DistributedBehavior)
{
// ...
}
This convention is used by the shared test driver to select tests according to the MPI process count.
Test paths¶
Use gtests/test_paths.h for input and output paths.
Typical helpers include:
TestPaths::data_filename("tests/one_cylinder.vtk");
TestPaths::parameter_path("gtests/parameters/case.prm");
TestPaths::output_directory("my-test");
Generated output belongs below TEST_OUTPUT_DIR.
Do not:
write generated VTK or result files to the source tree;
use
chdir()to make a relative test path work;add cwd-dependent paths to inline parameter strings;
create
build/data -> source/datasymlinks.
For inline parameter text, either construct absolute paths with the helpers or
use configured tokens and expand_configured_paths().
VTK-dependent tests¶
When a test requires VTK support, guard it consistently with the project’s
existing DEAL_II_WITH_VTK convention and exercise the same code path used by
the application where practical.
Focused test runs¶
CTest is the authoritative orchestration layer and exposes the test hierarchy through labels:
ctest --test-dir build-debug -L quick --output-on-failure
ctest --test-dir build-debug -L unit --output-on-failure
ctest --test-dir build-debug -L integration --output-on-failure
ctest --test-dir build-debug -L validation --output-on-failure
ctest --test-dir build-debug -L application --output-on-failure
ctest --test-dir build-debug --output-on-failure
Unit and integration tests are marked quick; MPI is independent and is
marked mpi. Pull-request CI runs quick, while pushes to master run the
full manifest. Do not add a second full GoogleTest invocation to CI.
Run one serial test with:
build-debug/gtests/gtests_debug \
--gtest_filter='SuiteName.TestName'
Run one MPI test with:
mpirun -np 2 build-debug/gtests/gtests_debug \
--gtest_filter='SuiteName.MPI_TestName'
The MPI test driver may augment the filter to select only MPI-named tests.
Working-directory robustness¶
Tests must behave the same when launched from different working directories.
For path-sensitive changes, exercise at least:
build-debug/gtests/gtests_debug
(cd /path/to/repository && \
/absolute/path/to/build-debug/gtests/gtests_debug)
(cd /tmp && \
/absolute/path/to/build-debug/gtests/gtests_debug)
(cd /tmp && \
mpirun -np 2 /absolute/path/to/build-debug/gtests/gtests_debug)
deal.II-style regression tests¶
The tests/ tree uses the deal.II testsuite infrastructure and
DEAL_II_PICKUP_TESTS().
Source-driven tests¶
A typical source-driven test consists of:
tests/<category>/<name>.cc
tests/<category>/<name>.output
The program writes deterministic output, usually through deallog, and the
.output file contains the expected result.
Parameter-driven tests¶
A parameter-driven test uses an existing executable together with a generated parameter file.
Store the parameter source as:
tests/<category>/<name>.prm.in
CMake generates the runtime .prm below the build tree.
The corresponding testsuite directory configures the target through
TEST_TARGET, TEST_TARGET_DEBUG, or TEST_TARGET_RELEASE as appropriate.
Running regression tests¶
List tests:
ctest --test-dir build-debug -N
Run all configured tests:
ctest --test-dir build-debug --output-on-failure
Run one regression test verbosely:
ctest --test-dir build-debug -V -R '<category>/<name>'
Use numdiff where floating-point expected output requires tolerant
comparison.
Running applications manually¶
When debugging application behavior, pass the generated build-tree parameter file explicitly.
Example:
build-debug/elasticity_debug \
build-debug/gtests/parameters/elasticity_strong_dirichlet.prm
Do not substitute the source .prm.in template or an old source-tree .prm
file, because that can hide preprocessing or path problems.
Keep manually generated output in a build-tree or temporary scratch directory.
CMake changes¶
When modifying CMake:
keep builds out of source;
avoid source-tree-relative runtime assumptions;
keep installed targets relocatable;
use standard target-based CMake where possible;
use
GNUInstallDirs-style destinations for installable artifacts;ensure installed targets do not depend on test-only build-tree resources.
Public headers should remain under:
include/immersx/
and public API should live in:
namespace ImmersX
{
// ...
}
After adding sources, tests, .in templates, or CMake targets, rerun CMake
configuration before concluding that the build system has not detected them.
Installation and packaging checks¶
For changes affecting installation or packaging, validate an install into a temporary prefix.
Example:
cmake -S . -B build-release \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INSTALL_PREFIX=/tmp/immersx-install
cmake --build build-release -j
cmake --install build-release
Inspect the resulting prefix and verify that:
public headers are installed in the intended include location;
libraries are installed in the correct library directory;
executables are installed in the correct binary directory;
required runtime data/resources are installed where documented;
test-only fixtures are not accidentally required at runtime.
If downstream find_package() support is changed or added, validate it with a
small external consumer project.
Documentation¶
Documentation sources live in doc/ and use Sphinx/MyST.
When adding a new page, add it to the doc/index.md toctree.
Install documentation requirements and build with:
python3 -m pip install -r doc/requirements.txt
./scripts/build_doc.sh
Documentation examples should use the current public API and the real build-tree path conventions.
Prefer documenting the ordinary user-facing API before internal machinery.
Validation strategy¶
During development, use the smallest test that exercises the code being changed.
Before considering a substantial change complete, broaden validation according to scope.
For core, solver, distributed, packaging, or cross-cutting changes, typically run:
focused unit/integration tests;
complete Debug build;
serial GoogleTest suite;
two-rank MPI GoogleTest suite;
relevant CTest/regression tests;
Release build and relevant tests;
working-directory robustness checks for path-sensitive changes.
Typical commands:
cmake --build build-debug -j
build-debug/gtests/gtests_debug
mpirun -np 2 build-debug/gtests/gtests_debug
ctest --test-dir build-debug --output-on-failure
For Release:
cmake --build build-release -j
build-release/gtests/gtests
mpirun -np 2 build-release/gtests/gtests
ctest --test-dir build-release --output-on-failure
Keep independent correctness tests when they validate different layers of the software. For example, a physics-level residual/Jacobian test and a solver-level integration test are complementary rather than redundant.
Formatting and commits¶
The repository formatter is:
./scripts/indent
Run it from the repository root before every commit.
Because it formats the repository globally, inspect the complete resulting diff and make sure unrelated changes are not included.
Useful checks:
git diff
git diff --check
git status
Implementation work should be split into small, meaningful local commits at logical milestones.
Do not push, open a pull request, or merge into master unless explicitly
requested.
CI reference¶
The repository GitHub Actions workflows under .github/workflows/ are the
authoritative reference for the current CI matrix.
When reproducing CI locally, inspect the workflow first rather than assuming that image tags, optional dependencies, or exact commands have remained unchanged.
The normal local baseline remains:
Debug and Release when supported;
serial GoogleTests;
two-rank MPI GoogleTests;
CTest regression tests.
Final contributor checklist¶
Before handing off a substantial change:
[ ] Build is out of source.
[ ] No generated output was written into the source tree.
[ ] New test inputs use the
.inpreprocessing convention where appropriate.[ ] MPI GoogleTests contain
MPI_in the test name.[ ] Tests are independent of the working directory.
[ ] Relevant Debug tests pass.
[ ] Relevant MPI tests pass.
[ ] Relevant CTest tests pass.
[ ] Release validation was performed when appropriate.
[ ] Documentation was updated when public behavior changed.
[ ] New documentation pages were added to
doc/index.md.[ ]
./scripts/indentwas run before each commit.[ ] The complete diff was reviewed.
[ ] Commits are small and meaningful.
[ ] Nothing was pushed and no pull request was opened unless explicitly requested.