import numpy as np
import matplotlib.pyplot as plt
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(11, 9))
# ── Top: pipe snapshot ────────────────────────────────────────────────────────
# Products in pipe, as fraction of total pipe length (1147 km)
# One full rotation carries 5 product types plus transmix buffers
# Approximate batch sizes (km of pipe):
batch_lengths = {
'Premium\nGasoline': 180,
'Regular\nGasoline': 200,
'Diesel\n(ULSD)': 220,
'Jet A-1\n(Aviation)': 160,
'Diesel\n(buffer)': 170,
}
transmix_km = 1.2 # ~half a km each side, shown as 1.2 km for visibility
prod_colours = {
'Premium\nGasoline': '#f1c40f',
'Regular\nGasoline': '#e67e22',
'Diesel\n(ULSD)': '#2980b9',
'Jet A-1\n(Aviation)': '#8e44ad',
'Diesel\n(buffer)': '#1a5276',
}
tmx_colour = '#bdc3c7'
total_km = sum(batch_lengths.values()) + len(batch_lengths) * transmix_km * 2
x = 0.0
plot_y = 0.35
bar_h = 0.30
# Draw batches and interfaces
for name, blen in batch_lengths.items():
# leading transmix
ax1.barh(plot_y, transmix_km, bar_h, left=x, color=tmx_colour,
edgecolor='none')
ax1.text(x + transmix_km/2, plot_y, 'tmx', ha='center', va='center',
fontsize=6, color='#7f8c8d')
x += transmix_km
# product batch
ax1.barh(plot_y, blen, bar_h, left=x, color=prod_colours[name],
edgecolor='white', linewidth=0.5, alpha=0.88)
ax1.text(x + blen/2, plot_y, name, ha='center', va='center',
fontsize=8.5, fontweight='bold', color='white')
x += blen
# trailing transmix
ax1.barh(plot_y, transmix_km, bar_h, left=x, color=tmx_colour, edgecolor='none')
x += transmix_km
ax1.set_xlim(0, total_km + 20)
ax1.set_ylim(0, 1)
ax1.set_xlabel('Position in pipe [km from Edmonton]', fontsize=10)
ax1.set_yticks([])
ax1.set_title('Trans Mountain Products Pipeline — Pipe Contents (Snapshot)\n'
'One complete product cycle: Edmonton (left) → Burnaby (right)',
fontsize=11, pad=8)
ax1.axvline(1147, color='#c0392b', linewidth=1.5, linestyle='--')
ax1.text(1147, 0.85, 'Burnaby\nterminal', ha='center', va='top',
color='#c0392b', fontsize=9)
ax1.axvline(0, color='#27ae60', linewidth=1.5, linestyle='--')
ax1.text(5, 0.85, 'Edmonton\ninjection', ha='left', va='top',
color='#27ae60', fontsize=9)
ax1.grid(axis='x', alpha=0.2)
# ── Bottom: spacetime diagram ─────────────────────────────────────────────────
transit_days = 9.5
L_km = 1147.0
# Three cycles of batches
cycle_products = [
('Premium Gasoline', '#f1c40f', 4.0),
('Regular Gasoline', '#e67e22', 4.5),
('Diesel', '#2980b9', 5.0),
('Jet A-1', '#8e44ad', 3.5),
('Diesel (buffer)', '#1a5276', 3.8),
]
cycle_duration = sum(d for _, _, d in cycle_products) + 0.5
t_inj_start = 0.0
for cycle in range(3):
t_inj = t_inj_start + cycle * cycle_duration
for name, col, dur in cycle_products:
# Injection window: [t_inj, t_inj+dur] at km=0
# Arrival window at Burnaby: [t_inj+transit, t_inj+dur+transit]
# Draw parallelogram
xs = [0, 0, L_km, L_km, 0]
ys = [t_inj, t_inj + dur, t_inj + dur + transit_days,
t_inj + transit_days, t_inj]
ax2.fill(xs, ys, color=col, alpha=0.75, edgecolor='white',
linewidth=0.4)
# Label in centre of parallelogram
ax2.text(L_km/2, t_inj + transit_days/2 + dur/2, name,
ha='center', va='center', fontsize=7.5,
color='white', fontweight='bold')
# transmix (thin grey band)
xs_tmx = [0, 0, L_km, L_km, 0]
ys_tmx = [t_inj + dur, t_inj + dur + 0.15,
t_inj + dur + 0.15 + transit_days,
t_inj + dur + transit_days, t_inj + dur]
ax2.fill(xs_tmx, ys_tmx, color='#bdc3c7', alpha=0.6, edgecolor='none')
t_inj += dur
ax2.axvline(0, color='#27ae60', linewidth=1.5)
ax2.axvline(L_km, color='#c0392b', linewidth=1.5)
ax2.text(5, -0.8, 'Edmonton\n(injection)', color='#27ae60', fontsize=8, ha='left')
ax2.text(L_km-5, -0.8, 'Burnaby\n(delivery)', color='#c0392b', fontsize=8, ha='right')
ax2.set_xlim(0, L_km)
ax2.set_ylim(-1, 3 * cycle_duration + transit_days + 1)
ax2.set_xlabel('Position in pipeline [km]', fontsize=10)
ax2.set_ylabel('Time [days from start]', fontsize=10)
ax2.set_title('Spacetime Diagram — Three Complete Product Cycles\n'
f'Each batch takes {transit_days} days to cross the 1,147 km pipeline',
fontsize=11, pad=8)
ax2.grid(alpha=0.2)
ax2.annotate('', xy=(L_km, transit_days), xytext=(0, 0),
arrowprops=dict(arrowstyle='->', color='#2c3e50', lw=1.4))
ax2.text(L_km/2 + 80, transit_days/2, f'{transit_days} days\ntransit',
ha='left', va='center', fontsize=8.5, color='#2c3e50')
plt.tight_layout()
plt.show()