# Oil-spill consequence – catastrophe rates

After the per-accident-type frequencies are calculated (drifting, powered, collision), OMRAT can roll them up into annual catastrophe exceedance frequencies: how often, on average per year, an oil spill larger than a user-defined size is expected to happen.

The module is reached via the Consequence menu in the plugin toolbar. Four dialogs configure the inputs; the actual calculation runs as the last phase of Run Model and writes its result into the “Catastrophe results” table on the Run Analysis tab.

## At a glance

For each ship cell `(ship_type, length_interval)` the model multiplies:

- the annual accident frequency (events/year, from the per-cell breakdown the four model phases emit),

- a conditional spill probability (rows 0-3 = “no spill”, “small spill”, “medium spill”, “catastrophic spill”, in percent),

- a spill fraction of the ship’s oil-onboard quantity (also in percent), and

- the cell’s oil onboard (m3).

It then counts each (accident, level) contribution toward every catastrophe threshold whose quantity it exceeds. The output is a list of `{level_name, quantity, exceedance}` rows ordered by quantity ascending.

## The four input dialogs

Open each from Consequence > … in the menu bar.

### Maximum oil onboard

A ship-type x length-interval matrix in m3. Defaults are:

- Tankers: `80 x average_length` (m3) per cell, where average length is the midpoint of the cell’s length interval and the open-ended top bin uses `min + 50`.

- Everything else: 100 m3 per cell.

Override any cell directly; the dialog uses spinboxes ranged 0 to 107, two decimals, 10 m3 step.

### Spill probability per accident

8 rows (one per accident category) x 4 columns (no spill / small / medium / catastrophic) in percent. Each row must sum to 100% +/- 0.05% – the dialog refuses to save until it does.

Defaults:

- Drifting accidents: `[98, 2, 0, 0]` – almost all benign.

- All other accidents: `[97, 1, 1, 1]`.

### Spill fraction per accident

Same 8 x 4 shape; each cell is the fraction of the ship’s oil onboard that escapes for the given (accident, spill-level) combination, in percent. No row constraint – you can choose any spill-fraction distribution. Default is `[0, 10, 30, 100]` for every accident.

### Catastrophe levels

A list of `{name, quantity_m3}` rows; at least two are required. The Run Analysis “Catastrophe results” table will have one row per catastrophe level, sorted by quantity ascending. Defaults are Minor / Major / Catastrophic at 50 / 500 / 5000 m3.

## Worked example

Suppose a single tanker cell sees 0.001 powered groundings per year, the tanker’s oil onboard is 100 000 m3, and the spill probability for powered grounding is the default `[97, 1, 1, 1]` with fractions `[0, 10, 30, 100]`.

Per year the cell produces:

- 0.001 x 0.97 = 9.7e-4 “no spill” events (volume 0).

- 0.001 x 0.01 = 1e-5 “small spill” events of 10 000 m3.

- 0.001 x 0.01 = 1e-5 “medium spill” events of 30 000 m3.

- 0.001 x 0.01 = 1e-5 “catastrophic” events of 100 000 m3.

With the default thresholds:

- >50 m3 (“Minor”) fires 3e-5 times/yr (all three spill levels).

- >500 m3 (“Major”) fires 3e-5 times/yr (the smallest spill is already 10 000 m3).

- >5000 m3 (“Catastrophic”) fires 3e-5 times/yr.

The table renders one row per level with the per-year exceedance.

## Headless validation

For batch / CI use, `compute.consequence.validate_consequence` runs the same constraints the dialogs enforce (row sums, minimum two catastrophe levels, non-negative values) and returns a structured report instead of popping a UI:

    from compute.consequence import validate_consequence
    rep = validate_consequence(consequence_block)
    if not rep.ok:
        print("\n".join(rep.errors))

## Behind the scenes

- Code: `compute/consequence.py:compute_catastrophe_exceedance`.

- Defaults / reshaping: `omrat_utils/consequence_defaults.py`.

- UI handler (live state, dialog wiring, row-sum validation): `omrat_utils/handle_consequence.py`.

- Result table: `omrat_utils/accident_results_mixin.py ._populate_catastrophe_results_table` writes into the `TWCatastropheResults` widget.

Per-cell accident frequencies feeding the consequence calc come from each model phase’s `by_cell` dict (or `by_cell_allision` / `by_cell_grounding` for drifting). Pair-wise collisions split each pair contribution 50/50 between the participating cells; bend distributes proportionally to per-cell traffic on the leg.

## Limitations and TODOs

- Tankers split between laden and ballast voyages would carry different spill volumes – not yet modelled. Today every voyage uses the configured `oil_onboard`.

- Merging collisions share the IWRAP causation factor with `crossing`. The model already separates them in `by_cell` based on a 30 deg crossing-angle threshold, but to be a fully separate accident type `merging` needs its own `pc` entry.