Creating the tidal forcing#

[1]:
from roms_tools import Grid

We first create our grid object. In this example, the grid contains Iceland and has a horizontal resolution of 1000km/500 = 2km with 100 vertical layers.

[2]:
grid = Grid(
    nx=500,
    ny=500,
    size_x=1000,
    size_y=1000,
    center_lon=-20,
    center_lat=65,
    rot=0,
    N=100,
    topography_source={
        "name": "SRTM15",
        "path": "/anvil/projects/x-ees250129/Datasets/SRTM15/SRTM15_V2.6.nc",
    },
)

Given our grid, our goal is now to create the necessary tidal forcing fields to run a ROMS simulation.

Tidal forcing is based on the TPXO atlas and uses three key files:

  • Grid file: TPXO bathymetry and land mask

  • H file: Tidal elevation

  • U file: Tidal transports

ROMS-Tools applies a Self-Attraction and Loading (SAL) correction by subtracting it from the equilibrium tide to compute the tidal potential. This correction, which accounts for Earth deformation and ocean mass redistribution, is sourced internally from TPXO9v2a, as it is not included in the regularly updated TPXO datasets.

The three TPXO files are available on Perlmutter at the following locations:

[3]:
tpxo_path = "/anvil/projects/x-ees250129/Datasets/TPXO/TPXO10.v2a/"
tpxo_dict = {
    "grid": tpxo_path + "grid_tpxo10v2a.nc",
    "h": tpxo_path + "h_tpxo10.v2a.nc",
    "u": tpxo_path + "u_tpxo10.v2a.nc",
}

You can also download your own version. For more details, please refer to this page.

We now create our tidal forcing for a model reference date of January 1st, 2000.

[4]:
from roms_tools import TidalForcing
[5]:
from datetime import datetime
[6]:
model_reference_date = datetime(2000, 1, 1)
[7]:
%%time

tidal_forcing = TidalForcing(
    grid=grid,
    source={"name": "TPXO", "path": tpxo_dict},
    ntides=15,  # Number of constituents to consider <= 15. Default is 10.
    model_reference_date=model_reference_date,  # Model reference date. Default is January 1, 2000.
    use_dask=True
)
Downloading file 'sal_tpxo10.v2a.nc' from 'https://github.com/CWorthy-ocean/roms-tools-data/raw/main/sal_tpxo10.v2a.nc' to '/home/x-smaticka/.cache/roms-tools'.
CPU times: user 1min 43s, sys: 4.02 s, total: 1min 47s
Wall time: 25.5 s

Note

In the cell above, we created our tidal forcing with use_dask = True. This enables Dask, a Python library designated to facilitate scalable, out-of-memory data processing by distributing computations across multiple threads or processes. Here you can learn more about using Dask with ROMS-Tools.

To see the values of the tidal forcing variables we can examine the xarray.Dataset object returned by the .ds property.

[8]:
tidal_forcing.ds
[8]:
<xarray.Dataset> Size: 121MB
Dimensions:  (ntides: 15, eta_rho: 502, xi_rho: 502, xi_u: 501, eta_v: 501)
Coordinates:
  * ntides   (ntides) |S3 45B b'm2' b's2' b'n2' b'k2' ... b'ms4' b'2n2' b's1'
    omega    (ntides) float64 120B 0.0001405 0.0001454 ... 0.0001352 7.272e-05
Dimensions without coordinates: eta_rho, xi_rho, xi_u, eta_v
Data variables:
    ssh_Re   (ntides, eta_rho, xi_rho) float32 15MB dask.array<chunksize=(1, 502, 502), meta=np.ndarray>
    ssh_Im   (ntides, eta_rho, xi_rho) float32 15MB dask.array<chunksize=(1, 502, 502), meta=np.ndarray>
    pot_Re   (ntides, eta_rho, xi_rho) float32 15MB dask.array<chunksize=(1, 502, 502), meta=np.ndarray>
    pot_Im   (ntides, eta_rho, xi_rho) float32 15MB dask.array<chunksize=(1, 502, 502), meta=np.ndarray>
    u_Re     (ntides, eta_rho, xi_u) float32 15MB dask.array<chunksize=(1, 502, 501), meta=np.ndarray>
    u_Im     (ntides, eta_rho, xi_u) float32 15MB dask.array<chunksize=(1, 502, 501), meta=np.ndarray>
    v_Re     (ntides, eta_v, xi_rho) float32 15MB dask.array<chunksize=(1, 501, 502), meta=np.ndarray>
    v_Im     (ntides, eta_v, xi_rho) float32 15MB dask.array<chunksize=(1, 501, 502), meta=np.ndarray>
Attributes:
    title:                 ROMS tidal forcing created by ROMS-Tools
    roms_tools_version:    3.6.1.dev31+g34326e51e
    source:                TPXO
    model_reference_date:  2000-01-01 00:00:00

Plotting the tidal forcing fields#

We can also plot any of the tidal forcing fields via the .plot method.

