Drifting Risk Calculations¶
This chapter explains, step by step, how OMRAT calculates the risk that a ship which loses propulsion (blackout) drifts into a shallow area (grounding), a structure (allision) or succeeds in anchoring before it does so.
The chapter is built around the five worked examples in
drifting/debug/level_1 ... level_5. Each example runs OMRAT’s
actual calculation functions, prints every intermediate value, and
produces a figure showing the leg, the obstacles and the drift corridor.
Re-running the scripts reproduces the numbers quoted below exactly.
Overview of the Drifting Model¶
When a ship suffers a blackout it becomes powerless and drifts under wind and current until one of the following happens:
The crew repairs the engine,
The crew successfully drops the anchor (in water shallow enough for anchoring), or
The ship reaches an obstacle (grounds on a shallow, collides with a structure or reaches the reach-distance limit without incident).
The probability that a drifting ship causes an accident on an obstacle \(X\) from leg \(i\) in compass direction \(d\) is
and for anchoring (which saves ships rather than causing an accident)
The total drifting accident rate is the sum over all wind-rose directions, all obstacles, all legs and all ship categories:
with \(\sum_{d=1}^{8} r_{p,d} = 1\). For a uniform wind rose (used in the worked examples) \(r_{p,d} = 0.125\). Anchoring events are summed separately since they represent saved — not lost — ships.
The base exposure is
The symbols are listed in the table below. Sections Exposure Base, Repair Time and P_{NR}, The Probability “Hole” h_X, Directional Distance to an Obstacle and run analysis vs run model define each term in detail, and the five worked examples in Sections Level 1 — Single Polygon, Single Ship, Single Leg through Level 5 — Complete Cascade show how they combine.
Symbol |
Meaning |
Unit / type |
|---|---|---|
\(L_i\) |
Length of leg \(i\) |
metres |
\(V_k\) |
Service speed of ship category \(k\) |
knots |
\(1852\) |
Metres per nautical mile |
constant |
\(f_{i,k}\) |
Transits per year of category \(k\) on leg \(i\) |
1/year |
\(\lambda_{bo}\) |
Blackout rate while at sea |
1/year |
\(\lambda_{bo,\mathrm{hour}}\) |
Blackouts per hour = \(\lambda_{bo}/(365.25\cdot 24)\) |
1/hour |
\(r_{p,d}\) |
Wind-rose probability for compass direction \(d\) (8 directions, sum = 1) |
dimensionless |
\(h_X\) |
Probability hole of obstacle \(X\) (the fraction of the lateral traffic distribution whose drift rays intersect \(X\)) |
dimensionless |
\(h_{X,\mathrm{eff},d}\) |
Effective hole of obstacle \(X\) in direction \(d\) after upstream shadowing / anchoring |
dimensionless |
\(d_X\) |
Along-drift distance from the leg reference to obstacle \(X\) |
metres |
\(V_{\mathrm{drift}}\) |
Drift speed |
m/s |
\(P_{NR}(d_X)\) |
Probability the blackout is not repaired by the time the ship has drifted \(d_X\) |
dimensionless |
\(a_p\) |
Anchoring success probability |
dimensionless |
\(a_d\) |
Anchoring depth multiplier (anchoring applies if depth is less than \(a_d\cdot T_{\mathrm{ship}}\)) |
dimensionless |
\(T_{\mathrm{ship}}\) |
Ship draught |
metres |
\(S_X\) |
Quad-sweep shadow polygon of obstacle \(X\) along the drift direction |
polygon |
run analysis vs run model¶
Two buttons in the OMRAT QGIS plugin trigger different code paths.
Button |
Entry point |
What it does |
|---|---|---|
Run analysis |
|
Builds the drift corridor polygons (per leg, per direction)
and draws them as QGIS vector layers. Uses the quad-sweep
shadow algorithm and the corridor-clipping code in
|
Run model |
|
Computes the actual drifting risk — \(P(\mathrm{ground})\), \(P(\mathrm{allision})\), \(P(\mathrm{anchor})\) — for every (leg, direction, obstacle, ship-category). This is the path described in the rest of this chapter. |
The quad-sweep algorithm described in Shadow Algorithm (Quad-Sweep) is only used by “run analysis” (for drawing the corridors); the probability calculation uses the shadow-coverage formulation described per level below, which operates directly on the obstacle polygons and their quad-sweep shadows as a geometric subtraction rather than a corridor-clipping step.
Common Parameters Used in the Examples¶
All five worked examples use Leg 3 from the proj_3_3 test scenario
and an 8-vertex 12 m depth polygon (BD5A4C46) as the target. The
values below are the exact inputs the scripts pass to the OMRAT
functions — re-running the scripts reproduces every number shown in
this chapter.
Parameter |
Value |
Symbol / note |
|---|---|---|
Leg 3 length |
34 113.2 m |
\(L\) |
Leg 3 start (lon, lat) |
(14.24187°, 55.16728°) |
EPSG:4326 |
Leg 3 end (lon, lat) |
(14.59271°, 55.39937°) |
EPSG:4326 |
Ship speed (service) |
12.5 kts |
\(V\) |
Ship frequency (Leg 3) |
610 transits/year |
\(f\) |
Ship draught (oil tanker) |
14.27 m |
\(T_{\mathrm{ship}}\) |
Drift speed |
1.94 kts (0.998 m/s) |
\(V_{\mathrm{drift}}\) |
Blackout rate |
1.0 /year |
\(\lambda_{bo}\) |
Blackouts per hour |
\(1.1408\times 10^{-4}\) |
\(\lambda_{bo,\mathrm{hour}}\) |
Wind rose (uniform) |
1/8 = 0.125 per direction |
\(r_p\) |
Lateral sigma |
500 m |
\(\sigma\) |
Reach distance |
50 000 m |
max drift distance |
Repair time distribution |
lognormal(std=1, loc=0, scale=1) |
\(F_{\mathrm{lognorm}}\) |
Anchoring success |
\(a_p = 0.70\) |
|
Anchoring depth multiplier |
\(a_d = 7.0\) |
threshold = \(a_d\cdot T_{\mathrm{ship}}\) |
Drift direction (examples) |
315° (NW) |
\(\theta_{\mathrm{compass}}\) |
Compass-to-math conversion used throughout OMRAT:
For \(\theta_{\mathrm{compass}}=315\degree\) (NW) this gives \(\theta_{\mathrm{math}}=135\degree\) and a unit drift vector \(\hat v = (-0.7071, 0.7071)\).
Exposure Base¶
The base exposure counts how many blackouts per year are expected while ships are on the leg (equation (4)).
With the shared parameters:
The unit of \(\mathrm{base}\) is blackouts per year on this leg from ships of this category. Every grounding, allision and anchoring probability below is multiplied by this factor and the wind-rose probability.
compute/basic_equations.py:9 – get_drifting_prob()
Repair Time and \(P_{NR}\)¶
The time required to repair a blackout is modelled by a lognormal distribution with parameters \((\sigma, \mu_{\mathrm{loc}}, s)\). The probability that the ship is not repaired by the time it has drifted a distance \(d\) is
In the examples we use \(\sigma=1.0\), \(\mu_{\mathrm{loc}}=0\), \(s=1.0\). For the average target edge at \(d=11\,775\,\mathrm{m}\) this evaluates to
i.e. about 11.8% of blackouts are still unresolved three hours after the event.
compute/basic_equations.py:30 – get_not_repaired()
The Probability “Hole” \(h_X\)¶
The hole of an obstacle \(X\) is the fraction of the lateral
traffic distribution whose straight-line drift rays intersect \(X\).
OMRAT computes it analytically by slicing the leg into N cross-sections
and integrating the lateral PDF over the y-intervals where the drift ray
hits the polygon (geometries/analytical_probability.py).
For a lateral normal \(\mathcal N(0, \sigma^2)\) the result is deterministic (no Monte Carlo noise):
where \(R(s)\) is the union of y-intervals at slice position \(s\) where the drift ray hits \(X\), and \(W=5\sigma\) is the lateral integration range.
Numerical value in the examples: for the 8-vertex 12 m target polygon with drift direction NW, \(h_{\mathrm{target}} = 2.4915\times 10^{-2}\).
geometries/analytical_probability.py –
compute_probability_analytical().
The analytical path is the default
(data['use_analytical']=True in run_drifting_model); a Monte
Carlo fallback exists in
geometries/calculate_probability_holes.py but is only used when
the user explicitly disables the analytical integrator.
Directional Distance to an Obstacle¶
The distance \(d_X\) that goes into \(P_{NR}(d_X)\) is the
along-drift distance from a leg reference line to the obstacle. OMRAT
measures it by casting a reverse ray from each vertex of the
obstacle back along the drift direction and intersecting with the
reference line. The per-edge distance is the average of its two vertex
distances (edge_average_distance_m in drifting/engine.py).
Two reference lines are supported:
Leg centreline (
use_leg_offset = False, default): distance is measured from the charted leg line itself.Mean-offset line (
use_leg_offset = True): distance is measured from a parallel line offset by \(\mathrm{mean\_offset\_m}\) from the leg. This is the correct choice when the traffic in a direction is systematically biased to one side of the charted leg.
For the target polygon (NW drift, leg centreline), the three front-facing edges give
Edge |
vertex 0 (m) |
vertex 1 (m) |
average (m) |
|---|---|---|---|
5 |
11 662.9 |
11 578.4 |
11 620.6 |
6 |
11 578.4 |
11 461.5 |
11 519.9 |
7 |
11 461.5 |
12 066.2 |
11 763.8 |
drifting/engine.py:215 – directional_distance_to_point_from_offset_leg() |
drifting/engine.py:288 – edge_average_distance_m()
Level 1 — Single Polygon, Single Ship, Single Leg¶
Script: drifting/debug/level_1_single_polygon.py
The simplest case. Leg 3 drifts NW toward the 8-vertex 12 m polygon
BD5A4C46. One ship category (oil tanker 225–250 m, draught 14.27 m,
610 transits/year) contributes because the target depth (12 m) is less
than the draught.
Step 1 — base exposure (section Exposure Base):
Step 2 — analytical target hole (section The Probability “Hole” h_X):
Step 3 — distribute the hole over the three front-facing edges in proportion to their length (total = 1215.0 m):
Edge idx |
length (m) |
distance (m) |
\(h_{\mathrm{edge}}\) |
|---|---|---|---|
5 |
168.2 |
11 620.6 |
\(3.458\times 10^{-3}\) |
6 |
119.7 |
11 519.9 |
\(2.462\times 10^{-3}\) |
7 |
927.0 |
11 763.8 |
\(1.906\times 10^{-2}\) |
Step 4 — \(P_{NR}\) per edge (section Repair Time and P_{NR}):
Step 5 — per-edge grounding contribution:
Edge |
Contribution (events/year) |
|---|---|
5 |
\(5.329\times 10^{-6}\) |
6 |
\(3.849\times 10^{-6}\) |
7 |
\(2.878\times 10^{-5}\) |
Total |
\(3.7955\times 10^{-5}\) |
So P(grounding) = 3.7955 × 10⁻⁵ events/year for this single (leg, ship, direction) combination.
Level 1b — Distance from the Leg Centreline vs the Distribution Centre¶
Script: drifting/debug/level_1_single_polygon.py (second figure)
A charted leg is a single line, but the traffic on that leg is typically split into two directions (ships going one way and ships going the other way) and those two populations may be centred on slightly different parallel lines. OMRAT supports this via two reference-line modes for the \(d_X\) measurement (Directional Distance to an Obstacle):
Mode (a) — ``leg_center`` (default): distance is measured from the charted leg line. Used everywhere in Levels 1–5 unless stated.
Mode (b) — ``distribution_center``: distance is measured from a mean-offset line, one per traffic direction. This shifts the distance values by the mean lateral offset of the traffic for that direction.
The same front-facing target edges computed with both modes (\(\mathrm{mean\_offset}_A = +500\,\mathrm{m}\), \(\mathrm{mean\_offset}_B = -500\,\mathrm{m}\)):
Edge |
Leg centreline (v0 / v1 / avg) |
Offset A = +500 (v0 / v1 / avg) |
Offset B = -500 (v0 / v1 / avg) |
Shift |
|---|---|---|---|---|
5 |
11 662.9 / 11 578.4 / 11 620.6 |
11 161.8 / 11 077.4 / 11 119.6 |
12 163.9 / 12 079.5 / 12 121.7 |
±500 m |
6 |
11 578.4 / 11 461.5 / 11 519.9 |
11 077.4 / 10 960.4 / 11 018.9 |
12 079.5 / 12 562.5 / 12 320.9 |
±500 m |
7 |
11 461.5 / 12 066.2 / 11 763.8 |
10 960.4 / 11 565.1 / 11 262.8 |
11 961.5 / 12 566.8 / 12 264.0 |
±500 m |
Using the mean-offset distance changes \(P_{NR}(d_X)\) (shorter distance → ship more likely to still be drifting). Combined over the two traffic directions this produces a more realistic risk than treating both directions as centred on the charted leg.
To enable this mode set use_leg_offset_for_distance = True in the
DriftConfig and provide a non-zero
LegState.mean_offset_m per direction.
drifting/engine.py:215 – directional_distance_to_point_from_offset_leg(use_leg_offset=True) |
drifting/engine.py:46 – DriftConfig.use_leg_offset_for_distance
Level 2 — Two Legs, Multiple Ship Categories¶
Script: drifting/debug/level_2_two_legs_multi_ships.py
This example introduces:
A second traffic leg (Leg 6, 7 489 m, bearing ~41° NE) with its own drift direction (N = 0°).
Five ship categories with different draughts and frequencies.
Only ship categories with \(T_{\mathrm{ship}} > d_{\mathrm{polygon}} = 12\,\mathrm{m}\) can ground on this polygon. Three of the five qualify:
Ship category |
Draught (m) |
Grounds? |
Freq L3 |
Freq L6 |
Speed |
|---|---|---|---|---|---|
Oil tanker 225-250 m |
14.27 |
yes |
610 |
50 |
12.5 |
General cargo 225-250 m |
11.82 |
no |
450 |
40 |
13.0 |
Bulk carrier 250-275 m |
16.53 |
yes |
180 |
15 |
13.5 |
Container 275-300 m |
13.50 |
yes |
95 |
8 |
18.0 |
Passenger 100-125 m |
5.80 |
no |
320 |
280 |
16.0 |
Per-leg analytical holes (NW for Leg 3, N for Leg 6):
Leg |
\(h_X\) (NW for L3, N for L6) |
|---|---|
LEG_3 |
\(2.4979\times 10^{-2}\) |
LEG_6 |
\(8.1012\times 10^{-2}\) |
The per-(leg, ship) exposure base scales with that ship’s speed and frequency:
Summing over all valid (leg, ship, edge) combinations and the single NW direction yields
Leg / ship |
Base |
P(ground) |
Share |
|---|---|---|---|
LEG_3 / Oil tanker 225-250 m |
0.10254 |
\(3.7955\times 10^{-5}\) |
70.8% |
LEG_3 / Bulk carrier 250-275 m |
0.02802 |
\(1.0370\times 10^{-5}\) |
19.4% |
LEG_3 / Container 275-300 m |
0.01109 |
\(4.1049\times 10^{-6}\) |
7.7% |
LEG_6 / Oil tanker 225-250 m |
0.001845 |
\(8.281\times 10^{-7}\) |
1.5% |
LEG_6 / Bulk carrier 250-275 m |
0.000513 |
\(2.300\times 10^{-7}\) |
0.4% |
LEG_6 / Container 275-300 m |
0.000205 |
\(9.201\times 10^{-8}\) |
0.2% |
Total (NW) |
\(\mathbf{5.358\times 10^{-5}}\) |
Multiplying by the rose probability and summing over all 8 wind directions gives the full per-leg risk used in equation (3).
Level 3 — Blocking Polygon (Shadow Coverage)¶
Script: drifting/debug/level_3_blocking_polygon.py
When an obstacle sits upstream of another obstacle on the drift axis, drifting ships that would otherwise reach the downstream obstacle may be blocked by the upstream one. In OMRAT the blocking effect is purely geometric: a blocker removes exactly the drift rays it physically intercepts, nothing more.
Shadow-Coverage Model¶
Define the blocker’s shadow polygon \(S_B\) as the quad-sweep of the blocker along the drift direction (see Shadow Algorithm (Quad-Sweep)). The effective hole of the downstream target becomes
which is computed by the same analytical integrator used for any other hole, but evaluated on the unshadowed part of the target polygon. Equivalently,
where \(\mathrm{cov}_B\) is the probability-hole coverage (not the geometric area fraction — the lateral PDF weights the target unevenly). The grounding probability is
Important
There is no multiplicative “remaining” factor in the blocker cascade. The blocker only changes the target’s effective hole via equation (5).
Three Scenarios¶
Using the same target polygon as Level 1/2 and a rectangular blocker placed 4 km upstream of the target, the script generates three coverage scenarios by varying the blocker’s cross-drift width. For each, it reports the effective hole, the blocker’s own hole and the target probability.
Scenario |
cov(hole) |
\(h_{\mathrm{target,eff}}\) |
P(target) |
|---|---|---|---|
A — blocker laterally offset |
0.0% |
\(2.4915\times 10^{-2}\) |
\(3.7559\times 10^{-5}\) |
B — 30 % coverage |
30.0% |
\(1.7441\times 10^{-2}\) |
\(2.6291\times 10^{-5}\) |
C — 100 % coverage |
100.0% |
0 |
0 |
Baseline (no blocker) |
\(2.4915\times 10^{-2}\) |
\(3.7559\times 10^{-5}\) |
Detailed Calculation (Scenario B, 30% coverage)¶
The blocker itself may also be a grounding or allision hazard; the script reports its contribution separately using its own hole, distance and (for grounding/allision) \(P_{NR}\).
Level 4 — Anchoring¶
Script: drifting/debug/level_4_anchoring.py
An anchoring polygon is an area where ships can successfully drop anchor because the water depth is shallower than \(a_d\cdot T_{\mathrm{ship}}\) but deeper than the ship’s draught (so it is not a grounding hazard). Anchoring is modelled probabilistically: a ship drifting through the anchor zone anchors with probability \(a_p\) and keeps drifting with probability \((1-a_p)\).
The anchoring formulation uses the same shadow-coverage geometry as Level 3, combined with the probabilistic success factor. If \(S_A\) is the anchor polygon’s quad-sweep shadow along the drift direction, the effective target hole is
The grounding and anchoring contributions are
When \(a_p \to 1\), the anchor behaves like a pure blocker (the Level 3 full-coverage case). When \(a_p = 0\) it has no effect.
Parameters specific to this example:
\(a_p = 0.70\), \(a_d = 7.0\), \(T_{\mathrm{ship}} = 14.27\,\mathrm{m}\)
Anchoring threshold: \(a_d\cdot T_{\mathrm{ship}} = 99.9\,\mathrm{m}\)
Anchor polygon depth: 50 m (< 99.9 m, so anchoring applies)
Three Scenarios¶
The script varies the anchor polygon’s cross-drift width to produce three coverage cases.
Scenario |
cov(hole) |
\(h_{\mathrm{target,eff}}\) |
P(anchor) |
P(target) |
|---|---|---|---|---|
A — no anchor polygon |
0.0% |
\(2.4915\times 10^{-2}\) |
0 |
\(3.7559\times 10^{-5}\) |
B — 30% coverage |
30.0% |
\(1.9479\times 10^{-2}\) |
\(9.353\times 10^{-5}\) |
\(2.9672\times 10^{-5}\) |
C — 100% coverage |
100.0% |
\(7.475\times 10^{-3}\) |
\(3.283\times 10^{-4}\) |
\(1.1268\times 10^{-5}\) |
In Scenario C the anchor reduces the target hole by exactly \(a_p = 70\%\), giving \(P(\mathrm{target})\) equal to 30% of the baseline. In Scenario B it reduces it by \(0.3\cdot 0.7 = 21\%\), consistent with the reported -21%.
Detailed Calculation (Scenario C, 100% coverage)¶
Level 5 — Complete Cascade¶
Script: drifting/debug/level_5_complete_cascade.py
The full cascade combines all three obstacle types in the same drift path:
Anchoring polygon (50 m depth, 8 km upstream, wider than target): probabilistic, reduces subsequent rays by \(a_p = 0.70\) over the cross-drift intersection.
Allision structure (platform / turbine cluster, 600 m × 200 m, 4 km upstream of target, directly in front on the cross-drift axis): pure geometric blocker.
Grounding target (same 8-vertex 12 m polygon).
Combined Shadow-Coverage Formulation¶
Let \(S_s\) be the structure’s quad-sweep shadow and \(S_a\) be the anchor’s. Only the part of the target not shadowed by the structure is reachable:
Of that reachable part, some rays also pass through the anchor zone and are reduced by \(a_p\):
The structure itself is reduced by the anchor for its own rays that pass through the anchor zone first:
Anchoring is unconditional — every ray passing through the anchor zone is counted, regardless of whether a downstream structure would later have blocked it:
Probability contributions:
The total accident rate is \(P(\mathrm{allision}) + P(\mathrm{ground})\). \(P(\mathrm{anchor})\) is shown separately since those ships are saved.
Numerical Walk-Through¶
Values computed by the script (see drifting/debug/level_5_complete_cascade.py):
Obstacle |
\(h_X\) |
\(d_X\) (m) |
|---|---|---|
Anchor (50 m, 8 km upstream) |
\(4.2542\times 10^{-2}\) |
3 734 |
Structure (600 m × 200 m, 4 km upstream of target) |
\(1.7640\times 10^{-2}\) |
7 734 |
Target (12 m) |
\(2.4915\times 10^{-2}\) |
11 775 |
Shadow coverages (computed by the script):
Coverage |
Value |
|---|---|
Structure shadow on target hole |
70.8% |
Anchor shadow on REACHABLE target hole |
100.0% |
Anchor shadow on structure hole |
100.0% |
Effective holes:
Probability contributions (using \(P_{NR}(7734)=0.22164\) and \(P_{NR}(11775)=0.11761\)):
Scenario Comparison¶
Dropping one obstacle at a time shows how each component affects the total:
Scenario |
P(anchor) |
P(allision) |
P(ground) |
|---|---|---|---|
target only |
0 |
0 |
\(3.756\times 10^{-5}\) |
target + anchor |
\(3.817\times 10^{-4}\) |
0 |
\(1.127\times 10^{-5}\) |
target + structure |
0 |
\(5.011\times 10^{-5}\) |
\(1.097\times 10^{-5}\) |
target + anchor + structure |
\(3.817\times 10^{-4}\) |
\(1.503\times 10^{-5}\) |
\(3.290\times 10^{-6}\) |
The structure blocks 70.8% of the target’s rays (the part directly behind it); the anchor reduces the remaining target rays (and the structure’s rays) by \(a_p = 0.7\).
Shadow Algorithm (Quad-Sweep)¶
Note
The quad-sweep algorithm described in this section is the same
geometric operation used everywhere else in the chapter (for
example, \(S_B\) in (5) and \(S_s\),
\(S_a\) in Level 5). It is also the only place a shadow
polygon is explicitly drawn: in the run analysis code path
that produces the QGIS drift-corridor layers. During the
probability calculation (run model) the shadow polygons are
built on the fly per obstacle and used directly as a
shapely.Polygon.difference / Polygon.intersection
argument — the result is the effective-hole equation already shown
per level. There is no corridor-clipping or MultiPolygon
reachability filter on the probability path.
OMRAT computes shadow polygons by sweeping each obstacle along the drift direction and joining the original polygon to its translated copy via a quadrilateral per edge.
Given polygon \(P = (v_0, v_1, \ldots, v_{n-1})\) and drift vector \(\hat v \cdot L\):
Translate \(P\) by \(\hat v \cdot L\) to produce \(P'\).
For each edge \((v_i, v_{i+1})\) create a quadrilateral \(Q_i = (v_i, v_{i+1}, v'_{i+1}, v'_i)\).
The shadow is \(S = P \cup P' \cup \bigcup_i Q_i\).
This preserves the original contour of the obstacle even for concave shapes (e.g. a reef with a navigable gap) — the quads fill only the region physically swept by each edge. A convex-hull shortcut would incorrectly fill navigable channels.
Reproducing this operation manually in the worked examples:
def build_quad_shadow(poly, extrude_length):
far = translate(poly, xoff=drift_ux * extrude_length,
yoff=drift_uy * extrude_length)
orig = list(poly.exterior.coords)[:-1]
far_c = list(far.exterior.coords)[:-1]
quads = []
for i in range(len(orig)):
j = (i + 1) % len(orig)
q = Polygon([orig[i], orig[j], far_c[j], far_c[i]])
if q.is_valid and q.area > 0:
quads.append(q)
return unary_union([poly, far] + quads)
geometries/drift/shadow.py:18 – create_obstacle_shadow()
(visualisation path only; used by ``run analysis`` to produce the
corridor layers)
Drift Corridor Visualisation (run analysis only)¶
The drift corridor layers drawn on the map come from a separate
code path (run analysis) and are not part of the probability
calculation. They are a convenience for the user to see, for each leg
and each of the 8 compass directions, the reachable area after all
obstacles have been clipped out.
Base surface width:
Maximum drift distance:
clamped to \([10\,000, 50\,000]\,\mathrm{m}\) for numerical stability.
The visualisation corridor is the convex hull of the base surface and
its translation by \(d_{\mathrm{max}}\hat v\), clipped by every
obstacle’s quad-sweep shadow (Shadow Algorithm (Quad-Sweep)) and
filtered for reachability. This clipping happens only on the
visualisation path — during run model the probability
calculation uses the shadow polygons directly (via
shadow-coverage subtraction, per-level equations above).
omrat.py:884 – run_drift_analysis() |
geometries/drift/corridor.py:16 – create_base_surface() |
geometries/drift/clipping.py:16 – clip_corridor_at_obstacles()
Pipeline and Source Code Pointers¶
Probability pipeline (``run model``):
omrat.py:599 run_calculation() →
CalculationTask →
compute/drifting_model.py:2096 run_drifting_model() →
analytical hole +
edge distance +
P_NR +
shadow-coverage subtraction per Levels 3–5.
Visualisation pipeline (``run analysis``):
omrat.py:884 run_drift_analysis() →
DriftCorridorTask →
DriftCorridorGenerator →
quad-sweep shadow +
corridor clipping.
The worked-example scripts in drifting/debug/ import the same
functions OMRAT uses during run model, so the numbers printed
above match the results the QGIS plugin produces for the same inputs.
Summary¶
For every (leg, direction, obstacle, ship) tuple the drifting probability is \(P = \mathrm{base}\cdot r_p\cdot h_{\mathrm{eff}}\cdot P_{NR}(d)\), and anchoring is \(P_{\mathrm{anchor}} = \mathrm{base}\cdot r_p\cdot a_p\cdot h_{\mathrm{anchor}}\).
\(h_{\mathrm{eff}}\) is the analytical hole of the obstacle minus everything that is geometrically or probabilistically blocked upstream. Blocking polygons (Level 3) remove the rays they intercept; anchoring zones (Levels 4–5) reduce those rays by the factor \(a_p\). No multiplicative “remaining” factor is applied across obstacles — the correct formulation is the shadow-coverage model, identical in form across blockers (\(h - h(\cap S)\)) and anchoring (\(h - a_p\cdot h(\cap S_A)\)).
The final drifting rate is the sum over all 8 wind-rose directions, all obstacles, all legs and all ship categories (equation (3)).
Levels 1–5 of the worked examples in
drifting/debug/reproduce every intermediate value in this chapter.The quad-sweep shadow algorithm is used in both the probability calculation (via polygon subtraction) and the drift-corridor visualisation; the corridor-clipping step is visualisation-only.