pkft 0.1.0a1__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.

Potentially problematic release.


This version of pkft might be problematic. Click here for more details.

Files changed (51) hide show
  1. pkft-0.1.0a1/.github/workflows/docs.yml +41 -0
  2. pkft-0.1.0a1/.github/workflows/release.yml +109 -0
  3. pkft-0.1.0a1/.gitignore +9 -0
  4. pkft-0.1.0a1/.prettierrc +7 -0
  5. pkft-0.1.0a1/.readthedocs.yaml +17 -0
  6. pkft-0.1.0a1/.ruff.toml +5 -0
  7. pkft-0.1.0a1/.zenodo.json +8 -0
  8. pkft-0.1.0a1/LICENSE +21 -0
  9. pkft-0.1.0a1/MANIFEST.in +7 -0
  10. pkft-0.1.0a1/Makefile +97 -0
  11. pkft-0.1.0a1/PKG-INFO +132 -0
  12. pkft-0.1.0a1/README.md +104 -0
  13. pkft-0.1.0a1/assets/images/repo-banner.png +0 -0
  14. pkft-0.1.0a1/docs/Makefile +42 -0
  15. pkft-0.1.0a1/docs/_static/css/console.css +97 -0
  16. pkft-0.1.0a1/docs/_static/css/darkmode.css +33 -0
  17. pkft-0.1.0a1/docs/_static/js/darkmode.js +43 -0
  18. pkft-0.1.0a1/docs/api.rst +66 -0
  19. pkft-0.1.0a1/docs/conf.py +71 -0
  20. pkft-0.1.0a1/docs/index.rst +33 -0
  21. pkft-0.1.0a1/docs/installation.rst +8 -0
  22. pkft-0.1.0a1/docs/make.bat +35 -0
  23. pkft-0.1.0a1/docs/pkft-logo.svg +7 -0
  24. pkft-0.1.0a1/docs/requirements.txt +8 -0
  25. pkft-0.1.0a1/docs/usage.rst +73 -0
  26. pkft-0.1.0a1/environment.yml +10 -0
  27. pkft-0.1.0a1/pkft/__init__.py +3 -0
  28. pkft-0.1.0a1/pkft/api.py +114 -0
  29. pkft-0.1.0a1/pkft/curves.py +56 -0
  30. pkft-0.1.0a1/pkft/data/__init__.py +0 -0
  31. pkft-0.1.0a1/pkft/data/demo-trayler-2020.csv +49 -0
  32. pkft-0.1.0a1/pkft/mixture.py +194 -0
  33. pkft-0.1.0a1/pkft/models.py +62 -0
  34. pkft-0.1.0a1/pkft/static/app.css +338 -0
  35. pkft-0.1.0a1/pkft/static/app.js +492 -0
  36. pkft-0.1.0a1/pkft/static/index.html +94 -0
  37. pkft-0.1.0a1/pkft/static/pkft-logo.svg +7 -0
  38. pkft-0.1.0a1/pkft.egg-info/PKG-INFO +132 -0
  39. pkft-0.1.0a1/pkft.egg-info/SOURCES.txt +49 -0
  40. pkft-0.1.0a1/pkft.egg-info/dependency_links.txt +1 -0
  41. pkft-0.1.0a1/pkft.egg-info/entry_points.txt +2 -0
  42. pkft-0.1.0a1/pkft.egg-info/requires.txt +10 -0
  43. pkft-0.1.0a1/pkft.egg-info/scm_file_list.json +45 -0
  44. pkft-0.1.0a1/pkft.egg-info/scm_version.json +8 -0
  45. pkft-0.1.0a1/pkft.egg-info/top_level.txt +1 -0
  46. pkft-0.1.0a1/pyproject.toml +48 -0
  47. pkft-0.1.0a1/pyrightconfig.json +14 -0
  48. pkft-0.1.0a1/pytest.ini +11 -0
  49. pkft-0.1.0a1/setup.cfg +4 -0
  50. pkft-0.1.0a1/tests/test_api.py +90 -0
  51. pkft-0.1.0a1/tests/test_mixture.py +50 -0
