Skip to content

Latest commit

 

History

History
270 lines (209 loc) · 8.23 KB

File metadata and controls

270 lines (209 loc) · 8.23 KB

🌊 How Trajectory Paths Are Generated - Technical Explanation

🤔 YOUR QUESTION: "How do you get the intermediate paths?"

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:


🔬 THE PHYSICS SIMULATION PROCESS

1. Particle-Based Modeling (Like OpenDrift)

# 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(...)

2. Force Calculation Every Hour

Each hour, the container experiences multiple forces:

Wind Force (Surface Effect)

# 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

Current Force (Underwater Effect)

# 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

Combined Movement Each Hour

# Update position based on combined forces
current_lat += wind_lat_change + current_lat_change  
current_lon += wind_lon_change + current_lon_change

🎯 HOW THIS MIMICS OPENDRIFT

OpenDrift's Lagrangian Method

OpenDrift uses the same principle but with more sophisticated physics:

  1. Seeded Particles: Launch multiple particles from the same point
  2. Time Integration: Move particles using numerical integration (Runge-Kutta)
  3. Environmental Forcing: Use real oceanographic data (wind, currents, waves)
  4. 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 Simplified Version

# 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)

📊 HOW TRAJAN ENHANCES THIS

Trajan's CF-Compliant Data Structure

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)

Advanced Analysis Capabilities

# What Trajan enables
analysis.speed()           # Calculate speed along trajectory
analysis.acceleration()    # Calculate acceleration
analysis.distance()        # Total distance traveled
analysis.plot()           # Professional visualization

🔍 INTERMEDIATE PATH GENERATION - STEP BY STEP

Hour 0: Launch

Position: 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)

Hour 1: First Movement

# 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°E

Hour 2-168: Continued Simulation

This 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

🎬 ANIMATION VISUALIZATION

What You See in the Animation

  1. Multiple colored lines: Each represents one container's path
  2. Real-time progression: Shows hour-by-hour movement over 7 days
  3. Color coding:
    • Green = reaches target (success)
    • Orange = close miss (within 5km)
    • Red = miss (5-15km away)
    • Dark red = far miss (>15km away)

Why Multiple Paths?

# 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 point

🔬 WHY THE ROPE SEEMS TO "DO NOTHING"

The rope effect is subtle but real:

Rope Physics in the Model

# 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)  

Real Effect

  • 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.


VALIDATION AGAINST YOUR CSV DATA

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:

  1. Your exact coordinates as launch points
  2. 2L bottle specifications (volume=2.0, mass=1.0)
  3. Summer conditions with dawn launch timing

🚀 NOW LET'S TEST IT!

Run these commands to see the physics in action:

Test 2L Bottles with Your Coordinates

python test_bottles_with_coordinates.py

Test Individual Coordinate

python run_optimization.py \
  --coordinates "31.3,34.0" \
  --container-volume 2.0 \
  --container-mass 1.0 \
  --target-success 0.8

Compare Bottles vs Jerry Cans

# 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.5

The animations will show you exactly how the containers move hour by hour through the Mediterranean, and why some paths succeed while others fail! 🌊