gomd 2.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
gomd/maritime.py ADDED
@@ -0,0 +1,952 @@
1
+ """
2
+ gomd.maritime — Maritime Decarbonization, IMO CII, EU ETS / FuelEU Maritime,
3
+ Poseidon Principles Ship Finance Alignment, and Paris MoU PSC Risk Models
4
+ ============================================================================
5
+ Implements regulatory and econometric formulas for international shipping:
6
+ 1. IMO Carbon Intensity Indicator (CII) Attained AER, Required CII, and A-E Rating
7
+ (Resolutions MEPC.336(76), MEPC.337(76), MEPC.338(76), MEPC.354(78))
8
+ 2. EU ETS Maritime Directive (2023/959) & FuelEU Maritime (2023/1805) Carbon Cost
9
+ 3. Poseidon Principles Climate Alignment Delta (Delta_PP %) & Green Loan Pricing
10
+ 4. Paris MoU / Med MoU New Inspection Regime (NIR) Ship Risk Profile & Detention Probability
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ import math
16
+ from typing import Any, Dict, Optional, Union
17
+
18
+ # Official IMO MEPC.364(79) / EEDI Carbon Conversion Factors C_F (t CO2 / t fuel)
19
+ IMO_CF_FACTORS: Dict[str, float] = {
20
+ "hfo": 3.114,
21
+ "vlsfo": 3.151,
22
+ "lfo": 3.151,
23
+ "mdo": 3.206,
24
+ "mgo": 3.206,
25
+ "lng": 2.750,
26
+ "lpg_propane": 3.000,
27
+ "lpg_butane": 3.030,
28
+ "methanol": 1.375,
29
+ "ethanol": 1.913,
30
+ "bio_methanol": 0.275,
31
+ "e_ammonia": 0.000,
32
+ "e_hydrogen": 0.000,
33
+ }
34
+
35
+ # IMO MEPC.337(76) Table 1 Reference Line Parameters (a, c) & MEPC.354(78) dd vectors (d1..d4)
36
+ IMO_CII_SHIP_PARAMS: Dict[str, Dict[str, Any]] = {
37
+ "bulk_carrier": {"a": 4745.0, "c": 0.622, "dd": (0.86, 0.94, 1.06, 1.18)},
38
+ "tanker": {"a": 5247.0, "c": 0.610, "dd": (0.82, 0.93, 1.08, 1.28)},
39
+ "container": {"a": 1984.0, "c": 0.489, "dd": (0.83, 0.94, 1.07, 1.19)},
40
+ "general_cargo": {"a": 31948.0, "c": 0.792, "dd": (0.83, 0.94, 1.06, 1.19)},
41
+ "lng_carrier": {"a": 14479E7, "c": 2.673, "dd": (0.89, 0.98, 1.06, 1.13)},
42
+ "ro_ro_cargo": {"a": 5739.0, "c": 0.631, "dd": (0.86, 0.94, 1.06, 1.16)},
43
+ "cruise_passenger": {"a": 930.0, "c": 0.383, "dd": (0.87, 0.95, 1.06, 1.16)},
44
+ }
45
+
46
+ # IMO MEPC.338(76) Annual Reduction Factors Z (%) relative to 2019 reference line
47
+ IMO_CII_REDUCTION_Z: Dict[int, float] = {
48
+ 2019: 0.0,
49
+ 2020: 1.0,
50
+ 2021: 2.0,
51
+ 2022: 3.0,
52
+ 2023: 5.0,
53
+ 2024: 7.0,
54
+ 2025: 9.0,
55
+ 2026: 11.0,
56
+ 2027: 13.5,
57
+ 2028: 16.0,
58
+ 2029: 18.5,
59
+ 2030: 21.5,
60
+ }
61
+
62
+
63
+ def calculate_imo_cii(
64
+ fuel_consumption_tonnes: Union[float, Dict[str, float]],
65
+ dwt_or_gt: float,
66
+ distance_nm: float,
67
+ ship_type: str = "bulk_carrier",
68
+ fuel_type: str = "vlsfo",
69
+ year: int = 2026,
70
+ ) -> Dict[str, Any]:
71
+ """
72
+ Compute the IMO Attained Annual Efficiency Ratio (AER), Required CII,
73
+ and A/B/C/D/E Operational Carbon Intensity Rating (MEPC.336(76)–MEPC.354(78)).
74
+
75
+ Parameters
76
+ ----------
77
+ fuel_consumption_tonnes : float or dict[str, float]
78
+ Either total fuel mass in metric tonnes (using `fuel_type`) or a dictionary
79
+ mapping fuel names to metric tonnes, e.g. `{"vlsfo": 4200, "mdo": 350}`.
80
+ dwt_or_gt : float
81
+ Vessel Deadweight Tonnage (DWT) or Gross Tonnage (for cruise/ro-ro passenger).
82
+ distance_nm : float
83
+ Total distance sailed in nautical miles over the reporting period.
84
+ ship_type : str, default "bulk_carrier"
85
+ One of `'bulk_carrier'`, `'tanker'`, `'container'`, `'general_cargo'`,
86
+ `'lng_carrier'`, `'ro_ro_cargo'`, `'cruise_passenger'`.
87
+ fuel_type : str, default "vlsfo"
88
+ Fuel type when `fuel_consumption_tonnes` is a single float.
89
+ year : int, default 2026
90
+ Reporting calendar year (2019–2030).
91
+ """
92
+ if isinstance(fuel_consumption_tonnes, dict):
93
+ co2_tonnes = 0.0
94
+ for f_name, f_mass in fuel_consumption_tonnes.items():
95
+ cf = IMO_CF_FACTORS.get(f_name.strip().lower(), 3.151)
96
+ co2_tonnes += float(f_mass) * cf
97
+ else:
98
+ cf = IMO_CF_FACTORS.get(fuel_type.strip().lower(), 3.151)
99
+ co2_tonnes = float(fuel_consumption_tonnes) * cf
100
+
101
+ capacity = max(float(dwt_or_gt), 1.0)
102
+ dist = max(float(distance_nm), 1.0)
103
+
104
+ # Attained AER in g CO2 / (dwt * nm)
105
+ attained_aer = (co2_tonnes * 1e6) / (capacity * dist)
106
+
107
+ st_key = ship_type.strip().lower()
108
+ params = IMO_CII_SHIP_PARAMS.get(st_key, IMO_CII_SHIP_PARAMS["bulk_carrier"])
109
+ # Cap DWT for bulk carrier >= 279,000 per MEPC.337(76)
110
+ ref_cap = min(capacity, 279000.0) if st_key == "bulk_carrier" else capacity
111
+ cii_ref_2019 = params["a"] * (ref_cap ** (-params["c"]))
112
+
113
+ z_pct = IMO_CII_REDUCTION_Z.get(int(year), 11.0 if year <= 2026 else 21.5)
114
+ required_cii = cii_ref_2019 * (1.0 - z_pct / 100.0)
115
+
116
+ d1, d2, d3, d4 = params["dd"]
117
+ ratio = attained_aer / required_cii
118
+ if ratio <= d1:
119
+ rating = "A"
120
+ elif ratio <= d2:
121
+ rating = "B"
122
+ elif ratio <= d3:
123
+ rating = "C"
124
+ elif ratio <= d4:
125
+ rating = "D"
126
+ else:
127
+ rating = "E"
128
+
129
+ boundaries = {
130
+ "A_max": round(required_cii * d1, 4),
131
+ "B_max": round(required_cii * d2, 4),
132
+ "C_max": round(required_cii * d3, 4),
133
+ "D_max": round(required_cii * d4, 4),
134
+ }
135
+ return {
136
+ "ship_type": st_key,
137
+ "year": int(year),
138
+ "total_co2_tonnes": round(co2_tonnes, 2),
139
+ "attained_aer_g_dwt_nm": round(attained_aer, 4),
140
+ "attained_aer_g_co2_dwt_nm": round(attained_aer, 4),
141
+ "cii_ref_2019_g_dwt_nm": round(cii_ref_2019, 4),
142
+ "reduction_factor_z_pct": z_pct,
143
+ "required_cii_g_dwt_nm": round(required_cii, 4),
144
+ "attained_to_required_ratio": round(ratio, 4),
145
+ "cii_rating": rating,
146
+ "boundaries_g_dwt_nm": boundaries,
147
+ "rating_boundaries": boundaries,
148
+ "regulatory_action_required": rating in ("D", "E"),
149
+ }
150
+
151
+
152
+ def calculate_eu_ets_exposure(
153
+ co2_tonnes: float,
154
+ ch4_tonnes: float = 0.0,
155
+ n2o_tonnes: float = 0.0,
156
+ voyage_scope: str = "intra_eu",
157
+ year: int = 2026,
158
+ eua_price_eur: float = 75.0,
159
+ ) -> Dict[str, Any]:
160
+ """
161
+ Calculate maritime GHG emissions liability and EU Allowance (EUA) cost under
162
+ Directive (EU) 2023/959 (EU ETS extension to maritime transport).
163
+
164
+ Includes:
165
+ - IPCC AR5 GWP_100 conversion for Methane (CH4 = 28) and Nitrous Oxide (N2O = 265)
166
+ mandatory from 2026 onwards
167
+ - Geographical scope multiplier: 100% for intra-EU / EU port berth, 50% for extra-EU
168
+ - Phase-in surrender percentage: 40% (2024), 70% (2025), 100% (2026+)
169
+ """
170
+ scope_mult = 0.50 if voyage_scope.strip().lower() in ("extra_eu", "eu_extra", "50%", "half") else 1.00
171
+ phase_in = 0.40 if year == 2024 else (0.70 if year == 2025 else 1.00)
172
+
173
+ co2e_total = float(co2_tonnes) + (28.0 * float(ch4_tonnes) + 265.0 * float(n2o_tonnes) if year >= 2026 else 0.0)
174
+ scoped_co2e = co2e_total * scope_mult
175
+ surrender_euas = scoped_co2e * phase_in
176
+ cost_eur = surrender_euas * float(eua_price_eur)
177
+
178
+ return {
179
+ "year": int(year),
180
+ "voyage_scope": voyage_scope,
181
+ "scope_multiplier": scope_mult,
182
+ "phase_in_rate": phase_in,
183
+ "total_co2e_tonnes": round(co2e_total, 2),
184
+ "scoped_co2e_tonnes": round(scoped_co2e, 2),
185
+ "surrender_euas_required": round(surrender_euas, 2),
186
+ "surrender_allowances_eua": round(surrender_euas, 2),
187
+ "eua_price_eur": round(float(eua_price_eur), 2),
188
+ "estimated_ets_cost_eur": round(cost_eur, 2),
189
+ "estimated_cost_eur": round(cost_eur, 2),
190
+ }
191
+
192
+
193
+ def poseidon_principles_alignment(
194
+ attained_aer: float,
195
+ required_trajectory_aer: float,
196
+ loan_outstanding_usd: float = 25_000_000.0,
197
+ base_margin_bps: float = 225.0,
198
+ ) -> Dict[str, Any]:
199
+ """
200
+ Evaluate vessel or portfolio climate alignment under the Poseidon Principles
201
+ Technical Guidance and simulate Sustainability-Linked Loan (SLL) margin ratchets.
202
+
203
+ Formula:
204
+ Delta_PP (%) = ((AER_attained - AER_trajectory) / AER_trajectory) * 100
205
+ """
206
+ att = float(attained_aer)
207
+ traj = max(float(required_trajectory_aer), 1e-6)
208
+ delta_pct = ((att - traj) / traj) * 100.0
209
+ aligned = delta_pct <= 0.0
210
+
211
+ # Green Loan / SLL interest rate ratchet (-30 bps discount to +25 bps penalty)
212
+ if delta_pct <= -10.0:
213
+ bps_adj = -30.0
214
+ elif delta_pct <= 0.0:
215
+ bps_adj = -15.0
216
+ elif delta_pct <= 10.0:
217
+ bps_adj = 10.0
218
+ else:
219
+ bps_adj = 25.0
220
+
221
+ annual_interest_delta_usd = float(loan_outstanding_usd) * (bps_adj / 10000.0)
222
+
223
+ return {
224
+ "attained_aer": round(att, 4),
225
+ "trajectory_aer": round(traj, 4),
226
+ "alignment_delta_pct": round(delta_pct, 2),
227
+ "poseidon_aligned": aligned,
228
+ "sll_margin_adjustment_bps": bps_adj,
229
+ "effective_margin_bps": round(float(base_margin_bps) + bps_adj, 2),
230
+ "annual_interest_saving_or_penalty_usd": round(annual_interest_delta_usd, 2),
231
+ }
232
+
233
+
234
+ def psc_detention_risk_score(
235
+ ship_age_years: float,
236
+ flag_tier: str = "white",
237
+ ro_performance: str = "high",
238
+ deficiencies_36m: int = 0,
239
+ detentions_36m: int = 0,
240
+ ship_type: str = "bulk_carrier",
241
+ ) -> Dict[str, Any]:
242
+ """
243
+ Compute Paris MoU / Mediterranean MoU New Inspection Regime (NIR) Ship Risk
244
+ Profile points, classification (HRS / SRS / LRS), and logistic detention probability.
245
+ """
246
+ points = 0
247
+ st = ship_type.strip().lower()
248
+ if st in ("bulk_carrier", "tanker", "chemical_tanker", "gas_carrier", "passenger", "cruise_passenger"):
249
+ points += 2
250
+ if float(ship_age_years) > 12.0:
251
+ points += 1
252
+
253
+ ft = flag_tier.strip().lower()
254
+ if ft == "black":
255
+ points += 2
256
+ elif ft == "grey":
257
+ points += 1
258
+
259
+ ro = ro_performance.strip().lower()
260
+ if ro in ("low", "very_low"):
261
+ points += 1
262
+
263
+ if int(detentions_36m) >= 2:
264
+ points += 2
265
+ elif int(detentions_36m) == 1:
266
+ points += 1
267
+
268
+ if int(deficiencies_36m) > 15:
269
+ points += 2
270
+ elif int(deficiencies_36m) > 5:
271
+ points += 1
272
+
273
+ if points >= 5:
274
+ profile = "HRS (High Risk Ship — Priority Inspection Window: 5-6 months)"
275
+ elif points == 0 and ft == "white" and ro == "high" and int(detentions_36m) == 0 and int(deficiencies_36m) <= 5:
276
+ profile = "LRS (Low Risk Ship — Extended Window: 24-36 months)"
277
+ else:
278
+ profile = "SRS (Standard Risk Ship — Window: 10-12 months)"
279
+
280
+ # Calibrated logistic detention probability
281
+ logit = -3.45 + 0.38 * points + 0.045 * max(float(ship_age_years) - 10.0, 0.0) + 0.12 * int(deficiencies_36m)
282
+ prob = 1.0 / (1.0 + math.exp(-logit))
283
+
284
+ return {
285
+ "nir_risk_points": points,
286
+ "ship_risk_profile": profile,
287
+ "estimated_detention_probability": round(prob, 4),
288
+ "estimated_detention_risk_pct": round(prob * 100.0, 2),
289
+ }
290
+
291
+
292
+ # Regulation (EU) 2023/1805 FuelEU Maritime default Well-to-Wake (WtW) GHG intensities (gCO2e/MJ)
293
+ # and Lower Calorific Values (LCV, MJ/g)
294
+ FUELEU_WTW_PROFILES: Dict[str, Dict[str, Any]] = {
295
+ "HFO": {"lcv_mj_per_g": 0.0405, "wtw_gco2e_mj": 91.74, "is_rfnbo": False},
296
+ "VLSFO": {"lcv_mj_per_g": 0.0410, "wtw_gco2e_mj": 91.16, "is_rfnbo": False},
297
+ "MGO": {"lcv_mj_per_g": 0.0427, "wtw_gco2e_mj": 90.77, "is_rfnbo": False},
298
+ "LNG_OTTO": {"lcv_mj_per_g": 0.0491, "wtw_gco2e_mj": 76.20, "is_rfnbo": False},
299
+ "LNG_DIESEL": {"lcv_mj_per_g": 0.0491, "wtw_gco2e_mj": 69.50, "is_rfnbo": False},
300
+ "BIO_LNG": {"lcv_mj_per_g": 0.0491, "wtw_gco2e_mj": 24.50, "is_rfnbo": False},
301
+ "BIO_METHANOL": {"lcv_mj_per_g": 0.0199, "wtw_gco2e_mj": 14.80, "is_rfnbo": False},
302
+ "E_METHANOL": {"lcv_mj_per_g": 0.0199, "wtw_gco2e_mj": 6.40, "is_rfnbo": True},
303
+ "E_AMMONIA": {"lcv_mj_per_g": 0.0186, "wtw_gco2e_mj": 3.20, "is_rfnbo": True},
304
+ "E_HYDROGEN": {"lcv_mj_per_g": 0.1200, "wtw_gco2e_mj": 2.10, "is_rfnbo": True},
305
+ }
306
+
307
+
308
+ def calculate_fueleu_maritime(
309
+ fuel_mix_tonnes: Dict[str, float],
310
+ year: int = 2025,
311
+ voyage_scope_share: float = 1.0,
312
+ ops_shore_power_mj: float = 0.0,
313
+ **kwargs: Any,
314
+ ) -> Dict[str, Any]:
315
+ """
316
+ Calculate Well-to-Wake (WtW) GHG Intensity (gCO2e/MJ), RFNBO 2x Multiplier Reward,
317
+ Compliance Balance (tCO2e), and Financial Penalty (EUR) under Regulation (EU) 2023/1805
318
+ (FuelEU Maritime, 2025-2050).
319
+
320
+ Post-2020 Regulatory & Scientific Reference:
321
+ - Regulation (EU) 2023/1805 of the European Parliament and of the Council of
322
+ 13 September 2023 on the use of renewable and low-carbon fuels in maritime
323
+ transport (FuelEU Maritime).
324
+ - Baseline 2020 fleet WtW intensity: 91.16 gCO2e/MJ.
325
+ - Step-down reductions: 2025 (-2%), 2030 (-6%), 2035 (-14.5%), 2040 (-31%),
326
+ 2045 (-62%), 2050 (-80%).
327
+ - Financial Penalty for deficit: EUR 2,400 per metric tonne of VLSFO-equivalent
328
+ energy (41,000 MJ/t).
329
+ """
330
+ baseline_2020 = 91.16
331
+ yr = int(year)
332
+ if yr < 2025:
333
+ red_pct = 0.0
334
+ elif yr < 2030:
335
+ red_pct = 2.0
336
+ elif yr < 2035:
337
+ red_pct = 6.0
338
+ elif yr < 2040:
339
+ red_pct = 14.5
340
+ elif yr < 2045:
341
+ red_pct = 31.0
342
+ elif yr < 2050:
343
+ red_pct = 62.0
344
+ else:
345
+ red_pct = 80.0
346
+
347
+ target_wtw = baseline_2020 * (1.0 - red_pct / 100.0)
348
+ scope = min(max(float(voyage_scope_share), 0.0), 1.0)
349
+
350
+ total_energy_mj = max(float(ops_shore_power_mj), 0.0) * scope
351
+ weighted_energy_denom_mj = max(float(ops_shore_power_mj), 0.0) * scope
352
+ total_wtw_grams = 0.0
353
+
354
+ for f_name, tonnes in fuel_mix_tonnes.items():
355
+ key = f_name.strip().upper()
356
+ prof = FUELEU_WTW_PROFILES.get(key, FUELEU_WTW_PROFILES["VLSFO"])
357
+ mass_g = max(float(tonnes), 0.0) * 1e6 * scope
358
+ e_mj = mass_g * float(prof["lcv_mj_per_g"])
359
+ total_energy_mj += e_mj
360
+ # RFNBO 2x reward multiplier in denominator between 2025 and 2033 per Annex I
361
+ rfnbo_mult = 2.0 if (prof["is_rfnbo"] and 2025 <= yr <= 2033) else 1.0
362
+ weighted_energy_denom_mj += e_mj * rfnbo_mult
363
+ total_wtw_grams += e_mj * float(prof["wtw_gco2e_mj"])
364
+
365
+ if weighted_energy_denom_mj <= 0.0:
366
+ raise ValueError("Total energy in fuel_mix_tonnes must be positive.")
367
+
368
+ attained_wtw = total_wtw_grams / weighted_energy_denom_mj
369
+ # Compliance Balance in grams CO2e: (GHGIE_target - GHGIE_actual) * Energy_MJ
370
+ cb_grams = (target_wtw - attained_wtw) * total_energy_mj
371
+ cb_tco2e = cb_grams / 1e6
372
+
373
+ # If Compliance Balance < 0, Penalty = |CB_grams| / (attained_wtw * 41,000) * 2,400 EUR
374
+ penalty_eur = 0.0
375
+ vlsfo_eq_deficit_tonnes = 0.0
376
+ if cb_grams < 0.0 and attained_wtw > 0.0:
377
+ vlsfo_eq_deficit_tonnes = abs(cb_grams) / (attained_wtw * 41000.0)
378
+ penalty_eur = vlsfo_eq_deficit_tonnes * 2400.0
379
+
380
+ status_str = "SURPLUS (Bankable / Poolable)" if cb_tco2e >= 0.0 else "DEFICIT (Penalty / Pooling Required)"
381
+ return {
382
+ "year": yr,
383
+ "baseline_2020_gco2e_mj": baseline_2020,
384
+ "reduction_target_pct": red_pct,
385
+ "target_wtw_gco2e_mj": round(target_wtw, 4),
386
+ "attained_wtw_gco2e_mj": round(attained_wtw, 4),
387
+ "attained_wtw_ghg_intensity_gco2e_mj": round(attained_wtw, 4),
388
+ "total_covered_energy_gj": round(total_energy_mj / 1000.0, 2),
389
+ "compliance_balance_tco2e": round(cb_tco2e, 2),
390
+ "status": status_str,
391
+ "compliance_status": status_str,
392
+ "vlsfo_equivalent_deficit_tonnes": round(vlsfo_eq_deficit_tonnes, 2),
393
+ "fueleu_penalty_eur": round(penalty_eur, 2),
394
+ "reference": "Regulation (EU) 2023/1805 (FuelEU Maritime Annex I & IV)",
395
+ }
396
+
397
+
398
+ def imo_2023_strive_trajectory(
399
+ baseline_2008_aer: float,
400
+ current_aer: float,
401
+ annual_emissions_tco2e: float,
402
+ annual_efficiency_gain_pct: float = 1.8,
403
+ current_year: int = 2026,
404
+ shadow_carbon_levy_usd: float = 150.0,
405
+ ) -> Dict[str, Any]:
406
+ """
407
+ Evaluate Vessel Alignment against the IMO 2023 Revised GHG Strategy (Resolution
408
+ MEPC.377(80), July 2023) Indicative Checkpoints (2030 & 2040 Base vs. Striving),
409
+ identify the Stranded-Asset Year, and compute Maritime Carbon Value-at-Risk (CVaR).
410
+
411
+ Post-2020 Regulatory & Scientific Reference:
412
+ - IMO Resolution MEPC.377(80) (Adopted 7 July 2023): 2023 IMO Strategy on
413
+ Reduction of GHG Emissions from Ships.
414
+ - Checkpoints:
415
+ * 2030: >=40% carbon intensity reduction (vs 2008); 20% striving for 30% total GHG reduction.
416
+ * 2040: 70% striving for 80% total GHG reduction.
417
+ * 2050: Net-zero GHG emissions.
418
+ """
419
+ b08 = max(float(baseline_2008_aer), 1e-4)
420
+ cur = max(float(current_aer), 1e-4)
421
+ g_rate = float(annual_efficiency_gain_pct) / 100.0
422
+
423
+ # IMO MEPC.377(80) trajectory checkpoints relative to 2008 baseline
424
+ checkpoints = {
425
+ 2030: {"base_aer": round(b08 * 0.60, 4), "strive_aer": round(b08 * 0.50, 4)},
426
+ 2040: {"base_aer": round(b08 * 0.30, 4), "strive_aer": round(b08 * 0.20, 4)},
427
+ 2050: {"base_aer": 0.0, "strive_aer": 0.0},
428
+ }
429
+
430
+ # Find stranded asset year where vessel's projected AER exceeds linear striving corridor
431
+ stranded_year = None
432
+ for yr in range(int(current_year), 2051):
433
+ years_ahead = yr - int(current_year)
434
+ proj_vessel_aer = cur * ((1.0 - g_rate) ** years_ahead)
435
+ # Linear interpolation of striving trajectory between 2026 (0.65*b08), 2030 (0.50*b08), 2040 (0.20*b08), 2050 (0.0)
436
+ if yr <= 2030:
437
+ allowed = b08 * (0.65 - 0.15 * ((yr - 2025) / 5.0))
438
+ elif yr <= 2040:
439
+ allowed = b08 * (0.50 - 0.30 * ((yr - 2030) / 10.0))
440
+ else:
441
+ allowed = b08 * max(0.0, 0.20 - 0.20 * ((yr - 2040) / 10.0))
442
+ if proj_vessel_aer > allowed and stranded_year is None:
443
+ stranded_year = yr
444
+
445
+ # 10-year discounted Carbon Value-at-Risk under universal maritime GHG levy
446
+ em = max(float(annual_emissions_tco2e), 0.0)
447
+ levy = max(float(shadow_carbon_levy_usd), 0.0)
448
+ cvar_10yr_usd = sum(
449
+ (em * ((1.0 - g_rate) ** t) * levy) / ((1.04) ** t) for t in range(1, 11)
450
+ )
451
+
452
+ return {
453
+ "baseline_2008_aer": round(b08, 4),
454
+ "current_aer": round(cur, 4),
455
+ "reduction_vs_2008_pct": round((1.0 - cur / b08) * 100.0, 2),
456
+ "mepc80_checkpoints": checkpoints,
457
+ "stranded_asset_year_strive": stranded_year if stranded_year is not None else 2050,
458
+ "maritime_carbon_var_10yr_usd": round(cvar_10yr_usd, 2),
459
+ "reference": "IMO Resolution MEPC.377(80) (July 2023 Revised GHG Strategy)",
460
+ }
461
+
462
+
463
+ def ais_speed_power_emissions(
464
+ mcr_kw: float,
465
+ design_speed_kn: float,
466
+ observed_speed_kn: float,
467
+ design_draft_m: float = 12.0,
468
+ observed_draft_m: float = 11.0,
469
+ voyage_hours: float = 240.0,
470
+ aux_power_kw: float = 650.0,
471
+ weather_fouling_margin: float = 1.12,
472
+ sfoc_g_kwh: float = 175.0,
473
+ cf_tco2_per_tfuel: float = 3.151,
474
+ ) -> Dict[str, Any]:
475
+ """
476
+ Bottom-Up AIS Admiralty-Cube Propulsion Power, Fuel Consumption, Emissions &
477
+ Slow-Steaming Elasticity Model (IMO 4th GHG Study 2020 & STEAM3 Framework).
478
+
479
+ Formula:
480
+ P_main = 0.85 * MCR * (Draft_obs / Draft_des)^(2/3) * (V_obs / V_des)^3 * w_margin
481
+ """
482
+ v_des = max(float(design_speed_kn), 1.0)
483
+ v_obs = max(float(observed_speed_kn), 0.5)
484
+ d_ratio = (max(float(observed_draft_m), 1.0) / max(float(design_draft_m), 1.0)) ** (2.0 / 3.0)
485
+ v_ratio = (v_obs / v_des) ** 3.0
486
+
487
+ p_main_kw = 0.85 * float(mcr_kw) * d_ratio * v_ratio * float(weather_fouling_margin)
488
+ p_total_kw = p_main_kw + float(aux_power_kw)
489
+ hrs = max(float(voyage_hours), 0.1)
490
+ dist_nm = v_obs * hrs
491
+
492
+ fuel_tonnes = (p_total_kw * hrs * float(sfoc_g_kwh)) / 1e6
493
+ co2_tonnes = fuel_tonnes * float(cf_tco2_per_tfuel)
494
+
495
+ # Slow-steaming counterfactual: 10% speed reduction over same distance (time increases by 1/0.9)
496
+ v_slow = v_obs * 0.90
497
+ hrs_slow = dist_nm / v_slow
498
+ p_main_slow = p_main_kw * (0.90 ** 3.0)
499
+ fuel_slow = ((p_main_slow + float(aux_power_kw)) * hrs_slow * float(sfoc_g_kwh)) / 1e6
500
+ co2_saved_tonnes = max(co2_tonnes - fuel_slow * float(cf_tco2_per_tfuel), 0.0)
501
+ abate_pct = (co2_saved_tonnes / co2_tonnes) * 100.0 if co2_tonnes > 0 else 0.0
502
+
503
+ return {
504
+ "voyage_distance_nm": round(dist_nm, 2),
505
+ "main_engine_power_kw": round(p_main_kw, 2),
506
+ "total_power_kw": round(p_total_kw, 2),
507
+ "fuel_consumption_tonnes": round(fuel_tonnes, 3),
508
+ "co2_emissions_tonnes": round(co2_tonnes, 3),
509
+ "slow_steaming_10pct_co2_saved_tonnes": round(co2_saved_tonnes, 3),
510
+ "slow_steaming_10pct_abatement_pct": round(abate_pct, 2),
511
+ "reference": "IMO Fourth GHG Study (2020) & STEAM3 AIS Bottom-Up Model",
512
+ }
513
+
514
+
515
+ def marine_insurance_actuarial_pricing(
516
+ hull_value_usd: float = 65_000_000.0,
517
+ gross_tonnage: float = 42_000.0,
518
+ cargo_value_usd: float = 28_000_000.0,
519
+ vessel_age_years: float = 8.0,
520
+ cii_rating: str = "B",
521
+ observed_aer: float = 4.65,
522
+ required_ppmi_aer: float = 5.10,
523
+ psc_detentions_3yr: int = 0,
524
+ war_risk_chokepoint_zone: bool = False,
525
+ green_dual_fuel: bool = False,
526
+ chokepoint_transits_yr: Optional[int] = None,
527
+ **kwargs: Any,
528
+ ) -> Dict[str, Any]:
529
+ """
530
+ Actuarial Marine & Shipping Insurance Underwriting Model: Hull & Machinery (H&M),
531
+ International Group of P&I Clubs Mutual Call, Cargo CIF-FOB Risk, War-Risk Surcharge,
532
+ and Poseidon Principles for Marine Insurance (PPMI) Climate Alignment Pricing.
533
+
534
+ Post-2020 Scientific & Industry References:
535
+ - International Union of Marine Insurance (IUMI, 2024/2025). IUMI Global Marine
536
+ Insurance Report: Hull, Cargo, Offshore Energy & Protection and Indemnity.
537
+ - Poseidon Principles for Marine Insurance (PPMI, 2021/2024). A Global Framework
538
+ for Assessing and Disclosing the Climate Alignment of Marine Insurance Portfolios.
539
+ - International Group of P&I Clubs (IG P&I, 2024). Pooling Agreement & Reinsurance
540
+ General Excess of Loss (GXL) Actuarial Structure.
541
+ """
542
+ hv = max(float(hull_value_usd), 100_000.0)
543
+ gt = max(float(gross_tonnage), 500.0)
544
+ cv = max(float(cargo_value_usd), 0.0)
545
+ age = max(int(vessel_age_years), 0)
546
+ rating = str(cii_rating).strip().upper() or "C"
547
+ if chokepoint_transits_yr is not None and int(chokepoint_transits_yr) > 0:
548
+ war_risk_chokepoint_zone = True
549
+
550
+ # 1. Base Hull & Machinery (H&M) rate (~0.42% of insured hull value, IUMI global benchmark)
551
+ base_hm_rate = 0.0042
552
+ age_mult = 1.0 + max(age - 5, 0) * 0.032 + max(age - 15, 0) * 0.045
553
+ cii_mult_map = {"A": 0.90, "B": 0.95, "C": 1.00, "D": 1.12, "E": 1.28}
554
+ cii_mult = cii_mult_map.get(rating, 1.00)
555
+ psc_mult = 1.0 + min(max(int(psc_detentions_3yr), 0), 5) * 0.18
556
+
557
+ # Poseidon Principles for Marine Insurance (PPMI) climate alignment delta (%)
558
+ req_aer = max(float(required_ppmi_aer), 0.1)
559
+ obs_aer = max(float(observed_aer), 0.0)
560
+ ppmi_delta_pct = ((obs_aer - req_aer) / req_aer) * 100.0
561
+
562
+ # Green underwriting discount / surcharge (up to -12.5% discount for PPMI-aligned dual-fuel vessels)
563
+ ppmi_mult = 1.0 + max(min(ppmi_delta_pct / 100.0 * 0.25, 0.20), -0.10)
564
+ if green_dual_fuel:
565
+ ppmi_mult *= 0.94
566
+
567
+ hm_premium_usd = hv * base_hm_rate * age_mult * cii_mult * psc_mult * ppmi_mult
568
+
569
+ # 2. International Group of P&I Clubs mutual call + GXL reinsurance ($3.85/GT baseline)
570
+ base_pi_per_gt = 3.85
571
+ pi_call_usd = gt * base_pi_per_gt * (0.95 if rating in ("A", "B") else (1.18 if rating == "E" else 1.0)) * psc_mult
572
+
573
+ # 3. Marine Cargo insurance (CIF-FOB merchandise protection, ~0.16% per voyage/annualized)
574
+ cargo_premium_usd = cv * 0.0016 * (0.94 if rating in ("A", "B") else 1.05)
575
+
576
+ # 4. War Risk / High-Risk Chokepoint (Red Sea / Bab el-Mandeb / Black Sea) Additional Premium (AP)
577
+ war_risk_usd = (hv * 0.0035 + cv * 0.0012) if war_risk_chokepoint_zone else 0.0
578
+
579
+ total_premium_usd = hm_premium_usd + pi_call_usd + cargo_premium_usd + war_risk_usd
580
+ expected_loss_ratio_pct = min(max(58.5 * cii_mult * (1.0 + 0.08 * int(psc_detentions_3yr)), 38.0), 96.0)
581
+ combined_ratio_pct = expected_loss_ratio_pct + 24.5 # 24.5% acquisition & administrative expense ratio
582
+ solvency_ii_scr_var995_usd = (hv * 0.045 + gt * 18.0) * math.sqrt(psc_mult * cii_mult)
583
+ ppmi_adj_pct = (ppmi_mult - 1.0) * 100.0
584
+
585
+ return {
586
+ "hull_machinery_premium_usd": round(hm_premium_usd, 2),
587
+ "pi_club_mutual_call_usd": round(pi_call_usd, 2),
588
+ "marine_cargo_premium_usd": round(cargo_premium_usd, 2),
589
+ "war_risk_chokepoint_surcharge_usd": round(war_risk_usd, 2),
590
+ "total_annual_marine_insurance_usd": round(total_premium_usd, 2),
591
+ "total_annual_premium_usd": round(total_premium_usd, 2),
592
+ "ppmi_climate_alignment_delta_pct": round(ppmi_delta_pct, 2),
593
+ "ppmi_climate_alignment_adjustment_pct": round(ppmi_adj_pct, 2),
594
+ "ppmi_status": "ALIGNED (Green Underwriting Tier)" if ppmi_delta_pct <= 0.0 else "MISALIGNED (Transition Risk Surcharge)",
595
+ "expected_loss_ratio_pct": round(expected_loss_ratio_pct, 2),
596
+ "combined_ratio_pct": round(combined_ratio_pct, 2),
597
+ "solvency_ii_var995_capital_usd": round(solvency_ii_scr_var995_usd, 2),
598
+ "reference": "IUMI Global Marine Insurance Report (2025); Poseidon Principles for Marine Insurance (PPMI, 2024); IG P&I (2024)",
599
+ }
600
+
601
+
602
+ def global_chokepoint_disruption_index(
603
+ baseline_transits_yr: float,
604
+ observed_transits_yr: float,
605
+ detour_distance_nm: float = 3500.0,
606
+ vessel_speed_knots: float = 14.5,
607
+ daily_fuel_tonnes: float = 42.0,
608
+ fuel_price_usd_t: float = 640.0,
609
+ eua_carbon_price_usd_t: float = 75.0,
610
+ ) -> Dict[str, Any]:
611
+ """
612
+ Compute the Global Maritime Chokepoint Supply-Chain Disruption & Rerouting Cost
613
+ Index (Verschuur et al., Nature 2023; IMF PortWatch 2024; UNCTAD 2024) for major
614
+ chokepoints (Suez, Bab el-Mandeb, Panama, Malacca, Hormuz, Bosporus, Dardanelles).
615
+ """
616
+ base_n = max(float(baseline_transits_yr), 1.0)
617
+ obs_n = max(float(observed_transits_yr), 0.0)
618
+ transit_drop_pct = max(0.0, ((base_n - obs_n) / base_n) * 100.0)
619
+ rerouted_vessels = max(0.0, base_n - obs_n)
620
+ extra_days_per_voyage = float(detour_distance_nm) / (max(float(vessel_speed_knots), 1.0) * 24.0)
621
+ extra_fuel_t_per_voyage = extra_days_per_voyage * float(daily_fuel_tonnes)
622
+ extra_co2_t_per_voyage = extra_fuel_t_per_voyage * 3.151
623
+ extra_cost_per_voyage_usd = (
624
+ extra_fuel_t_per_voyage * float(fuel_price_usd_t)
625
+ + extra_co2_t_per_voyage * float(eua_carbon_price_usd_t)
626
+ )
627
+ disruption_score = min(100.0, round(transit_drop_pct * 1.35 + extra_days_per_voyage * 2.2, 2))
628
+ return {
629
+ "transit_drop_pct": round(transit_drop_pct, 2),
630
+ "rerouted_vessels_count": round(rerouted_vessels, 1),
631
+ "extra_sea_days_per_voyage": round(extra_days_per_voyage, 2),
632
+ "extra_fuel_tonnes_per_voyage": round(extra_fuel_t_per_voyage, 2),
633
+ "extra_co2_tonnes_per_voyage": round(extra_co2_t_per_voyage, 2),
634
+ "extra_voyage_cost_usd": round(extra_cost_per_voyage_usd, 2),
635
+ "aggregate_rerouting_co2_mt": round((rerouted_vessels * extra_co2_t_per_voyage) / 1e6, 4),
636
+ "disruption_severity_index_0_100": disruption_score,
637
+ "reference": "Verschuur et al. (2023, Nature); IMF PortWatch (2024); UNCTAD Review of Maritime Transport (2024)",
638
+ }
639
+
640
+
641
+ def kunming_montreal_30x30_gap(
642
+ current_mpa_pct: float,
643
+ eez_area_km2: float,
644
+ current_year: int = 2025,
645
+ target_year: int = 2030,
646
+ target_pct: float = 30.0,
647
+ ) -> Dict[str, Any]:
648
+ """
649
+ Evaluate national or global ocean basin progress toward the CBD Kunming-Montreal
650
+ Global Biodiversity Framework (GBF) Target 3 ('30x30' by 2030) and UN BBNJ High-Seas
651
+ Treaty (Sala et al., Nature 2021; UNEP-WCMC Protected Planet Report 2024).
652
+ """
653
+ cur_pct = max(0.0, min(100.0, float(current_mpa_pct)))
654
+ area = max(0.0, float(eez_area_km2))
655
+ tgt = max(0.1, min(100.0, float(target_pct)))
656
+ yrs_left = max(1, int(target_year) - int(current_year))
657
+ protected_km2 = area * (cur_pct / 100.0)
658
+ target_km2 = area * (tgt / 100.0)
659
+ gap_pct = max(0.0, tgt - cur_pct)
660
+ gap_km2 = max(0.0, target_km2 - protected_km2)
661
+ req_cagr_pct = (((tgt / max(cur_pct, 0.05)) ** (1.0 / yrs_left)) - 1.0) * 100.0 if cur_pct < tgt else 0.0
662
+ return {
663
+ "current_mpa_pct": round(cur_pct, 2),
664
+ "target_30x30_pct": round(tgt, 2),
665
+ "mpa_gap_percentage_points": round(gap_pct, 2),
666
+ "protected_marine_area_km2": round(protected_km2, 1),
667
+ "additional_area_needed_km2": round(gap_km2, 1),
668
+ "required_annual_expansion_km2_yr": round(gap_km2 / yrs_left, 1),
669
+ "required_cagr_pct": round(req_cagr_pct, 2),
670
+ "status": "ACHIEVED (>=30% Protected)" if cur_pct >= tgt else "EXPANSION_REQUIRED",
671
+ "reference": "CBD Kunming-Montreal GBF Target 3 (2022); Sala et al. (2021, Nature 592); UNEP-WCMC WDPA (2024)",
672
+ }
673
+
674
+
675
+ def arctic_route_navigability_index(
676
+ corridor: str = "CORR_NSR",
677
+ september_ice_extent_mkm2: float = 0.62,
678
+ baseline_2005_ice_extent_mkm2: float = 1.45,
679
+ polar_distance_nm: float = 7200.0,
680
+ suez_distance_nm: float = 11200.0,
681
+ vessel_ice_class: str = "PC6",
682
+ daily_fuel_tonnes: float = 45.0,
683
+ speed_suez_knots: float = 15.0,
684
+ speed_polar_knots: float = 12.5,
685
+ ) -> Dict[str, Any]:
686
+ """
687
+ Compute the Arctic Polar Maritime Corridor Navigability, Distance/Fuel Saving &
688
+ IMO Polar Code Risk Index (Melia et al., Geophysical Research Letters 2016;
689
+ Stephenson et al., Nature Climate Change; Arctic Council PAME ASTD 2024; NSIDC).
690
+ """
691
+ ice_cur = max(0.01, float(september_ice_extent_mkm2))
692
+ ice_base = max(0.1, float(baseline_2005_ice_extent_mkm2))
693
+ d_pol = max(100.0, float(polar_distance_nm))
694
+ d_suez = max(100.0, float(suez_distance_nm))
695
+ v_suez = max(5.0, float(speed_suez_knots))
696
+ v_pol = max(4.0, float(speed_polar_knots))
697
+ f_day = max(1.0, float(daily_fuel_tonnes))
698
+
699
+ ice_retreat_fraction = max(0.0, min(1.0, 1.0 - (ice_cur / ice_base)))
700
+ distance_saving_pct = max(0.0, ((d_suez - d_pol) / d_suez) * 100.0)
701
+ days_suez = d_suez / (v_suez * 24.0)
702
+ days_polar = d_pol / (v_pol * 24.0)
703
+ days_saved = days_suez - days_polar
704
+ # Ice-resistance fuel penalty factor (+12% per day in polar waters)
705
+ fuel_suez_t = days_suez * f_day
706
+ fuel_polar_t = days_polar * f_day * 1.12
707
+ co2_saved_t = (fuel_suez_t - fuel_polar_t) * 3.151
708
+
709
+ ic = vessel_ice_class.strip().upper()
710
+ class_bonus = 1.0 if ic in ("PC1", "PC2", "PC3", "PC4", "PC5", "ARC7") else (0.85 if ic in ("PC6", "PC7", "ARC4", "1A") else 0.55)
711
+ navigability_score = min(100.0, (0.55 * ice_retreat_fraction * 100.0 + 0.45 * distance_saving_pct) * class_bonus)
712
+
713
+ return {
714
+ "corridor": str(corridor).upper(),
715
+ "vessel_ice_class": ic,
716
+ "september_ice_extent_mkm2": round(ice_cur, 3),
717
+ "ice_retreat_fraction_vs_2005": round(ice_retreat_fraction, 4),
718
+ "distance_saving_pct_vs_suez": round(distance_saving_pct, 2),
719
+ "transit_days_suez": round(days_suez, 2),
720
+ "transit_days_polar": round(days_polar, 2),
721
+ "sea_days_saved": round(days_saved, 2),
722
+ "net_co2_saved_tonnes": round(co2_saved_t, 2),
723
+ "polar_navigability_index_0_100": round(navigability_score, 2),
724
+ "reference": "Arctic Council PAME ASTD (2024); NSIDC Sea Ice Index v3; IMO Polar Code (MEPC 68/21)",
725
+ }
726
+
727
+
728
+ def seaborne_trade_climate_coupling_index(
729
+ merchandise_trade_gdp_pct: float = 54.0,
730
+ export_volume_index_2015_100: float = 132.0,
731
+ transport_service_share_pct: float = 24.5,
732
+ hub_wind_max_kmh: float = 52.0,
733
+ renewable_electricity_share_pct: float = 42.0,
734
+ ) -> Dict[str, Any]:
735
+ """
736
+ Compute the Global Seaborne Merchandise Trade Openness, ERA5 Port/Chokepoint
737
+ Climate Disruption & Green Transition Coupling Index (Verschuur et al., Nature
738
+ Communications 2023; Hersbach et al., Q. J. R. Meteorol. Soc. 2020; UNCTAD 2024;
739
+ WTO World Trade Report 2024).
740
+ """
741
+ trd_gdp = max(0.0, float(merchandise_trade_gdp_pct))
742
+ exp_vol = max(1.0, float(export_volume_index_2015_100))
743
+ trn_shr = max(0.0, min(100.0, float(transport_service_share_pct)))
744
+ wind_max = max(0.0, float(hub_wind_max_kmh))
745
+ ren_elc = max(0.0, min(100.0, float(renewable_electricity_share_pct)))
746
+
747
+ openness_score = min(100.0, (trd_gdp / 120.0) * 50.0 + (exp_vol / 200.0) * 30.0 + (trn_shr / 45.0) * 20.0)
748
+ # Beaufort >= 8 gale threshold (~62 km/h) increases port downtime & chokepoint queue risk
749
+ weather_disruption_pct = min(100.0, max(0.0, ((wind_max - 35.0) / 55.0) * 100.0))
750
+ green_port_readiness = min(100.0, ren_elc * 0.85 + 15.0)
751
+ net_resilience_index = max(0.0, min(100.0, 0.45 * openness_score + 0.35 * green_port_readiness - 0.20 * weather_disruption_pct))
752
+
753
+ return {
754
+ "merchandise_trade_gdp_pct": round(trd_gdp, 2),
755
+ "export_volume_index_2015_100": round(exp_vol, 2),
756
+ "transport_service_share_pct": round(trn_shr, 2),
757
+ "era5_hub_wind_max_kmh": round(wind_max, 2),
758
+ "renewable_electricity_share_pct": round(ren_elc, 2),
759
+ "seaborne_openness_score_0_100": round(openness_score, 2),
760
+ "era5_port_weather_disruption_risk_0_100": round(weather_disruption_pct, 2),
761
+ "green_shore_power_readiness_0_100": round(green_port_readiness, 2),
762
+ "trade_climate_resilience_index_0_100": round(net_resilience_index, 2),
763
+ "reference": "Verschuur et al. (2023, Nat. Commun. 14:1341); Hersbach et al. (2020, ERA5); UNCTAD (2024)",
764
+ }
765
+
766
+
767
+ def fueleu_cii_ets_compliance_cost(
768
+ annual_fuel_tonnes: float = 11500.0,
769
+ wtw_ghg_intensity_gco2e_mj: float = 89.34,
770
+ target_wtw_gco2e_mj: float = 89.34,
771
+ eu_voyage_share_pct: float = 65.0,
772
+ eua_carbon_price_eur_t: float = 78.5,
773
+ attained_aer_gco2_dwt_nm: float = 4.65,
774
+ required_aer_gco2_dwt_nm: float = 4.50,
775
+ ) -> Dict[str, Any]:
776
+ """
777
+ Compute integrated Maritime Regulatory Carbon Exposure combining Regulation (EU) 2023/1805
778
+ (FuelEU Maritime WtW GHG Intensity Deficit Penalty at 2,400 EUR/t VLSFOe = 58.5 EUR/GJ),
779
+ Directive (EU) 2023/959 (100% EU ETS Maritime Allowance Liability from 2026), and
780
+ IMO MARPOL Annex VI CII (AER Rating A–E) across the 15 global fleet segments.
781
+ """
782
+ fuel_t = max(1.0, float(annual_fuel_tonnes))
783
+ wtw_act = max(0.0, float(wtw_ghg_intensity_gco2e_mj))
784
+ wtw_tgt = max(1.0, float(target_wtw_gco2e_mj))
785
+ eu_share = max(0.0, min(100.0, float(eu_voyage_share_pct))) / 100.0
786
+ eua_eur = max(0.0, float(eua_carbon_price_eur_t))
787
+ aer_act = max(0.1, float(attained_aer_gco2_dwt_nm))
788
+ aer_req = max(0.1, float(required_aer_gco2_dwt_nm))
789
+
790
+ # Energy in MJ (assuming 41,000 MJ/t VLSFO equivalent)
791
+ total_energy_mj = fuel_t * 41000.0 * eu_share
792
+ ghg_balance_tco2e = ((wtw_tgt - wtw_act) * total_energy_mj) / 1e6
793
+ if ghg_balance_tco2e < 0.0 and wtw_act > 0.0:
794
+ fueleu_penalty_eur = (abs(ghg_balance_tco2e) * 1e6 / (wtw_act * 41000.0)) * 2400.0
795
+ else:
796
+ fueleu_penalty_eur = 0.0
797
+
798
+ # EU ETS CO2e liability (3.114 tCO2/t fuel * eu_share)
799
+ ets_verified_tco2e = fuel_t * 3.114 * eu_share
800
+ ets_cost_eur = ets_verified_tco2e * eua_eur
801
+
802
+ aer_ratio = aer_act / aer_req
803
+ if aer_ratio <= 0.86:
804
+ cii_grade = "A"
805
+ elif aer_ratio <= 0.94:
806
+ cii_grade = "B"
807
+ elif aer_ratio <= 1.06:
808
+ cii_grade = "C"
809
+ elif aer_ratio <= 1.18:
810
+ cii_grade = "D"
811
+ else:
812
+ cii_grade = "E"
813
+
814
+ return {
815
+ "annual_fuel_tonnes": round(fuel_t, 2),
816
+ "eu_scope_energy_gj": round(total_energy_mj / 1000.0, 2),
817
+ "fueleu_ghg_balance_tco2e": round(ghg_balance_tco2e, 2),
818
+ "fueleu_penalty_eur": round(fueleu_penalty_eur, 2),
819
+ "eu_ets_verified_tco2e": round(ets_verified_tco2e, 2),
820
+ "eu_ets_allowance_cost_eur": round(ets_cost_eur, 2),
821
+ "total_regulatory_carbon_cost_eur": round(fueleu_penalty_eur + ets_cost_eur, 2),
822
+ "imo_cii_aer_ratio": round(aer_ratio, 4),
823
+ "imo_cii_rating": cii_grade,
824
+ "reference": "Regulation (EU) 2023/1805 (FuelEU Maritime); Directive (EU) 2023/959 (EU ETS); IMO MEPC.377(80)",
825
+ }
826
+
827
+
828
+ def subsea_corridor_vulnerability_index(
829
+ daily_vessel_transits: float = 58.0,
830
+ rerouting_detour_nm: float = 3450.0,
831
+ subsea_cable_count: int = 16,
832
+ cable_fault_rate_per_yr: float = 2.4,
833
+ geopolitical_hazard_score_0_100: float = 68.0,
834
+ ) -> Dict[str, Any]:
835
+ """
836
+ Evaluate Global Maritime Chokepoint, Trade Corridor & Subsea Telecommunication/HVDC
837
+ Cable Infrastructure Vulnerability Index (0–100) following Verschuur et al. (Nature
838
+ Communications 2023), IMF PortWatch (2024–2026), Carter et al. (UNEP-WCMC/ICPC 2009),
839
+ and UN BBNJ High-Seas Governance metrics.
840
+ """
841
+ transits = max(0.0, float(daily_vessel_transits))
842
+ detour = max(0.0, float(rerouting_detour_nm))
843
+ cables = max(0, int(subsea_cable_count))
844
+ faults = max(0.0, float(cable_fault_rate_per_yr))
845
+ geo_haz = max(0.0, min(100.0, float(geopolitical_hazard_score_0_100)))
846
+
847
+ # Additional transit days at 14.5 knots service speed
848
+ added_days = detour / (14.5 * 24.0)
849
+ freight_surcharge_multiplier = 1.0 + 0.085 * added_days + 0.004 * geo_haz
850
+
851
+ s_detour = min(100.0, (detour / 4500.0) * 100.0)
852
+ s_cable = min(100.0, (cables / 22.0) * 55.0 + (faults / 5.0) * 45.0)
853
+ composite_vuln = 0.40 * geo_haz + 0.35 * s_detour + 0.25 * s_cable
854
+
855
+ return {
856
+ "daily_vessel_transits": round(transits, 1),
857
+ "rerouting_detour_nm": round(detour, 1),
858
+ "estimated_detour_days_at_14_5_kn": round(added_days, 2),
859
+ "subsea_cable_corridor_count": cables,
860
+ "annual_anchor_trawl_fault_rate": round(faults, 2),
861
+ "freight_war_risk_multiplier": round(freight_surcharge_multiplier, 3),
862
+ "corridor_infrastructure_vulnerability_0_100": round(composite_vuln, 2),
863
+ "reference": "Verschuur et al. (2023, Nat. Commun.); IMF PortWatch (2026); ICPC / TeleGeography (2026)",
864
+ }
865
+
866
+
867
+ def rfmo_stock_iuu_sustainability(
868
+ ssb_bmsy_ratio: float = 1.04,
869
+ f_fmsy_ratio: float = 0.96,
870
+ iuu_risk_index_0_100: float = 34.0,
871
+ bycatch_compliance_pct: float = 68.0,
872
+ annual_tac_kt: float = 825.0,
873
+ ) -> Dict[str, Any]:
874
+ """
875
+ Evaluate Regional Fisheries Management Organization (RFMO) Kobe Plot Quadrant Status
876
+ (SSB/BMSY vs. F/FMSY), Illegal, Unreported and Unregulated (IUU) Depletion Risk, and
877
+ Sustainable Harvest Governance Index across all 18 Global RFMOs (Hilborn et al., PNAS 2020;
878
+ FAO SOFIA 2024; Agnew et al., PLOS ONE 2009).
879
+ """
880
+ b_ratio = max(0.05, float(ssb_bmsy_ratio))
881
+ f_ratio = max(0.05, float(f_fmsy_ratio))
882
+ iuu = max(0.0, min(100.0, float(iuu_risk_index_0_100)))
883
+ bycatch = max(0.0, min(100.0, float(bycatch_compliance_pct)))
884
+ tac = max(0.0, float(annual_tac_kt))
885
+
886
+ if b_ratio >= 1.0 and f_ratio <= 1.0:
887
+ kobe_quadrant = "GREEN (Sustainable: Not Overfished, No Overfishing)"
888
+ elif b_ratio < 1.0 and f_ratio > 1.0:
889
+ kobe_quadrant = "RED (Depleted & Overfishing Active)"
890
+ elif b_ratio < 1.0 and f_ratio <= 1.0:
891
+ kobe_quadrant = "YELLOW_REBUILDING (Overfished, Rebuilding Under Low F)"
892
+ else:
893
+ kobe_quadrant = "ORANGE_WARNING (Biomass Above BMSY but Overfishing Occurring)"
894
+
895
+ bio_health = min(100.0, max(0.0, (b_ratio / 1.35) * 55.0 + max(0.0, (2.0 - f_ratio) / 1.5) * 45.0))
896
+ governance_score = 0.45 * bio_health + 0.30 * (100.0 - iuu) + 0.25 * bycatch
897
+
898
+ return {
899
+ "ssb_bmsy_ratio": round(b_ratio, 3),
900
+ "f_fmsy_ratio": round(f_ratio, 3),
901
+ "kobe_plot_quadrant": kobe_quadrant,
902
+ "iuu_risk_index_0_100": round(iuu, 2),
903
+ "bycatch_compliance_pct": round(bycatch, 2),
904
+ "annual_tac_kt": round(tac, 1),
905
+ "rfmo_sustainability_index_0_100": round(governance_score, 2),
906
+ "reference": "Hilborn et al. (2020, PNAS); FAO SOFIA (2024); Cullis-Suzuki & Pauly (2010, Mar. Policy)",
907
+ }
908
+
909
+
910
+ def desalination_brine_green_h2_nexus(
911
+ desal_capacity_m3_day: float = 850000.0,
912
+ specific_energy_kwh_m3: float = 3.25,
913
+ brine_salinity_anomaly_psu: float = 2.4,
914
+ green_h2_nh3_output_kt_yr: float = 210.0,
915
+ marine_renewable_mw: float = 380.0,
916
+ ) -> Dict[str, Any]:
917
+ """
918
+ Model the Coastal Seawater Desalination (SWRO), Hypersaline Brine Plume Dilution Demand,
919
+ and Green Maritime Hydrogen / e-Ammonia Electrolysis Nexus across 25 Global Hubs
920
+ (Jones et al., Sci. Total Environ. 2019; IRENA / IEA Green Hydrogen & Maritime e-Fuels 2024).
921
+ """
922
+ cap_m3d = max(100.0, float(desal_capacity_m3_day))
923
+ sec = max(1.8, float(specific_energy_kwh_m3))
924
+ psu_anom = max(0.1, float(brine_salinity_anomaly_psu))
925
+ h2_kt = max(0.0, float(green_h2_nh3_output_kt_yr))
926
+ ren_mw = max(0.0, float(marine_renewable_mw))
927
+
928
+ # Brine discharge volume assuming 45% SWRO recovery -> 1.22 m3 brine per m3 permeate
929
+ brine_discharge_m3_day = cap_m3d * 1.22
930
+ annual_power_gwh = (cap_m3d * sec * 365.0) / 1e6
931
+ # Stoichiometric water for electrolysis (~9 m3 ultrapure water per tonne H2) as % of annual desal output
932
+ electrolysis_water_share_pct = min(100.0, ((h2_kt * 1000.0 * 9.0) / (cap_m3d * 365.0)) * 100.0)
933
+ nexus_efficiency_score = min(100.0, max(0.0, (4.8 - sec) / 3.0 * 40.0 + max(0.0, (4.5 - psu_anom) / 4.5) * 30.0 + min(30.0, (ren_mw / 500.0) * 30.0)))
934
+
935
+ return {
936
+ "desal_capacity_m3_day": round(cap_m3d, 1),
937
+ "estimated_brine_discharge_m3_day": round(brine_discharge_m3_day, 1),
938
+ "specific_energy_kwh_m3": round(sec, 3),
939
+ "annual_desal_electricity_demand_gwh": round(annual_power_gwh, 2),
940
+ "brine_salinity_anomaly_psu": round(psu_anom, 2),
941
+ "green_h2_nh3_output_kt_yr": round(h2_kt, 2),
942
+ "electrolysis_feedwater_share_pct": round(electrolysis_water_share_pct, 3),
943
+ "coastal_water_energy_nexus_score_0_100": round(nexus_efficiency_score, 2),
944
+ "reference": "Jones et al. (2019, Sci. Total Environ.); IRENA & IMO Green Shipping Corridors (2024)",
945
+ }
946
+
947
+
948
+
949
+
950
+
951
+
952
+