tracmass.output, particle, monitoring#
Where trajectory files live, and loading them back.
The settings-driven recorder (see tracmass.runsetup) writes to
{base_output_dir}[/{output_name or env}]/{stem}_{forward|reverse}_{date}.parquet
where stem is output_name or trm_{env}, the sub-folder is present
when use_sub_folder is truthy, and date is the run’s start date.
output_folder() / output_stem() build that location from a
settings environment, and load() finds and reads a file from it — by
file name, by environment and start date, or simply the newest one.
- tracmass.output.output_folder(st)[source]#
Directory trajectory files of st are written to.
base_output_dir(current directory when unset), plus a sub-folder namedoutput_name(else the environment) whenuse_sub_folderis truthy.- Parameters:
st (
dynaconf.LazySettings) – Settings of one environment.- Returns:
Path- Return type:
- tracmass.output.output_filename(stem, reverse=False, dtm_start=None)[source]#
{stem}_{forward|reverse}[_{date}].parquet.
- tracmass.output.load(filename=None, *, env=None, dtm=None, reverse=None, columns=None, filters=None, lonlat=False, gcm=None, clean=True, rampup=False)[source]#
Load a trajectory file written by
ParticleRecorder.The file is chosen by, in order of precedence:
filename, if given;
env and/or dtm: the file the settings-driven recorder writes for that environment (default: the active one) and run start date —
{output_folder}/{stem}_{forward|reverse}_{dtm}.parquet(also looked for inbase_output_diritself and its{env}/{output_name}sub-folders, where files written with a differentuse_sub_foldersetting end up). Without dtm, the newest file of that environment;nothing given: the newest
.parquetfile inbase_output_diror one of its sub-folders, i.e. the most recently written run.
- Parameters:
filename (
strorPathorNone, optional) – Path of the file to read.env (
strorNone, optional) –tracmass.tomlenvironment whosebase_output_dir/use_sub_folder/output_namelocate the file. Read withsettings.from_env; the active environment is not changed.dtm (
strorpd.TimestamporNone, optional) – Run start date (the date in the file name).reverse (
boolorNone, optional) – Run direction. DefaultNone: either, as long as only one matches.columns (
listofstrorNone, optional) – Columns to read. Default: all.filters (
dictorlistorNone, optional) – Rows to read, applied while reading (row groups that cannot match are skipped). A dict{"window": 0}meanscolumn == valuefor each item (a list/tuple/set value meanscolumn in value); otherwise pyarrow filters: a list of(column, op, value)tuples (ANDed), or a list of such lists (ORed). Filter columns need not be in columns. Combined (AND) with the clean row filter. Default: all rows.lonlat (
bool, optional) – Addlon/latcolumns (degrees), converted fromx/ywith the GCM adapter’sxy_to_lonlat(glorys,ecco2).x/yare read for this even if not in columns. DefaultFalse.gcm (
strorNone, optional) – Adapter for lonlat. Default:gcmin the file’s metadata, else[env.gcm] nameof env, of theenvin the file’s metadata, or of the active environment. The file’sgcm_product(e.g."glorys12v1") selects which of the adapter’s grids is used, whatever product is currently configured.clean (
bool, optional) – Return only the snapshot rows (event_type == "snapshot"; no deactivation events) and only the columnsid,x,y,dtm(pluslon,latwith lonlat). An explicit columns takes precedence over the column part. DefaultTrue;clean=Falsereturns every row and column.rampup (
bool, optional) – With clean, also keep theevent_type == "rampup"rows (the extra saves inside the first windows of a run withrampup_windows), and add theevent_typecolumn (unless columns is given) to tell them from the window snapshots. The time between consecutive saves isdf.attrs["tracmass.run"] ["save_dt_seconds"]. DefaultFalse.
- Returns:
pd.DataFrame– The trajectory rows.df.attrs["path"]is the file read anddf.attrs["tracmass.run"]its run metadata ({}if none; seetracmass.runsetup.run_metadata()for the keys).- Raises:
FileNotFoundError – If no matching file exists.
TypeError – If filename is given together with env / dtm.
ValueError – If dtm matches both a forward and a reverse run and reverse is not given, or lonlat is set and no GCM with
xy_to_lonlatcan be found.
- Return type:
Examples
>>> df = tracmass.load() # newest run, id/x/y/dtm >>> df = tracmass.load(clean=False) # every row and column >>> df = tracmass.load(env="omi_falkor") # newest omi_falkor run >>> df = tracmass.load(env="omi_falkor", dtm="2007-06-01") >>> df = tracmass.load("traj.parquet", columns=["id", "dtm", "x", "y"]) >>> df = tracmass.load(filters={"window": 0}) # first window only >>> df = tracmass.load(filters=[("window", "<", 10), ("reason", "==", 2)]) >>> df = tracmass.load(env="omi_falkor", lonlat=True) # + lon, lat columns >>> df = tracmass.load(lonlat=True) # id, x, y, dtm, lon, lat >>> df = tracmass.load(rampup=True) # + ramp-up saves, event_type column
- class tracmass.particle.ParticleState(x, y, z, ia, ja, ka, tt, ts, t0, subvol, niter, active, reason)[source]#
Bases:
NamedTupleState vector for all particles — each field is shape (n,).
- Parameters:
- classmethod concat(*states)[source]#
Join particle states end to end.
Particles keep their order, so the particles of the first state keep their indices (the
idwritten byParticleRecorder) and those of later states get new ones after them.- Parameters:
*states (
ParticleState) – States to join, in order.- Returns:
ParticleState– NumPy arrays if every input field is a NumPy array (e.g. the host-side state of a batched run), JAX arrays otherwise.- Return type:
Examples
>>> both = ParticleState.concat(state, new_particles) >>> len(both.x) == len(state.x) + len(new_particles.x) True
- tracmass.particle.write_trajectories_parquet(filepath, state, nff=1)[source]#
Write a single particle state snapshot to a Parquet file.
- Parameters:
filepath (
str) – Output.parquetfile path.state (
ParticleState) – Final (or snapshot) particle state.nff (
int) – Time direction (1=forward, -1=backward).ttandtsare stored asnff * valueso backward trajectories have negative times, matching the Fortran TRACMASS convention.
- Returns:
str– The path that was written.- Return type:
- class tracmass.particle.ParticleRecorder(filepath, metadata=None)[source]#
Bases:
objectStreams trajectory snapshots and deactivation events to a Parquet file.
Each call to
record()appends active-particle positions as a new row group withevent_type="snapshot"(at a window boundary); each call torecord_rampup()the same withevent_type="rampup"(extra saves inside the first windows of a run). Each call torecord_deactivations()appends newly-stopped particles withevent_type="deactivated". Callclose()(or use the recorder as a context manager) to finalise the file.The unified schema contains 13 columns:
event_type,window,dtm,id,x,y,z,ia,ja,ka,tt,t0,reason. Filter onevent_typewhen analysing a single event class.x/iaandy/jaare written in full-grid (absolute) indices:grid_offset— set fromgrid.i_offset/grid.j_offsetby the integrators — is added to the in-memory, subset-relative positions. A non-zero offset is also stored in the run metadata asi_offset/j_offset.Run metadata (GCM adapter name, integration strategy, etc.) is stored as a JSON object under the Parquet schema metadata key
"tracmass.run". Read it back without loading row data:import json, pyarrow.parquet as pq meta = json.loads(pq.read_schema("traj.parquet").metadata[b"tracmass.run"])
- Parameters:
filepath (
str) – Output.parquetfile path. The file is created on the first write.metadata (
dict, optional) –Arbitrary JSON-serialisable key/value pairs stored in the Parquet schema metadata under the key
"tracmass.run". Typical fields:"gcm"— GCM adapter name, e.g."glorys"or"theoretical""integration"— function used, e.g."time_dependent""n_windows"— number of GCM windows"no_wflux"— whether vertical fluxes were disabled
- grid_offset#
(i_offset, j_offset)added tox/iaandy/jaon write. Default(0, 0).- Type:
tuple[int,int]
- metadata#
Run metadata written with the file (a copy of metadata); may be extended until the first write.
- Type:
Examples
>>> meta = dict(gcm="glorys", integration="time_dependent", ... n_windows=50, no_wflux=True) >>> with ParticleRecorder("traj.parquet", metadata=meta) as rec: ... result = tracmass.time_dependent( ... state, grid, lazy, fluxes_fn, n_windows=50, recorder=rec, ... no_wflux=True, ... )
- record(state, dtm=None, *, window=0)[source]#
Append active-particle positions as a new row group.
- Parameters:
state (
ParticleState) – Current particle state; only active particles are written.dtm (
pd.Timestampordatetime-like, optional) – Timestamp stored in thedtmcolumn.None→NaT.window (
int, optional) – Window index (0-based). Default0.
- Return type:
None
- record_rampup(state, dtm=None, *, window=0)[source]#
Append active-particle positions saved inside a window.
Like
record(), but withevent_type="rampup": the extra saves of the ramp-up period (seerampup_windowsintracmass.time_dependent()). The run metadata’ssave_dt_secondsgives the time between consecutive saves.- Parameters:
state (
ParticleState) – Current particle state; only active particles are written.dtm (
pd.Timestampordatetime-like, optional) – Time of the save (inside the window).window (
int, optional) – Index of the window the save falls in.
- Return type:
None
- record_deactivations(state, mask, dtm=None, *, window=0)[source]#
Append deactivated particles identified by mask.
- Parameters:
state (
ParticleState) – Particle state after the integration window.mask (
array-likeofbool,shape (N,)) – Boolean mask selecting the newly-deactivated particles. Typicallyactive_before & (reason != 0).dtm (
pd.Timestampordatetime-like, optional) – Timestamp stored in thedtmcolumn.None→NaT.window (
int, optional) – Window index (0-based). Default0.
- Return type:
None
Run monitoring for TRACMASS-JAX integration loops.
Provides RunMonitor, a context-manager that receives structured
events from time_dependent() and renders them via
one of three backends:
"print"— simpleprint()output, reproducing the original behaviour."log"— structuredloggingrecords (one per window + summary)."rich"— live terminal dashboard powered byrich.
- tracmass.monitoring.sec_str(s)[source]#
Format a duration in seconds as a compact human-readable string.
- tracmass.monitoring.dt_str(dt)[source]#
Format a
pandas.Timedeltaas a compact string.- Parameters:
dt (
pandas.Timedelta)- Returns:
- Return type:
- class tracmass.monitoring.RunMonitor(backend='print', logger=None, max_table_rows=10)[source]#
Bases:
objectMonitor for
time_dependent()runs.Receives structured events from the integration loop and renders them via the chosen backend. Use as a context manager so the
"rich"backend can properly start and stop its live display.- Parameters:
backend (
{"print", "log", "rich"}) – Output backend."print"reproduces the original stdout output."log"emitsloggingrecords."rich"shows a live terminal dashboard.logger (
logging.Logger, optional) – Logger used by the"log"backend. Defaults to the"tracmass"logger.max_table_rows (
int, optional) – Maximum number of window rows shown in the"rich"table at any one time. Older rows scroll out of view. Default10.
Examples
>>> with tracmass.RunMonitor(backend="rich") as mon: ... result = tracmass.time_dependent(state, grid, fluxes, ... n_windows=50, monitor=mon)
>>> # Plain print (default when monitor is not supplied): >>> result = tracmass.time_dependent(state, grid, fluxes, n_windows=50)
- on_window_start(win, n_active, dtm)[source]#
Called at the start of each window, before I/O and advection.
- Parameters:
win (
int) – Zero-based window index.n_active (
int) – Number of active particles entering this window.dtm (
pandas.Timestamp) – Timestamp of the current GCM time window.
- Return type:
None
- on_window_end(win, dtm, n_active_before, n_active_after, n_stopped, reason_tally, n_sub, limit_hit, t_io, t_jax, t_py)[source]#
Called at the end of each window after advection and reactivation.
- Parameters:
win (
int) – Zero-based window index.dtm (
pandas.Timestamp) – Timestamp of the window just completed.n_active_before (
int) – Active count at window entry.n_active_after (
int) – Active count after advection + reactivation.n_stopped (
int) – Particles permanently deactivated this window.reason_tally (
dict[str,int]) – Counts per deactivation reason name.n_sub (
int) – Number of sub-cycles executed.limit_hit (
bool) – Whether the sub-cycle limit was reached.t_io (
float) – Wall time spent in I/O (Dask/Zarr reads), seconds.t_jax (
float) – Wall time spent in JAX advection (device_put + while_loop), seconds.t_py (
float) – Wall time spent in Python overhead (reactivation, GC, flux swap), seconds.
- Return type:
None
Verbose debug output: timestamped progress messages for each run stage.
tracmass logs what it is doing (adapter calls, flux reads with their size,
seeding, kernel compilation, each window’s phases) to the standard
logging logger "tracmass" at DEBUG level. Nothing is shown
unless verbose output is turned on, in any of these ways:
tracmass.run(..., verbose=True)orverbose = truein the activetracmass.tomlenvironment (also honoured by the integrators);set_verbose()for the rest of the session;standard logging config, e.g.
logging.getLogger("tracmass").setLevel("DEBUG")plus a handler.
Messages go to stderr, one line each, with the wall-clock time, so a run
that seems stuck shows which step it is in and how long the previous steps
took.
- tracmass.verbose.set_verbose(verbose=True)[source]#
Turn verbose debug output on or off for the session.
- Parameters:
verbose (
bool, optional) – True attaches astderrhandler to the"tracmass"logger and sets it toDEBUG; False removes the handler again and restores the previous level. Default True.- Return type:
None
Examples
>>> tracmass.set_verbose() >>> result = tracmass.run(env="gog")
- tracmass.verbose.is_verbose()[source]#
Truewhileset_verbose()output is on.- Return type:
- tracmass.verbose.resolve_verbose(verbose=None)[source]#
verbose if given, else the
verbosesetting (default False).
- tracmass.verbose.verbose_output(verbose=None)[source]#
Turn verbose output on for a
withblock, if verbose resolves True.Output that was already on (from
set_verbose()or an outer block) stays on afterwards; output turned on here is turned off on exit.- Parameters:
verbose (bool | None)