Skip to content

Latest commit

 

History

5 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

rangefinder.js

JavaScript port of Rangefinder: automated ranging, species identification, and composition for atom probe tomography (APT) mass spectra, running entirely in Node.js or in the browser.

Live browser app · MIT license · Build status

Parity with the Python implementation

The port mirrors the Python pipeline stage by stage and is verified against a frozen baseline of the Python package (see test/python-baseline/BASELINE.md, PyPI release 0.2.1). On all four public controls the outputs match the Python reference:

control peaks peak positions peak areas at.% isotope fractions
Si (APAV) 30/30 < 1e-11 Da < 4e-8 rel exact* exact*
ODS steel (Wang) 55/55 < 3e-12 Da < 2e-8 rel exact* exact*
Ck10 steel (Felfer) 30/30 < 3e-11 Da < 1e-8 rel exact* exact*
MoHf (Leitner) 47/47 < 7e-12 Da < 3e-8 rel exact* exact*

* identical to comparison precision (1e-4 at.%, 1e-5 fraction); top species match on every peak.

Residual differences come from optimizer convergence paths (the TRF least-squares and L-BFGS-B ports converge to the same minima but not bit-identically). One class of divergence to know about: when two histogram bins are mathematically tied, the winner is decided by float noise; the JS Savitzky-Golay uses exact integer arithmetic so true ties resolve as plateaus (matching scipy's plateau midpoints).

Scope

Ported: POS loading, multi-resolution detection, crowded-window mixture deblending, atomic + molecular family fingerprint recovery, adaptive ranging (shadow EER), significance gate, joint-optimizer assignment with element pruning, overlap deconvolution, composition/species/isotope tables, isotope anomaly table (raw, un-reconciled), and fixed-ranging regional composition when the caller supplies an event label for every ion. Regional element, species, and isotope counts are checked to recombine with the whole-sample solution.

Not ported (yet): the isotope audit (EMG tail refits; only affects anomaly reconciliation), spatial label detection, spatial stability diagnostics, the two-pass assignment backend, plotting, and the benchmark harness. .epos files are not supported.

Node usage

git clone https://github.com/kylemcdonald/rangefinder.js.git
cd rangefinder.js
npm test
node cli/run.js sample.pos out/             # analyze, write JSON outputs
node cli/compare.js <python-ref-dir> out/   # diff against a Python reference run

There are no runtime npm dependencies.

The parity/worker tests expect the control .pos files at ../rangefinder/controls/ (they skip if absent).

Browser app

Open the hosted app, then drop a .pos or .cpos file anywhere on the page. The file is decoded and analyzed locally in a Web Worker and is never uploaded to a server. Drop an .rrng file to make its expert ranges available, or drop matching data and .rrng files together. A dropped RRNG becomes active immediately. Paired files skip the automatic analysis for a faster initial load; the Use rangefinder setting runs Rangefinder explicitly when desired.

CPOS beta files are expanded to their original ion count. Their rare bins retain complete 12-bit tuples, common bins use the stored 0.002 Da mass distribution, and the quick RRNG path reads that source spectrum directly. Only the current 224-byte beta layout is accepted; earlier CPOS layouts have no fallback decoder.

To run the app locally, serve the repository root (the app imports ../src/* from web/):

python3 -m http.server 8000
# open http://localhost:8000/

Analysis runs with a progress bar; when done you get:

  • center — the reconstruction as a WebGL point cloud (4M-point render budget, automatic point opacity with a manual override, manual point size, translucent rendering with a dimmed haze for out-of-range ions, nm-tick bounds guide; double-click resets camera). The budget is allocated per peak group by waterfilling (web/budget.js): rare groups keep every ion and only the most abundant peaks (and the background) are uniformly thinned, with per-group alpha compensation so perceived density stays faithful — selecting a 6K-ion trace element always shows all of its ions. The coloring follows the active table tab: one saturated color per row (elements blend for multi-element species; the isotope view colors only the dominant-charge atomic families). Selecting rows hides points outside the selection by default; their opacity can be raised independently;
  • bottom — the full spectrum (wheel = zoom, drag = brush a m/z window that filters the 3D view, drag inside the window = move it, drag the y-axis = log↔linear, double-click = reset, hover for counts + nearest species);
  • right — Elements / Species / Isotopes tables; clicking rows toggles them and highlights the matching peak regions in the spectrum and the matching points in 3D (multi-select, select all, deselect all, and single-row arrow-key stepping supported). The Settings panel exposes point size, automatic/manual opacity, perspective/orthographic projection, continuous Composition Contrast, outside-selection opacity, and additive blending. Settings persist locally across reloads. The Slice panel clips the reconstruction independently along x, y, and z, with a flip control for choosing the retained side. Point rendering always leaves depth testing off. Each composition tab remembers its own row selection;
  • top — workspace PNG, RRNG, and three-table CSV exports, plus independent visibility controls for the Settings and Composition panels. CSV counts are deterministic integer apportionments of the probabilistic composition; fractional expected values are retained separately as weighted_counts. Exported RRNG files retain full-precision boundaries and Rangefinder identity/composition metadata, so re-importing them with the same POS restores the same result while remaining readable by standard RRNG tools.

Keeping parity as the Python algorithm evolves

The Python package is under active development. The workflow:

  1. Freeze the new Python state: update test/python-baseline/ (git archive) and BASELINE.md.
  2. Regenerate references: test/scripts/gen_reference.py per control (run with the baseline on PYTHONPATH, enable_segmentation=False).
  3. Re-run node cli/run.js + node cli/compare.js per control; adapt the JS (usually src/config.js for new tuning constants, plus whatever stage changed) until everything passes.
  4. npm test must stay green; test/scripts/gen_fixtures.py regenerates the scipy fixtures if the numeric primitives ever change.

src/config.js mirrors config/defaults.yaml and must be kept in sync by hand — a config-only change in Python usually needs only that file.

Layout

  • src/lib/ — numeric primitives ported from scipy/numpy: filters (savgol/gaussian/median), peak finding (find_peaks/prominences/widths), NNLS, bounded trust-region least squares (TRF + soft_l1), projected L-BFGS-B, small-matrix SVD/rank/cond/pinv.
  • src/analysis/ — the pipeline stages, file-for-file mirrors of rangefinder/analysis/*.py.
  • src/data/isotopes.data.js — NIST isotope table generated from the Python package (test/scripts/gen_isotopes.py).
  • cli/ — node runner + reference comparator.
  • web/ — the browser app (worker, spectrum panel, three.js scene, tables).
  • test/ — scipy fixtures, parity tests, frozen Python baseline + reference outputs.

About

Automated ranging, species identification, and composition for atom probe tomography in JavaScript

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages