Skip to content

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:

  1. 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.
  2. Compose the orsa PDF pack for that batch and download it, over the batch-shaped report routes issue #415 added. This is scripts/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 baseline column resolves every horizon to that one ingested dataset, so its EAD/AAI is identical across 2030/2050/2080 — correctly, since there is genuinely only one baseline dataset to run against.
  • The ssp2-4.5/ssp5-8.5 columns 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 — and error_message naming 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 httpx installed (pip install httpx — the SDK example imports the SDK directly from the checkout, so no pip install climate-lama is needed) and requests installed (pip install requests — scripts/orsa_report_demo.py uses it the same way scripts/smoke_e2e.py does)
  • A clone of this repository, with .env created 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:

docker compose down -v

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 baseline scenario, so the ssp2-4.5/ssp5-8.5 cells resolve to a per-cell error rather than a figure. Run python 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 the river_flood grid resolves and reports its own EAD/AAI.
  • Asset- or portfolio-level views. Neither scripts/bootstrap_env.py nor scripts/smoke_e2e.py creates 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 — since scripts/smoke_e2e.py needs 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.