Download Instructions#

This page covers how to download the datasets required by ROMS-Tools. For an overview of all datasets and their required fields, see the Datasets page.

Downloading GSHHG coastline data#

  1. Download the GSHHG shapefile package gshhg-shp-2.3.7.zip from the NOAA website.

  2. After unzipping, you will find a directory named GSHHS_shp containing five subdirectories (c, f, h, i, l). Each corresponds to a different resolution of the coastline data:

    • f (full): highest-resolution, original dataset

    • h (high): ~80% reduction in detail and size

    • i (intermediate): additional ~80% reduction

    • l (low): additional ~80% reduction

    • c (crude): additional ~80% reduction

  3. For ROMS-Tools, you only need the Level-1 (L1) shapefiles, which contain coastline polygons. (L2L6 represent lakes, rivers, and other inland water bodies and are not required.)

    Make sure all four L1 companion files are present, for example:

    • GSHHS_f_L1.dbf

    • GSHHS_f_L1.prj

    • GSHHS_f_L1.shp

    • GSHHS_f_L1.shx

    Even though you only point ROMS-Tools to the .shp file (e.g., GSHHS_f_L1.shp), the other files must be in the same directory. See this notebook for an example.

Downloading GLORYS data#

You can download GLORYS data from the Copernicus Marine Data Store.
To access the data, register for a Copernicus Marine Service account to obtain a username and password.

Once registered, install the copernicusmarine package to download the datasets:

