operandomerge 0.1.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. operandomerge-0.1.1/.github/workflows/ci.yml +82 -0
  2. operandomerge-0.1.1/.github/workflows/release.yml +73 -0
  3. operandomerge-0.1.1/.gitignore +14 -0
  4. operandomerge-0.1.1/CHANGELOG.md +29 -0
  5. operandomerge-0.1.1/CITATION.cff +10 -0
  6. operandomerge-0.1.1/CONTRIBUTING.md +20 -0
  7. operandomerge-0.1.1/LICENSE +22 -0
  8. operandomerge-0.1.1/PKG-INFO +244 -0
  9. operandomerge-0.1.1/README.md +203 -0
  10. operandomerge-0.1.1/docs/configuration.md +28 -0
  11. operandomerge-0.1.1/docs/dependency-licenses.md +19 -0
  12. operandomerge-0.1.1/docs/images/example-alignment.png +0 -0
  13. operandomerge-0.1.1/docs/images/showcase-before-after.png +0 -0
  14. operandomerge-0.1.1/docs/images/social-preview.png +0 -0
  15. operandomerge-0.1.1/docs/interchange-draft.md +61 -0
  16. operandomerge-0.1.1/docs/scientific-validation.md +43 -0
  17. operandomerge-0.1.1/examples/example-config.json +47 -0
  18. operandomerge-0.1.1/examples/gc.csv +4 -0
  19. operandomerge-0.1.1/examples/ms.csv +6 -0
  20. operandomerge-0.1.1/examples/reactor_logger.csv +6 -0
  21. operandomerge-0.1.1/examples/showcase/README.md +46 -0
  22. operandomerge-0.1.1/examples/showcase/config.json +134 -0
  23. operandomerge-0.1.1/examples/showcase/gas_chromatograph.csv +4 -0
  24. operandomerge-0.1.1/examples/showcase/generate_showcase.py +342 -0
  25. operandomerge-0.1.1/examples/showcase/mass_spectrometer.csv +302 -0
  26. operandomerge-0.1.1/examples/showcase/mfc_controller.csv +31 -0
  27. operandomerge-0.1.1/examples/showcase/reactor_logger.csv +62 -0
  28. operandomerge-0.1.1/examples/showcase/xrd_scans.csv +12 -0
  29. operandomerge-0.1.1/pyproject.toml +83 -0
  30. operandomerge-0.1.1/src/operandomerge/__init__.py +26 -0
  31. operandomerge-0.1.1/src/operandomerge/cli.py +84 -0
  32. operandomerge-0.1.1/src/operandomerge/config.py +94 -0
  33. operandomerge-0.1.1/src/operandomerge/controller.py +63 -0
  34. operandomerge-0.1.1/src/operandomerge/export.py +66 -0
  35. operandomerge-0.1.1/src/operandomerge/gui.py +506 -0
  36. operandomerge-0.1.1/src/operandomerge/io.py +27 -0
  37. operandomerge-0.1.1/src/operandomerge/merge.py +252 -0
  38. operandomerge-0.1.1/src/operandomerge/models.py +194 -0
  39. operandomerge-0.1.1/src/operandomerge/py.typed +1 -0
  40. operandomerge-0.1.1/src/operandomerge/qc.py +73 -0
  41. operandomerge-0.1.1/src/operandomerge/service.py +20 -0
  42. operandomerge-0.1.1/src/operandomerge/timeline.py +141 -0
  43. operandomerge-0.1.1/tests/conftest.py +21 -0
  44. operandomerge-0.1.1/tests/integration/test_cli_gui.py +111 -0
  45. operandomerge-0.1.1/tests/integration/test_io_config_export.py +107 -0
  46. operandomerge-0.1.1/tests/scientific/test_known_offsets.py +22 -0
  47. operandomerge-0.1.1/tests/scientific/test_type_aware_merge.py +185 -0
  48. operandomerge-0.1.1/tests/showcase/test_operando_showcase.py +145 -0
  49. operandomerge-0.1.1/tests/unit/test_models.py +100 -0
  50. operandomerge-0.1.1/tests/unit/test_qc.py +20 -0
  51. operandomerge-0.1.1/tests/unit/test_release_workflow.py +15 -0
  52. operandomerge-0.1.1/tests/unit/test_timeline.py +88 -0
  53. operandomerge-0.1.1/tests/unit/test_version_metadata.py +29 -0
