Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Making a new domain: CStarSpecBuilder

This notebook demonstrates how to create a new domain and run a ROMS simulation using C-STAR Forge.

Setup

First, import the necessary modules and define the domain configuration parameters.

%load_ext autoreload
%autoreload 2

import cstar_forge
import cstar.execution.handler as handler
import time
from datetime import datetime
from IPython.display import Markdown, display

Environment and Machine Information

Record the execution environment and machine details for reproducibility.

env = cstar_forge.config.get_environment_info()

# Display summary
summary = f"""
### Machine Information
- **Hostname**: `{env.hostname}`
- **System Tag**: `{env.system_tag}`
- **OS**: `{env.os_info}`

### Environment Summary
- **Python Version**: `{env.python_version}`
- **Python Executable**: `{env.python_executable}`
- **Conda/Micromamba Environment**: `{env.env_info}`
- **Kernel**: `{env.kernel_spec}`
"""

display(Markdown(summary))
print(f"Execution timestamp: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
Loading...
Execution timestamp: 2026-05-04 16:19:18
grid_name = "test-tiny"
model_name = "cson_roms-marbl_v0.1"
start_time = datetime(2012, 1, 1)
end_time = datetime(2012, 1, 2)

grid_kwargs = dict[str, float](
    nx=6,
    ny=2,
    size_x=500,
    size_y=1000,
    center_lon=0,
    center_lat=55,
    rot=10,
    N=3,  # number of vertical levels
    theta_s=5.0,  # surface control parameter
    theta_b=2.0,  # bottom control parameter
    hc=250.0,  # critical depth
)

boundaries={
        "south": False,
        "east": True,
        "north": True,
        "west": False, 
    }

partitioning = {
    "n_procs_x": 1, # number of partitions in xi (x) 
    "n_procs_y": 1, # number of partitions in eta (y) 
}

Generate CDR Forcing for this grid using ROMS-tools

from roms_tools import TracerPerturbation, CDRForcing, Grid

tp = TracerPerturbation(
    name="my_cdr",
    lat=grid_kwargs["center_lat"]+4,  # degree N
    lon=grid_kwargs["center_lon"]+1,  # degree E
    hsc=10,
    vsc=10,
    depth=1,  # m
    tracer_fluxes={"ALK": 2 * 10**6},  # meq/s
)

cdr = CDRForcing(
    grid=Grid(**grid_kwargs), # for plotting, could omit
    start_time=start_time,
    end_time=end_time,
    releases=[tp],
)

cdr.plot_locations()
cdr.plot_distribution("my_cdr")
cdr.plot_tracer_flux("ALK")

# we should decide how we want to pass the cdr information:
# dict/kwargs? yaml file? object?
cdr_as_dict = cdr.model_dump()["CDRForcing"]

# cdr_as_dict
<Figure size 1300x700 with 1 Axes>
<Figure size 1200x550 with 3 Axes>
<Figure size 700x400 with 1 Axes>

Initialize CstarSpecBuilder

Create a CstarSpecBuilder instance with the domain configuration. This initializes the PRECONFIG stage, creating the grid object and blueprint structure.

ocn = cstar_forge.CstarSpecBuilder(
    description="Test tiny",
    model_name=model_name,
    grid_name=grid_name,
    grid_kwargs=grid_kwargs,
    open_boundaries=boundaries,    
    start_time=start_time,
    end_time=end_time,
    partitioning=partitioning,
    cdr_forcing = cdr_as_dict,
    #catalog_root="local"
)
CstarSpecBuilder: planned NetCDF outputs
  - /Users/eilerman/cson-forge-data/input-data/cson_roms-marbl_v0_1_test-tiny_1procs/cson_roms-marbl_v0_1_test-tiny_1procs_grid.nc
  - /Users/eilerman/cson-forge-data/input-data/cson_roms-marbl_v0_1_test-tiny_1procs/cson_roms-marbl_v0_1_test-tiny_1procs_initial_conditions.nc
  - /Users/eilerman/cson-forge-data/input-data/cson_roms-marbl_v0_1_test-tiny_1procs/cson_roms-marbl_v0_1_test-tiny_1procs_surface-physics.nc
  - /Users/eilerman/cson-forge-data/input-data/cson_roms-marbl_v0_1_test-tiny_1procs/cson_roms-marbl_v0_1_test-tiny_1procs_surface-bgc.nc
  - /Users/eilerman/cson-forge-data/input-data/cson_roms-marbl_v0_1_test-tiny_1procs/cson_roms-marbl_v0_1_test-tiny_1procs_boundary-physics.nc
  - /Users/eilerman/cson-forge-data/input-data/cson_roms-marbl_v0_1_test-tiny_1procs/cson_roms-marbl_v0_1_test-tiny_1procs_boundary-bgc.nc
  - /Users/eilerman/cson-forge-data/input-data/cson_roms-marbl_v0_1_test-tiny_1procs/cson_roms-marbl_v0_1_test-tiny_1procs_tidal.nc
  - /Users/eilerman/cson-forge-data/input-data/cson_roms-marbl_v0_1_test-tiny_1procs/cson_roms-marbl_v0_1_test-tiny_1procs_river.nc

CstarSpecBuilder: output locations
  NetCDF files: /Users/eilerman/cson-forge-data/input-data/cson_roms-marbl_v0_1_test-tiny_1procs
  YAML files: /Users/eilerman/git/cson-forge/cson_forge/catalog/blueprints/MacOS/cson_roms-marbl_v0.1_test-tiny_1procs
  Compile-time code: /Users/eilerman/git/cson-forge/cson_forge/catalog/builds/cson_roms-marbl_v0.1_test-tiny_1procs/compile-time
  Run-time code: /Users/eilerman/git/cson-forge/cson_forge/catalog/builds/cson_roms-marbl_v0.1_test-tiny_1procs/run-time
  Simulation output (scratch): /Users/eilerman/cson-forge-data/cson-forge-run/cson_roms-marbl_v0.1_test-tiny_1procs_20120101-20120102

Visualize the grid

ocn.grid.plot()
<Figure size 1300x700 with 2 Axes>

Prepare Source Data

Ensure that all required source datasets (GLORYS, UNIFIED, SRTM15, etc.) are staged locally. This downloads and prepares the data needed for input generation.

# ensure that source data is staged locally
ocn.ensure_source_data()
✔️  Using existing GLORYS_REGIONAL file for 2011-12-31: cmems_mod_glo_phy_my_0.083deg_P1D-m_REGIONAL_test-tiny_20111231.nc
✔️  Using existing GLORYS_REGIONAL file for 2012-01-01: cmems_mod_glo_phy_my_0.083deg_P1D-m_REGIONAL_test-tiny_20120101.nc
✔️  Using existing GLORYS_REGIONAL file for 2012-01-02: cmems_mod_glo_phy_my_0.083deg_P1D-m_REGIONAL_test-tiny_20120102.nc
✔️  Using existing GLORYS_REGIONAL file for 2012-01-03: cmems_mod_glo_phy_my_0.083deg_P1D-m_REGIONAL_test-tiny_20120103.nc
✔️  TPXO dataset verified at: /Users/eilerman/cson-forge-data/source-data/TPXO/TPXO10.v2
✔️  Using existing BGC dataset: /Users/eilerman/cson-forge-data/source-data/UNIFIED_BGC/BGCdataset.nc

Generate Input Files

Generate all model input files (grid, initial conditions, forcing) from the source data. This completes the POSTCONFIG stage and updates the blueprint with actual file paths.

The system will look for an existing blueprint and, if a match is found, it will use that data unless the user sets clobber=True to force re-generating the data.

# prepare model input
ocn.generate_inputs(clobber=True) # setting clobber=True will overwrite existing files
⚠️  Clobber=True: removing 11 existing .nc files in /Users/eilerman/cson-forge-data/input-data/cson_roms-marbl_v0_1_test-tiny_1procs...

▶️  [1/9] Writing ROMS grid...

▶️  [2/9] Generating initial conditions...
2026-05-04 22:19:20,527 [WARNING] - utils.py:109 - Optional variables missing (but not critical): ['Lig', 'DIC_ALT_CO2', 'Alk_ALT_CO2']
[########################################] | 100% Completed | 1.97 sms

▶️  [3/9] Generating surface forcing...
Numba: Attempted to fork from a non-main thread, the TBB library may be in an invalid state in the child process.
[########################################] | 100% Completed | 636.29 ms
[########################################] | 100% Completed | 1.24 sms
2026-05-04 22:19:40,057 [WARNING] - utils.py:109 - Optional variables missing (but not critical): ['pco2_air_alt']

▶️  [4/9] Generating surface forcing...
[########################################] | 100% Completed | 211.54 ms

▶️  [5/9] Generating boundary forcing...
[########################################] | 100% Completed | 108.09 ms
[########################################] | 100% Completed | 314.98 ms
2026-05-04 22:19:41,239 [WARNING] - utils.py:109 - Optional variables missing (but not critical): ['Lig', 'DIC_ALT_CO2', 'Alk_ALT_CO2']

▶️  [6/9] Generating boundary forcing...
[########################################] | 100% Completed | 3.31 sms

▶️  [7/9] Generating tidal forcing...
[########################################] | 100% Completed | 14.19 s

▶️  [8/9] Generating river forcing...
2026-05-04 22:20:20,057 [WARNING] - cdr_forcing.py:1010 - Grid not provided: cannot verify whether the specified lat/lon/depth location is within the domain or on land. Please check manually or provide a grid when instantiating the class.

▶️  [9/9] Generating CDR forcing...

✅ All input files generated.

RomsMarblBlueprint(name='cson_roms-marbl_v0.1_test-tiny_1procs', description='Test tiny', application=<Application.ROMS_MARBL: 'roms_marbl'>, state=<BlueprintState.NotSet: 'notset'>, valid_start_date=datetime.datetime(2012, 1, 1, 0, 0), valid_end_date=datetime.datetime(2012, 1, 2, 0, 0), code={'roms': {'documentation': '', 'locked': False, 'location': 'https://github.com/CWorthy-ocean/ucla-roms.git', 'commit': '45a9221', 'branch': '', 'filter': None}, 'run_time': {'documentation': '', 'locked': False, 'location': 'placeholder://run_time', 'commit': '', 'branch': 'main', 'filter': None}, 'compile_time': {'documentation': '', 'locked': False, 'location': 'placeholder://compile_time', 'commit': '', 'branch': 'main', 'filter': None}, 'marbl': {'documentation': '', 'locked': False, 'location': 'https://github.com/marbl-ecosys/MARBL.git', 'commit': 'marbl0.45.0', 'branch': '', 'filter': None}}, initial_conditions={'documentation': '', 'locked': False, 'data': [{'location': PosixPath('/Users/eilerman/cson-forge-data/input-data/cson_roms-marbl_v0_1_test-tiny_1procs/cson_roms-marbl_v0_1_test-tiny_1procs_initial_conditions.nc'), 'partitioned': False}]}, grid={'documentation': '', 'locked': False, 'data': [{'location': '/Users/eilerman/cson-forge-data/input-data/cson_roms-marbl_v0_1_test-tiny_1procs/cson_roms-marbl_v0_1_test-tiny_1procs_grid.nc', 'partitioned': False}]}, forcing={'boundary': {'documentation': '', 'locked': False, 'data': [{'location': PosixPath('/Users/eilerman/cson-forge-data/input-data/cson_roms-marbl_v0_1_test-tiny_1procs/cson_roms-marbl_v0_1_test-tiny_1procs_boundary-physics_201112.nc'), 'partitioned': False}, {'location': PosixPath('/Users/eilerman/cson-forge-data/input-data/cson_roms-marbl_v0_1_test-tiny_1procs/cson_roms-marbl_v0_1_test-tiny_1procs_boundary-physics_201201.nc'), 'partitioned': False}, {'location': PosixPath('/Users/eilerman/cson-forge-data/input-data/cson_roms-marbl_v0_1_test-tiny_1procs/cson_roms-marbl_v0_1_test-tiny_1procs_boundary-bgc_clim.nc'), 'partitioned': False}]}, 'surface': {'documentation': '', 'locked': False, 'data': [{'location': PosixPath('/Users/eilerman/cson-forge-data/input-data/cson_roms-marbl_v0_1_test-tiny_1procs/cson_roms-marbl_v0_1_test-tiny_1procs_surface-physics_201112.nc'), 'partitioned': False}, {'location': PosixPath('/Users/eilerman/cson-forge-data/input-data/cson_roms-marbl_v0_1_test-tiny_1procs/cson_roms-marbl_v0_1_test-tiny_1procs_surface-physics_201201.nc'), 'partitioned': False}, {'location': PosixPath('/Users/eilerman/cson-forge-data/input-data/cson_roms-marbl_v0_1_test-tiny_1procs/cson_roms-marbl_v0_1_test-tiny_1procs_surface-bgc_clim.nc'), 'partitioned': False}]}, 'tidal': {'documentation': '', 'locked': False, 'data': [{'location': PosixPath('/Users/eilerman/cson-forge-data/input-data/cson_roms-marbl_v0_1_test-tiny_1procs/cson_roms-marbl_v0_1_test-tiny_1procs_tidal.nc'), 'partitioned': False}]}, 'river': {'documentation': '', 'locked': False, 'data': [{'location': PosixPath('/Users/eilerman/cson-forge-data/input-data/cson_roms-marbl_v0_1_test-tiny_1procs/cson_roms-marbl_v0_1_test-tiny_1procs_river.nc'), 'partitioned': False}]}, 'corrections': None}, partitioning={'documentation': '', 'locked': False, 'hash': None, 'n_procs_x': 1, 'n_procs_y': 1}, model_params=None, runtime_params=None, cdr_forcing={'documentation': '', 'locked': False, 'data': [{'location': '/Users/eilerman/cson-forge-data/input-data/cson_roms-marbl_v0_1_test-tiny_1procs/cson_roms-marbl_v0_1_test-tiny_1procs_cdr.nc', 'partitioned': False}]}, nesting_info=None)

Access Generated Input Datasets

Input datasets are available on the datasets attribute of the CstarSpecBuilder after generate_inputs() has completed. The datasets dictionary only contains keys for fields that exist in the blueprint and have been successfully generated.

Note: Datasets are only available after generate_inputs() has run and populated the blueprint with actual file paths. If a key doesn’t exist, it means that field wasn’t generated or isn’t in the blueprint.

for key in ocn.datasets.keys():
    print("-"*100)
    print(key)
    print(ocn.datasets[key])
----------------------------------------------------------------------------------------------------
grid
<xarray.Dataset> Size: 3kB
Dimensions:       (eta_rho: 4, xi_rho: 8, xi_u: 7, eta_v: 3, eta_coarse: 3,
                   xi_coarse: 5, s_rho: 3, s_w: 4)
Coordinates:
    lat_rho       (eta_rho, xi_rho) float64 256B ...
    lon_rho       (eta_rho, xi_rho) float64 256B ...
    lat_u         (eta_rho, xi_u) float64 224B ...
    lon_u         (eta_rho, xi_u) float64 224B ...
    lat_v         (eta_v, xi_rho) float64 192B ...
    lon_v         (eta_v, xi_rho) float64 192B ...
    lat_coarse    (eta_coarse, xi_coarse) float64 120B ...
    lon_coarse    (eta_coarse, xi_coarse) float64 120B ...
Dimensions without coordinates: eta_rho, xi_rho, xi_u, eta_v, eta_coarse,
                                xi_coarse, s_rho, s_w
Data variables: (12/15)
    angle         (eta_rho, xi_rho) float64 256B ...
    f             (eta_rho, xi_rho) float64 256B ...
    pm            (eta_rho, xi_rho) float64 256B ...
    pn            (eta_rho, xi_rho) float64 256B ...
    spherical     |S1 1B ...
    mask_rho      (eta_rho, xi_rho) int32 128B ...
    ...            ...
    mask_coarse   (eta_coarse, xi_coarse) int32 60B ...
    h             (eta_rho, xi_rho) float64 256B ...
    sigma_r       (s_rho) float32 12B ...
    Cs_r          (s_rho) float32 12B ...
    sigma_w       (s_w) float32 16B ...
    Cs_w          (s_w) float32 16B ...
Attributes: (12/14)
    title:                   ROMS grid created by ROMS-Tools
    roms_tools_version:      3.6.1.dev17+g97b5fe39e
    size_x:                  500
    size_y:                  1000
    center_lon:              0
    center_lat:              55
    ...                      ...
    close_narrow_channels:   False
    topography_source_name:  ETOPO5
    hmin:                    5.0
    theta_s:                 5.0
    theta_b:                 2.0
    hc:                      250.0
----------------------------------------------------------------------------------------------------
initial_conditions
<xarray.Dataset> Size: 15kB
Dimensions:      (ocean_time: 1, s_rho: 3, eta_rho: 4, xi_rho: 8, xi_u: 7,
                  eta_v: 3, s_w: 4)
Coordinates:
    abs_time     (ocean_time) datetime64[ns] 8B ...
  * ocean_time   (ocean_time) float64 8B 3.787e+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 384B ...
    salt         (ocean_time, s_rho, eta_rho, xi_rho) float32 384B ...
    u            (ocean_time, s_rho, eta_rho, xi_u) float32 336B ...
    v            (ocean_time, s_rho, eta_v, xi_rho) float32 288B ...
    zeta         (ocean_time, eta_rho, xi_rho) float32 128B ...
    ubar         (ocean_time, eta_rho, xi_u) float32 112B ...
    ...           ...
    Lig          (ocean_time, s_rho, eta_rho, xi_rho) float32 384B ...
    DIC_ALT_CO2  (ocean_time, s_rho, eta_rho, xi_rho) float32 384B ...
    ALK_ALT_CO2  (ocean_time, s_rho, eta_rho, xi_rho) float32 384B ...
    w            (ocean_time, s_w, eta_rho, xi_rho) float32 512B ...
    Cs_r         (s_rho) float32 12B ...
    Cs_w         (s_w) float32 16B ...
Attributes:
    title:                                ROMS initial conditions file create...
    roms_tools_version:                   3.6.1.dev17+g97b5fe39e
    ini_time:                             2012-01-01 00: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:                                   250.0
----------------------------------------------------------------------------------------------------
forcing.surface
[<xarray.Dataset> Size: 8kB
Dimensions:   (time: 1, eta_rho: 4, xi_rho: 8, rad_time: 28)
Coordinates:
    abs_time  (time) datetime64[ns] 8B ...
  * time      (time) float64 8B 4.383e+03
  * rad_time  (rad_time) float64 224B 4.383e+03 4.383e+03 ... 4.384e+03
Dimensions without coordinates: eta_rho, xi_rho
Data variables:
    uwnd      (time, eta_rho, xi_rho) float32 128B ...
    vwnd      (time, eta_rho, xi_rho) float32 128B ...
    swrad     (rad_time, eta_rho, xi_rho) float32 4kB ...
    lwrad     (rad_time, eta_rho, xi_rho) float32 4kB ...
    Tair      (time, eta_rho, xi_rho) float32 128B ...
    rain      (time, eta_rho, xi_rho) float32 128B ...
    qair      (time, eta_rho, xi_rho) float32 128B ...
Attributes:
    title:                 ROMS surface forcing file created by ROMS-Tools
    roms_tools_version:    3.6.1.dev17+g97b5fe39e
    start_time:            2012-01-01 00:00:00
    end_time:              2012-01-02 00:00:00
    source:                ERA5
    correct_radiation:     True
    wind_dropoff:          False
    use_coarse_grid:       False
    model_reference_date:  2000-01-01 00:00:00
    type:                  physics, <xarray.Dataset> Size: 25kB
Dimensions:   (time: 27, eta_rho: 4, xi_rho: 8, rad_time: 28)
Coordinates:
    abs_time  (time) datetime64[ns] 216B ...
  * time      (time) float64 216B 4.383e+03 4.383e+03 ... 4.384e+03 4.384e+03
  * rad_time  (rad_time) float64 224B 4.383e+03 4.383e+03 ... 4.384e+03
Dimensions without coordinates: eta_rho, xi_rho
Data variables:
    uwnd      (time, eta_rho, xi_rho) float32 3kB ...
    vwnd      (time, eta_rho, xi_rho) float32 3kB ...
    swrad     (rad_time, eta_rho, xi_rho) float32 4kB ...
    lwrad     (rad_time, eta_rho, xi_rho) float32 4kB ...
    Tair      (time, eta_rho, xi_rho) float32 3kB ...
    rain      (time, eta_rho, xi_rho) float32 3kB ...
    qair      (time, eta_rho, xi_rho) float32 3kB ...
Attributes:
    title:                 ROMS surface forcing file created by ROMS-Tools
    roms_tools_version:    3.6.1.dev17+g97b5fe39e
    start_time:            2012-01-01 00:00:00
    end_time:              2012-01-02 00:00:00
    source:                ERA5
    correct_radiation:     True
    wind_dropoff:          False
    use_coarse_grid:       False
    model_reference_date:  2000-01-01 00:00:00
    type:                  physics, <xarray.Dataset> Size: 10kB
Dimensions:       (time: 12, eta_rho: 4, xi_rho: 8)
Coordinates:
    month         (time) int32 48B ...
    abs_time      (time) datetime64[ns] 96B ...
    pco2_time     (time) float64 96B ...
    iron_time     (time) float64 96B ...
    dust_time     (time) float64 96B ...
    nox_time      (time) float64 96B ...
    nhy_time      (time) float64 96B ...
Dimensions without coordinates: time, eta_rho, xi_rho
Data variables:
    pco2_air      (time, eta_rho, xi_rho) float32 2kB ...
    dust          (time, eta_rho, xi_rho) float32 2kB ...
    iron          (time, eta_rho, xi_rho) float32 2kB ...
    nox           (time, eta_rho, xi_rho) float32 2kB ...
    nhy           (time, eta_rho, xi_rho) float32 2kB ...
    pco2_air_alt  (time, eta_rho, xi_rho) float32 2kB ...
Attributes:
    title:                 ROMS surface forcing file created by ROMS-Tools
    roms_tools_version:    3.6.1.dev17+g97b5fe39e
    start_time:            2012-01-01 00:00:00
    end_time:              2012-01-02 00:00:00
    source:                UNIFIED
    correct_radiation:     False
    wind_dropoff:          False
    use_coarse_grid:       False
    model_reference_date:  2000-01-01 00:00:00
    type:                  bgc
    climatology:           True]
----------------------------------------------------------------------------------------------------
forcing.boundary
[<xarray.Dataset> Size: 704B
Dimensions:     (bry_time: 1, s_rho: 3, xi_u: 7, xi_rho: 8, eta_rho: 4, eta_v: 3)
Coordinates:
    abs_time    (bry_time) datetime64[ns] 8B ...
  * bry_time    (bry_time) float64 8B 4.382e+03
Dimensions without coordinates: s_rho, xi_u, xi_rho, eta_rho, eta_v
Data variables: (12/14)
    u_north     (bry_time, s_rho, xi_u) float32 84B ...
    v_north     (bry_time, s_rho, xi_rho) float32 96B ...
    temp_north  (bry_time, s_rho, xi_rho) float32 96B ...
    salt_north  (bry_time, s_rho, xi_rho) float32 96B ...
    zeta_north  (bry_time, xi_rho) float32 32B ...
    ubar_north  (bry_time, xi_u) float32 28B ...
    ...          ...
    v_east      (bry_time, s_rho, eta_v) float32 36B ...
    temp_east   (bry_time, s_rho, eta_rho) float32 48B ...
    salt_east   (bry_time, s_rho, eta_rho) float32 48B ...
    zeta_east   (bry_time, eta_rho) float32 16B ...
    ubar_east   (bry_time, eta_rho) float32 16B ...
    vbar_east   (bry_time, eta_v) float32 12B ...
Attributes:
    title:                                ROMS boundary forcing file created ...
    roms_tools_version:                   3.6.1.dev17+g97b5fe39e
    start_time:                           2012-01-01 00:00:00
    end_time:                             2012-01-02 00:00:00
    source:                               GLORYS
    model_reference_date:                 2000-01-01 00:00:00
    apply_2d_horizontal_fill:             False
    adjust_depth_for_sea_surface_height:  False
    theta_s:                              5.0
    theta_b:                              2.0
    hc:                                   250.0, <xarray.Dataset> Size: 2kB
Dimensions:     (bry_time: 3, s_rho: 3, xi_u: 7, xi_rho: 8, eta_rho: 4, eta_v: 3)
Coordinates:
    abs_time    (bry_time) datetime64[ns] 24B ...
  * bry_time    (bry_time) float64 24B 4.383e+03 4.384e+03 4.385e+03
Dimensions without coordinates: s_rho, xi_u, xi_rho, eta_rho, eta_v
Data variables: (12/14)
    u_north     (bry_time, s_rho, xi_u) float32 252B ...
    v_north     (bry_time, s_rho, xi_rho) float32 288B ...
    temp_north  (bry_time, s_rho, xi_rho) float32 288B ...
    salt_north  (bry_time, s_rho, xi_rho) float32 288B ...
    zeta_north  (bry_time, xi_rho) float32 96B ...
    ubar_north  (bry_time, xi_u) float32 84B ...
    ...          ...
    v_east      (bry_time, s_rho, eta_v) float32 108B ...
    temp_east   (bry_time, s_rho, eta_rho) float32 144B ...
    salt_east   (bry_time, s_rho, eta_rho) float32 144B ...
    zeta_east   (bry_time, eta_rho) float32 48B ...
    ubar_east   (bry_time, eta_rho) float32 48B ...
    vbar_east   (bry_time, eta_v) float32 36B ...
Attributes:
    title:                                ROMS boundary forcing file created ...
    roms_tools_version:                   3.6.1.dev17+g97b5fe39e
    start_time:                           2012-01-01 00:00:00
    end_time:                             2012-01-02 00:00:00
    source:                               GLORYS
    model_reference_date:                 2000-01-01 00:00:00
    apply_2d_horizontal_fill:             False
    adjust_depth_for_sea_surface_height:  False
    theta_s:                              5.0
    theta_b:                              2.0
    hc:                                   250.0, <xarray.Dataset> Size: 56kB
Dimensions:            (bry_time: 12, s_rho: 3, xi_rho: 8, eta_rho: 4)
Coordinates:
    month              (bry_time) int32 48B ...
    abs_time           (bry_time) datetime64[ns] 96B ...
  * bry_time           (bry_time) float64 96B 15.5 45.0 74.5 ... 319.0 349.5
Dimensions without coordinates: s_rho, xi_rho, eta_rho
Data variables: (12/64)
    PO4_north          (bry_time, s_rho, xi_rho) float32 1kB ...
    NO3_north          (bry_time, s_rho, xi_rho) float32 1kB ...
    SiO3_north         (bry_time, s_rho, xi_rho) float32 1kB ...
    Fe_north           (bry_time, s_rho, xi_rho) float32 1kB ...
    O2_north           (bry_time, s_rho, xi_rho) float32 1kB ...
    DIC_north          (bry_time, s_rho, xi_rho) float32 1kB ...
    ...                 ...
    diazFe_east        (bry_time, s_rho, eta_rho) float32 576B ...
    spCaCO3_east       (bry_time, s_rho, eta_rho) float32 576B ...
    zooC_east          (bry_time, s_rho, eta_rho) float32 576B ...
    Lig_east           (bry_time, s_rho, eta_rho) float32 576B ...
    DIC_ALT_CO2_east   (bry_time, s_rho, eta_rho) float32 576B ...
    ALK_ALT_CO2_east   (bry_time, s_rho, eta_rho) float32 576B ...
Attributes:
    title:                                ROMS boundary forcing file created ...
    roms_tools_version:                   3.6.1.dev17+g97b5fe39e
    start_time:                           2012-01-01 00:00:00
    end_time:                             2012-01-02 00:00:00
    source:                               UNIFIED
    model_reference_date:                 2000-01-01 00:00:00
    apply_2d_horizontal_fill:             False
    adjust_depth_for_sea_surface_height:  False
    theta_s:                              5.0
    theta_b:                              2.0
    hc:                                   250.0
    climatology:                          True]
----------------------------------------------------------------------------------------------------
forcing.tidal
<xarray.Dataset> Size: 14kB
Dimensions:  (ntides: 15, eta_rho: 4, xi_rho: 8, xi_u: 7, eta_v: 3)
Coordinates:
  * ntides   (ntides) |S3 45B b'm2' b's2' b'n2' b'k2' ... b'ms4' b'2n2' b's1'
    omega    (ntides) float64 120B ...
Dimensions without coordinates: eta_rho, xi_rho, xi_u, eta_v
Data variables:
    ssh_Re   (ntides, eta_rho, xi_rho) float32 2kB ...
    ssh_Im   (ntides, eta_rho, xi_rho) float32 2kB ...
    pot_Re   (ntides, eta_rho, xi_rho) float32 2kB ...
    pot_Im   (ntides, eta_rho, xi_rho) float32 2kB ...
    u_Re     (ntides, eta_rho, xi_u) float32 2kB ...
    u_Im     (ntides, eta_rho, xi_u) float32 2kB ...
    v_Re     (ntides, eta_v, xi_rho) float32 1kB ...
    v_Im     (ntides, eta_v, xi_rho) float32 1kB ...
Attributes:
    title:                 ROMS tidal forcing created by ROMS-Tools
    roms_tools_version:    3.6.1.dev17+g97b5fe39e
    source:                TPXO
    model_reference_date:  2000-01-01 00:00:00
----------------------------------------------------------------------------------------------------
forcing.river
<xarray.Dataset> Size: 140kB
Dimensions:           (river_time: 12, nriver: 41, ntracers: 34, eta_rho: 4,
                       xi_rho: 8)
Coordinates:
    month             (river_time) int32 48B ...
    river_name        (nriver) object 328B ...
  * nriver            (nriver) int32 164B 1 2 3 4 5 6 7 ... 35 36 37 38 39 40 41
    abs_time          (river_time) datetime64[ns] 96B ...
  * river_time        (river_time) float64 96B 15.0 45.0 74.0 ... 319.0 349.0
    tracer_name       (ntracers) object 272B ...
    tracer_unit       (ntracers) object 272B ...
    tracer_long_name  (ntracers) object 272B ...
Dimensions without coordinates: ntracers, eta_rho, xi_rho
Data variables:
    river_volume      (river_time, nriver) float64 4kB ...
    river_tracer      (river_time, ntracers, nriver) float64 134kB ...
    river_index       (eta_rho, xi_rho) float32 128B ...
    river_fraction    (eta_rho, xi_rho) float32 128B ...
Attributes:
    climatology:  True
----------------------------------------------------------------------------------------------------
cdr_forcing
<xarray.Dataset> Size: 1kB
Dimensions:           (time: 2, ncdr: 1, ntracers: 34)
Coordinates:
  * time              (time) datetime64[ns] 16B 2012-01-01 2012-01-02
    release_name      (ncdr) object 8B ...
    tracer_name       (ntracers) object 272B ...
    tracer_unit       (ntracers) object 272B ...
    tracer_long_name  (ntracers) object 272B ...
Dimensions without coordinates: ncdr, ntracers
Data variables:
    cdr_time          (time) float64 16B ...
    cdr_lon           (ncdr) float64 8B ...
    cdr_lat           (ncdr) float64 8B ...
    cdr_dep           (ncdr) float64 8B ...
    cdr_hsc           (ncdr) float64 8B ...
    cdr_vsc           (ncdr) float64 8B ...
    cdr_trcflx        (time, ntracers, ncdr) float64 544B ...

Configure Build

Render the Jinja2 templates to generate compile-time and run-time configuration files (.opt files, roms.in, etc.). This prepares the BUILD stage.

# configure and build the model
ocn.configure_build(compile_time_settings={}, run_time_settings={})

Run C-Star

ocn.prep_cstar_environment(
   account_key = None,  # None gets from machine config or override here
   queue_name = None,  # None gets from machine config or override here
   walltime = "00:10:00",
   clobber = True,  # recommend True, but it will clear previous results from this run 
   n_procs_available = 0,  # 0 is auto-detect, change if on a login or shared node to not overuse resources
)
# Run C-Star via CLI
# This is recommended in order to get people used to using the CLI for these operations

import os
os.environ["THIS_BP_PATH"] = str(ocn.path_blueprint(stage="build"))

!echo $THIS_BP_PATH
!cstar blueprint run $THIS_BP_PATH
/Users/eilerman/git/cson-forge/cson_forge/catalog/blueprints/MacOS/cson_roms-marbl_v0.1_test-tiny_1procs/B_cson_roms-marbl_v0.1_test-tiny_1procs_build.yml
2026-05-04 16:20:26,675 [INFO] - worker.py:275 - Creating simulation runner for /Users/eilerman/git/cson-forge/cson_forge/catalog/blueprints/MacOS/cson_roms-marbl_v0.1_test-tiny_1procs/B_cson_roms-marbl_v0.1_test-tiny_1procs_build.yml
2026-05-04 16:20:26,893 [INFO] - simulation.py:1288 - 🛠️ Configuring ROMSSimulation
2026-05-04 16:20:26,893 [INFO] - simulation.py:1291 - 🔧 Setting up ROMSExternalCodeBase...
2026-05-04 16:20:36,304 [INFO] - simulation.py:1291 - 🔧 Setting up MARBLExternalCodeBase...
2026-05-04 16:20:50,209 [INFO] - simulation.py:1305 - 📦 Fetching compile-time code...
2026-05-04 16:20:50,214 [INFO] - simulation.py:1310 - 📦 Fetching runtime code... 
2026-05-04 16:20:50,215 [INFO] - simulation.py:1315 - 📦 Fetching input datasets...
2026-05-04 16:20:50,216 [INFO] - input_dataset.py:97 - 🔗 Created symlink: /Users/eilerman/cson-forge-data/cson-forge-run/cson_roms-marbl_v0.1_test-tiny_1procs_20120101-20120102/work/cdr.nc → /Users/eilerman/cson-forge-data/cson-forge-run/cson_roms-marbl_v0.1_test-tiny_1procs_20120101-20120102/input/input_datasets/cson_roms-marbl_v0_1_test-tiny_1procs_cdr.nc
2026-05-04 16:21:01,300 [INFO] - input_dataset.py:279 - Partitioning /Users/eilerman/cson-forge-data/cson-forge-run/cson_roms-marbl_v0.1_test-tiny_1procs_20120101-20120102/input/input_datasets/cson_roms-marbl_v0_1_test-tiny_1procs_grid.nc into (1,1)
2026-05-04 16:21:01,593 [INFO] - input_dataset.py:279 - Partitioning /Users/eilerman/cson-forge-data/cson-forge-run/cson_roms-marbl_v0.1_test-tiny_1procs_20120101-20120102/input/input_datasets/cson_roms-marbl_v0_1_test-tiny_1procs_initial_conditions.nc into (1,1)
2026-05-04 16:21:01,620 [INFO] - input_dataset.py:279 - Partitioning /Users/eilerman/cson-forge-data/cson-forge-run/cson_roms-marbl_v0.1_test-tiny_1procs_20120101-20120102/input/input_datasets/cson_roms-marbl_v0_1_test-tiny_1procs_tidal.nc into (1,1)
2026-05-04 16:21:01,627 [INFO] - input_dataset.py:279 - Partitioning /Users/eilerman/cson-forge-data/cson-forge-run/cson_roms-marbl_v0.1_test-tiny_1procs_20120101-20120102/input/input_datasets/cson_roms-marbl_v0_1_test-tiny_1procs_river.nc into (1,1)
2026-05-04 16:21:01,635 [INFO] - input_dataset.py:279 - Partitioning /Users/eilerman/cson-forge-data/cson-forge-run/cson_roms-marbl_v0.1_test-tiny_1procs_20120101-20120102/input/input_datasets/cson_roms-marbl_v0_1_test-tiny_1procs_boundary-physics_201112.nc into (1,1)
2026-05-04 16:21:01,645 [INFO] - input_dataset.py:279 - Partitioning /Users/eilerman/cson-forge-data/cson-forge-run/cson_roms-marbl_v0.1_test-tiny_1procs_20120101-20120102/input/input_datasets/cson_roms-marbl_v0_1_test-tiny_1procs_boundary-physics_201201.nc into (1,1)
2026-05-04 16:21:01,655 [INFO] - input_dataset.py:279 - Partitioning /Users/eilerman/cson-forge-data/cson-forge-run/cson_roms-marbl_v0.1_test-tiny_1procs_20120101-20120102/input/input_datasets/cson_roms-marbl_v0_1_test-tiny_1procs_boundary-bgc_clim.nc into (1,1)
2026-05-04 16:21:01,786 [INFO] - input_dataset.py:279 - Partitioning /Users/eilerman/cson-forge-data/cson-forge-run/cson_roms-marbl_v0.1_test-tiny_1procs_20120101-20120102/input/input_datasets/cson_roms-marbl_v0_1_test-tiny_1procs_surface-physics_201112.nc into (1,1)
2026-05-04 16:21:01,794 [INFO] - input_dataset.py:279 - Partitioning /Users/eilerman/cson-forge-data/cson-forge-run/cson_roms-marbl_v0.1_test-tiny_1procs_20120101-20120102/input/input_datasets/cson_roms-marbl_v0_1_test-tiny_1procs_surface-physics_201201.nc into (1,1)
2026-05-04 16:21:01,803 [INFO] - input_dataset.py:279 - Partitioning /Users/eilerman/cson-forge-data/cson-forge-run/cson_roms-marbl_v0.1_test-tiny_1procs_20120101-20120102/input/input_datasets/cson_roms-marbl_v0_1_test-tiny_1procs_surface-bgc_clim.nc into (1,1)
2026-05-04 16:21:01,813 [INFO] - simulation.py:1671 - Running mpirun -n 1 ./roms cstar_generated_roms.in
2026-05-04 16:21:06,830 [WARNING] - handler.py:152 - This job is currently not running (completed). Live updates cannot be provided. See /Users/eilerman/cson-forge-data/cson-forge-run/cson_roms-marbl_v0.1_test-tiny_1procs_20120101-20120102/logs/cstar_worker_20260504_222022.out for job output
2026-05-04 16:21:06,830 [INFO] - worker.py:226 - Simulation is not running (completed). Allowing shutdown.
2026-05-04 16:21:06,830 [INFO] - worker.py:226 - Simulation is not running (completed). Allowing shutdown.
2026-05-04 16:21:06,830 [INFO] - service.py:319 - Shutting down service.
2026-05-04 16:21:06,830 [INFO] - service.py:319 - Shutting down service.
2026-05-04 16:21:06,833 [INFO] - simulation.py:92 - Joining netCDF files output_cdr.20120101020000.*.nc...
2026-05-04 16:21:06,833 [INFO] - simulation.py:92 - Joining netCDF files output_rst.20120102000000.*.nc...
Numba: Attempted to fork from a non-main thread, the TBB library may be in an invalid state in the child process.
Numba: Attempted to fork from a non-main thread, the TBB library may be in an invalid state in the child process.
2026-05-04 16:21:07,160 [INFO] - simulation.py:101 - Done spatially joining /Users/eilerman/cson-forge-data/cson-forge-run/cson_roms-marbl_v0.1_test-tiny_1procs_20120101-20120102/output/output_cdr.20120101020000.nc
2026-05-04 16:21:07,162 [INFO] - simulation.py:101 - Done spatially joining /Users/eilerman/cson-forge-data/cson-forge-run/cson_roms-marbl_v0.1_test-tiny_1procs_20120101-20120102/output/output_rst.20120102000000.nc
2026-05-04 16:21:07,163 [INFO] - worker.py:97 - Completed simulation logs at: /Users/eilerman/cson-forge-data/cson-forge-run/cson_roms-marbl_v0.1_test-tiny_1procs_20120101-20120102/logs/cstar_worker_20260504_222022.out
2026-05-04 16:21:07,163 [INFO] - worker.py:97 - Completed simulation logs at: /Users/eilerman/cson-forge-data/cson-forge-run/cson_roms-marbl_v0.1_test-tiny_1procs_20120101-20120102/logs/cstar_worker_20260504_222022.out
2026-05-04 16:21:07,163 [INFO] - worker.py:100 - Simulation completed successfully.
2026-05-04 16:21:07,163 [INFO] - worker.py:100 - Simulation completed successfully.
Blueprint execution completed
# Or use the wrapper function on the SpecBuilder
# This might be needed if the environment on an HPC doesn't handle the !bash commands correctly

# await ocn.run()

# Note: if you want to call this run method from a non-notebook script, you need to do the following:
# import asyncio
# asyncio.run(ocn.run())

Visualize Model Output

After the model run completes, you can load and visualize the output data. The code below:

  1. Finds output files: Uses glob to locate all BGC (biogeochemical) output files in the JOINED_OUTPUT directory

  2. Opens the dataset: Uses xarray.open_mfdataset() to open multiple NetCDF files as a single dataset

  3. Applies land mask: Masks out land points using the grid’s mask_rho variable

  4. Plots a variable: Creates a plot of dissolved inorganic carbon (DIC) at the first time step and bottom vertical level (s_rho=-1)

The JOINED_OUTPUT directory contains the spatially-joined output files created by post_run(), which combine partitioned output files from parallel runs into single files.

str(ocn.run_output_dir / "joined_output" / (ocn.casename + "_bgc.*"))
'/Users/eilerman/cson-forge-data/cson-forge-run/cson_roms-marbl_v0.1_test-tiny_1procs_20120101-20120102/joined_output/cson_roms-marbl_v0.1_test-tiny_1procs_20120101-20120102_bgc.*'
# import xarray as xr
# from glob import glob

# files = glob(str(ocn.run_output_dir / "output" / ("output_bgc.*")))
# ds = xr.open_mfdataset(files)
# ds = ds.where(ocn.grid.ds.mask_rho)
# ds.DIC.isel(eta_rho=2, xi_rho=6, s_rho=-1).plot(marker="o", color="red")

CDR Plots

from roms_tools import ROMSOutput

roms_out = ROMSOutput(grid=ocn.grid, path=ocn.run_output_dir / "joined_output" / "output_cdr.*.nc", use_dask=True)
roms_out.cdr_metrics()
<Figure size 1000x400 with 1 Axes>

Set Blueprint State

The set_blueprint_state() method updates the state of the blueprint, which tracks the workflow stage of the simulation specification. Blueprint states indicate the current stage of the workflow (e.g., “draft”, “configured”, “ready”) and are used by the C-Star orchestration system to manage the simulation lifecycle.

Common blueprint states include:

  • "draft": The blueprint is in development and not yet finalized

  • "validated": The blueprint has been validated

Setting the state to "draft" is useful when you want to mark the blueprint as a work-in-progress that may need further modifications before execution.

ocn.set_blueprint_state(state="draft")

Save Executed Notebook

Save a timestamped copy of this notebook to executed/forge/{os}/ for reproducibility and record-keeping. The copy is organized by operating system (macOS or Ubuntu/Linux) to track execution history across different platforms.

The saved notebook includes all executed cells and outputs, providing a complete record of the simulation workflow for future reference.

# Save the notebook copy
cstar_forge.save_notebook_copy(notebook_name="CStarSpecBuilder-demo.ipynb")
Notebook copy saved to: executed/forge/MacOS/CStarSpecBuilder-demo_MacOS.ipynb
PosixPath('executed/forge/MacOS/CStarSpecBuilder-demo_MacOS.ipynb')