Back to Main Site

WindMar Technical Documentation

Weather-aware maritime route optimization for MR product tankers

Development Log v0.0.5 — Last Stable
v0.0.5 2026-02-09 Weather DB architecture, temporal weather provisioning, Monte Carlo with correlated perturbations, CII compliance, PostgreSQL persistence
v0.0.4 2026-02-09 Frontend refactor, Monte Carlo simulation, grid weather provider
v0.0.3 2026-02-09 Data sources docs, credential setup, modernize pyproject.toml
v0.0.2 2026-02-08 GFS near-real-time wind data + 5-day forecast timeline
v0.0.1 2026-02-08 Initial public release
Maritime Operations Tool
WindMar is a physics-based vessel performance modeling system with weather-aware A* route optimization, fuel consumption minimization, and IMO CII compliance tracking. It combines hydrodynamic resistance calculations, seakeeping analysis, and real-time weather data to find the safest and most fuel-efficient routes for MR product tankers.

What is WindMar?

WindMar is an end-to-end maritime route optimization platform designed for Medium Range (MR) product tankers. It models the complete chain of vessel performance physics — from calm water resistance through wind and wave added resistance, engine SFOC curves, and propulsion efficiency — to predict fuel consumption under real weather conditions. The A* pathfinding algorithm then searches for the route that minimizes total fuel consumption while respecting safety constraints and regulatory zones. All calculations are grounded in established naval architecture methods: Holtrop-Mennen for hull resistance, Blendermann for wind loads, Kwon for wave added resistance, and simplified strip theory for seakeeping motions.

The system ingests near-real-time wind data from NOAA GFS (0.25° resolution, via NOMADS), wave and current data from Copernicus Marine Service (CMEMS), with ERA5 reanalysis as a secondary wind fallback. A 5-day forecast timeline (f000–f120, 3-hourly steps) enables animated wind visualization. The system evaluates vessel motions (roll, pitch, vertical acceleration) and enforces safety criteria that block or penalize dangerous sea states. Finally, it calculates the IMO Carbon Intensity Indicator (CII) rating for the completed voyage, enabling operators to assess regulatory compliance before departure.

Vessel Performance

  • Holtrop-Mennen calm water resistance
  • Blendermann wind resistance model
  • Kwon wave added resistance formula
  • Engine SFOC curves with load-dependent correction

Route Optimization

  • Weather-aware A* pathfinding
  • Variable speed optimization per leg
  • Land avoidance with global-land-mask
  • Path smoothing with fuel impact verification

Seakeeping Safety

  • Roll, pitch, and acceleration prediction
  • Slamming probability assessment
  • Parametric roll risk detection
  • Safety constraints in route optimization

Weather Integration

  • NOAA GFS near-real-time wind (0.25°)
  • 5-day forecast timeline with animation
  • CMEMS wave and ocean current data
  • ERA5 wind fallback, climatology blending

IMO Compliance

  • CII rating calculator (A through E)
  • MEPC.339(76) reference values
  • Multi-year reduction factor projections
  • CO2 emission factors for multiple fuel types

Vessel Calibration

  • Noon report processing from Excel
  • scipy.optimize parameter fitting
  • Per-component calibration factors
  • RMSE-based objective minimization

Architecture

Component Overview

WindMar follows a layered architecture with a clear separation between the user-facing frontend, the API backend, the physics-based optimization engine, and external data providers. Each component runs as an independent service orchestrated via Docker Compose.

Component Technology Responsibility
Frontend Next.js 15 Map visualization, route input, results display, CII dashboard
Backend FastAPI (Python) REST API, request validation, orchestration of optimization engine
Optimization Engine Python (NumPy, SciPy) Physics models, A* pathfinding, speed optimization, seakeeping
Weather Provider NOAA GFS / CMEMS / ERA5 GFS wind (primary), CMEMS waves and currents, ERA5 wind fallback, 5-day forecast
Database PostgreSQL 16 Vessel specs, route history, calibration data, regulatory zones
Cache Redis 7 / In-memory Rate limiting, session state; weather fields cached in-memory

Data Flow

  1. Route Definition — User defines departure, arrival, and waypoints on the map interface. Vessel condition (laden/ballast) and speed preferences are selected.
  2. Weather Fetch — Backend requests weather data from Copernicus for the route corridor. Wind, wave, and current fields are interpolated to the optimization grid.
  3. Performance Calculation — For each grid cell, the optimization engine computes total resistance (calm water + wind + waves), brake power, SFOC, and fuel consumption at candidate speeds.
  4. A* Pathfinding — The A* algorithm searches the grid using fuel consumption as the edge cost. Safety constraints block or penalize dangerous cells. Regulatory zones modify costs.
  5. Speed Optimization — Each leg of the optimal path is individually speed-optimized to minimize fuel per nautical mile given local weather conditions.
  6. Results — The optimized route, fuel estimate, ETA, leg-by-leg breakdown, seakeeping assessment, and CII rating are returned to the frontend for display.

Docker Services

services:
  db:
    image: postgres:16-alpine
    ports: ["${DB_PORT:-5432}:5432"]

  redis:
    image: redis:7-alpine
    ports: ["${REDIS_PORT:-6379}:6379"]

  api:
    build: .
    ports: ["${API_PORT:-8000}:8000"]
    depends_on: [db, redis]

  frontend:
    build: ./frontend
    ports: ["${FRONTEND_PORT:-3000}:3000"]
    depends_on: [api]

All ports and credentials are configured via environment variables in .env (see .env.example for defaults). Copy and customize before starting.

Installation

Prerequisites

  • Python 3.10 or later
  • Node.js 18 or later
  • Docker & Docker Compose

Docker Compose (Recommended)

git clone https://github.com/SL-Mar/windmar.git
cd windmar
cp .env.example .env    # Edit with your settings
docker compose up -d --build

This starts all four services (PostgreSQL, Redis, API, frontend). By default the frontend is accessible at http://localhost:3000 and the backend API at http://localhost:8000. Ports are configurable via .env.

Manual Setup

Backend

pip install -r requirements.txt
python api/main.py

Frontend

cd frontend
npm install --legacy-peer-deps
npm run dev

Weather Data Sources

WindMar uses a three-tier provider chain that automatically falls back when a source is unavailable. See Weather Data Acquisition for full technical details.

Data TypePrimary SourceFallbackCredentials
WindNOAA GFS (0.25°)ERA5 → SyntheticNone (free)
WavesCMEMS wave modelSyntheticCMEMS account
CurrentsCMEMS physics modelSyntheticCMEMS account
ForecastGFS f000–f120 (5-day)None
Wind works out of the box — GFS data from NOAA NOMADS is freely available without authentication. Only CMEMS wave/current data requires credentials.

Obtaining Weather Credentials

CMEMS (waves and currents): Register at marine.copernicus.eu, then set in .env:

COPERNICUSMARINE_SERVICE_USERNAME=your_username
COPERNICUSMARINE_SERVICE_PASSWORD=your_password

CDS ERA5 (wind fallback): Register at cds.climate.copernicus.eu, copy your Personal Access Token from your profile, then set:

CDSAPI_KEY=your_personal_access_token

Environment Variables

Copy .env.example to .env and configure. Key variables:

DATABASE_URL=postgresql://windmar:password@db:5432/windmar
REDIS_URL=redis://:password@redis:6379/0
API_SECRET_KEY=generate_with_openssl_rand_hex_32
COPERNICUSMARINE_SERVICE_USERNAME=your_username
COPERNICUSMARINE_SERVICE_PASSWORD=your_password
CDSAPI_KEY=your_cds_personal_access_token
Security
Never commit .env to version control. The .gitignore excludes it by default. See .env.example for all available variables with descriptions.

Tech Stack

Backend

