Skip to content

Fortran cursor rules #97

@apetros

Description

@apetros

I code in Fortran for scientific research purposes, and I've put together a Fortran cursor rules. Please check and if useful, add to the list. I've shared my cursor rules on fortran-lang.discourse.group and incorporated 2 suggestions by other people:

---
description: 
globs: **/*.f,**/*.f90,**/*.f95,**/*.f03,**/*.f08,**/*.for,**/*.ftn,CMakeLists.txt,*.cmake,Makefile
alwaysApply: false
---

# Fortran Programming Guidelines

## Basic Principles

- Use English for all code and documentation.
- Always declare variables with `implicit none` in every program component.
- Create necessary derived types and modules.
- Use comments to document public modules, subroutines, and functions.
- Always add last modified date and the name of the author in the documentation.
- Follow consistent indentation in control structures.
- Use structured programming techniques.
- Avoid programming tricks that subvert the intended purpose of the language.
- Do not write programs that modify themselves as they execute.
- Always declare real variables as `real(kind=dp)` with `dp` imported from a kind_mod module.
- Always declare integer variables as `real(kind=i4)` with `i4` imported from a kind_mod module.
- Always place procedures in modules for better organization and reusability.
- If kind_mod doesn't exist, create one:
  !> Numerical storage size parameters for real and integer values
  module kind_mod
    implicit none
    public
    !> Single precision real numbers, 6 digits, range 10⁻³⁷ to 10³⁷-1; 32 bits
    integer, parameter :: sp = selected_real_kind(6, 37)
    !> Double precision real numbers, 15 digits, range 10⁻³⁰⁷ to 10³⁰⁷-1; 64 bits
    integer, parameter :: dp = selected_real_kind(15, 307)
    !> Quadruple precision real numbers, 33 digits, range 10⁻⁴⁹³¹ to 10⁴⁹³¹-1; 128 bits
    integer, parameter :: qp = selected_real_kind(33, 4931)
    !> Char length for integers, range -2⁷ to 2⁷-1; 8 bits
    integer, parameter :: i1 = selected_int_kind(2)
    !> Short length for integers, range -2¹⁵ to 2¹⁵-1; 16 bits
    integer, parameter :: i2 = selected_int_kind(4)
    !> Length of default integers, range -2³¹ to 2³¹-1; 32 bits
    integer, parameter :: i4 = selected_int_kind(9)
    !> Long length for integers, range -2⁶³ to 2⁶³-1; 64 bits
    integer, parameter :: i8 = selected_int_kind(18)
  end module kind_mod

## Nomenclature

- Use lowercase for all Fortran constructs (do, subroutine, module, etc.).
- Follow short mathematical notation for mathematical variables/functions (Ylm, Gamma, etc.).
- Use all lowercase for other names: try to keep names to one or two syllables.
- Use underscores to clarify longer names (spline_interp, stop_error, etc.).
- Avoid abbreviations that could be ambiguous (e.g., "int" could mean integration or integer).
- Variables should be defined with units specified in comments.
- Use consistent naming conventions across modules.
- Never use the same name with different uppercase/lowercase spelling in the same scope.
- Avoid using the letters O and l and the digits 0 and 1 in names where they might be confused.

## Subroutines and Functions

- Write short subroutines and functions with a single purpose.
- Name functions with a verb and something else.
- If it returns a logical value, use isX or hasX, canX, etc.
- Always use `intent` for all arguments (in, out, inout).
- Functions should have no side effects - all function arguments should be intent(in).
- Avoid nesting blocks by:
  - Early checks and returns.
  - Extraction to utility subroutines.
- Use a single level of abstraction.
- Use 'Javadoc'-style comments to define inputs, outputs, and purpose.
- Make all subroutines have an explicit interface (place them in modules).

## Data

- Don't abuse primitive types and encapsulate data in derived types.
- Avoid data validations in functions and use modules with internal validation.
- Prefer immutability for data.
- Use parameter for constants.
- Specify array bounds explicitly when needed.
- Use allocatable arrays rather than pointers when possible.
- Check array sizes at the beginning of subroutines if necessary.
- Never assume that pi is a built-in constant; define it explicitly.
- Never declare variables more than once.
- Never declare superfluous variables (e.g., loop indices that aren't used).
- Always declare variables before any executable code, or use block construct.

## Modules

- Modules should always have their own file.
- Related functions, subroutines, and variables should be grouped together into modules.
- Avoid using module level variables when possible.
- Always specify `use, only` when importing from another module.
- Set entities within a module to private by default.
- Explicitly declare as public any module entities that need to be accessed externally.
- Write code that compiles with gfortran -fmodule-private.
- Avoid COMMON blocks.

## Exceptions and Error Handling

- Build error checks into the program.
- Use clear error messages.
- Implement consistent error handling mechanisms.
- Consider using error codes or status variables for expected failures.
- Document error handling procedures.

## Memory Management

- Properly deallocate allocatable arrays when no longer needed.
- Check if arrays are already allocated before allocation.
- Avoid dynamic memory and pointers when possible as they make code difficult to navigate.
- Use allocatable arrays instead of pointers when feasible.

## Testing

- Write unit tests for each public subroutine and function.
- Use test doubles to simulate dependencies.
- Write integration tests for each module.
- Test boundary conditions and error cases.

## Project Structure

- Use modular architecture.
- Organize code into logical directories.
- Use a build system like CMake.
- Use namespaces (modules) to organize code logically.
- Create utility modules for common functions.

## Standard Compliance

- Follow modern Fortran standards (Fortran 2003, 2008, or newer).
- Avoid obsolete features.
- Use modern syntax when available (e.g., array constructors with square brackets).
- Avoid GOTO statements and numeric labels.
- Do not use non-standard syntax just for convenience.

## Code Formatting

- Use consistent indentation (typically 2-3 spaces).
- Indent blocks in control structures (do, if, select case).
- Use blank lines to separate related parts of a program.
- Use white space freely in statements (around delimiters).
- FORTRAN keywords must be clearly set off by a blank character.
- Indent the executable statements in a DO loop at least three spaces from the DO statement.
- All elements related to a specific level of the control structure should be aligned to the same column.
- Repeat function/subroutine/module name after `end`.
- Use `end do` and `end if` consistently.
- Variable declarations must use a double colon `::` even if no other attributes are present.
- If multiple declarations occur with the same attributes, list the attributes in the same order.
- Require that loops with cycle or exit have a construct label that is used in those statements, for clarity.

## Common LLM Mistakes to Avoid

- Do not assume pi is a built-in constant; define it explicitly in your code.
- Never declare variables after executable code without using a block construct.
- Never use print or write statements in pure procedures.
- Never use random_number in pure procedures.
- Remember that random_number is a subroutine that must be called with an argument, not a function.
- Do not declare superfluous variables such as loop indices that aren't used.
- Never declare the same variable more than once in the same scope.
- Always explicitly declare as public any module entities that need to be accessed externally.
- Never have two variables in the same scope that differ only by case (e.g., Value and value).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions