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,kare 1-based TRACMASS cell indices, as in Fortran.With a
gridthat comes from a subset, thei/jyou pass are absolute (full-grid) indices. They are converted withgrid.i_offset/grid.j_offset. Passuse_subset_coordinates=Trueto give them relative to the subset instead.k = 1is the first model level. Cells withk > kmt(below the sea floor) and land cells are skipped when agridis 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), sot0tells the cohorts apart, andtraj_max_timecounts 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_freqandseed_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),
)