Quick Start Guide#
Welcome to shoot! This guide will help you get started with detecting and tracking ocean eddies.
What is shoot?#
SHom Ocean Objects Tracker (shoot) is a Python package for detecting and tracking ocean features from observations or simulated data.
Currently implemented features:
Detect eddies using the Local Normalized Angular Momentum (LNAM) method
Track eddies through time using optimal matching algorithms
Analyze 3D eddy structure across depth levels
Additional features (fronts, acoustic impacts, profile associations, etc.) are planned for future releases.
Basic Concepts#
Eddies#
Eddies are rotating water masses that can be detected from velocity fields or sea surface height (SSH) anomalies. shoot identifies:
Cyclonic eddies - rotating counterclockwise in Northern Hemisphere (positive LNAM)
Anticyclonic eddies - rotating clockwise in Northern Hemisphere (negative LNAM)
Each detected eddy includes:
Center position (lon, lat)
Radius (at maximum velocity contour)
Rossby number (intensity measure)
Ellipse fit parameters
Speed contours and enclosed areas
Eddy Detection Method#
shoot uses the LNAM method (Local Normalized Angular Momentum):
Compute angular momentum from u/v velocity fields
Normalize by distance from potential center
Find local extrema as eddy centers
Extract contours around centers
Fit ellipses and compute properties
Eddy Tracking#
Eddy tracking associates eddies between consecutive time steps:
Uses cost function based on distance and similarity
Employs Hungarian algorithm for optimal matching
Creates trajectories over time
Tracks eddy evolution (radius, intensity)
Installation#
From the repository:
cd shoot/
pip install -e .
This installs shoot and its dependencies including xarray, numpy, scipy, and xoa.
First Detection Example#
Let’s detect eddies from satellite altimetry data.
Load Data#
import xarray as xr
from shoot.eddies.eddies2d import Eddies2D
from shoot.samples import get_sample_file
# Load sample satellite data
path = get_sample_file("OBS/SATELLITE/jan2024_ionian_sea_duacs.nc")
ds = xr.open_dataset(path).isel(time=0)
# Data contains ugos (zonal velocity) and vgos (meridional velocity)
print(ds)
Configure Detection#
Set detection parameters:
# Window size (km) for computing LNAM and finding centers
window_center = 50 # Typically ~Rossby radius
# Window size (km) for fitting SSH and diagnostics
window_fit = 120 # Typically ~10 * Rossby radius
# Minimum eddy radius (km) to retain
min_radius = 10 # Around Rossby radius of deformation
# Maximum ellipse fitting error (fraction)
ellipse_error = 0.05 # 5% error tolerance
Run Detection#
# Detect eddies
eddies = Eddies2D.detect_eddies(
ds.ugos, # Zonal velocity
ds.vgos, # Meridional velocity
window_center, # Center detection window
window_fit=window_fit,
ssh=ds.adt, # Absolute dynamic topography (optional)
min_radius=min_radius,
ellipse_error=ellipse_error,
paral=False # Set True for parallel processing
)
print(f"Detected {len(eddies.eddies)} eddies")
Access Results#
# Loop through detected eddies
for eddy in eddies.eddies:
print(f"Eddy at ({eddy.lon:.2f}, {eddy.lat:.2f})")
print(f" Type: {eddy.eddy_type}") # "cyclone" or "anticyclone"
print(f" Radius: {eddy.radius:.1f} km")
print(f" Rossby number: {eddy.ro:.3f}")
Visualize Results#
import matplotlib.pyplot as plt
from shoot.plot import create_map, pcarr
# Create map
fig, ax = create_map(ds)
# Plot SSH
pcarr(ds.adt, ax=ax, cmap="RdBu_r", vmin=-0.3, vmax=0.3)
# Overlay detected eddies
for eddy in eddies.eddies:
# Plot eddy center
ax.plot(eddy.lon, eddy.lat, 'ko', markersize=8)
# Plot maximum velocity contour
if hasattr(eddy, 'vmax_contour'):
ax.plot(eddy.vmax_contour.lon,
eddy.vmax_contour.lat, 'k-', linewidth=2)
plt.title(f"Detected {len(eddies.eddies)} eddies")
plt.show()
First Tracking Example#
Track eddies through multiple time steps.
Load Time Series#
from shoot.eddies.track import Track
# Load data with time dimension
path = get_sample_file("OBS/SATELLITE/jan2024_ionian_sea_duacs.nc")
ds = xr.open_dataset(path)
Detect at Each Time Step#
# Store detected eddies for each time
eddies_list = []
for t in range(len(ds.time)):
ds_t = ds.isel(time=t)
eddies_t = Eddies2D.detect_eddies(
ds_t.ugos, ds_t.vgos,
window_center=50,
window_fit=120,
min_radius=10
)
eddies_list.append(eddies_t)
print(f"Detected eddies at {len(eddies_list)} time steps")
Track Eddies#
# Initialize tracking
tracks = []
# First time step - create new tracks
for eddy in eddies_list[0].eddies:
eddy.track_id = len(tracks)
tracks.append(Track(eddy, ds.time[0].values))
# Subsequent time steps - associate and update
for t in range(1, len(eddies_list)):
# Association happens here (see in-depth guide)
# Updates existing tracks or creates new ones
pass # See indepth_tracking for full implementation
Using the Command Line#
shoot provides a CLI for common tasks.
Detect Eddies#
# Detect eddies from a NetCDF file
shoot eddies detect input.nc \
--window-center 50 \
--window-fit 120 \
--min-radius 10 \
-o eddies_detected.nc
Track Eddies#
# Track detected eddies over time
shoot eddies track eddies_detected.nc \
--max-distance 50 \
-o eddies_tracked.nc
Show Diagnostics#
# Display eddy statistics
shoot eddies diags eddies_tracked.nc
Finding coordinates and variables#
shoot uses xoa for metadata handling and CF convention support.
The xoa package provides:
CF-compliant coordinate detection (lon, lat, depth, time)
Standard name searches (SSH, velocities, temperature, salinity)
CROCO model support
Coordinate transformations
shoot includes internal metadata specifications in shoot/meta.cfg that define extended lists of CF standard names for ocean variables. See Metadata Specification for details on the internal specifications.
Example using shoot’s metadata wrappers:
from shoot import meta as smeta
# Automatically find coordinates
lon = smeta.get_lon(ds)
lat = smeta.get_lat(ds)
depth = smeta.get_depth(ds, errors='ignore')
# Find variables by standard name
u = smeta.get_u(ds)
v = smeta.get_v(ds)
ssh = smeta.get_ssh(ds)
You can easily use a configuration file for your own dataset (see Metadata Specification for how to create custom specifications).
From python:
from shoot import meta as smeta
smeta.set_meta_specs("my_config_file.cfg")
From the commandline:
# Detect eddies with custom metadata file
shoot eddies detect --meta-file my_config_file.cfg input.nc -o output.nc
Next Steps#
Now that you’ve run your first detection and tracking:
Read the In-Depth Guide guide for detailed explanations
Explore the examples gallery
Check the Library for API documentation
See Commandline interface for all command-line options
Common Questions#
- How do I choose window sizes?
Start with
window_centeraround the Rossby radius of deformation (~50 km mid-latitudes, ~20 km tropics) andwindow_fitaround 10 times that.- Why aren’t eddies detected?
Check that your velocity fields have sufficient resolution and magnitude. Try reducing
min_radiusor adjustingwindow_center.- Can I use model output?
Yes! shoot works with any gridded velocity data. It supports CROCO native grids and you can provide specifications for your own model.
- How accurate is the tracking?
Tracking accuracy depends on time resolution. For daily data, use
max_distancearound 50-100 km. For weekly data, increase accordingly.- What about 3D eddies?
See Eddy Detection for 3D detection across depth levels.