pip install copernicusmarine
[1]:
import copernicusmarine
/Users/noraloose/miniconda3/envs/romstools-test/lib/python3.13/site-packages/requests/__init__.py:86: RequestsDependencyWarning: Unable to find acceptable character detection dependency (chardet or charset_normalizer).
  warnings.warn(

When you first log in with copernicusmarine, your credentials are saved in a .copernicusmarine-credentials file. This one-time setup gives you seamless access to all Copernicus Marine services without re-entering credentials.

copernicusmarine.login(username="YOUR_USERNAME", password="YOUR_PASSWORD")

Downloading global data#

This example demonstrates how to download the global GLORYS dataset for a specified time range, defined by start_time and end_time. In this case, we select January 2012.

[2]:
from datetime import datetime
[3]:
start_time = datetime(2012, 1, 1)
end_time = datetime(2012, 2, 1)
[4]:
%%time

copernicusmarine.subset(
    dataset_id="cmems_mod_glo_phy_my_0.083deg_P1D-m",
    variables=["thetao", "so", "uo", "vo", "zos"],
    minimum_longitude=None, # global data
    maximum_longitude=None, # global data
    minimum_latitude=None, # global data
    maximum_latitude=None, # global data
    start_datetime=start_time,
    end_datetime=end_time,
    coordinates_selection_method="outside",
    output_filename = "global_GLORYS_Jan2012.nc",
    output_directory = "source-data"
)
INFO - 2025-09-23T20:35:31Z - Selected dataset version: "202311"
INFO - 2025-09-23T20:35:31Z - Selected dataset part: "default"
INFO - 2025-09-23T20:35:33Z - Starting download. Please wait...
INFO - 2025-09-24T18:49:02Z - Successfully downloaded to source-data/global_GLORYS_Jan2012.nc
CPU times: user 17min 22s, sys: 15min 31s, total: 32min 53s
Wall time: 22h 13min 35s
[4]:
ResponseSubset(file_path=PosixPath('source-data/global_GLORYS_Jan2012.nc'), output_directory=PosixPath('source-data'), filename='global_GLORYS_Jan2012.nc', file_size=138819.17330534352, data_transfer_size=630106.1276335877, variables=['thetao', 'so', 'uo', 'vo', 'zos'], coordinates_extent=[GeographicalExtent(minimum=-180.0, maximum=179.9166717529297, unit='degrees_east', coordinate_id='longitude'), GeographicalExtent(minimum=-80.0, maximum=90.0, unit='degrees_north', coordinate_id='latitude'), TimeExtent(minimum='2012-01-01T00:00:00+00:00', maximum='2012-02-02T00:00:00+00:00', unit='iso8601', coordinate_id='time'), GeographicalExtent(minimum=0.49402499198913574, maximum=5727.9169921875, unit='m', coordinate_id='depth')], status='000', message='The request was successful.', file_status='DOWNLOADED')

Downloading a spatial subset#

If you don’t want to download the entire global dataset (which can be very time-consuming) you can instead download a spatial subset of GLORYS data for a specific domain. This requires specifying minimum_longitude, maximum_longitude, minimum_latitude, and maximum_latitude.

Because ROMS grids (at least those created by ROMS-Tools) are not regular lat-lon grids, determining these bounds is not straightforward. Additionally, ROMS-Tools requires a safety margin to perform lateral fill and regridding, which helps prevent boundary artifacts. ROMS-Tools provides a function that can compute appropriate bounds given a grid.

[5]:
from roms_tools import Grid
[6]:
grid = Grid(
    nx=100,  # number of grid points in x-direction
    ny=80,  # number of grid points in y-direction
    size_x=2000,  # domain size in x-direction (in km)
    size_y=1600,  # domain size in y-direction (in km)
    center_lon=-89,  # longitude of the center of the domain
    center_lat=24,  # latitude of the center of the domain
    rot=0,  # rotation of the grid (in degrees)
    N=20,  # number of vertical layers
)
[7]:
from roms_tools import get_glorys_bounds
[8]:
bounds = get_glorys_bounds(grid)
[9]:
bounds
[9]:
{'minimum_latitude': 14.75,
 'maximum_latitude': 33.0,
 'minimum_longitude': 258.75,
 'maximum_longitude': 283.25}
[10]:
%%time

copernicusmarine.subset(
    dataset_id="cmems_mod_glo_phy_my_0.083deg_P1D-m",
    variables=["thetao", "so", "uo", "vo", "zos"],
    **bounds, # regional data
    start_datetime=start_time,
    end_datetime=end_time,
    coordinates_selection_method="outside",
    output_filename = "GoM_GLORYS_Jan2012.nc",
    output_directory = "source-data"
)
INFO - 2025-09-24T20:23:38Z - Selected dataset version: "202311"
2025-09-24 20:23:38 - INFO - Selected dataset version: "202311"
INFO - 2025-09-24T20:23:38Z - Selected dataset part: "default"
2025-09-24 20:23:38 - INFO - Selected dataset part: "default"
INFO - 2025-09-24T20:23:40Z - Starting download. Please wait...
2025-09-24 20:23:40 - INFO - Starting download. Please wait...
INFO - 2025-09-24T21:03:21Z - Successfully downloaded to source-data/GoM_GLORYS_Jan2012.nc
2025-09-24 21:03:21 - INFO - Successfully downloaded to source-data/GoM_GLORYS_Jan2012.nc
CPU times: user 1min 21s, sys: 1min 8s, total: 2min 30s
Wall time: 39min 50s
[10]:
ResponseSubset(file_path=PosixPath('source-data/GoM_GLORYS_Jan2012.nc'), output_directory=PosixPath('source-data'), filename='GoM_GLORYS_Jan2012.nc', file_size=1021.8164351145039, data_transfer_size=52508.84396946565, variables=['thetao', 'so', 'uo', 'vo', 'zos'], coordinates_extent=[GeographicalExtent(minimum=-101.25, maximum=-76.75, unit='degrees_east', coordinate_id='longitude'), GeographicalExtent(minimum=14.75, maximum=33.0, unit='degrees_north', coordinate_id='latitude'), TimeExtent(minimum='2012-01-01T00:00:00+00:00', maximum='2012-02-02T00:00:00+00:00', unit='iso8601', coordinate_id='time'), GeographicalExtent(minimum=0.49402499198913574, maximum=5727.9169921875, unit='m', coordinate_id='depth')], status='000', message='The request was successful.', file_status='DOWNLOADED')

Downloading ERA5 data#

ROMS-Tools can stream ERA5 directly from the cloud (the analysis-ready ARCO ERA5 store on Google Cloud), so downloading is usually optional. To use ERA5 offline (e.g. on an HPC system without internet, or to avoid re-streaming), you can pre-save a subset to local files with xarray. (The official Copernicus Climate Data Store landing page is here; the ARCO store contains the same ERA5 data in cloud-optimized form and is far faster to subset than the queued CDS API.)

Saving from the cloud requires gcsfs:

pip install gcsfs

The example below writes one file per day for January 2012 — edit start_date/end_date to cover any period, and set fmt to "netcdf" or "zarr". Daily files keep each download small and resumable, and ROMS-Tools recombines them via a glob when reading. Global hourly ERA5 is large (~0.8 GB/day globally), so uncomment the spatial subset to save just your region.

import os

import pandas as pd
import xarray as xr

# --- Edit these to change the period (saves one file per day; any length) ---
start_date = "2012-01-01"
end_date = "2012-01-31"  # inclusive

fmt = "netcdf"  # "netcdf" or "zarr"
output_dir = "source-data"

# ARCO ERA5 uses long variable names; rename to the short names that
# ROMS-Tools expects when reading ERA5 from a local file.
era5_vars = {
    "10m_u_component_of_wind": "u10",
    "10m_v_component_of_wind": "v10",
    "surface_net_solar_radiation": "ssr",
    "surface_thermal_radiation_downwards": "strd",
    "2m_temperature": "t2m",
    "2m_dewpoint_temperature": "d2m",
    "total_precipitation": "tp",
    "sea_surface_temperature": "sst",
}

# Open the cloud store once, then write one file per day.
ds = xr.open_zarr(
    "gs://gcp-public-data-arco-era5/ar/full_37-1h-0p25deg-chunk-1.zarr-v3",
    chunks={},
    consolidated=None,
    storage_options={"token": "anon"},
)
ds = ds[list(era5_vars)].rename(era5_vars)
# Drop the source's chunk/compressor encoding so it doesn't carry over on write
# (the ARCO store's codecs are otherwise rejected when writing Zarr).
ds = ds.drop_encoding()

# Optional: subset to your region to shrink the download (leave a few degrees of
# margin around the ROMS domain). ERA5 longitude is 0-360, latitude is descending.
# ds = ds.sel(latitude=slice(33, 15), longitude=slice(258, 283))

os.makedirs(output_dir, exist_ok=True)

for day in pd.date_range(start_date, end_date, freq="D"):
    day_data = ds.sel(time=slice(day, day + pd.Timedelta(hours=23)))
    stamp = day.strftime("%Y-%m-%d")  # zero-padded so files sort chronologically
    if fmt == "netcdf":
        day_data.to_netcdf(f"{output_dir}/ERA5_{stamp}.nc")
    else:  # zarr
        day_data.to_zarr(f"{output_dir}/ERA5_{stamp}.zarr", mode="w")
    print(f"saved ERA5_{stamp}")

Point SurfaceForcing at the daily files with a glob, and set use_dask=True so they load lazily and chunked. ROMS-Tools applies the same processing as the cloud path, so results are identical to streaming:

from datetime import datetime

from roms_tools import Grid, SurfaceForcing

sf = SurfaceForcing(
    grid=grid,
    start_time=datetime(2012, 1, 1),
    end_time=datetime(2012, 2, 1),
    source={"name": "ERA5", "path": "source-data/ERA5_*.nc"},
    type="physics",
    use_dask=True,
)

The zero-padded YYYY-MM-DD names sort chronologically, so the glob concatenates the files in time order.

Note: reading local Zarr stores back through ROMS-Tools is not wired up yet — the local-file path currently expects NetCDF. Use fmt="netcdf" for input for now (local-Zarr support would be a small, separate change).

Downloading the Unified BGC Dataset#

This section demonstrates how to download a unified biogeochemical (BGC) climatology, which integrates multiple observational and model-based sources:

  • Nutrients (NO₃⁻, PO₄³⁻, SiO₄⁴⁻) and dissolved oxygen from the 2018 World Ocean Atlas

  • Dissolved iron (Fe) and nitrous oxide (N₂O) from in-situ measurements

  • Dissolved inorganic carbon (DIC) and total alkalinity (ALK) from the GLODAPv2 global product

  • Other nutrients (ammonium NH₄⁺, nitrite NO₂⁻, organic nitrogen) and dissolved organic matter (DOM) from CESM model simulations

The dataset is hosted on Google Drive and can be downloaded using the following procedure.

[11]:
import gdown
import os
[12]:
url = "https://drive.google.com/uc?id=1wUNwVeJsd6yM7o-5kCx-vM3wGwlnGSiq"
[13]:
output_dir = "source-data"
[14]:
os.makedirs(output_dir, exist_ok=True)
[15]:
gdown.download(url, f"{output_dir}/BGCdataset.nc", quiet=False)
Downloading...
From (original): https://drive.google.com/uc?id=1wUNwVeJsd6yM7o-5kCx-vM3wGwlnGSiq
From (redirected): https://drive.google.com/uc?id=1wUNwVeJsd6yM7o-5kCx-vM3wGwlnGSiq&confirm=t&uuid=7bd915fc-672a-4b7f-b5f1-5282d7f3cb7a
To: /Users/noraloose/roms-tools/docs/source-data/BGCdataset.nc
100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 21.4G/21.4G [32:01<00:00, 11.1MB/s]
[15]:
'source-data/BGCdataset.nc'

Handling File Download Limits#

Note

If you encounter a FileURLRetrievalError, it usually means the file has been accessed or downloaded too many times recently. This often happens with large files or files shared by many users.

Workaround: Download the file manually using the following link: unified BGC dataset

After downloading, place the file in the appropriate directory for your workflow.

Downloading WOA salinity data#

This section instructs how to download the World Ocean Atlas salinity data from the NOAA website. It is a collection of salinity (and other variables) means based on profile data from the World Ocean Database (WOD). The salinity data used in ROMS-Tools, needs to be gridded data. The s_an variable provided is the ‘Objectively analyzed mean fields for sea_water_salinity’ and is needed by ROMS-Tools.

To download the needed 12 months of data for a climatology record, files with suffixes from s01-s12 are needed. In this notebook, we use the decadal averaged data (i.e. files with suffix including decav.

  • The 2018 Atlas from the notebook mentioned above can be found here.

  • Likewise, the 2023 Atlas can be found here.

Downloading the MBL co2 Dataset#

This section demonstrates how to download a time-varying CO2 dataset from NOAA’s GML, Marine Boundary Layer Reference, which integrates observations from a subset of sites from the their Cooperative Global Air Sampling Network. After processing, their data are available approximately weekly (7.6 days).

The dataset can be downloaded using the following procedure.

[16]:
import urllib.request
[17]:
url = "https://gml.noaa.gov/ccgg/mbl/tmp/co2_GHGreference.1785677502_surface.txt"
[18]:
urllib.request.urlretrieve(url, "co2_GHGreference.1785677502_surface.txt")
[18]:
('co2_GHGreference.1785677502_surface.txt',
 <http.client.HTTPMessage at 0x1546fb9b1450>)

Downloading the OceanSODA-ETHZ Dataset#

This section demonstrates how to download a monthly dataset from NOAA’s NCEI. This dataset is calculated from machine learning estimates of Total Alkalinity (TA) and the fugacity of carbon dioxide (fCO2), and the sea surface DIC and ALK data are used for restoring forces in ROMS.

The dataset can be downloaded using the following procedure.

[19]:
import urllib.request
[20]:
url = "https://www.ncei.noaa.gov/data/oceans/archive/arc0160/0220059/6.6/data/0-data/OceanSODA_ETHZ-v2025.OCADS.01-1982-2024.nc"
[21]:
urllib.request.urlretrieve(url, "OceanSODA_ETHZ-v2025.OCADS.01-1982-2024.nc")
[21]:
('OceanSODA_ETHZ-v2025.OCADS.01-1982-2024.nc',
 <http.client.HTTPMessage at 0x1546fb774c30>)
[ ]: