Skip to content

Address Copilot review: curl retries, missing config vars, GCC install - #3

Closed
cenamiller wants to merge 91 commits into
masterfrom
feature-ci-test-cases
Closed

Address Copilot review: curl retries, missing config vars, GCC install#3
cenamiller wants to merge 91 commits into
masterfrom
feature-ci-test-cases

Conversation

@cenamiller

Copy link
Copy Markdown
Collaborator

Summary

Follow-up fixes from Copilot review comments on PR #2. Addresses 3 of 4 suggestions (the 4th — changing ECT_PERTURB_MAGNITUDE to 1e-14 — was intentionally left as-is since the current value of 1e-5 is correct for single-precision runs).

  • validate-ect/action.yml: Add --retry 5 --retry-delay 5 to the curl download of the ensemble summary file, consistent with all other download steps in the CI.
  • 120km/config.env: Add missing RESOLUTION=120km and DATA_REPO=NCAR/mpas-ci-data variables required by the download-testdata action.
  • unit-tests.yml: Add apt-get install step for the GCC toolchain since gcc-14/gfortran-14 are not guaranteed on ubuntu-latest.

Test plan

  • Verify unit-tests.yml passes across GCC 12/13/14 after merge

Made with Cursor

cenamiller and others added 30 commits February 23, 2026 15:17
- Add build-mpas and run-mpas composite actions
- Add validate-logs action with compare_logs.py
- Add test-case configs and reference logs for 240km and 120km
- Add coverage.yml workflow for Codecov integration
- Add test-ga-nogpu.yml and CIRRUS workflow updates
- Download test case archives from NCAR/mpas-ci-data at CI runtime

Co-authored-by: Cursor <cursoragent@cursor.com>
- Run 240km test case with both 1 and 4 MPI ranks in test-ga-nogpu.yml
- Add decomposition test: compare 4-proc output against 1-proc output
  to verify results are independent of domain decomposition
- Add --decomposition-test and --filter flags to compare_logs.py
- Update validate-logs action with log-filter input

Co-authored-by: Cursor <cursoragent@cursor.com>
Based on Price-Broncucia et al. (2025), doi:10.5194/gmd-18-2349-2025

- Add ect-test.yml: runs 3 perturbed 120km members and validates
  against a pre-built ensemble summary file using PyCECT. Intended
  for code changes not expected to alter science.
- Add ect-ensemble-gen.yml: generates 200 perturbed ensemble members
  (batched 10/job) and produces a summary file via pyEnsSumMPAS.
  Manual-only, for major version/science changes.
- Add perturb-ic action: applies O(10^-14) perturbation to theta
  field in IC file. Placeholder until native MPAS-A perturbation
  code is re-added.
- Add ECT parameters to 120km config.env

Co-authored-by: Cursor <cursoragent@cursor.com>
The tar tzf | head -1 pipeline triggers SIGPIPE (exit 141) under
bash -o pipefail when head closes the pipe. Add || true and a
fallback directory detection, matching the pattern already used
in run-mpas/action.yml.

Co-authored-by: Cursor <cursoragent@cursor.com>
The AlmaLinux 9 container does not have pip on PATH. Bootstrap it
with python3 -m ensurepip, then use python3 -m pip for installs.

Co-authored-by: Cursor <cursoragent@cursor.com>
AlmaLinux 9 containers only provide python3 on PATH.

Co-authored-by: Cursor <cursoragent@cursor.com>
The run job was skipped entirely when any build matrix entry failed
because it lacked an `if: !cancelled()` condition. Now it proceeds
for whichever builds succeeded, with continue-on-error on the
artifact download so individual entries gracefully skip when their
build artifact is missing. The validate job also handles the case
where no logs are available.

Matches the pattern already used in CIRRUS_build_run240.yml.

Co-authored-by: Cursor <cursoragent@cursor.com>
gfortran exits with code 2 on IEEE_UNDERFLOW/DENORMAL warnings even
when the run completes successfully. Wrap mpirun with set +e/set -e
so the loop can continue, and use the presence of a history file as
the real success indicator instead of the exit code.

Co-authored-by: Cursor <cursoragent@cursor.com>
The output stream in streams.atmosphere had output_interval="none",
so no history files were ever written. Add sed to set the output
stream interval to ECT_HISTORY_INTERVAL (30 min), producing 4 time
slices in the 2-hour run (needed for ECT_TSLICE=3).

The apparent exit-code-2 crash was actually from `ls history.*.nc |
tail -1` failing under set -e + pipefail when no files existed. Fix
with || true.

Co-authored-by: Cursor <cursoragent@cursor.com>
Show clear markers for when perturbation starts, model run starts,
and model run finishes, with wall-clock timestamps for each.

Co-authored-by: Cursor <cursoragent@cursor.com>
After generating the ensemble summary, push it to NCAR/mpas-ci-data
along with ect_summary_metadata.json containing MPAS version, branch,
commit, resolution, ensemble size, date, and workflow run link.

Requires MPAS_CI_DATA_TOKEN secret (PAT with repo scope). Gracefully
skips if the secret is not configured.

Co-authored-by: Cursor <cursoragent@cursor.com>
- Create .github/test-cases/ect-120km/config.env with all ECT
  parameters (run settings, PyCECT config, perturbation settings,
  summary file naming) in one place.
- Remove ECT parameters from .github/test-cases/120km/config.env.
- Version summary files: archived as ect-summaries/<prefix>_v<ver>_<date>.nc
  in mpas-ci-data; current copy kept as <prefix>.nc for ect-test.yml.
- Both ECT workflows now source from ect-120km/config.env.

Co-authored-by: Cursor <cursoragent@cursor.com>
The EOF heredoc terminator at column 1 inside a YAML block scalar
was parsed as a YAML key, breaking the workflow file. Replace the
heredoc with string concatenation.

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
OpenMPI refuses to run more processes than available CPU slots
without --oversubscribe. GitHub Actions runners may expose fewer
cores than requested ranks. Also adds CPU/RAM diagnostics to
help debug the separate gcc/mpich3 heap corruption issue.

Co-authored-by: Cursor <cursoragent@cursor.com>
Both combinations crash with heap corruption during parallel mesh
bootstrapping (glibc malloc assertions). This appears to be a
container library incompatibility rather than an MPAS code issue.
1-proc runs for these combinations continue to work fine.

Co-authored-by: Cursor <cursoragent@cursor.com>
Let gcc/mpich3 and nvhpc/openmpi 4-proc runs fail visibly rather
than hiding them from the matrix.

Co-authored-by: Cursor <cursoragent@cursor.com>
gcc/mpich3 crashes with heap corruption at 4 MPI ranks. gcc/openmpi
works reliably with --allow-run-as-root --oversubscribe flags.

Co-authored-by: Cursor <cursoragent@cursor.com>
…ble size

pyEnsSumMPAS exits 0 even when it fails (e.g. too few members for
the number of variables). Add file existence check after generation
and raise the minimum ensemble count to 63 (matching the ~63 output
variables PyCECT needs to analyze).

Co-authored-by: Cursor <cursoragent@cursor.com>
Remove old workflows and shell scripts that have been replaced by
the current composite actions and workflow structure:

Deleted workflows:
  - smoketest.yml (hardcoded feature-ci branch)
  - build_run240.yml (hardcoded feature-ci branch)
  - CIRRUS_build_run240.yml (replaced by test-cirrus-nvhpc.yml)

Deleted shell scripts (replaced by composite actions):
  - build_mpas.sh -> actions/build-mpas
  - run_mpas_240km.sh -> actions/run-mpas
  - run_mpas.sh -> actions/run-mpas
  - build_mpas_coverage.sh (unused)
  - interrogate_env.sh (only used by deleted workflows)
  - build_common.cfg (only sourced by deleted scripts)

Other cleanup:
  - Remove 240km.tar.gz from tracking (belongs in mpas-ci-data)
  - Remove duplicate reference log from validation/ (canonical
    copy is in test-cases/240km/)
  - Update coverage.yml branch triggers (feature-ci -> develop)
  - Add .DS_Store, test archives, editor dirs to .gitignore
  - Add AGENT_GUIDE.md for AI assistant context

Remaining active workflows:
  - test-ga-nogpu.yml (GitHub Actions build/run)
  - test-cirrus-nvhpc.yml (CIRRUS NVHPC GPU/CPU)
  - ect-ensemble-gen.yml (ECT ensemble generation)
  - ect-test.yml (ECT validation)
  - coverage.yml (code coverage)

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Three combined optimizations to shrink history files from ~556MB to
an estimated ~50-80MB per member (with compression):

1. Reduce time slices: set output_interval = run_duration so MPAS
   writes only at t=0 and t=2h instead of every 30 minutes (5 -> 2).

2. Remove 22 unneeded variables: Ertel PV diagnostics (16 expensive
   3D fields), PV tendency terms, edge velocity, integers, and time
   metadata. List maintained in ect_excluded_vars.txt.

3. Compress + extract: trim_history.py extracts only the target time
   slice and applies NetCDF4 deflation before artifact upload.

At ~70MB per member, 200 members = ~14GB, fitting within the ~30GB
free disk on standard GitHub Actions runners.

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Avoid any risk of data alteration in the ECT validation pipeline.
Deflation is technically lossless but adds unnecessary complexity.

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Centralizes test case download + extraction into a reusable action at
.github/actions/download-testcase/. Replaces duplicated curl/tar/mv
blocks in run-mpas, ect-ensemble-gen, and ect-test workflows.

Also adds RESOLUTION and DATA_REPO to 240km/config.env for
consistency with the ect-120km config.

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
…d_mpas.sh

The build_mpas.sh script was removed during workflow cleanup but
build-mpas/action.yml still referenced it. Inlines the compiler-to-make-target
mapping and make command directly in the action.

Co-authored-by: Cursor <cursoragent@cursor.com>
Each ect-validate matrix job now writes its result (PASSED/FAILED/
SKIPPED/ERROR) to a small artifact. A new ect-summary job collects
all results and renders a single markdown table to the GitHub Actions
job summary, showing all compiler/MPI/IO/rank combinations at a glance.

Made-with: Cursor
Add actions/cache to avoid re-downloading large test case archives
(120km ~280MB, 240km ~77MB) from GitHub LFS on every job. The cache
key is testdata-v1-{archive} — bump the v1 prefix to bust the cache
when test data is updated. On cache hit, only the fast local tar
extraction runs.

Made-with: Cursor
All workflow_dispatch workflows now accept mpas-repository and mpas-ref
inputs, allowing CI to build and test MPAS source from any public fork
(e.g. MPAS-Dev/MPAS-Model) using this repos CI infrastructure. Build
jobs use a two-step checkout: MPAS source first, then overlay .github/
from MPAS-Model-CI so composite actions resolve correctly.

Made-with: Cursor
Adds repository layout tree, branch structure, workflow overviews,
MPI matrix values and mapping logic, ECT job structure details,
test data caching, CLI examples, development history, and expanded
pitfalls list including numpy compat and NVHPC portability.

Made-with: Cursor
Centralizes the PyCECT version in ect-120km/config.env as PYCECT_TAG
so all four ECT workflows clone the same pinned release instead of
HEAD. Prepares for more frequent upstream PyCECT updates.

Made-with: Cursor
Layer 2 — ECT Modularity:
- Create validate-ect action encapsulating PyCECT install, clone,
  summary download, validation, and enriched result file output
- Create ect-summary action with auto-discovered columns from
  key=value dimension metadata in result files
- Refactor ect-validate/ect-summary jobs in test-ga-nogpu.yml,
  test-cirrus-nvhpc.yml, and ect-test.yml to use new actions
  (net -240 lines of duplicated inline logic)

Layer 1 — Docs Generator Rewrite:
- Restructure generate_ci_docs.py for contributor audience: lead
  with what CI checks and why, resolve matrix values into human-
  readable counts, add "How to read CI results" section, move
  developer details into collapsible reference
- Filter template expressions from container image output
- Use MkDocs admonitions and collapsible sections

Update AGENT_GUIDE.md with new actions and repo layout.

Made-with: Cursor
Draft materials for sharing current MPAS CI status with NOAA/GSL
collaborators and for the upcoming ISS talk.

Made-with: Cursor
MPAS writes an initial-state time slice before the main loop for
cold-start (non-restart) runs. The old hardcoded tslice=0 grabbed
that un-integrated state instead of the 6-hour forecast, causing
PyCECT to see near-zero ensemble variability.

The action now auto-detects the last time slice in each history
file and passes it to trim_history.py, which also gains support
for negative indexing (e.g. -1 = last).

Made-with: Cursor
Adapted from ESCOMP/CAM-SIMA MPAS dynamical core CI. Runs
fortitude-lint on src/ with --exit-zero (non-blocking) to
establish a baseline. Excludes src/external and WRF physics.

Triggered on PRs and pushes that touch src/ or linting config.

Made-with: Cursor
Adapted from ESCOMP/CAM-SIMA MPAS dynamical core CI. Sets up:
- CMake build system for testable MPAS procedures (tests/)
- pFUnit test framework with GCC 12/13/14 matrix
- Starter test suite for mpas_spline_interpolation: linear
  interpolation, cubic spline coefficients, node reproduction,
  accuracy against sin(x), and integration of linear/sine functions

The test library builds only standalone modules with minimal
dependencies (currently mpas_kind_types + mpas_spline_interpolation).
More source files and tests can be added incrementally.

Made-with: Cursor
gfortran treats .F as fixed-form Fortran, but MPAS sources are
free-form with CPP directives. Add -cpp -ffree-form (GNU) and
-fpp -free (Intel) to the testable_procedures library.

