CLI reference

Every arrayprobe subcommand's help text, generated from the real parser – never hand-typed.

In short: one section per subcommand, and one per nested verb (soda2 run, validate timing-closure), each a fenced block of arrayprobe <command> --help's actual output. Regenerate with docs/_build/clidoc.py (needs the venv) whenever a flag changes; --check fails if this page has drifted.

See also Reference · User guide · Configuration.

Start with arrayprobe make-demo-flight, which writes a small synthetic raw file, then process a flight with arrayprobe run <config.toml>: it runs every stage on the engine the file names ([run].engine, required; use "3.5", UIOOPS v3.5, for new data; examples/configs/ has configs). To run one stage at a time, use arrayprobe run with [run].stages. arrayprobe engines says which engine does what.

Commands at a glance

Run a whole flight. Start here. One configuration file names every choice; these commands run it, or re-run one product exactly as it was made.

CommandWhat it does
runthe one line: run a whole flight from one configuration file (the [run] table in the file drives it)
soda2the SODA-2 engine (arrayprobe_soda2, a faithful port of NCAR's IDL): run a config, or list its enhancements
reprocessre-run the stage that wrote a proc/sd product, exactly as its .call stamp records, and report whether the result is identical

Run one stage at a time. Single steps on their own: stitch a flight's size distributions into one product, compute inter-arrival thresholds, find stuck diodes, write a self-describing product. run runs the whole chain, from the raw file to the stitched flight, on the engine its configuration file names.

CommandWhat it does
combinestitch a flight's sd files into one product (combine_sizedist)
intarr-thresholdper-particle inter-arrival thresholds (IntArrAnalysis_time)
diode-masksfind the stuck diodes in first-pass proc files (find_shadow_diodes.m) and show the per-diode occupancy beside them
attributedwrite the self-describing sibling of a faithful product

Inspect and check what was written. Read-only: look at particles, compare two files, or ask what made a product. None of these changes a number in any file.

CommandWhat it does
comparejoin two output files on particle identity, then diff by tolerance class
provenancewhat wrote these products, read from their .call sidecars
validatediagnostic checks on files the pipeline already wrote
viewdiagnostic views of files the pipeline already wrote
galleryrender a whole flight's particles to disk, with index pages
triagestart the particle review and labelling server

Inputs and setup. Make the small inputs a run needs when you do not have them, and see which package implements what.

CommandWhat it does
make-demo-flightwrite a synthetic raw .2DS file (no campaign data required)
make-tas-filewrite the aircraft TAS file the v3.5 engine requires
tas-from-nasa-amesconvert a NASA-Ames 1001 true-air-speed file into the .tas.csv sidecar
engineswhich engine to use for what; –catalogue for the per-module detail

Other commands. Commands not yet sorted into a group above.

CommandWhat it does
inventorylist raw files with size, SHA-256 and empty/truncated flags (JSON + CSV)
check-countsreconcile a size-distribution file's binned counts with its particle file, second by second, naming the cause of every excluded particle
triage-recheckwrite a blind re-check queue of particles a person already labelled
triage-agreementreport label agreement (Cohen's kappa) between first and re-check labels
tunewhat a user can change for the default engine, and whether it has any effect
reproduce-reportcompare a product with an archived product, listing every differing cell

How to read this page

Each command below is shown exactly as arrayprobe <command> --help prints it. In the usage line:

NotationMeaning
[ ... ]optional – leave it out and the command still runs
no bracketsrequired – the command refuses to start without it
UPPERCASEa value you supply, e.g. a file path or a number
{a,b}pick exactly one of the listed values
raw ...one or more of these, separated by spaces

positional arguments are given in order with no flag in front; options start with - or --. A flag with no text after it has no further explanation in the parser. "Default off" means the run is unchanged unless you pass the flag.

run

usage: arrayprobe run [-h] [--dry-run] [--yes] [--jobs N] [--enh-jobs N]
                      [--s2-jobs N] [--chunks N] [--jit] [--enhancements]
                      [--accept-preset-mismatch] [--accept-unstamped]
                      [--accept-flagged] [--accept-implausible]
                      [--allow-mixed-config] [--tas-grid {probe,regular}]
                      [--tas-ratio {off,on}] [--iat-file IAT_FILE]
                      config [raw ...]

One line of code to run and one file to configure. Every choice -- engine,
fidelity profile, campaign preset, output directory, which stages -- is named
in the file (arrayprobe.config); nothing here is assumed. Without [run].out,
output goes to an 'output' folder beside the config file. Runs the batch
driver (arrayprobe.driver.run_flight) with every setting read from [run] -- no
new pipeline logic.

positional arguments:
  config                a run configuration TOML (arrayprobe.config)
  raw                   raw files; wins over [run].raw in the file

options:
  -h, --help            show this help message and exit
  --dry-run             preflight only, decode nothing
  --yes, -y             accept the configuration this prints without being
                        asked. A config that declares [thresholds] needs no
                        confirmation and ignores this; one that declares none
                        is confirmed before the run starts, and where stdin is
                        not a terminal (a detached run, CI) it refuses to
                        start without this flag rather than blocking on a
                        prompt nobody can answer.
  --jobs N              process N raw files at once, one worker process each
                        (default 1). Helps a flight recorded as many files;
                        outputs are the same as a one-at-a-time run.
  --enh-jobs N          run the out-of-focus correction across N worker
                        processes when --enhancements applies it (default 1).
                        Separate from --jobs, which spreads raw files: the
                        corrections run after every file is done. The written
                        files are the same for any N.
  --s2-jobs N           decode one raw file across N worker processes (default
                        1). Helps a flight recorded as one large file, where
                        --jobs has nothing to spread; the decoded file is
                        byte-identical to a one-process decode. 2D-S/HVPS
                        (SPEC) files only: where the engine or format has no
                        parallel decoder the run refuses the flag rather than
                        ignore it.
  --chunks N            split each file's image processing (particle framing)
                        into N record ranges run in parallel and merged
                        (default 1); the merged file is byte-identical to an
                        unsplit run. Independent of --jobs, which spreads
                        files rather than one file's records.
  --jit                 speed only, default off: the image-processing stage's
                        hot kernels through numba (arrayprobe.jit_kernels);
                        the proc file is byte-identical, which the test suite
                        checks
  --enhancements        default off: after the stages finish, apply the
                        corrections this config's [enhancements] table names
                        to the products the run wrote, each into its own
                        <stem>.enh.<stage>.nc with an `enhancements=` fragment
                        in its .call. Off, the run is byte-identical to one
                        without the flag; the port's own outputs are never
                        overwritten either way. Needs `pip install arrayprobe-
                        uiops-enhancements`.
  --accept-preset-mismatch
                        default off, forwarded to the batch driver: run a raw
                        file whose extension names a different probe than the
                        preset (a renamed file); recorded as
                        accept_preset_mismatch=on in the .call stamps
  --accept-unstamped    default off, forwarded to the batch driver: accept an
                        input product with no .call stamp instead of refusing
                        it
  --accept-flagged      default off, forwarded to the batch driver: process a
                        raw file the input inventory flags as empty instead of
                        refusing it; the decode .call records the inventory
                        flags (inventory_empty=1), not the override
  --accept-implausible  default off, forwarded to the batch driver: accept a
                        size distribution the plausibility gate refuses; the
                        override is recorded in the .call stamps
  --allow-mixed-config  default off, forwarded to the batch driver: allow
                        inputs stamped with differing configurations to be
                        combined
  --tas-grid {probe,regular}
                        forwarded to the batch driver only when given: the
                        time vector the size-distribution stage gets under a
                        TAS source; run_flight's default when not given
  --tas-ratio {off,on}  forwarded to the batch driver only when given: v3.5
                        only: write tasRatio from an aircraft airspeed source
                        (changes the product); run_flight's default (off) when
                        not given
  --iat-file IAT_FILE   forwarded to the batch driver only when given:
                        sizeDist's twelfth argument, the inter-arrival
                        threshold file; run_flight's default (a threshold file
                        that does not exist, so the per-probe default
                        threshold applies) when not given

reprocess

usage: arrayprobe reprocess [-h] --preset PRESET [--tas-file TAS_FILE] product

Reads the product's .call sidecar, re-runs the one stage that wrote it (image
processing for proc.*, size distribution for sd.*; either engine, from the
stamp) under exactly that configuration, from the input beside it, and diffs
the fresh file against the product. Refuses -- exit 2, nothing run -- unless
the batch driver's own label function regenerates the recorded stamp character
for character, and unless the stage's input carries the matching stamp. Exit 0
identical (Creation Time skipped, source paths compared by basename), 1
differs. The fresh file is written to a temporary directory and discarded; the
product is never modified. Decode and combine products are refused: their
stamps do not record the raw file or the combine call.