LibraryVersionPurpose
FastAPI0.104+REST API framework
Uvicorn0.24+ASGI server
SQLAlchemy2.0+ORM and database access
Pydantic2.0+Request/response validation
Alembic1.13+Database migrations

Frontend

LibraryVersionPurpose
Next.js15React framework with SSR
React19UI component library
Leaflet1.9+Map rendering and route display
Recharts2.10+Charts for fuel, CII, seakeeping data
Tailwind CSS3.4+Utility-first styling

Scientific Computing

LibraryVersionPurpose
NumPy1.26+Array operations, vector math
SciPy1.11+Optimization (Nelder-Mead), interpolation
global-land-mask1.0+Land/ocean grid classification
xarray2024.1+NetCDF weather data handling
copernicusmarine1.0+Copernicus CMEMS API client

Database & Cache

ServiceVersionPurpose
PostgreSQL16Persistent storage for vessel specs, routes, zones
Redis7Weather field caching, session state

External APIs

ServiceDataResolution
Copernicus CMEMSSignificant wave height, wave period, wave direction0.25 deg, 3-hourly
ERA5 Reanalysis10m wind speed, wind direction0.25 deg, hourly
Copernicus CMEMSOcean current speed and direction0.083 deg, daily

Calm Water Resistance

Calm water resistance is the foundation of all fuel consumption predictions. WindMar implements the Holtrop-Mennen method, the industry-standard empirical approach for estimating hull resistance of displacement vessels. The method decomposes total resistance into frictional, wave-making, and appendage components, each derived from the vessel's principal dimensions and hull form coefficients.

Holtrop-Mennen Method

Total Calm Water Resistance
\[R_{\text{total}} = R_f + R_w + R_{\text{app}}\]

Where:

  • \(R_f\) = Frictional resistance (dominant at low Froude numbers)
  • \(R_w\) = Wave-making resistance (grows exponentially with speed)
  • \(R_{\text{app}}\) = Appendage resistance (rudder, shaft brackets, etc.)

A) Frictional Resistance (R_f)

Frictional resistance arises from the viscous shear stress between the hull surface and the surrounding water. It dominates at service speeds typical of MR tankers (Froude number 0.12-0.18). The ITTC 1957 model-ship correlation line provides the friction coefficient, and the Holtrop form factor accounts for the three-dimensional shape of the hull.

Frictional Resistance
\[R_f = \tfrac{1}{2} \cdot \rho_{sw} \cdot V^2 \cdot S \cdot C_f \cdot (1 + k_1)\]

Where:

  • \(\rho_{sw} = 1025 \; \text{kg/m}^3\) (seawater density)
  • \(V\) = ship speed (m/s)
  • \(S\) = wetted surface area (m²)
  • \(C_f = \frac{0.075}{(\log_{10} Re - 2)^2}\) (ITTC 1957 friction line)
  • \(Re = \frac{V \cdot L_{pp}}{\nu_{sw}}\) (Reynolds number)
  • \(\nu_{sw} = 1.19 \times 10^{-6} \; \text{m}^2\text{/s}\) (kinematic viscosity at 15°C)

At service speed of 14.5 knots (7.46 m/s) with L_pp = 176 m, the Reynolds number is approximately 1.1 × 109, placing the flow firmly in the turbulent regime. The friction coefficient C_f is then approximately 0.00145.

Form Factor (1 + k_1)

The form factor corrects the flat-plate friction coefficient for the three-dimensional hull shape. Holtrop-Mennen provides a regression formula for tanker hull forms based on the principal dimension ratios and block coefficient.

Holtrop-Mennen Form Factor for Tankers
\[k_1 = 0.93 + 0.4871 \cdot \frac{B}{L_{pp}} - 0.2156 \cdot \frac{B}{T} + 0.1027 \cdot C_b\]

Default MR tanker values:

  • \(B / L_{pp} = 32 / 176 = 0.182\)
  • \(B / T_{\text{laden}} = 32 / 11.8 = 2.71\)
  • \(B / T_{\text{ballast}} = 32 / 6.5 = 4.92\)
  • \(C_{b,\text{laden}} = 0.82\)
  • \(C_{b,\text{ballast}} = 0.75\)

Resulting form factors:

  • \((1 + k_1)_{\text{laden}} = 1 + 0.93 + 0.4871 \times 0.182 - 0.2156 \times 2.71 + 0.1027 \times 0.82 \approx 1.52\)
  • \((1 + k_1)_{\text{ballast}} \approx 1.35\)

B) Wave-Making Resistance (R_w)

Wave-making resistance is generated by the pressure field around the hull that creates surface waves. For MR tankers operating at low Froude numbers (Fr < 0.2), wave-making resistance is a small fraction of total resistance. However, it increases exponentially with speed, making it the dominant factor limiting maximum speed.

Wave-Making Resistance
\[R_w = c_1 \cdot c_7 \cdot \Delta \cdot \rho_{sw} \cdot g \cdot e^{-0.4 \cdot Fr^{-2}} \quad \text{for } Fr < 0.4\] \[R_w = 0 \quad \text{for } Fr \ge 0.4\]

Where:

  • \(Fr = \frac{V}{\sqrt{g \cdot L_{pp}}}\) (Froude number)
  • \(c_1 = 2223105 \cdot C_b^{3.78613} \cdot (T/B)^{1.07961}\)
  • \(c_7 = 0.229577 \cdot (B/L_{pp})^{1/3}\)
  • \(\Delta\) = displacement (tonnes)
  • \(g = 9.81 \; \text{m/s}^2\)

At the design speed of 14.5 knots, the Froude number for an MR tanker is approximately Fr = 7.46 / sqrt(9.81 × 176) ≈ 0.179. The exponential term exp(-0.4 × 0.179-2) is very small, confirming that wave-making resistance contributes less than 5% of total resistance at service speed.

C) Appendage Resistance (R_app)

Appendage resistance accounts for the drag of the rudder, propeller shaft brackets, bilge keels, and other hull appendages. For MR tankers with standard appendage configurations, this is estimated as a fixed percentage of frictional resistance.

Appendage Resistance
\[R_{\text{app}} = 0.05 \times R_f \quad \text{(5\% of frictional resistance)}\]

Default MR Tanker Specifications

Parameter Laden Ballast
LOA183.0 m183.0 m
L_pp176.0 m176.0 m
Beam32.0 m32.0 m
Draft11.8 m6.5 m
Displacement65,000 MT20,000 MT
Block Coefficient (C_b)0.820.75
Wetted Surface7,500 m²5,200 m²
Service Speed14.5 kts15.0 kts
DWT49,000 MT
MCR8,840 kW8,840 kW
SFOC at MCR171 g/kWh171 g/kWh

Wind Resistance

Wind resistance is computed using the Blendermann method, which models the aerodynamic forces on the vessel's above-water structure as a function of relative wind speed, direction, and the vessel's projected areas. The method accounts for both longitudinal (drag) and transverse (side force) components, with the longitudinal component directly opposing forward motion and the transverse component contributing indirectly through induced drift.

Blendermann Method

Relative Wind Angle
\[\theta_{\text{rel}} = \left| \left( (\text{wind\_dir} - \text{heading}) + 180 \right) \bmod 360 - 180 \right|\]

Range: \(0°\) (head wind) to \(180°\) (following wind)

Wind Force Coefficients

Longitudinal coefficient:

\[C_x = -0.6 \cdot \cos(\theta_{\text{rel}}) + 0.8 \cdot \cos^2(\theta_{\text{rel}})\]

Transverse coefficient:

\[C_y = 0.9 \cdot \sin(\theta_{\text{rel}})\]
Wind Forces and Total Resistance

Longitudinal force:

\[F_x = \tfrac{1}{2} \cdot \rho_{\text{air}} \cdot V_{\text{wind}}^2 \cdot A_{\text{frontal}} \cdot |C_x|\]

Transverse force:

