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

  1. Compute angular momentum from u/v velocity fields

  2. Normalize by distance from potential center

  3. Find local extrema as eddy centers

  4. Extract contours around centers

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

  1. Read the In-Depth Guide guide for detailed explanations

  2. Explore the examples gallery

  3. Check the Library for API documentation

  4. See Commandline interface for all command-line options

Common Questions#

How do I choose window sizes?

Start with window_center around the Rossby radius of deformation (~50 km mid-latitudes, ~20 km tropics) and window_fit around 10 times that.

Why aren’t eddies detected?

Check that your velocity fields have sufficient resolution and magnitude. Try reducing min_radius or adjusting window_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_distance around 50-100 km. For weekly data, increase accordingly.

What about 3D eddies?

See Eddy Detection for 3D detection across depth levels.