positional arguments:
  product              a proc.*.nc (image processing) or sd.*.nc (size
                       distribution) file with its .call sidecar beside it

options:
  -h, --help           show this help message and exit
  --preset PRESET      the campaign preset the product was run under (the
                       batch driver's PRESETS). REQUIRED: no stamp records the
                       preset, so it is named, never assumed
  --tas-file TAS_FILE  the aircraft TAS file, for a proc.* product whose stamp
                       names one (the stamp records its basename only, which
                       must match). Not needed for a product made under a TAS
                       source chain: the chain's own <stem>.tas_<tier>.cdf
                       beside it is read instead

combine

usage: arrayprobe combine [-h] --flight-date FLIGHT_DATE --summary-time
                          SUMMARY_TIME --engine {3.5} [--borrowed-combine]
                          sd_files [sd_files ...] outfile

positional arguments:
  sd_files              sd files, IN ORDER (see combine_sizedist's own
                        docstring -- order is a contract, not a convenience)
  outfile

options:
  -h, --help            show this help message and exit
  --flight-date FLIGHT_DATE
                        'YYYYMMDD', full year
  --summary-time SUMMARY_TIME
                        'YY_MM_DD_HH_MM_SS', the campaign summary file's own
                        basename
  --engine {3.5}        which UIOOPS engine this stage runs: 3.5. Required,
                        with no default, so no run silently picks a side;
                        recorded in the .call stamp.
  --borrowed-combine    with --engine 3.5 only, and required there: opt in to
                        a campaign fork's combine_sizedist over v3.5 sd files.
                        No stock UIOOPS release ships a combine step, so on
                        3.5 it is borrowed and off by default.

compare

usage: arrayprobe compare [-h] [-v] [--var NAME] [--classes]
                          [--min-join MIN_JOIN]
                          [left] [right]

Read the join block first, always. A value difference reported against a bad
join is an artefact of the alignment, not a finding, and the two are
indistinguishable once you are looking at a column of numbers. Exit status is
0 when the comparison is ACCEPTED (>=--min-join join, no failure in an
asserting tolerance class, no unclassified variable, no duplicate join keys)
and 1 otherwise, so it can gate a run without anyone reading the output; 2 is
a usage or file error.

positional arguments:
  left                 the file under test (usually ours)
  right                the reference file

options:
  -h, --help           show this help message and exit
  -v, --verbose        list bit-exact variables too, not only the differing
                       ones
  --var NAME           restrict the value comparison to these variables
                       (repeatable); the join is always computed in full
  --classes            print the tolerance classes and what each one means,
                       then exit
  --min-join MIN_JOIN  join fraction required for acceptance (default 0.999)
usage: arrayprobe gallery [-h] --decoded-dir DECODED_DIR --flight FLIGHT --out
                          OUT
                          [--classes {accepted,rejected,unsure} [{accepted,rejected,unsure} ...]]
                          [--scale SCALE] [--no-crop] [--limit LIMIT]
                          [--no-index]

Visualisation only -- it reads a decoded file and its proc file and writes
images; it computes nothing and changes nothing.

options:
  -h, --help            show this help message and exit
  --decoded-dir DECODED_DIR
                        directory holding <flight>.cdf and proc.<flight>.nc
  --flight FLIGHT       flight stem including the channel, e.g.
                        base170817073523.H
  --out OUT             output root; a <flight>/ subdirectory is created under
                        it
  --classes {accepted,rejected,unsure} [{accepted,rejected,unsure} ...]
                        which classes to write (default: accepted only, which
                        keeps a large flight's gallery a manageable size)
  --scale SCALE         pixel scale (default 3)
  --no-crop             keep the full diode-search window instead of cropping
                        to the particle. The window can vastly exceed the
                        shadow right after a record reset, which is why
                        cropping is the default
  --limit LIMIT         stop after N particles
  --no-index            skip the index pages

inventory

usage: arrayprobe inventory [-h] --out OUT [--format {spec,dmt,pms_2d}] dir

Reads raw bytes only; writes <out>.json and <out>.csv, one row per file.
Computes no product and changes nothing.

positional arguments:
  dir                   directory of raw files (or one file)

options:
  -h, --help            show this help message and exit
  --out OUT             output stem; writes <stem>.json and <stem>.csv
  --format {spec,dmt,pms_2d}
                        record format for every file (default: guessed per
                        file)

check-counts

usage: arrayprobe check-counts [-h] [--out OUT] [--tolerance TOLERANCE]
                               sd proc

Reads the sd and proc files only and writes nothing but --out. Prints every
second whose binned count differs from the accepted particle count, each
excluded particle with its cause, a closure line and the probe's own reported
total (not available until a reader writes it). Exit 0 when closure is exact,
1 otherwise.

positional arguments:
  sd                    size-distribution netCDF (its .call sidecar beside it)
  proc                  the per-particle netCDF the sd file was made from

options:
  -h, --help            show this help message and exit
  --out OUT             write the full reconciliation as JSON
  --tolerance TOLERANCE
                        largest per-second residual still counted as closed
                        (default 0)

triage

usage: arrayprobe triage [-h] --decoded-dir DECODED_DIR [--port PORT]
                         [--queue-file QUEUE_FILE] [--reviewer REVIEWER]
                         [--met-dir MET_DIR] [--state-file STATE_FILE]
                         [--state-var STATE_VAR] [--accept-unstamped]
                         [--run-root RUN_ROOT]

Manual labelling and visualisation. It writes verdicts to <decoded-
dir>/triage_verdicts/ and changes no computed number. --decoded-dir is
required, as it is for `python -m arrayprobe.triage_server`.

options:
  -h, --help            show this help message and exit
  --decoded-dir DECODED_DIR
                        directory containing proc.*.H.nc files and their
                        decodes (*.H.nc)
  --port PORT
  --queue-file QUEUE_FILE
                        JSON file of named curated particle lists for a
                        specific review pass
  --reviewer REVIEWER   who is labelling; REQUIRED to save labels and recorded
                        with every verdict (or set ARRAYPROBE_REVIEWER). With
                        neither, the server starts but refuses every save
                        (HTTP 400) -- the id is never guessed
  --met-dir MET_DIR     folder of ARM AAF merged files
                        (enaaafmergedF1.c1.*.nc); the particle page then shows
                        the air temperature at the particle's record time
  --state-file STATE_FILE
                        netCDF state-variable file for the air temperature
                        (any names: the temperature is chosen by units and
                        standard_name/long_name, and a file with several
                        candidates is refused, listing them). Takes precedence
                        over --met-dir. With neither, the index page asks for
                        the file
  --state-var STATE_VAR
                        the temperature variable in --state-file (default:
                        chosen by content)
  --accept-unstamped    index products whose provenance is unrecorded. WITHOUT
                        this flag they are refused and listed by name on the
                        index page under 'Refused: no provenance' -- never
                        silently dropped. Labels attached to a product of
                        unknown configuration cannot be traced afterwards.
                        Pass this when you know what you are looking at and
                        the stamp is simply absent.
  --run-root RUN_ROOT   folder whose _runs/ the Run page lists at /runs
                        (default: the output folder of the first run launched
                        from /run)

triage-recheck

usage: arrayprobe triage-recheck [-h] --decoded-dir DECODED_DIR --flight
                                 FLIGHT --out OUT [--fraction FRACTION]
                                 [--seed SEED]

Draws a seeded random fraction of one flight's human labels into a queue file
whose name starts 'recheck-'. The triage server stores its verdicts separately
(triage_verdicts/<flight>.recheck.json) and hides the first label, so the
second is blind. Changes no computed number.

options:
  -h, --help            show this help message and exit
  --decoded-dir DECODED_DIR
                        the directory the triage server uses
  --flight FLIGHT       flight name as the triage server shows it
  --out OUT             queue file to write (JSON)
  --fraction FRACTION   share of the human-labelled particles to re-show
                        (default 0.2)
  --seed SEED           random seed, recorded in the file

triage-agreement

usage: arrayprobe triage-agreement [-h] --decoded-dir DECODED_DIR --flight
                                   FLIGHT

Pairs each re-checked particle's first human label with its re-check label and
prints the observed and chance agreement, Cohen's kappa and the confusion
matrix.

options:
  -h, --help            show this help message and exit
  --decoded-dir DECODED_DIR
                        the directory the triage server uses
  --flight FLIGHT       flight name as the triage server shows it

provenance

usage: arrayprobe provenance [-h] [--require] paths [paths ...]

Per file: the engine, profile and stage its stamp records, the stamp's format,
its link count, and its state -- stamped, unstamped, orphan-stamp (a sidecar
whose product is gone), in-flight, legacy-json or unparseable. A directory
expands to every .nc/.cdf in it plus every orphan sidecar. Nothing here infers
an engine from a date, a filename or a neighbouring file: a product whose
stamp does not name one is reported as unrecorded, which is the true answer.
The git revision printed at the end is THIS CHECKOUT's, not the product's --
no stamp records the revision that wrote it.

positional arguments:
  paths       products or directories

options:
  -h, --help  show this help message and exit
  --require   exit 1 unless every path is 'stamped' -- the form a script
              wants, and the form that makes 'all clear' mean something

diode-masks

usage: arrayprobe diode-masks [-h] --proc PROC [--image IMAGE] [--date DATE]
                              [--time TIME] [--orientation {H,V}]
                              [--out-dir OUT_DIR] [--no-write]
                              [--profile NAME] [--probe NAME]

Runs masks.find_shadow_diodes -- a field-campaign fork's find_shadow_diodes.m,
bug for bug -- on each --proc, writing the diode.<date><time>.<or>[empty].mat
MATLAB writes (an upstream defect: an empty result is named ...<or>empty.mat,
which the second pass never loads) and nothing else. Beside it, prints
bin_stats and, when --image is given, diode_report's occupancy and SPEC's
noisy-diode flags. The mask reaches a second pass only when the diodes are
declared in [run].diode_masks AND the run is given --diode-corrected.

options:
  -h, --help           show this help message and exit
  --proc PROC          a first-pass proc file; repeat for several
  --image IMAGE        the decoded image .cdf for the matching --proc, in the
                       same order; enables the occupancy / SPEC view
  --date DATE          'YYMMDD'; default: parsed from the proc name
  --time TIME          'HHMMSS'; default: parsed from the proc name
  --orientation {H,V}  default: parsed from the proc name
  --out-dir OUT_DIR    where the .mat goes; default the proc's own directory
                       (MATLAB's `path`, which it both reads from and saves
                       to)
  --no-write           report only; write no .mat
  --profile NAME       the fidelity profile, when the proc file's stamp names
                       none (it decides whether bin_stats counts lit or
                       shadowed slices)
  --probe NAME         the probe whose image_slice_offset enters S for a
                       shadow count

validate

usage: arrayprobe validate [-h] {timing-closure,by-condition} ...

Read-only checks. Nothing here computes or writes a pipeline number. Each
check reports a comparison and returns 0 on success -- none of them issues a
pass/fail verdict, because none of the sources they implement publishes a
tolerance.

positional arguments:
  {timing-closure,by-condition}
    timing-closure      does the probe's particle clock agree with its
                        housekeeping clock?
    by-condition        per-second agreement against the archived product,
                        split by how much cloud the probe was in

options:
  -h, --help            show this help message and exit

validate timing-closure

usage: arrayprobe validate timing-closure [-h]
                                          [--day-rollover-h DAY_ROLLOVER_H]
                                          decoded proc

McFarquhar et al. (2017) ch. 11 p. 11.20. Compares the proc file's particle-
clock time span against the decoded file's own record-clock span, which is
measured by a different clock in the instrument. Probetype 2 (2D-S / HVPS)
only -- see arrayprobe.timing_closure for why.

positional arguments:
  decoded               the decoded file the proc file was framed from
                        (.H.nc/.V.nc from engine 3.5, or an archived
                        .H.cdf/.V.cdf); pairing them correctly is the caller's
                        responsibility
  proc                  the proc (image-processing) file

options:
  -h, --help            show this help message and exit
  --day-rollover-h DAY_ROLLOVER_H
                        hours added once if the record span looks negative (a
                        single midnight crossing); default 24.0

validate by-condition

usage: arrayprobe validate by-condition [-h] [--product PRODUCT]
                                        [--product-dir PRODUCT_DIR]
                                        [--window WINDOW]
                                        [--drop-bins DROP_BINS]
                                        combined

A single headline percentage can hide a whole regime. The band for each second
is decided by the REFERENCE's own per-second particle total, never by ours --
using ours would make the stratification depend on the thing being tested.

positional arguments:
  combined              our combined product

options:
  -h, --help            show this help message and exit
  --product PRODUCT     the archived product; when omitted, found by date in
                        --product-dir
  --product-dir PRODUCT_DIR
  --window WINDOW       HHMMSS-HHMMSS, inclusive-exclusive, so a partially
                        processed flight is compared only where its raw files
                        are all present
  --drop-bins DROP_BINS
                        bins excluded from the 'used' columns (default 5)

view

usage: arrayprobe view [-h] {strip,second,hollow} ...

Visualisation only: each view reads the port's own outputs and computes no
pipeline number (arrayprobe.diagnostic_views). The triage server serves the
record strip at /strip/<flight>.

positional arguments:
  {strip,second,hollow}
    strip               whole image records with their particles in place
                        (HydroViewer's view)
    second              one second of two sd files bin by bin, with the
                        particles behind the differing cells
    hollow              how much of a between-engine difference hollow ('H')
                        particles can make

options:
  -h, --help            show this help message and exit

view strip

usage: arrayprobe view strip [-h] [--record RECORD] [--n-records N_RECORDS]
                             [--colour-by {reject,habit}] --out OUT
                             image proc

Consecutive 1700-slice records stacked one above the next, boundary slices
marked, each particle's shadow coloured by its reject code or habit, and every
fifth particle ticked in red.

positional arguments:
  image                 the decoded image file (.H.nc)
  proc                  the proc file framed from it

options:
  -h, --help            show this help message and exit
  --record RECORD       first record, 1-based (default 1)
  --n-records N_RECORDS
                        records to show (default 6)
  --colour-by {reject,habit}
  --out OUT             output file: .png for the image alone, anything else
                        for an HTML page with the legend and particle table

view second

usage: arrayprobe view second [-h] --second SECOND [--variable VARIABLE]
                              [--proc PROC] [--diameter-var DIAMETER_VAR]
                              [--rtol RTOL] [--out OUT]
                              ours theirs

For validation: the port's second beside the archive's. Exact comparison
unless --rtol is given. The particle list is where to look, not what was
counted: `count` also requires the reject codes, centre-in and time window to
pass.

positional arguments:
  ours                  our sd (or combined) file
  theirs                the file to compare against, e.g. the archived product

options:
  -h, --help            show this help message and exit
  --second SECOND       the second, as HHMMSS
  --variable VARIABLE   a (time, bin) variable (default count)
  --proc PROC           the proc file behind OURS; lists the particles in
                        differing bins
  --diameter-var DIAMETER_VAR
                        the proc variable particles are placed in bins by
                        (default image_diam_minR); printed with every listing
  --rtol RTOL           relative tolerance; 0 (default) is an exact comparison
  --out OUT             also write an HTML page here

view hollow

usage: arrayprobe view hollow [-h]
                              (--edges-um EDGES_UM | --edges-from EDGES_FROM)
                              [--min-fraction MIN_FRACTION]
                              proc

Reads one UIOOPS v3.5 proc file. Per size bin: accepted particles, accepted
'H' (hollow, Poisson-spot) particles, their share, how many the Korolev size
correction moves out of and into the bin, and the count with the correction
undone. Ends with a verdict: whether hollow sizing can explain a difference
between engines (SODA-2 does not divide) in this size range. Reads the file
and writes nothing.

positional arguments:
  proc                  the v3.5 proc file (proc.<stem>.<channel>.nc)

options:
  -h, --help            show this help message and exit
  --edges-um EDGES_UM   comma-separated bin edges in micrometres, e.g.
                        55,65,75,85,95,105,115
  --edges-from EDGES_FROM
                        a size-distribution file whose Bin Minimum/Bin Maximum
                        (mm) give the bins
  --min-fraction MIN_FRACTION
                        verdict threshold as a fraction of the range's count
                        (default 0.05)

intarr-threshold

usage: arrayprobe intarr-threshold [-h] --probe PROBE --flight-date
                                   FLIGHT_DATE [--blocks BLOCKS]
                                   [--extra-thresholds-json EXTRA_THRESHOLDS_JSON]
                                   [--plot PLOT] [--fix-b8]
                                   [--mat-workspace MAT_WORKSPACE]
                                   proc_file outfile

Fit each population of inter-arrival times and write the threshold file
sizeDist accepts as its optional twelfth argument. NOTE: the production call
behind the archived products this port is validated against passed a threshold
file that does not exist, so nothing in them depends on this stage.

positional arguments:
  proc_file             a per-particle file (proc.*.nc) or a combined one
  outfile               threshold .cdf for sizeDist's optional twelfth
                        argument

options:
  -h, --help            show this help message and exit
  --probe PROBE         probe name, e.g. 2DS
  --flight-date FLIGHT_DATE
                        'YYYYMMDD', full year
  --blocks BLOCKS       number of populations to split the file into (the
                        source's fileNum); the block size is derived from it
                        exactly as IntArrAnalysis_time does
  --extra-thresholds-json EXTRA_THRESHOLDS_JSON
                        opt-in: write the two thresholds the source computes
                        but never writes (threshold, threshold_ww) to this
                        JSON sidecar, never a netCDF file (arrayprobe.intarr_a
                        nalysis.write_extra_thresholds_sidecar)
  --plot PLOT           opt-in: render IntArrAnalysis_time.m's contour figure
                        (arrayprobe.intarr_plots.plot_intarr_contour) to this
                        image path (matplotlib; requires the scipy/matplotlib
                        extras)
  --fix-b8              with --plot: use the corrected time axis instead of
                        reproducing the original's NaN gaps before 10:00:00 (a
                        defect in the original time axis)
  --mat-workspace MAT_WORKSPACE
                        opt-in: write IntArrAnalysis_time.m's :331 workspace
                        dump (arrayprobe.intarr_analysis.write_mat_workspace)
                        to this .mat path (requires the scipy extra)

attributed

usage: arrayprobe attributed [-h] [--accept-unstamped]
                             [--enhancements ENHANCEMENTS]
                             [--layout {dimensions,particles}]
                             source [out]

Writes a SECOND file beside a faithful product, carrying the same values plus
the whole .call sidecar and a sourced unit on every variable, so a recipient
can answer 'where did this number come from' from one file. The faithful
product is opened READ-ONLY and never modified -- it is the byte-comparison
artefact and carries no attributes because the MATLAB combine step writes
none. Every unit is quoted from the MATLAB line that writes it; a product
shape with no sourced unit table is refused by name rather than exported with
a guess. Design: docs/developer/ATTRIBUTED_EXPORT.md.

positional arguments:
  source                the faithful product (its .call sidecar is read too)
  out                   output path (default: <source>.attributed.nc)

options:
  -h, --help            show this help message and exit
  --accept-unstamped    export a product with no .call sidecar; the file then
                        RECORDS that its provenance is not established, rather
                        than staying silent about the gap
  --enhancements ENHANCEMENTS
                        comma-separated names of enhancements the producing
                        chain applied; empty (the default) means none, which
                        is the port's own case by construction
  --layout {dimensions,particles}
                        'dimensions' (the default, unchanged) writes the
                        attributed netCDF4 sibling; 'particles' instead reads
                        a proc (per-particle) file and writes one Parquet
                        table, one row per particle (default
                        <source>.particles.parquet); needs the [parquet] extra

make-demo-flight

usage: arrayprobe make-demo-flight [-h] [--seconds SECONDS]
                                   [--tas-mps TAS_MPS] [--dense]
                                   [--distinct-counters] [--with-config]
                                   path

Builds a raw SPEC-shaped .2DS recording from nothing -- the quickstart input,
so the pipeline can be exercised without any archived data or Octave.
Synthetic, not validated science: it proves the chain runs and writes valid
files, and is not a stand-in for the oracle-backed validation the fidelity
claims rest on. Name the output base<YYMMDDhhmmss>.2DS if you intend to reach
the combine stage, which reads the flight date from the stem. WITHOUT --dense
the flight is deliberately sparse: its one particle per second has an
arbitrary inter-arrival time, so sizeDist rejects every particle as sub-6 us
and the combined product is all fill value. Pass --dense for a run that yields
a real size distribution.

positional arguments:
  path                 output path, e.g. base160101000000.2DS

options:
  -h, --help           show this help message and exit
  --seconds SECONDS    number of one-second records to write (default: 5)
  --tas-mps TAS_MPS    synthetic true airspeed in m/s (default: 130.0)
  --dense              write the DENSE demo instead: many particles per second
                       with realistic inter-arrival gaps, so the size
                       distribution comes out populated. This is the demo to
                       run first.
  --distinct-counters  give each image packet its own particle counter
                       (default: the constant 7), so SODA-2's spec_index keeps
                       every particle; changes the written bytes
  --with-config        also create the output folder if needed and write a
                       ready-to-run run.toml beside the flight, so `arrayprobe
                       run <folder>/run.toml <flight> --yes` works from a
                       plain pip install, without the repository's examples/
                       folder

make-tas-file

usage: arrayprobe make-tas-file [-h] [--from-tas-csv FROM_TAS_CSV]
                                [--constant-tas CONSTANT_TAS]
                                [--start-hhmmss START_HHMMSS]
                                [--seconds SECONDS]
                                [--second-rule {round,floor}]
                                [--neighbour {adjacent,nearest-present}]
                                [--on-unsettled {default,refuse}]
                                [--from-merge FROM_MERGE]
                                [--from-aircraft FROM_AIRCRAFT]
                                [--chain {auto,merge,aircraft,probe}]
                                [--date DATE] [--search SEARCH]
                                [--probe-csv PROBE_CSV]
                                [--cutoff-mps CUTOFF_MPS]
                                [--default-mps DEFAULT_MPS]
                                [--soda2-pth SODA2_PTH]
                                path

Writes the aircraft file stock imgProc_sm.m reads as varargin{1}: a netCDF
file with two 1-D f8 variables, `Time` (HHMMSS, e.g. 60638 for 06:06:38) and
`TAS` (m/s); the image-processing stage reads those exact names. The v3.5
engine -- the package default -- refuses to run without one. Build it from the
.tas.csv sidecar the decode already wrote, or state a constant speed
explicitly.

positional arguments:
  path                  output path, e.g. aircraft_160906.cdf

options:
  -h, --help            show this help message and exit
  --from-tas-csv FROM_TAS_CSV
                        the <stem>.tas.csv the decode wrote (columns:
                        timehhmmss, tas)
  --constant-tas CONSTANT_TAS
                        flat series at this speed, NOT a measurement (needs
                        --start-hhmmss and --seconds)
  --start-hhmmss START_HHMMSS
                        first sample's time as HHMMSS
  --seconds SECONDS     number of 1-Hz samples for --constant-tas
  --second-rule {round,floor}
                        fractional HHMMSS to whole seconds; default round
                        (this package's choice)
  --neighbour {adjacent,nearest-present}
                        the neighbouring second the duplicate-second rule
                        compares against; default adjacent (this package's
                        choice)
  --on-unsettled {default,refuse}
                        a duplicated second the duplicate-second rule cannot
                        settle; default: it takes --default-mps (this
                        package's choice)
  --from-merge FROM_MERGE
                        a campaign merge file, e.g.
                        mrg1_<platform>_<yyyymmdd>_R*.nc
  --from-aircraft FROM_AIRCRAFT
                        another registered aircraft file, e.g. ARM
                        enaaafmergedF1.c1.*.nc
  --chain {auto,merge,aircraft,probe}
                        resolve the source through the fallback chain (merge
                        -> aircraft -> probe)
  --date DATE           --chain: the flight date YYYYMMDD
  --search SEARCH       --chain: a directory holding merge/aircraft files
                        (repeatable)
  --probe-csv PROBE_CSV
                        --chain: the decode's <stem>.tas.csv
  --cutoff-mps CUTOFF_MPS
                        TAS above this takes --default-mps; default 300 (this
                        package's choice)
  --default-mps DEFAULT_MPS
                        the fall-back value; default 170, UIOOPS's own fall-
                        back airspeed
  --soda2-pth SODA2_PTH
                        also write the series as a SODA-2 op.pth ASCII file

tas-from-nasa-ames

usage: arrayprobe tas-from-nasa-ames [-h] --out OUT --flight-date YYYY-MM-DD
                                     [--drop-missing] [--variable SUBSTRING]
                                     [--assume-units {as-declared,knots}]
                                     [--round-seconds]
                                     path

Reads a NASA-Ames 1001 aircraft true-air-speed file (plain or .gz) and writes
the .tas.csv sidecar arrayprobe.readers.tasinfo.load_tas_info reads, which
`make-tas-file --from-tas-csv` then turns into the aircraft netCDF the v3.5
engine requires. This is the SEA/CIP chain's route to a MEASURED TAS: `make-
tas-file --constant-tas` fabricates one and says so, and nothing on that chain
writes a sidecar of its own.

positional arguments:
  path                  the NASA-Ames 1001 file, e.g. <stem>.SODA2_TAS.1Hz.gz

options:
  -h, --help            show this help message and exit
  --out OUT             the .tas.csv to write
  --flight-date YYYY-MM-DD
                        the flight this series is for; must equal the file's
                        own DATE
  --drop-missing        leave out rows carrying the file's missing-value code
                        instead of refusing
  --variable SUBSTRING  name which true-air-speed variable to use, when the
                        file declares more than one (UND's own .basic.1Hz
                        declares two). The reader still refuses to choose on
                        its own; this is how an operator chooses, and the full
                        name of the column used is reported
  --assume-units {as-declared,knots}
                        declare the column's REAL units when they contradict
                        the file's header; 'knots' multiplies by 0.514444 and
                        reports it
  --round-seconds       attribute each sample to its nearest whole second, for
                        a file whose 1 Hz grid is offset from the integer
                        second (UND's .basic.1Hz sits 0.21 s off on the 2016
                        flights). The shift is reported

engines

usage: arrayprobe engines [-h] [--catalogue] [--module NAME]

Prints which engine to use for what: UIOOPS v3.5 (the default) for new data
and SODA-2 as an independent second processing. Then states what the chosen
engine's results rest on. --catalogue adds, per module, which package
implements it and why. Reads nothing and writes nothing.

options:
  -h, --help     show this help message and exit
  --catalogue    also list every module and which package implements it for
                 that engine, and why
  --module NAME  describe only this module; repeatable

tune

usage: arrayprobe tune [-h] {settings,sample,distributions,diodes,score} ...

Tuning a run on the default engine (UIOOPS v3.5). Nothing here runs the
pipeline or changes a product; `sample` writes only a review queue file and
`distributions` and `score` only a JSON report.

positional arguments:
  {settings,sample,distributions,diodes,score}
    settings            list every setting: what it does, its default, where
                        to set it
    sample              draw review queues from a proc file: particles inside
                        time windows, per size bin
    distributions       per-particle distributions inside time windows of a
                        proc file, thresholds marked
    diodes              stuck diodes from a proc file's bin_stats, with the
                        run-file lines that mask them
    score               the reviewer's labels per reject code and size bin:
                        what each code rejected that was labelled real, and
                        accepted that was labelled artifact

options:
  -h, --help            show this help message and exit

tune settings

usage: arrayprobe tune settings [-h] [--probe-case NAME]
                                [--ia-thresh-type {0,1,2,3}] [--json]

Lists every setting a run on the default engine (UIOOPS v3.5) can change: what
it does, its default, where it is set (run file, run_flight.py flag, probe
case, profile or preset), and whether engine 3.5 accepts it. Given a probe
case, it also says whether each setting has any effect there: the shattering
settings change nothing where the case's applyIntArrThresh is 0, or where the
run's iaThreshType is 0 (engine 3.5's default without a threshold file). Each
default is cited to the source line it was read from; the JSON form lists them
all.

options:
  -h, --help            show this help message and exit
  --probe-case NAME     also say whether each setting has any effect on this
                        registered probe case (v35-2ds-ladder-fix is always
                        available)
  --ia-thresh-type {0,1,2,3}
                        the run's iaThreshType for the effect column (default
                        0, engine 3.5's default without a threshold file; 2 is
                        [shattering].ia_thresh_type = 2)
  --json                print the table as JSON, with every source line

tune sample

usage: arrayprobe tune sample [-h] --proc PROC --windows W --queue-out Q
                              [--route R] [--flight FLIGHT]
                              [--queue-stem QUEUE_STEM]
                              [--size-edges MM [MM ...]] [--per-bin PER_BIN]
                              [--seed SEED]

From one proc file, take the particles whose record header time (Time, HHMMSS)
falls inside any window (seconds of day, both ends included) and whose
image_diam_minR is at least the first size edge, and draw up to --per-bin of
each size bin at random with a fixed seed. Two queues: the accepted image
codes only (0HhuR), and every code. They are merged into --queue-out, the file
`arrayprobe triage --queue-file` reads, with a coverage block per queue; other
queues in that file are kept.

options:
  -h, --help            show this help message and exit
  --proc PROC           the proc file to draw from
  --windows W           time windows as lo-hi seconds of day
                        ('47925-47959,51671-51797'), or a file of such pairs
                        (one or more per line, # for comments)
  --queue-out Q         queue file to write, or to add the two queues to
  --route R             how the proc file was made, recorded in the coverage
  --flight FLIGHT       flight name the triage server knows the particles by
                        (default: the proc file name without 'proc.' and
                        '.nc')
  --queue-stem QUEUE_STEM
                        queue name prefix; the queues are
                        <stem>-accepted-n<N>-s<seed> and
                        <stem>-all-n<N>-s<seed> (default: sample)
  --size-edges MM [MM ...]
                        size-bin edges in mm, increasing; the first is the
                        size floor and 'inf' closes the last bin (default:
                        0.055 0.1 0.2 0.4 1.0 inf)
  --per-bin PER_BIN     particles drawn per size bin (default: 40)
  --seed SEED           random seed; the same seed and inputs give the same
                        queues (default: 20260930)

tune distributions

usage: arrayprobe tune distributions [-h] --proc PROC --windows W --out REPORT
                                     [--probe-case NAME] [--area-ratio-z Z]
                                     [--size-edges MM [MM ...]]

From one proc file, for the particles whose record header time (Time, HHMMSS)
falls inside any window (seconds of day, both ends included): particles per
second, inter-arrival time, area ratio and axis ratio per size bin, the reject
codes in words, habit codes and the hollow fraction. Each active threshold is
marked where it cuts, with where its value was read from: the area-ratio cut,
the image-stage aspect-ratio ceilings and, given --probe-case, the shattering
threshold, with whether it has any effect on that case. Non-finite diameters
get their own size bin. Writes only the JSON report.

options:
  -h, --help            show this help message and exit
  --proc PROC           the proc file to read
  --windows W           time windows as lo-hi seconds of day
                        ('47925-47959,51671-51797'), or a file of such pairs
                        (one or more per line, # for comments)
  --out REPORT          JSON report to write
  --probe-case NAME     mark this registered probe case's shattering threshold
                        and say whether its gate is on (v35-2ds-ladder-fix is
                        always available)
  --area-ratio-z Z      the area-ratio cut to mark: the value the run passes
                        for this flag (default: v3.5's stock cut)
  --size-edges MM [MM ...]
                        size-bin edges in mm, increasing; 'inf' closes the
                        last bin (default: 0 0.055 0.1 0.2 0.4 1.0 inf)

tune diodes

usage: arrayprobe tune diodes [-h] --proc PROC [--profile NAME] [--probe NAME]
                              [--stem STEM] [--json] [--jobs N]

For each proc file, read bin_stats as lit counts (a shadow count is converted
as S - shadow, S the particles' slice total from position) and apply the
archived-product variant's stuck-diode rule (find_shadow_diodes.m:66-97):
below 0.80 x max, or below 0.80 x mean when a diode reads above 1.2 x mean.
The engine and profile come from the file's provenance; an unstamped file
needs --profile. Prints a [masking.diodes] entry per file, applied by a run
given --diode-corrected. Reads only.

options:
  -h, --help      show this help message and exit
  --proc PROC     a proc file to read (repeatable)
  --profile NAME  the fidelity profile, when the file's stamp names none
  --probe NAME    the probe whose image_slice_offset enters S (default: the
                  offset every probe of the file's diode count shares)
  --stem STEM     the raw stem the [masking.diodes] entry is keyed by
                  (default: the base<12 digits> in the proc file's name)
  --json          print the full report as JSON instead of the summary
  --jobs N        worker processes reading position for S (default: up to 8
                  above 2 M particles, else 1; 1 forces a serial read). A
                  parallel read that fails or stalls falls back to the serial
                  one; S is identical either way

tune score

usage: arrayprobe tune score [-h] --queue-file Q --decoded-dir D
                             [--out REPORT] [--phase-verdicts V]

For each queue in --queue-file, join the verdicts and phases recorded in
`arrayprobe triage` (the verdict store under --decoded-dir; a recheck- queue
reads its own store) to the queue's particles, and count them per size bin and
per reject code (the code each particle carried when the queue was drawn). For
a rejecting code: how many it rejected that were labelled real; for an
accepted code: how many it accepted that were labelled artifact. Per size bin,
the ice fraction ice / (ice + drizzle) with a Wilson 95 % interval. Writes
only the JSON report.

options:
  -h, --help          show this help message and exit
  --queue-file Q      the queue file the labels were made from
  --decoded-dir D     the directory `arrayprobe triage --decoded-dir` was
                      given; verdicts are read from its triage_verdicts/
  --out REPORT        JSON report to write (default: print the summary only)
  --phase-verdicts V  comma-separated verdicts whose phase enters the ice
                      fraction (default: real)

soda2

usage: arrayprobe soda2 [-h] {run,enhancements} ...

`soda2 run` reads [soda2] (the driver's op fields) from a config whose
[run].engine is "soda2", runs soda2_process_2d and writes a deterministic
<stem>.pbp.call sidecar beside the PBP outputs, <stem> being theirs. With
the optional correction package installed, every resolved-ON correction the
driver adapter can route is applied and the sidecar records which were
applied and which skipped, with the reason. `soda2 enhancements` lists the
optional correction package's registry. The UIOOPS version options do not
apply to this engine, and `arrayprobe run` refuses a soda2 config with a
pointer here. What a soda2 config may hold, and the IDL-type limit on
op.rate: `soda2 run --help`.

positional arguments:
  {run,enhancements}
    run               run soda2_process_2d from a config file
    enhancements      list the SODA-2 corrections (reachability; resolved
                      state from a config if given)

options:
  -h, --help          show this help message and exit

soda2 run

usage: arrayprobe soda2 run [-h] [--out OUT] [--jobs N] [--chunks N] [--jit]
                            [--protective {auto,on,off}]
                            config

Run soda2_process_2d with op = the file's [soda2] table and write
<stem>.pbp.call beside the PBP outputs. Exit 2 for a config it cannot run, 1
when the driver returns no data.

positional arguments:
  config                a run configuration TOML with a [soda2] table

options:
  -h, --help            show this help message and exit
  --out OUT             output directory; overrides [soda2].outdir
  --jobs N              split soda2_process_2d's buffer loop across N
                        processes (default 1, serial). Byte-identical to
                        serial -- asserted by tests/test_soda2_parallel.py,
                        not claimed here -- and refused by name where it would
                        not be (op.stuckbits, op.activetimefrommissed, more
                        than one raw file). It composes with the correction
                        package: the enhancements are rebuilt by name inside
                        each worker and the split stays byte-identical to
                        serial with them ON
                        (tests/test_soda2_parallel_enhancements.py), so --jobs
                        is not a choice between fast and corrected. A resource
                        knob, not a threshold: it is a flag and not a config
                        key, because a config file may select a run's science
                        and never describe it
  --chunks N            how many contiguous buffer ranges --jobs splits the
                        loop into (default 0 = 4 per job). More than one per
                        job because a buffer's cost is its particle count, so
                        equal buffer counts are not equal work. SODA-2 engine
                        only, and read ONLY when --jobs > 1: with --jobs left
                        at 1 this value is accepted and never looked at
                        (process_2d.py's `if jobs > 1:` is its one reader), so
                        it is a balance knob for the split and not a way to
                        break one long run into resumable pieces
  --jit                 SODA-2 engine only. Speed only, default off: route
                        label_blobs's flood fill -- and fillholes' own call to
                        it -- through numba (arrayprobe_soda2.jit_kernels;
                        needs pip install "arrayprobe[jit]"). The PBP files
                        are byte-identical to a run without it, asserted in
                        tests/test_soda2_jit_product_identity.py (the written
                        product, off against on) over
                        tests/test_soda2_label_region_jit_identity.py (the
                        kernel's own array) and not claimed here, so it is not
                        written into op and the .pbp.call sidecar is the same
                        either way. Measured 36.2x at the kernel. It installs
                        no substitution (a --jit run leaves every
                        enhancement_hooks point pristine, measured), so it
                        composes with --jobs and --chunks at the engine level
                        and the same module asserts that. With arrayprobe-
                        soda2-enhancements installed, the two speedups and the
                        corrections compose in one run. `[run].jit = true` in
                        the config does the same thing; the two are ORed, so
                        this flag can turn the kernel on and never off
  --protective {auto,on,off}
                        The protective-corrections rule applied to SODA-2:
                        which of
                        arrayprobe_soda2_enhancements.registry.PROTECTIVE (the
                        corrections for data-destroying upstream defects) run
                        by default. 'auto' (the default) resolves
                        registry.protective_set for this config's
                        [soda2].probetype; 'on' applies every PROTECTIVE name
                        regardless of probe; 'off' applies none of them.
                        Omitted, the config's [enhancements] protective =
                        true/false decides (else 'auto'). An explicit
                        [enhancements] <name> = false in the config still wins
                        over a protective default. Recorded in the .pbp.call
                        sidecar's enhancements.protective_mode/protective_set

The config file: [run] holds engine = "soda2" and, optionally, jit = true
(the same switch as --jit, and the two are ORed); [soda2] holds the op
fields, names unchanged from IDL; [enhancements] is optional. No [fidelity]
table: the SODA-2 engine has one source tree and reads no UIOOPS profile, so
naming one is refused. Raw file and output
directory are [soda2].fn and [soda2].outdir (--out overrides outdir).

IDL types: TOML carries no IDL type, so every [soda2] number arrives as a
Python int or float, where the IDL op held INT or FLOAT. A field left out
takes soda2_update_op's default with IDL's own dtype. In IDL, activetime =
fltarr(n) + op.rate is FLOAT unless op.rate is DOUBLE (1.0d). The driver
reads a Python int rate as INT, so rate = 1 gives FLOAT as example_batch.pro's
rate=1 does; a TOML float such as rate = 1.0 is DOUBLE. A nonzero FLOAT
timeoffset is widened from FLOAT in IDL; to reproduce it, call the driver
from Python with numpy.float32 values, e.g. op["timeoffset"] =
numpy.float32(0.5):
  from arrayprobe_soda2.process_2d import soda2_process_2d

soda2 enhancements

usage: arrayprobe soda2 enhancements [-h] [--protective {auto,on,off}]
                                     [config]

positional arguments:
  config

options:
  -h, --help            show this help message and exit
  --protective {auto,on,off}
                        same meaning as `soda2 run --protective`

reproduce-report

usage: arrayprobe reproduce-report [-h] [--out OUT] [--json JSON]
                                   product archived

Read-only. Compares every variable the two netCDF files share, cell by cell,
and reports cells compared, exact matches, max absolute and relative residual,
every differing cell, and the engine and .call stamp of each file. Exits 0
when exact, 1 otherwise.

positional arguments:
  product      the product to check (netCDF)
  archived     the archived product (netCDF)

options:
  -h, --help   show this help message and exit
  --out OUT    write the Markdown report here (default: stdout)
  --json JSON  also write the report as JSON here