\[F_y = \tfrac{1}{2} \cdot \rho_{\text{air}} \cdot V_{\text{wind}}^2 \cdot A_{\text{lateral}} \cdot |C_y|\]

Total wind resistance:

\[R_{\text{wind}} = F_x + 0.1 \cdot F_y\]

Where:

  • \(\rho_{\text{air}} = 1.225 \; \text{kg/m}^3\) (air density at 15°C, sea level)
  • \(V_{\text{wind}}\) = true wind speed (m/s)
  • \(A_{\text{frontal}}\) = projected frontal area (m²)
  • \(A_{\text{lateral}}\) = projected lateral area (m²)

Wind Projection Areas

Area Laden Ballast
Frontal (A_frontal)450 m²850 m²
Lateral (A_lateral)2,100 m²2,800 m²
Ballast Wind Exposure
In ballast condition the vessel rides higher, exposing 89% more frontal area (850 vs 450 m²) and 33% more lateral area (2,800 vs 2,100 m²) to the wind. This results in significantly higher windage forces, particularly in head wind conditions where ballast wind resistance can exceed laden resistance by a factor of two or more.

Physical Interpretation by Wind Angle

  • Head wind (0°) — Maximum longitudinal resistance. C_x reaches its peak value. This is the worst-case scenario for fuel consumption.
  • Beam wind (90°) — Minimal longitudinal component but maximum transverse force. The 10% coupling factor from F_y captures the drift-induced resistance.
  • Following wind (180°) — Minimal resistance. The wind partially assists forward motion. C_x is near zero or slightly negative (assisting).

Wave Resistance

Wave added resistance is the increase in hull resistance caused by the vessel's interaction with ocean waves. WindMar uses the Kwon empirical formula, which estimates added resistance as a function of significant wave height, beam, Froude number, and the encounter angle between the vessel heading and the dominant wave direction.

Kwon Empirical Formula

Wave Added Resistance
\[R_{\text{wave}} = f_{\text{dir}} \cdot 4.5 \cdot \rho_{sw} \cdot g \cdot B \cdot H_s^2 \cdot (1 + Fr)\]

Directional factor:

\[f_{\text{dir}} = \frac{1 + \cos(\theta_{\text{encounter}})}{2}\]

Values by encounter angle:

  • Head seas (\(\theta = 0°\)): \(f_{\text{dir}} = 1.0\)
  • Bow quarter (\(\theta = 45°\)): \(f_{\text{dir}} = 0.85\)
  • Beam seas (\(\theta = 90°\)): \(f_{\text{dir}} = 0.5\)
  • Stern quarter (\(\theta = 135°\)): \(f_{\text{dir}} = 0.15\)
  • Following seas (\(\theta = 180°\)): \(f_{\text{dir}} = 0.0\)

Where:

  • \(H_s\) = significant wave height (m)
  • \(B\) = beam (m)
  • \(Fr = \frac{V}{\sqrt{g \cdot L_{pp}}}\) (Froude number)
  • \(\rho_{sw} = 1025 \; \text{kg/m}^3\)
  • \(g = 9.81 \; \text{m/s}^2\)

The Kwon formula produces a quadratic dependence on wave height: doubling H_s from 2 m to 4 m increases wave added resistance by a factor of four. The directional factor ensures that head seas produce maximum added resistance while following seas produce none, which matches the physical expectation that waves opposing the vessel's motion cause the greatest speed loss.

For a laden MR tanker in head seas with H_s = 3 m at service speed (Fr ≈ 0.179), the wave added resistance is approximately:

Example Calculation
\[R_{\text{wave}} = 1.0 \times 4.5 \times 1025 \times 9.81 \times 32 \times 3^2 \times (1 + 0.179)\] \[= 1.0 \times 4.5 \times 1025 \times 9.81 \times 32 \times 9 \times 1.179 \approx 15{,}400 \; \text{N} \approx 15.4 \; \text{kN}\]

SFOC Curve

Specific Fuel Oil Consumption (SFOC) describes the mass of fuel consumed per unit of energy delivered by the engine. It varies with engine load: marine diesel engines have an optimal efficiency zone around 75% of Maximum Continuous Rating (MCR), with increasing specific consumption at both lower and higher loads. WindMar models this behavior with a piecewise linear correction applied to the MCR reference SFOC.

SFOC Load Correction

Below optimal load (\(l_f < 0.75\)):

\[\text{SFOC} = \text{SFOC}_{MCR} \times \bigl(1.0 + 0.15 \times (0.75 - l_f)\bigr)\]

At or above optimal load (\(l_f \ge 0.75\)):

\[\text{SFOC} = \text{SFOC}_{MCR} \times \bigl(1.0 + 0.05 \times (l_f - 0.75)\bigr)\]

Constraints:

  • \(l_f = \max\bigl(0.15,\; \min(1.0,\; P_{\text{brake}} / P_{MCR})\bigr)\)
  • \(\text{SFOC}_{MCR} = 171 \; \text{g/kWh}\) (manufacturer specification)

The asymmetric correction reflects the physical reality of diesel engine combustion: at low loads, incomplete combustion and thermal losses increase specific consumption more steeply (15% penalty per 0.1 load fraction below 75%) than the mild increase at high loads (5% penalty per 0.1 above 75%) caused by increased cylinder pressures and thermal stress.

SFOC Behavior by Engine Load

Engine Load SFOC (g/kWh) Efficiency Note
15% (minimum)~186Minimum load limit; high specific consumption
50%~177Part-load penalty; below optimal range
75%~171Optimal operating point; minimum SFOC
85%~172Near optimal; slight overload penalty
100% (MCR)~173Full load; mild thermal penalty

The engine load is clamped to the range [0.15, 1.0]. Below 15% load, the engine is considered non-operational. Above 100% MCR, the engine cannot deliver more power, so the brake power is capped at P_MCR = 8,840 kW.

Propulsion Chain

The propulsion chain converts total hull resistance into brake power demand and then into fuel consumption. It accounts for the efficiencies of the propeller, the hull-propeller interaction, and the relative rotative efficiency of the propulsion system.

Brake Power Calculation

Tow Power to Brake Power

Tow power (power to overcome resistance):

\[P_{\text{tow}} = \frac{R_{\text{total}} \cdot V}{1000} \quad [\text{kW}]\]

Where:

  • \(R_{\text{total}} = R_{\text{calm}} + R_{\text{wind}} + R_{\text{wave}}\) [N]
  • \(V\) = ship speed [m/s]

Brake power (engine output):

\[P_{\text{brake}} = \frac{P_{\text{tow}}}{\eta_{\text{prop}} \cdot \eta_{\text{hull}} \cdot \eta_{\text{rre}}}\]

Where:

  • \(\eta_{\text{prop}} = 0.65\) (propeller open-water efficiency)
  • \(\eta_{\text{hull}} = 1.05\) (hull efficiency; > 1 due to wake gain)
  • \(\eta_{\text{rre}} = 1.00\) (relative rotative efficiency)

Combined propulsive efficiency:

\[\eta_{\text{total}} = \eta_{\text{prop}} \cdot \eta_{\text{hull}} \cdot \eta_{\text{rre}} = 0.65 \times 1.05 \times 1.00 = 0.6825\]

Power capped at MCR:

\[P_{\text{brake}} = \min(P_{\text{brake}},\; P_{MCR}) \quad \text{where } P_{MCR} = 8{,}840 \; \text{kW}\]

Hull efficiency exceeds 1.0 because the wake behind the hull reduces the effective inflow velocity to the propeller relative to the ship speed. The propeller operates in a flow field that is slower than the free-stream velocity, requiring less thrust to overcome the same resistance.

Fuel Consumption

Fuel Consumption per Time Step
\[\text{Fuel} \; [\text{MT}] = \frac{P_{\text{brake}} \times \text{SFOC} \times t_{\text{hours}}}{1{,}000{,}000}\]

