Seeding trajectories#

Seeding creates the initial ParticleState. In Fortran TRACMASS this is set by the INIT_SEEDING namelist group (seeding boxes ist1…kst2, SeedType, nqua, partQuant, seed files). In TRACMASS-JAX you call a function in tracmass.seed, or describe the seeding in an [env.seed] settings table.

All seeding functions return particles at time zero (tt = t0 = 0), active, with reason = 0.

Index conventions#

  • i, j, k are 1-based TRACMASS cell indices, as in Fortran.

  • With a grid that comes from a subset, the i/j you pass are absolute (full-grid) indices. They are converted with grid.i_offset / grid.j_offset. Pass use_subset_coordinates=True to give them relative to the subset instead.

  • k = 1 is the first model level. Cells with k > kmt (below the sea floor) and land cells are skipped when a grid is given.

Seeding from boxes — seed.ijk#

The counterpart of seeding from namelist boxes (ist1, ist2, jst1, jst2, kst1, kst2). One particle at the centre of every ocean cell in the box:

from tracmass import seed

state = seed.ijk(10, 20, 30, 40, 1, 1, grid=grid)          # 11 x 11 cells, level 1
state = seed.ijk(10, 20, 30, 40, 1, 5, grid=grid)          # levels 1-5
state = seed.ijk(10, 20, 30, 40, 1, 1, grid=grid,
                 use_subset_coordinates=True)               # box relative to the subset

From settings:

[myrun.seed]
approach = "ijk"
box = { i1 = 2610, i2 = 2620, j1 = 510, j2 = 520, k1 = 1, k2 = 1 }

Number of particles per cell#

Fortran sets the number of particles per cell with nqua and partQuant. TRACMASS-JAX places them on a regular sub-grid inside each cell, with sub_dist="rect_xyz" (the default) and the number in each direction:

state = seed.ijk(10, 20, 30, 40, 1, 1, grid=grid,
                 i_sub_num=4, j_sub_num=4, k_sub_num=1)    # 16 per cell

The particles sit at the centres of an i_sub_num × j_sub_num × k_sub_num sub-division of the cell, so with 1 in each direction (the default) there is one particle at the cell centre. Every seeding function except latlon_list accepts these options, and so does a settings table:

[myrun.seed]
approach = "ijk"
sub_dist = "rect_xyz"
i_sub_num = 40
j_sub_num = 40
k_sub_num = 1
box = { i1 = 2610, i2 = 2620, j1 = 510, j2 = 520, k1 = 1, k2 = 1 }

Each particle carries subvol (Fortran’s transport per trajectory). Seeding by transport (Fortran nqua = 2, 3) is not implemented yet.

Note

sub_dist="gaus_xyz" (Gaussian sub-cell positions) is not working yet; it is marked as an expected failure in tests/test_seed.py.

Seeding in geographic coordinates#

These approaches need an adapter with a geographic grid (glorys, ecco2, scrippsgog). The adapter is taken from gcm= or from [env.gcm] name.

A lat/lon box — seed.latlon_box#

Every ocean cell whose centre lies in the box:

state = seed.latlon_box(-64, -62, -70, -68, grid=grid, k1=1,
                        i_sub_num=10, j_sub_num=10)

Longitudes run eastward from lon1 to lon2 in any convention, so lon1 > lon2 gives a box across the date line (e.g. 170 → -170).

[myrun.seed]
approach = "latlon_box"
i_sub_num = 10
j_sub_num = 10
box = { lat1 = -64, lat2 = -62, lon1 = -70, lon2 = -68, k1 = 1, k2 = 1 }

Around a point — seed.latlon_point#

A box of half-width dlat (and dlon, default dlat) around a point:

state = seed.latlon_point(-63.0, -69.0, dlat=0.5, grid=grid)
[myrun.seed]
approach = "latlon_point"
lat = -63.0
lon = -69.0
dlat = 0.5

At exact positions — seed.latlon_list#

One particle at each listed position, at the exact fractional position rather than the cell centre. A point on land moves to the centre of the nearest ocean cell.

import pandas as pd

df = pd.DataFrame({"lat": [-63.0, -62.5], "lon": [-69.0, -68.2]})
state = seed.latlon_list(df, grid=grid)                    # level kpos=1
state = seed.latlon_list(seed_file="stations.csv", grid=grid)
[myrun.seed]
approach = "latlon_list"
seed_file = "stations.csv"     # lat, lon (and optionally k) columns
kpos = 1                       # level when the file has no k column
# or inline:  lat = [-63.0, -62.5]   lon = [-69.0, -68.2]

Seeding from a mask — seed.mask#

The counterpart of Fortran’s maskfile. One particle (or one sub-grid) per True cell of a boolean array:

from tracmass.seagrid import ecco2

kmt = ecco2.grid_definition().kmt.values      # (jmt, imt), as read from file
state = seed.mask(kmt > 0, kpos=1)            # every ocean cell at level 1

Important

The mask must be in the data’s own orientation, latitude first (jmt, imt) (or (jmt, imt, km) for 3-D). This is the opposite of GridFields.kmt, which is (imt, jmt). A warning is raised if the first axis is longer than the second, which usually means the mask was transposed. Use seed.ijk(..., grid=grid) to seed from grid.kmt.

Seeding from a file — seed.from_df#

The counterpart of Fortran’s seed file (SeedType = 2). Cells are given by the integer columns of a DataFrame:

df = pd.read_csv("seed_cells.csv")                         # columns i, j, k
state = seed.from_df(df, i_col_name="i", j_col_name="j", k_col_name="k")

seed.from_df_xyz takes fractional positions instead, and places the particles exactly there.

Repeated releases — seed_freq#

Fortran releases particles at several times with tst1, tst2 or a timeFile. In TRACMASS-JAX, seed_freq releases the initial particles again every seed_freq, a pandas frequency counted from dtm_start (backward in time for a backward run):

[gog_upwelling.seed]
approach = "ijk"
seed_freq = "30D"         # or "7D", "MS" (month starts), "QS", ...
box = { i1 = 3323, i2 = 3589, j1 = 1390, j2 = 1603, k1 = 23, k2 = 23 }
tracmass.time_dependent(state, grid, lazy, seed_freq="MS")
df = tracmass.load(env="gog_upwelling")
df.groupby("t0").id.nunique()     # one cohort per release
  • Each release is a copy of the initial state, appended after the existing particles at the start of the window the seed time falls in. A seed time inside a window moves to the next window start, with a warning.

  • A released particle’s clock starts at the release (tt = t0 = release time), so t0 tells the cohorts apart, and traj_max_time counts from each particle’s own release.

  • Existing particle ids do not change, and new particles get the next unused ids.

  • The run keeps going through windows with no active particles while a release is still to come.

  • The file metadata gets seed_freq and seed_windows.

  • Each release makes the particle arrays bigger, so the kernel recompiles once per release.

Seeding with a function in tracmass.run#

tracmass.run also accepts a function of the grid, which is called once the grid exists. This is handy when the seeding needs the grid but you still want the rest of the run to come from settings:

result = tracmass.run(
    "2020-07-01", env="tempvel",
    state=lambda grid: seed.ijk(10, 20, 10, 20, 1, 1, grid=grid),
)