Metadata-Version: 2.4
Name: chromparser
Version: 0.1.0
Summary: Read chromatography raw files (Chromeleon, Thermo, Agilent, Shimadzu) into one data model and convert them to HDF5.
Keywords: chromatography,GC,GC-MS,HPLC,Chromeleon,Agilent,Shimadzu,Thermo,hdf5
Author: Sebastian Rejman
Author-email: Sebastian Rejman <s.rejman@uu.nl>
License-Expression: MIT
License-File: LICENSE
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Science/Research
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Scientific/Engineering :: Chemistry
Requires-Dist: numpy>=1.22
Requires-Dist: h5py>=3.0
Requires-Dist: olefile>=0.46
Requires-Python: >=3.10
Project-URL: homepage, https://github.com/MyonicS/chromparser
Project-URL: documentation, https://myonics.github.io/chromparser/
Project-URL: repository, https://github.com/MyonicS/chromparser
Project-URL: issues, https://github.com/MyonicS/chromparser/issues
Project-URL: changelog, https://github.com/MyonicS/chromparser/blob/main/CHANGELOG.md
Description-Content-Type: text/markdown

# chromparser

[![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
[![Python 3.10+](https://img.shields.io/badge/python-3.10%2B-blue.svg)](pyproject.toml)
[![Tests](https://github.com/MyonicS/chromparser/actions/workflows/tests.yml/badge.svg)](https://github.com/MyonicS/chromparser/actions/workflows/tests.yml)
[![Docs](https://github.com/MyonicS/chromparser/actions/workflows/docs.yml/badge.svg)](https://myonics.github.io/chromparser/)
[![Status: beta](https://img.shields.io/badge/status-beta-yellow.svg)](CHANGELOG.md)
[![Code style: ruff](https://img.shields.io/badge/code%20style-ruff-261230.svg)](https://github.com/astral-sh/ruff)

chromparser reads chromatography raw files (Chromeleon, Thermo, Agilent,
Shimadzu, ANDI netCDF) into one Python data model, and converts them to a
single HDF5 layout or to ANDI netCDF. Pure Python on numpy, h5py and olefile;
MIT licensed; every format is clean-room and documented.

## Supported formats

| Vendor | Files | Reader | Format id |
| --- | --- | --- | --- |
| Thermo Chromeleon 7 | `.cmbx` archives (signals and embedded MS) | `read_cmbx` | `chromeleon-cmbx` |
| | raw signal files (`SignHdr`, from a data vault) | `read_chromeleon_raw` | `chromeleon-raw` |
| | text exports (`.txt`), 7.2 and 7.3, any locale | `read_chromeleon_txt` | `chromeleon-txt` |
| Thermo | `.raw` MS files (v66, centroid, single quad), e.g. extracted from `.cmbx` | `read_thermo_raw` | `thermo-raw` |
| Agilent | `.ch` (versions 8, 81, 179, 181) | `read_agilent_ch` | `agilent-ch` |
| | `.D` folders | `read_agilent_d` | `agilent-d` |
| | OpenLab `.dx` archives | `read_agilent_dx` | `agilent-dx` |
| Shimadzu GCMSsolution | `.qgd` (GC-MS, centroids) | `read_qgd` | `shimadzu-qgd` |
| | `.gcd` (GC/FID) | `read_gcd` | `shimadzu-gcd` |
| ANDI (netCDF) | ANDI-MS `.cdf` (GC-MS, centroids) | `read_andi_ms` | `andi-ms` |
| | ANDI chromatography / AIA `.cdf` (one signal) | `read_andi_chrom` | `andi-chrom` |

The format is detected from the content (magic bytes, OLE2 storages, zip
members, netCDF variables, folder contents), never from the extension.
Everything chromparser reads can also be written as ANDI files
(`write_andi`), which AMDIS, NIST MS Search, MZmine, OpenChrom, MS-DIAL and
XCMS read; ANDI loses metadata, HDF5 keeps everything
([`docs/formats/andi.md`](docs/formats/andi.md)).

> Chromeleon and Thermo are trademarks of Thermo Fisher Scientific, Agilent
> and OpenLab of Agilent Technologies, Shimadzu and GCMSsolution of Shimadzu
> Corporation. This project is not affiliated with or endorsed by them. The
> formats were reverse engineered for interoperability; see `docs/formats/`.

## Installation

```bash
pip install chromparser          # or: uv add chromparser
```

Python 3.10 or newer.

## Usage

### Command line

```bash
chromparser info run.cmbx sample.qgd data.D           # injections, channels, MS runs
chromparser convert run.cmbx                           # writes run.h5 (layout v3)
chromparser convert *.qgd --overwrite
chromparser convert 152612018.raw --signal-unit pA     # unit is not in raw files
chromparser convert old.h5                             # cmbx-reader v1/v2 -> old.v3.h5
chromparser convert run.cmbx --to andi -o out/         # one ANDI .cdf per signal / MS run
chromparser extract-ms run.cmbx -d out/                # the embedded Thermo .raw files
```

`chromparser convert --no-ms` skips the MS data of `.cmbx` archives.

### Python

```python
import chromparser

for injection in chromparser.read("run.cmbx"):  # always a list
    print(injection.name, injection.inject_time, injection.metadata)
    for channel, signal in injection.signals.items():
        signal.time, signal.values, signal.unit  # minutes, as stored
    for name, ms in injection.ms.items():
        mz, intensity = ms.spectrum(ms.scan_number[0])
        eic = ms.xic(57.0, tolerance=0.5)  # one value per scan

injection = chromparser.read_one("FID1A.ch")  # files with one injection
chromparser.sniff("mystery.dat")  # e.g. "shimadzu-qgd"
chromparser.convert("sample.qgd", "sample.h5")
injections = chromparser.read_hdf5("sample.h5")  # == chromparser.read("sample.qgd")
chromparser.write_andi(injections, "out/")  # out/sample_MS.cdf, for AMDIS etc.
```

### Data model

See [`docs/model.md`](docs/model.md) for the data model and the reader
contract, and [`docs/hdf5.md`](docs/hdf5.md) for the HDF5 layout. In short:

* `Signal`: `time` (min, float64), `values` (dtype as stored), `unit`,
  `acquisition_start` (UTC), `sampling_interval` (s), `metadata`.
* `MSData`: per scan `scan_number`, `retention_time` (min), `tic`, optional
  `base_peak_*`, `filters`, `ms_level`; per peak `mz`, `intensity` (dtypes as
  stored: float32 for Thermo, float64 m/z and uint32 intensity for Shimadzu)
  and optional `flags` (bit 0 = saturated), CSR-indexed by `peak_offset`.
* `inject_time` is local time: timezone-aware when the file has the offset,
  naive when it stores only the wall time (Agilent `.ch`, Chromeleon 7.2
  text exports).
* Unsupported variants raise `UnsupportedVariant` saying what was found;
  readers never return partial data silently. Recoverable oddities are
  warnings, recorded in `metadata["warnings"]`.

## Scope and limitations

* Files and data types not in the table above are probably not supported
  yet. chromparser then raises an error saying what it found; it does not
  return partial data.
* Agilent `.ch` versions 8, 81 and 181 are implemented from the documented
  encodings but have not been checked against real files yet; please share
  a sample if you have one.
* Spectra are kept as centroids; binning to nominal m/z is left to the user
  (`np.rint(ms.mz)`).

If a file fails to read, please open an issue; a sample file helps a lot.

## Development

```bash
uv sync                    # environment with the dev tools
uv run pytest              # runs without local data
uv run ruff check . && uv run ruff format --check .
uv sync --group docs && uv run mkdocs serve   # the documentation site
```

Large local files make more tests run; see [`tests/README.md`](tests/README.md).
Contributor notes are in [`AGENTS.md`](AGENTS.md).

## License

MIT. The Agilent `.ch` decoders are adapted from chemplexity/chromatography
(MIT, Copyright (c) 2014 James Dillon); see [`LICENSE`](LICENSE).