[9]:
tidal_forcing.plot("ssh_Re", ntides=0)  # first tidal constituent
[########################################] | 100% Completed | 203.07 ms
_images/tides_15_1.png
[10]:
tidal_forcing.plot("pot_Im", ntides=0)  # first tidal constituent
[########################################] | 100% Completed | 102.58 ms
_images/tides_16_1.png
[11]:
tidal_forcing.plot("u_Re", ntides=0)  # first tidal constituent
[########################################] | 100% Completed | 305.52 ms
_images/tides_17_1.png
[12]:
tidal_forcing.plot("v_Re", ntides=9)  # tenth tidal constituent
[########################################] | 100% Completed | 204.66 ms
_images/tides_18_1.png

Saving as NetCDF or YAML file#

Finally, we can save our tidal forcing as a netCDF file via the .save method.

[13]:
filepath = "/anvil/scratch/x-smaticka/forcing/my_tidal_forcing.nc"
[14]:
%time tidal_forcing.save(filepath)
2026-06-18 17:51:59 - INFO - Writing the following NetCDF files:
/anvil/scratch/x-smaticka/forcing/my_tidal_forcing.nc
[########################################] | 100% Completed | 4.46 sms
CPU times: user 2min 47s, sys: 1.17 s, total: 2min 48s
Wall time: 4.77 s
[14]:
[PosixPath('/anvil/scratch/x-smaticka/forcing/my_tidal_forcing.nc')]

We can also export the parameters of our TidalForcing object to a YAML file.

[15]:
yaml_filepath = "/anvil/scratch/x-smaticka/forcing/my_tidal_forcing.yaml"
[16]:
tidal_forcing.to_yaml(yaml_filepath)
[17]:
# Open and read the YAML file
with open(yaml_filepath, "r") as file:
    file_contents = file.read()

# Print the contents
print(file_contents)
---
roms_tools_version: 3.6.1.dev31+g34326e51e
---
Grid:
  nx: 500
  ny: 500
  size_x: 1000
  size_y: 1000
  center_lon: -20
  center_lat: 65
  rot: 0
  N: 100
  theta_s: 5.0
  theta_b: 2.0
  hc: 300.0
  topography_source:
    name: SRTM15
    path: /anvil/projects/x-ees250129/Datasets/SRTM15/SRTM15_V2.6.nc
  mask_shapefile: null
  close_narrow_channels: false
  hmin: 5.0
  filename: null
TidalForcing:
  source:
    name: TPXO
    path:
      grid: /anvil/projects/x-ees250129/Datasets/TPXO/TPXO10.v2a/grid_tpxo10v2a.nc
      h: /anvil/projects/x-ees250129/Datasets/TPXO/TPXO10.v2a/h_tpxo10.v2a.nc
      u: /anvil/projects/x-ees250129/Datasets/TPXO/TPXO10.v2a/u_tpxo10.v2a.nc
  ntides: 15
  model_reference_date: '2000-01-01T00:00:00'
  bypass_validation: false

Creating tidal forcing from an existing YAML file#

[18]:
%time the_same_tidal_forcing = TidalForcing.from_yaml(yaml_filepath, use_dask=True)
CPU times: user 3min 16s, sys: 3.14 s, total: 3min 20s
Wall time: 20 s
[19]:
the_same_tidal_forcing.ds
[19]:
<xarray.Dataset> Size: 121MB
Dimensions:  (ntides: 15, eta_rho: 502, xi_rho: 502, xi_u: 501, eta_v: 501)
Coordinates:
  * ntides   (ntides) |S3 45B b'm2' b's2' b'n2' b'k2' ... b'ms4' b'2n2' b's1'
    omega    (ntides) float64 120B 0.0001405 0.0001454 ... 0.0001352 7.272e-05
Dimensions without coordinates: eta_rho, xi_rho, xi_u, eta_v
Data variables:
    ssh_Re   (ntides, eta_rho, xi_rho) float32 15MB dask.array<chunksize=(1, 502, 502), meta=np.ndarray>
    ssh_Im   (ntides, eta_rho, xi_rho) float32 15MB dask.array<chunksize=(1, 502, 502), meta=np.ndarray>
    pot_Re   (ntides, eta_rho, xi_rho) float32 15MB dask.array<chunksize=(1, 502, 502), meta=np.ndarray>
    pot_Im   (ntides, eta_rho, xi_rho) float32 15MB dask.array<chunksize=(1, 502, 502), meta=np.ndarray>
    u_Re     (ntides, eta_rho, xi_u) float32 15MB dask.array<chunksize=(1, 502, 501), meta=np.ndarray>
    u_Im     (ntides, eta_rho, xi_u) float32 15MB dask.array<chunksize=(1, 502, 501), meta=np.ndarray>
    v_Re     (ntides, eta_v, xi_rho) float32 15MB dask.array<chunksize=(1, 501, 502), meta=np.ndarray>
    v_Im     (ntides, eta_v, xi_rho) float32 15MB dask.array<chunksize=(1, 501, 502), meta=np.ndarray>
Attributes:
    title:                 ROMS tidal forcing created by ROMS-Tools
    roms_tools_version:    3.6.1.dev31+g34326e51e
    source:                TPXO
    model_reference_date:  2000-01-01 00:00:00
[ ]:

[ ]: