TNL (Template Numerical Library) is a C++17 library for developing efficient numerical solvers with native support for modern parallel architectures. It provides a unified interface for multi-core CPUs (OpenMP), NVIDIA GPUs (CUDA), and AMD GPUs (HIP), along with MPI support for distributed systems.
Key characteristics:
- Language: C++17 with CUDA/HIP extensions
- Build system: CMake 3.30+ with Ninja generator
- Target platforms:
- Linux (Arch Linux in CI), with support for GCC, Clang, NVHPC, and CUDA/HIP toolchains
- macOS (not tested in CI)
- License: MIT
Project structure:
src/TNL/- Core library headers (template-based, header-only,TNL::namespace)src/Examples/- Stand-alone examplessrc/Benchmarks/- Performance benchmarkssrc/Tools/- Pre/post-processing utilitiessrc/Python/- Python utilities and wrapperssrc/UnitTests/- Google Test-based test suiteDocumentation/- Documentation pages (Doxygen), examples included in the documentation, users' guidecmake/- CMake modules and configurationscripts/- Utility scripts
- CMake 3.30+
- C++17 compiler (GCC, Clang, or NVHPC)
- CUDA toolkit (optional)
- ROCm/HIP (optional)
- OpenMP (optional)
- MPI (optional)
The project uses just as a command runner. Key recipes:
just configure # Configure with default options
just build # Build the project (all targets by default)
just build tests benchmarks # Build specific targets
just test # Run all tests
just check # Run checks (typos, formatting, linting, etc.)
just format # Reformat all files
just clean # Clean the build directoryctest -R DenseMatrixTest # Run a single test by name
ctest -L Matrices # Run tests by label
ctest --preset matrix-tests # Using test presetsThe CMake build system can be configured manually with custom options:
cmake -B build -S . -G Ninja -DCMAKE_BUILD_TYPE=DebugCommon build options:
| Option | Default | Description |
|---|---|---|
TNL_USE_CUDA |
ON | Enable CUDA support |
TNL_USE_HIP |
ON | Enable HIP support |
TNL_USE_OPENMP |
ON | Enable OpenMP support |
TNL_USE_MPI |
ON | Enable MPI support |
TNL_USE_CI_FLAGS |
OFF | Enable strict CI flags (-Werror) |
Available presets (all use CMAKE_BUILD_TYPE=RelWithDebInfo):
toolchain-gcc- GCC host-onlytoolchain-clang- Clang host-only (with libc++)toolchain-nvhpc-host- NVHPC (nvc++) host-onlytoolchain-gcc-nvcc- GCC + CUDA (nvcc)toolchain-clang-cuda- Clang + CUDAtoolchain-hip- ROCm/HIP
Usage: cmake --preset toolchain-gcc or cmake --preset toolchain-gcc --fresh
The --fresh option is preferred over removing the build directory manually.
- Indentation: 3 spaces for C++/CUDA/HIP, 4 spaces for Python/CMake, 2 spaces for YAML/.clangd
- Line length: 128 characters for C++, 88 for Python
- Line endings: LF (Unix)
- Final newline: Required
Use just format or just check-format for automatic formatting.
- Use title-case in first-level headings only:
# Page Title - Use sentence-case in all other section levels:
## First section,### Some subsection
Use git for managing the local repository and glab for the remote repository on GitLab (issues, merge requests, CI, etc).
Branches:
- The
mainbranch is protected and cannot be pushed to directly - Use feature branches named like
AB/some-feature, where AB are the initials of the author - Run
git config user.nameto get the author's full name
Commits:
- Never run
git add -Aorgit add .- always stage files explicitly withgit add <filepath>to avoid committing unintended changes (e.g., benchmark logs or script outputs)
Commit message style:
- Subject line: ≤72 characters, no period at end, imperative mood
- Body: ≤72 characters per line, blank line after subject
- Content: Describe what changed and why, not how
- Attribution: When AI tools contribute, add
Assisted-by: AGENT_NAME:MODEL_VERSION(Linux kernel convention fromDocumentation/process/coding-assistants.rst). Example:Assisted-by: Opencode:glm-5.2. Optional tool names may follow:Assisted-by: Claude:claude-3-opus coccinelle sparse. Do NOT useSigned-off-byfor AI — only humans certify DCO. - Workflow: Human developers use rebase-based workflow and squash small commits.
Hence, small commits that fix issues in previous commits should be marked accordingly.
Create such commits with
git commit --squash=<commit-hash-to-squash-into>. Such commits will be processed later bygit rebase --interactive --autosquash.
- Follow existing code formatting ("mirroring" principle)
- Namespace indentation: None (flatten nested namespaces)
- Prefer range-based for loops, modern C++ features
- Use portable facilities in
src/TNL/Backend/for CUDA and HIP - Use
__cuda_callable__instead of separate__host__/__device__ - Use wrappers in
src/MPI/for MPI operations - Benchmarks must use the
TNL::Benchmarks::Benchmarkclass for measuring and logging.
- Header guards:
#pragma once - Include order: standard library first, then third-party libraries, then project headers
- Prefer angle brackets for TNL headers
- Group and sort includes logically
- Avoid cyclic dependencies; use forward declarations when appropriate
- TNL_ASSERT_ macros* for debugging (disabled in optimized builds with
NDEBUG)TNL_ASSERT_TRUE,TNL_ASSERT_FALSE,TNL_ASSERT_EQ,TNL_ASSERT_NETNL_ASSERT_LT,TNL_ASSERT_LE,TNL_ASSERT_GT,TNL_ASSERT_GE
- TNL_BACKEND_SAFE_CALL(call) for low-level CUDA/HIP API calls
- TNL::Exceptions::BackendRuntimeError for runtime errors
- Framework: Google Test (GTest) via CTest
- Test structure: Tests in
src/UnitTests/subdirectories, each with its ownCMakeLists.txt - Test labels:
Matrices,MPI, etc. - File extensions:
.cpp,.cu(CUDA),.hip(HIP)
- CUDA compiler errors: Resolve
nvccerrors sequentially (top to bottom) -nvccproduces cascading errors - Runtime errors: Use
compute-sanitizerfor CUDA/HIP errors or assertion failures - Segfaults: Build with
RelWithDebInfoorDebug, then run throughgdb
just check # Full check suite
just check-typos # Spell checking
just check-format # Code formatting (clang-format, ruff, gersemi)
just check-recipes # justfile shell scripts
just check-shellcheck # Shell scripts
just check-python # Python (ruff)
just check-clang-tidy # C++ (clang-tidy)Note: clang-tidy requires a non-CUDA build in Debug mode.
gitlab-ci-local can be used to run GitLab CI pipelines locally using podman or docker.
The project uses .gitlab-ci-local-env for default options:
EVALUATE_RULE_CHANGES=falseThis ignores rules:changes so all jobs are available regardless of file changes.
# List available jobs
gitlab-ci-local --list
gitlab-ci-local --list-all # include when:never jobs
# Run all jobs in a stage
gitlab-ci-local --stage lint --concurrency 4
gitlab-ci-local --stage build:host --concurrency 2
gitlab-ci-local --stage build:cuda --concurrency 1
gitlab-ci-local --stage build:hip_rocm --concurrency 1
# Run a specific job
gitlab-ci-local "ruff format"
gitlab-ci-local "gcc: [tests,Debug]"
# Run CUDA jobs with NVIDIA GPU passthrough (requires NVIDIA CDI)
gitlab-ci-local --device nvidia.com/gpu=all "nvcc: [tests,Debug]"
# Run HIP/ROCm jobs with AMD GPU passthrough
gitlab-ci-local --device /dev/kfd --device /dev/dri "hip_rocm: [tests,Debug]"
# Verify GPU passthrough with dummy check jobs
gitlab-ci-local --device nvidia.com/gpu=all "cuda-gpu-check"
gitlab-ci-local --device /dev/kfd --device /dev/dri "rocm-gpu-check"
# Override matrix variables
gitlab-ci-local --variable MATRIX_TARGET=tests --variable BUILD_TYPE=Release gcc
# Force shell executor instead of containers
gitlab-ci-local --force-shell-executor "typos"- NVIDIA:
--device nvidia.com/gpu=all(via CDI, no--privilegedneeded) - AMD:
--device /dev/kfd --device /dev/dri(CDI does not cover ROCm)
| Option | Description |
|---|---|
--container-executable podman |
Use podman instead of docker |
--evaluate-rule-changes false |
Ignore rules:changes (run all jobs) |
--concurrency N |
Limit parallel jobs |
--device nvidia.com/gpu=all |
NVIDIA GPU passthrough via CDI |
--device /dev/kfd |
AMD KFD device for ROCm |
--device /dev/dri |
AMD DRI render nodes for ROCm |
--stage <name> |
Run all jobs in a stage |
--force-shell-executor |
Run on host without containers |
--shell-isolation |
Enable artifact isolation for shell jobs |
--variable KEY=VAL |
Set CI variables |
--pull-policy if-not-present |
Avoid re-pulling images |
- Untracked files are not synced into job containers — only git-tracked files. Run
git addon new files before executing. - Per-stage concurrency is not supported — run each stage separately with different
--concurrencyvalues. - NVIDIA GPU passthrough requires
nvidia-ctkCDI setup; use--device nvidia.com/gpu=all. - AMD GPU passthrough requires
--device /dev/kfd --device /dev/dri(CDI does not cover ROCm). rules:changesmay exclude jobs — use--evaluate-rule-changes falseto run all jobs regardless.artifacts,needs, andpagesfeatures are not fully supported by local execution.
- CUDA/HIP builds: CUDA and HIP are mutually exclusive; some tools only work with CPU builds
- Build artifacts: Located in
build/directory; executables are inbuild/bin/(flat layout);compile_commands.jsonfor LSP support - Installation: Header-only library with optional compiled components
- CMake best practices: Use modern CMake with interface targets (
TNL::TNL), export sets, andFetchContentfor dependencies