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#
Download the GSHHG shapefile package gshhg-shp-2.3.7.zip from the NOAA website.
After unzipping, you will find a directory named
GSHHS_shpcontaining 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
For
ROMS-Tools, you only need the Level-1 (L1) shapefiles, which contain coastline polygons. (L2–L6represent lakes, rivers, and other inland water bodies and are not required.)Make sure all four
L1companion files are present, for example:GSHHS_f_L1.dbfGSHHS_f_L1.prjGSHHS_f_L1.shpGSHHS_f_L1.shx
Even though you only point ROMS-Tools to the
.shpfile (e.g.,GSHHS_f_L1.shp), the other files must be in the same directory. See this notebook for an example.
Downloading GLORYS data#
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-Toolsis not wired up yet — the local-file path currently expects NetCDF. Usefmt="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.
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>)
[ ]: