Walkthrough: the ORSA module on a bootstrapped stack¶
Read ORSA climate module first — in particular its "what it is NOT" section. This page demonstrates exactly two things, and is explicit about where each one stops:
- Provision a hazard/exposure pair → submit a scenario × horizon matrix → poll it → read each
cell's EAD/AAI. This is
sdk/python/examples/orsa_matrix.py. - Compose the
orsaPDF pack for that batch and download it, over the batch-shaped report routes issue #415 added. This isscripts/orsa_report_demo.py, a thin HTTP client — same as step 1, it runs from the host and needs no container access. There is no SDK wrapper method or UI button for this step yet, only the two REST routes it drives directly.
POST /v1/compute/impact/matrix resolves each cell's own hazard dataset from its
scenario_label/horizon_year against the catalog (issue #420) — a cell is no longer a
relabeled copy of the same run. Because the walkthrough's provisioning step
(scripts/smoke_e2e.py) ingests only one hazard dataset (scenario: "baseline", no scenario
siblings), that resolution plays out unevenly on the stack this page builds:
- The
baselinecolumn resolves every horizon to that one ingested dataset, so its EAD/AAI is identical across2030/2050/2080— correctly, since there is genuinely only one baseline dataset to run against. - The
ssp2-4.5/ssp5-8.5columns have no matching dataset on this stack, so those six cells resolve to a clear per-cell error (job_id: null,status: null— never dispatched — anderror_messagenaming the missing scenario/horizon) instead of silently reusing the baseline dataset. That is the correct, current behaviour on this stack — not a bug in this walkthrough.
To see every cell actually differentiated, ingest the real scenario-conditioned Aqueduct
pack first — python scripts/ingest_scenario_hazards.py ingest --org-slug <your-org-slug>
(issues #386/#891 — the target org is explicit, there is no default) — which
records each dataset's own scenario/horizon so every cell in a river_flood matrix
over it resolves to a distinct dataset and a distinct EAD. See that script's module
docstring and the EIOPA scenario mapping concept
page for what it ingests and why.
This walkthrough is private-stack-only: everything below runs against your own
docker compose stack, never a hosted environment, and nothing here is published
anywhere.
Prerequisites¶
- Docker and Docker Compose
- Python 3.10+ with
httpxinstalled (pip install httpx— the SDK example imports the SDK directly from the checkout, so nopip install climate-lamais needed) andrequestsinstalled (pip install requests—scripts/orsa_report_demo.pyuses it the same wayscripts/smoke_e2e.pydoes) - A clone of this repository, with
.envcreated from.env.example
1. Bootstrap an admin account¶
Running an impact matrix and requesting a report both require Role.ANALYST; an org_admin
bootstrap admin satisfies that (it ranks above analyst). There is no demo data anywhere in
the product, so this page's stack starts empty and needs an account before anything else. Set
CL_BOOTSTRAP_ADMIN_EMAIL / CL_BOOTSTRAP_ADMIN_PASSWORD in .env (password policy: >=12
chars, >=1 uppercase, >=1 digit):
cat >> .env <<'EOF'
CL_BOOTSTRAP_ADMIN_EMAIL=you@your-org.example
CL_BOOTSTRAP_ADMIN_PASSWORD=SomethingStrong1
EOF
If you already started this stack without these set, wipe and start over so the compose
bootstrap one-shot picks them up:
2. Start the stack and provision a real hazard/exposure pair¶
docker compose up -d --wait
mkdir -p data
cp tests/fixtures/reference_datasets/river_flood.tif data/
cp tests/fixtures/reference_datasets/river_flood_cyprus.tif data/
SDK_SMOKE_EMAIL="$(grep CL_BOOTSTRAP_ADMIN_EMAIL .env | cut -d= -f2)" \
SDK_SMOKE_PASSWORD="$(grep CL_BOOTSTRAP_ADMIN_PASSWORD .env | cut -d= -f2)" \
SDK_SMOKE_ORG_SLUG=default \
ADMIN_LOADER_DATABASE_URL=postgresql://climate_lama:password@localhost:5433/climate_lama \
uv run python scripts/smoke_e2e.py --skip-bootstrap
ADMIN_LOADER_DATABASE_URL points the boundary loader at the host-published port (5433) rather
than the in-container hostname, since this walk runs on the host, not inside a container. The
compose bootstrap one-shot already provisioned the account from step 1
(--skip-bootstrap tells the walk to reuse it rather than provisioning its own). Since issue
1045 the walk provisions two country legs on this account, Greece then Cyprus — the second¶
raster staged above is that leg's, and both must be present or the Cyprus leg's ingest has
nothing to read. Expect two final lines:
E2E_READY_GRC hazard_id=... exposure_id=... impact_function_id=... scenario_id=... result_id=...
and the same shape again as E2E_READY_CYP. This ingests one baseline river-flood hazard dataset
and one exposure dataset per leg, and seeds the JRC flood impact function once (shared by
both) — see
Verifying a clean clone — the end-to-end walk
for the full reference on scripts/smoke_e2e.py.
The rest of this page only uses the Greek hazard/exposure pair: step 3's SDK example below
picks hazards[0] / the first exposure dataset_id off GET /v1/hazards and GET /v1/exposures
(see sdk/python/examples/orsa_matrix.py), and the Greek leg ingests first — the Cyprus dataset
sits alongside it in the same org but this walkthrough does not reference it.
3. Run the scenario matrix (SDK, over HTTP)¶
From a checkout of this repo (no install needed — the example imports the SDK package directly):
cd sdk/python
CLIMATE_LAMA_BASE_URL=http://localhost:8000 \
CLIMATE_LAMA_EMAIL="$(grep CL_BOOTSTRAP_ADMIN_EMAIL ../../.env | cut -d= -f2)" \
CLIMATE_LAMA_PASSWORD="$(grep CL_BOOTSTRAP_ADMIN_PASSWORD ../../.env | cut -d= -f2)" \
CLIMATE_LAMA_ORG_SLUG=default \
PYTHONPATH=. python examples/orsa_matrix.py
This logs in, submits a 3×3 (baseline/ssp2-4.5/ssp5-8.5 × 2030/2050/2080)
scenario matrix against the hazard/exposure pair step 2 ingested, polls
GET /v1/compute/impact/matrix/{batch_id} to completion, and prints each cell's EAD/AAI
plus the org_id and batch_id you need for the next step. On this stack, expect
the baseline rows to print matching EAD/AAI and the ssp2-4.5/ssp5-8.5 rows to print
failed in place of a figure, per the note above.
4. Render and download the ORSA pack (over HTTP, from the host)¶
Using the batch_id the previous step printed, and the same bootstrap credentials as step 3.
This script is repo-root-relative, so cd back out of sdk/python first:
cd ../..
SDK_SMOKE_EMAIL="$(grep CL_BOOTSTRAP_ADMIN_EMAIL .env | cut -d= -f2)" \
SDK_SMOKE_PASSWORD="$(grep CL_BOOTSTRAP_ADMIN_PASSWORD .env | cut -d= -f2)" \
SDK_SMOKE_ORG_SLUG=default \
python scripts/orsa_report_demo.py --batch-id <batch_id>
This calls POST /v1/compute/impact/matrix/{batch_id}/report to queue the render
(202 + job_id), polls GET /v1/jobs/{job_id} until it completes, then downloads the
finished document from GET /v1/compute/impact/matrix/{batch_id}/report — the
batch-shaped report routes issue #415
added, mirroring how POST/GET /v1/results/{id}/report already work for a
single-result report. It runs from the host, the same as step 3 — no
docker compose exec needed — and writes orsa_report.pdf to the current directory
(override with --out).
Open orsa_report.pdf. You should see the cover/scope box, the executive-summary grid
(baseline figures populated, the ssp2-4.5/ssp5-8.5 rows reported as absent per the
note above, not as zero loss), the horizon-comparison chart, one exceedance-probability
panel per scenario/horizon that has a result, the methodology section citing
EIOPA scenario mapping, and the assumptions and
attribution annexes.
What this walkthrough does not demonstrate¶
- A fully-populated matrix on this stack. See the note above — this walkthrough's
provisioning step only ingests the
baselinescenario, so thessp2-4.5/ssp5-8.5cells resolve to a per-cell error rather than a figure. Runpython scripts/ingest_scenario_hazards.py ingest --org-slug <your-org-slug>(issues #386/#891) first to load the real scenario-conditioned Aqueduct datasets, then re-run step 3 for a matrix where every cell in theriver_floodgrid resolves and reports its own EAD/AAI. - Asset- or portfolio-level views. Neither
scripts/bootstrap_env.pynorscripts/smoke_e2e.pycreates Assets or portfolio membership (issue #405); this walkthrough only needs an exposure dataset and a hazard dataset, both of which step 2 provisions. (Step 2 does load two real GADM admin boundaries — Greece and Attica — sincescripts/smoke_e2e.pyneeds one to arm the region vocabulary before it can ingest; that is incidental to this walkthrough, not a substitute for asset/portfolio data.) - A real EIOPA-ORSA filing. See ORSA climate module.