The trajectory paths you see in the animations are generated through hour-by-hour physics simulation that mimics how OpenDrift and Trajan work. Here's the detailed explanation:
# Each trajectory represents one "particle" (container)
for particle_id in range(num_particles):
current_lat = launch_lat # Start at launch point
current_lon = launch_lon
path = [] # Store hourly positions
# Simulate hour by hour for 7 days (168 hours)
for hour in range(168):
# Record current position
path.append({
'hour': hour,
'lat': current_lat,
'lon': current_lon
})
# Calculate forces and move to next position
current_lat, current_lon = calculate_next_position(...)Each hour, the container experiences multiple forces:
# Wind pushes the container on the surface
wind_speed = 5.5 # m/s (Mediterranean summer NW winds)
wind_direction = 315 # degrees (NW)
wind_effect = 0.15 if bottle else 0.08 # Bottles more affected
# Hourly wind drift
wind_drift_km_hour = wind_speed * wind_effect / 24.0
wind_lat_change = wind_drift_km_hour * cos(wind_direction) / 111.0
wind_lon_change = wind_drift_km_hour * sin(wind_direction) / 111.0# Mediterranean current flows eastward
current_speed = 0.3 # m/s (eastward current)
current_direction = 90 # degrees (east)
current_effect = 1.8 if bottle else 2.2 # Jerry cans more affected
# Hourly current drift
current_drift_km_hour = current_speed * current_effect * 3.6
current_lat_change = current_drift_km_hour * cos(current_direction) / 111.0
current_lon_change = current_drift_km_hour * sin(current_direction) / 111.0# Update position based on combined forces
current_lat += wind_lat_change + current_lat_change
current_lon += wind_lon_change + current_lon_changeOpenDrift uses the same principle but with more sophisticated physics:
- Seeded Particles: Launch multiple particles from the same point
- Time Integration: Move particles using numerical integration (Runge-Kutta)
- Environmental Forcing: Use real oceanographic data (wind, currents, waves)
- Physics Models: Include turbulence, vertical motion, beaching, etc.
# Simplified OpenDrift process
model = OceanDrift()
model.seed_elements(lon=launch_lon, lat=launch_lat, number=50)
# Time loop (similar to our hour-by-hour)
for time_step in range(simulation_duration):
# Get environmental data at current positions
wind_u, wind_v = get_wind_at_positions(positions, time)
current_u, current_v = get_current_at_positions(positions, time)
# Calculate drift velocity
drift_u = wind_u * wind_drift_factor + current_u
drift_v = wind_v * wind_drift_factor + current_v
# Update positions (Euler or Runge-Kutta integration)
new_positions = positions + drift_velocity * time_step# Our equivalent process
for hour in range(168): # 7 days
# Get forces at current position
wind_force = calculate_wind_force(container_type, hour)
current_force = calculate_current_force(container_type, hour)
# Add randomness (turbulence)
wind_force += random_noise()
current_force += random_noise()
# Move container
new_position = current_position + (wind_force + current_force) * 1_hour
path.append(new_position)Trajan standardizes trajectory data following Climate & Forecast conventions:
# Our trajectory data becomes CF-compliant
trajectory_dataset = xr.Dataset({
'lat': (['trajectory', 'time'], lat_array),
'lon': (['trajectory', 'time'], lon_array),
'time': (['time'], time_array)
}, attrs={
'Conventions': 'CF-1.8',
'title': 'Gaza Delivery Trajectories'
})
# Trajan can then analyze this data
import trajan
analysis = trajan.traj.Trajectory(trajectory_dataset)# What Trajan enables
analysis.speed() # Calculate speed along trajectory
analysis.acceleration() # Calculate acceleration
analysis.distance() # Total distance traveled
analysis.plot() # Professional visualizationPosition: 31.3000°N, 34.0000°E (Launch point)
Wind: 5.5 m/s NW (pushes container SE)
Current: 0.3 m/s E (pushes container E)
# Wind effect (1 hour)
wind_push_lat = -0.001° # Southward push
wind_push_lon = +0.001° # Eastward push
# Current effect (1 hour)
current_push_lat = 0.0° # No north/south
current_push_lon = +0.002° # Eastward push
# New position
new_lat = 31.3000 - 0.001 = 31.2990°N
new_lon = 34.0000 + 0.003 = 34.0030°EThis process repeats every hour, with:
- Random variations in wind/current strength
- Directional changes due to weather patterns
- Particle-specific differences (each container behaves slightly differently)
- Weak steering toward target after day 1
- Multiple colored lines: Each represents one container's path
- Real-time progression: Shows hour-by-hour movement over 7 days
- Color coding:
- Green = reaches target (success)
- Orange = close miss (within 5km)
- Red = miss (5-15km away)
- Dark red = far miss (>15km away)
# Each particle has different random factors
particle_1: wind_noise = +0.1, current_noise = -0.05 # Faster drift
particle_2: wind_noise = -0.1, current_noise = +0.05 # Slower drift
particle_3: wind_noise = 0.0, current_noise = 0.0 # Average drift
# Results in different trajectories from same launch pointThe rope effect is subtle but real:
# Rope adds drag and affects current sensitivity
rope_drag = rope.length * 0.1 * rope_drag_coefficient
total_drag = container_drag + rope_drag
# Longer rope = more current effect
current_factor = 1.0 + (rope.length / 50.0) - 5m rope:
current_factor = 1.1(10% more current effect) - 15m rope:
current_factor = 1.3(30% more current effect) - No rope:
current_factor = 1.0(baseline)
The effect is realistic - ropes don't dramatically change trajectories, they provide stability and slight steering.
Your CSV shows:
- offshore_20 → almawasi_center: 96% success with 2L bottle
- offshore_16 → almawasi_center: 88% success with 2L bottle
- offshore_11 → almawasi_center: 92% success with 2L bottle
Our simulation should produce similar success rates when using:
- Your exact coordinates as launch points
- 2L bottle specifications (volume=2.0, mass=1.0)
- Summer conditions with dawn launch timing
Run these commands to see the physics in action:
python test_bottles_with_coordinates.pypython run_optimization.py \
--coordinates "31.3,34.0" \
--container-volume 2.0 \
--container-mass 1.0 \
--target-success 0.8# 2L Bottle
python run_optimization.py --coordinates-file launch_coordinates.txt --container-volume 2.0 --container-mass 1.0
# 5L Jerry Can
python run_optimization.py --coordinates-file launch_coordinates.txt --container-volume 5.0 --container-mass 2.5The animations will show you exactly how the containers move hour by hour through the Mediterranean, and why some paths succeed while others fail! 🌊