Prescribed ocean geometry and custom fields

To define a custom experiment, supply geometry (the grid’s shape and sizes), masks (values marking ocean and land), starting model values and prescribed fields (inputs supplied to the model). A grid divides the modeled area into cells. You can do this without importing internal modules or editing Veris source. This small CPU (computer processor) example uses arrays (tables of numbers) for physical cells in (x, y) order, with the x direction first:

import numpy as np
from veris import Execution, Experiment, Forcing, Grid, OceanGeometry, Simulation

grid = Grid(6, 8)
shape = (grid.nx, grid.ny)
wet = np.ones(shape, dtype=np.float32)
wet[2:4, 3:5] = 0
dx = np.full(grid.nx, 8000, dtype=np.float32)
dy = np.full(grid.ny, 9000, dtype=np.float32)
area = dx[:, None] * dy[None, :]
geometry = OceanGeometry(
    maskT=wet[..., None],
    maskU=(wet * np.roll(wet, 1, axis=0))[..., None],
    maskV=(wet * np.roll(wet, 1, axis=1))[..., None],
    ht=np.full(shape, -100, dtype=np.float32),
    coriolis_t=np.full(shape, 1e-4, dtype=np.float32),
    dxt=dx, dxu=dx, dyt=dy, dyu=dy,
    area_t=area, area_u=area, area_v=area,
)
experiment = Experiment(
    grid=grid, geometry=geometry, physics="coupled", steps=3,
    settings={"dtype": "float32", "deltatDyn": 600,
              "deltatTherm": 600, "nEVPsteps": 2},
    initial_conditions={"hIceMean": 1.0, "hSnowMean": 0.05,
                        "Area": 0.8, "TSurf": 260},
    forcing=Forcing(
        constant={"uWind": 4, "vWind": 0, "theta": 271.35,
                  "ocSalt": 34.7, "ATemp": 260, "LWdown": 260,
                  "aqh": 0.001, "Qsw": 0},
        series={"Qnet": [80, 100, 120]},
    ),
    execution=Execution(backend="cpu"),
)
with Simulation.initialize(experiment) as simulation:
    result = simulation.run()
    simulation.snapshot("custom-final.nc",
                        variables=("hIceMean", "Area", "iceMask"))
assert result.completed_steps == 3

maskT, maskU and maskV mark ocean at cell centers, west faces (western edges) and south faces (southern edges). They have shape (nx, ny, z); their last level is the surface. Zero means dry. Fractional masks keep the model’s existing weighting of calculations. The model sets initial ice amounts to zero on dry cells and ice velocities to zero on blocked faces. Spacing vectors list distances in meters; cell/face areas use m². Bottom elevation ht uses meters. Coriolis frequency, which describes the effect of Earth’s rotation, uses s⁻¹. Supply positive spacings/areas and finite masks (no infinite or undefined values) in [0, 1]. Initialization calculates corner metrics (grid measurements at cell corners), reciprocals (one divided by a value), ice load (mass per area), inverse thickness and wind magnitude (speed). It also prepares field-specific boundary halos, extra cells at grid edges set according to each field’s boundary rules.

All ice and prescribed spatial fields are scalars (single values) or (nx, ny) arrays. Time-varying forcing, or supplied inputs that change with time, has shape (time,) or (time, nx, ny). The model selects values using the total number of steps already completed, including earlier runs. Series override constants with the same name. Thickness is averaged over the whole grid cell and measured in meters. Area is concentration, the fraction covered by ice, in [0, 1]. Temperatures are kelvin and salinity is g kg⁻¹. Heat fluxes (heat transferred per area per time) are W/m², positive upward. Coupled execution, which combines ice motion and growth, requires equal dynamics/thermodynamic timesteps (intervals of simulated time). Supported names and more input checks are in Experiment definitions.

enable_cyclic_y=False closes the north and south (meridional) boundaries. Initialization and execution apply wall rules to staggered fields, which store values at different grid locations. This includes the physical north-wall shear traction (force per area along the wall) stored in its first halo column. With an explicit mesh (a group of devices), internal boundaries between grid partitions exchange values between neighboring partitions.

To save a definition you can load later, write experiment.to_dict() as JSON, a text format, and load it with Experiment.from_file. JSON stores arrays as lists. A complete restart file stores numerical inputs with their types intact and preserves the evolved State (the arrays holding the model’s current values). For very large inputs, build the Python definition from your data loader using the physical shapes above. Use Simulation.load_fields to load selected fields from netCDF, a file format for arrays. Changing geometry requires a new initialization.