kinepet 0.4.2__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.
- kinepet-0.4.2/.github/workflows/deploy-pages.yml +31 -0
- kinepet-0.4.2/.github/workflows/docs.yml +60 -0
- kinepet-0.4.2/.github/workflows/pages.yml +31 -0
- kinepet-0.4.2/.github/workflows/publish.yml +213 -0
- kinepet-0.4.2/.github/workflows/tests.yml +49 -0
- kinepet-0.4.2/.github/workflows/wheels.yml +80 -0
- kinepet-0.4.2/.gitignore +18 -0
- kinepet-0.4.2/.python-version +1 -0
- kinepet-0.4.2/CHANGELOG.md +80 -0
- kinepet-0.4.2/CITATION.cff +21 -0
- kinepet-0.4.2/CMakeLists.txt +108 -0
- kinepet-0.4.2/CONTRIBUTING.md +32 -0
- kinepet-0.4.2/Dockerfile.docs +19 -0
- kinepet-0.4.2/LICENSE +661 -0
- kinepet-0.4.2/PKG-INFO +186 -0
- kinepet-0.4.2/README.md +164 -0
- kinepet-0.4.2/THIRD_PARTY_NOTICES.md +79 -0
- kinepet-0.4.2/docs/architecture.md +167 -0
- kinepet-0.4.2/docs/assets/figures/reversible-2tcm.pdf +0 -0
- kinepet-0.4.2/docs/assets/figures/reversible-2tcm.svg +174 -0
- kinepet-0.4.2/docs/cli.md +97 -0
- kinepet-0.4.2/docs/data-format.md +54 -0
- kinepet-0.4.2/docs/development.md +86 -0
- kinepet-0.4.2/docs/getting-started.md +149 -0
- kinepet-0.4.2/docs/index.md +59 -0
- kinepet-0.4.2/docs/models.md +164 -0
- kinepet-0.4.2/docs/python-api/batch-fitting.md +126 -0
- kinepet-0.4.2/docs/python-api/helpers.md +93 -0
- kinepet-0.4.2/docs/python-api/index.md +52 -0
- kinepet-0.4.2/docs/python-api/results.md +56 -0
- kinepet-0.4.2/docs/python-api/single-tac.md +70 -0
- kinepet-0.4.2/docs/releasing.md +338 -0
- kinepet-0.4.2/docs/reproducibility.md +42 -0
- kinepet-0.4.2/kinepet/__init__.py +37 -0
- kinepet-0.4.2/kinepet/_validation.py +60 -0
- kinepet-0.4.2/kinepet/evaluation.py +91 -0
- kinepet-0.4.2/kinepet/fitting.py +174 -0
- kinepet-0.4.2/kinepet/models.py +91 -0
- kinepet-0.4.2/kinepet/py.typed +1 -0
- kinepet-0.4.2/kinepet/results.py +93 -0
- kinepet-0.4.2/mkdocs.yml +42 -0
- kinepet-0.4.2/pyproject.toml +57 -0
- kinepet-0.4.2/scripts/check_distributions.py +47 -0
- kinepet-0.4.2/scripts/check_release.py +54 -0
- kinepet-0.4.2/scripts/wheel_smoke.py +28 -0
- kinepet-0.4.2/src/aif.cpp +93 -0
- kinepet-0.4.2/src/aif.hpp +12 -0
- kinepet-0.4.2/src/frame_weights.cpp +30 -0
- kinepet-0.4.2/src/frame_weights.hpp +10 -0
- kinepet-0.4.2/src/lls/formulations.hpp +21 -0
- kinepet-0.4.2/src/lls/irreversible.cpp +78 -0
- kinepet-0.4.2/src/lls/regression.cpp +67 -0
- kinepet-0.4.2/src/lls/regression.hpp +34 -0
- kinepet-0.4.2/src/lls/reversible.cpp +97 -0
- kinepet-0.4.2/src/lls.cpp +45 -0
- kinepet-0.4.2/src/lls.hpp +25 -0
- kinepet-0.4.2/src/model.cpp +44 -0
- kinepet-0.4.2/src/model.hpp +40 -0
- kinepet-0.4.2/src/models/convolution.cpp +49 -0
- kinepet-0.4.2/src/models/convolution.hpp +9 -0
- kinepet-0.4.2/src/models/irreversible.cpp +46 -0
- kinepet-0.4.2/src/models/reversible.cpp +155 -0
- kinepet-0.4.2/src/models/tissue.hpp +19 -0
- kinepet-0.4.2/src/npy.cpp +224 -0
- kinepet-0.4.2/src/npy.hpp +36 -0
- kinepet-0.4.2/src/parameters.cpp +184 -0
- kinepet-0.4.2/src/parameters.hpp +59 -0
- kinepet-0.4.2/src/python_bindings.cpp +467 -0
- kinepet-0.4.2/src/small_linalg.hpp +115 -0
- kinepet-0.4.2/src/solver.cpp +523 -0
- kinepet-0.4.2/src/solver.hpp +61 -0
- kinepet-0.4.2/src/tac_batch.cpp +268 -0
- kinepet-0.4.2/src/tac_batch.hpp +65 -0
- kinepet-0.4.2/src/tac_fit_main.cpp +290 -0
- kinepet-0.4.2/src/tcm_model.cpp +57 -0
- kinepet-0.4.2/src/tcm_model.hpp +19 -0
- kinepet-0.4.2/src/validation.hpp +31 -0
- kinepet-0.4.2/tests/cpp/test_core.cpp +207 -0
- kinepet-0.4.2/tests/fixtures/warm_starts.txt +12 -0
- kinepet-0.4.2/tests/test_bindings.py +292 -0
- kinepet-0.4.2/tests/test_cli.py +93 -0
- kinepet-0.4.2/tests/test_models.py +129 -0
- kinepet-0.4.2/tests/test_physical_reference.py +42 -0
- kinepet-0.4.2/tests/test_release_notes.py +49 -0
- kinepet-0.4.2/tests/test_validation.py +88 -0
- kinepet-0.4.2/third_party_licenses/GCC-GPL-3.0.txt +674 -0
- kinepet-0.4.2/third_party_licenses/GCC-Runtime-Exception.txt +73 -0
- kinepet-0.4.2/third_party_licenses/LLVM-OpenMP.txt +361 -0
- kinepet-0.4.2/uv.lock +668 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: 2026 Christian Salomonsen
|
|
2
|
+
# SPDX-License-Identifier: AGPL-3.0-only
|
|
3
|
+
|
|
4
|
+
name: Deploy Documentation Artifact
|
|
5
|
+
|
|
6
|
+
on:
|
|
7
|
+
workflow_call:
|
|
8
|
+
|
|
9
|
+
permissions:
|
|
10
|
+
contents: read
|
|
11
|
+
|
|
12
|
+
concurrency:
|
|
13
|
+
group: github-pages
|
|
14
|
+
cancel-in-progress: false
|
|
15
|
+
|
|
16
|
+
jobs:
|
|
17
|
+
deploy:
|
|
18
|
+
name: Deploy documentation to GitHub Pages
|
|
19
|
+
runs-on: ubuntu-latest
|
|
20
|
+
permissions:
|
|
21
|
+
pages: write
|
|
22
|
+
id-token: write
|
|
23
|
+
environment:
|
|
24
|
+
name: github-pages
|
|
25
|
+
url: ${{ steps.deployment.outputs.page_url }}
|
|
26
|
+
steps:
|
|
27
|
+
- name: Configure GitHub Pages
|
|
28
|
+
uses: actions/configure-pages@v5
|
|
29
|
+
- name: Deploy the validated artifact from this run
|
|
30
|
+
id: deployment
|
|
31
|
+
uses: actions/deploy-pages@v4
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: 2026 Christian Salomonsen
|
|
2
|
+
# SPDX-License-Identifier: AGPL-3.0-only
|
|
3
|
+
|
|
4
|
+
name: Documentation
|
|
5
|
+
|
|
6
|
+
on:
|
|
7
|
+
push:
|
|
8
|
+
branches: [main]
|
|
9
|
+
paths:
|
|
10
|
+
- "docs/**"
|
|
11
|
+
- "mkdocs.yml"
|
|
12
|
+
- "pyproject.toml"
|
|
13
|
+
- "uv.lock"
|
|
14
|
+
- ".github/workflows/docs.yml"
|
|
15
|
+
- ".github/workflows/pages.yml"
|
|
16
|
+
- ".github/workflows/deploy-pages.yml"
|
|
17
|
+
pull_request:
|
|
18
|
+
paths:
|
|
19
|
+
- "docs/**"
|
|
20
|
+
- "mkdocs.yml"
|
|
21
|
+
- "pyproject.toml"
|
|
22
|
+
- "uv.lock"
|
|
23
|
+
- ".github/workflows/docs.yml"
|
|
24
|
+
- ".github/workflows/pages.yml"
|
|
25
|
+
- ".github/workflows/deploy-pages.yml"
|
|
26
|
+
workflow_dispatch:
|
|
27
|
+
workflow_call:
|
|
28
|
+
inputs:
|
|
29
|
+
source_ref:
|
|
30
|
+
description: Source tag, branch, or commit; defaults to the triggering commit
|
|
31
|
+
type: string
|
|
32
|
+
default: ""
|
|
33
|
+
upload_pages:
|
|
34
|
+
description: Upload the built site for the Pages deployment workflow
|
|
35
|
+
type: boolean
|
|
36
|
+
default: false
|
|
37
|
+
|
|
38
|
+
permissions:
|
|
39
|
+
contents: read
|
|
40
|
+
|
|
41
|
+
jobs:
|
|
42
|
+
docs:
|
|
43
|
+
name: Build documentation
|
|
44
|
+
runs-on: ubuntu-latest
|
|
45
|
+
steps:
|
|
46
|
+
- uses: actions/checkout@v4
|
|
47
|
+
with:
|
|
48
|
+
ref: ${{ inputs.source_ref || github.sha }}
|
|
49
|
+
- uses: actions/setup-python@v5
|
|
50
|
+
with:
|
|
51
|
+
python-version: "3.13"
|
|
52
|
+
- run: python -m pip install uv
|
|
53
|
+
- run: uv sync --locked --group docs
|
|
54
|
+
- run: uv run --group docs mkdocs build --strict
|
|
55
|
+
- name: Upload GitHub Pages artifact
|
|
56
|
+
if: inputs.upload_pages
|
|
57
|
+
uses: actions/upload-pages-artifact@v4
|
|
58
|
+
with:
|
|
59
|
+
path: site
|
|
60
|
+
retention-days: 90
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: 2026 Christian Salomonsen
|
|
2
|
+
# SPDX-License-Identifier: AGPL-3.0-only
|
|
3
|
+
|
|
4
|
+
name: Publish Documentation
|
|
5
|
+
|
|
6
|
+
on:
|
|
7
|
+
workflow_dispatch:
|
|
8
|
+
inputs:
|
|
9
|
+
source_ref:
|
|
10
|
+
description: Release tag or reviewed documentation-only branch/commit to publish
|
|
11
|
+
required: true
|
|
12
|
+
type: string
|
|
13
|
+
|
|
14
|
+
permissions:
|
|
15
|
+
contents: read
|
|
16
|
+
|
|
17
|
+
jobs:
|
|
18
|
+
build:
|
|
19
|
+
if: github.ref == 'refs/heads/main' && inputs.source_ref != ''
|
|
20
|
+
uses: ./.github/workflows/docs.yml
|
|
21
|
+
with:
|
|
22
|
+
source_ref: ${{ inputs.source_ref }}
|
|
23
|
+
upload_pages: true
|
|
24
|
+
|
|
25
|
+
deploy:
|
|
26
|
+
needs: build
|
|
27
|
+
permissions:
|
|
28
|
+
contents: read
|
|
29
|
+
pages: write
|
|
30
|
+
id-token: write
|
|
31
|
+
uses: ./.github/workflows/deploy-pages.yml
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: 2026 Christian Salomonsen
|
|
2
|
+
# SPDX-License-Identifier: AGPL-3.0-only
|
|
3
|
+
|
|
4
|
+
name: Publish Distributions
|
|
5
|
+
|
|
6
|
+
on:
|
|
7
|
+
push:
|
|
8
|
+
tags: ["v*"]
|
|
9
|
+
workflow_dispatch:
|
|
10
|
+
|
|
11
|
+
permissions:
|
|
12
|
+
contents: read
|
|
13
|
+
|
|
14
|
+
concurrency:
|
|
15
|
+
group: release-${{ github.ref }}
|
|
16
|
+
cancel-in-progress: false
|
|
17
|
+
|
|
18
|
+
jobs:
|
|
19
|
+
release_metadata:
|
|
20
|
+
runs-on: ubuntu-latest
|
|
21
|
+
steps:
|
|
22
|
+
- uses: actions/checkout@v4
|
|
23
|
+
- uses: actions/setup-python@v5
|
|
24
|
+
with:
|
|
25
|
+
python-version: "3.13"
|
|
26
|
+
- name: Check tag, version, license, and release notes
|
|
27
|
+
env:
|
|
28
|
+
RELEASE_REF: ${{ github.ref }}
|
|
29
|
+
RELEASE_TAG: ${{ github.ref_name }}
|
|
30
|
+
run: |
|
|
31
|
+
test "${RELEASE_REF#refs/tags/}" != "$RELEASE_REF"
|
|
32
|
+
python scripts/check_release.py --tag "$RELEASE_TAG" --notes-output "$RUNNER_TEMP/release-notes.md"
|
|
33
|
+
|
|
34
|
+
tests:
|
|
35
|
+
needs: release_metadata
|
|
36
|
+
uses: ./.github/workflows/tests.yml
|
|
37
|
+
with:
|
|
38
|
+
full_matrix: true
|
|
39
|
+
|
|
40
|
+
docs:
|
|
41
|
+
needs: release_metadata
|
|
42
|
+
uses: ./.github/workflows/docs.yml
|
|
43
|
+
with:
|
|
44
|
+
upload_pages: true
|
|
45
|
+
|
|
46
|
+
distributions:
|
|
47
|
+
needs: release_metadata
|
|
48
|
+
uses: ./.github/workflows/wheels.yml
|
|
49
|
+
|
|
50
|
+
prepare_artifacts:
|
|
51
|
+
name: Validate and save release artifacts
|
|
52
|
+
runs-on: ubuntu-latest
|
|
53
|
+
needs: [tests, docs, distributions]
|
|
54
|
+
outputs:
|
|
55
|
+
artifact_id: ${{ steps.upload.outputs.artifact-id }}
|
|
56
|
+
steps:
|
|
57
|
+
- uses: actions/checkout@v4
|
|
58
|
+
- uses: actions/setup-python@v5
|
|
59
|
+
with:
|
|
60
|
+
python-version: "3.13"
|
|
61
|
+
- uses: actions/download-artifact@v4
|
|
62
|
+
with:
|
|
63
|
+
name: sdist
|
|
64
|
+
path: release/dist
|
|
65
|
+
- uses: actions/download-artifact@v4
|
|
66
|
+
with:
|
|
67
|
+
pattern: wheels-*
|
|
68
|
+
path: release/dist
|
|
69
|
+
merge-multiple: true
|
|
70
|
+
- name: Check distribution licenses and contents
|
|
71
|
+
run: python scripts/check_distributions.py release/dist --require-license
|
|
72
|
+
- name: Check package metadata and README rendering
|
|
73
|
+
run: |
|
|
74
|
+
python -m pip install twine
|
|
75
|
+
python -m twine check --strict release/dist/*
|
|
76
|
+
- name: Prepare release notes
|
|
77
|
+
env:
|
|
78
|
+
RELEASE_TAG: ${{ github.ref_name }}
|
|
79
|
+
run: python scripts/check_release.py --tag "$RELEASE_TAG" --notes-output release/release-notes.md
|
|
80
|
+
- name: Record distribution checksums
|
|
81
|
+
working-directory: release/dist
|
|
82
|
+
run: sha256sum * > ../SHA256SUMS
|
|
83
|
+
- uses: actions/upload-artifact@v4
|
|
84
|
+
id: upload
|
|
85
|
+
with:
|
|
86
|
+
name: release-distributions
|
|
87
|
+
path: release/
|
|
88
|
+
if-no-files-found: error
|
|
89
|
+
retention-days: 90
|
|
90
|
+
|
|
91
|
+
testpypi:
|
|
92
|
+
name: Publish to TestPyPI
|
|
93
|
+
needs: prepare_artifacts
|
|
94
|
+
runs-on: ubuntu-latest
|
|
95
|
+
environment:
|
|
96
|
+
name: testpypi
|
|
97
|
+
url: https://test.pypi.org/p/kinepet
|
|
98
|
+
permissions:
|
|
99
|
+
contents: read
|
|
100
|
+
id-token: write
|
|
101
|
+
steps:
|
|
102
|
+
- uses: actions/download-artifact@v4
|
|
103
|
+
with:
|
|
104
|
+
artifact-ids: ${{ needs.prepare_artifacts.outputs.artifact_id }}
|
|
105
|
+
path: release
|
|
106
|
+
merge-multiple: true
|
|
107
|
+
- name: Verify saved artifacts
|
|
108
|
+
working-directory: release/dist
|
|
109
|
+
run: sha256sum --check ../SHA256SUMS
|
|
110
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
111
|
+
with:
|
|
112
|
+
packages-dir: release/dist/
|
|
113
|
+
repository-url: https://test.pypi.org/legacy/
|
|
114
|
+
|
|
115
|
+
draft_release:
|
|
116
|
+
name: Prepare draft GitHub release
|
|
117
|
+
outputs:
|
|
118
|
+
release_id: ${{ steps.draft.outputs.result }}
|
|
119
|
+
needs: [prepare_artifacts, testpypi]
|
|
120
|
+
runs-on: ubuntu-latest
|
|
121
|
+
permissions:
|
|
122
|
+
contents: write
|
|
123
|
+
steps:
|
|
124
|
+
- uses: actions/download-artifact@v4
|
|
125
|
+
with:
|
|
126
|
+
artifact-ids: ${{ needs.prepare_artifacts.outputs.artifact_id }}
|
|
127
|
+
path: release
|
|
128
|
+
merge-multiple: true
|
|
129
|
+
- name: Create draft from the changelog
|
|
130
|
+
id: draft
|
|
131
|
+
uses: actions/github-script@v7
|
|
132
|
+
with:
|
|
133
|
+
script: |
|
|
134
|
+
const tag = context.ref.replace(/^refs\/tags\//, '');
|
|
135
|
+
// The tag lookup endpoint excludes drafts; list releases with write access.
|
|
136
|
+
const releases = await github.paginate(github.rest.repos.listReleases, {
|
|
137
|
+
...context.repo, per_page: 100
|
|
138
|
+
});
|
|
139
|
+
const existing = releases.find(release => release.tag_name === tag);
|
|
140
|
+
if (existing && !existing.draft) {
|
|
141
|
+
throw new Error(`Release ${tag} is already published; do not republish this version.`);
|
|
142
|
+
}
|
|
143
|
+
// Preserve any notes the maintainer edited while reviewing a draft.
|
|
144
|
+
const release = existing || (await github.rest.repos.createRelease({
|
|
145
|
+
...context.repo,
|
|
146
|
+
tag_name: tag,
|
|
147
|
+
name: `KinePET ${tag}`,
|
|
148
|
+
body: require('fs').readFileSync('release/release-notes.md', 'utf8'),
|
|
149
|
+
draft: true,
|
|
150
|
+
prerelease: /(?:a|b|rc)\d/.test(tag)
|
|
151
|
+
})).data;
|
|
152
|
+
await core.summary
|
|
153
|
+
.addHeading('Ready for release review')
|
|
154
|
+
.addLink('Review draft release notes', release.html_url)
|
|
155
|
+
.addRaw('\n\nVerify the TestPyPI installation, then approve the pypi environment deployment.\n')
|
|
156
|
+
.write();
|
|
157
|
+
return release.id;
|
|
158
|
+
|
|
159
|
+
pypi:
|
|
160
|
+
name: Approve and publish to PyPI
|
|
161
|
+
needs: [prepare_artifacts, draft_release]
|
|
162
|
+
runs-on: ubuntu-latest
|
|
163
|
+
environment:
|
|
164
|
+
name: pypi
|
|
165
|
+
url: https://pypi.org/p/kinepet
|
|
166
|
+
permissions:
|
|
167
|
+
contents: read
|
|
168
|
+
id-token: write
|
|
169
|
+
steps:
|
|
170
|
+
- uses: actions/download-artifact@v4
|
|
171
|
+
with:
|
|
172
|
+
artifact-ids: ${{ needs.prepare_artifacts.outputs.artifact_id }}
|
|
173
|
+
path: release
|
|
174
|
+
merge-multiple: true
|
|
175
|
+
- name: Verify the artifacts tested on TestPyPI
|
|
176
|
+
working-directory: release/dist
|
|
177
|
+
run: sha256sum --check ../SHA256SUMS
|
|
178
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
179
|
+
with:
|
|
180
|
+
packages-dir: release/dist/
|
|
181
|
+
|
|
182
|
+
github_release:
|
|
183
|
+
name: Publish GitHub release
|
|
184
|
+
needs: [pypi, draft_release]
|
|
185
|
+
runs-on: ubuntu-latest
|
|
186
|
+
permissions:
|
|
187
|
+
contents: write
|
|
188
|
+
steps:
|
|
189
|
+
- uses: actions/github-script@v7
|
|
190
|
+
env:
|
|
191
|
+
RELEASE_ID: ${{ needs.draft_release.outputs.release_id }}
|
|
192
|
+
with:
|
|
193
|
+
script: |
|
|
194
|
+
const tag = context.ref.replace(/^refs\/tags\//, '');
|
|
195
|
+
const {data: release} = await github.rest.repos.getRelease({
|
|
196
|
+
...context.repo, release_id: Number(process.env.RELEASE_ID)
|
|
197
|
+
});
|
|
198
|
+
if (release.tag_name !== tag) throw new Error("Release tag changed during review");
|
|
199
|
+
if (release.draft) {
|
|
200
|
+
await github.rest.repos.updateRelease({
|
|
201
|
+
...context.repo, release_id: release.id, draft: false
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
await core.summary.addLink('Published release', release.html_url).write();
|
|
205
|
+
|
|
206
|
+
documentation:
|
|
207
|
+
name: Publish released documentation
|
|
208
|
+
needs: github_release
|
|
209
|
+
permissions:
|
|
210
|
+
contents: read
|
|
211
|
+
pages: write
|
|
212
|
+
id-token: write
|
|
213
|
+
uses: ./.github/workflows/deploy-pages.yml
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: 2026 Christian Salomonsen
|
|
2
|
+
# SPDX-License-Identifier: AGPL-3.0-only
|
|
3
|
+
|
|
4
|
+
name: Tests
|
|
5
|
+
|
|
6
|
+
on:
|
|
7
|
+
push:
|
|
8
|
+
branches: [main]
|
|
9
|
+
pull_request:
|
|
10
|
+
workflow_dispatch:
|
|
11
|
+
inputs:
|
|
12
|
+
full_matrix:
|
|
13
|
+
description: Test every supported Python version
|
|
14
|
+
type: boolean
|
|
15
|
+
default: true
|
|
16
|
+
workflow_call:
|
|
17
|
+
inputs:
|
|
18
|
+
full_matrix:
|
|
19
|
+
type: boolean
|
|
20
|
+
default: false
|
|
21
|
+
|
|
22
|
+
permissions:
|
|
23
|
+
contents: read
|
|
24
|
+
|
|
25
|
+
jobs:
|
|
26
|
+
tests:
|
|
27
|
+
name: Python and C++ tests (${{ matrix.python-version }})
|
|
28
|
+
runs-on: ubuntu-latest
|
|
29
|
+
strategy:
|
|
30
|
+
fail-fast: false
|
|
31
|
+
matrix:
|
|
32
|
+
python-version: ${{ fromJSON(inputs.full_matrix && '["3.11", "3.12", "3.13"]' || '["3.13"]') }}
|
|
33
|
+
steps:
|
|
34
|
+
- uses: actions/checkout@v4
|
|
35
|
+
- uses: actions/setup-python@v5
|
|
36
|
+
with:
|
|
37
|
+
python-version: ${{ matrix.python-version }}
|
|
38
|
+
- name: Install uv
|
|
39
|
+
run: python -m pip install --upgrade uv
|
|
40
|
+
- name: Install dependencies
|
|
41
|
+
run: uv sync --locked --group dev --python ${{ matrix.python-version }}
|
|
42
|
+
- name: Configure CMake
|
|
43
|
+
run: cmake -S . -B build -DPython_EXECUTABLE="$(uv run --python ${{ matrix.python-version }} python -c 'import sys; print(sys.executable)')"
|
|
44
|
+
- name: Build CLI and extension
|
|
45
|
+
run: cmake --build build --parallel
|
|
46
|
+
- name: Run C++ numerical tests
|
|
47
|
+
run: ctest --test-dir build --output-on-failure
|
|
48
|
+
- name: Run Python tests, including CLI parity
|
|
49
|
+
run: uv run --python ${{ matrix.python-version }} python -m pytest -q
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: 2026 Christian Salomonsen
|
|
2
|
+
# SPDX-License-Identifier: AGPL-3.0-only
|
|
3
|
+
|
|
4
|
+
name: Build Distributions
|
|
5
|
+
|
|
6
|
+
on:
|
|
7
|
+
workflow_dispatch:
|
|
8
|
+
workflow_call:
|
|
9
|
+
|
|
10
|
+
permissions:
|
|
11
|
+
contents: read
|
|
12
|
+
|
|
13
|
+
jobs:
|
|
14
|
+
build_sdist:
|
|
15
|
+
name: Build sdist
|
|
16
|
+
runs-on: ubuntu-latest
|
|
17
|
+
|
|
18
|
+
steps:
|
|
19
|
+
- name: Check out source
|
|
20
|
+
uses: actions/checkout@v4
|
|
21
|
+
|
|
22
|
+
- name: Set up Python
|
|
23
|
+
uses: actions/setup-python@v5
|
|
24
|
+
with:
|
|
25
|
+
python-version: "3.11"
|
|
26
|
+
|
|
27
|
+
- name: Build sdist
|
|
28
|
+
run: |
|
|
29
|
+
python -m pip install --upgrade build
|
|
30
|
+
python -m build --sdist
|
|
31
|
+
|
|
32
|
+
- name: Check source archive
|
|
33
|
+
run: python scripts/check_distributions.py dist
|
|
34
|
+
|
|
35
|
+
- name: Upload sdist
|
|
36
|
+
uses: actions/upload-artifact@v4
|
|
37
|
+
with:
|
|
38
|
+
name: sdist
|
|
39
|
+
path: dist/*.tar.gz
|
|
40
|
+
|
|
41
|
+
build_wheels:
|
|
42
|
+
name: Build wheels (${{ matrix.os }})
|
|
43
|
+
runs-on: ${{ matrix.os }}
|
|
44
|
+
strategy:
|
|
45
|
+
fail-fast: false
|
|
46
|
+
matrix:
|
|
47
|
+
os:
|
|
48
|
+
- ubuntu-latest
|
|
49
|
+
- windows-latest
|
|
50
|
+
- macos-15
|
|
51
|
+
|
|
52
|
+
steps:
|
|
53
|
+
- name: Check out source
|
|
54
|
+
uses: actions/checkout@v4
|
|
55
|
+
|
|
56
|
+
- name: Set up Python
|
|
57
|
+
uses: actions/setup-python@v5
|
|
58
|
+
with:
|
|
59
|
+
python-version: "3.11"
|
|
60
|
+
|
|
61
|
+
- name: Install cibuildwheel
|
|
62
|
+
run: python -m pip install --upgrade cibuildwheel
|
|
63
|
+
|
|
64
|
+
- name: Build wheels
|
|
65
|
+
env:
|
|
66
|
+
CIBW_BUILD: cp311-* cp312-* cp313-*
|
|
67
|
+
CIBW_ARCHS_MACOS: arm64
|
|
68
|
+
CIBW_BUILD_FRONTEND: pip
|
|
69
|
+
CIBW_BUILD_VERBOSITY: 1
|
|
70
|
+
CIBW_TEST_COMMAND: python {project}/scripts/wheel_smoke.py
|
|
71
|
+
run: python -m cibuildwheel --output-dir wheelhouse
|
|
72
|
+
|
|
73
|
+
- name: Check wheel contents
|
|
74
|
+
run: python scripts/check_distributions.py wheelhouse
|
|
75
|
+
|
|
76
|
+
- name: Upload wheels
|
|
77
|
+
uses: actions/upload-artifact@v4
|
|
78
|
+
with:
|
|
79
|
+
name: wheels-${{ matrix.os }}
|
|
80
|
+
path: wheelhouse/*.whl
|
kinepet-0.4.2/.gitignore
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.13
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.4.2 (2026-11-11)
|
|
4
|
+
|
|
5
|
+
This release updates packaging, release automation, and documentation hosting.
|
|
6
|
+
The fitting algorithms and public model API are unchanged from 0.4.1.
|
|
7
|
+
|
|
8
|
+
### Changed
|
|
9
|
+
|
|
10
|
+
- Build distributions once, upload them to TestPyPI for review, then publish the
|
|
11
|
+
same files to official PyPI after approval through the `pypi` environment.
|
|
12
|
+
Saved artifact IDs and SHA-256 checksums preserve the files between uploads.
|
|
13
|
+
- Generate draft GitHub release notes from the matching changelog entry and
|
|
14
|
+
publish the release after the PyPI upload succeeds.
|
|
15
|
+
- Deploy the validated release documentation to GitHub Pages after successful
|
|
16
|
+
PyPI and GitHub release publication, retaining `https://kinepet.salodev.no/`.
|
|
17
|
+
Development pushes validate documentation without updating the live site;
|
|
18
|
+
manual publication requires an explicit source tag, branch, or commit.
|
|
19
|
+
- Run one Linux/Python configuration for routine pushes and pull requests.
|
|
20
|
+
Reserve the full supported Python test matrix and platform distribution builds
|
|
21
|
+
for release tags and manual runs.
|
|
22
|
+
- Document Trusted Publishing setup, release review and recovery, and GitHub
|
|
23
|
+
Pages configuration. Update citation metadata and add the Zenodo DOI badge.
|
|
24
|
+
|
|
25
|
+
### Fixed
|
|
26
|
+
|
|
27
|
+
- Replace the retired `macos-14` runner with `macos-15` and explicitly build
|
|
28
|
+
Apple Silicon (`arm64`) wheels. The unavailable runner previously prevented
|
|
29
|
+
the distribution workflow from reaching publication.
|
|
30
|
+
|
|
31
|
+
## 0.4.1 (2026-11-11)
|
|
32
|
+
|
|
33
|
+
### Added
|
|
34
|
+
|
|
35
|
+
- Explicit Python `Irreversible2TCM` and `Reversible2TCM` descriptions with
|
|
36
|
+
`FittedVB()` or `FixedVB(value)`. Accepted by fitting, forward evaluation, and
|
|
37
|
+
bounds helpers alongside the existing `irr` / `rev` names.
|
|
38
|
+
- Direct C++ tests for reversible coefficient recovery, parameter layout,
|
|
39
|
+
rejected warm starts, and pre-refactor initialization baselines.
|
|
40
|
+
- Python model-configuration and CLI/API parity tests, including shared/per-TAC
|
|
41
|
+
inputs and optional AIF parameters. C++ numerical tests run in CLI CI.
|
|
42
|
+
- Architecture and equation-location guide in `docs/architecture.md`.
|
|
43
|
+
|
|
44
|
+
### Changed
|
|
45
|
+
|
|
46
|
+
- Move paper-specific comparison scripts and tests, standalone simulations,
|
|
47
|
+
and the synthetic benchmark to the companion validation repository. Library
|
|
48
|
+
numerical tests, CLI parity checks, and wheel validation remain in KinePET.
|
|
49
|
+
- Group contributor and maintenance documentation separately from user guides.
|
|
50
|
+
|
|
51
|
+
- C++ models carry their blood-fraction setting explicitly; optional AIF settings
|
|
52
|
+
are grouped rather than passed as repeated boolean/value arguments.
|
|
53
|
+
- Model-specific LLS regressions and kinetic parameter recovery have separate,
|
|
54
|
+
named implementations with coefficient definitions beside the equations.
|
|
55
|
+
- Tissue kinetics, convolution, AIF transformations, vascular mixing, parameter
|
|
56
|
+
layout, bounds, and default frame weights have distinct source files.
|
|
57
|
+
- Internal per-curve C++ entrypoints are named `fit_tac` and
|
|
58
|
+
`fit_tac_with_initial_guess`, replacing the legacy voxel terminology.
|
|
59
|
+
- Python public exports, model descriptions, fitting, evaluation, validation, and
|
|
60
|
+
result containers have separate modules.
|
|
61
|
+
- Distribution builds omit C++ test targets; standalone CMake builds retain CTest.
|
|
62
|
+
|
|
63
|
+
The model API refactor preserves string-model calls, effective defaults, result
|
|
64
|
+
shapes, CLI flags and filenames, status semantics, bounds, and numerical algorithms.
|
|
65
|
+
With a model object, configure blood fraction on that object; combining it with
|
|
66
|
+
legacy `fit_vb` / `fixed_vb` options raises `ValueError`. A standalone evaluation
|
|
67
|
+
also rejects a numeric `vB` that conflicts with the model's `FixedVB`.
|
|
68
|
+
|
|
69
|
+
### Fixes and distribution updates
|
|
70
|
+
|
|
71
|
+
- Reject nonfinite inputs, invalid time grids/weights, and invalid solver options
|
|
72
|
+
in the shared C++ path. Preserve per-TAC invalid status 5 in batch outputs;
|
|
73
|
+
guard objective, derivative, and linear-step overflow against false convergence.
|
|
74
|
+
- Fix in-place AIF transformation for negative delays and dispersion.
|
|
75
|
+
- Add numerical input regressions and independent compartment ODE checks.
|
|
76
|
+
- Resolve public development dependencies without a private index. Prepare
|
|
77
|
+
Trusted Publishing with release metadata/license gates and numerical wheel tests.
|
|
78
|
+
- Add dependency notices, contribution guidance, citation metadata, scientific
|
|
79
|
+
conventions, validation scope, and release instructions.
|
|
80
|
+
- Add AGPL-3.0-only licensing and SPDX notices to project source and build files.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
cff-version: 1.2.0
|
|
2
|
+
message: "Please cite the version of KinePET used in your work, including its release number or commit identifier."
|
|
3
|
+
type: software
|
|
4
|
+
title: "KinePET: TAC-based fitting of dynamic PET two-tissue compartment models"
|
|
5
|
+
authors:
|
|
6
|
+
- family-names: "Salomonsen"
|
|
7
|
+
given-names: "Christian"
|
|
8
|
+
orcid: "https://orcid.org/0009-0007-4958-4544"
|
|
9
|
+
doi: 10.5281/zenodo.24132816
|
|
10
|
+
date-released: 2026-11-11
|
|
11
|
+
repository-code: "https://github.com/c-salomonsen/KinePET"
|
|
12
|
+
url: "https://kinepet.salodev.no/"
|
|
13
|
+
abstract: "A bounded nonlinear C++ solver with Python bindings for reversible and irreversible two-tissue compartment models, with linear least-squares initialization."
|
|
14
|
+
keywords:
|
|
15
|
+
- PET
|
|
16
|
+
- kinetic modeling
|
|
17
|
+
- compartment models
|
|
18
|
+
identifiers:
|
|
19
|
+
- type: doi
|
|
20
|
+
value: "10.5281/zenodo.24132816"
|
|
21
|
+
description: "The concept DOI for the collection containing all versions of the software."
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: 2026 Christian Salomonsen
|
|
2
|
+
# SPDX-License-Identifier: AGPL-3.0-only
|
|
3
|
+
|
|
4
|
+
cmake_minimum_required(VERSION 3.20)
|
|
5
|
+
|
|
6
|
+
project(tcm_fit LANGUAGES CXX)
|
|
7
|
+
|
|
8
|
+
set(CMAKE_CXX_STANDARD 20)
|
|
9
|
+
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
|
10
|
+
set(CMAKE_CXX_EXTENSIONS OFF)
|
|
11
|
+
|
|
12
|
+
if (NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
|
|
13
|
+
set(CMAKE_BUILD_TYPE Release CACHE STRING "Choose the type of build." FORCE)
|
|
14
|
+
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo")
|
|
15
|
+
endif()
|
|
16
|
+
|
|
17
|
+
option(TCM_ENABLE_OPENMP "Enable OpenMP TAC parallelism when available" ON)
|
|
18
|
+
option(TCM_ENABLE_PYTHON_BINDINGS "Build the nanobind Python extension" ON)
|
|
19
|
+
|
|
20
|
+
add_library(
|
|
21
|
+
tcm_solver_core
|
|
22
|
+
src/model.cpp
|
|
23
|
+
src/parameters.cpp
|
|
24
|
+
src/aif.cpp
|
|
25
|
+
src/frame_weights.cpp
|
|
26
|
+
src/models/convolution.cpp
|
|
27
|
+
src/models/irreversible.cpp
|
|
28
|
+
src/models/reversible.cpp
|
|
29
|
+
src/lls.cpp
|
|
30
|
+
src/lls/regression.cpp
|
|
31
|
+
src/lls/irreversible.cpp
|
|
32
|
+
src/lls/reversible.cpp
|
|
33
|
+
src/solver.cpp
|
|
34
|
+
src/tac_batch.cpp
|
|
35
|
+
src/tcm_model.cpp
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
target_include_directories(tcm_solver_core PUBLIC src)
|
|
39
|
+
set_target_properties(tcm_solver_core PROPERTIES POSITION_INDEPENDENT_CODE ON)
|
|
40
|
+
|
|
41
|
+
if(MSVC)
|
|
42
|
+
target_compile_options(tcm_solver_core PRIVATE /W4 /O2)
|
|
43
|
+
else()
|
|
44
|
+
target_compile_options(tcm_solver_core PRIVATE -Wall -Wextra -Wpedantic -O3)
|
|
45
|
+
endif()
|
|
46
|
+
|
|
47
|
+
if(TCM_ENABLE_OPENMP)
|
|
48
|
+
find_package(OpenMP QUIET)
|
|
49
|
+
if(OpenMP_CXX_FOUND)
|
|
50
|
+
target_link_libraries(tcm_solver_core PUBLIC OpenMP::OpenMP_CXX)
|
|
51
|
+
target_compile_definitions(tcm_solver_core PUBLIC TCM_HAS_OPENMP=1)
|
|
52
|
+
endif()
|
|
53
|
+
endif()
|
|
54
|
+
|
|
55
|
+
add_executable(
|
|
56
|
+
tcm_fit_tacs
|
|
57
|
+
src/npy.cpp
|
|
58
|
+
src/tac_fit_main.cpp
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
target_link_libraries(tcm_fit_tacs PRIVATE tcm_solver_core)
|
|
62
|
+
target_include_directories(tcm_fit_tacs PRIVATE src)
|
|
63
|
+
|
|
64
|
+
if(MSVC)
|
|
65
|
+
target_compile_options(tcm_fit_tacs PRIVATE /W4 /O2)
|
|
66
|
+
else()
|
|
67
|
+
target_compile_options(tcm_fit_tacs PRIVATE -Wall -Wextra -Wpedantic -O3)
|
|
68
|
+
endif()
|
|
69
|
+
|
|
70
|
+
if(TCM_ENABLE_PYTHON_BINDINGS)
|
|
71
|
+
if (CMAKE_VERSION VERSION_LESS 3.18)
|
|
72
|
+
set(DEV_MODULE Development)
|
|
73
|
+
else()
|
|
74
|
+
set(DEV_MODULE Development.Module)
|
|
75
|
+
endif()
|
|
76
|
+
|
|
77
|
+
find_package(Python 3.11 COMPONENTS Interpreter ${DEV_MODULE} REQUIRED)
|
|
78
|
+
|
|
79
|
+
execute_process(
|
|
80
|
+
COMMAND "${Python_EXECUTABLE}" -m nanobind --cmake_dir
|
|
81
|
+
RESULT_VARIABLE TCM_NANOBIND_STATUS
|
|
82
|
+
OUTPUT_VARIABLE nanobind_ROOT
|
|
83
|
+
OUTPUT_STRIP_TRAILING_WHITESPACE
|
|
84
|
+
)
|
|
85
|
+
if (NOT TCM_NANOBIND_STATUS EQUAL 0)
|
|
86
|
+
message(FATAL_ERROR "Could not locate nanobind. Install it in the active Python environment first.")
|
|
87
|
+
endif()
|
|
88
|
+
|
|
89
|
+
find_package(nanobind CONFIG REQUIRED)
|
|
90
|
+
|
|
91
|
+
nanobind_add_module(
|
|
92
|
+
_core
|
|
93
|
+
NB_STATIC
|
|
94
|
+
src/python_bindings.cpp
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
target_link_libraries(_core PRIVATE tcm_solver_core)
|
|
98
|
+
target_include_directories(_core PRIVATE src)
|
|
99
|
+
|
|
100
|
+
install(TARGETS _core LIBRARY DESTINATION kinepet)
|
|
101
|
+
endif()
|
|
102
|
+
|
|
103
|
+
include(CTest)
|
|
104
|
+
if(BUILD_TESTING)
|
|
105
|
+
add_executable(test_core tests/cpp/test_core.cpp)
|
|
106
|
+
target_link_libraries(test_core PRIVATE tcm_solver_core)
|
|
107
|
+
add_test(NAME core_numerics COMMAND test_core "${CMAKE_CURRENT_SOURCE_DIR}/tests/fixtures/warm_starts.txt")
|
|
108
|
+
endif()
|