Where:

  • \(P_{\text{brake}}\) = brake power [kW]
  • \(\text{SFOC}\) = specific fuel oil consumption [g/kWh] (load-dependent)
  • \(t_{\text{hours}}\) = duration of the leg [hours]
  • \(1{,}000{,}000\) = conversion from grams to metric tonnes

For example, at service speed in calm water with P_brake = 5,500 kW and SFOC = 172 g/kWh, a 24-hour transit consumes approximately:

Fuel = (5500 × 172 × 24) / 1,000,000 = 22.7 MT/day

Ship Motions

Ship motion prediction is essential for seakeeping assessment and safety enforcement during route optimization. WindMar implements simplified strip-theory models for roll, pitch, and vertical acceleration. These models provide physically meaningful motion estimates that capture the dominant effects of wave height, encounter frequency, and vessel loading condition.

Roll Motion — Single DOF Oscillator

Roll is modeled as a single degree-of-freedom oscillator driven by the transverse wave slope. The response amplitude operator (RAO) captures the frequency-dependent magnification, with resonance occurring when the encounter frequency matches the vessel's natural roll frequency.

Roll Amplitude

Roll amplitude [rad]:

\[\phi = \frac{\text{wave\_slope}}{GM} \cdot \frac{H_s}{2} \cdot \text{RAO} \cdot \sin(\theta_{\text{beam}})\]

Response Amplitude Operator:

\[\text{RAO} = \frac{1}{\sqrt{\left(1 - \left(\frac{\omega_e}{\omega_{\text{roll}}}\right)^2\right)^2 + \left(2 \zeta \cdot \frac{\omega_e}{\omega_{\text{roll}}}\right)^2}}\]

Where:

  • \(\omega_{\text{roll}} = \frac{2\pi}{T_{\text{roll}}}\) (natural roll frequency)
  • \(T_{\text{roll}}\): 14 s (laden), 10 s (ballast)
  • \(\zeta = 0.05\) (5% critical damping)
  • \(GM\): 2.5 m (laden), 4.0 m (ballast)
  • \(\theta_{\text{beam}}\) = angle from beam (90° = pure beam seas)
  • \(\omega_e\) = encounter frequency (depends on ship speed & heading)

The natural roll period is longer in laden condition (14 s) due to the higher metacentric height combined with greater mass moment of inertia. Ballast condition has a shorter period (10 s) because the higher GM (4.0 m) produces a stiffer restoring moment relative to the reduced inertia. The 5% critical damping ratio is conservative and accounts for hull friction, bilge keel effects, and cargo damping.

Pitch Motion

Pitch is driven primarily by head seas and bow-quarter seas. The amplitude depends on the wave slope, the encounter angle (maximum in head seas), and a pitch factor that captures the wavelength-to-ship-length tuning effect.

Pitch Amplitude

Pitch amplitude [deg]:

\[\theta_{\text{pitch}} = 10 \cdot \text{wave\_slope} \cdot |\cos(\theta_{\text{enc}})| \cdot f_{\text{pitch}}\]

Pitch factor depends on \(L/\lambda\) ratio:

\[f_{\text{pitch}} = \begin{cases} 2 \cdot (L / \lambda) & L/\lambda < 0.5 \\ 1 - 0.3 \cdot |L/\lambda - 1| & 0.5 \le L/\lambda < 1.5 \\ 0.5 / (L / \lambda) & L/\lambda \ge 1.5 \end{cases}\]

Where:

  • \(\lambda = \frac{g \cdot T_{\text{wave}}^2}{2\pi}\) (deep water wavelength)
  • \(L = L_{pp} = 176 \; \text{m}\)
  • \(T_{\text{wave}}\) = peak wave period (s)

The pitch factor peaks near L/λ = 1 (ship length equals wavelength), where the vessel "straddles" consecutive wave crests and troughs, producing maximum pitching motion. When the wavelength is much shorter or longer than the ship, the pitch factor decreases because the vessel either averages out multiple short waves or rides on a single long wave slope.

Vertical Acceleration

Vertical acceleration at specific locations along the ship length combines the heave acceleration at the center of gravity with the pitch-induced acceleration component. Points far from midship (bridge, bow) experience amplified accelerations due to the pitch lever arm.

Vertical Acceleration

Heave acceleration (at center of gravity):

\[a_{\text{heave}} = \frac{H_s}{2} \cdot \omega_e^2 \cdot \bigl(0.3 + 0.7 \cdot \cos(\theta_{\text{enc}})\bigr)\]

Point acceleration (at distance \(x\) from midship):

\[a_{\text{point}} = \sqrt{a_{\text{heave}}^2 + \bigl(x \cdot \theta_{\text{pitch,rad}} \cdot \omega_e^2\bigr)^2}\]

Reference locations:

  • Bridge: \(x = -70\) m from midship (aft)
  • Bow: \(x = +88\) m from midship (forward)
  • Midship: \(x = 0\) m (minimum acceleration)

The bow experiences the highest vertical accelerations because it is the farthest point forward from the pitch axis. At the bridge (70 m aft of midship), accelerations are also amplified but less than at the bow. Vertical acceleration is the primary crew comfort criterion and drives speed reduction decisions in heavy weather.

Safety Criteria

WindMar enforces a three-tier safety classification (Safe, Marginal, Dangerous) based on vessel motion amplitudes and derived seakeeping parameters. These criteria are applied during route optimization to block unsafe routes and penalize marginally safe routes.

Safety Limits

Motion Parameter Safe Marginal Dangerous
Roll amplitude < 15° 15° – 25° > 25°
Pitch amplitude < 5° 5° – 8° > 8°
Vertical acceleration < 0.2g 0.2g – 0.3g > 0.3g
Slamming probability < 3% 3% – 10% > 10%
Route Optimization Enforcement
Safety criteria are enforced during A* route optimization. Grid cells classified as Dangerous are assigned infinite cost, effectively blocking the route through that cell. Marginal cells receive a 1.5× cost multiplier, encouraging the optimizer to find alternative paths when available. Safe cells use the nominal fuel-based cost.

The overall safety classification for a cell is determined by the worst-case criterion: if any single parameter falls in the Dangerous range, the cell is classified as Dangerous regardless of the other parameters.

Slamming & Parametric Roll

Slamming (Ochi Criteria)

Slamming occurs when the bow emerges from the water and re-enters with high velocity, generating impulsive loads on the hull structure. The Ochi criteria estimate the probability of slamming based on bow emergence and re-entry velocity. Slamming risk is highest in ballast condition (lower forward draft) and head seas.

Bow Vertical Motion at Forward Perpendicular
\[y_{\text{bow}} = \frac{H_s}{2} + L_{fp} \cdot \sin(\theta_{\text{pitch}})\]

Where:

  • \(H_s\) = significant wave height (m)
  • \(L_{fp}\) = distance from midship to forward perpendicular (m)
  • \(\theta_{\text{pitch}}\) = pitch amplitude (rad)
Emergence Probability and Impact Velocity

Emergence probability:

\[P_{\text{emergence}} = f(y_{\text{bow}},\; \text{freeboard})\]

When \(y_{\text{bow}}\) exceeds bow freeboard, the bow emerges from the water and slamming becomes possible on re-entry.

Re-entry velocity:

\[v_{\text{impact}} = \sqrt{2 \cdot g \cdot \max(0,\; y_{\text{bow}} - \text{freeboard})}\]

Where:

  • \(g = 9.81 \; \text{m/s}^2\)
  • freeboard = distance from waterline to deck at bow (m)
  • Laden: freeboard \(\approx 5.2\) m
  • Ballast: freeboard \(\approx 10.5\) m (higher deck but lower draft = more emergence)

Despite the higher freeboard in ballast, the reduced forward draft means the keel rises above the still water surface more frequently, particularly in head seas. The combination of high emergence amplitude and significant re-entry velocity produces the impulsive slamming loads that can cause structural damage to the forward hull plating.

Parametric Roll Risk

Parametric rolling is a dangerous phenomenon where roll amplitudes build up rapidly due to periodic variation in the vessel's waterplane area (and thus stability) as waves pass along the hull. It occurs primarily in head or following seas when the wave encounter period is approximately twice the natural roll period.

Parametric Roll Conditions

Parametric roll risk is elevated when:

  1. Encounter period ratio: \(\frac{T_{\text{wave}}}{T_{\text{roll}}} \approx 2.0\)   (tolerance: \(1.8 < T_{\text{wave}}/T_{\text{roll}} < 2.2\))
  2. Wave height exceeds critical threshold: \(H_s > H_{\text{critical}}\)
  3. Head seas or following seas: \(\theta_{\text{encounter}} < 30°\) or \(\theta_{\text{encounter}} > 150°\)

When all three conditions are met, the route cell is flagged for elevated parametric roll risk.

Parametric roll can develop in just a few wave cycles, reaching amplitudes of 30–40 degrees or more. It is particularly dangerous because it can occur in moderate sea states where other motion criteria appear safe. Detection and avoidance through route optimization is a key safety feature.

A* Algorithm

Route optimization uses the A* pathfinding algorithm to find the minimum-cost path through a weather-dependent grid. Each grid cell has a traversal cost determined by the fuel consumption required to cross it at the locally optimal speed, considering all resistance components and safety constraints.

Grid Construction

  • Resolution: Configurable, default 0.5 degrees (~30 nm at the equator)
  • Land masking: Land cells are removed using the global-land-mask library, which provides a 1-km resolution land/ocean classification
  • Corridor: The search grid extends ±5 degrees latitude and longitude around the direct great-circle route to limit search space while allowing sufficient deviation for weather routing
  • Connectivity: 8-connected grid (cardinal + diagonal neighbors)

A* Search

A* Cost Function
\[f(n) = g(n) + h(n)\]

Where:

  • \(f(n)\) = total estimated cost of path through node \(n\)
  • \(g(n)\) = actual cost from start to node \(n\) (accumulated fuel consumption)
  • \(h(n)\) = heuristic estimate of cost from node \(n\) to goal

Heuristic:

\[h(n) = d_{\text{goal}} \times r_{\text{fuel,min}}\]

(admissible: never overestimates actual cost)

Move cost (node \(m\) to neighbor \(n\)):

\[\text{cost}(m, n) = \text{fuel\_consumption}(m, n, V_{\text{optimal}})\]

Where fuel_consumption accounts for:

  • Calm water resistance (Holtrop-Mennen)
  • Wind resistance (Blendermann) at local wind conditions
  • Wave resistance (Kwon) at local wave conditions
  • Engine SFOC at resulting load
  • Safety penalty multiplier (\(1.0\times\) safe, \(1.5\times\) marginal, \(\infty\) dangerous)

Diagonal cost:

\[\text{cost}_{\text{diagonal}} = \sqrt{2} \times \text{cost}_{\text{cardinal}}\]

Search Procedure

  1. Initialize priority queue (min-heap ordered by f-score) with start node
  2. Pop node with lowest f-score
  3. If goal reached, reconstruct path and return
  4. For each of 8 neighbors:
    • Skip if land cell or already visited with lower cost
    • Compute weather at neighbor location (interpolated from grid)
    • Compute fuel cost to traverse cell at optimal speed
    • Apply safety and regulatory zone multipliers
    • Update neighbor's g-score if new path is cheaper
    • Add to priority queue
  5. Terminate if cells explored exceeds 50,000 (safety limit)

Cost Calculation Code

def compute_cell_cost(self, from_cell, to_cell, weather):
    """Compute fuel cost to traverse a grid cell."""
    distance_nm = haversine(from_cell.lat, from_cell.lon,
                            to_cell.lat, to_cell.lon)

    # Find optimal speed for this cell
    best_fuel = float('inf')
    best_speed = self.vessel.service_speed

    for speed_kts in np.linspace(6.0, 18.0, 7):
        speed_ms = speed_kts * 0.5144

        # Total resistance
        r_calm = self.vessel.calm_water_resistance(speed_ms)
        r_wind = self.vessel.wind_resistance(
            speed_ms, weather.wind_speed, weather.wind_dir,
            self.heading)
        r_wave = self.vessel.wave_resistance(
            speed_ms, weather.wave_height, weather.wave_dir,
            self.heading)
        r_total = r_calm + r_wind + r_wave

        # Brake power and fuel
        p_tow = r_total * speed_ms / 1000
        p_brake = min(p_tow / self.vessel.eta_total,
                      self.vessel.mcr)
        sfoc = self.vessel.sfoc(p_brake / self.vessel.mcr)
        time_h = distance_nm / speed_kts
        fuel_mt = p_brake * sfoc * time_h / 1e6

        if fuel_mt < best_fuel:
            best_fuel = fuel_mt
            best_speed = speed_kts

    # Apply safety multiplier
    safety = self.assess_safety(weather, best_speed)
    if safety == 'dangerous':
        return float('inf')
    elif safety == 'marginal':
        best_fuel *= 1.5

    return best_fuel

Path Smoothing

After A* finds the optimal grid path, a smoothing pass removes unnecessary waypoints to produce a more natural route. The algorithm iteratively removes intermediate waypoints and checks:

  • The straight-line segment does not cross land
  • The fuel consumption difference is within 1% of the unsmoothed path
  • All intermediate points maintain safe seakeeping conditions

Variable Speed Optimization

Rather than maintaining a constant speed throughout the voyage, WindMar optimizes speed independently for each leg of the route. This allows the vessel to slow down in adverse weather (reducing resistance cubically) and speed up in calm conditions, minimizing total fuel consumption for the voyage.

Per-Leg Speed Optimization

  • Speed range: 6 to 18 knots, tested at 7 discrete values (6, 8, 10, 12, 14, 16, 18 kts)
  • Objective: Minimize fuel consumption per nautical mile for each leg
  • Weather-dependent: The optimal speed varies with local wind, wave, and current conditions
  • Power constraint: Speed is limited by MCR — if the required brake power exceeds MCR at a given speed, that speed is not achievable
Speed Optimization Objective

For each leg \(i\) with distance \(d_i\) and weather conditions \(w_i\):

\[V_i^* = \arg\min_{V} \left[ \frac{\text{fuel}(V, w_i)}{d_i} \right]\]

Subject to:

\[6 \; \text{kts} \le V \le 18 \; \text{kts}, \quad P_{\text{brake}}(V, w_i) \le P_{MCR}\] \[\text{fuel}(V, w_i) = \frac{P_{\text{brake}}(V, w_i) \times \text{SFOC}(\text{load}) \times (d_i / V)}{10^6}\]

The cubic relationship between speed and resistance means that a small speed reduction produces a large fuel saving. For example, reducing speed from 14 to 12 knots (14% reduction) can reduce fuel consumption by approximately 35% per hour, although the voyage takes longer. The optimizer balances this trade-off based on the fuel-per-mile metric.

Safety Constraints

Safety constraints are integrated directly into the A* search, ensuring that the optimizer never produces a route that violates safety criteria or regulatory requirements. Constraints are applied at two levels: seakeeping safety and regulatory zone enforcement.

Seakeeping Constraints in A* Search

  • Dangerous conditions: Cell cost = infinity. The A* algorithm will not expand through this cell, forcing the route around the hazardous area. This applies when any motion parameter exceeds the Dangerous threshold.
  • Marginal conditions: Cell cost × 1.5. The optimizer can still route through the cell, but at a 50% cost penalty. This encourages deviation when alternative routes are available but permits marginal conditions when no safe alternative exists.
  • Safe conditions: Nominal fuel-based cost with no penalty.

Regulatory Zone Enforcement

Regulatory zones modify the A* cost function based on the zone type. Three enforcement modes are supported:

Zone Type Effect on A* Cost Example
EXCLUSION cost = infinity (blocks route) HRA (High Risk Areas), environmental exclusion zones
PENALTY cost × penalty_factor ECA (Emission Control Areas) requiring fuel switching
MANDATORY Ensures path includes zone TSS (Traffic Separation Schemes), mandatory reporting points

Exclusion zones and penalty zones are applied per-cell during the A* expansion. Mandatory zones are enforced as waypoint constraints: the route must pass through at least one cell in each mandatory zone.

IMO CII Rating

The Carbon Intensity Indicator (CII) is a mandatory IMO measure under MEPC.339(76) that rates a ship's operational carbon efficiency on an A-to-E scale. WindMar calculates the attained CII for a voyage and compares it against the required CII to determine the rating. This enables operators to assess whether a planned voyage will contribute positively or negatively to their annual CII rating.

Attained CII

Attained Carbon Intensity Indicator
\[\text{CII}_{\text{attained}} = \frac{\text{Total CO}_2 \; [\text{MT}] \times 10^6}{\text{Capacity} \; [\text{DWT}] \times \text{Distance} \; [\text{nm}]}\]

Unit: grams CO2 per deadweight-tonne per nautical mile [g CO2 / DWT·nm]

Where:

  • Total CO2 = Total fuel consumed [MT] \(\times\) CO2 emission factor [g CO2 / g fuel]
  • Capacity = vessel DWT (49,000 MT for MR tanker)
  • Distance = total voyage distance [nm]

Required CII

Required CII with Annual Reduction
\[\text{CII}_{\text{ref}} = a \cdot \text{Capacity}^{-c}\] \[\text{CII}_{\text{req}} = \text{CII}_{\text{ref}} \times \left(1 - \frac{Z}{100}\right)\]

Where:

  • \(a = 5247\) (tanker reference parameter, MEPC.339(76))
  • \(c = 0.610\) (tanker capacity exponent, MEPC.339(76))
  • \(Z\) = reduction factor (% per year, increasing annually)

Rating Boundaries

Rating Condition
A (Superior)Attained ≤ 0.86 × Required
B (Good)0.86 < Attained ≤ 0.94 × Required
C (Moderate)0.94 < Attained ≤ 1.06 × Required
D (Below average)1.06 < Attained ≤ 1.18 × Required
E (Inferior)Attained > 1.18 × Required

Annual Reduction Factors

Year Reduction Factor (Z)
20235%
20247%
20259%
202611%
202713%
202815%
202917%
203019%

CO2 Emission Factors

Fuel Type g CO2 / g fuel
HFO (Heavy Fuel Oil)3.114
VLSFO (Very Low Sulphur Fuel Oil)3.114
MDO (Marine Diesel Oil)3.206
LNG (Liquefied Natural Gas)2.750

Regulatory Zones

WindMar supports the definition and enforcement of regulatory zones that constrain route optimization. Zones are defined as GeoJSON polygons and classified by type, each with a specific effect on the A* cost function.

Zone Types

Zone Type Description Effect
ECA Emission Control Areas (SOx/NOx) Penalty cost (fuel switching overhead)
HRA High Risk Areas (piracy, conflict) Exclusion (route blocked)
TSS Traffic Separation Schemes Mandatory (route must include)

GeoJSON Import/Export

Zones are stored as GeoJSON Feature Collections. Each feature includes a properties object specifying the zone type, name, and any associated parameters (e.g., penalty factor).

{
  "type": "FeatureCollection",
  "features": [
    {
      "type": "Feature",
      "properties": {
        "name": "North Sea ECA",
        "zone_type": "PENALTY",
        "penalty_factor": 1.3
      },
      "geometry": {
        "type": "Polygon",
        "coordinates": [[[...], [...], ...]]
      }
    }
  ]
}

Point-in-Polygon Detection

During A* expansion, each candidate cell is tested against all active regulatory zones using the ray casting algorithm. For a point (lat, lon), the algorithm casts a horizontal ray and counts the number of intersections with the polygon boundary. An odd count means the point is inside the polygon; an even count means outside.

Weather API

The Weather API provides access to weather fields for the route optimization grid and map visualization. Wind data is sourced from NOAA GFS (primary) with ERA5 fallback. Wave and current data comes from Copernicus CMEMS. Weather fields are cached in-memory for repeated queries. See Weather Data Acquisition for full details.

Grid Endpoints

GET /api/weather/wind

Returns the wind field grid (U/V components) for a bounding box.

Query Parameters:

  • lat_min, lat_max — Latitude bounds (default: 30–60)
  • lon_min, lon_max — Longitude bounds (default: -15–40)
  • resolution — Grid resolution in degrees (default: 0.25)
  • time — Optional ISO 8601 datetime

Response: JSON with lats, lons, u, v 2D arrays, source (gfs/era5/synthetic), ocean mask.

GET /api/weather/wind/velocity

Wind data in leaflet-velocity JSON format for particle animation.

Query Parameters: Same as /api/weather/wind, plus forecast_hour (0–120, step 3).

Source: NOAA GFS via NOMADS GRIB filter.

GET /api/weather/waves

Wave field grid (significant height, period, direction) from CMEMS.

Query Parameters: Same bounding box + time.

GET /api/weather/currents

Ocean current field grid (U/V components) from CMEMS.

Query Parameters: Same bounding box + time.

GET /api/weather/currents/velocity

Ocean currents in leaflet-velocity JSON format for particle animation.

Forecast Endpoints

GET /api/weather/forecast/status

Returns GFS run info and which forecast hours (f000–f120) are cached.

Response: run_date, run_hour, total_hours (41), cached_hours, complete flag, prefetch_running flag.

POST /api/weather/forecast/prefetch

Triggers background download of all 41 GFS forecast hours (f000–f120, 3h steps). Skips already-cached files. Rate-limited to 2s between NOMADS requests.

GET /api/weather/forecast/frames

Bulk returns all cached forecast frames in leaflet-velocity format. Single response containing all 41 frames (~5–20 MB). Frontend calls this once after prefetch completes.

Route API

The Route API handles route optimization requests, waypoint-based route calculation, RTZ file parsing, and voyage planning with ETA computation.

POST /api/optimize/route

Full weather-aware route optimization using A* pathfinding with variable speed optimization.

Request Body:

{
  "departure": {
    "lat": 51.95,
    "lon": 1.33,
    "name": "Rotterdam"
  },
  "arrival": {
    "lat": 29.75,
    "lon": -93.85,
    "name": "Houston"
  },
  "departure_time": "2026-02-10T08:00:00Z",
  "vessel_condition": "laden",
  "optimization_objective": "fuel",
  "grid_resolution": 0.5,
  "speed_range": [6.0, 18.0],
  "enable_safety_constraints": true,
  "regulatory_zones": ["eca_north_sea", "tss_dover"]
}

Response:

{
  "route": {
    "waypoints": [
      {"lat": 51.95, "lon": 1.33, "speed_kts": 14.0, "fuel_mt": 0.0},
      {"lat": 50.50, "lon": -2.10, "speed_kts": 13.5, "fuel_mt": 12.4},
      ...
    ],
    "total_distance_nm": 5120,
    "total_fuel_mt": 285.3,
    "total_time_hours": 372.5,
    "eta": "2026-02-25T20:30:00Z"
  },
  "cii": {
    "attained": 4.82,
    "required": 5.31,
    "rating": "B"
  },
  "safety_summary": {
    "max_roll_deg": 12.3,
    "max_pitch_deg": 4.1,
    "max_acceleration_g": 0.18,
    "dangerous_cells_avoided": 14,
    "marginal_cells_traversed": 7
  },
  "legs": [
    {
      "from": {"lat": 51.95, "lon": 1.33},
      "to": {"lat": 50.50, "lon": -2.10},
      "distance_nm": 156,
      "speed_kts": 13.5,
      "fuel_mt": 12.4,
      "time_hours": 11.6,
      "weather": {
        "wind_speed_ms": 8.2,
        "wind_dir": 245,
        "wave_height_m": 2.1,
        "wave_dir": 260
      }
    }
  ]
}
POST /api/routes/from-waypoints

