.omrat data format¶
This chapter is the field-level reference for the .omrat JSON
project file and the data dict passed into every risk model. Use
it when you’re writing a project file by hand, migrating from another
tool, reviewing a test fixture, or tracing a number back to its input.
The file format is stable for reading across versions – older
projects load into newer OMRAT releases via
_normalize_legacy_to_schema.
The schema validated at load time is defined by
RootModelSchema (Pydantic).
Top-level layout¶
An .omrat file is a single JSON object. The top-level keys are:
{
"pc": { ... causation factors ... },
"drift": { ... drift settings + wind rose + repair ... },
"traffic_data": { ... per-leg, per-direction traffic matrices ... },
"segment_data": { ... per-leg geometry + distributions + ai ... },
"depths": [ [id, depth_m, wkt_polygon], ... ],
"objects": [ [id, height_m, wkt_polygon], ... ],
"ship_categories": { ... type names + LOA bins ... },
"traffic_scaling": { ... global multiplier + per-row follow flags ... }
}
project_name (optional string) is the Project name from the Run
Analysis tab; Previous runs groups the project’s runs under it. It is
left out when the field is empty, and older files without it load
unchanged. omrat_version records the OMRAT release that wrote the
file.
Some optional keys are also emitted by the plugin when present:
results, drifting_report, collision_report. These are
calculation outputs, not inputs; they’re round-tripped so a saved
project reopens with its last result visible.
pc – causation factors¶
"pc": {
"p_pc": 0.00016,
"d_pc": 1e-4,
"headon": 4.9e-5,
"overtaking": 1.1e-4,
"crossing": 1.3e-4,
"merging": 1.3e-4,
"bend": 1.3e-4,
"grounding": 1.6e-4,
"allision": 1.9e-4,
"grounding_cat1": 1.6e-4,
"allision_cat1": 1.9e-4,
"allision_drifting_rf": 1.0,
"grounding_drifting_rf": 1.0
}
Key |
Type |
Used by |
|---|---|---|
|
float |
|
|
float |
|
|
float |
The same two models, as the \(P_c\) in the Category I
(obstacle already in the lane) term \(N_I = P_c Q m\).
Added in v0.15.0; a project file without the keys uses the
IWRAP default (identical to the Cat II figure). IWRAP export
writes them to |
|
float |
Risk-reduction factor applied to drifting totals after the cascade (defaults 1.0 – set <1 to model e.g. pilot-on-board adjustment for a whole area). |
|
float |
Legacy fields. |
IWRAP causation-factor mapping¶
IWRAP splits powered grounding and allision into two categories, each with its own causation factor:
IWRAP attribute |
Meaning |
|---|---|
|
Category I – the obstacle already lies in the lane; no course change was required. |
|
Category II – a turn was required at a waypoint and the ship failed to make it. |
OMRAT models both categories (see Powered Grounding and Allision). grounding and
allision are the Category-II factors, grounding_cat1 and
allision_cat1 the Category-I ones:
Export writes each to its own attribute. A project file that predates the
*_cat1keys exports its Category-II value on the Category-I attribute rather than a constant, because IWRAP computes the Category-I geometry regardless of what we write – scoring it with a factor nobody chose is how the pre-v0.14.0 exporter produced unaccountable numbers.Import puts
p_*_causationon*_cat1andp_*_no_turn_causationongrounding/allision. The Category-II keys also accept the Category-I attribute as a fallback when a file sets only that one, with_no_turnwinning when both are present.
The two directions are driven by _CF_EXPORT_MAP and
_CF_IMPORT_MAP in compute/iwrap_convertion.py; a test
asserts they name the same attribute set, so a round-trip through
IWRAP XML cannot silently lose a factor.
Warning
Changed in v0.14.0
Before v0.14.0 the exporter ignored pc entirely and wrote a
fixed block of constants, and the importer put IWRAP’s allision
causation into an unread key. Both directions now round-trip.
With IWRAP’s own defaults the two categories carry identical
values, so this only changes results for a project that tunes
them – but a benchmark comparison is exactly that case.
drift – drifting model parameters¶
"drift": {
"drift_p": 1.0,
"blackout_by_ship_type": {"18": 1.0, "9": 0.1, ...},
"anchor_p": 0.7,
"anchor_d": 7.0,
"speed": 1.94,
"rose": {"0": 0.125, "45": 0.125, "90": 0.125, "135": 0.125,
"180": 0.125, "225": 0.125, "270": 0.125, "315": 0.125},
"repair": {
"use_lognormal": true,
"dist_type": "lognormal",
"std": 1.0,
"loc": 0.0,
"scale": 1.0,
"func": "__import__('scipy.stats', fromlist=['norm'])...cdf(x)"
}
}
Key |
Meaning |
|---|---|
|
Blackout rate in events per ship-year. Default 1.0.
Multiplied by |
|
Dict from ship-type index (string key of integer 0-20) to a multiplier. IWRAP uses 0.1 for RoRo/Passenger, 1.0 otherwise. |
|
Conditional probability that a drifting ship in anchorable water successfully anchors (saves itself). Default 0.7. |
|
Anchor-depth factor. A ship with draught \(T\) can anchor in water of depth \(< a_d \cdot T\). |
|
Drift speed in knots. Converted to m/s internally. |
|
Wind rose – eight compass-angle keys (“0” … “315”) mapping to probability weights. Sum should be 1.0 (normalisation is applied if it isn’t). |
|
|
|
Parameters of the selected distribution. |
|
A Python expression evaluated with |
traffic_data – traffic matrix per leg/direction¶
"traffic_data": {
"1": {
"East going": {
"Frequency (ships/year)": [[0, 12, ...], [...], ...],
"Speed (knots)": [[0, 10, ...], [...], ...],
"Draught (meters)": [[0, 5.0, ...], [...], ...],
"Ship heights (meters)": [[0, 18.0, ...], [...], ...],
"Ship Beam (meters)": [[0, 22.0, ...], [...], ...],
"Scaling (%)": [[100, 100, ...], [...], ...]
},
"West going": { ... }
},
"2": { ... }
}
Keys are segment IDs (strings). Nested keys are direction
labels matching the segment’s Dirs array in segment_data.
Each variable is a 2-D matrix with shape
(n_ship_types, n_loa_bins) – for the default ship categories
that’s (21, 15).
Cells can be "", 0, or a numeric value. Empty cells are
treated as zero and skipped in the inner loops.
Scaling (%) is a per-cell frequency multiplier in percent
(default 100 – no scaling). Applied once at the top of
compute.calculation_task.CalculationTask.run() via
compute.data_preparation.apply_traffic_scaling():
Q_effective = Q * Scaling / 100. Every model reads
Frequency (ships/year) afterwards, so the multiplier is inherited
by ship-ship, powered, drifting, and consequence in one shot. AIS
refresh + IWRAP import never overwrite this column, so a saved
+30 % survives every traffic update.
traffic_scaling – project-level scaling state¶
"traffic_scaling": {
"global_percent": 130.0,
"follow_global": [true, false, true, true, ...]
}
Companion to the per-cell Scaling (%) matrices in traffic_data.
global_percent– the value of the Global scaling [%] spinbox on the Traffic Data tab. Re-broadcast into the per-cell matrix whenever it changes, but only for ship-type rows whosefollow_globalflag istrue. Compute never reads this number directly – it reads the per-cell matrix that the broadcast has already updated.follow_global– one boolean per ship-type row.truemeans “this type’s Scaling cells track the global spinbox”. Toggling a rowtrue -> falsekeeps the current cell values; togglingfalse -> truere-broadcasts the current global. Editing a Scaling cell manually auto-unticks the row – the typed value is the explicit “I want this row at X %” signal.
Optional – legacy projects pre-date this feature. The legacy
normaliser in omrat_utils.storage.Storage seeds the block
with global_percent = 100.0 and follow_global = [] (the
empty list grows back to n_ship_types on first save).
segment_data – per-leg geometry and parameters¶
"segment_data": {
"1": {
"Start_Point": "14.0 55.2",
"End_Point": "14.2 55.2",
"Dirs": ["East going", "West going"],
"Width": 1000,
"line_length": 12850.7,
"Route_Id": 1,
"Leg_name": "leg 1",
"Segment_Id": "1",
"bearing": 90.0,
"ai1": 180.0,
"ai2": 180.0,
"mean1_1": 0.0, "std1_1": 200.0, "weight1_1": 1.0,
"mean1_2": 0.0, "std1_2": 0.0, "weight1_2": 0.0,
"mean1_3": 0.0, "std1_3": 0.0, "weight1_3": 0.0,
"mean2_1": 0.0, "std2_1": 200.0, "weight2_1": 1.0,
"mean2_2": 0.0, "std2_2": 0.0, "weight2_2": 0.0,
"mean2_3": 0.0, "std2_3": 0.0, "weight2_3": 0.0,
"u_min1": 0.0, "u_max1": 0.0, "u_p1": 0.0,
"u_min2": 0.0, "u_max2": 0.0, "u_p2": 0.0,
"dist1": [], "dist2": [],
"bend_angle": 0.0
}
}
Geometry¶
Key |
Meaning |
|---|---|
|
|
|
Display width (metres). Not used by the risk integration. |
|
Leg length (metres). Recomputed whenever geometry changes. |
|
Human labels used by the UI and reports. |
|
Compass bearing of Start -> End, degrees. 0 = N, 90 = E, clockwise. |
|
IWRAP position-check interval (seconds) for directions 1 and 2. Plugged into \(\exp(-d/(a_i V))\) for powered models. |
|
Bend angle at the downstream waypoint, in degrees. > 5 deg enables the bend-collision formula for this leg. |
Scenario: suppressed leg¶
"suppressed": true,
"traffic_redirect": [
{"from_dir": 0, "leg": "5", "dir": 0, "share": 80.0},
{"from_dir": 0, "leg": "15", "dir": 0, "share": 20.0},
{"from_dir": 1, "leg": "5", "dir": 1, "share": 100.0}
]
Key |
Meaning |
|---|---|
|
|
|
Segment id of the lead leg this leg is suppressed together with
(Example 3: moving a whole route). The leg is left out without moving any
traffic, and its own |
|
Where the suppressed leg’s ships go. |
The redirect is applied by
compute.traffic_redirect.apply_traffic_redirects() right after
compute.data_preparation.apply_traffic_scaling(). It works on the
calculation’s private copy of the data: frequencies are added to the
target cells, speed / draught / height / beam become frequency-weighted
means, and the suppressed legs are then removed from segment_data,
traffic_data and junctions. The saved file always keeps the
original traffic.
Lateral distribution¶
Key |
Meaning |
|---|---|
|
Mean of the \(i\)-th Gaussian component for direction \(d\) (d = 1 or 2; i = 1, 2, 3). Metres. |
|
Standard deviation of the same. |
|
Component weight (weights are normalised to sum to 1). |
|
Uniform-component bounds and weight. |
|
Legacy raw-PDF arrays (unused in the current risk path). |
depths – bathymetry polygons¶
A flat array of [id, depth_m, wkt_polygon] triples:
"depths": [
["d1", "5.0", "POLYGON((14.08 55.22, ...))"],
["d2", "10.0", "POLYGON((...))"],
...
]
idis a short label used in results (d1,d2, …).depthis stored as a string of a number – OMRAT casts to float internally. Chart-datum metres below MSL.wktis a valid shapely / OGC WKT polygon in EPSG:4326. MultiPolygons are accepted; OMRAT splits them into components on load.
Used by: drifting grounding (as a hazard), drifting anchoring (if shallow enough), powered grounding (depth-bin filtering).
objects – structure polygons¶
Same shape as depths but with height instead of depth:
"objects": [
["s1", "20.0", "POLYGON((14.09 55.208, ...))"],
["s2", "50.0", "POLYGON((...))"]
]
height– height above waterline (metres). A ship withship_height < object_heightpasses under the structure without colliding (powered allision only).
Used by: drifting allision (any drift contact), powered allision (with height filter).
ship_categories – type + LOA bin definitions¶
"ship_categories": {
"types": [
"Other ship", "Search & rescue", "Sailing vessel", ...
],
"length_intervals": [
{"min": 0.0, "max": 50.0, "label": "0-50"},
{"min": 50.0, "max": 100.0, "label": "50-100"},
...
],
"selection_mode": "ais"
}
types– display names for the 21 ship-type rows in the Traffic matrix.length_intervals– the LOA bin columns. Each bin hasmin/max(metres) and alabel. The midpoint of a bin is used to estimate ship length when the traffic cell stores only frequency.selection_mode–"ais"or"manual". Controls whether the UI wires Update AIS buttons.
Output keys (round-tripped, not inputs)¶
When a project is saved after a successful run, the risk results are written into the same file for convenience. They are NOT inputs – loading a file ignores them; Run model overwrites them.
"results": {
"drifting_allision_prob": 1.148e-01,
"drifting_grounding_prob": 8.329e-03,
"ship_collision_prob": 5.2e-06,
"powered_grounding_prob": 3.1e-05,
"powered_allision_prob": 1.7e-06
},
"drifting_report": {
"totals": {"allision": ..., "grounding": ..., "anchoring": ...},
"by_leg_direction": { ... },
"by_object": { ... },
"by_structure_legdir": { ... },
"by_depth_legdir": { ... },
"by_anchoring_legdir": { ... },
"by_structure_segment_legdir": { ... },
"by_depth_segment_legdir": { ... },
"by_anchoring_segment_legdir": { ... },
"debug_obstacles": { ... if debug_trace is enabled ... }
},
"collision_report": {
"totals": {"head_on": ..., "overtaking": ..., "crossing": ..., "bend": ..., "total": ...},
"by_leg": { ... },
"causation_factors": {...}
}
Optional / developer-only flags¶
Key |
Meaning |
|---|---|
|
|
|
|
|
|
Run history: master DB + per-run GeoPackages¶
OMRAT splits run history into two locations to keep the master database small even after many runs.
Master history database: omrat_history.sqlite¶
One file in the user’s app-data folder (see User Guide), auto-created on first run. Holds only metadata – one row per run, no spatial features – so a plain SQLite file suffices and the extension correctly advertises that.
Existing installs that have an older omrat_history.gpkg (or
omrat_runs.gpkg from earlier releases) are auto-migrated on the
next plugin start: the file is renamed in place; nothing is lost
because the format is identical.
Table omrat_runs¶
Column |
Meaning |
|---|---|
|
Auto-increment primary key. |
|
Model name from the Run Analysis tab, or
|
|
Save time as |
|
Wall time of the run, in seconds ( |
|
Drifting cascade totals (annual frequency). |
|
Cat II powered totals. |
|
Ship-collision totals. |
|
Folder containing the per-run GeoPackage (the user-selected output folder at the time of the run). |
|
Filename of the per-run GeoPackage inside |
|
Free-text user note (currently unused; reserved). |
Table omrat_run_info¶
Project tag and OMRAT version per run. It is a separate table so that
adding it needs no change to omrat_runs: it is created on the next
plugin start in any existing history file, runs recorded before it
simply have no row, and an older OMRAT still reads omrat_runs
unchanged.
Column |
Meaning |
|---|---|
|
The |
|
The project name (Project name on the Run Analysis tab, or
Set project…). |
|
OMRAT version that produced the run. Filled in once from the
run’s |
Per-run GeoPackages¶
One file per run, written under the user-selected output folder.
Filename: <slug(name)>_<YYYYMMDD_HHMMSS>.gpkg. Each contains up
to six layers (only those with non-zero contributions are written):
Layer |
Geometry |
Attributes |
|---|---|---|
|
Polygon |
|
|
Polygon |
same shape as Allision. |
|
LineString (per leg) |
|
|
LineString (per leg) |
|
|
LineString |
|
|
Point |
|
Open a per-run file directly in QGIS (it’s a standard OGC GeoPackage), or use Add selected run results to map on the Previous-runs table to load all six layers in one click with graduated styling applied.
Files next to each run¶
Each run also writes these files beside its GeoPackage, named after
its stem (<stem> = <slug(name)>_<YYYYMMDD_HHMMSS>). Each is
written only when there is something to put in it, and each is
optional when read back: a run recorded before a file existed simply
lacks it, and the dialog that needs it falls back as described.
File |
Content and reader |
|---|---|
|
Read-only snapshot of the project inputs. The View dialogs and the Compare tab rebuild their inputs from it. |
|
Combined Markdown report of the run. |
|
The ship-ship collision report (collision View tables). |
|
The drifting report (drifting View table: contributions and % of total per leg, direction and drift direction). |
|
The catastrophe-exceedance result (catastrophe table for previous runs). |
|
Per leg-direction powered frequencies for the powered View
table (Frequency and % of total). One block per mode, left out
when that model produced no report. Keys of
|
Shape of <stem>.powered.json (the grounding block, when
present, has the same keys with "grounding" as the total):
{
"allision": {
"totals": {"allision": 1.07e-03, "cat1": 1.07e-03, "cat2": 3.36e-06},
"by_leg_direction": {"17:0": 6.10e-04, "17:1": 4.58e-04, "9:0": 1.18e-06},
"by_obstacle": {"1_0": 1.07e-03},
"causation_factor": 1.9e-04,
"causation_factor_cat1": 1.9e-04
}
}
Minimal valid project (synthetic)¶
The smallest project that passes Pydantic validation and runs the
drifting cascade is in tests/test_cascade_minimal.py
(_build_minimal_project): one east-going 10 km leg, one 12 m
depth polygon NW of the leg, one 20 m structure N of the leg, a
single cargo-type cell with 100 ships/year at 10 knots / 13 m
draught. Useful as a starting skeleton when building a fixture.