Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
128 changes: 128 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
# CAM AI agent guidance

This repository (`ESCOMP/CAM`) is the Community Atmosphere Model, the production
atmosphere component of CESM. The default branch is `cam_development`; the `main`
branch contains only a README to prevent inadvertent checkouts of unsupported code.

The atmos_phys submodule (`src/atmos_phys`) contains the CCPP-compliant physics shared
by CAM and CAM-SIMA and has its own `AGENTS.md`; work there follows that guidance and
is PR'd to `ESCOMP/atmospheric_physics`. Using a new atmos_phys scheme directory in CAM
additionally requires adding it to the Filepath list in `bld/configure` (directories
are not discovered automatically).

## AI disclosure

CAM has a higher bar for AI-reported issues and bug reports.
If AI is used to discover or author ANY GitHub issue:
- The issue must clearly state that AI was used.
- The reporter must additionally create a run demonstrating the bug is real and not an
AI hallucination, and include the details to recreate that run in the issue.

CAM **requires** disclosure of AI-assisted contributions in PR descriptions or commit messages.
The human contributor remains responsible for all committed code at all times.

## If you remember nothing else

1. **Every PR states its answer impact.** The default for refactors, cleanups, and bug
infrastructure is bit-for-bit: do not reorder floating-point arithmetic, change
parenthesization, or "simplify" expressions as a side effect of other work.
2. **Never hand-edit generated chemistry code.** In each `src/chemistry/pp_*` directory
the Fortran (`mo_*.F90`, `chem_mods.F90`, ...) is generated by the `chem_proc`
preprocessor (submodule at `chem_proc/`) from the mechanism file `chem_mech.in`.
Change the mechanism and regenerate; never edit the generated solver code.
3. **Full builds require an HPC machine with a CIME port**: NCAR's Derecho/Izumi or a
ported university cluster; you cannot produce a working executable on a laptop.
Answer impacts are only possible to validate on the full set of regression tests
(`aux_cam`, run via `test/system/test_driver.sh` on NCAR machines and coordinated
with the CAM gatekeepers). Unless the user says they are on a machine where CAM
builds, hand off build and test verification to the human.
4. **Externals are Git submodules** checked out with `bin/git-fleximod update` (pinned
by `fxtag` in `.gitmodules`). All submodules must be tagged. Hashes are not acceptable.
5. **CAM conventions are not CCPP/CAM-SIMA conventions.** Chunked physics arrays sized
with `pcols`, the physics buffer (`pbuf`), `ptend`, `outfld`/`addfld`, `endrun`, and
`r8` are correct in this codebase. Do not "modernize" CAM code toward CCPP style.
Portable CCPP schemes live in `src/atmos_phys` and follow that repo's rules instead.
New physics parameterizations should be developed as portable CCPP schemes in
atmospheric_physics where feasible (consult the CAM SEs), not as new CAM-side physics.

## Build system

CAM builds with CIME (not directly with CMake or make) from a case directory. Source
directories are not auto-discovered: `bld/configure` writes the Filepath list of
directories to compile, and a new source directory must be added there. New `.F90`
files in an already-listed directory are auto-discovered.

## Namelists

The runtime namelist (`atm_in`) is generated by the Perl script `bld/build-namelist`
(invoked by CIME via `cime_config/buildnml`). A variable's effective value comes from
the first of these sources that sets it, highest precedence first:

1. `CAM_NAMELIST_OPTS` (case XML variable, set by some compsets).
This silently overrides `user_nl_cam`; such values can only be changed with
`xmlchange CAM_NAMELIST_OPTS`.
2. `user_nl_cam` in the case directory. Case-derived values (`ncdata`, `dtime`, ...)
are injected here by `buildnml`, but explicit `user_nl_cam` lines override them.
3. The use case XML `bld/namelist_files/use_cases/<name>.xml`, selected by the
`CAM_NML_USE_CASE` case XML variable (set by the compset).
4. `bld/namelist_files/namelist_defaults_cam.xml`, applied only where `build-namelist`
calls `add_default` for that variable; without such a call the XML default is inert.
Among multiple entries for one variable, the best fit is chosen by matching entry
attributes (`dyn`, `phys`, `chem`, `hgrid`, ...) against the build configuration
(`config_cache.xml`, from `configure`); ties go to the first entry in the file.
5. If nothing above sets it, the variable is absent from `atm_in` and the run silently
uses whatever the owning Fortran module initialized it to (e.g.
`apply_lunar_tides = .false.` in `lunar_tides.F90`). Many variables in
`namelist_definition.xml` have no `add_default` call and work this way.

So `namelist_defaults_cam.xml` alone never tells you what value a run used. When a case
exists, read the generated `atm_in` in the run directory or `CaseDocs/`
(`./preview_namelists` regenerates it); a variable absent there falls back to its
Fortran initialization (point 5).

A new namelist variable needs:
1) an entry in `bld/namelist_files/namelist_definition.xml`,
2) a default in `namelist_defaults_cam.xml` plus the `add_default` call
in `bld/build-namelist` that applies it, and
3) a read in the owning module's `*_readnl` routine (masterproc read + MPI broadcast;
follow an existing `*_readnl` as the template).
Initialize non-logical namelist variables to invalid sentinels so an unset variable
fails loudly instead of running with a hidden compiled-in default.

## PRs and ChangeLog

- PRs target `cam_development` from a personal fork (never push branches under
`ESCOMP`) and are paired with a GitHub issue.
- Before merge, add a `doc/ChangeLog` entry following `doc/ChangeLog_template`,
stating answer impact and any namelist/build changes.

## Fortran code style

Full standards: https://github.com/ESCOMP/CAM/wiki/CAM-Coding-Standards. Much legacy
code predates them; follow the standards for new and modified lines, but do not
reformat code you are not otherwise changing.

1. Every `real` variable and literal has an explicit kind: `1.5_r8` with `r8` from
`shr_kind_mod`, never `1.5` or `1.5d0`. Literals include the decimal point.
2. Never initialize a local variable on its declaration line; it acquires implicit
`SAVE` and is not thread-safe. Initialize at the top of the executable section.
3. No naked `use`: always `use mod, only: symbol`, imported at the smallest scope
(inside the subroutine unless needed at module level).
4. Modules have `implicit none` in the preamble and are default `private`; public
interfaces are declared explicitly.
5. Physics-grid arrays declare their horizontal dimension with `pcols`.
6. Optional arguments are passed by keyword, never positionally.
7. `intent` on all dummy arguments except pointers; declarations use `::` and
`character(len=*)`-style syntax; one dummy argument declaration per line, in
argument-list order.
8. Functions have no side effects and carry the `pure` keyword (or document in the
preamble why they cannot); functions may not use pointers.
9. Multi-line `if` statements use `then`; no semicolons joining statements; symbolic
comparison operators (`==`, `/=`, `<`), not `.eq.`-style.
10. Spaces, not tabs; no trailing whitespace; lines under 133 characters; indentation
follows scope (3 spaces recommended, self-consistent within a module).
11. Avoid preprocessor `#if`/`#ifdef` where runtime logic works; break long formulas
into temporaries; do not keep commented-out code "for later".

Python must pass pylint with a score of 10.0 using the configuration described in the
coding standards wiki page.
Loading