Quickstart Guide
mom6_forge library can be utilized via its Python API, i.e., directly within Python
scripts or within Jupyter notebooks. In this quickstart guide, we describe how
the tool can be utilized within a Jupyter Notebook, but the majority of
these instructions apply to Python scripts as well.
Step 1: Import modules
The first step is to import the Grid and Topo classes of the
mom6_forge package. The Grid class represents
horizontal MOM6 grids, and is to be instantiated with the desired grid
configuration and resolution. After creating a grid instance, a Topo class
instance is to be created to generate an associated bathymetry.
from mom6_forge.grid import Grid
from mom6_forge.topo import Topo
Step 2: Create the horizontal grid
After having imported the modules, we can now create a horizontal grid. An example Grid instantiation:
grid = Grid(
nx = 180, # Number of grid points in x direction
ny = 80, # Number of grid points in y direction
lenx = 360.0, # grid length in x direction, e.g., 360.0 (degrees)
leny = 160, # grid length in y direction
cyclic_x = True, # reentrant, spherical domain
ystart = -80.0 # start/end 10 degrees above/below poles to avoid singularity
)
In the above example, the Grid object, named grid, is constructed by
specifying the required arguments nx, ny, config, axis_units, lenx,
and leny, in addition to the optional argument ystart. The full list of
Grid arguments and their descriptions may be printed by running
Grid? statement on a notebook cell:
Grid?
...
Parameters
----------
nx : int
Number of grid points in x direction
ny : int
Number of grid points in y direction
lenx : float
grid length in x direction, e.g., 360.0 (degrees)
leny : float
grid length in y direction, e.g., 160.0 (degrees)
srefine : int, optional
refinement factor for the supergrid. 2 by default
xstart : float, optional
starting x coordinate. 0.0 by default.
ystart : float, optional
starting y coordinate. -0.5*leny by default.
cyclic_x : bool, optional
flag to make the grid cyclic in x direction. False by default.
tripolar_n : bool, optional
flag to make the grid tripolar. False by default.
displace_pole : bool, optional
flag to make the grid displaced polar. False by default.
Note that tripolar and displaced pole grids cannot yet be created from scratch, but existing tripolar and displaced pole grids can be modified via mom6_forge.
Instead of specifying nx/ny directly, they can be derived from a target
resolution (in the grid’s axis units):
grid = Grid(lenx=20.0, leny=10.0, resolution=0.25, xstart=270.0, ystart=10.0)
Alternative Grid Constructors
Beyond the uniform-degree grid shown above, Grid provides several alternative
constructors for domains that aren’t naturally expressed as nx/ny/lenx/leny:
Grid.from_center(center_lat, center_lon, width_m, height_m, resolution_m, angle_deg=0.0)— builds a rectangular grid centred at a geographic point using an azimuthal equidistant projection, optionally rotatedangle_degdegrees clockwise from north. Useful for aligning a regional domain with a coastline or estuary.Grid.from_projection(crs, x_min, x_max, y_min, y_max, resolution_m)— builds a uniform grid in a givenpyprojCRS (e.g. a polar stereographic or Lambert conformal projection) and reprojects it to geographic coordinates, remaining accurate at high latitudes.Grid.from_supergrid(path)— loads a grid from an existing MOM6 supergrid file.Grid.from_esmf_mesh(path)— loads a grid from an existing ESMF mesh file.
Example:
grid = Grid.from_center(
center_lat=44.9,
center_lon=-63.5,
width_m=400_000,
height_m=250_000,
resolution_m=5_000,
angle_deg=30.0,
)
These constructors are also available interactively through the GridCreator
widget — see UI Widgets.
Avoiding singularity points
To avoid singularity points within the ocean grid:
The grid poles (which may be different than the true poles) must be left out of the grid, by making sure that the extent of the grid in the y-direction do not cover the poles, e.g., by setting
ystartto -80.0 degrees andlenyto 160.0 degrees.Alternatively, one or two singularities (typically, in the northern hemisphere) may be displaced into land masses if
displace_poleortripolar_noptions are to be used. The other singularity (typically, in the southern hemisphere) would still need to be left out the geographic extent of the grid.
If a singularity (a pole) is present within the ocean grid, a land component (active or data) must be added to the pose of hiding the singularity points of spherical ocean grids within the CESM framework.
Grid Metrics and Attributes
When a Grid instance gets created, several grid metrics and attributes
on all staggerings are automatically computed and populated. These metrics and attributes
are accessible via the accessor operator (.). For example, to access “the array
of t-grid longitutes” of grid:
grid.tlon
The full list of grid metrics and attributes:
tlon: array of t-grid longitudestlat: array of t-grid latitudesulon: array of u-grid longitudesulat: array of u-grid latitudesvlon: array of v-grid longitudesvlat: array of v-grid latitudesqlon: array of corner longitudesqlat: array of corner latitudesdxt: x-distance between U points, centered at tdyt: y-distance between V points, centered at tdxCv: x-distance between q points, centered at vdyCu: y-distance between q points, centered at udxCu: x-distance between y points, centered at udyCv: y-distance between t points, centered at vangle: angle T-grid makes with latitude linetarea: T-cell area
Supergrid
In addition to above grid metrics and attributes, the Grid class incorporates an
underlying supergrid instance associated the grid instance, which is again
accessible via the (.) operator:
grid.supergrid
Any user changes to coordinates, e.g., increasing the equatorial resolution,
must be applied to the supergrid using the update_supergrid method. This is
because the supergrid is the underlying refined grid that is used to determine the
the four staggered grids (T,U,V,Q) that forms the actual computational grid.
Users can modify the supergrid by providing a new x and y coordinate arrays, e.g.,
as follows:
grid.update_supergrid(xdat, ydat)
where xdat and ydat are user-defined 2-dimensional numpy arrays containing
the new x and y coordinates of the supergrid. Running the update_supergrid
method of a Grid instance automatically updates all other grid metrics listed
above.
Step 3: Create the Vertical Grid
mom6_forge also provides a VGrid class to define the vertical layering of
the ocean model, independently of the horizontal Grid.
from mom6_forge.vgrid import VGrid
A vertical grid is fundamentally an array of layer thicknesses (dz, in
meters). VGrid provides two constructors for generating common vertical
spacings, plus a way to load one from an existing file.
Uniform Vertical Grid
vgrid = VGrid.uniform(nk=75, depth=6000.0)
nk is the number of vertical levels and depth is the total depth of the
water column (meters). The bottom layer’s thickness is adjusted slightly so
that the sum of all layers exactly equals depth.
Hyperbolic (Stretched) Vertical Grid
To concentrate resolution near the surface, use a hyperbolic-tangent profile,
where ratio is the target ratio of the bottom layer’s thickness to the top
layer’s thickness:
vgrid = VGrid.hyperbolic(nk=75, depth=6000.0, ratio=10.0)
Loading from an Existing File
vgrid = VGrid.from_file(
"existing_vgrid.nc",
variable_name="dz",
variable_type="layer_thickness", # or "cell_center" or "cell_interface"
)
Attributes
vgrid.dz: array of layer thicknesses (meters)vgrid.nk: number of vertical levelsvgrid.depth: total water column depth (meters)vgrid.zl: array of layer-center depths (meters)vgrid.zi: array of layer-interface depths (meters), sizenk + 1
Writing the Vertical Grid File
vgrid.write("my_vgrid.nc")
vgrid.write_z_file("my_vgrid_z.nc") writes an alternative file containing
the interface (zi) and center (zl) depths directly, rather than
thicknesses.
Like horizontal grids, vertical grids can also be created and edited
interactively via the VGridCreator widget — see UI Widgets.
Step 4: Create Bathymetry
After having generated the horizontal grid, we can now create an associated bathymetry object as follows:
topo = Topo(grid, min_depth=10.0)
The first argument (grid) of Topo constructor is the horizontal grid instance for which
the bathymetry is to be created, while the second argument (min_depth) is the minimum ocean depth.
Any column in the ocean grid with a depth shallower than min_depth is masked out of the ocean
domain. The minimum depth attribute of a bathymetry instance may be changed afterwards using the
assignment operator. For example:
topo.min_depth = 5.0
Predefined Bathymetry Configurations
The Topo class provides three predefined bathymetry configurations, which are also
available in MOM6 as idealized configurations. (See TOPO_CONFIG parameter in MOM_input)
flat: flat bottom set to MAXIMUM_DEPTH. Example:bowl: an analytically specified bowl-shaped basin ranging between MAXIMUM_DEPTH and MINIMUM_DEPTH.spoon: a similar shape to ‘bowl’, but with an vertical wall at the southern face.
Examples:
# flat bottom
topo.set_flat(D=500.0)
# bowl
topo.set_bowl(500.0, 50.0, expdecay=1e7)
# spoon
topo.set_spoon(500.0, 50.0, expdecay=1e7)
The first and the second arguments of set_bowl and set_spoon methods are maximum depth
and minimum depth, respectively.
Check out the following notebook to see examples of above predefined bathymetry options: 1_spherical_grid.ipynb
Custom Bathymetry
In addition to the above predefined configurations, users may provide their own depth arrays. For example:
import numpy as np
# define a custom depth
i = grid.tlat.nx.data # array of x-indices
j = grid.tlat.ny.data[:,np.newaxis] # array of y-indices
custom_depth = 400.0 + 80.0 * np.sin(i*np.pi/6.) * np.cos(j*np.pi/6.)
# update the bathymetry:
topo.depth = custom_depth
Adding ridges
Simpler model bathymetry configurations typically include ridges to represent straits and
continents in an idealized manner. The Topo class provides apply_ridge method
to add ridges to the bathymetry. Example usage:
topo.apply_ridge(height=200, width=8, lon=240, ilat=(10,80) )
Example notebook: 3_custom_bathy.ipynb
Bathymetry from a Real Dataset
For regional or realistic configurations, bathymetry is usually derived from
an observational dataset (e.g., GEBCO) rather than an idealized shape. The
Topo.set_from_dataset method is a high-level, opinionated workflow that sets
both the depth and the land/ocean mask from a source dataset in a single
call, automatically choosing a masking and interpolation strategy based on how
the source dataset’s resolution compares to the model grid’s:
topo.set_from_dataset(
bathymetry_path="GEBCO_2023.nc",
longitude_coordinate_name="lon",
latitude_coordinate_name="lat",
vertical_coordinate_name="elevation",
fill_channels=True,
)
By default, set_from_dataset diagnoses whether the source dataset is much
finer-resolution than the model grid and picks accordingly:
If the grid is coarse relative to the source (each cell spans many source pixels — the model resolution is 12x or more coarser than the dataset’s), it derives an ocean-fraction mask from sub-sampling statistics and fills depth via Cressman distance-weighted interpolation (mirroring the
tx2_3high-resolution topography workflow).Otherwise (the grid and source are closer in resolution, or the grid is finer than the source), it uses a Natural Earth land mask and a direct xESMF regrid of depth.
See the Bathymetry Workflow guide for the full decision flow (including
how to override each choice independently) and the Regridding & Mapping guide and
8_cressman_interpolation.ipynb notebook for the Cressman math.
Both the masking method (mask_method: 'naturalearth', 'ocean_frac',
'dataset', or 'manual') and the depth method (depth_method: 'stats',
'cressman', or 'xesmf') can be overridden explicitly instead of relying on
the automatic diagnosis. This is still an opinionated, multi-step workflow —
inspect the resulting mask and depth afterward, and adjust manually (e.g., via
TopoEditor) as needed. For the reasoning behind this workflow and how it
relates to NCAR’s global bathymetry pipeline, see Bathymetry Workflow.
Channel Width Constraints
Some straits and channels are too narrow to be resolved at a given grid
resolution, but still need to permit flow in MOM6. ChannelWidth and
ChannelWidthList (in mom6_forge.channel_width) record effective
channel-width overrides that are applied on top of the bathymetry at runtime,
rather than by editing the depth/mask fields themselves:
from mom6_forge.channel_width import ChannelWidth, ChannelWidthList
channel_widths = ChannelWidthList()
channel_widths.add(
ChannelWidth(
component="U_width",
lon1=-6.50, lon2=-4.75,
lat1=35.60, lat2=36.30,
width=12000.0,
place="Strait of Gibraltar",
)
)
A ChannelWidthList can be passed directly to the Topo constructor
(Topo(grid, min_depth=10.0, channel_widths=channel_widths)) or loaded from
an existing ASCII file (ChannelWidthList(filepath="channel_widths.txt")).
Unlike depth/mask edits, channel width constraints are not tracked by the
Topo version-control history (see UI Widgets) — they must be written out
separately:
topo.channel_widths.write("channel_widths.txt")
Step 5: Write Model Input Files
The final step of mom6_forge workflow is to write out the netcdf files containing grid
and bathymetry data. These files are to be read in by CESM and MOM6 during runtime.
Supergrid File
The write_supergrid method of a Grid instance writes out the MOM6 supergrid file
in netcdf format. The GRID_FILE parameter in MOM_input file can then be set to
the path of the supergrid file written by the Grid instance.
grid.write_supergrid("my_ocean_hgrid.nc")
The supergrid file is the only input file that is written by the Grid class. All other
input files require either topography (depth) or mask information. Hence, they are to be
written by the Topo class.
Topography (Bathymetry) File
The write_topo method of the Topo class writes out the MOM6 bathymetry file in netcdf format.
TOPO_FILE parameter in MOM_input file can then be set to the path of the topography file
written by the Topo instance.
topo.write_topo("my_ocean_topog.nc")
CICE grid file
If the model is to be run with the CICE component, the write_cice_grid method of the
Topo class writes out the CICE grid file in netcdf format. The relevant CICE namelist
parameters can then be updated to read in the CICE grid file written by the Topo instance.
topo.write_cice_grid("my_cice_grid.nc")
ESMF Mesh file
In addition to the MOM6 supergrid file, MOM6 topography file and CICE grid file, an
ESMF mesh file is required when running CESM. The ESMF mesh file is used
by the NUOPC coupler to acquire grid and mask information. The write_esmf_mesh method
of the Topo class writes out the ESMF mesh file in netcdf format.
topo.write_esmf_mesh("my_esmf_mesh.nc")
WW3 (WaveWatch III) Input Files
If the configuration includes a WaveWatch III wave component, the
write_ww3_input method of the Topo class writes the text-based WW3 grid
input files (ww3_grid.inp, and the <grid_alias>_x.inp, <grid_alias>_y.inp,
<grid_alias>_mapsta.inp, <grid_alias>_bottom.inp files) that WW3’s
mod_def creator reads before runtime.
topo.write_ww3_input("ww3_input/", grid_alias="my_grid")
By default the ocean cells on all four edges of a regional (non-cyclic) grid
are written as WW3 active boundary points (status 2 in
<grid_alias>_mapsta.inp); a cyclic-x grid gets none, so a global grid does
not declare boundary points it has no data for. These are
the points ww3_bounc can feed spectral boundary data (nest.ww3) into;
without such data WW3 leaves them at zero energy, which still absorbs
outgoing waves. To restrict the boundary to the edges you generate spectra
for, or to write a plain land/sea mask (ww3_grid then excludes the
perimeter sea cells), pass boundary_edges:
topo.write_ww3_input("ww3_input/", grid_alias="my_grid",
boundary_edges=["south", "west"]) # or [] for none
Edges are named by grid index (south is j=0, north is j=ny-1, west
is i=0, east is i=nx-1), so on a rotated or projected grid they need
not match compass directions. East/west cannot be listed on a grid that is
reentrant in x, and tripolar grids are not supported.
SCRIP Grid File
Modern CESM configurations use ESMF mesh files rather than SCRIP files for
most purposes, but a SCRIP file is still needed to generate custom
ocean-runoff mapping files (see Regridding & Mapping). The write_scrip_grid method
writes it out:
topo.write_scrip_grid("my_ocean_scrip.nc")
Step 6: Editing Grids and Bathymetry
Beyond creating standard grids and simple topographies, mom6_forge provides advanced tools for interactively editing and creating complex model domains. These features are designed to facilitate reproducible workflows for custom model configurations and model tuning. These domain configurators can be used for tasks such as:
Editing Bathymetry: Manually or programmatically modifying ocean depths.
Creating New Grids: Defining entirely new horizontal grid structures.
Creating Vertical Grids: Specifying the vertical layering of the ocean model.
Check out the notebook for examples of these advanced features: 7_demo_editors.ipynb
Further steps
The remaining steps of configuring the model, which include specifying initial conditions,
forcings, and runtime parameters, are beyond the scope of the mom6_forge tool. Note that a
complementary tool called visualCaseGen, which includes mom6_forge as a submodule, can be used
to generate a complete model configuration. visualCaseGen provides a graphical user interface
to set up the model grid, bathymetry, initial conditions, forcing, and runtime parameters for MOM6
and other CESM components. Hence, new users are encouraged to use visualCaseGen for a complete
model configuration. See: visualCaseGen