Choosing an engine

Two engines, UIOOPS v3.5 and SODA-2, plus optional corrections packages: which one answers your question, decided by what you need to cite.

The engines are not alternatives picked by taste: each answers a different question. The side-by-side comparison — validation layers, output formats, thresholds, configuration — is in the developer documentation; this page is the decision.

See also Why reproduce the original first · Which enhancements apply to which engine · Configuration

The engines behind one selector

The package carries two engines on equal footing: UIOOPS v3.5 and SODA-2, each a port of its upstream's own source and each validated against that source. An earlier translation of UIOOPS v3.1.4 reproduced archived products and is not part of this package.

One entry point

arrayprobe.engine() is the selector every schematic on this site draws — one call reaching both engines, dispatching and computing nothing:

import arrayprobe

eng = arrayprobe.engine()           # v3.5 -- the default
eng = arrayprobe.engine("soda2")    # the SODA-2 lineage

uiops.engine() is the narrower selector for which UIOOPS release, and refuses SODA-2 because SODA-2 is not an answer to that question. .validation on either result states what that engine's numbers rest on.

Start from the question

You want to…UseWhat you can cite
process new data with UIOOPSthe UIOOPS v3.5 engine (arrayprobe.engine() or uiops.engine())equivalence to Octave running v3.5's own MATLAB source
process new data with SODA-2the SODA-2 engine (arrayprobe.engine("soda2"), or arrayprobe_soda2 directly)equivalence to GDL running NCAR's own IDL source
compare the two processing chains on the same databoth engines, then the hollow-particle check beloweach engine's own validation
apply corrections the UIOOPS developers have not madethe UIOOPS engine plus the enhancements package, where the correction applies to itthe correction's design document, and a comparison of its output with the correction off and on

Which to cite

arrayprobe.engine() with no argument returns UIOOPS v3.5 because a call needs a default, not because one engine outranks the other. A paper should say which engine it used and why.

arrayprobe engines prints this choice and the command for each engine; --catalogue adds, per module, which package implements what for a given UIOOPS version and why.

How to run each one

arrayprobe engines --catalogue
arrayprobe make-demo-flight --dense --with-config demo/base160101000000.2DS
arrayprobe run demo/run.toml demo/base160101000000.2DS --yes

Corrections engine 3.5 applies by default

EngineCorrectionOn by default forTurn it off
3.5stray_pixel_fixevery probe on engine 3.5, 2D-S/HVPS included--stray-pixel off, or [enhancements] stray_pixel_fix = false
3.5ncar_2dc_timing_fix2DC/2DP--ncar-2dc-timing off, or [enhancements] ncar_2dc_timing_fix = false
3.5bin_table_fixa 2D-S that falls to v3.5's HVPS bin table, when the run names no --probe-case--protective off, or [enhancements] bin_table_fix = false
3.5particle-clock offset (no key; the stamp records particle_clock_fix=<+N>s)probes whose particle clock is off the airspeed clock by an unambiguous offset (CIP/PIP)--protective off
3.5repeated second in the time grid, and NaN instead of infinity for a zero-sample-volume second (no key)every probe on engine 3.5--protective off
3.5particle_time_glitch_fix (drops the affected particles)CIP/PIP--protective off, or [enhancements] particle_time_glitch_fix = false
3.5particle_time_int8_fix2DC/2DP, Fast2DC--protective off, or [enhancements] particle_time_int8_fix = false
3.5tas_grid_fix2DC/2DP, CIP/PIP, Fast2DC--protective off, or [enhancements] tas_grid_fix = false
SODA-2no_stuck_diode_fix, last_buffer_index_fix, same_time_buffer_fix, pbp_rejectionflag_ubyte, dilation_clamp_fixevery probe[enhancements] <key> = false

Off on the ORACLES 2D-S/HVPS lineage, which reproduces an archived product. The empty-size-distribution refusal (--accept-implausible) changes no number and is not a correction.

Each correction is recorded in the stage's .call stamp. Detail, stamps and the reasoning behind each are in the developer documentation.

A 2D-S with no archived product defaults to --tas-grid regular when the run has a --tas-source chain; --tas-grid probe, --protective off or tas_grid_fix = false keeps the probe's own grid.

Enhancements are per engine, and the resolver enforces it

A correction the UIOOPS developers already made in v3.5 must not be applied on top of v3.5, or the defect is corrected twice; a correction for code found only in one campaign's modified copy of the UIOOPS MATLAB (a variant with its own thresholds) has nothing to act on in a stock release. So which enhancement may run on which engine is declared per module and enforced when a configuration is resolved — on v3.5 a superseded correction is left off, and a file that names one is refused rather than silently ignored. The same declaration renders Which enhancements apply to which engine.

When two engines disagree: check the hollow particles first

UIOOPS v3.5 divides a hollow particle's size (reject code 'H', a Poisson-spot image) by its Korolev size_factor, 1.07-1.9, before binning it. SODA-2 bins the uncorrected circle unless apply_psc is set. Where hollow particles are common, the two engines put the same particles in different bins, and the difference looks like a missing block of data. One command, reading one v3.5 proc file and writing nothing, says how much of a difference that can make:

arrayprobe view hollow proc.<stem>.H.nc --edges-um 55,65,75,85,95,105,115

(--edges-from <combined file> uses the product's own bins instead.) Each row is one size bin:

columnmeaning
accepted, H, H shareaccepted particles (codes 0HhuR, centre in) at their written size, how many are hollow, and the fraction
outhollow particles the correction moves out of this bin (their uncorrected size is here)
inhollow particles it moves into this bin (their corrected size is here)
undivided, undiv/writtenthe count with the correction undone, and its ratio to the written count: what an engine that does not divide would count, from sizing alone

accepted = undivided - out + in in every bin. The last line is the verdict for the whole range: YES when the correction changes the range's count by at least 5 % and by more than twice its Poisson standard error (--min-fraction changes the 5 %). 5 % is the counting noise of a range of 400 particles; the Poisson clause keeps a sparse range from firing. The counts are the proc file's accepted particles, before sizeDist's area-ratio and interarrival rejects, so they are not the product's Counts; the ratio is the quantity to read.

When the difference is quoted above one size (an N ratio at or above a threshold), use the one-range form from Python: arrayprobe.diagnostic_views.hollow_share_at_or_above(proc_path, threshold_um) returns the accepted count, the hollow count and share, and ratio_undivided_to_written over [threshold_um, inf). Divide the measured between-engine ratio by it: a result near 1 means hollow sizing explains the difference; what remains is something else. Both calls run on a real proc file in the last two cells of examples/1_getting_started/03_choosing_an_engine.ipynb.

To check your own data for the same divergence, run examples/5_data_quality/07_hollow_particles.ipynb: it counts one set of particles with and without the Korolev size factor and reports the large-bin ratio and a no-hollow control. On its demonstration data (stored outputs of section 4b) particles of 600 um and above give B/A = 1.59 (+2.0 dB), the 900-1000 um bin +7.4 dB, and the control with hollow particles removed 0 dB; its last cell runs the same check on one of your own proc files.

Worked example: a 2D-S flight with no archived product. SODA-2's used-bin concentration (55-115 µm) was 1.4655 × v3.5's, against 1.1081 on the next day's flight. In one ten-minute block, v3.5 accepted 3046 particles at 55-115 µm as written and 5341 with the hollow division undone; SODA-2 accepted 4676. Of the 2808 accepted hollow particles whose uncorrected size was 55-115 µm, the correction put 2396 below 55 µm, and that flight had almost nothing above 115 µm to move down in their place. With the division undone, the median concentration ratio falls from 1.4655 to 1.0206. On the next day's flight about half the used-size particles are also hollow, but particles corrected down from above 115 µm refill the range, so its shift is smaller.

A result from arrayprobe_soda2 cites SODA-2, in the form its author, Aaron Bansemer, provided:

Aaron Bansemer. (2026). abansemer/soda2: v2.26.0 (Version v2.26.0) [Computer software]. Zenodo. https://doi.org/10.5281/zenodo.18331418