Skip to content

Commit 21eed46

Browse files
authored
Merge branch 'develop' into pr_3888
2 parents f14de15 + 065d7fc commit 21eed46

35 files changed

Lines changed: 1037 additions & 313 deletions
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
name: Docker build (fork PRs)
2+
3+
# A fast check for Docker build errors introduced by the current PR.
4+
# Implemented separately from the full-stack build in docker.yml because
5+
# pull requests opened from forks get a read-only GITHUB_TOKEN and cannot push
6+
# to ghcr.io/pecanproject, so the normal Docker GHA workflow (docker.yml) skips
7+
# them (see the gate on its `rversion` job). This workflow gives those PRs a
8+
# real "does the image stack still build" signal without pushing anything.
9+
#
10+
# The images build on top of each other (depends -> base -> models -> sipnet).
11+
# The normal workflow runs each as a separate job and pulls the parent back from
12+
# ghcr, which needs a registry we can't push to from a fork. Here we build the
13+
# whole chain in a single job with plain `docker build`, so each image stays in
14+
# the runner's local image store and the next `FROM pecan/<parent>:latest`
15+
# resolves locally. No registry, no push, no secrets.
16+
#
17+
# Scope is the core linear chain only (depends -> base -> models -> sipnet). It
18+
# fits the default runner disk (~19 GB free; the resident stack is ~5-6 GB). The
19+
# other model binaries, baseplus (docs/executor/api) and extras images are not
20+
# built here to keep the job within disk and time budget.
21+
22+
on:
23+
pull_request:
24+
workflow_dispatch:
25+
26+
permissions:
27+
contents: read
28+
29+
jobs:
30+
build-stack:
31+
# Only fork PRs. Same-repo PRs are already covered by docker.yml, which can
32+
# push pr-* tags to ghcr because they run with a writable token.
33+
if: github.event.pull_request.head.repo.full_name != github.repository
34+
runs-on: ubuntu-latest
35+
env:
36+
R_VERSION: "4.4"
37+
steps:
38+
- uses: actions/checkout@v6
39+
40+
- name: Disk space before build
41+
run: df -h /
42+
43+
# NOTE: do not add docker/setup-buildx-action here. Plain `docker build`
44+
# uses the default docker-engine builder, which resolves each
45+
# `FROM pecan/<parent>:latest` from the local image store built by the
46+
# previous step. The docker-container buildx driver would not see those
47+
# local images and every FROM after depends would fail.
48+
- name: Build depends -> base -> models -> sipnet (no push)
49+
run: |
50+
set -euxo pipefail
51+
52+
# depends: FROM rocker/tidyverse:${R_VERSION}
53+
docker build -f docker/depends/Dockerfile \
54+
--build-arg R_VERSION="${R_VERSION}" \
55+
-t pecan/depends:latest \
56+
docker/depends
57+
58+
# base: FROM pecan/depends:latest (resolved from the local store)
59+
docker build -f docker/base/Dockerfile \
60+
-t pecan/base:latest \
61+
.
62+
63+
# models: FROM pecan/base:latest
64+
docker build -f docker/models/Dockerfile \
65+
-t pecan/models:latest \
66+
docker/models
67+
68+
# sipnet: FROM pecan/models:latest
69+
docker build -f models/sipnet/Dockerfile \
70+
--build-arg MODEL_VERSION=git \
71+
-t pecan/model-sipnet-git:latest \
72+
models/sipnet
73+
74+
- name: Disk space after build
75+
if: always()
76+
run: df -h /