Also update fortitude linting to use reviewdog for PR-scoped
annotations (only flags issues on changed lines).

Made-with: Cursor
Reverts fortran-linting.yml and fortitude_config.toml to their
original versions; reviewdog integration was not desired.

Made-with: Cursor
Prepend docker.io/ to all ncarcisl container image references.
Required by upcoming Cirrus cluster upgrade that no longer assumes
docker.io as the default registry.

Made-with: Cursor
Move the restart file push from the generate-summary job to the
spinup job so it persists even if later steps (e.g. pyEnsSumMPAS)
fail. Also cache the restart with actions/cache so ect-test and
cirrus workflows can skip the 1.2 GB download when a cache hit
is available.

Made-with: Cursor
MPICH4 F08 bindings call CFI_is_contiguous at runtime, which crashes
with unsupported array rank on NVHPC versions before 2024 due to
incomplete CFI support. Force MPAS_MPI_F08=0 so MPAS uses the standard
mpi module instead, bypassing the broken code path entirely.

See: pmodels/mpich#6505
Made-with: Cursor
in prep for PR to master branch
Preparing for PR for master branch
MPICH_GPU_SUPPORT_ENABLED=0 alone is insufficient to prevent yaksa
from probing CUDA during MPI_Init, causing SIGSEGV on CUDA runners.
MPIR_CVAR_ENABLE_GPU=0 disables GPU initialization at a lower level.
OpenACC GPU computation is unaffected.

See: pmodels/mpich#6523
Made-with: Cursor
The nvhpc-mpich-cuda and nvhpc-mpich3-cuda container images do not set
LD_LIBRARY_PATH in config_env.sh, unlike the OpenMPI variant. Without
it the NVHPC runtime cannot find libcuda.so injected by the Kubernetes
NVIDIA device plugin, causing cuInit to fail with error 34. Set a
fallback LD_LIBRARY_PATH when the container does not provide one.

Made-with: Cursor
The O(1e-14) perturbation magnitude from Price-Broncucia et al. (2025)
is below single-precision machine epsilon (~1.2e-7), causing all ensemble
members to produce identical results. Build MPAS with PRECISION=double
for all ECT workflows so perturbations survive the float representation.

Shorten ECT member runs from 6h (30 steps) to 2h36m (13 steps at the
12-min default dt), just past the ~12 step normality transition the
paper identifies. The Cirrus workflow gets a separate build-ect job so
regular 240km runs remain single precision.

Made-with: Cursor
- Add compiler and mpi-impl workflow_dispatch inputs (default: nvhpc/mpich)
- Derive container image from inputs so all jobs use consistent toolchain
- Fix metadata to record actual MPAS source repo/branch/commit instead of
  workflow trigger context (GITHUB_REF_NAME/GITHUB_SHA)
- Remove dead ECT_RAW_TSLICE variable
- Use dynamic ECT_SUMMARY_PREFIX based on RESOLUTION variable
- Update perturbation magnitude to 1e-7 for testing
- Clarify run duration and output interval comments in config.env

Made-with: Cursor
The env context is not available in job-level container.image or name
fields. Replace env.CONTAINER_IMAGE, env.COMPILER, and env.MPI_IMPL
with inline inputs expressions.

Made-with: Cursor
…n failure

- Add validation step that checks each history file with netCDF4 before
  passing to pyEnsSumMPAS, removing corrupted files from artifact download
- Split cleanup into two steps: always delete build artifacts, but only
  delete ensemble history artifacts after generate-summary succeeds.
  This allows re-running the summary job after transient download failures.

Made-with: Cursor
Record compiler, MPI implementation, precision, run duration,
perturbation magnitude, and perturbation variable in both the
ect_summary_metadata.json pushed to mpas-ci-data and the workflow
summary log. Filenames stay stable for consumers; the metadata
JSON provides full traceability of how each summary was generated.

Made-with: Cursor
Made-with: Cursor

# Conflicts:
#	.github/test-cases/240km/reference_log.atmosphere.0000.out
- Add --retry 5 --retry-delay 5 to curl in validate-ect (consistency
  with other download steps)
- Add RESOLUTION and DATA_REPO to 120km/config.env (required by
  download-testdata action)
- Add GCC toolchain install step to unit-tests.yml (gcc-14 not
  guaranteed on ubuntu-latest)

Made-with: Cursor
Copilot AI review requested due to automatic review settings March 24, 2026 21:00
@cenamiller cenamiller closed this Mar 25, 2026
@cenamiller
cenamiller deleted the feature-ci-test-cases branch March 26, 2026 21:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant