# Title of Dataset: Single Pellet Profile Reactor for Measuring Concentration and Temperature Patterns on Porous Catalyst Pellets - Source Code

## Contact Data
Author
Name: Mats Finkeldei
ORCID: 0009-0006-3119-0611
Institution: Hamburg University of Technology - Institute of Chemical Reaction Engineering
Address: Eissendorfer Str. 38, 21073 Hamburg, Germany
Email: mats.finkeldei@tuhh.de

Author
Name: Philipp Reinold
ORCID: 0009-0004-2854-0145
Institution: Institute of Chemical Process Engineering
Address: Fritz-Haber-Weg 2, 76131 Karlsruhe, Baden-Württemberg, Germany
Email: philipp.reinold@kit.edu

Author
Name: Dr. Oliver Korup
Institution: Hamburg University of Technology - Institute of Chemical Reaction Engineering
Address: Eissendorfer Str. 38, 21073 Hamburg, Germany
Email: korup@tuhh.de

Author
Name: Prof. Dr.-Ing. Gregor D. Wehinger
ORCID: 0000-0002-1774-3391
Institution: Institute of Chemical Process Engineering
Address: Fritz-Haber-Weg 2, 76131 Karlsruhe, Baden-Württemberg, Germany
Email: gregor.wehinger@kit.edu

Author/Principal Investigator Information
Name: Prof. Dr. Raimund Horn
ORCID: 0000-0001-8457-3161
Institution: Hamburg University of Technology - Institute of Chemical Reaction Engineering
Address: Eissendorfer Str. 38, 21073 Hamburg, Germany
Email: horn@tuhh.de

Analysis code for a CO2 methanation (Sabatier reaction) study combining
infrared (IR) thermography and mass spectrometry (MS) on a cylindrical
catalyst pellet:

```
CO2 + 4 H2 -> CH4 + 2 H2O
```

This repository contains **code only**. The corresponding experimental
dataset (16 experiments, raw and processed IR/MS data) is published
separately — see [Data](#data) below.

## Equipment

- **IR camera**: Image IR 8380 hp, InfraTec GmbH, Dresden, Germany
- **Mass spectrometer**: HAL300S/3, Hiden Analytical, Warrington, England

## Experimental Matrix

- 4 feed temperatures: 290, 320, 350, 380 °C
- 4 CO2:H2 feed ratios: 1:4 (stoichiometric), 1:3, 1:2, 1:1
- 16 experiments total, all measured on 2025-10-23
- Total flow constant at 1.0 mL/min (reactive gas + 0.2 mL/min Ar carrier)

Two measurement techniques are combined per experiment:

- **IR thermography** — a camera looks down onto the pellet's top face; a
  conical mirror surrounding the pellet reflects the pellet's curved side
  wall into the same field of view, giving access to the axial temperature
  profile along the flow direction (see `src/ir_processing_v4.py` module
  docstring and the "IR Camera Geometry" notes below).
- **Mass spectrometry (MS)** — a scanning capillary measures gas
  composition at 10 positions along the pellet axis.

## Repository Structure

```
code/
├── src/
│   ├── ir_processing_v4.py     # IR image loading, unwrapping, temperature analysis
│   ├── ms_processor.py         # MS peak integration, calibration, mole fractions
│   └── mats_plot_settings.py   # Shared matplotlib style
├── notebooks/
│   ├── preprocess_ir_images.ipynb   # Geometry setup + batch IR unwrapping
│   ├── average_from_unwrapped.ipynb # Temperature analysis + comparison plots
│   ├── process_ms.ipynb             # Single-experiment MS processing
│   ├── ms_results.ipynb             # Aggregate all 16 experiments, publication plots
│   └── calibration/
│       └── calibration.ipynb        # Derives MS calibration factors
├── requirements.txt
└── LICENSE
```

## Installation

```bash
python -m venv .venv
source .venv/bin/activate   # or .venv\Scripts\activate on Windows
pip install -r requirements.txt
```

Requires Python 3.10+ (developed and tested on 3.11). Notebooks use
`ipywidgets`/`ipympl` for interactive geometry drawing — install the Jupyter
widget extension for your environment if figures don't render interactively.

## Data

This code expects the companion dataset to be available locally, either:

1. **Next to this folder**, as a sibling `data/` directory (i.e.
   `<repo>/code/` and `<repo>/data/`), or
2. **Anywhere else**, with the `METHANATION_DATA_ROOT` environment variable
   pointing at it, e.g.:

   ```bash
   export METHANATION_DATA_ROOT=/path/to/downloaded/data
   ```

See the dataset's own `data/README.md` for the expected internal structure
(`data/`, `calibration/`, `mask_config.yaml`, `results/`) and a full
description of every file format. Every notebook in this repository reads
this location through a `DATA_ROOT` variable defined in its first cell.

## Pipeline

1. **MS Calibration** (`notebooks/calibration/calibration.ipynb`) — fits a
   linear calibration (`slope`, `intercept`) per species from known-
   concentration calibration runs. The fitted values must currently be
   copied by hand into `ms_results.ipynb`'s `calibration_factors` dict (see
   [Known Issues](#known-issues)).
2. **IR Preprocessing** (`notebooks/preprocess_ir_images.ipynb`) —
   subtracts a reference frame from each sequence frame, lets you draw the
   inner/outer ellipse geometry interactively, and unwraps the annular
   mirror reflection into a 200 (axial) x 360 (angular) rectangular grid
   per frame (`src/ir_processing_v4.unwrap_annulus`).
3. **Temperature Analysis** (`notebooks/average_from_unwrapped.ipynb`) —
   builds an angular cold-spot mask (to exclude needle/capillary/mounting-
   hole artifacts), time-averages the steady-state window (frames 40-80,
   i.e. t = 400-1000 s at 10 s/frame), reduces to an axial profile, and
   produces the cross-experiment comparison plots.
4. **MS Processing** (`notebooks/process_ms.ipynb`) — integrates MS peaks
   per measurement cycle, averages over the cycle ranges corresponding to
   each capillary position, corrects CO for CO2 fragmentation, and
   normalizes by Argon.
5. **Aggregation & Calibration** (`notebooks/ms_results.ipynb`) — combines
   all 16 experiments, applies the MS calibration, infers H2O
   stoichiometrically, converts to mole fractions, and produces the main
   publication figures.

Each notebook's cells are documented with markdown headers describing what
each step does and why; see the notebook itself for step-by-step detail
beyond this overview.

## Code Documentation

All functions in `src/` have NumPy-style docstrings. Docstring `Notes`
sections call out known quirks (unused parameters, hardcoded values,
side effects) inline at the function they apply to; the summary below
collects the ones most likely to matter to a new reader.

## Known Issues / Limitations

These are documented in place (docstrings / notebook markdown) but
collected here for visibility. None have been changed from the code's
original behavior — fixing them could change published numeric results, so
that decision is left to the maintainer.

- **`temp_unit` encoding artifact** — `metadata.json`'s `"temp_unit": "°C"`
  is read back as `"Â°C"` (UTF-8 bytes decoded as Latin-1/cp1252) in several
  places, including the aggregated `calibrated_df.csv`. Cosmetic (does not
  affect numeric columns) but worth fixing at the source before further use.
- **Duplicate CO-correction code paths** — `ms_processor.correct_co_signal`
  hardcodes the CO2-fragmentation factor to `0.11`, duplicating
  `MSProcessor.correct_co_fragmentation`'s configurable
  `fragmentation_factor` parameter (which also clips negative results to
  zero and copies its input; `correct_co_signal` does neither).
- **`calibrate()` parameter shadowing** — `ms_processor.calibrate(df,
  species, calibration_factors)`'s loop variable `for species in
  calibration_factors.keys():` shadows the `species` parameter, so the
  species actually calibrated are driven entirely by `calibration_factors`,
  not by the `species` argument passed in.
- **`config.yaml` vs. `config_new.yaml`** — per-experiment IR configs exist
  in two files with overlapping but non-identical keys;
  `run_full_pipeline_for_all` reads `config_new.yaml` while
  `save_ellipses_to_yaml` writes `config.yaml`. Clarify which is canonical
  before relying on both staying in sync.
- **Manual calibration hand-off** — the fitted `(slope, intercept)` values
  from `calibration.ipynb` are copied by hand into `ms_results.ipynb`; there
  is no automated link between the two notebooks. The `CO` calibration
  factor used in `ms_results.ipynb` is not derived anywhere in
  `calibration.ipynb` (only H2, CO2, CH4 are fit there) — its provenance
  should be confirmed.
- **`plot_normalized_peaks` ignores some of its own parameters** — the
  `figsize` and `ylabel` arguments are accepted but overridden internally by
  hardcoded values; `title`, `show_legend`, and `grid` have no effect.
  Pellet-boundary lines are hardcoded at x = 16/21 mm in this function,
  versus the ±2.5 mm pellet-centered convention used elsewhere
  (`plot_species_*` functions, `ms_results.ipynb`).
- **`### Labels falsch herum??`** — an unresolved note in
  `average_from_unwrapped.ipynb` questioning whether the ratio-to-line
  mapping in `plot_avg_temperature_vs_feed` is correct; verify against raw
  `metadata.json` `ratio` values before relying on that figure.
- **No automated tests** — correctness currently relies on manual/visual
  inspection of the notebook outputs.

## Citation

If you use this code, please cite it as:

Finkeldei, M., Reinold, P., Korup, O., Wehinger, G. D., & Horn, R. (2026).
*Single Pellet Profile Reactor for Measuring Concentration and Temperature
Patterns on Porous Catalyst Pellets - Source Code* [Software]. TORE
(Technische Universität Hamburg Open Repository).
https://doi.org/10.15480/882.17444

## License

MIT — see [LICENSE](LICENSE). The companion dataset is licensed separately
under the Public Domain Mark Universal (see its own `data/README.md`).