Calculate fuel consumption and ETA for a user-defined waypoint sequence (no optimization, fixed route).

Request Body:

{
  "waypoints": [
    {"lat": 51.95, "lon": 1.33},
    {"lat": 48.50, "lon": -5.10},
    {"lat": 29.75, "lon": -93.85}
  ],
  "departure_time": "2026-02-10T08:00:00Z",
  "vessel_condition": "laden",
  "speed_kts": 14.5
}
POST /api/routes/parse-rtz

Parse an RTZ (Route Transfer Exchange) file per IEC 61174 standard and extract waypoints.

Request Body: Multipart form data with RTZ XML file.

POST /api/voyage/calculate

Full voyage calculation with intermediate port calls, bunkering stops, and ETAs for each leg.

Vessel API

The Vessel API manages vessel specifications, calibration, and compliance calculations.

GET /api/vessel/specs

Returns the current vessel specifications including dimensions, areas, engine parameters, and loading condition.

POST /api/vessel/specs

Update vessel specifications. All physical model parameters (wetted surface, wind areas, block coefficient, etc.) are recalculated from the updated dimensions.

Request Body:

{
  "loa": 183.0,
  "lpp": 176.0,
  "beam": 32.0,
  "draft_laden": 11.8,
  "draft_ballast": 6.5,
  "displacement_laden": 65000,
  "displacement_ballast": 20000,
  "dwt": 49000,
  "mcr_kw": 8840,
  "sfoc_mcr": 171,
  "service_speed_laden": 14.5,
  "service_speed_ballast": 15.0
}
POST /api/vessel/calibrate

Calibrate vessel performance model using noon report data. Accepts an Excel file with voyage data and returns optimized calibration factors.

Request Body: Multipart form data with Excel file (.xlsx).

Response:

{
  "calibration_factors": {
    "calm_water": 1.12,
    "wind": 0.95,
    "waves": 1.08
  },
  "rmse_before": 3.45,
  "rmse_after": 1.23,
  "improvement_pct": 64.3,
  "data_points": 42
}
POST /api/compliance/cii

Calculate the IMO CII rating for a completed or planned voyage.

Request Body:

{
  "total_fuel_mt": 285.3,
  "fuel_type": "VLSFO",
  "distance_nm": 5120,
  "dwt": 49000,
  "year": 2026
}

Response:

{
  "attained_cii": 4.82,
  "reference_cii": 5.97,
  "required_cii": 5.31,
  "reduction_factor": 11,
  "rating": "B",
  "boundaries": {
    "A_upper": 4.57,
    "B_upper": 4.99,
    "C_upper": 5.63,
    "D_upper": 6.27
  }
}

Weather Database Architecture

Starting with v0.0.5, WindMar pre-fetches weather grids on a 6-hourly schedule and stores them as compressed blobs in PostgreSQL. This eliminates the 30–60 second CMEMS/GFS download latency during route calculations, reducing optimization time from minutes to seconds.

Database Schema

Two tables manage the weather grid lifecycle:

TablePurposeKey Columns
weather_forecast_runs Tracks ingestion runs per source source, run_time, status, forecast_hours[]
weather_grid_data Compressed grid blobs per forecast hour run_id, forecast_hour, parameter, lats/lons/data (BYTEA)

Compression

Each grid is stored as a zlib-compressed float32 numpy array. A global 0.5° grid (341 × 721 = ~246K points) compresses to ~200–400 KB per parameter per forecast hour. Total storage per ingestion cycle: 7 parameters × 41 hours × ~300 KB ≈ 86 MB.

Provider Fallback Chain

Weather data is resolved through a multi-tier fallback chain. Each tier is tried in order; the first successful response is used:

  1. Redis shared cache — cross-worker, 15-minute TTL
  2. PostgreSQL pre-ingested grids — latest complete forecast run
  3. Live CMEMS/GFS download — direct API call (30–60s)
  4. Synthetic data — deterministic test/demo fallback

Ingestion Service

The WeatherIngestionService runs as a FastAPI background task on a 6-hour loop. It downloads global grids from CMEMS (waves, currents) and GFS (wind) and stores them compressed in PostgreSQL. Runs older than 24 hours are automatically marked as superseded.

Wave forecast — When available, the ingestion service stores multi-timestep wave forecasts (0–120h, 3-hourly = 41 steps), enabling the Monte Carlo simulator to use time-varying wave conditions along the voyage.

DbWeatherProvider

The DbWeatherProvider class reads compressed grids from PostgreSQL, decompresses them into numpy arrays, crops to the requested bounding box, and returns WeatherData objects. It selects the best available forecast hour for time-specific queries using nearest-neighbour matching against available forecast hours.

Performance

OperationBefore (live)After (DB)
Route optimization (10-leg)90–180s2–5s
Voyage calculation30–60s< 1s
Monte Carlo (100 sims)N/A< 500ms

Monte Carlo Simulation

WindMar includes a parametric Monte Carlo simulator that produces P10/P50/P90 confidence intervals for ETA, fuel consumption, and voyage time. The simulator accounts for weather forecast uncertainty by applying temporally correlated perturbations to base weather conditions along the route.

For the full mathematical framework, see the Parametric Monte Carlo Simulation technical article with academic references.

Architecture

The simulation proceeds in four stages:

  1. Route timeline — The voyage is divided into up to 100 evenly-spaced time slices (~1 slice per 1.2 hours). Each slice has a timestamp and interpolated (lat, lon) position along the rhumb-line route.
  2. Weather pre-fetch — Base weather (wind, wave, current) is loaded for all slices. Wave data comes from multi-timestep DB grids (forecast hours 0–120h), currents from a single DB snapshot, and wind from the pre-built GridWeatherProvider.
  3. Correlated perturbation — For each of N simulations, temporally correlated random factors are generated via Cholesky decomposition of an exponential correlation matrix. These factors perturb wind speed, wave height, current speed, and directions.
  4. Voyage calculation — Each simulation runs a full voyage calculation with the perturbed weather provider. Results are collected and percentiles computed.

Perturbation Model

ParameterDistributionσCorrelation
Wind speedLog-normal, E[f]=10.35Temporal (Cholesky)
Wave heightLog-normal, E[f]=10.2070% with wind + 30% independent
Current speedLog-normal, E[f]=10.15Independent temporal
DirectionsGaussian offset15°Temporal (Cholesky)

Temporal Correlation

Exponential Correlation Matrix
\[\text{cov}(i, j) = \exp\!\left(-\frac{|t_i - t_j|}{\tau}\right)\]

where \(t \in [0, 1]\) is normalised time along the voyage, \(\tau = 0.3\) (correlation length as fraction of voyage duration)

Cholesky decomposition:

\[L \cdot L^T = \text{cov}\]

Correlated normals:

\[\mathbf{z} = L \cdot \boldsymbol{\varepsilon}, \quad \boldsymbol{\varepsilon} \sim \mathcal{N}(\mathbf{0}, I)\]

The exponential kernel ensures that weather perturbations at nearby time slices are strongly correlated (a storm at hour 12 implies bad weather at hour 15), while distant slices are nearly independent. The correlation length τ = 0.3 corresponds to ~1.5 days for a 5-day voyage.

Wind–Wave Coupling

Wave height perturbations are 70% correlated with wind perturbations, reflecting the physical relationship between wind forcing and sea state:

