Orientation for OAP scientists

You know optical array probes and at least one processing chain. You do not know this package. Start here.

In short: this is UIOOPS — the McFarquhar-group MATLAB chain — re-implemented in Python, its v3.5 release as the engine, validated against Octave runs of v3.5's own sources. An earlier translation of UIOOPS v3.1.4 reproduced archived products and is not part of this package (Validation). It is not a new processing philosophy. What is new: the chain runs without a licence, every accept/reject and sizing decision is a tested line of code you can read, and what the reproduction found in the original is written up for its authors.

See also User guide · Validation · Glossary · Known gaps

In your terms

If you use…Then arrayprobe is…
UIOOPSthe same algorithms — read_binary_*, imgProc_sm, sizeDist, the combine — with the same outputs, one MATLAB file:line cited per function. The shattering gate, the six d_choice sizing definitions, Holroyd habits, the reject ladder and the sample-area methods behave as in the MATLAB, and the findings say where that behaviour is not what the code comments say
SODA-2a different chain with a narrower purpose. SODA-2 offers several sizing methods, water processing with the Korolev correction, stuck-bit correction and a GUI; this translates one chain exactly, puts corrections in a separate opt-in package, and reviews particles in a browser tool rather than a processing GUI
OASIS or a lab-internal chainan open, tested reference you can run beside your own and diff against, on any machine with Python
NCAR's Process2D (NCAR/aircraft_oap)an independent open chain: Process2D is complete in its own right (C++, Apache-2.0; translators into the RAF OAP file format, then process2d to size distributions) and in production use at NCAR's Research Aviation Facility under its own algorithm family; this one translates UIOOPS and SODA-2 instead, so the two are separate opinions on the same instrument
SPIF / SPIFpycomplementary: SPIF standardises the decoded-image layer; this one carries a decode through to size distributions under one published algorithm family

The shape of the work: the port surfaced findings about the MATLAB, and those findings are fixed — in the v3.5 engine, the default, where the developers addressed them, and otherwise in the opt-in enhancements, each correction measured by running both ways on the same data. In the v3.5 engine, preserved upstream behaviour is likewise kept unless it destroys data, and each case carries a regression test. See Choosing an engine, CLI reference and Which enhancements apply to which engine.

Running NCAR's Process2D on the same images

Two chains given the same raw file differ in decoding as well as in algorithms, so a difference in their size distributions cannot be pinned on either. Exporting this package's decoded 2D-S images in NCAR's RAF OAP file format (.2d) lets Process2D run on the identical images, so what remains between the two is attributable to the algorithms.

python -m arrayprobe_uiops.export_oap DECODE OUT --probe 2DS

The reader arrayprobe_uiops.oap_read (read_oap_2ds) decodes a .2d file by process2d's rule, written independently of the exporter, so the round trip checks the export against code it does not share. Neither module computes a number used by any product.

From Process2D into this package. field2006_shatter transcribes Process2D's Field et al. (2006) per-second inter-arrival fit (a double-Poisson fit over the last 400 inter-arrival times, cutoff at 1/20 of the first mode's peak) as an opt-in enhancement. It runs beside a size distribution and writes its fit to a copy; the batch driver runs it when a run's configuration names it.

Procedures learned from comparing chains, each a notebook on demo data:

What has been shown, and what has not

Fifteen flights of one field campaign — six from 2016, nine from 2017 — reproduce their archived 2D-S products cell by cell, ten of them exactly — every non-zero cell of Counts and Size Distribution in bins 6+ equal, over the seconds both products hold; the table with every residual attributed is Validation. Read it with the Glossary open: a percentage there is computed over one of four nested cell populations spanning three orders of magnitude, and the headline is always the smallest and hardest — non-zero cells in the used size bins.

What has not been shown:

The thirty-minute tour

git clone https://github.com/sgupta92/arrayprobe && cd arrayprobe
python -m venv .venv
# install the clone as CONTRIBUTING.md describes          # Linux / macOS: .venv/bin/python
.venv/Scripts/python.exe -m unittest discover -s tests -p "test_*.py"     # ~10 min, no data needed

The suite needs no MATLAB, no Octave and no campaign data: most Octave reference files are committed and run on a fresh clone. Reference outputs cut from real campaign data are not distributed, so the checks that need them (among them the v3.5 sizeDist and combine drivers, the CIP/PIP/2DC/Fast2DC paths and the SODA-2 real-file runs) report as skipped, with the reason; those checks were run against Octave during development. On Linux and macOS read .venv/bin/python for .venv/Scripts/python.exe.