@@ -0,0 +1,82 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ pull_request:
6
+
7
+ permissions:
8
+ contents: read
9
+
10
+ concurrency:
11
+ group: ci-${{ github.workflow }}-${{ github.ref }}
12
+ cancel-in-progress: true
13
+
14
+ jobs:
15
+ quality:
16
+ name: Lint and type check
17
+ runs-on: ubuntu-latest
18
+ steps:
19
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
20
+ with:
21
+ persist-credentials: false
22
+ - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7
23
+ with:
24
+ python-version: "3.12"
25
+ cache: pip
26
+ - run: python -m pip install -e '.[dev]'
27
+ - run: ruff check .
28
+ - run: mypy src
29
+
30
+ test:
31
+ name: Test Python ${{ matrix.python-version }}
32
+ runs-on: ubuntu-latest
33
+ strategy:
34
+ matrix:
35
+ python-version: ["3.10", "3.12"]
36
+ steps:
37
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
38
+ with:
39
+ persist-credentials: false
40
+ - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7
41
+ with:
42
+ python-version: ${{ matrix.python-version }}
43
+ cache: pip
44
+ - run: python -m pip install -e '.[dev]'
45
+ - run: pytest
46
+ - run: operandomerge inspect examples/reactor_logger.csv
47
+ - run: operandomerge merge examples/example-config.json --excel /tmp/report.xlsx --plot /tmp/alignment.png
48
+ - run: test -s /tmp/report.xlsx && test -s /tmp/alignment.png
49
+ - if: matrix.python-version == '3.12'
50
+ run: >-
51
+ python examples/showcase/generate_showcase.py
52
+ --output-dir /tmp/operandomerge-showcase
53
+ - if: matrix.python-version == '3.12'
54
+ run: >-
55
+ test -s /tmp/operandomerge-showcase/operandomerge-showcase.xlsx &&
56
+ test -s /tmp/operandomerge-showcase/before-after.png &&
57
+ test -s docs/images/showcase-before-after.png &&
58
+ test -s docs/images/social-preview.png
59
+ - if: matrix.python-version == '3.12'
60
+ run: sudo apt-get update && sudo apt-get install -y python3-tk xvfb
61
+ - if: matrix.python-version == '3.12'
62
+ run: >-
63
+ xvfb-run -a python -c
64
+ "from operandomerge.gui import create_app;
65
+ root, app = create_app(); root.update(); root.destroy()"
66
+
67
+ package:
68
+ name: Build and clean-install package
69
+ runs-on: ubuntu-latest
70
+ steps:
71
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
72
+ with:
73
+ persist-credentials: false
74
+ - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7
75
+ with:
76
+ python-version: "3.12"
77
+ cache: pip
78
+ - run: python -m pip install build
79
+ - run: python -m build
80
+ - run: python -m venv /tmp/operandomerge-clean
81
+ - run: /tmp/operandomerge-clean/bin/pip install dist/*.whl
82
+ - run: /tmp/operandomerge-clean/bin/operandomerge --version
@@ -0,0 +1,73 @@
1
+ name: Publish released package
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+
7
+ permissions:
8
+ contents: read
9
+
10
+ concurrency:
11
+ group: release-${{ github.event.release.id }}
12
+ cancel-in-progress: false
13
+
14
+ jobs:
15
+ build:
16
+ name: Build and verify release artifacts
17
+ if: github.event.release.prerelease == false
18
+ runs-on: ubuntu-latest
19
+ steps:
20
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
21
+ with:
22
+ persist-credentials: false
23
+ - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7
24
+ with:
25
+ python-version: "3.12"
26
+ - run: python -m pip install build twine
27
+ - run: python -m build
28
+ - run: python -m twine check dist/*
29
+ - run: python -m venv /tmp/operandomerge-release
30
+ - run: /tmp/operandomerge-release/bin/pip install dist/*.whl
31
+ - run: /tmp/operandomerge-release/bin/python -m pip check
32
+ - run: >-
33
+ /tmp/operandomerge-release/bin/python -c
34
+ "import operandomerge; print(operandomerge.__version__)"
35
+ - name: Verify release tag matches installed package
36
+ env:
37
+ RELEASE_TAG: ${{ github.event.release.tag_name }}
38
+ run: |
39
+ INSTALLED_VERSION=$(/tmp/operandomerge-release/bin/python -c "import operandomerge; print(operandomerge.__version__)")
40
+ test "${RELEASE_TAG}" = "v${INSTALLED_VERSION}"
41
+ - run: /tmp/operandomerge-release/bin/operandomerge --help
42
+ - run: >-
43
+ /tmp/operandomerge-release/bin/operandomerge merge
44
+ examples/showcase/config.json
45
+ --excel /tmp/release-showcase.xlsx
46
+ --plot /tmp/release-showcase.png
47
+ - run: test -s /tmp/release-showcase.xlsx && test -s /tmp/release-showcase.png
48
+ - run: sudo apt-get update && sudo apt-get install -y python3-tk xvfb
49
+ - run: >-
50
+ xvfb-run -a /tmp/operandomerge-release/bin/python -c
51
+ "from operandomerge.gui import create_app;
52
+ root, app = create_app(); root.update(); root.destroy()"
53
+ - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
54
+ with:
55
+ name: python-package
56
+ path: dist/
57
+ if-no-files-found: error
58
+
59
+ publish:
60
+ name: Publish to PyPI with trusted publishing
61
+ needs: build
62
+ runs-on: ubuntu-latest
63
+ environment:
64
+ name: pypi
65
+ url: https://pypi.org/p/operandomerge
66
+ permissions:
67
+ id-token: write # Required for PyPI trusted publishing; no API token is stored.
68
+ steps:
69
+ - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
70
+ with:
71
+ name: python-package
72
+ path: dist/
73
+ - uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # release/v1
@@ -0,0 +1,14 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *.egg-info/
4
+ .coverage
5
+ .mypy_cache/
6
+ .pytest_cache/
7
+ .ruff_cache/
8
+ .venv/
9
+ build/
10
+ dist/
11
+ *.xlsx
12
+ *.png
13
+ !docs/images/*.png
14
+
@@ -0,0 +1,29 @@
1
+ # Changelog
2
+
3
+ All notable changes follow [Keep a Changelog](https://keepachangelog.com/) and
4
+ the project uses [Semantic Versioning](https://semver.org/).
5
+
6
+ ## [0.1.1] - Unreleased
7
+
8
+ ### Added
9
+
10
+ - Five-instrument operando showcase with reactor, MFC, MS, GC, and XRD clocks.
11
+ - Actual before/after and social-preview images generated from `MergeService` results.
12
+ - Showcase regressions for known delays, boundary semantics, provenance, and GC non-interpolation.
13
+ - Experimental interchange concepts for channels, units, metadata, provenance, QC, and sources.
14
+ - Release-triggered trusted-publishing workflow with isolated build and publish jobs.
15
+
16
+ ### Changed
17
+
18
+ - Expanded project metadata, README quickstart, assumptions, limitations, and related-tool links.
19
+ - CI now regenerates and verifies the full showcase on Python 3.12.
20
+
21
+ ## [0.1.0] - 2026-08-23
22
+
23
+ ### Added
24
+
25
+ - Lossless time normalization for absolute, injection, elapsed, and local clocks.
26
+ - Manual/reference-event alignment and explicit physical delay components.
27
+ - Data-type-aware merge without discrete/event interpolation.
28
+ - Value provenance, QC, plots, CSV/Excel export, CLI, GUI, and public API.
29
+ - Synthetic scientific regression suite and reproducible example workflow.
@@ -0,0 +1,10 @@
1
+ cff-version: 1.2.0
2
+ message: "If you use OperandoMerge in research, please cite this software."
3
+ title: "OperandoMerge"
4
+ type: software
5
+ version: 0.1.1
6
+ authors:
7
+ - family-names: "Kim"
8
+ given-names: "Hyun Dong"
9
+ repository-code: "https://github.com/hdkim99/OperandoMerge"
10
+ license: MIT
@@ -0,0 +1,20 @@
1
+ # Contributing
2
+
3
+ Please open an issue before changing scientific definitions. A contribution must
4
+ preserve raw values, document clock/delay semantics, include a hand-checkable or
5
+ synthetic regression case, and keep CLI/GUI calculations in `MergeService`/core.
6
+
7
+ ```bash
8
+ python -m venv .venv
9
+ . .venv/bin/activate
10
+ python -m pip install -e '.[dev]'
11
+ ruff check .
12
+ mypy src
13
+ pytest
14
+ python -m build
15
+ ```
16
+
17
+ Use focused commits. Do not weaken tolerances merely to pass a noisy test. Report
18
+ whether a failure is an implementation, test, scientific-definition, unit,
19
+ data-model, or environment problem.
20
+
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 OperandoMerge contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
@@ -0,0 +1,244 @@
1
+ Metadata-Version: 2.5
2
+ Name: operandomerge
3
+ Version: 0.1.1
4
+ Summary: Synchronize heterogeneous experimental data onto one reproducible timeline.
5
+ Project-URL: Homepage, https://github.com/hdkim99/OperandoMerge
6
+ Project-URL: Documentation, https://github.com/hdkim99/OperandoMerge#readme
7
+ Project-URL: Issues, https://github.com/hdkim99/OperandoMerge/issues
8
+ Project-URL: Source, https://github.com/hdkim99/OperandoMerge
9
+ Project-URL: Changelog, https://github.com/hdkim99/OperandoMerge/blob/main/CHANGELOG.md
10
+ Author: Hyun Dong Kim
11
+ Maintainer: Hyun Dong Kim
12
+ License-Expression: MIT
13
+ License-File: LICENSE
14
+ Keywords: catalysis,chemical-engineering,operando,scientific-data,synchronization,time-series
15
+ Classifier: Development Status :: 3 - Alpha
16
+ Classifier: Intended Audience :: Science/Research
17
+ Classifier: License :: OSI Approved :: MIT License
18
+ Classifier: Operating System :: OS Independent
19
+ Classifier: Programming Language :: Python :: 3
20
+ Classifier: Programming Language :: Python :: 3.10
21
+ Classifier: Programming Language :: Python :: 3.11
22
+ Classifier: Programming Language :: Python :: 3.12
23
+ Classifier: Programming Language :: Python :: 3.13
24
+ Classifier: Programming Language :: Python :: 3.14
25
+ Classifier: Topic :: Scientific/Engineering :: Chemistry
26
+ Classifier: Topic :: Scientific/Engineering :: Information Analysis
27
+ Classifier: Typing :: Typed
28
+ Requires-Python: >=3.10
29
+ Requires-Dist: matplotlib<4,>=3.7
30
+ Requires-Dist: numpy<3,>=1.24
31
+ Requires-Dist: openpyxl<4,>=3.1
32
+ Requires-Dist: pandas<3,>=2.0
33
+ Provides-Extra: dev
34
+ Requires-Dist: build>=1.2; extra == 'dev'
35
+ Requires-Dist: mypy>=1.10; extra == 'dev'
36
+ Requires-Dist: pandas-stubs>=2.0; extra == 'dev'
37
+ Requires-Dist: pytest-cov>=5; extra == 'dev'
38
+ Requires-Dist: pytest>=8; extra == 'dev'
39
+ Requires-Dist: ruff>=0.6; extra == 'dev'
40
+ Description-Content-Type: text/markdown
41
+
42
+ # OperandoMerge
43
+
44
+ > Heterogeneous instruments. One reproducible experiment timeline.
45
+
46
+ [![CI](https://github.com/hdkim99/OperandoMerge/actions/workflows/ci.yml/badge.svg)](https://github.com/hdkim99/OperandoMerge/actions/workflows/ci.yml)
47
+ [![Python 3.10+](https://img.shields.io/badge/python-3.10%2B-blue.svg)](https://www.python.org/)
48
+ [![License: MIT](https://img.shields.io/badge/license-MIT-yellow.svg)](LICENSE)
49
+
50
+ OperandoMerge aligns reactor loggers, MFCs, high-frequency MS, sparse GC, XRD,
51
+ Raman, FTIR, and other CSV/XLSX logs without hiding where values came from. It
52
+ uses channel semantics to interpolate continuous measurements, hold stepwise
53
+ setpoints causally, and keep event/discrete samples strictly discrete.
54
+
55
+ ![Actual OperandoMerge before-and-after showcase](https://raw.githubusercontent.com/hdkim99/OperandoMerge/main/docs/images/showcase-before-after.png)
56
+
57
+ The figure is generated from the repository's real five-instrument synthetic
58
+ workflow. Purple GC values exist only at 120, 300, and 480 s: the other 298
59
+ canonical rows remain missing rather than receiving invented chromatograms.
60
+
61
+ ## Install
62
+
63
+ Python 3.10 or newer is required. PyPI publication is prepared but is not yet
64
+ available, so install from the public source repository:
65
+
66
+ ```bash
67
+ python -m pip install "operandomerge @ git+https://github.com/hdkim99/OperandoMerge.git"
68
+ ```
69
+
70
+ For development:
71
+
72
+ ```bash
73
+ git clone https://github.com/hdkim99/OperandoMerge.git
74
+ cd OperandoMerge
75
+ python -m pip install -e '.[dev]'
76
+ ```
77
+
78
+ ## 30-second showcase
79
+
80
+ ```bash
81
+ mkdir -p showcase-output
82
+
83
+ operandomerge inspect examples/showcase/reactor_logger.csv
84
+
85
+ operandomerge merge examples/showcase/config.json \
86
+ --excel showcase-output/operandomerge-showcase.xlsx \
87
+ --plot showcase-output/alignment-cli.png
88
+ ```
89
+
90
+ Expected CLI summary:
91
+
92
+ ```text
93
+ Merged 5 dataset(s), 301 timeline row(s); QC: 0 error(s), 0 warning(s)
94
+ ```
95
+
96
+ The Excel report contains complete `merged`, `metadata`, `provenance`, `qc`, and
97
+ `configuration` sheets. To regenerate the source CSVs and showcase figures from
98
+ their deterministic equations:
99
+
100
+ ```bash
101
+ python examples/showcase/generate_showcase.py --output-dir showcase-output
102
+ ```
103
+
104
+ Launch the same workflow through the desktop UI with:
105
+
106
+ ```bash
107
+ operandomerge-gui
108
+ ```
109
+
110
+ Python's Tk bindings are included with python.org installers. Some distro or
111
+ Homebrew Python builds require their matching optional Tk package, such as
112
+ `python3-tk` on Debian/Ubuntu.
113
+
114
+ ## What is being aligned?
115
+
116
+ The showcase represents one 600 s catalyst experiment with deliberately
117
+ different clocks and sampling intervals:
118
+
119
+ | Instrument | Raw sampling | Alignment evidence | Result semantics |
120
+ |---|---:|---|---|
121
+ | Reactor logger | 10 s | absolute UTC experiment origin | temperature/pressure, continuous |
122
+ | MFC controller | 20 s | local event at 100 s = experiment event at 120 s | H₂/Ar flow, stepwise |
123
+ | MS | 2 s | known 8 s transport delay | m/z 44/28, continuous |
124
+ | GC | three injections | 12 s sampling + 18 s analysis delay | compositions, discrete samples |
125
+ | XRD | 60 s | −5 s clock offset + 15 s analysis delay | phase stepwise; lattice discrete |
126
+
127
+ The raw clocks and applied corrections remain in metadata/provenance. See the
128
+ [showcase definition](examples/showcase/README.md) for hand-checkable invariants.
129
+
130
+ ## Scientific rules
131
+
132
+ The canonical coordinate is `experiment_time_s`:
133
+
134
+ ```text
135
+ experiment_time_s = source_time_s + alignment_offset_s - total_delay_s
136
+ total_delay_s = manual + sampling + transport + dead_volume + analysis
137
+ ```
138
+
139
+ A positive delay means a reported value corresponds to an earlier physical event,
140
+ so it is subtracted. Reference-event alignment uses
141
+ `target_event_time_s - source_event_time_s`. Absolute timestamps use an explicit
142
+ ISO-8601 experiment origin when provided; otherwise the earliest absolute record
143
+ becomes zero.
144
+
145
+ | Channel semantic | Available policy | Scientific constraint |
146
+ |---|---|---|
147
+ | `continuous` | linear, nearest, none | bounded by measured range; no extrapolation |
148
+ | `stepwise` | previous, none | causal hold; never takes a future setpoint |
149
+ | `event` | exact timestamp only | never interpolated |
150
+ | `discrete_sample` | exact timestamp only | never interpolated |
151
+
152
+ For every emitted value, provenance identifies the source file, column, row,
153
+ original timestamp, offset, delay, and interpolation method. Linear values retain
154
+ both bracketing source rows/timestamps.
155
+
156
+ ## GUI and Python API
157
+
158
+ The GUI workflow is: add files → map clocks/channels → assign channel semantics →
159
+ enter offsets/delays/events → choose the timeline → preview → inspect values/QC →
160
+ export. Controls are converted to the same configuration models used by the CLI;
161
+ the GUI contains no independent scientific formulas.
162
+
163
+ ```python
164
+ from pathlib import Path
165
+
166
+ from operandomerge.config import load_config
167
+ from operandomerge.export import export_excel
168
+ from operandomerge.service import MergeService
169
+
170
+ datasets, policy = load_config(Path("examples/showcase/config.json"))
171
+ result = MergeService().run(datasets, policy)
172
+ export_excel(result, Path("operandomerge-showcase.xlsx"))
173
+ ```
174
+
175
+ ## Supported, experimental, planned
176
+
177
+ Supported in 0.1.1:
178
+
179
+ - multiple CSV/XLSX inputs and one selected worksheet per dataset;
180
+ - absolute/injection timestamps, elapsed seconds/minutes, and midnight-wrapping
181
+ instrument-local clocks;
182
+ - absolute, elapsed, manual-offset, and reference-event alignment;
183
+ - explicit manual/sampling/transport/dead-volume/analysis delays;
184
+ - union or named-reference timeline, type-aware merging, QC, provenance, figures,
185
+ CSV bundle, Excel, CLI, Tk GUI, and public Python API; and
186
+ - Python 3.10+ packaging with scientific/unit/integration/showcase regressions.
187
+
188
+ Experimental:
189
+
190
+ - automatic GUI time-column guesses, which must be reviewed;
191
+ - robust-MAD possible-outlier warnings; and
192
+ - the [interchange draft](docs/interchange-draft.md), which documents concepts
193
+ but is not a stable cross-project contract.
194
+
195
+ Planned, not implemented:
196
+
197
+ - cross-correlation alignment. It will not be added until candidate offsets have
198
+ quantitative confidence, multi-channel agreement, a visual approval step, and
199
+ complete provenance;
200
+ - vendor-native binary adapters;
201
+ - automatic dead-volume calculation from apparatus geometry; and
202
+ - configurable regular target grids.
203
+
204
+ ## Assumptions and limitations
205
+
206
+ - OperandoMerge cannot prove that two clock events represent the same physical
207
+ event. Users must justify every offset and delay.
208
+ - Signal units are preserved as source metadata/names but are not converted or
209
+ dimensionally validated in this release.
210
+ - Numeric channels are required for resampling. Categorical phase labels should be
211
+ encoded as documented numeric states or retained outside the merged value table.
212
+ - On a named reference timeline, off-grid event/discrete measurements remain
213
+ absent rather than being snapped or interpolated.
214
+ - Duplicate times are reported by QC; numeric merging deterministically uses the
215
+ final row at that time while raw files remain untouched.
216
+ - GUI tables preview up to 500 rows; exports always contain the complete result.
217
+ - Synthetic curves demonstrate synchronization behavior, not validated reactor
218
+ kinetics, mass balances, or diffraction physics.
219
+
220
+ Validation details are in [scientific validation](docs/scientific-validation.md),
221
+ and every JSON setting is described in the
222
+ [configuration reference](docs/configuration.md).
223
+
224
+ ## Related tools
225
+
226
+ These are independent repositories. Direct interoperability is planned, not
227
+ currently claimed:
228
+
229
+ - [Ordifile](https://github.com/hdkim99/ordifile) — chromatographic file conversion
230
+ - [ReactorCheck](https://github.com/hdkim99/ReactorCheck) — catalytic reactor calculation and QC
231
+ - [TPxLab](https://github.com/hdkim99/TPxLab) — temperature-programmed characterization analysis
232
+
233
+ ## Development and citation
234
+
235
+ ```bash
236
+ ruff check .
237
+ mypy src
238
+ pytest
239
+ python -m build
240
+ ```
241
+
242
+ Contributions are welcome under [CONTRIBUTING.md](CONTRIBUTING.md). Cite the
243
+ software using [CITATION.cff](CITATION.cff). The project is MIT-licensed; the
244
+ [dependency license review](docs/dependency-licenses.md) records compatibility.