diff --git a/.cursor/rules/mfc-agent-rules.mdc b/.cursor/rules/mfc-agent-rules.mdc index 6a8baddd64..a63276f493 100644 --- a/.cursor/rules/mfc-agent-rules.mdc +++ b/.cursor/rules/mfc-agent-rules.mdc @@ -1,32 +1,30 @@ ---- -description: Full MFC project rules – consolidated for Agent Mode -alwaysApply: true ---- +---- +-description: Full MFC project rules – consolidated for Agent Mode +-alwaysApply: true +---- # 0 Purpose & Scope -Consolidated guidance for the MFC exascale, many-physics solver. -Written primarily for Fortran/Fypp; the OpenACC and style sections matter only when -`.fpp` / `.f90` files are in view. +Consolidated guidance for the MFC exascale, many-physics solver. +Written primarily for Fortran/Fypp; the GPU and style sections matter only when `.fpp` / `.f90` files are in view. --- # 1 Global Project Context (always) -- **Project**: *MFC* is modern Fortran 2008+ generated with **Fypp**. - - Sources `src/`, tests `tests/`, examples `examples/`. - - Most sources are `.fpp`; CMake transpiles them to `.f90`. -- **Fypp macros** live in `src//include/` you should scan these first. - `` ∈ {`simulation`,`common`,`pre_process`,`post_process`}. -- Only `simulation` (+ its `common` calls) is GPU-accelerated via **OpenACC** or **OpenMP**. -- Assume free-form Fortran 2008+, `implicit none`, explicit `intent`, and modern - intrinsics. -- Prefer `module … contains … subroutine foo()`; avoid `COMMON` blocks and - file-level `include` files. -- **Read the full codebase and docs *before* changing code.** - Docs: and the respository root `README.md`. +- **Project**: *MFC* is modern Fortran 2008+ generated with **Fypp**. + - Sources `src/`, tests `tests/`, examples `examples/`. + - Most sources are `.fpp`; CMake transpiles them to `.f90`. +- **Fypp macros** live in `src//include/` you should scan these first. + `` ∈ {`simulation`,`common`,`pre_process`,`post_process`}. +- Only `simulation` (+ its `common` calls) is GPU-accelerated via **OpenACC** or **OpenMP**. +- Assume free-form Fortran 2008+, `implicit none`, explicit `intent`, and modern intrinsics. +- Prefer `module … contains … subroutine foo()`; avoid `COMMON` blocks and file-level `include` files. +- **Read the full codebase and docs *before* changing code.** + - Docs: and the respository root `README.md`. ### Incremental-change workflow -1. Draft a step-by-step plan. -2. After each step, build: + +1. Draft a step-by-step plan. +2. After each step, build: ```bash ./mfc.sh build -t pre_process simulation -j $(nproc) ``` @@ -49,12 +47,10 @@ Written primarily for Fortran/Fypp; the OpenACC and style sections matter only w * Subroutine → `s__` (e.g. `s_compute_flux`) * Function → `f__` * Private helpers stay in the module; avoid nested procedures. -* **Size limits**: subroutine ≤ 500 lines, helper ≤ 150, function ≤ 100, - module/file ≤ 1000. -* ≤ 6 arguments per routine; otherwise pass a derived-type “params” struct. +* **Size limits**: subroutine ≤ 500 lines, helper ≤ 150, function ≤ 100, module/file ≤ 1000. +* ≤ 6 arguments per routine; otherwise pass a derived-type "params" struct. * No `goto` (except unavoidable legacy); no global state (`COMMON`, `save`). -* Every variable: `intent(in|out|inout)` + appropriate `dimension` / `allocatable` - / `pointer`. +* Every variable: `intent(in|out|inout)` + appropriate `dimension` / `allocatable` / `pointer`. * Use `s_mpi_abort()` for errors, not `stop`. * Mark GPU-callable helpers that are called from GPU parallel loops immediately after declaration: ```fortran @@ -66,9 +62,10 @@ Written primarily for Fortran/Fypp; the OpenACC and style sections matter only w --- -# 3 FYPP Macros for GPU acceleration Pogramming Guidelines (for kernels) +# 3 FYPP Macros for GPU acceleration Pogramming Guidelines (for GPU kernels) -Do not directly use OpenACC or OpenMP directives directly. Instead, use the FYPP macros contained in src/common/include/parallel_macros.fpp +Do not directly use OpenACC or OpenMP directives directly. +Instead, use the FYPP macros contained in src/common/include/parallel_macros.fpp Wrap tight loops with @@ -82,3 +79,100 @@ $:GPU_PARALLEL_FOR(private='[...]', copy='[...]') * **Do not** place `stop` / `error stop` inside device code. * Must compile with Cray `ftn` and NVIDIA `nvfortran` for GPU offloading; also build CPU-only with GNU `gfortran` and Intel `ifx`/`ifort`. + +--- + +# 4 File & Module Structure + +- **File Naming**: + - `.fpp` files: Fypp preprocessed files that get translated to `.f90` + - Modules are named with `m_` prefix followed by feature name: `m_helper_basic`, `m_viscous` + - Primary program file is named `p_main.fpp` + +- **Module Layout**: + - Start with Fypp include for macros: `#:include 'macros.fpp'` + - Header comments using `!>` style documentation + - `module` declaration with name matching filename + - `use` statements for dependencies + - `implicit none` statement + - `private` declaration followed by explicit `public` exports + - `contains` section + - Implementation of subroutines and functions + +# 5 Fypp Macros and GPU Acceleration + +## Use of Fypp +- **Fypp Directives**: + - Start with `#:` (e.g., `#:include`, `#:def`, `#:enddef`) + - Macros defined in `include/*.fpp` files + - Used for code generation, conditional compilation, and GPU offloading + +## Some examples + +Documentation on how to use the Fypp macros for GPU offloading is available at https://mflowcode.github.io/documentation/md_gpuParallelization.html + +Some examples include: +- `$:GPU_ROUTINE(parallelism='[seq]')` - Marks GPU-callable routines +- `$:GPU_PARALLEL_LOOP(collapse=N)` - Parallelizes loops +- `$:GPU_LOOP(parallelism='[seq]')` - Marks sequential loops +- `$:GPU_UPDATE(device='[var1,var2]')` - Updates device data +- `$:GPU_ENTER_DATA(copyin='[var]')` - Copies data to device +- `$:GPU_EXIT_DATA(delete='[var]')` - Removes data from device + +# 6 Documentation Style + +- **Subroutine/Function Documentation**: + ```fortran + !> This procedure + !! @param param_name Description of the parameter + !! @return Description of the return value (for functions) + ``` +which conforms to the Doxygen Fortran format. + +# 7 Error Handling + +- **Assertions**: + - Use the fypp `ASSERT` macro for validating conditions + - Example: `@:ASSERT(predicate, message)` + +- **Error Reporting**: + - Use `s_mpi_abort()` for error termination, not `stop` + - No `stop` / `error stop` inside device code + +# 8 Memory Management + +- **Allocation/Deallocation**: + - Use fypp macro `@:ALLOCATE(var1, var2)` macro for device-aware allocation + - Use fypp macro `@:DEALLOCATE(var1, var2)` macro for device-aware deallocation + +# 9. Additional Observed Patterns + +- **Derived Types**: + - Extensive use of derived types for encapsulation + - Use pointers within derived types (e.g., `pointer, dimension(:,:,:) => null()`) + - Clear documentation of derived type components + +- **Pure & Elemental Functions**: + - Use `pure` and `elemental` attributes for side-effect-free functions + - Combine them for operations on arrays (`pure elemental function`) + +- **Precision Handling**: + - Use `wp` (working precision) parameter from `m_precision_select` + - Never hardcode precision with `real*8` or similar + +- **Loop Optimization**: + - Favor array operations over explicit loops when possible + - Use `collapse=N` directive to optimize nested loops + +# 10. Fortran Practices to Avoid + +- **Fixed Format**: Only free-form Fortran is used + - No column-position dependent code + +- **Older Intrinsics**: Avoid outdated Fortran features like: + - `equivalence` statements + - `data` statements (use initialization expressions) + - Character*N (use `character(len=N)` instead) + +- **Using same variable for multiple purposes**: Maintain single responsibility + - Each variable should have one clear purpose