Setting up an experiment#

In Fortran TRACMASS an experiment is a project (the GCM-specific code in projects/<NAME>/) plus a case (a namelist). You choose them in Makefile.prj and recompile. TRACMASS-JAX splits things the same way, but nothing is compiled ahead of time:

Fortran TRACMASS

TRACMASS-JAX

PROJECT = NEMO in Makefile.prj

[env.gcm] name = "glorys", an adapter module in tracmass.gcm

CASE = ..., namelist_CASE.in

a named environment [env] in tracmass.toml

make && ./runtracmass

tracmass.run(env="...")

This page builds a regional GLORYS12 experiment step by step. Swap glorys for ecco2, scrippsgog or theoretical to use another GCM.

1. Pick an environment name and a settings file#

Settings live in TOML files read by Dynaconf. Each top-level table is an environment, and [default] holds values shared by all of them. Put a tracmass.toml in the directory you run from:

[default]
base_output_dir = "/data/raid/trm/"
runmonitor_backend = "rich"

[omi]
dtm_start = "2010-06-01"
runtime = "180D"            # 180 days forward; "-180D" runs backward
move_vertically = false     # keep particles on their seeding level
output_name = "trm_omi"

You can also put the [omi] table in its own trm_omi.toml next to a shared tracmass.toml. Every trm_*.toml in the current directory is read too (see Settings files).

2. Choose the GCM and the region#

The [env.gcm] table names the adapter, and its optional subset limits the part of the grid that is read:

[omi.gcm]
name = "glorys"
subset = { i1 = 2600, i2 = 3700, j1 = 500, j2 = 1600, k1 = 1, k2 = 2 }

i1/i2/j1/j2 are 0-based Python slice limits on the model’s x/y axes, and k1/k2 are 1-based levels. A subset makes every window much cheaper to read. See the Subsets tutorial for details, including what happens at the edges.

3. Seed the particles#

The [env.seed] table says where particles start:

[omi.seed]
approach = "latlon_box"     # or "ijk", "latlon_point", "latlon_list"
i_sub_num = 10              # 10 x 10 particles per cell
j_sub_num = 10
box = { lat1 = -64, lat2 = -62, lon1 = -70, lon2 = -68, k1 = 1, k2 = 1 }

See the Seeding tutorial for every approach, sub-cell distributions and repeated releases (seed_freq).

4. Choose the integrator and its options#

[omi]
integrator = "time_dependent_patched"   # full grid / patched / streaming
traj_max_time = "90D"                   # stop each particle 90 days after release
rampup_windows = 2                      # denser output in the first two days

Integrator

Reads per window

Good for

time_dependent (default)

the whole (sub)grid

small grids, subsets, particles everywhere

time_dependent_patched

a box around the particles

clustered particles on a large grid

time_dependent_streaming

the six faces of each particle’s cell

scattered particles, very long runs

See Memory-efficient integration.

5. Run#

import tracmass

result = tracmass.run(env="omi")                        # all from settings
result = tracmass.run("2011-06-01", env="omi")          # override the start date
result = tracmass.run(env="omi", iter_sub=4, verbose=True)

tracmass.run resolves every argument you leave out from the active environment:

Argument

From settings when omitted

gcm

[env.gcm] name

dtm_start

dtm_start, else the adapter’s own default date

dtm_end

dtm_end, else dtm_start + runtime, else one window

integrator

integrator, default "time_dependent"

state

the [env.seed] table

verbose

verbose, default false

Other keyword arguments, such as iter_sub, patch_buffer or n_windows, go straight to the integrator. The integrator in turn reads monitor, recorder, no_wflux, n_particle_batches, traj_max_time, seed_freq and the ramp-up options from the settings when they are not passed.

6. Find the output#

With output_name set, the trajectories are written to

{base_output_dir}/{output_name}_{forward|reverse}_{dtm_start}.parquet

e.g. /data/raid/trm/trm_omi_forward_2010-06-01.parquet. Read them back with

df = tracmass.load(env="omi")                   # newest omi file
df = tracmass.load(env="omi", lonlat=True)      # with lon/lat columns

See Output.

The same thing in plain Python#

Every step above has a Python equivalent. That is useful in notebooks and for experiments that settings cannot express:

import tracmass
from tracmass import gcm, seed

tracmass.settings.setenv("omi")                      # optional
grid, lazy = tracmass.new_flux(
    "glorys", dtm_start="2010-06-01", dtm_end="2010-11-28"
)
state = seed.latlon_box(-64, -62, -70, -68, grid=grid, gcm="glorys",
                        i_sub_num=10, j_sub_num=10)
result = tracmass.time_dependent_patched(
    state, grid, lazy, iter_sub=4,
    monitor="rich", recorder="omi_test.parquet",
)