Skip to content

Style Guide

Manjaree Binjolkar edited this page Jul 23, 2025 · 1 revision

General C++ and CUDA Style Guidelines

The Tiger HLM routing code is written in C++ with GPU acceleration provided by CUDA. It uses a hybrid parallel design where the CPU is responsible for managing simulation setup, configuration, file I/O, and MPI communication, while the GPU handles the core numerical integration of hydrologic equations.

Although the project is still growing, the current modular structure enables efficient scaling across compute clusters and supports long-term maintainability. To ensure the code remains performant, readable, and extensible, the following guidelines should be followed when editing or contributing to the codebase:


Language and Compilation

  • All host-side code must conform to the C++17 standard.
  • The code must compile cleanly with g++ (host) and nvcc (device) without warnings or errors.
  • All device code must target CUDA compute capability 8.0 or higher (e.g., NVIDIA A100).
  • Use -rdc=true for compiling device code when using separate compilation and linking.

Code Structure and Formatting

  • Declare all reusable types, functions, and constants in header (.hpp) files.
  • Place host-only logic in .cpp files and GPU kernels or device utilities in .cu / .cuh files.
  • Keep functions short and well-scoped. Avoid mixing I/O, MPI, and model logic in a single function.
  • The main.cpp file acts as the program entry point and should remain stable—avoid placing custom logic here.

CPU Responsibilities

  • All file input/output is handled on the CPU side, including:

    • Reading YAML configuration files
    • Loading hillslope parameters from CSV
    • Ingesting forcing data (e.g., precipitation, temperature) from NetCDF files
    • Writing simulation outputs (final states, hydrographs) to NetCDF
  • MPI is initialized and managed on the host, with Rank 0 coordinating spatial parameter distribution and global outputs.

  • Prefer std::vector, std::array, and other STL containers for host-side memory management.

  • Use const and constexpr where appropriate to clarify intent and avoid accidental mutation.


CUDA Responsibilities

  • Device-side logic is isolated to CUDA kernels that solve ODEs for hillslope systems.
  • All GPU buffers must be explicitly allocated and freed, with error checking using the CUDA_CHECK() macro.
  • Kernel launches should be occupancy-aware and avoid excessive branching within warps.
  • Use __constant__ memory for global, read-only data such as model parameters or forcing metadata.
  • Kernels should be templated by model type, and all device-side indexing must be bounds-safe.

Comments and Documentation

  • Document each function with a brief comment explaining its purpose and key arguments.
  • Use inline comments to explain non-obvious or performance-critical logic, especially within CUDA kernels.
  • Do not duplicate what is already clear from variable names or function signatures.
  • Use visual separators (e.g., // ── Step 1: Load Forcings ──) in longer functions for readability.

Naming Conventions

  • Use descriptive and consistent names for variables and functions.
  • Device buffers must be prefixed with d_ (e.g., d_y_final_all), and shared memory with sm_ if applicable.
  • Constants should use UPPER_CASE_WITH_UNDERSCORES.
  • Avoid overly short or cryptic names unless they are standard (e.g., i, j for loop indices).

Code Practices

  • Avoid global variables unless explicitly required (e.g., device pointers to constants).
  • Avoid using namespace std; in headers or shared files. Prefer explicit std:: prefixes for clarity.
  • Separate I/O, MPI, and model logic into distinct modules.
  • Initialize all variables and avoid magic numbers.

Version Control

  • All contributions must be submitted through pull requests with clear commit messages.
  • Keep commits focused and logical — avoid lumping unrelated changes together.

Maintaining a clean and consistent code style across both host and device components ensures the Tiger HLM routing model remains stable and scalable. As new models and solver types are introduced, these practices will make it easier to integrate new features without compromising performance or clarity.

Clone this wiki locally