Creating the initial conditions#
[1]:
from roms_tools import Grid, InitialConditions
We start by creating a grid that contains Iceland and has a horizontal resolution of 2 km and 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",
},
)
Next, we specify the time that we want to make the initial conditions for.
[3]:
from datetime import datetime
[4]:
ini_time = datetime(2012, 1, 2, 12, 0, 0) # noon on January 2, 2012
Physical initial conditions from GLORYS#
In this section, we use GLORYS data to create our physical initial conditions, i.e., temperature, salinity, sea surface height, and velocities. (We will learn how to add biogeochemical initial conditions further down in the notebook.) Our downloaded GLORYS data sits at the following location.
[5]:
path = "/anvil/projects/x-ees250129/Datasets/GLORYS/NA/2012/mercatorglorys12v1_gl12_mean_20120102.nc"
Note
It would also be okay to provide a filename that contains data for more than just the day of interest. ROMS-Tools will pick out the correct day (and complain if the day of interest is not in the provided filename.) Or we can even use wildcards, such as path="/global/cfs/projectdirs/m4746/Datasets/GLORYS/NA/2012/*.nc". Note, however, that ROMS-Tools will operate more efficiently when the filename is as specific as possible.
You can also download your own GLORYS data, which must span the desired ROMS domain and ini_time. For more details, please refer to this page.
We can now create the InitialConditions object.
[6]:
%%time
initial_conditions = InitialConditions(
grid=grid,
ini_time=ini_time,
source={"name": "GLORYS", "path": path},
model_reference_date=datetime(2000, 1, 1), # this is the default
use_dask=True,
)
CPU times: user 32 s, sys: 900 ms, total: 32.9 s
Wall time: 8.05 s
Note
In the cell above, we created our initial conditions 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.
The initial conditions variables are held in an xarray.Dataset that is accessible via the .ds property.
[7]:
initial_conditions.ds
[7]:
<xarray.Dataset> Size: 508MB
Dimensions: (ocean_time: 1, s_rho: 100, eta_rho: 502, xi_rho: 502,
xi_u: 501, eta_v: 501, s_w: 101)
Coordinates:
* ocean_time (ocean_time) float64 8B 3.788e+08
abs_time (ocean_time) datetime64[ns] 8B 2012-01-02T12:00:00
Dimensions without coordinates: s_rho, eta_rho, xi_rho, xi_u, eta_v, s_w
Data variables:
temp (ocean_time, s_rho, eta_rho, xi_rho) float32 101MB dask.array<chunksize=(1, 100, 409, 409), meta=np.ndarray>
salt (ocean_time, s_rho, eta_rho, xi_rho) float32 101MB dask.array<chunksize=(1, 100, 409, 409), meta=np.ndarray>
u (ocean_time, s_rho, eta_rho, xi_u) float32 101MB dask.array<chunksize=(1, 100, 409, 409), meta=np.ndarray>
v (ocean_time, s_rho, eta_v, xi_rho) float32 101MB dask.array<chunksize=(1, 100, 409, 409), meta=np.ndarray>
zeta (ocean_time, eta_rho, xi_rho) float32 1MB -0.7428 ... -0.8718
ubar (ocean_time, eta_rho, xi_u) float32 1MB dask.array<chunksize=(1, 409, 409), meta=np.ndarray>
vbar (ocean_time, eta_v, xi_rho) float32 1MB dask.array<chunksize=(1, 409, 409), meta=np.ndarray>
w (ocean_time, s_w, eta_rho, xi_rho) float32 102MB 0.0 0.0 ... 0.0
Cs_r (s_rho) float32 400B -0.992 -0.9753 ... -8.89e-05 -9.874e-06
Cs_w (s_w) float32 404B -1.0 -0.9837 -0.9667 ... -3.95e-05 0.0
Attributes:
title: ROMS initial conditions file create...
roms_tools_version: 10000.dev361+g2e6c47bd8
ini_time: 2012-01-02 12:00:00
model_reference_date: 2000-01-01 00:00:00
adjust_depth_for_sea_surface_height: False
source: GLORYS
theta_s: 5.0
theta_b: 2.0
hc: 300.0Let’s make some plots! As an example, let’s have a look at the temperature field temp. It is three-dimensional with horizontal dimensions eta_rho and xi_rho, and vertical dimension s_rho.
We first want to plot different layers of the temperature field, i.e., slice along the vertical dimension s.
[8]:
initial_conditions.plot("temp", s=-1) # plot uppermost layer
[########################################] | 100% Completed | 1.01 sms
[9]:
initial_conditions.plot("temp", s=0, depth_contours=True) # plot bottom layer
Next, we slice our domain along one of the horizontal dimensions and look at temperature along these sections.
[10]:
initial_conditions.plot("temp", xi=0, layer_contours=True)
Note that even though we have a total of 100 layers, layer_contours = True will create a plot with a maximum of 10 contours to ensure plot clarity.
[11]:
initial_conditions.plot("temp", eta=50, layer_contours=False)
We can also plot a depth profile at a certain spatial location.
[12]:
initial_conditions.plot("temp", eta=0, xi=0)
Finally, we can look at a transect in a certain layer and at a fixed eta_rho (similarly xi_rho).
[13]:
initial_conditions.plot("temp", eta=0, s=-1)
Plotting velocity fields works similarly.
[14]:
initial_conditions.plot("u", s=-1) # plot uppermost layer
[########################################] | 100% Completed | 1.51 sms
[15]:
initial_conditions.plot("u", eta=0)
[16]:
initial_conditions.plot("u", xi=0)
[17]:
initial_conditions.plot("v", xi=0)
[########################################] | 100% Completed | 1.51 sms
Adding Biogeochemical (BGC) Initial Conditions#
To prepare a ROMS simulation with MARBL biogeochemistry (BGC), we need to generate both physical and biogeochemical initial conditions. Since ROMS requires a single initial conditions file, we create both sets of conditions together.
We use the following datasets:
- Physical Initial Conditions:Derived from GLORYS data, including temperature, salinity, sea surface height, and velocities.
- Biogeochemical (BGC) Initial Conditions:A unified BGC climatology with 1° horizontal resolution, that combines multiple observationally and model based sources:
Nutrient concentrations (nitrate, phosphate, silicate) and dissolved oxygen from the 2018 World Ocean Atlas
Dissolved iron and nitrous oxide from in-situ measurements
Dissolved inorganic carbon (DIC) and alkalinity from the global GLODAPv2 product
Remaining nutrients (ammonium, nitrite, and organic nitrogen) and dissolved organic matter from CESM model simulations
The BGC climatology can be accessed at the following location:
[18]:
unified_bgc_path = "/anvil/projects/x-ees250129/Datasets/UNIFIED/BGCdataset_v2_filled.nc"
The initial conditions are created as above, but now with additional information about the bgc_source.
[19]:
%%time
initial_conditions_with_unified_bgc = InitialConditions(
grid=grid,
ini_time=ini_time,
source={"name": "GLORYS", "path": path},
bgc_source={
"name": "UNIFIED",
"path": unified_bgc_path,
"climatology": True,
}, # bgc_source is optional
model_reference_date=datetime(2000, 1, 1), # this is the default
use_dask=True,
)
/anvil/projects/x-ees250129/x-smaticka/roms-tools/roms_tools/datasets/lat_lon_datasets.py:1716: UserWarning: rename 'lon' to 'longitude' does not create an index anymore. Try using swap_dims instead or use set_index after rename to create an indexed coordinate.
ds = ds.rename({"lon": "longitude", "lat": "latitude", "dep": "depth"})
/anvil/projects/x-ees250129/x-smaticka/roms-tools/roms_tools/datasets/lat_lon_datasets.py:1716: UserWarning: rename 'lat' to 'latitude' does not create an index anymore. Try using swap_dims instead or use set_index after rename to create an indexed coordinate.
ds = ds.rename({"lon": "longitude", "lat": "latitude", "dep": "depth"})
/anvil/projects/x-ees250129/x-smaticka/roms-tools/roms_tools/datasets/lat_lon_datasets.py:1716: UserWarning: rename 'dep' to 'depth' does not create an index anymore. Try using swap_dims instead or use set_index after rename to create an indexed coordinate.
ds = ds.rename({"lon": "longitude", "lat": "latitude", "dep": "depth"})
2026-07-09 14:09:58 - WARNING - Optional variables missing (but not critical): ['Lig', 'DIC_ALT_CO2', 'Alk_ALT_CO2']
CPU times: user 26.1 s, sys: 1.56 s, total: 27.7 s
Wall time: 10.5 s
[20]:
initial_conditions_with_unified_bgc.ds
[20]:
<xarray.Dataset> Size: 4GB
Dimensions: (ocean_time: 1, s_rho: 100, eta_rho: 502, xi_rho: 502,
xi_u: 501, eta_v: 501, s_w: 101)
Coordinates:
abs_time (ocean_time) datetime64[ns] 8B 2012-01-02T12:00:00
* ocean_time (ocean_time) float64 8B 3.788e+08
Dimensions without coordinates: s_rho, eta_rho, xi_rho, xi_u, eta_v, s_w
Data variables: (12/42)
temp (ocean_time, s_rho, eta_rho, xi_rho) float32 101MB dask.array<chunksize=(1, 100, 409, 409), meta=np.ndarray>
salt (ocean_time, s_rho, eta_rho, xi_rho) float32 101MB dask.array<chunksize=(1, 100, 409, 409), meta=np.ndarray>
u (ocean_time, s_rho, eta_rho, xi_u) float32 101MB dask.array<chunksize=(1, 100, 409, 409), meta=np.ndarray>
v (ocean_time, s_rho, eta_v, xi_rho) float32 101MB dask.array<chunksize=(1, 100, 409, 409), meta=np.ndarray>
zeta (ocean_time, eta_rho, xi_rho) float32 1MB dask.array<chunksize=(1, 502, 502), meta=np.ndarray>
ubar (ocean_time, eta_rho, xi_u) float32 1MB dask.array<chunksize=(1, 409, 409), meta=np.ndarray>
... ...
Lig (ocean_time, s_rho, eta_rho, xi_rho) float32 101MB dask.array<chunksize=(1, 100, 409, 409), meta=np.ndarray>
DIC_ALT_CO2 (ocean_time, s_rho, eta_rho, xi_rho) float32 101MB dask.array<chunksize=(1, 100, 409, 409), meta=np.ndarray>
ALK_ALT_CO2 (ocean_time, s_rho, eta_rho, xi_rho) float32 101MB dask.array<chunksize=(1, 100, 409, 409), meta=np.ndarray>
w (ocean_time, s_w, eta_rho, xi_rho) float32 102MB 0.0 ... 0.0
Cs_r (s_rho) float32 400B -0.992 -0.9753 ... -8.89e-05 -9.874e-06
Cs_w (s_w) float32 404B -1.0 -0.9837 -0.9667 ... -3.95e-05 0.0
Attributes:
title: ROMS initial conditions file create...
roms_tools_version: 4.0.0a2.dev29+gdcb35eb01
ini_time: 2012-01-02 12:00:00
model_reference_date: 2000-01-01 00:00:00
adjust_depth_for_sea_surface_height: False
source: GLORYS
bgc_source: UNIFIED
theta_s: 5.0
theta_b: 2.0
hc: 300.0[21]:
initial_conditions_with_unified_bgc.plot("PO4", xi=0, layer_contours=True)
[########################################] | 100% Completed | 1.11 sms
[22]:
initial_conditions_with_unified_bgc.plot("ALK", eta=0)
Interpolate BGC variables by density or mixed-layer depth instead of plain depth#
By default (bgc_interpolation_method="depth"), BGC tracers are interpolated onto the model’s vertical grid in depth. Because biogeochemical fields tend to follow water masses rather than fixed depths, two alternatives are available:
bgc_interpolation_method="density"interpolates the tracers in potential-density (isopycnal) space. The density coordinate is built from the BGC dataset’s own temperature/salinity for the source profile and from the model’s temperature/salinity for the target.bgc_interpolation_method="density_mld"finds the mixed layer depth (MLD) in the source and target density fields, scales the source mixed layer so its MLD matches the target’s, and then interpolates 1:1 in depth below the MLD. This keeps the mixed layers aligned and preserves the absolute depth of sub-mixed-layer features, while avoiding the surface degeneracy of pure density space.
Both density methods require the BGC source to provide temperature/salinity (e.g. the unified dataset’s temp_WOA/salt_WOA); otherwise interpolation falls back to depth space.
[23]:
%%time
initial_conditions_with_unified_bgc_mld = InitialConditions(
grid=grid,
ini_time=ini_time,
source={"name": "GLORYS", "path": path},
bgc_source={
"name": "UNIFIED",
"path": unified_bgc_path,
"climatology": True,
}, # bgc_source is optional
model_reference_date=datetime(2000, 1, 1), # this is the default
use_dask=True,
bgc_interpolation_method="density_mld",
)
/home/x-kthyng/packages/roms-tools/roms_tools/datasets/lat_lon_datasets.py:1539: UserWarning: rename 'lon' to 'longitude' does not create an index anymore. Try using swap_dims instead or use set_index after rename to create an indexed coordinate.
ds = ds.rename({"lon": "longitude", "lat": "latitude", "dep": "depth"})
/home/x-kthyng/packages/roms-tools/roms_tools/datasets/lat_lon_datasets.py:1539: UserWarning: rename 'lat' to 'latitude' does not create an index anymore. Try using swap_dims instead or use set_index after rename to create an indexed coordinate.
ds = ds.rename({"lon": "longitude", "lat": "latitude", "dep": "depth"})
/home/x-kthyng/packages/roms-tools/roms_tools/datasets/lat_lon_datasets.py:1539: UserWarning: rename 'dep' to 'depth' does not create an index anymore. Try using swap_dims instead or use set_index after rename to create an indexed coordinate.
ds = ds.rename({"lon": "longitude", "lat": "latitude", "dep": "depth"})
2026-06-26 19:39:37 - WARNING - Optional variables missing (but not critical): ['Lig', 'DIC_ALT_CO2', 'Alk_ALT_CO2']
CPU times: user 3min 28s, sys: 2.65 s, total: 3min 30s
Wall time: 11.4 s
[24]:
initial_conditions_with_unified_bgc_mld.plot("ALK", eta=0)
Initializing from ROMS Restart Files#
When running a nested ROMS simulation, it is necessary to initialize the child simulation using a restart file from a previously run parent simulation. For a detailed guide on preparing nested simulations, see Nested Simulations.
Typically, you will have already created both the parent and child grid objects, as described in the nesting guide linked above. To initialize the child simulation, we first create the corresponding grid objects for both simulations.
In this example, the parent grid covers the northeast Pacific at a horizontal resolution of 3km and with 60 vertical layers.
[25]:
parent_grid = Grid(filename="/anvil/projects/x-ees250129/Datasets/ROMSOutput/eastpac3km/eastpac_3km_grd.nc")
[26]:
parent_grid.plot()
We will use the following restart file, produced by the parent grid simulation for December 31, 2023, at 08:25:20, to initialize the child grid.
[27]:
restart_date = datetime(2023, 12, 31, 8, 25, 20)
restart_file = "/anvil/projects/x-ees250129/Datasets/ROMSOutput/eastpac3km/eastpac3km_rst.20231231082520.nc"
The child grid covers the Salish Sea at a horizontal resolution of 600m and 100 vertical layers. Here, we create a standard Grid that has not been adapted to the parent grid yet.
[28]:
child_grid = Grid(
nx = 960,
ny = 640,
size_x = 576,
size_y = 384,
center_lon=-125.0,
center_lat=49.2,
rot=-30.0,
N=100,
hc=300.0,
)
[29]:
child_grid.plot()
Before we make the initial conditions for the child simulation, we need to smooth the child grid boundaries into the parent grid. See the nesting documentation.
[30]:
from roms_tools import align_grids
child_grid = align_grids(parent_grid=parent_grid, child_grid=child_grid, verbose=True)
2026-06-26 19:39:55 - INFO - No `boundaries` provided. Using mask-based defaults: {'south': True, 'east': False, 'north': False, 'west': True}
2026-06-26 19:43:00 - INFO - === Modifying the child mask ===
2026-06-26 19:43:33 - INFO - Total time: 33.032 seconds
2026-06-26 19:43:33 - INFO - ================================================================================================
2026-06-26 19:43:33 - INFO - === Modifying the child topography ===
2026-06-26 19:44:06 - INFO - Total time: 32.955 seconds
2026-06-26 19:44:06 - INFO - ================================================================================================
We now create the initial conditions for the child simulations as follows.
[31]:
%%time
initial_conditions_from_roms = InitialConditions(
grid=child_grid,
ini_time=restart_date,
source={"name": "ROMS", "grid": parent_grid, "path": restart_file},
use_dask=True,
bgc_source={
"name": "ROMS",
"grid": parent_grid,
"path": restart_file,
},
)
CPU times: user 14min 12s, sys: 3.18 s, total: 14min 15s
Wall time: 46.8 s
Note
In the cell above, we created the initial conditions using both source and bgc_source with the same information (the ROMS parent grid and restart file). If you do not provide bgc_source, ROMS-Tools will only read and prepare the physical variables from the restart file.
[32]:
initial_conditions_from_roms.ds
[32]:
<xarray.Dataset> Size: 9GB
Dimensions: (ocean_time: 1, eta_rho: 642, xi_rho: 962, s_rho: 100,
xi_u: 961, eta_v: 641, s_w: 101)
Coordinates:
* ocean_time (ocean_time) float64 8B 7.573e+08
abs_time (ocean_time) datetime64[ns] 8B 2023-12-31T08:25:20
Dimensions without coordinates: eta_rho, xi_rho, s_rho, xi_u, eta_v, s_w
Data variables: (12/42)
zeta (ocean_time, eta_rho, xi_rho) float32 2MB dask.array<chunksize=(1, 642, 962), meta=np.ndarray>
temp (ocean_time, s_rho, eta_rho, xi_rho) float32 247MB dask.array<chunksize=(1, 100, 642, 962), meta=np.ndarray>
salt (ocean_time, s_rho, eta_rho, xi_rho) float32 247MB dask.array<chunksize=(1, 100, 642, 962), meta=np.ndarray>
u (ocean_time, s_rho, eta_rho, xi_u) float32 247MB dask.array<chunksize=(1, 100, 642, 961), meta=np.ndarray>
v (ocean_time, s_rho, eta_v, xi_rho) float32 247MB dask.array<chunksize=(1, 100, 641, 962), meta=np.ndarray>
ubar (ocean_time, eta_rho, xi_u) float32 2MB dask.array<chunksize=(1, 642, 961), meta=np.ndarray>
... ...
diazFe (ocean_time, s_rho, eta_rho, xi_rho) float32 247MB dask.array<chunksize=(1, 100, 642, 962), meta=np.ndarray>
spCaCO3 (ocean_time, s_rho, eta_rho, xi_rho) float32 247MB dask.array<chunksize=(1, 100, 642, 962), meta=np.ndarray>
zooC (ocean_time, s_rho, eta_rho, xi_rho) float32 247MB dask.array<chunksize=(1, 100, 642, 962), meta=np.ndarray>
w (ocean_time, s_w, eta_rho, xi_rho) float32 250MB 0.0 ... 0.0
Cs_r (s_rho) float32 400B -0.992 -0.9753 ... -8.89e-05 -9.874e-06
Cs_w (s_w) float32 404B -1.0 -0.9837 -0.9667 ... -3.95e-05 0.0
Attributes:
title: ROMS initial conditions file create...
roms_tools_version: 10000.dev361+g2e6c47bd8
ini_time: 2023-12-31 08:25:20
model_reference_date: 2000-01-01 00:00:00
adjust_depth_for_sea_surface_height: True
source: ROMS
bgc_source: ROMS
theta_s: 5.0
theta_b: 2.0
hc: 300.0Let’s make some plots as before.
[33]:
initial_conditions_from_roms.plot("temp", s=-1)
[########################################] | 100% Completed | 8.47 sms
[34]:
initial_conditions_from_roms.plot("ALK", eta=0)
Strict vs. flexible initial time#
The daily GLORYS dataset provides one time stamp per day, which in our locally available dataset is always at 12:00 UTC (noon). Similarly, the restart files typically contain 1–2 time stamps that may be at irregular times (as seen above).
But what happens if we request an initial time that does not exactly match one of these available time stamps?
[35]:
midnight_ini_time = datetime(2012, 1, 2, 0, 0, 0) # midnight on (i.e., the start of) January 2, 2012
[36]:
initial_conditions_strict_midnight = InitialConditions(
grid=grid,
ini_time=midnight_ini_time,
source={"name": "GLORYS", "path": path},
use_dask=True,
)
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
Cell In[40], line 1
----> 1 initial_conditions_strict_midnight = InitialConditions(
2 grid=grid,
3 ini_time=midnight_ini_time,
4 source={"name": "GLORYS", "path": path},
File <string>:15, in __create_fn__.<locals>.__init__(self, grid, ini_time, source, bgc_source, model_reference_date, allow_flex_time, use_dask, chunks, initial_slice_bounds, bypass_validation, bgc_interpolation_method, use_density_interpolation)
File ~/packages/roms-tools/roms_tools/setup/initial_conditions.py:219, in InitialConditions.__post_init__(self)
216 self._input_checks()
218 processed_fields = {}
--> 219 processed_fields = self._process_data(processed_fields, type="physics")
221 if self.bgc_source is not None:
222 processed_fields = self._process_data(processed_fields, type="bgc")
File ~/packages/roms-tools/roms_tools/setup/initial_conditions.py:247, in InitialConditions._process_data(self, processed_fields, type)
244 def _process_data(self, processed_fields, type="physics"):
245 target_coords = get_target_coords(self.grid)
--> 247 data = self._get_data(forcing_type=type)
248 data.choose_subdomain(
249 target_coords,
250 unchunk_lateral_dims=True,
251 )
252 # Enforce double precision to ensure reproducibility
File ~/packages/roms-tools/roms_tools/setup/initial_conditions.py:655, in InitialConditions._get_data(self, forcing_type)
652 else:
653 self.adjust_depth_for_sea_surface_height = False
--> 655 data = data_type(
656 filename=source_dict["path"], # type: ignore
657 start_time=self.ini_time,
658 climatology=source_dict["climatology"], # type: ignore
659 allow_flex_time=self.allow_flex_time,
660 use_dask=self.use_dask,
661 chunks=self.chunks,
662 initial_slice_bounds=self.initial_slice_bounds,
663 )
665 return data
File <string>:19, in __create_fn__.<locals>.__init__(self, filename, start_time, end_time, dim_names, var_names, opt_var_names, climatology, has_encoded_times, needs_lateral_fill, use_dask, chunks, initial_slice_bounds, read_zarr, allow_flex_time, apply_post_processing, ds_loader_fn)
File ~/packages/roms-tools/roms_tools/datasets/lat_lon_datasets.py:189, in LatLonDataset.__post_init__(self)
187 if "time" in self.dim_names and self.start_time is not None:
188 ds = self.add_time_info(ds)
--> 189 ds = self.select_relevant_times(ds)
191 if self.dim_names["time"] != "time":
192 ds = ds.rename({self.dim_names["time"]: "time"})
File ~/packages/roms-tools/roms_tools/datasets/lat_lon_datasets.py:353, in LatLonDataset.select_relevant_times(self, ds)
350 if self.start_time is None:
351 raise ValueError("select_relevant_times called but start_time is None.")
--> 353 ds = select_relevant_times(
354 ds=ds,
355 time_dim=time_dim,
356 time_coord=time_dim,
357 start_time=self.start_time,
358 end_time=self.end_time,
359 climatology=self.climatology,
360 allow_flex_time=self.allow_flex_time,
361 )
363 return ds
File ~/packages/roms-tools/roms_tools/datasets/utils.py:294, in select_relevant_times(ds, time_dim, time_coord, start_time, end_time, climatology, allow_flex_time)
290 ds = ds.assign_coords({time_dim: convert_cftime_to_datetime(ds[time_coord])})
292 if not end_time:
293 # Assume we are looking for exactly one time record for initial conditions
--> 294 return _select_initial_time(
295 ds, time_dim, time_coord, start_time, climatology, allow_flex_time
296 )
298 if climatology:
299 return ds
File ~/packages/roms-tools/roms_tools/datasets/utils.py:397, in _select_initial_time(ds, time_dim, time_coord, ini_time, climatology, allow_flex_time)
394 else:
395 # Strict match required
396 if not (ds[time_coord].values == np.datetime64(ini_time)).any():
--> 397 raise ValueError(
398 f"No exact match found for initial time {ini_time}. Consider setting allow_flex_time to True."
399 )
401 ds = ds.sel({time_coord: np.datetime64(ini_time)})
403 if time_dim not in ds.dims:
ValueError: No exact match found for initial time 2012-01-02 00:00:00. Consider setting allow_flex_time to True.
If you request an initial time that does not exactly match one of these entries, ROMS-Tools will by default raise an error because it cannot find an exact match. This behavior is controlled by the allow_flex_time flag:
False (default): requires an exact match to
ini_time. AValueErroris raised if no match exists (see above example).True: allows a +24-hour search window starting from
ini_timeand selects the closest available entry within that window. If no match is found, aValueErroris still raised.
[37]:
initial_conditions_flex_midnight = InitialConditions(
grid=grid,
ini_time=midnight_ini_time,
allow_flex_time=True,
source={"name": "GLORYS", "path": path},
use_dask=True,
)
2026-06-26 19:46:48 - WARNING - Selected time entry closest to the specified start_time in +24 hour range: ['2012-01-02T12:00:00.000000000']
The logging output indicates which time stamp was actually selected. To verify, the following output confirms that ROMS-Tools adjusted the requested midnight_ini_time to the nearest available timestamp, which in this case was noon, 12 hours later.
[38]:
initial_conditions_flex_midnight.ds.abs_time
[38]:
<xarray.DataArray 'abs_time' (ocean_time: 1)> Size: 8B
array(['2012-01-02T12:00:00.000000000'], dtype='datetime64[ns]')
Coordinates:
* ocean_time (ocean_time) float64 8B 3.788e+08
abs_time (ocean_time) datetime64[ns] 8B 2012-01-02T12:00:00
Attributes:
long_name: absolute timeThe same behavior applies when creating InitialConditions from a ROMS restart file. Instead of specifying the exact time December 31, 2023, at 08:25:20, we simply provide December 31, 2023, at 00:00 (midnight), together with allow_flex_time = True.
[39]:
%%time
initial_conditions_from_roms_flex_time = InitialConditions(
grid=child_grid,
ini_time=datetime(2023, 12, 31),
allow_flex_time=True,
source={"name": "ROMS", "grid": parent_grid, "path": restart_file},
use_dask=True,
)
2026-06-26 19:46:52 - WARNING - Selected time entry closest to the specified start_time in +24 hour range: 2023-12-31T08:22:50.000000000
CPU times: user 1min 23s, sys: 1.51 s, total: 1min 24s
Wall time: 16.2 s
[40]:
initial_conditions_from_roms_flex_time.ds.abs_time
[40]:
<xarray.DataArray 'abs_time' (ocean_time: 1)> Size: 8B
array(['2023-12-31T08:22:50.000000000'], dtype='datetime64[ns]')
Coordinates:
* ocean_time (ocean_time) float64 8B 7.573e+08
abs_time (ocean_time) datetime64[ns] 8B 2023-12-31T08:22:50
Attributes:
long_name: absolute timeSaving as NetCDF or YAML file#
We can now save the dataset as a NetCDF file.
[41]:
filepath = "/anvil/scratch/x-smaticka/initial_conditions/my_initial_conditions.nc"
[42]:
%time initial_conditions_with_unified_bgc.save(filepath)
2026-07-09 14:10:50 - INFO - Writing the following NetCDF files:
/anvil/scratch/x-smaticka/initial_conditions/my_initial_conditions.nc
[########################################] | 100% Completed | 26.88 ss
CPU times: user 3min 8s, sys: 13 s, total: 3min 21s
Wall time: 27.5 s
[42]:
[PosixPath('/anvil/scratch/x-smaticka/initial_conditions/my_initial_conditions.nc')]
We can also export the parameters of our InitialConditions object to a YAML file.
[43]:
yaml_filepath = "/anvil/scratch/x-smaticka/initial_conditions/my_initial_conditions.yaml"
[44]:
initial_conditions_with_unified_bgc.to_yaml(yaml_filepath)
[45]:
# 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: 4.0.0a2.dev29+gdcb35eb01
---
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
InitialConditions:
ini_time: '2012-01-02T12:00:00'
source:
name: GLORYS
path: /anvil/projects/x-ees250129/Datasets/GLORYS/NA/2012/mercatorglorys12v1_gl12_mean_20120102.nc
climatology: false
bgc_source:
name: UNIFIED
path: /anvil/projects/x-ees250129/Datasets/UNIFIED/BGCdataset_v2_filled.nc
climatology: true
model_reference_date: '2000-01-01T00:00:00'
allow_flex_time: false
chunks: null
initial_slice_bounds: null
bypass_validation: false
bgc_interpolation_method: depth
Creating initial conditions from an existing YAML file#
[46]:
%time the_same_initial_conditions_with_unified_bgc = InitialConditions.from_yaml(yaml_filepath, use_dask=True)
/anvil/projects/x-ees250129/x-smaticka/roms-tools/roms_tools/datasets/lat_lon_datasets.py:1716: UserWarning: rename 'lon' to 'longitude' does not create an index anymore. Try using swap_dims instead or use set_index after rename to create an indexed coordinate.
ds = ds.rename({"lon": "longitude", "lat": "latitude", "dep": "depth"})
/anvil/projects/x-ees250129/x-smaticka/roms-tools/roms_tools/datasets/lat_lon_datasets.py:1716: UserWarning: rename 'lat' to 'latitude' does not create an index anymore. Try using swap_dims instead or use set_index after rename to create an indexed coordinate.
ds = ds.rename({"lon": "longitude", "lat": "latitude", "dep": "depth"})
/anvil/projects/x-ees250129/x-smaticka/roms-tools/roms_tools/datasets/lat_lon_datasets.py:1716: UserWarning: rename 'dep' to 'depth' does not create an index anymore. Try using swap_dims instead or use set_index after rename to create an indexed coordinate.
ds = ds.rename({"lon": "longitude", "lat": "latitude", "dep": "depth"})
2026-07-09 14:11:49 - WARNING - Optional variables missing (but not critical): ['Lig', 'DIC_ALT_CO2', 'Alk_ALT_CO2']
CPU times: user 2min 52s, sys: 1.6 s, total: 2min 54s
Wall time: 16.7 s
[47]:
the_same_initial_conditions_with_unified_bgc.ds
[47]:
<xarray.Dataset> Size: 4GB
Dimensions: (ocean_time: 1, s_rho: 100, eta_rho: 502, xi_rho: 502,
xi_u: 501, eta_v: 501, s_w: 101)
Coordinates:
abs_time (ocean_time) datetime64[ns] 8B 2012-01-02T12:00:00
* ocean_time (ocean_time) float64 8B 3.788e+08
Dimensions without coordinates: s_rho, eta_rho, xi_rho, xi_u, eta_v, s_w
Data variables: (12/42)
temp (ocean_time, s_rho, eta_rho, xi_rho) float32 101MB dask.array<chunksize=(1, 100, 409, 409), meta=np.ndarray>
salt (ocean_time, s_rho, eta_rho, xi_rho) float32 101MB dask.array<chunksize=(1, 100, 409, 409), meta=np.ndarray>
u (ocean_time, s_rho, eta_rho, xi_u) float32 101MB dask.array<chunksize=(1, 100, 409, 409), meta=np.ndarray>
v (ocean_time, s_rho, eta_v, xi_rho) float32 101MB dask.array<chunksize=(1, 100, 409, 409), meta=np.ndarray>
zeta (ocean_time, eta_rho, xi_rho) float32 1MB dask.array<chunksize=(1, 502, 502), meta=np.ndarray>
ubar (ocean_time, eta_rho, xi_u) float32 1MB dask.array<chunksize=(1, 409, 409), meta=np.ndarray>
... ...
Lig (ocean_time, s_rho, eta_rho, xi_rho) float32 101MB dask.array<chunksize=(1, 100, 409, 409), meta=np.ndarray>
DIC_ALT_CO2 (ocean_time, s_rho, eta_rho, xi_rho) float32 101MB dask.array<chunksize=(1, 100, 409, 409), meta=np.ndarray>
ALK_ALT_CO2 (ocean_time, s_rho, eta_rho, xi_rho) float32 101MB dask.array<chunksize=(1, 100, 409, 409), meta=np.ndarray>
w (ocean_time, s_w, eta_rho, xi_rho) float32 102MB 0.0 ... 0.0
Cs_r (s_rho) float32 400B -0.992 -0.9753 ... -8.89e-05 -9.874e-06
Cs_w (s_w) float32 404B -1.0 -0.9837 -0.9667 ... -3.95e-05 0.0
Attributes:
title: ROMS initial conditions file create...
roms_tools_version: 4.0.0a2.dev29+gdcb35eb01
ini_time: 2012-01-02 12:00:00
model_reference_date: 2000-01-01 00:00:00
adjust_depth_for_sea_surface_height: False
source: GLORYS
bgc_source: UNIFIED
theta_s: 5.0
theta_b: 2.0
hc: 300.0[ ]: