diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000000..b68329f973 --- /dev/null +++ b/AGENTS.md @@ -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/.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.