Oceananigans.jl rules for agent-assisted development
Project Overview
Oceananigans.jl is a Julia package for fast, friendly, flexible, ocean-flavored fluid dynamics simulations on CPUs and GPUs. It provides a framework for solving the incompressible (or Boussinesq) Navier-Stokes equations with various model configurations including:
- Nonhydrostatic models with free surfaces
- Hydrostatic models for large-scale ocean simulations
- Shallow water models
- Support for a variety of grids: RectilinearGrid, LatitudeLongitudeGrid, CubedSphereGrid
- Support for complex domains using ImmersedBoundaryGrid
Language & Environment
- Language: Julia 1.10+
- Architectures: CPU and GPU (CUDA, AMD, Metal, OneAPI)
- Key Packages: KernelAbstractions.jl, CUDA.jl, Enzyme.jl, Reactant.jl, AMDGPU.jl, Metal.jl, oneAPI.jl
- Testing: Comprehensive test suite covering all model types and features
Code Style & Conventions
Julia Best Practices
Explicit Imports: Use
ExplicitImports.jlstyle - explicitly import all used functions/types- Import from modules explicitly (already done in src/Oceananigans.jl)
- Tests automatically check for proper imports
Type Stability: Prioritize type-stable code for performance
- All structs must be concretely typed
Kernel Functions: For GPU compatibility:
- Use KernelAbstractions.jl syntax for kernels, eg
@kernel,@index - Keep kernels type-stable and allocation-free
- Short-circuiting if-statements should be avoided if possible, ifelse should always be used if possible
- No error messages inside kernels
- Models never go inside kernels
- Use KernelAbstractions.jl syntax for kernels, eg
Documentation:
- Use DocStringExtensions.jl for consistent docstrings
- Include
$(SIGNATURES)for automatic signature documentation - Add examples in docstrings when helpful
- CRITICAL: ALWAYS use
jldoctestblocks, NEVER use plainjuliablocks in docstrings (see "Docstring Examples" section below for details)
Memory efficiency
- Favor doing lots of computations inline versus allocating temporary memory
- Generally minimize memory allocation
- Design solutions that work within the existing framework
Naming Conventions
- Files: snakecase (e.g., `nonhydrostaticmodel.jl
,computehydrostaticfreesurfacetendencies.jl`) - Types: PascalCase (e.g.,
NonhydrostaticModel,HydrostaticFreeSurfaceModel,SeawaterBuoyancy) - Functions: snakecase (e.g., `timestep!
,compute_tendencies!`) - Kernels: "Kernels" (functions prefixed with
@kernel) may be prefixed with an underscore (e.g.,_compute_tendency_kernel) - Variables: Use either an English long name, or mathematical notation with readable unicode
Module Structure
src/
├── Oceananigans.jl # Main module, exports
├── Architectures.jl # CPU/GPU architecture abstractions
├── Grids/ # Grid types and constructors
├── Fields/ # Field types and operations
├── Operators/ # Finite difference operators
├── BoundaryConditions/ # Boundary condition types
├── Models/ # Model implementations
│ ├── NonhydrostaticModels/
│ ├── HydrostaticFreeSurfaceModels/
│ ├── ShallowWaterModels/
│ └── LagrangianParticleTracking/
├── TimeSteppers/ # Time stepping schemes
├── Solvers/ # Poisson and tridiagonal solvers
├── TurbulenceClosures/ # LES and eddy viscosity models
├── Advection/ # Advection schemes
├── BuoyancyFormulations/ # Buoyancy models
├── OutputWriters/ # File I/O
├── Simulations/ # High-level simulation interface
└── Utils/ # Utilities and helpersTesting Guidelines
Running Tests
# All tests
Pkg.test("Oceananigans")
# Run specific test groups by setting the TEST_GROUP environment variable
ENV["TEST_GROUP"] = "unit" # or "time_stepping", "regression", etc.
Pkg.test("Oceananigans")
# CPU-only (disable GPU)
ENV["CUDA_VISIBLE_DEVICES"] = "-1"
ENV["TEST_ARCHITECTURE"] = "CPU"
Pkg.test("Oceananigans")- GPU tests may fail with "dynamic invocation error". In that case, the tests should be run on CPU. If the error goes away, the problem is GPU-specific, and often a type-inference issue.
Writing Tests
- Place tests in
test/directory - Follow the existing test group structure
- Test on both CPU and GPU when possible
- Name test files descriptively (snake_case)
- Include both unit tests and integration tests
- Test numerical accuracy where analytical solutions exist
Quality Assurance
- Ensure doctests pass
- Use Aqua.jl for package quality checks
Docstring Examples (CRITICAL)
NEVER use plain julia code blocks in docstrings. ALWAYS use jldoctest blocks.
Plain code blocks ($$julia) are NOT tested and can become stale or incorrect. Doctests ($jldoctest$) are automatically tested and verified to work.
✅ CORRECT - use jldoctest:
\"\"\"
my_function(x)
Example:
jldoctest using Oceananigans
grid = RectilinearGrid(size=(4, 4, 4), extent=(1, 1, 1)) typeof(grid)
output
RectilinearGrid{Float64, Periodic, Periodic, Bounded, Nothing, Nothing, Nothing, Nothing}
\"\"\"❌ WRONG - never use plain julia blocks in docstrings:
\"\"\"
my_function(x)
Example:
julia
This code is NOT tested and may be wrong!
grid = RectilinearGrid(size=(4, 4, 4), extent=(1, 1, 1))
\"\"\"Key doctest requirements:
- Always include expected output after
# output - Use simple, verifiable output (e.g.,
typeof(result), accessing a field that returns a simple value) - Doctests should exercise
Base.showto verify objects display correctly - Keep doctests minimal but complete enough to verify the feature works
Common Development Tasks
Adding New Physics or Features
- Create module in appropriate subdirectory
- Define types/structs with docstrings
- Implement kernel functions (GPU-compatible)
- Add unit tests
- If the user interface is changed, update main module exports in
src/Oceananigans.jl - Add validation examples in
validation/orexamples/when appropriate
Modifying Core Models
- Nonhydrostatic model:
src/Models/NonhydrostaticModels/ - Hydrostatic model:
src/Models/HydrostaticFreeSurfaceModels/ - Shallow water model:
src/Models/ShallowWaterModels/ - Time stepping:
src/TimeSteppers/ - Always maintain compatibility with existing model types
Documentation
Building Docs Locally
julia --project=docs/ docs/make.jlViewing Docs
using LiveServer
serve(dir="docs/build")Documentation Style
- Use Documenter.jl syntax for cross-references
- Include code examples in documentation pages
- Add references to papers from the literature by adding bibtex to
oceananigans.bib, and then a corresponding citation - Make use of cross-references with equations
- In example code, use explicit imports as sparingly as possible. NEVER explicitly import a name that is already exported by the user interface. Always rely on
using Oceananigansfor imports and keep imports clean. Explicit imports should only be used for source code.
Writing Doctests
- Use
jldoctestblocks for testable examples in docstrings - Do NOT use boolean comparisons as the final line (e.g., avoid
x ≈ 1.0orobj isa Type) - Instead, make the final line invoke a
showmethod that prints something useful - This serves two purposes:
- Helps users understand what the code produces
- Tests that our
showmethods are high quality and informative
- Example of what NOT to do:
plt = surface!(ax, T) plt isa CairoMakie.Surface # BAD: boolean comparison # output true - Example of what TO do:
x, y, z = spherical_coordinates(0.0, 0.0) (x, y, z) # GOOD: shows the actual output # output (1.0, 0.0, 0.0)
Writing examples
- Explain at the top of the file what a simulation is doing
- Let code "speak for itself" as much as possible, to keep an explanation concise. In other words, use a Literate style.
- Use visualization interspersed with model setup or simulation running when needed to give an understanding of a complex grid, initial condition, or other model property.
- Look at previous examples. New examples should add as much value as possible while remaining simple. This requires judiciously introducing new features and doing creative and surprising things with simulations that will spark readers' imagination.
- Don't "over import". Use names that are exported by
using Oceananigans. If there are names that are not exported, but are needed in common/basic examples, consider exporting those names fromOceananigans.jl. - Literate.jl comment conventions: Examples in
examples/are processed by Literate.jl.- Single
#comments become markdown blocks in the generated documentation - Double
##comments remain as code comments within code blocks - Use
##for inline code comments that should stay with the code (e.g.,## Helper function) - Use single
#only for narrative text that should render as markdown
- Single
Important Files to Know
Core Implementation
src/Oceananigans.jl- Main module, all exportssrc/Models/NonhydrostaticModels/nonhydrostatic_model.jl- Nonhydrostatic model definitionsrc/Models/HydrostaticFreeSurfaceModels/hydrostatic_free_surface_model.jl- Hydrostatic modelsrc/Grids/- Grid implementationssrc/Fields/- Field types and operationssrc/TimeSteppers/- Time integration schemes
Configuration
Project.toml- Package dependencies and compat boundstest/runtests.jl- Test configuration
Examples
examples/langmuir_turbulence.jl- Ocean mixed layer with Langmuir turbulenceexamples/internal_wave.jl- Internal wave propagationexamples/shallow_water_Bickley_jet.jl- Shallow water instabilityexamples/baroclinic_adjustment.jl- Baroclinic instabilityexamples/two_dimensional_turbulence.jl- 2D turbulence- Many more examples in the
examples/directory
Physics Domain Knowledge
Ocean and Fluid Dynamics
- Incompressible/Boussinesq approximation for ocean flows
- Hydrostatic approximation for large-scale flows
- Free surface dynamics with implicit/explicit time stepping
- Coriolis effects and planetary rotation
- Stratification and buoyancy-driven flows
- Turbulence modeling via LES and eddy viscosity closures
Numerical Methods
- Finite volume on structured grids (Arakawa C-grid)
- Staggered grid locations: velocities at cell faces, tracers at cell centers
- Various advection schemes: centered, upwind, WENO
- Pressure Poisson solver for incompressibility constraint
- Time stepping: RungeKutta, Adams-Bashforth, Quasi-Adams-Bashforth
- Take care of staggered grid location when writing operators or designing diagnostics
Common Pitfalls
- Type Instability: Especially in kernel functions - ruins GPU performance
- Overconstraining types: Julia compiler can infer types. Type annotations should be used primarily for multiple dispatch, not for documentation.
- Forgetting Explicit Imports: Tests will fail - add to using statements
- Using plain
juliablocks in docstrings: NEVER do this. ALWAYS usejldoctestblocks so examples are tested and verified to work. Plainjuliablocks are not tested and will become stale.
Implementing Validation Cases / Reproducing Paper Results
When implementing a simulation from a published paper:
1. Parameter Extraction
- Read the paper carefully and extract ALL parameters: domain size, resolution, physical constants, boundary conditions, initial conditions, forcing, closure parameters
- Look for parameter tables (often "Table 1" or similar)
- Check figure captions for additional details
- Note the coordinate system and conventions used
2. Geometry Verification (BEFORE running long simulations)
- Always visualize the grid/domain geometry first
- Check that:
- Domain extents match the paper
- Topography/immersed boundaries are correct
- Coordinate orientations match (which direction is "downslope"?)
- Compare your geometry plot to figures in the paper
3. Initial Condition Verification
- After setting initial conditions, check:
minimum(field)andmaximum(field)make physical sense- Spatial distribution looks correct (visualize if needed)
- Dense water is where it should be, stratification is correct, etc.
4. Short Test Runs
Before running a long simulation:
- Run for a few timesteps on CPU at low resolution
- Verify:
- No NaNs appear (check
maximum(abs, u)etc.) - Flow is developing as expected (velocities increasing from zero)
- Output files contain meaningful data
- No NaNs appear (check
- Then test on GPU to catch GPU-specific issues
5. Progressive Validation
- Run a short simulation (e.g., 1 hour sim time) and visualize
- Check that the physics looks right:
- Dense water flowing in the correct direction?
- Velocities reasonable magnitude?
- Mixing/entrainment happening where expected?
- Compare to early-time figures in the paper if available
6. Comparison to Paper Figures
- Create visualizations that match the paper's figure format
- Use the same colormaps, axis ranges, and time snapshots if possible
- Quantitative comparison: compute the same diagnostics as the paper
7. Common Issues
- NaN blowups: Usually from timestep too large, unstable initial conditions, or if-else statements on GPU (use
ifelseinstead) - Nothing happening: Check that buoyancy anomaly has the right sign, that initial conditions are actually applied, that forcing is active
- Wrong direction of flow: Check coordinate conventions (is y increasing upslope or downslope?)
- GPU issues: Avoid branching, ensure type stability, use
randn()carefully
Git Workflow
- Follow ColPrac (Collaborative Practices for Community Packages)
- Create feature branches for new work
- Write descriptive commit messages
- Update tests and documentation with code changes
- Check CI passes before merging
Helpful Resources
- Oceananigans docs: https://clima.github.io/OceananigansDocumentation/stable/
- Discussions: https://github.com/CliMA/Oceananigans.jl/discussions
- KernelAbstractions.jl: https://github.com/JuliaGPU/KernelAbstractions.jl
- Reactant.jl: https://github.com/EnzymeAD/Reactant.jl
- Reactant.jl docs: https://enzymead.github.io/Reactant.jl/stable/
- Enzyme.jl: https://github.com/EnzymeAD/Enzyme.jl
- Enzyme.jl docs: https://enzyme.mit.edu/julia/dev
- YASGuide: https://github.com/jrevels/YASGuide
- ColPrac: https://github.com/SciML/ColPrac
When Unsure
- Check existing examples in
examples/directory - Look at similar implementations in the codebase
- Review tests for usage patterns
- Ask in GitHub discussions
- Check documentation in
docs/src/ - Check validation cases in
validation/
AI Assistant Behavior
- Prioritize type stability and GPU compatibility
- Follow established patterns in existing code
- Add tests for new functionality
- Update exports in main module when adding public API
- Consider both CPU and GPU architectures
- Reference physics equations in comments when implementing dynamics
- Maintain consistency with the existing codebase style
Current Development Focus
Active areas of development to be aware of:
- Performance optimization: Reactant.jl integration for XLA compilation
- Enzyme.jl integration: Automatic differentiation support
- Multi-architecture support: AMD, Metal, OneAPI in addition to CUDA
- MPI/distributed computing: Improvements to distributed grid capabilities
- Output improvements: NetCDF and other output format enhancements
- Grid flexibility: Enhanced support for lat-lon, cubed sphere, immersed boundaries
- Lagrangian particle tracking: Enhanced particle capabilities
- Validation: Expanding the validation test suite