Wind–Wave Coupling
\[z_{\text{wave}} = 0.7 \cdot z_{\text{wind}} + \sqrt{1 - 0.7^2} \cdot z_{\text{wave,indep}}\] \[f_{\text{wave}} = \exp\!\left(\sigma_{\text{wave}} \cdot z_{\text{wave}} - 0.5 \cdot \sigma_{\text{wave}}^2\right)\]

API Usage

POST /api/voyage/monte-carlo
{
  "waypoints": [
    {"lat": 51.95, "lon": 4.05, "name": "Rotterdam"},
    {"lat": 37.23, "lon": 15.22, "name": "Augusta"}
  ],
  "calm_speed_kts": 14.5,
  "is_laden": true,
  "departure_time": "2026-02-10T06:00:00Z",
  "n_simulations": 100
}

Response:
{
  "n_simulations": 100,
  "eta_p10": "2026-02-14T13:22:00Z",
  "eta_p50": "2026-02-14T14:55:00Z",
  "eta_p90": "2026-02-14T16:28:00Z",
  "fuel_p10": 285.4,
  "fuel_p50": 302.1,
  "fuel_p90": 321.8,
  "time_p10": 103.4,
  "time_p50": 104.9,
  "time_p90": 106.5,
  "computation_time_ms": 399.2
}

References

  • Dickson, T., Farr, H., Sear, D., and Blake, J. (2019). “Uncertainty in marine weather routing.” arXiv:1901.03840
  • Aijjou, A.E. et al. (2021). “A Comprehensive Approach to Account for Weather Uncertainties in Ship Route Optimization.” J. Mar. Sci. Eng. 9(12):1434

Model Validation

WindMar includes a comprehensive test suite that validates physical models against expected behaviors, empirical relationships, and boundary conditions. Tests are structured to verify both individual model components and the integrated optimization pipeline.

Unit Tests

The following physical constraints are validated:

  • Resistance increases with speed — Total calm water resistance at 16 kts exceeds resistance at 12 kts for both laden and ballast conditions
  • Laden uses more fuel than ballast — At the same speed, the laden condition produces higher calm water resistance due to greater displacement and wetted surface
  • Weather increases fuel consumption — Total fuel with wind and waves always exceeds calm water fuel for the same speed and distance
  • SFOC optimal at 75-85% load — SFOC at 80% load is lower than SFOC at 50% load and lower than SFOC at 100% load
  • Head wind worse than following wind — Wind resistance at 0 degrees (head wind) exceeds wind resistance at 180 degrees (following wind)
  • A* finds path avoiding land — No waypoint in the optimized path falls on a land cell as classified by global-land-mask
  • Safety constraints block dangerous routes — When dangerous conditions exist on the direct path, the optimizer produces a detour that avoids them
  • Zone exclusions force detours — An exclusion zone on the direct path forces the optimizer to route around it, increasing total distance

Integration Tests

  • Full optimization flow — End-to-end test from route request through weather fetch, A* search, speed optimization, and CII calculation
  • API endpoint validation — All REST endpoints return correct HTTP status codes, response schemas, and handle invalid input gracefully
  • Weather provider fallback — Synthetic weather provider produces consistent results when Copernicus credentials are not available

Physical Constants

Constant Symbol Value Unit
Seawater densityρ_sw1025kg/m³
Seawater kinematic viscosityν_sw1.19 × 10-6m²/s
Air densityρ_air1.225kg/m³
Gravitational accelerationg9.81m/s²
Knot to m/s conversion0.5144m/s per knot
Validation Status
All physical models produce results consistent with empirical data for MR product tankers. The Holtrop-Mennen calm water resistance has been validated against published resistance data for vessels of similar dimensions and hull form. SFOC curves match manufacturer specifications for two-stroke marine diesel engines in the 8,000-10,000 kW range. Seakeeping predictions show correct trends for roll, pitch, and acceleration as functions of wave height and encounter angle.

Calibration

Calibration adjusts the physics model parameters to match observed vessel performance from noon report data. This accounts for hull fouling, propeller degradation, and other vessel-specific factors that cause the default model to deviate from actual fuel consumption.

Noon Report Data Format

Calibration accepts an Excel file (.xlsx) with the following columns. Each row represents one noon-to-noon reporting period (approximately 24 hours).

Column Unit Description
SpeedknotsAverage speed over ground
DistancenmDistance traveled in reporting period
FuelMTTotal fuel consumed in reporting period
WindBeaufortAverage wind force (Beaufort scale 0-12)
WavesmAverage significant wave height
DraftmMean draft (used to determine laden/ballast)
Temperature°CSeawater temperature (for viscosity correction)

Optimization Method

Calibration uses scipy.optimize.minimize with the Nelder-Mead simplex algorithm to find the calibration factors that minimize the root mean square error (RMSE) between predicted and actual fuel consumption across all noon report entries.

Calibration Objective Function

Objective: minimize RMSE

\[\text{RMSE} = \sqrt{\frac{1}{N} \sum_{i=1}^{N} \bigl(\text{fuel}_{\text{predicted},i} - \text{fuel}_{\text{actual},i}\bigr)^2}\]

Predicted fuel for each reporting period:

\[R_{\text{total}} = k_{\text{calm}} \cdot R_{\text{calm}} + k_{\text{wind}} \cdot R_{\text{wind}} + k_{\text{wave}} \cdot R_{\text{wave}}\] \[\text{fuel}_{\text{predicted}} = f(R_{\text{total}},\; V,\; d,\; \text{SFOC})\]

Calibration factors (decision variables):

  • \(k_{\text{calm}} \in [0.5, 2.0]\) (calm water resistance multiplier)
  • \(k_{\text{wind}} \in [0.5, 2.0]\) (wind resistance multiplier)
  • \(k_{\text{wave}} \in [0.5, 2.0]\) (wave resistance multiplier)

Initial values: \(k_{\text{calm}} = k_{\text{wind}} = k_{\text{wave}} = 1.0\)

Method: Nelder-Mead (derivative-free simplex)

Interpretation of Calibration Factors

  • k_calm > 1.0 — Hull roughness or fouling increasing skin friction beyond the clean-hull model prediction
  • k_calm < 1.0 — Hull is cleaner than the model assumes (e.g., recent dry-docking)
  • k_wind > 1.0 — Above-water structure presents more windage than the default areas suggest
  • k_wave > 1.0 — Vessel is more sensitive to waves than the Kwon formula predicts (e.g., bluff bow form)

Factors are bounded to [0.5, 2.0] to prevent physically unreasonable corrections. A factor outside this range indicates either bad input data or a fundamental mismatch between the vessel and the default model parameters, requiring manual review of the vessel specifications.

Technical Articles

The following peer-reviewed-style articles provide in-depth coverage of WindMar's core subsystems. Each article presents the mathematical foundations, implementation details, and relevant references for a specific domain of the weather routing pipeline.

Weather Data Fields

  • Meteorological and oceanographic parameters: wind (GFS), waves (CMEMS), currents, and ocean masking used in vessel performance prediction.
Read article

Data Ingestion & Restitution

  • Pipeline architecture from acquisition through compressed PostgreSQL storage to trilinear-interpolated, route-ready weather grids.
Read article

Hydrodynamics & RAO

  • Holtrop-Mennen calm-water resistance, STAWAVE-1 added wave resistance, simplified RAO seakeeping, and noon-report calibration.
Read article

A* Pathfinding

  • Adaptation of A* graph search to minimum-fuel ocean routing with physics-based cost evaluation and time-varying weather integration.
Read article

Weather Data Acquisition

  • GFS and ERA5 wind data acquisition, CMEMS wave and current forecasts, and the unified forecast-climatology blending framework.
Read article

Monte Carlo Simulation

  • Parametric Monte Carlo simulation with temporal weather correlation for voyage uncertainty quantification of fuel and ETA.
Read article