@@ -0,0 +1,41 @@
1
+ ---
2
+ name: Build and Check Docs
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ tags: ["v*"]
7
+ pull_request:
8
+ jobs:
9
+ docs:
10
+ name: Test on ${{ matrix.os }} with Python ${{ matrix.python-version }}
11
+ runs-on: ${{ matrix.os }}
12
+ strategy:
13
+ fail-fast: false
14
+ matrix:
15
+ os: [ubuntu-24.04]
16
+ python-version: ["3.13"]
17
+ steps:
18
+ - name: Checkout repository
19
+ uses: actions/checkout@v6
20
+ with:
21
+ fetch-depth: 0
22
+ - name: Set up Python
23
+ uses: actions/setup-python@v6
24
+ with:
25
+ python-version: ${{ matrix.python-version }}
26
+ cache: "pip"
27
+ - name: Install documentation dependencies
28
+ run: |
29
+ python -m pip install --upgrade pip
30
+ pip install -e ".[dev]"
31
+ pip install -r docs/requirements.txt
32
+ - name: Build documentation
33
+ run: |
34
+ cd docs
35
+ make html
36
+ - name: Upload docs as artifact
37
+ uses: actions/upload-artifact@v6
38
+ with:
39
+ name: html-docs-${{ matrix.os }}-python-${{ matrix.python-version }}
40
+ path: docs/_build/html
41
+ if-no-files-found: error
@@ -0,0 +1,109 @@
1
+ ---
2
+ name: Test and Release
3
+ permissions:
4
+ contents: write
5
+ packages: write
6
+ on:
7
+ push:
8
+ branches: [main]
9
+ tags: ["v*"]
10
+ pull_request:
11
+ jobs:
12
+ test:
13
+ name: Test on ${{ matrix.os }} with Python ${{ matrix.python-version }}
14
+ runs-on: ${{ matrix.os }}
15
+ strategy:
16
+ fail-fast: false
17
+ matrix:
18
+ os: [ubuntu-24.04, macos-latest, windows-latest]
19
+ python-version: ["3.12", "3.13"]
20
+ steps:
21
+ - name: Checkout repository
22
+ uses: actions/checkout@v6
23
+ with:
24
+ fetch-depth: 0
25
+ - name: Set up Python
26
+ uses: actions/setup-python@v6
27
+ with:
28
+ python-version: ${{ matrix.python-version }}
29
+ cache: "pip"
30
+ - name: Install dependencies
31
+ run: |
32
+ python -m pip install --upgrade pip
33
+ pip install -e ".[dev]"
34
+ - name: Run tests and generate coverage report
35
+ run: |
36
+ pytest --cov=pkft --cov-branch --cov-report=xml
37
+ - name: Upload coverage reports to Codecov
38
+ uses: codecov/codecov-action@303a32d7a59b442fa8d48b6a1cc6825c09c847a5 # v7.1.1, node24 (v5 tag still nests github-script v7/node20)
39
+ with:
40
+ token: ${{ secrets.CODECOV_TOKEN }}
41
+ check-version:
42
+ name: Check if major or minor version
43
+ needs: test
44
+ if: startsWith(github.ref, 'refs/tags/v')
45
+ runs-on: ubuntu-24.04
46
+ outputs:
47
+ is_major_minor: ${{ steps.check_tag.outputs.is_major_minor }}
48
+ steps:
49
+ - name: Check tag format
50
+ id: check_tag
51
+ run: |
52
+ TAG=${GITHUB_REF#refs/tags/}
53
+ VERSION=${TAG#v}
54
+ VERSION_CORE=$(echo $VERSION | cut -d'-' -f1 | cut -d'+' -f1)
55
+ IFS='.' read -ra VERSION_PARTS <<< "$VERSION_CORE"
56
+ if [[ ${#VERSION_PARTS[@]} -eq 3 && ${VERSION_PARTS[2]} != "0" ]]; then
57
+ echo "Tag $TAG is a patch version"
58
+ echo "is_major_minor=false" >> $GITHUB_OUTPUT
59
+ elif [[ "$VERSION" == *"-"* ]]; then
60
+ echo "Tag $TAG is a prerelease version"
61
+ echo "is_major_minor=false" >> $GITHUB_OUTPUT
62
+ else
63
+ echo "Tag $TAG is a major or minor version"
64
+ echo "is_major_minor=true" >> $GITHUB_OUTPUT
65
+ fi
66
+ build:
67
+ name: Build and Upload to PyPI
68
+ needs: check-version
69
+ if: startsWith(github.ref, 'refs/tags/v')
70
+ runs-on: ubuntu-24.04
71
+ steps:
72
+ - name: Checkout repository
73
+ uses: actions/checkout@v6
74
+ with:
75
+ fetch-depth: 0
76
+ - name: Set up Python
77
+ uses: actions/setup-python@v6
78
+ with:
79
+ python-version: "3.13"
80
+ - name: Install build tools
81
+ run: |
82
+ python -m pip install --upgrade pip
83
+ pip install setuptools wheel twine build setuptools-scm
84
+ - name: Verify version
85
+ run: python -m setuptools_scm
86
+ - name: Build distribution
87
+ run: |
88
+ rm -rf dist build *.egg-info
89
+ python -m build
90
+ - name: Check dist artifacts
91
+ run: |
92
+ ls -lh dist
93
+ test -f dist/*.whl
94
+ test -f dist/*.tar.gz
95
+ - name: Upload to Test PyPI
96
+ run: twine upload --verbose --repository-url https://test.pypi.org/legacy/ dist/*
97
+ env:
98
+ TWINE_USERNAME: "__token__"
99
+ TWINE_PASSWORD: ${{ secrets.TEST_PYPI_API_TOKEN }}
100
+ - name: Upload to PyPI
101
+ run: twine upload --verbose dist/*
102
+ env:
103
+ TWINE_USERNAME: "__token__"
104
+ TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
105
+ - name: Upload dist to GitHub release
106
+ uses: softprops/action-gh-release@v3
107
+ with:
108
+ files: dist/*
109
+ prerelease: ${{ needs.check-version.outputs.is_major_minor != 'true' }}
@@ -0,0 +1,9 @@
1
+ **.DS_Store
2
+ tmp/
3
+ docs/_build/
4
+ *.egg-info/
5
+ __pycache__/
6
+ *.pyc
7
+ *.pyo
8
+ .coverage
9
+ coverage*.xml
@@ -0,0 +1,7 @@
1
+ {
2
+ "printWidth": 150,
3
+ "tabWidth": 2,
4
+ "useTabs": false,
5
+ "semi": true,
6
+ "endOfLine": "lf"
7
+ }
@@ -0,0 +1,17 @@
1
+ ---
2
+ version: 2
3
+
4
+ build:
5
+ os: ubuntu-lts-latest
6
+ tools:
7
+ python: "3.13"
8
+
9
+ sphinx:
10
+ configuration: docs/conf.py
11
+
12
+ python:
13
+ install:
14
+ - method: pip
15
+ path: .
16
+ extra_requirements: [dev]
17
+ - requirements: docs/requirements.txt
@@ -0,0 +1,5 @@
1
+ line-length = 150
2
+ target-version = "py310"
3
+
4
+ [format]
5
+ quote-style = "double"
@@ -0,0 +1,8 @@
1
+ {
2
+ "title": "pkft: Peakfit Explorer",
3
+ "description": "A FastAPI service for Gaussian mixture peak finding in 1D data with uncertainties.",
4
+ "creators": [{ "name": "Kerswell, Buchanan" }],
5
+ "license": "MIT",
6
+ "keywords": ["peak-finding", "mixture-model", "uncertainty", "fastapi"],
7
+ "upload_type": "software"
8
+ }
pkft-0.1.0a1/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2021 Buchanan Kerswell
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.
@@ -0,0 +1,7 @@
1
+ include README.md
2
+ include LICENSE
3
+ include pyproject.toml
4
+ recursive-include pkft *.py
5
+ recursive-include pkft/static *.html *.css *.js *.svg
6
+ recursive-include pkft/data *.csv
7
+ recursive-include tests *.py
pkft-0.1.0a1/Makefile ADDED
@@ -0,0 +1,97 @@
1
+ # Top-level dirs
2
+ PROJECT_ROOT := $(CURDIR)
3
+ APP := $(PROJECT_ROOT)/pkft
4
+ TESTS := $(PROJECT_ROOT)/tests
5
+ DOCS := $(PROJECT_ROOT)/docs
6
+
7
+ # Conda config
8
+ CONDA_ENV_ID := pkft
9
+ CONDA_ENV_YML := environment.yml
10
+ CONDA_PYTHON = $$(conda run -n $(CONDA_ENV_ID) which python)
11
+
12
+ # Server
13
+ APP_MODULE := pkft.api:app
14
+
15
+ # Targets
16
+ .PHONY: run docs test format environment clean deep-clean
17
+
18
+ all: environment test run
19
+
20
+ run:
21
+ @$(CONDA_PYTHON) -m uvicorn $(APP_MODULE) --host 127.0.0.1 --port 8000 --reload
22
+
23
+ # Cleanup directory
24
+ CLEAN := $(APP)/**/__pycache__ $(APP)/**/*.pyc $(TESTS)/**/__pycache__ $(CURDIR)/.pytest_cache $(CURDIR)/build $(CURDIR)/dist $(CURDIR)/*.egg-info $(CURDIR)/.coverage $(CURDIR)/coverage.xml
25
+ DEEP_CLEAN :=
26
+
27
+ docs:
28
+ @$(MAKE) -C docs html
29
+
30
+ test:
31
+ @$(CONDA_PYTHON) -m pytest --cov=pkft --cov-branch --cov-report=xml
32
+
33
+ format:
34
+ @$(CONDA_PYTHON) -m ruff format .
35
+ @npx --yes prettier --write "pkft/static/*.js" "pkft/static/*.css"
36
+
37
+ environment: $(CONDA_ENV_YML)
38
+ @if conda info --envs | awk '{print $$1}' | grep -qx "$(CONDA_ENV_ID)"; then \
39
+ echo "Environment '$$name' already exists. Skipping..."; \
40
+ else \
41
+ echo "Creating environment: $(CONDA_ENV_ID) from $(CONDA_ENV_YML)"; \
42
+ conda env create -n "$(CONDA_ENV_ID)" -f "$(CONDA_ENV_YML)"; \
43
+ fi
44
+
45
+ clean:
46
+ @echo "==> Cleaning ..."
47
+ @for item in $(CLEAN); do \
48
+ safe_rm() { \
49
+ if [ -e "$$1" ]; then \
50
+ ABS_PATH=$$(realpath "$$1"); \
51
+ case "$$ABS_PATH" in \
52
+ $(PROJECT_ROOT)*) \
53
+ echo "-> Safely removing\n $$ABS_PATH"; \
54
+ rm -rf "$$ABS_PATH";; \
55
+ *) \
56
+ echo "!! Skipping (outside project root)\n $$ABS_PATH";; \
57
+ esac; \
58
+ else \
59
+ echo "-- Skipping (not found)\n $$1"; \
60
+ fi; \
61
+ }; \
62
+ safe_rm "$$item"; \
63
+ done
64
+ @$(MAKE) -C docs clean || true
65
+ @find . -name ".DS_Store" -type f -delete
66
+
67
+ deep-clean:
68
+ @echo "==> Deep cleaning ..."
69
+ @for item in $(DEEP_CLEAN); do \
70
+ safe_rm() { \
71
+ if [ -e "$$1" ]; then \
72
+ ABS_PATH=$$(realpath "$$1"); \
73
+ case "$$ABS_PATH" in \
74
+ $(PROJECT_ROOT)*) \
75
+ echo "-> Safely removing\n $$ABS_PATH"; \
76
+ rm -rf "$$ABS_PATH";; \
77
+ *) \
78
+ echo "!! Skipping (outside project root)\n $$ABS_PATH";; \
79
+ esac; \
80
+ else \
81
+ echo "-- Skipping (not found)\n $$1"; \
82
+ fi; \
83
+ }; \
84
+ safe_rm "$$item"; \
85
+ done
86
+
87
+
88
+ help:
89
+ @echo "Available targets:"
90
+ @echo " run Run pkft app locally"
91
+ @echo " docs Build documentation"
92
+ @echo " test Run unit tests"
93
+ @echo " format Format code to 150 columns (ruff + prettier)"
94
+ @echo " environment Create Conda environment"
95
+ @echo " clean Cleanup unnecessary files and directories (safe)"
96
+ @echo " deep-clean Deep clean figures and results (use with caution!)"
97
+ @echo " help Show this help message"
pkft-0.1.0a1/PKG-INFO ADDED
@@ -0,0 +1,132 @@
1
+ Metadata-Version: 2.4
2
+ Name: pkft
3
+ Version: 0.1.0a1
4
+ Summary: A FastAPI service for Gaussian mixture peak finding in 1D geochronological data
5
+ Author-email: Buchanan Kerswell <buck.kerswell@gmail.com>
6
+ License: MIT
7
+ Project-URL: Repository, https://github.com/buchanankerswell/pkft
8
+ Classifier: Natural Language :: English
9
+ Classifier: Development Status :: 2 - Pre-Alpha
10
+ Classifier: Intended Audience :: Science/Research
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Operating System :: OS Independent
14
+ Classifier: Topic :: Scientific/Engineering :: Visualization
15
+ Requires-Python: >=3.10
16
+ Description-Content-Type: text/markdown
17
+ License-File: LICENSE
18
+ Requires-Dist: fastapi>=0.115
19
+ Requires-Dist: uvicorn>=0.30
20
+ Requires-Dist: pydantic>=2.0
21
+ Requires-Dist: numpy
22
+ Provides-Extra: dev
23
+ Requires-Dist: pytest; extra == "dev"
24
+ Requires-Dist: pytest-cov; extra == "dev"
25
+ Requires-Dist: httpx; extra == "dev"
26
+ Requires-Dist: ruff; extra == "dev"
27
+ Dynamic: license-file
28
+
29
+ # pkft: Peakfit Explorer
30
+
31
+ 📚 See the [documentation](https://pkft.readthedocs.io/en/latest/) for details.
32
+
33
+ Peak finding for 1D data with uncertainties by discrete Gaussian mixture
34
+ modelling (Galbraith & Laslett, 1993) in the spirit of IsoplotR
35
+ `peakfit` (Vermeesch, 2018), served as a FastAPI service with a minimal
36
+ dependency-free web UI.
37
+
38
+ ![Peakfit demo](assets/images/repo-banner.png)
39
+
40
+ *Figure: mixture fit for the bundled Trayler et al. 2020 demo dataset.*
41
+
42
+ ## Prerequisite software
43
+
44
+ This project is written in [python](https://www.python.org). For most users, I recommend installing the [miniforge](https://github.com/conda-forge/miniforge) python distribution. This distributions includes a minimal installation of python and the package manager [conda](https://docs.conda.io/en/latest/), which is required to build the necessary python environment for this software.
45
+
46
+ For macOS users, miniforge can be installed with [homebrew](https://brew.sh):
47
+
48
+ ```bash
49
+ brew install miniforge
50
+ ```
51
+
52
+ For Window's users, miniforge can be installed from a binary .exe. Follow the instructions for miniconda, anaconda, or miniforge (recommended) [here](https://docs.conda.io/projects/conda/en/stable/user-guide/install/windows.html).
53
+
54
+ ## Installation Guide
55
+
56
+ To install with Conda (recommended):
57
+
58
+ ``` bash
59
+ # Create conda environment
60
+ conda create -n pkft python=3.13 pip
61
+
62
+ # Activate conda environment
63
+ conda activate pkft
64
+
65
+ # Install pkft
66
+ pip install pkft
67
+ ```
68
+
69
+ If you want to install the development version for local testing in "editable" mode:
70
+
71
+ ``` bash
72
+ # Clone repo
73
+ git clone https://github.com/buchanankerswell/pkft.git
74
+ cd pkft
75
+
76
+ # Checkout develop branch
77
+ git checkout develop
78
+
79
+ # Create conda environment and install pkft locally in editable mode
80
+ # including optional development and documentation dependencies
81
+ make environment
82
+ ```
83
+
84
+ ## Usage Guide
85
+
86
+ After installing pkft using either method above, launch the server from the command line:
87
+
88
+ ``` bash
89
+ # Activate conda environment
90
+ conda activate pkft
91
+
92
+ # Launch server (UI at http://127.0.0.1:8000/)
93
+ pkft
94
+ ```
95
+
96
+ The same JSON serves browsers and agents:
97
+
98
+ ``` bash
99
+ curl -X POST http://127.0.0.1:8000/fit \
100
+ -H 'Content-Type: application/json' \
101
+ -d '{"observations": [{"value": 17.634, "standard_error": 0.016}], "peak_count": 1}'
102
+ ```
103
+
104
+ Open http://127.0.0.1:8000/ for the data explorer, http://127.0.0.1:8000/mixture for the mixture tab, or click **Load demo** for the bundled Trayler et al. 2020 dataset.
105
+
106
+ ## Method
107
+
108
+ Each observation `x[i]` with analytical variance `s[i]**2` contributes
109
+ `sum_j pi[j] * N(x[i] | mu[j], s[i]**2)` to the likelihood (Galbraith &
110
+ Laslett, 1993, discrete mixture; IsoplotR `peakfit` with `log=FALSE`).
111
+ Expectation–maximisation (10 restarts) maximises the log-likelihood;
112
+ `peak_count = 0` selects 1..5 components by BIC. Results maximise
113
+ the same likelihood as IsoplotR `peakfit` with `log=FALSE`, with small
114
+ numerical differences from the different optimiser and starting points.
115
+ See the docs for the full API reference.
116
+
117
+ ## Citation
118
+
119
+ Cite the app as Kerswell, B. (2023). Peakfit Explorer.
120
+ https://doi.org/10.17605/OSF.IO/2BU3C
121
+
122
+ ## Acknowledgement
123
+
124
+ I gratefully acknowledge feedback and suggestions provided by
125
+ [Matthew Kohn](https://scholar.google.com/citations?user=xSyB1KQAAAAJ&hl=en&oi=ao)
126
+ and [Kayleigh Harvey](https://www.linkedin.com/in/kayleigh-harvey-phd-98471952).
127
+ Peak modelling follows [IsoplotR](https://github.com/pvermees/IsoplotR/)
128
+ (Vermeesch, 2018); demo data from Trayler et al. (2020).
129
+
130
+ ## License
131
+
132
+ MIT License — see [LICENSE](LICENSE).
pkft-0.1.0a1/README.md ADDED
@@ -0,0 +1,104 @@
1
+ # pkft: Peakfit Explorer
2
+
3
+ 📚 See the [documentation](https://pkft.readthedocs.io/en/latest/) for details.
4
+
5
+ Peak finding for 1D data with uncertainties by discrete Gaussian mixture
6
+ modelling (Galbraith & Laslett, 1993) in the spirit of IsoplotR
7
+ `peakfit` (Vermeesch, 2018), served as a FastAPI service with a minimal
8
+ dependency-free web UI.
9
+
10
+ ![Peakfit demo](assets/images/repo-banner.png)
11
+
12
+ *Figure: mixture fit for the bundled Trayler et al. 2020 demo dataset.*
13
+
14
+ ## Prerequisite software
15
+
16
+ This project is written in [python](https://www.python.org). For most users, I recommend installing the [miniforge](https://github.com/conda-forge/miniforge) python distribution. This distributions includes a minimal installation of python and the package manager [conda](https://docs.conda.io/en/latest/), which is required to build the necessary python environment for this software.
17
+
18
+ For macOS users, miniforge can be installed with [homebrew](https://brew.sh):
19
+
20
+ ```bash
21
+ brew install miniforge
22
+ ```
23
+
24
+ For Window's users, miniforge can be installed from a binary .exe. Follow the instructions for miniconda, anaconda, or miniforge (recommended) [here](https://docs.conda.io/projects/conda/en/stable/user-guide/install/windows.html).
25
+
26
+ ## Installation Guide
27
+
28
+ To install with Conda (recommended):
29
+
30
+ ``` bash
31
+ # Create conda environment
32
+ conda create -n pkft python=3.13 pip
33
+
34
+ # Activate conda environment
35
+ conda activate pkft
36
+
37
+ # Install pkft
38
+ pip install pkft
39
+ ```
40
+
41
+ If you want to install the development version for local testing in "editable" mode:
42
+
43
+ ``` bash
44
+ # Clone repo
45
+ git clone https://github.com/buchanankerswell/pkft.git
46
+ cd pkft
47
+
48
+ # Checkout develop branch
49
+ git checkout develop
50
+
51
+ # Create conda environment and install pkft locally in editable mode
52
+ # including optional development and documentation dependencies
53
+ make environment
54
+ ```
55
+
56
+ ## Usage Guide
57
+
58
+ After installing pkft using either method above, launch the server from the command line:
59
+
60
+ ``` bash
61
+ # Activate conda environment
62
+ conda activate pkft
63
+
64
+ # Launch server (UI at http://127.0.0.1:8000/)
65
+ pkft
66
+ ```
67
+
68
+ The same JSON serves browsers and agents:
69
+
70
+ ``` bash
71
+ curl -X POST http://127.0.0.1:8000/fit \
72
+ -H 'Content-Type: application/json' \
73
+ -d '{"observations": [{"value": 17.634, "standard_error": 0.016}], "peak_count": 1}'
74
+ ```
75
+
76
+ Open http://127.0.0.1:8000/ for the data explorer, http://127.0.0.1:8000/mixture for the mixture tab, or click **Load demo** for the bundled Trayler et al. 2020 dataset.
77
+
78
+ ## Method
79
+
80
+ Each observation `x[i]` with analytical variance `s[i]**2` contributes
81
+ `sum_j pi[j] * N(x[i] | mu[j], s[i]**2)` to the likelihood (Galbraith &
82
+ Laslett, 1993, discrete mixture; IsoplotR `peakfit` with `log=FALSE`).
83
+ Expectation–maximisation (10 restarts) maximises the log-likelihood;
84
+ `peak_count = 0` selects 1..5 components by BIC. Results maximise
85
+ the same likelihood as IsoplotR `peakfit` with `log=FALSE`, with small
86
+ numerical differences from the different optimiser and starting points.
87
+ See the docs for the full API reference.
88
+
89
+ ## Citation
90
+
91
+ Cite the app as Kerswell, B. (2023). Peakfit Explorer.
92
+ https://doi.org/10.17605/OSF.IO/2BU3C
93
+
94
+ ## Acknowledgement
95
+
96
+ I gratefully acknowledge feedback and suggestions provided by
97
+ [Matthew Kohn](https://scholar.google.com/citations?user=xSyB1KQAAAAJ&hl=en&oi=ao)
98
+ and [Kayleigh Harvey](https://www.linkedin.com/in/kayleigh-harvey-phd-98471952).
99
+ Peak modelling follows [IsoplotR](https://github.com/pvermees/IsoplotR/)
100
+ (Vermeesch, 2018); demo data from Trayler et al. (2020).
101
+
102
+ ## License
103
+
104
+ MIT License — see [LICENSE](LICENSE).
@@ -0,0 +1,42 @@
1
+ # Top-level dirs
2
+ PROJECT_ROOT := $(realpath $(dir $(lastword $(MAKEFILE_LIST)))/..)
3
+ DOCS := $(PROJECT_ROOT)/docs
4
+
5
+ # Python config
6
+ SPHINX_BUILD := sphinx-build
7
+
8
+ # Sphinx build
9
+ SPHINX_OPTS ?=
10
+ SOURCE_DIR := $(DOCS)
11
+ BUILD_DIR := $(DOCS)/_build
12
+
13
+ # Cleanup directory
14
+ CLEAN := $(BUILD_DIR)/*
15
+
16
+ # Targets
17
+ .PHONY: help Makefile
18
+
19
+ help:
20
+ @$(SPHINX_BUILD) -M help "$(SOURCE_DIR)" "$(BUILD_DIR)" $(SPHINX_OPTS) $(O)
21
+
22
+ %: Makefile
23
+ @$(SPHINX_BUILD) -M $@ "$(SOURCE_DIR)" "$(BUILD_DIR)" $(SPHINX_OPTS) $(O)
24
+
25
+ clean:
26
+ @for item in $(CLEAN); do \
27
+ safe_rm() { \
28
+ if [ -e "$$1" ]; then \
29
+ ABS_PATH=$$(realpath "$$1"); \
30
+ case "$$ABS_PATH" in \
31
+ $(PROJECT_ROOT)*) \
32
+ echo "-> Safely removing\n $$ABS_PATH"; \
33
+ rm -rf "$$ABS_PATH";; \
34
+ *) \
35
+ echo "!! Skipping (outside project root)\n $$ABS_PATH";; \
36
+ esac; \
37
+ else \
38
+ echo "-- Skipping (not found)\n $$1"; \
39
+ fi; \
40
+ }; \
41
+ safe_rm "$$item"; \
42
+ done