Ten minutes, not two. 589 s measured for the default run, which gates out ~51 tests whose cost is their own subject (stall-timer watchdogs, subprocess-driven notebook and paper-build checks). ARRAYPROBE_SLOW=1 runs those too, a further ~119 s, as CI does; the number of tests executed (collected minus skipped) matches the project's only with that variable set. A full run of the shipped suite collects 4,728 tests on this release.

Then process a flight. arrayprobe run takes raw .2DS files through decode, particle framing, size distributions and the combine, with every choice — engine, campaign preset, profile, output folder, stages — named in one run file. Copy examples/start_here/run.toml, set its preset, airspeed source and out for your campaign; a campaign the package does not ship is declared in a file you own (Configuration):

.venv/Scripts/python.exe -m arrayprobe run my_run.toml raw/base*.2DS --yes --chunks 8 --s2-jobs 8

Why --yes. A run that names no threshold policy prints the configuration it will use and asks before starting; --yes answers in advance. Where stdin is not a terminal — nohup, CI, any script, which is how a multi-hour run is launched — it refuses to start and exits 2. Declaring --regime or a [thresholds] section in a config file removes the question; see Configuration.

Before the first run: Disk — the decoded image file is UIOOPS's own int32 layout and can reach twelve times the raw size, the per-particle file several times more; the driver estimates the worst case and refuses to start without room. Time — particle framing is about 80 % of a run (on the one 2D-S flight timed by stage, engine v3.5, Speed); a 1.5 GB two-file flight took 5.3 hours on 12 cores with the parallel flags above, and the flags never change a written byte — a test, not a claim.

Finally look at the particles, because a size distribution cannot tell you whether the particles behind it were framed and accepted correctly:

.venv/Scripts/python.exe -m arrayprobe triage --decoded-dir <your run's out folder>

--decoded-dir is the run's out folder, and it must still hold the decode images. The viewer pairs each proc.<flight>.<channel>.nc with the decode <flight>.<channel>.nc beside it. If the decode was deleted to reclaim disk, the flight is indexed but reads no matching raw/image file found. Point the viewer where both files survive, or re-run stage 2. A self-contained example needing no campaign data is on the visualisation page.

Knobs you already know, and where they live

In UIOOPS you set…Here it is…
d_choice (sizing definition)a named entry in the size-definition registry; the driver's preset fixes 6, the minimum enclosing circle, because that is the call it was transcribed from — a preset for another campaign names its own
SAmethodnever defaulted, any more than d_choice is; with arrayprobe run <config.toml> ([run].engine = "3.5", [run].stages = "4" for the size distribution alone) both come from the preset, which fixes centre-in.
the twelfth sizeDist argument (inter-arrival threshold file)[shattering].threshold_file in the run file, and it matters more than it looks: naming a file that does not exist enables shattering on the per-probe default, while passing nothing skips the block and accepts ~58 % more particles. The output cannot say which happened, so every stage writes a .call sidecar that can
reject thresholds (rej_hollow, max_edge_img_ar, …)fields of a named FidelityProfile, never a free number. The archived products behind the validation used three thresholds that differ from the released defaults; both profiles ship and the choice is explicit
probeName / projectNamerows in a configuration table — a project the table does not name falls through to its otherwise branch exactly as sizeDist.m does. A campaign needs a new row only if its probe constants differ
stuck-diode masks--diode-corrected, off by default, always writing to a separate file

Every fidelity-sensitive argument is required rather than defaulted, so a run cannot quietly acquire someone else's configuration. Configuration has the run-file form.

Reading the repository

Three directories are the package; the rest can wait.

ReadSkip on a first pass
src/ — the enginestests/ — read a test when you doubt a claim
the upstream-findings list — what reproducing UIOOPS found in the MATLAB, for its authorsscripts/ — the batch driver and helpers behind the CLI
these pages and examples/ — the notebooks, each on demo datathe upstream MATLAB sources; each engine cites them by file:line
enhancements_uiops/ — corrections and new science, separate from the port

What would help most

One raw-plus-product pair for any probe or campaign this project does not hold — a full ice-phase 2D-S raw-plus-UIOOPS-product pair, or any CIP/PIP/2DC pair — closes a gap the port cannot close alone. Second, an answer to the HVPS count question from anyone who processed HVPS data with UIOOPS. Third, a reading of the findings by someone who runs the MATLAB: each is checkable in Octave in a minute, and the items marked read are flagged for that check.