.github/workflows/docker.yml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,15 @@ jobs:
4242
# As an ugly workaround, we assign it to a job output instead.
4343
# ----------------------------------------------------------------------
4444
rversion:
45+
# Every build job below needs this job, so gating it here skips the whole
46+
# push-based Docker stack for pull requests opened from forks. Forks get a
47+
# read-only GITHUB_TOKEN and cannot push to ghcr.io/pecanproject, so those
48+
# jobs always failed at the push step. Fork PRs are instead built (without
49+
# pushing) by docker-build-pr.yml. Non-fork events (push, merge_group,
50+
# schedule, workflow_dispatch) and same-repo PRs still run the full stack.
51+
if: >-
52+
github.event_name != 'pull_request' ||
53+
github.event.pull_request.head.repo.full_name == github.repository
4554
runs-on: ubuntu-latest
4655
steps:
4756
- id: mon

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ For more information about this file see also [Keep a Changelog](http://keepacha
99
## Unreleased
1010

1111
### Added
12+
- Added `make_scorecard.sh` and documentation to `inst/ilamb/` in PEcAn.benchmark for generating and serving the ILAMB HTML scorecard.
1213
- New function `PEcAn.utils::netcdf2df()` flattens all dims and vars of a netCDF into a dataframe,
1314
with units attached as an attribute.
1415
- New package `PEcAn.RothC` runs the RothC soil carbon model.
@@ -31,6 +32,7 @@ For more information about this file see also [Keep a Changelog](http://keepacha
3132
- Added `PEcAn.data.land::event_parquet_to_json` for generating PEcAn `event.json` files from well-formatted event parquet files, with support for ensembles of events.
3233

3334
### Fixed
35+
- Docker GHA workflow no longer fails on pull requests opened from forks (#3618).
3436
- Removed unused `grid2netcdf()` from `PEcAn.data.remote` and fixed R CMD check reference notes for `download.LandTrendr.AGB()` (#2758).
3537
- Fixed broken pecanproject.github.io, pecan.gitbooks.io, and other outdated documentation links across book_source, tutorials, models, modules, web, and shiny files (#3710).
3638
- Added note to DEV-INTRO.md documenting Traefik workaround for Apple Silicon (ARM64) Macs: use `traefik:v2.11` with `platform: linux/arm64` to fix 404 errors (#3910)

docker/depends/pecan_package_dependencies.csv

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -495,6 +495,7 @@
495495
"rcrossref","*","base/db","Suggests",FALSE
496496
"readr","*","models/ldndc","Imports",FALSE
497497
"readr","*","modules/assim.sequential","Suggests",FALSE
498+
"readr","*","modules/data.land","Suggests",FALSE
498499
"REddyProc","*","modules/data.atmosphere","Imports",FALSE
499500
"redland","*","modules/data.land","Suggests",FALSE
500501
"reshape","*","modules/data.remote","Suggests",FALSE
@@ -700,6 +701,7 @@
700701
"units","*","modules/data.atmosphere","Imports",FALSE
701702
"units",">= 0.8.7","base/utils","Imports",FALSE
702703
"urltools","*","base/remote","Imports",FALSE
704+
"usethis","*","modules/data.land","Suggests",FALSE
703705
"utils","*","base/all","Imports",FALSE
704706
"utils","*","base/logger","Imports",FALSE
705707
"utils","*","models/ed","Imports",FALSE

modules/benchmark/inst/ilamb/README.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -296,3 +296,21 @@ score relative to the other variables, and the `weight` on each dataset block
296296
sets how much that dataset counts within its variable. Both are relative
297297
weights, not percentages. See the inline comments in the config for the full
298298
per-field reference.
299+
300+
## Generating the scorecard
301+
302+
ILAMB produces an interactive HTML scorecard alongside the numeric scores. To
303+
generate it for a window, run `make_scorecard.sh` against that window's model
304+
root (built by the pipeline above):
305+
306+
```bash
307+
export ILAMB_ROOT=/path/to/ILAMB_ROOT
308+
./make_scorecard.sh 2012_2014
309+
./make_scorecard.sh 2015_2023
310+
```
311+
312+
Each run writes a self-contained static site (`index.html` plus assets) to a
313+
build directory, which can be served directly by copying it under a web
314+
server's document root. The scorecard uses the clean model roots, so it lists
315+
PEcAn alongside the individual CMIP6 and TRENDY models and the ensemble means,
316+
rather than the 100 individual PEcAn members used for the spread analysis.
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
#!/bin/bash
2+
#
3+
# Generate the ILAMB HTML scorecard for a PEcAn benchmarking window.
4+
#
5+
# Runs ilamb-run against a window's model root (PEcAn, the individual CMIP6
6+
# and TRENDY models, and the ensemble means) to produce the interactive HTML
7+
# scorecard ILAMB writes to the build directory. This is the public-facing
8+
# comparison view; it uses the clean model roots (no individual PEcAn members),
9+
# so the scorecard lists PEcAn alongside the models rather than 100 member rows.
10+
#
11+
# Prerequisites:
12+
# - ILAMB installed and on PATH (ilamb-run).
13+
# - ILAMB_ROOT set, with the benchmark datasets under $ILAMB_ROOT/DATA.
14+
# - The window model root already built (see build_window_ensembles.py),
15+
# e.g. ilamb_models_2012_2014 / ilamb_models_2015_2023.
16+
#
17+
# Usage:
18+
# ./make_scorecard.sh <window> [model_root] [build_dir]
19+
#
20+
# window Required. A label for the run, e.g. 2012_2014 or 2015_2023.
21+
# model_root Optional. Directory of model entities to score.
22+
# Default: ilamb_models_<window>.
23+
# build_dir Optional. Output directory for the HTML scorecard.
24+
# Default: ilamb_scorecard_<window>.
25+
#
26+
# Example:
27+
# export ILAMB_ROOT=/path/to/ILAMB_ROOT
28+
# ./make_scorecard.sh 2012_2014
29+
# ./make_scorecard.sh 2015_2023
30+
#
31+
# The generated build directory (index.html plus assets) is a self-contained
32+
# static site that can be served directly, for example by copying it under a
33+
# web server's document root.
34+
35+
set -euo pipefail
36+
37+
WINDOW="${1:?usage: make_scorecard.sh <window> [model_root] [build_dir]}"
38+
MODEL_ROOT="${2:-ilamb_models_${WINDOW}}"
39+
BUILD_DIR="${3:-ilamb_scorecard_${WINDOW}}"
40+
CONFIG="$(dirname "$0")/pecan_ilamb.cfg"
41+
42+
if [ -z "${ILAMB_ROOT:-}" ]; then
43+
echo "ERROR: ILAMB_ROOT is not set." >&2
44+
exit 1
45+
fi
46+
if [ ! -d "$MODEL_ROOT" ]; then
47+
echo "ERROR: model root '$MODEL_ROOT' not found. Build it first." >&2
48+
exit 1
49+
fi
50+
51+
echo "Generating scorecard for window '$WINDOW'"
52+
echo " model root: $MODEL_ROOT"
53+
echo " build dir: $BUILD_DIR"
54+
55+
ilamb-run --config "$CONFIG" \
56+
--model_root "$MODEL_ROOT" \
57+
--build_dir "$BUILD_DIR" \
58+
--regions global
59+
60+
echo "Done. Open $BUILD_DIR/index.html, or serve $BUILD_DIR as a static site."

modules/data.land/DESCRIPTION

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,13 +71,15 @@ Suggests:
7171
PEcAn.settings,
7272
PEcAn.visualization,
7373
raster,
74+
readr,
7475
redland,
7576
reticulate,
7677
rjags,
7778
sirt,
7879
sp,
7980
testthat (>= 3.1.0),
8081
traits,
82+
usethis,
8183
withr
8284
Remotes:
8385
github::ropensci/traits

modules/data.land/NAMESPACE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,8 @@ export(get_veg_module)
4646
export(ic_process)
4747
export(id_resolveable)
4848
export(load_veg)
49-
export(look_up_ca_compost_amendment)
5049
export(look_up_ca_n_rate)
50+
export(look_up_ca_organic_amendment)
5151
export(look_up_fertilizer_components)
5252
export(matchInventoryRings)
5353
export(match_pft)

modules/data.land/NEWS.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,12 @@
1212
* Datasets
1313
* `landiq_crop_mapping_codes` dataset mapping LandIQ crop classification codes to human-readable crop names.
1414
* `bism_kc_by_crop` dataset containing BISm crop coefficient schedules and stage timing references for use in ET estimation, including columns that map to LandIQ class and subclass.
15-
* `ca_n_application_rate` dataset with recommended N application rates (g N/m2) for 33 California crops from CDFA-FREP and UC ANR sources.
16-
* `ca_compost_amendment` dataset with C:N ratios, carbon, nitrogen, and PAN (g/m2) for 32 organic amendment materials.
15+
* `ca_n_application_rate` dataset with recommended N application rates (g N/m2) for 40 California crops from CDFA-FREP, UC ANR, Rosenstock 2013, Brown 2020 NBMP (Almonds age stages) and Lazicki 2016 (Potato) sources.
16+
* `ca_organic_amendment_properties` dataset with C:N ratios, nitrogen, and PAN for 32 organic amendment materials, plus a `material_class` column mapping each material to the CalRecycle taxonomy (14 CCR section 17852).
17+
* `ca_organic_amendment_app_rate` dataset with row-crop and orchard application rate envelopes (64 rows = 32 materials x 2 crop structures) that joins to `ca_organic_amendment_properties` on `material`.
1718
* Functions
1819
* `look_up_ca_n_rate()` for looking up crop-specific N application rates by name (exact match first, partial match suggestions on miss).
19-
* `look_up_ca_compost_amendment()` for looking up organic amendment properties by material name.
20+
* `look_up_ca_organic_amendment()` for looking up organic amendment properties by material name.
2021
* `to_co2e()` for converting SOC change, CH4, and N2O to CO2-equivalent emissions using IPCC Global Warming Potential values.
2122

2223
## Changed

0 commit comments

Comments
 (0)