Usage Guide

Basic Usage

Single Plotfile

Load a single AMReX plotfile at a specific level:

import xarray as xr

# Load level 0 (base resolution)
ds = xr.open_dataset('plt00000', engine='amrex', level=0)

# Access variables
temperature = ds['temp']
salinity = ds['salt']

print(f"Variables: {list(ds.data_vars)}")
print(f"Dimensions: {dict(ds.dims)}")

Time Series

Load multiple plotfiles as a time series:

from glob import glob

# Get sorted list of plotfiles
files = sorted(glob('simulation/plt*'))

# Load as time series
ds = xr.open_dataset(files, engine='amrex', level=0)

print(f"Time steps: {len(ds.ocean_time)}")
print(f"Time range: {ds.ocean_time.values[0]} to {ds.ocean_time.values[-1]}")

C-Grid Staggered Variables

The backend automatically detects and handles C-grid staggered variables:

Variable Types

ds = xr.open_dataset('plt00000', engine='amrex', level=0)

# Rho-points (cell centers)
temp = ds['temp']     # (ocean_time, z_rho, y_rho, x_rho)
salt = ds['salt']

# U-points (x-face centers)
u_vel = ds['u_vel']   # (ocean_time, z_rho, y_u, x_u)

# V-points (y-face centers)
v_vel = ds['v_vel']   # (ocean_time, z_rho, y_v, x_v)

# W-points (z-face centers)
w_vel = ds['w_vel']   # (ocean_time, z_w, y_w, x_w)

# 2D variables
zeta = ds['zeta']     # (ocean_time, y_rho, x_rho)

xgcm Integration

Use xgcm for grid-aware operations:

import xgcm

# Create grid object
grid = xgcm.Grid(ds, periodic=False)

# Interpolate to u-points
temp_at_u = grid.interp(ds.temp, 'X')

# Calculate horizontal divergence
div_h = grid.diff(ds.u_vel, 'X') + grid.diff(ds.v_vel, 'Y')

Multi-Level AMR

Loading Different Levels

Load any refinement level with proper coordinate scaling:

# Level 0: Base resolution
ds0 = xr.open_dataset(files, engine='amrex', level=0)

# Level 1: Refined (typically 2x or 3x finer)
ds1 = xr.open_dataset(files, engine='amrex', level=1)

print(f"Level 0: {dict(ds0.dims)}")
print(f"Level 1: {dict(ds1.dims)}")

# Coordinates are properly scaled
dx0 = (ds0.x_rho[1] - ds0.x_rho[0]).values
dx1 = (ds1.x_rho[1] - ds1.x_rho[0]).values
print(f"Refinement ratio: {dx0 / dx1:.1f}x")

Level Masking

When a level doesn’t exist at certain time steps, those times are filled with NaN:

# If some files don't have level 1
# plt00000: only level 0
# plt00100: levels 0 and 1
# plt00200: levels 0 and 1

ds_level1 = xr.open_dataset(['plt00000', 'plt00100', 'plt00200'],
                            engine='amrex', level=1)

# Time step 0: All NaN (level doesn't exist)
# Time steps 1-2: Valid data where level 1 exists

Coordinates

Coordinates reflect actual physical locations at each level:

# Level 0 covers full domain with coarse resolution
print(f"Level 0 x-range: {ds0.x_rho.min().values:.0f} to {ds0.x_rho.max().values:.0f}")

# Level 1 covers refined patch with fine resolution
print(f"Level 1 x-range: {ds1.x_rho.min().values:.0f} to {ds1.x_rho.max().values:.0f}")

Advanced Features

Lazy Loading

Data is loaded lazily using dask arrays:

ds = xr.open_dataset(files, engine='amrex', level=0)

# No data loaded yet - just metadata
temp = ds['temp']
print(type(temp.data))  # dask.array.core.Array

# Load only a subset
subset = temp.isel(ocean_time=0, z_rho=0).compute()

# Or select region before loading
region = temp.sel(x_rho=slice(0, 50000)).compute()

Drop Variables

Exclude variables to save memory:

ds = xr.open_dataset(
    files,
    engine='amrex',
    level=0,
    drop_variables=['w_vel', 'AKt']
)

Working with Large Datasets

# Load metadata only (fast)
ds = xr.open_dataset(files, engine='amrex', level=0)

# Select subset before computing
recent = ds.isel(ocean_time=slice(-10, None))  # Last 10 time steps
surface = recent.sel(z_rho=0)                  # Surface only
result = surface.compute()                     # Load only this subset

Common Patterns

Surface Temperature Time Series

files = sorted(glob('ocean_out/plt*'))
ds = xr.open_dataset(files, engine='amrex', level=0)

# Get surface temperature over time
surf_temp = ds.temp.sel(z_rho=0)

# Plot mean temperature
import matplotlib.pyplot as plt
surf_temp.mean(dim=['y_rho', 'x_rho']).plot()
plt.ylabel('Temperature')
plt.show()

Compare Refinement Levels

ds0 = xr.open_dataset(files, engine='amrex', level=0)
ds1 = xr.open_dataset(files, engine='amrex', level=1)

# Compare resolutions
print(f"Level 0: {len(ds0.x_rho)} × {len(ds0.y_rho)} cells")
print(f"Level 1: {len(ds1.x_rho)} × {len(ds1.y_rho)} cells")

# Compare cell sizes
dx0 = (ds0.x_rho[1] - ds0.x_rho[0]).values
dx1 = (ds1.x_rho[1] - ds1.x_rho[0]).values
print(f"Cell size ratio: {dx0/dx1:.1f}")

Extract Refined Region

# Get data from refined patch
ds1 = xr.open_dataset(files, engine='amrex', level=1)

# Level 1 only exists in a subregion
valid_data = ds1.temp.where(~ds1.temp.isnull())

print(f"Refined region x: {ds1.x_rho.min().values:.0f} to {ds1.x_rho.max().values:.0f}")
print(f"Refined region y: {ds1.y_rho.min().values:.0f} to {ds1.y_rho.max().values:.0f}")

Dataset Structure

A typical dataset has this structure:

<xarray.Dataset>
Dimensions:      (ocean_time: 2, z_rho: 32, y_rho: 48, x_rho: 38, ...)
Coordinates:
  * ocean_time   (ocean_time) float64 0.0 1000.0
  * z_rho        (z_rho) float64 -19.69 -18.91 ... -0.3125
  * y_rho        (y_rho) float64 1042 3125 ... 98958
  * x_rho        (x_rho) float64 1053 3158 ... 78947
    y_u          (y_u) float64 1042 3125 ... 98958
    x_u          (x_u) float64 0.0 2105 ... 80000
    y_v          (y_v) float64 0.0 2083 ... 100000
    x_v          (x_v) float64 1053 3158 ... 78947
    z_w          (z_w) float64 -20.0 -19.38 ... 0.0
Data variables:
    temp         (ocean_time, z_rho, y_rho, x_rho) float64 dask.array
    salt         (ocean_time, z_rho, y_rho, x_rho) float64 dask.array
    u_vel        (ocean_time, z_rho, y_u, x_u) float64 dask.array
    v_vel        (ocean_time, z_rho, y_v, x_v) float64 dask.array
    w_vel        (ocean_time, z_w, y_w, x_w) float64 dask.array
Attributes:
    level:              0
    max_level_ever:     1
    dimensionality:     3