ftproductlab 0.1.0__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.
- ftproductlab-0.1.0/.github/ISSUE_TEMPLATE/bug.yml +20 -0
- ftproductlab-0.1.0/.github/ISSUE_TEMPLATE/config.yml +5 -0
- ftproductlab-0.1.0/.github/ISSUE_TEMPLATE/dataset.yml +24 -0
- ftproductlab-0.1.0/.github/ISSUE_TEMPLATE/feature.yml +17 -0
- ftproductlab-0.1.0/.github/ISSUE_TEMPLATE/macos-gui.yml +36 -0
- ftproductlab-0.1.0/.github/ISSUE_TEMPLATE/scientific-result.yml +20 -0
- ftproductlab-0.1.0/.github/actionlint.yaml +3 -0
- ftproductlab-0.1.0/.github/dependabot.yml +10 -0
- ftproductlab-0.1.0/.github/workflows/ci.yml +98 -0
- ftproductlab-0.1.0/.github/workflows/macos.yml +74 -0
- ftproductlab-0.1.0/.github/workflows/release.yml +53 -0
- ftproductlab-0.1.0/.gitignore +11 -0
- ftproductlab-0.1.0/CHANGELOG.md +9 -0
- ftproductlab-0.1.0/CITATION.cff +26 -0
- ftproductlab-0.1.0/CONTRIBUTING.md +26 -0
- ftproductlab-0.1.0/LICENSE +28 -0
- ftproductlab-0.1.0/PKG-INFO +198 -0
- ftproductlab-0.1.0/README.md +158 -0
- ftproductlab-0.1.0/assets/social-preview.png +0 -0
- ftproductlab-0.1.0/docs/macos.md +35 -0
- ftproductlab-0.1.0/docs/naming-audit.md +52 -0
- ftproductlab-0.1.0/docs/scientific-basis.md +71 -0
- ftproductlab-0.1.0/docs/validation.md +30 -0
- ftproductlab-0.1.0/examples/sample_distribution.csv +32 -0
- ftproductlab-0.1.0/pyproject.toml +100 -0
- ftproductlab-0.1.0/scripts/run_example.py +26 -0
- ftproductlab-0.1.0/src/ftproductlab/__init__.py +32 -0
- ftproductlab-0.1.0/src/ftproductlab/__main__.py +5 -0
- ftproductlab-0.1.0/src/ftproductlab/application/__init__.py +17 -0
- ftproductlab-0.1.0/src/ftproductlab/application/service.py +85 -0
- ftproductlab-0.1.0/src/ftproductlab/cli/__init__.py +5 -0
- ftproductlab-0.1.0/src/ftproductlab/cli/main.py +96 -0
- ftproductlab-0.1.0/src/ftproductlab/core/__init__.py +41 -0
- ftproductlab-0.1.0/src/ftproductlab/core/asf.py +325 -0
- ftproductlab-0.1.0/src/ftproductlab/core/models.py +180 -0
- ftproductlab-0.1.0/src/ftproductlab/gui/__init__.py +3 -0
- ftproductlab-0.1.0/src/ftproductlab/gui/__main__.py +26 -0
- ftproductlab-0.1.0/src/ftproductlab/gui/app.py +190 -0
- ftproductlab-0.1.0/src/ftproductlab/io/__init__.py +6 -0
- ftproductlab-0.1.0/src/ftproductlab/io/csv.py +71 -0
- ftproductlab-0.1.0/src/ftproductlab/io/export.py +81 -0
- ftproductlab-0.1.0/src/ftproductlab/plotting/__init__.py +5 -0
- ftproductlab-0.1.0/src/ftproductlab/plotting/figures.py +165 -0
- ftproductlab-0.1.0/tests/gui/test_workflow.py +31 -0
- ftproductlab-0.1.0/tests/integration/test_cli.py +54 -0
- ftproductlab-0.1.0/tests/integration/test_import_isolation.py +15 -0
- ftproductlab-0.1.0/tests/integration/test_plotting.py +20 -0
- ftproductlab-0.1.0/tests/macos/test_macos_lifecycle.py +18 -0
- ftproductlab-0.1.0/tests/unit/test_aggregation.py +54 -0
- ftproductlab-0.1.0/tests/unit/test_asf.py +73 -0
- ftproductlab-0.1.0/tests/unit/test_csv_io.py +28 -0
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
name: Bug report
|
|
2
|
+
description: Report reproducible software behavior that appears incorrect.
|
|
3
|
+
title: "[Bug]: "
|
|
4
|
+
labels: [bug]
|
|
5
|
+
body:
|
|
6
|
+
- type: markdown
|
|
7
|
+
attributes:
|
|
8
|
+
value: Do not upload unpublished, confidential, proprietary, or identifying research data.
|
|
9
|
+
- type: textarea
|
|
10
|
+
id: description
|
|
11
|
+
attributes: {label: What happened?, description: Include the command and expected behavior.}
|
|
12
|
+
validations: {required: true}
|
|
13
|
+
- type: input
|
|
14
|
+
id: version
|
|
15
|
+
attributes: {label: FTProductLab version}
|
|
16
|
+
validations: {required: true}
|
|
17
|
+
- type: textarea
|
|
18
|
+
id: reproducer
|
|
19
|
+
attributes: {label: Minimal public or synthetic reproducer}
|
|
20
|
+
validations: {required: true}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
name: Real dataset compatibility
|
|
2
|
+
description: Propose a public dataset or report a documented import limitation.
|
|
3
|
+
title: "[Dataset]: "
|
|
4
|
+
labels: [dataset]
|
|
5
|
+
body:
|
|
6
|
+
- type: markdown
|
|
7
|
+
attributes:
|
|
8
|
+
value: Only public, redistributable data may be linked. Do not attach confidential research data.
|
|
9
|
+
- type: input
|
|
10
|
+
id: doi
|
|
11
|
+
attributes: {label: DOI or stable repository URL}
|
|
12
|
+
validations: {required: true}
|
|
13
|
+
- type: input
|
|
14
|
+
id: license
|
|
15
|
+
attributes: {label: Dataset license}
|
|
16
|
+
validations: {required: true}
|
|
17
|
+
- type: textarea
|
|
18
|
+
id: schema
|
|
19
|
+
attributes: {label: Files, checksum, and column definitions}
|
|
20
|
+
validations: {required: true}
|
|
21
|
+
- type: textarea
|
|
22
|
+
id: scope
|
|
23
|
+
attributes: {label: Proposed validation scope}
|
|
24
|
+
validations: {required: true}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
name: Feature request
|
|
2
|
+
description: Propose one evidence-backed research workflow.
|
|
3
|
+
title: "[Feature]: "
|
|
4
|
+
labels: [enhancement]
|
|
5
|
+
body:
|
|
6
|
+
- type: textarea
|
|
7
|
+
id: workflow
|
|
8
|
+
attributes: {label: Researcher workflow and decision supported}
|
|
9
|
+
validations: {required: true}
|
|
10
|
+
- type: textarea
|
|
11
|
+
id: science
|
|
12
|
+
attributes: {label: Scientific definition, assumptions, and references}
|
|
13
|
+
validations: {required: true}
|
|
14
|
+
- type: textarea
|
|
15
|
+
id: exclusions
|
|
16
|
+
attributes: {label: Explicit non-goals and validity limits}
|
|
17
|
+
validations: {required: true}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
name: macOS/GUI issue
|
|
2
|
+
description: Report installation, Tk/backend, window lifecycle, or export failures.
|
|
3
|
+
title: "[macOS GUI]: "
|
|
4
|
+
labels: [macos, gui]
|
|
5
|
+
body:
|
|
6
|
+
- type: markdown
|
|
7
|
+
attributes:
|
|
8
|
+
value: Remove secrets and confidential paths/data before posting the full traceback.
|
|
9
|
+
- type: input
|
|
10
|
+
id: macos
|
|
11
|
+
attributes: {label: macOS version}
|
|
12
|
+
validations: {required: true}
|
|
13
|
+
- type: dropdown
|
|
14
|
+
id: architecture
|
|
15
|
+
attributes: {label: Architecture, options: [Apple Silicon, Intel]}
|
|
16
|
+
validations: {required: true}
|
|
17
|
+
- type: input
|
|
18
|
+
id: python
|
|
19
|
+
attributes: {label: Python version}
|
|
20
|
+
validations: {required: true}
|
|
21
|
+
- type: input
|
|
22
|
+
id: package
|
|
23
|
+
attributes: {label: FTProductLab version}
|
|
24
|
+
validations: {required: true}
|
|
25
|
+
- type: input
|
|
26
|
+
id: framework
|
|
27
|
+
attributes: {label: Tk/Tcl version, description: Run `python -c "import tkinter; print(tkinter.TkVersion)"`.}
|
|
28
|
+
validations: {required: true}
|
|
29
|
+
- type: input
|
|
30
|
+
id: install
|
|
31
|
+
attributes: {label: Installation command or method}
|
|
32
|
+
validations: {required: true}
|
|
33
|
+
- type: textarea
|
|
34
|
+
id: traceback
|
|
35
|
+
attributes: {label: Full sanitized traceback}
|
|
36
|
+
validations: {required: true}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
name: Scientific result question
|
|
2
|
+
description: Ask about an equation, basis, fit convention, or numerical result.
|
|
3
|
+
title: "[Scientific result]: "
|
|
4
|
+
labels: [scientific-result]
|
|
5
|
+
body:
|
|
6
|
+
- type: markdown
|
|
7
|
+
attributes:
|
|
8
|
+
value: Do not post confidential or unpublished datasets. Reduce the case to synthetic or public data.
|
|
9
|
+
- type: textarea
|
|
10
|
+
id: definition
|
|
11
|
+
attributes: {label: Equation or definition, description: State input basis, fit range, and weighting.}
|
|
12
|
+
validations: {required: true}
|
|
13
|
+
- type: textarea
|
|
14
|
+
id: expected
|
|
15
|
+
attributes: {label: Independent calculation or reference, description: Provide DOI and exact convention where possible.}
|
|
16
|
+
validations: {required: true}
|
|
17
|
+
- type: input
|
|
18
|
+
id: version
|
|
19
|
+
attributes: {label: FTProductLab version}
|
|
20
|
+
validations: {required: true}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
name: DGX CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
workflow_dispatch:
|
|
9
|
+
|
|
10
|
+
permissions:
|
|
11
|
+
contents: read
|
|
12
|
+
|
|
13
|
+
concurrency:
|
|
14
|
+
group: ftproductlab-dgx-${{ github.event.pull_request.number || github.ref }}
|
|
15
|
+
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
|
|
16
|
+
|
|
17
|
+
jobs:
|
|
18
|
+
required:
|
|
19
|
+
name: DGX / required
|
|
20
|
+
# Persistent research hardware never executes an external fork's code.
|
|
21
|
+
if: >-
|
|
22
|
+
github.event_name != 'pull_request' ||
|
|
23
|
+
(github.actor == github.repository_owner &&
|
|
24
|
+
github.event.pull_request.head.repo.full_name == github.repository)
|
|
25
|
+
runs-on: [self-hosted, Linux, ARM64, dgx-spark]
|
|
26
|
+
timeout-minutes: 45
|
|
27
|
+
env:
|
|
28
|
+
PIP_NO_CACHE_DIR: "1"
|
|
29
|
+
MPLBACKEND: Agg
|
|
30
|
+
steps:
|
|
31
|
+
- name: Check out source
|
|
32
|
+
uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5
|
|
33
|
+
with:
|
|
34
|
+
clean: true
|
|
35
|
+
persist-credentials: false
|
|
36
|
+
- name: Set up Python
|
|
37
|
+
uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6
|
|
38
|
+
with:
|
|
39
|
+
python-version: "3.14"
|
|
40
|
+
- name: Configure established Tk and Xvfb runtime
|
|
41
|
+
env:
|
|
42
|
+
FTPRODUCTLAB_GUI_RUNTIME: /opt/catalysttwin-actions/shared-gui-runtime
|
|
43
|
+
run: |
|
|
44
|
+
set -eu
|
|
45
|
+
runtime="$FTPRODUCTLAB_GUI_RUNTIME"
|
|
46
|
+
bin_dir="$runtime/usr/bin"
|
|
47
|
+
lib_dir="$runtime/usr/lib/aarch64-linux-gnu"
|
|
48
|
+
tk_dir="$runtime/usr/share/tcltk/tk8.6"
|
|
49
|
+
test -d "$bin_dir"
|
|
50
|
+
test -d "$lib_dir"
|
|
51
|
+
test -d "$tk_dir"
|
|
52
|
+
printf '%s\n' "$bin_dir" >> "$GITHUB_PATH"
|
|
53
|
+
{
|
|
54
|
+
printf 'LD_LIBRARY_PATH=%s\n' "$lib_dir${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}"
|
|
55
|
+
printf 'TK_LIBRARY=%s\n' "$tk_dir"
|
|
56
|
+
} >> "$GITHUB_ENV"
|
|
57
|
+
export PATH="$bin_dir:$PATH"
|
|
58
|
+
export LD_LIBRARY_PATH="$lib_dir${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}"
|
|
59
|
+
export TK_LIBRARY="$tk_dir"
|
|
60
|
+
command -v Xvfb
|
|
61
|
+
command -v xvfb-run
|
|
62
|
+
python -c 'import tkinter; print(tkinter.TkVersion)'
|
|
63
|
+
- name: Create isolated environment
|
|
64
|
+
run: python -m venv .ci-venv
|
|
65
|
+
- name: Install project
|
|
66
|
+
run: |
|
|
67
|
+
.ci-venv/bin/python -m pip install --upgrade pip
|
|
68
|
+
.ci-venv/bin/python -m pip install -e ".[dev]"
|
|
69
|
+
- name: Format check
|
|
70
|
+
run: .ci-venv/bin/python -m ruff format --check .
|
|
71
|
+
- name: Lint
|
|
72
|
+
run: .ci-venv/bin/python -m ruff check .
|
|
73
|
+
- name: Strict type check
|
|
74
|
+
run: .ci-venv/bin/python -m mypy
|
|
75
|
+
- name: Scientific and integration tests
|
|
76
|
+
run: .ci-venv/bin/python -m pytest tests/unit tests/integration
|
|
77
|
+
- name: GUI workflow under Xvfb
|
|
78
|
+
run: xvfb-run -a .ci-venv/bin/python -m pytest tests/gui
|
|
79
|
+
- name: CLI and example smoke
|
|
80
|
+
run: |
|
|
81
|
+
.ci-venv/bin/ftproductlab validate
|
|
82
|
+
.ci-venv/bin/python scripts/run_example.py --output "$RUNNER_TEMP/ftproductlab-example"
|
|
83
|
+
- name: Build and metadata check
|
|
84
|
+
run: |
|
|
85
|
+
.ci-venv/bin/python -m build
|
|
86
|
+
.ci-venv/bin/python -m twine check dist/*
|
|
87
|
+
- name: Clean-wheel install and GUI-free CLI
|
|
88
|
+
run: |
|
|
89
|
+
python -m venv "$RUNNER_TEMP/ftproductlab-wheel"
|
|
90
|
+
"$RUNNER_TEMP/ftproductlab-wheel/bin/python" -m pip install --no-cache-dir dist/*.whl
|
|
91
|
+
"$RUNNER_TEMP/ftproductlab-wheel/bin/python" -m pip check
|
|
92
|
+
"$RUNNER_TEMP/ftproductlab-wheel/bin/ftproductlab" validate
|
|
93
|
+
"$RUNNER_TEMP/ftproductlab-wheel/bin/python" -c "import ftproductlab,sys; assert 'tkinter' not in sys.modules; assert 'matplotlib' not in sys.modules"
|
|
94
|
+
- name: Dependency audit
|
|
95
|
+
run: .ci-venv/bin/python -m pip_audit
|
|
96
|
+
- name: Remove isolated environment
|
|
97
|
+
if: always()
|
|
98
|
+
run: rm -rf .ci-venv
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
name: macOS GUI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
workflow_dispatch:
|
|
9
|
+
|
|
10
|
+
permissions:
|
|
11
|
+
contents: read
|
|
12
|
+
|
|
13
|
+
concurrency:
|
|
14
|
+
group: ftproductlab-macos-${{ github.event.pull_request.number || github.ref }}
|
|
15
|
+
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
|
|
16
|
+
|
|
17
|
+
jobs:
|
|
18
|
+
gui:
|
|
19
|
+
name: macOS 15 / ${{ matrix.architecture }} / Python ${{ matrix.python }}
|
|
20
|
+
# The hosted runner has no repository secrets; still reject external fork execution.
|
|
21
|
+
if: >-
|
|
22
|
+
github.event_name != 'pull_request' ||
|
|
23
|
+
(github.actor == github.repository_owner &&
|
|
24
|
+
github.event.pull_request.head.repo.full_name == github.repository)
|
|
25
|
+
strategy:
|
|
26
|
+
fail-fast: false
|
|
27
|
+
matrix:
|
|
28
|
+
include:
|
|
29
|
+
- runner: macos-15
|
|
30
|
+
architecture: arm64
|
|
31
|
+
python: "3.10"
|
|
32
|
+
- runner: macos-15
|
|
33
|
+
architecture: arm64
|
|
34
|
+
python: "3.14"
|
|
35
|
+
- runner: macos-15-intel
|
|
36
|
+
architecture: x86_64
|
|
37
|
+
# setup-python 3.10 on this image links Tk 8.5 headers to the
|
|
38
|
+
# image's Tk 8.6 runtime. Python 3.13 is the verified Intel pair.
|
|
39
|
+
python: "3.13"
|
|
40
|
+
runs-on: ${{ matrix.runner }}
|
|
41
|
+
timeout-minutes: 30
|
|
42
|
+
env:
|
|
43
|
+
MPLBACKEND: Agg
|
|
44
|
+
steps:
|
|
45
|
+
- name: Check out source
|
|
46
|
+
uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5
|
|
47
|
+
with:
|
|
48
|
+
persist-credentials: false
|
|
49
|
+
- name: Set up Python
|
|
50
|
+
uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6
|
|
51
|
+
with:
|
|
52
|
+
python-version: ${{ matrix.python }}
|
|
53
|
+
- name: Record platform and Tk
|
|
54
|
+
run: |
|
|
55
|
+
python -c "import platform; print(platform.platform(), platform.machine())"
|
|
56
|
+
python -c "import tkinter; print('Tk', tkinter.TkVersion)"
|
|
57
|
+
- name: Install source with GUI extras
|
|
58
|
+
run: python -m pip install -e ".[gui,dev]"
|
|
59
|
+
- name: Core, CLI, and import-isolation tests
|
|
60
|
+
run: |
|
|
61
|
+
python -m pytest tests/unit tests/integration/test_cli.py tests/integration/test_import_isolation.py
|
|
62
|
+
ftproductlab validate
|
|
63
|
+
- name: Real window workflow, export, close, and exit
|
|
64
|
+
run: python -m pytest tests/gui tests/macos
|
|
65
|
+
- name: Build and clean wheel
|
|
66
|
+
run: |
|
|
67
|
+
python -m build
|
|
68
|
+
python -m venv "$RUNNER_TEMP/wheel-env"
|
|
69
|
+
"$RUNNER_TEMP/wheel-env/bin/python" -m pip install "ftproductlab[gui] @ file://$PWD/$(ls dist/*.whl)"
|
|
70
|
+
"$RUNNER_TEMP/wheel-env/bin/python" -m pip check
|
|
71
|
+
"$RUNNER_TEMP/wheel-env/bin/ftproductlab" validate
|
|
72
|
+
- name: Clean-wheel GUI lifecycle
|
|
73
|
+
run: |
|
|
74
|
+
"$RUNNER_TEMP/wheel-env/bin/python" -c "import tkinter as tk; from ftproductlab.gui.app import MainWindow; root=tk.Tk(); window=MainWindow(root); root.update(); assert window.winfo_exists(); root.destroy()"
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [published]
|
|
6
|
+
|
|
7
|
+
permissions:
|
|
8
|
+
contents: read
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
build:
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
steps:
|
|
14
|
+
- name: Check out source
|
|
15
|
+
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
|
16
|
+
with:
|
|
17
|
+
persist-credentials: false
|
|
18
|
+
- name: Set up Python
|
|
19
|
+
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7
|
|
20
|
+
with:
|
|
21
|
+
python-version: "3.14"
|
|
22
|
+
- name: Build distributions
|
|
23
|
+
env:
|
|
24
|
+
RELEASE_TAG: ${{ github.event.release.tag_name }}
|
|
25
|
+
run: |
|
|
26
|
+
python -m pip install build==1.5.0 twine==6.2.0
|
|
27
|
+
version="$(python -c 'import tomllib; print(tomllib.load(open("pyproject.toml", "rb"))["project"]["version"])')"
|
|
28
|
+
test "$RELEASE_TAG" = "v$version"
|
|
29
|
+
python -m build
|
|
30
|
+
python -m twine check dist/*
|
|
31
|
+
- name: Upload distributions
|
|
32
|
+
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
|
33
|
+
with:
|
|
34
|
+
name: pypi-distributions
|
|
35
|
+
path: dist/
|
|
36
|
+
if-no-files-found: error
|
|
37
|
+
|
|
38
|
+
publish:
|
|
39
|
+
needs: build
|
|
40
|
+
runs-on: ubuntu-latest
|
|
41
|
+
environment:
|
|
42
|
+
name: pypi
|
|
43
|
+
url: https://pypi.org/p/ftproductlab
|
|
44
|
+
permissions:
|
|
45
|
+
id-token: write
|
|
46
|
+
steps:
|
|
47
|
+
- name: Download distributions
|
|
48
|
+
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
|
|
49
|
+
with:
|
|
50
|
+
name: pypi-distributions
|
|
51
|
+
path: dist/
|
|
52
|
+
- name: Publish with Trusted Publishing
|
|
53
|
+
uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # release/v1
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.1.0 - Unreleased
|
|
4
|
+
|
|
5
|
+
- Initial species-preserving carbon-number aggregation.
|
|
6
|
+
- Explicit molar, mass, and carbon input bases.
|
|
7
|
+
- User-ranged single-alpha ASF fitting with uniform or amount weighting.
|
|
8
|
+
- C1/C2 and per-carbon residual diagnostics, user-defined cuts, and O/P states.
|
|
9
|
+
- Shared Python API, CLI, Tkinter GUI, deterministic tables, and PNG/SVG/PDF figures.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
cff-version: 1.2.0
|
|
2
|
+
message: "If you use FTProductLab, please cite the software and the scientific sources for the ASF convention."
|
|
3
|
+
title: "FTProductLab"
|
|
4
|
+
type: software
|
|
5
|
+
version: 0.1.0
|
|
6
|
+
date-released: 2026-08-23
|
|
7
|
+
authors:
|
|
8
|
+
- family-names: "Kim"
|
|
9
|
+
given-names: "Peter"
|
|
10
|
+
repository-code: "https://github.com/hdkim99/FTProductLab"
|
|
11
|
+
url: "https://github.com/hdkim99/FTProductLab"
|
|
12
|
+
license: BSD-3-Clause
|
|
13
|
+
keywords:
|
|
14
|
+
- Fischer-Tropsch
|
|
15
|
+
- Anderson-Schulz-Flory
|
|
16
|
+
- catalysis
|
|
17
|
+
- product distribution
|
|
18
|
+
references:
|
|
19
|
+
- type: article
|
|
20
|
+
title: "Molecular Size Distribution in Linear Condensation Polymers"
|
|
21
|
+
authors:
|
|
22
|
+
- family-names: "Flory"
|
|
23
|
+
given-names: "Paul J."
|
|
24
|
+
journal: "Journal of the American Chemical Society"
|
|
25
|
+
year: 1936
|
|
26
|
+
doi: "10.1021/ja01301a016"
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
Use a focused issue or pull request and do not upload unpublished, confidential, export-
|
|
4
|
+
controlled, or personally identifiable research data to a public issue.
|
|
5
|
+
|
|
6
|
+
Scientific-core changes must state the equation or definition, units/basis, assumptions,
|
|
7
|
+
validity range, authoritative reference, independent numerical validation, and regression
|
|
8
|
+
test. A higher R² alone does not justify a new model. Do not add coefficients or thresholds
|
|
9
|
+
without a traceable source or derivation.
|
|
10
|
+
|
|
11
|
+
GUI changes must additionally record supported platforms, dependency changes, backend
|
|
12
|
+
impact, macOS smoke results, and CLI/core import isolation. PyQt and PySide must never be
|
|
13
|
+
mixed; introducing Qt requires a separate architecture decision and the compatibility tests
|
|
14
|
+
listed in the issue template. Tk code must create one root and must not update widgets from a
|
|
15
|
+
background thread.
|
|
16
|
+
|
|
17
|
+
Run before submitting:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
python -m ruff format --check .
|
|
21
|
+
python -m ruff check .
|
|
22
|
+
python -m mypy
|
|
23
|
+
python -m pytest
|
|
24
|
+
python -m build
|
|
25
|
+
python -m twine check dist/*
|
|
26
|
+
```
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
BSD 3-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026, Peter Kim
|
|
4
|
+
|
|
5
|
+
Redistribution and use in source and binary forms, with or without
|
|
6
|
+
modification, are permitted provided that the following conditions are met:
|
|
7
|
+
|
|
8
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
9
|
+
list of conditions and the following disclaimer.
|
|
10
|
+
|
|
11
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
12
|
+
this list of conditions and the following disclaimer in the documentation
|
|
13
|
+
and/or other materials provided with the distribution.
|
|
14
|
+
|
|
15
|
+
3. Neither the name of the copyright holder nor the names of its
|
|
16
|
+
contributors may be used to endorse or promote products derived from
|
|
17
|
+
this software without specific prior written permission.
|
|
18
|
+
|
|
19
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
20
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
21
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
22
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
23
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
24
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
25
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
26
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
27
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
28
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ftproductlab
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Reproducible Fischer-Tropsch product-distribution and ASF analysis
|
|
5
|
+
Project-URL: Homepage, https://github.com/hdkim99/FTProductLab
|
|
6
|
+
Project-URL: Documentation, https://github.com/hdkim99/FTProductLab#readme
|
|
7
|
+
Project-URL: Issues, https://github.com/hdkim99/FTProductLab/issues
|
|
8
|
+
Project-URL: Repository, https://github.com/hdkim99/FTProductLab
|
|
9
|
+
Author: Peter Kim
|
|
10
|
+
License-Expression: BSD-3-Clause
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: anderson-schulz-flory,catalysis,fischer-tropsch,product-distribution
|
|
13
|
+
Classifier: Development Status :: 3 - Alpha
|
|
14
|
+
Classifier: Environment :: Console
|
|
15
|
+
Classifier: Intended Audience :: Science/Research
|
|
16
|
+
Classifier: License :: OSI Approved :: BSD License
|
|
17
|
+
Classifier: Operating System :: MacOS
|
|
18
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
19
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
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
|
+
Requires-Python: <3.15,>=3.10
|
|
27
|
+
Provides-Extra: dev
|
|
28
|
+
Requires-Dist: build==1.5.0; extra == 'dev'
|
|
29
|
+
Requires-Dist: matplotlib<4,>=3.8; extra == 'dev'
|
|
30
|
+
Requires-Dist: mypy<2,>=1.15; extra == 'dev'
|
|
31
|
+
Requires-Dist: pip-audit<3,>=2.9; extra == 'dev'
|
|
32
|
+
Requires-Dist: pytest<10,>=8.3; extra == 'dev'
|
|
33
|
+
Requires-Dist: ruff<0.18,>=0.11; extra == 'dev'
|
|
34
|
+
Requires-Dist: twine<7,>=6.1; extra == 'dev'
|
|
35
|
+
Provides-Extra: gui
|
|
36
|
+
Requires-Dist: matplotlib<4,>=3.8; extra == 'gui'
|
|
37
|
+
Provides-Extra: plot
|
|
38
|
+
Requires-Dist: matplotlib<4,>=3.8; extra == 'plot'
|
|
39
|
+
Description-Content-Type: text/markdown
|
|
40
|
+
|
|
41
|
+
# FTProductLab
|
|
42
|
+
|
|
43
|
+
[](https://github.com/hdkim99/FTProductLab/actions/workflows/ci.yml)
|
|
44
|
+
[](https://github.com/hdkim99/FTProductLab/actions/workflows/macos.yml)
|
|
45
|
+
[](https://www.python.org/)
|
|
46
|
+
|
|
47
|
+
Reproducible Fischer–Tropsch carbon-number distribution, single-alpha ASF, product-cut,
|
|
48
|
+
deviation, and olefin/paraffin analysis.
|
|
49
|
+
|
|
50
|
+

|
|
51
|
+
|
|
52
|
+
## Why
|
|
53
|
+
|
|
54
|
+
FTProductLab keeps carbon-number definitions, input basis, fit range, regression weighting,
|
|
55
|
+
and truncation conventions visible. It does not turn a high R² into a claim that an ideal ASF
|
|
56
|
+
mechanism is valid. Species remain category-resolved while totals are aggregated by carbon
|
|
57
|
+
number for analysis.
|
|
58
|
+
|
|
59
|
+
## Install
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
python -m pip install ftproductlab
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
The base package contains the scientific core and CLI and has no GUI, Qt, or plotting
|
|
66
|
+
dependency. For figures and the Tk interface:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
python -m pip install "ftproductlab[gui]"
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Tk itself is supplied by Python, not PyPI. Homebrew Python users may need the matching
|
|
73
|
+
formula, for example `brew install python-tk@3.14`.
|
|
74
|
+
|
|
75
|
+
## 30-second example
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
ftproductlab analyze examples/sample_distribution.csv \
|
|
79
|
+
--basis molar --fit-min 5 --fit-max 20 \
|
|
80
|
+
--cut C1:1:1 --cut C2-C4:2:4 --cut C5+:5 \
|
|
81
|
+
--output ftproductlab-output --plots
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
The command writes a machine-readable `analysis.json`, point/cut/O-P CSV tables, and
|
|
85
|
+
PNG/SVG/PDF figures. Run the built-in hand-checkable regression with:
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
ftproductlab validate
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Python uses the same core:
|
|
92
|
+
|
|
93
|
+
```python
|
|
94
|
+
from ftproductlab import FitConfig, InputBasis, ProductRecord, analyze_distribution
|
|
95
|
+
|
|
96
|
+
records = [ProductRecord(n, 0.2 * 0.8 ** (n - 1)) for n in range(1, 21)]
|
|
97
|
+
result = analyze_distribution(records, InputBasis.MOLAR, FitConfig(3, 20))
|
|
98
|
+
assert abs(result.fit.alpha - 0.8) < 1e-12
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## GUI
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
python -m ftproductlab.gui
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
The GUI is deliberately Tkinter/ttk rather than Qt. It creates one `Tk()` root, calls the
|
|
108
|
+
same application service as the CLI, exports the same tables, and closes the event loop
|
|
109
|
+
normally. Plot exports explicitly select Matplotlib `Agg`; the core and CLI do not import
|
|
110
|
+
Tkinter, Matplotlib, PyQt, or PySide.
|
|
111
|
+
|
|
112
|
+
## CSV schema and basis
|
|
113
|
+
|
|
114
|
+
Required columns are `carbon_number` and `amount`. Optional columns are `category`,
|
|
115
|
+
`species`, `molecular_weight_g_mol`, and `below_detection`.
|
|
116
|
+
|
|
117
|
+
- `molar`: amount is proportional to moles of product molecules.
|
|
118
|
+
- `carbon`: amount is proportional to carbon moles; it is divided by carbon number before
|
|
119
|
+
fitting.
|
|
120
|
+
- `mass`: each record must provide molecular weight. FTProductLab never approximates all
|
|
121
|
+
paraffins, olefins, and oxygenates as `CH2`.
|
|
122
|
+
|
|
123
|
+
Amounts may use any consistent scale. Zero measurements remain in output but cannot enter a
|
|
124
|
+
logarithmic fit. Below-detection records remain visible and are excluded from fitting by
|
|
125
|
+
default. Repeat `--cut LABEL:MIN[:MAX]` to define CLI cuts; the GUI exposes the same cut
|
|
126
|
+
specification, and the Python API accepts `CutDefinition` objects directly.
|
|
127
|
+
|
|
128
|
+
## Scientific basis
|
|
129
|
+
|
|
130
|
+
For molecular amount at carbon number `n`, the ideal single-alpha model is
|
|
131
|
+
|
|
132
|
+
```text
|
|
133
|
+
x_n = (1 - alpha) alpha^(n - 1)
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
and the corresponding carbon fraction is
|
|
137
|
+
|
|
138
|
+
```text
|
|
139
|
+
w_n = n (1 - alpha)^2 alpha^(n - 1).
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
FTProductLab estimates the slope of `ln(molar-equivalent amount)` against `n` over the
|
|
143
|
+
user-specified range; `alpha = exp(slope)`. The intercept is free because experimental
|
|
144
|
+
datasets may be scaled or truncated. Uniform and amount-weighted regressions are explicit
|
|
145
|
+
choices. No fit range is selected automatically, and C1/C2 are not silently removed.
|
|
146
|
+
|
|
147
|
+
The primary/statistical foundation is Flory's chain-length distribution
|
|
148
|
+
([JACS 1936, DOI 10.1021/ja01301a016](https://doi.org/10.1021/ja01301a016)); its early FT
|
|
149
|
+
application includes Friedel and Anderson
|
|
150
|
+
([JACS 1950, DOI 10.1021/ja01159a039](https://doi.org/10.1021/ja01159a039)). A modern
|
|
151
|
+
discussion of ideal ASF and structured deviations is
|
|
152
|
+
[Vervloet et al., Catalysis Science & Technology 2013](https://doi.org/10.1039/C3CY00080J).
|
|
153
|
+
See [the scientific-basis document](docs/scientific-basis.md) for conventions and equations.
|
|
154
|
+
|
|
155
|
+
## Validation
|
|
156
|
+
|
|
157
|
+
- Exact synthetic geometric distributions recover alpha to floating-point precision.
|
|
158
|
+
- Infinite molecular/carbon normalization and analytical carbon-cut sums have independent
|
|
159
|
+
direct-summation tests.
|
|
160
|
+
- Species aggregation, mass-to-mole conversion, C1/C2 deviations, product-cut closure,
|
|
161
|
+
O/P zero denominators, detection flags, CSV, CLI, GUI export, plotting, and import
|
|
162
|
+
isolation are tested.
|
|
163
|
+
- The open-access Partington et al. Co/TiO2 study reports alpha=0.92 for C10–C40 and provides
|
|
164
|
+
a useful external target, but repository-level numerical reproduction remains pending
|
|
165
|
+
because a redistributable tabular C10–C40 fixture has not yet been extracted. Synthetic
|
|
166
|
+
fixtures are never labeled real data.
|
|
167
|
+
|
|
168
|
+
Details: [validation record](docs/validation.md).
|
|
169
|
+
|
|
170
|
+
## Supported platforms
|
|
171
|
+
|
|
172
|
+
- Python 3.10–3.14 for the core and CLI
|
|
173
|
+
- Linux scientific core/CLI and Xvfb GUI checks on the project DGX runner
|
|
174
|
+
- macOS GUI workflow on GitHub-hosted macOS plus local Apple Silicon smoke testing
|
|
175
|
+
- Tk 8.6 or newer; no Qt binding is used or installed
|
|
176
|
+
|
|
177
|
+
The hosted GUI matrix covers Apple Silicon on Python 3.10 and 3.14 and Intel on Python
|
|
178
|
+
3.13. The `setup-python` 3.10 build on the `macos-15-intel` image is not a supported GUI
|
|
179
|
+
combination because its Tk 8.5 build metadata conflicts with the image's Tk 8.6 runtime.
|
|
180
|
+
See [macOS notes](docs/macos.md).
|
|
181
|
+
|
|
182
|
+
## Limitations
|
|
183
|
+
|
|
184
|
+
- Version 0.1 implements an ideal, single-alpha descriptive fit only. Dual-alpha and
|
|
185
|
+
modified ASF models are intentionally unsupported.
|
|
186
|
+
- Missing products, detector response, phase collection, recycle, and analytical recovery
|
|
187
|
+
can structure residuals; the software does not correct them without supplied data.
|
|
188
|
+
- Observed product-cut fractions close over the supplied dataset. They are not an estimate
|
|
189
|
+
of an unmeasured heavy tail.
|
|
190
|
+
- Category names such as “gasoline”, “diesel”, and “wax” are not assigned automatically;
|
|
191
|
+
carbon-number cut definitions are shown explicitly.
|
|
192
|
+
- This is product-distribution decision support, not a reactor or kinetic mechanism model.
|
|
193
|
+
|
|
194
|
+
## Development and citation
|
|
195
|
+
|
|
196
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) before changing an equation or GUI dependency and
|
|
197
|
+
use [CITATION.cff](CITATION.cff) when citing the software. The name and competitive audit is
|
|
198
|
+
recorded in [docs/naming-audit.md](docs/naming-audit.md).
|