PyStormTracker 0.3.1__tar.gz → 0.3.3__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.
- pystormtracker-0.3.3/.github/workflows/ci.yml +183 -0
- pystormtracker-0.3.3/.github/workflows/docker-publish.yml +109 -0
- {pystormtracker-0.3.1 → pystormtracker-0.3.3}/.github/workflows/python-publish.yml +17 -2
- pystormtracker-0.3.3/.python-version +1 -0
- {pystormtracker-0.3.1 → pystormtracker-0.3.3}/CITATION.cff +3 -3
- pystormtracker-0.3.3/Dockerfile +55 -0
- {pystormtracker-0.3.1 → pystormtracker-0.3.3}/PKG-INFO +62 -55
- {pystormtracker-0.3.1 → pystormtracker-0.3.3}/README.md +40 -43
- {pystormtracker-0.3.1 → pystormtracker-0.3.3}/pyproject.toml +38 -20
- pystormtracker-0.3.3/src/pystormtracker/__init__.py +11 -0
- {pystormtracker-0.3.1 → pystormtracker-0.3.3}/src/pystormtracker/simple/detector.py +3 -0
- {pystormtracker-0.3.1 → pystormtracker-0.3.3}/uv.lock +282 -120
- pystormtracker-0.3.1/.github/workflows/ci.yml +0 -119
- pystormtracker-0.3.1/.github/workflows/docker-publish.yml +0 -88
- pystormtracker-0.3.1/.pre-commit-config.yaml +0 -23
- pystormtracker-0.3.1/Dockerfile +0 -35
- pystormtracker-0.3.1/src/pystormtracker/__init__.py +0 -4
- {pystormtracker-0.3.1 → pystormtracker-0.3.3}/.github/dependabot.yml +0 -0
- {pystormtracker-0.3.1 → pystormtracker-0.3.3}/.gitignore +0 -0
- {pystormtracker-0.3.1 → pystormtracker-0.3.3}/.readthedocs.yaml +0 -0
- {pystormtracker-0.3.1 → pystormtracker-0.3.3}/LICENSE +0 -0
- {pystormtracker-0.3.1 → pystormtracker-0.3.3}/codecov.yml +0 -0
- {pystormtracker-0.3.1 → pystormtracker-0.3.3}/data/test/tracks/era5_msl_2.5x2.5_v0.0.2_imilast.txt +0 -0
- {pystormtracker-0.3.1 → pystormtracker-0.3.3}/docs/IntercomparisonProtocol.pdf +0 -0
- {pystormtracker-0.3.1 → pystormtracker-0.3.3}/docs/conf.py +0 -0
- {pystormtracker-0.3.1 → pystormtracker-0.3.3}/docs/index.md +0 -0
- {pystormtracker-0.3.1 → pystormtracker-0.3.3}/src/pystormtracker/models/__init__.py +0 -0
- {pystormtracker-0.3.1 → pystormtracker-0.3.3}/src/pystormtracker/models/center.py +0 -0
- {pystormtracker-0.3.1 → pystormtracker-0.3.3}/src/pystormtracker/models/grid.py +0 -0
- {pystormtracker-0.3.1 → pystormtracker-0.3.3}/src/pystormtracker/models/time.py +0 -0
- {pystormtracker-0.3.1 → pystormtracker-0.3.3}/src/pystormtracker/models/tracks.py +0 -0
- {pystormtracker-0.3.1 → pystormtracker-0.3.3}/src/pystormtracker/simple/__init__.py +0 -0
- {pystormtracker-0.3.1 → pystormtracker-0.3.3}/src/pystormtracker/simple/linker.py +0 -0
- {pystormtracker-0.3.1 → pystormtracker-0.3.3}/src/pystormtracker/stormtracker.py +0 -0
- {pystormtracker-0.3.1 → pystormtracker-0.3.3}/tests/__init__.py +0 -0
- {pystormtracker-0.3.1 → pystormtracker-0.3.3}/tests/conftest.py +0 -0
- {pystormtracker-0.3.1 → pystormtracker-0.3.3}/tests/data_utils.py +0 -0
- {pystormtracker-0.3.1 → pystormtracker-0.3.3}/tests/test_center.py +0 -0
- {pystormtracker-0.3.1 → pystormtracker-0.3.3}/tests/test_integration.py +0 -0
- {pystormtracker-0.3.1 → pystormtracker-0.3.3}/tests/test_simple_detector.py +0 -0
- {pystormtracker-0.3.1 → pystormtracker-0.3.3}/tests/test_simple_linker.py +0 -0
- {pystormtracker-0.3.1 → pystormtracker-0.3.3}/tests/test_stormtracker.py +0 -0
- {pystormtracker-0.3.1 → pystormtracker-0.3.3}/tests/test_tracks.py +0 -0
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
paths:
|
|
6
|
+
- 'src/**'
|
|
7
|
+
- 'tests/**'
|
|
8
|
+
- 'pyproject.toml'
|
|
9
|
+
- 'uv.lock'
|
|
10
|
+
- 'Dockerfile'
|
|
11
|
+
push:
|
|
12
|
+
branches:
|
|
13
|
+
- main
|
|
14
|
+
- release/**
|
|
15
|
+
tags:
|
|
16
|
+
- v*
|
|
17
|
+
paths:
|
|
18
|
+
- 'src/**'
|
|
19
|
+
- 'tests/**'
|
|
20
|
+
- 'pyproject.toml'
|
|
21
|
+
- 'uv.lock'
|
|
22
|
+
- 'Dockerfile'
|
|
23
|
+
workflow_dispatch:
|
|
24
|
+
|
|
25
|
+
concurrency:
|
|
26
|
+
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref_name }}
|
|
27
|
+
cancel-in-progress: ${{ github.ref_type != 'tag' }}
|
|
28
|
+
|
|
29
|
+
permissions:
|
|
30
|
+
contents: read
|
|
31
|
+
|
|
32
|
+
jobs:
|
|
33
|
+
ruff-lint:
|
|
34
|
+
runs-on: ubuntu-24.04
|
|
35
|
+
steps:
|
|
36
|
+
- uses: actions/checkout@v6
|
|
37
|
+
with:
|
|
38
|
+
ref: ${{ github.ref }}
|
|
39
|
+
fetch-depth: 0
|
|
40
|
+
- name: Set up uv
|
|
41
|
+
uses: astral-sh/setup-uv@v7
|
|
42
|
+
with:
|
|
43
|
+
enable-cache: true
|
|
44
|
+
- name: Lint with Ruff
|
|
45
|
+
run: uv run ruff check .
|
|
46
|
+
|
|
47
|
+
ruff-format:
|
|
48
|
+
runs-on: ubuntu-24.04
|
|
49
|
+
steps:
|
|
50
|
+
- uses: actions/checkout@v6
|
|
51
|
+
with:
|
|
52
|
+
ref: ${{ github.ref }}
|
|
53
|
+
fetch-depth: 0
|
|
54
|
+
- name: Set up uv
|
|
55
|
+
uses: astral-sh/setup-uv@v7
|
|
56
|
+
with:
|
|
57
|
+
enable-cache: true
|
|
58
|
+
- name: Check formatting with Ruff
|
|
59
|
+
run: uv run ruff format --check .
|
|
60
|
+
|
|
61
|
+
mypy-typecheck:
|
|
62
|
+
runs-on: ubuntu-24.04
|
|
63
|
+
steps:
|
|
64
|
+
- uses: actions/checkout@v6
|
|
65
|
+
with:
|
|
66
|
+
ref: ${{ github.ref }}
|
|
67
|
+
fetch-depth: 0
|
|
68
|
+
- name: Set up uv
|
|
69
|
+
uses: astral-sh/setup-uv@v7
|
|
70
|
+
with:
|
|
71
|
+
enable-cache: true
|
|
72
|
+
- name: Install system dependencies
|
|
73
|
+
run: sudo apt-get update && sudo apt-get install -y libopenmpi-dev
|
|
74
|
+
- name: Install dependencies
|
|
75
|
+
run: uv sync --frozen --group dev
|
|
76
|
+
- name: Type check with Mypy
|
|
77
|
+
run: uv run mypy src/ tests/
|
|
78
|
+
|
|
79
|
+
unit-tests:
|
|
80
|
+
name: unit-tests (Python ${{ matrix.python-version }}${{ matrix.min-deps && ', min-deps' || '' }})
|
|
81
|
+
runs-on: ubuntu-24.04
|
|
82
|
+
strategy:
|
|
83
|
+
fail-fast: false
|
|
84
|
+
matrix:
|
|
85
|
+
python-version: ["3.11", "3.12", "3.13", "3.14"]
|
|
86
|
+
include:
|
|
87
|
+
- python-version: "3.11"
|
|
88
|
+
min-deps: true
|
|
89
|
+
steps:
|
|
90
|
+
- uses: actions/checkout@v6
|
|
91
|
+
with:
|
|
92
|
+
ref: ${{ github.ref }}
|
|
93
|
+
fetch-depth: 0
|
|
94
|
+
- name: Set up uv
|
|
95
|
+
uses: astral-sh/setup-uv@v7
|
|
96
|
+
with:
|
|
97
|
+
enable-cache: true
|
|
98
|
+
python-version: ${{ matrix.python-version }}
|
|
99
|
+
- name: Install system dependencies
|
|
100
|
+
run: sudo apt-get update && sudo apt-get install -y libopenmpi-dev
|
|
101
|
+
- name: Install dependencies
|
|
102
|
+
if: ${{ !matrix.min-deps }}
|
|
103
|
+
run: uv sync --frozen --group dev
|
|
104
|
+
- name: Install dependencies (min-deps)
|
|
105
|
+
if: ${{ matrix.min-deps }}
|
|
106
|
+
run: uv sync --group dev --resolution lowest-direct
|
|
107
|
+
- name: Run Unit Tests
|
|
108
|
+
run: |
|
|
109
|
+
uv run pytest -vv
|
|
110
|
+
|
|
111
|
+
integration-tests:
|
|
112
|
+
name: integration-tests (Python ${{ matrix.python-version }}, ${{ matrix.arch }})
|
|
113
|
+
runs-on: ${{ matrix.os }}
|
|
114
|
+
strategy:
|
|
115
|
+
fail-fast: false
|
|
116
|
+
matrix:
|
|
117
|
+
include:
|
|
118
|
+
- arch: amd64
|
|
119
|
+
os: ubuntu-24.04
|
|
120
|
+
python-version: "3.14"
|
|
121
|
+
- arch: arm64
|
|
122
|
+
os: ubuntu-24.04-arm
|
|
123
|
+
python-version: "3.14"
|
|
124
|
+
steps:
|
|
125
|
+
- uses: actions/checkout@v6
|
|
126
|
+
with:
|
|
127
|
+
ref: ${{ github.ref }}
|
|
128
|
+
fetch-depth: 0
|
|
129
|
+
- name: Set up uv
|
|
130
|
+
uses: astral-sh/setup-uv@v7
|
|
131
|
+
with:
|
|
132
|
+
enable-cache: true
|
|
133
|
+
python-version: ${{ matrix.python-version }}
|
|
134
|
+
- name: Install system dependencies
|
|
135
|
+
run: sudo apt-get update && sudo apt-get install -y openmpi-bin libopenmpi-dev
|
|
136
|
+
- name: Install dependencies
|
|
137
|
+
run: uv sync --frozen --group dev
|
|
138
|
+
- name: Run Integration Tests
|
|
139
|
+
run: |
|
|
140
|
+
uv run pytest -vv tests/test_integration.py --run-integration
|
|
141
|
+
- name: Upload coverage reports to Codecov
|
|
142
|
+
uses: codecov/codecov-action@v5
|
|
143
|
+
with:
|
|
144
|
+
token: ${{ secrets.CODECOV_TOKEN }}
|
|
145
|
+
|
|
146
|
+
docker-build:
|
|
147
|
+
name: docker-build
|
|
148
|
+
needs: [integration-tests]
|
|
149
|
+
runs-on: ubuntu-latest
|
|
150
|
+
steps:
|
|
151
|
+
- name: Checkout repository
|
|
152
|
+
uses: actions/checkout@v6
|
|
153
|
+
with:
|
|
154
|
+
ref: ${{ github.ref }}
|
|
155
|
+
fetch-depth: 0
|
|
156
|
+
|
|
157
|
+
- name: Set up Docker Buildx
|
|
158
|
+
uses: docker/setup-buildx-action@v4
|
|
159
|
+
|
|
160
|
+
- name: Build and load Docker image
|
|
161
|
+
uses: docker/build-push-action@v7
|
|
162
|
+
with:
|
|
163
|
+
context: .
|
|
164
|
+
push: false
|
|
165
|
+
load: true
|
|
166
|
+
platforms: linux/amd64
|
|
167
|
+
tags: "${{ vars.DOCKER_IMAGE_NAME }}:${{ github.sha }}"
|
|
168
|
+
cache-from: type=gha,scope=docker-build
|
|
169
|
+
cache-to: type=gha,mode=max,scope=docker-build
|
|
170
|
+
|
|
171
|
+
- name: Smoke test Docker image
|
|
172
|
+
run: |
|
|
173
|
+
docker run --rm ${{ vars.DOCKER_IMAGE_NAME }}:${{ github.sha }} --help
|
|
174
|
+
|
|
175
|
+
- name: Run Trivy vulnerability scanner
|
|
176
|
+
uses: aquasecurity/trivy-action@0.35.0
|
|
177
|
+
with:
|
|
178
|
+
image-ref: "${{ vars.DOCKER_IMAGE_NAME }}:${{ github.sha }}"
|
|
179
|
+
format: "table"
|
|
180
|
+
exit-code: "0"
|
|
181
|
+
ignore-unfixed: true
|
|
182
|
+
vuln-type: "os,library"
|
|
183
|
+
severity: "CRITICAL,HIGH"
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
name: Docker Publish
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches:
|
|
6
|
+
- main
|
|
7
|
+
release:
|
|
8
|
+
types: [published]
|
|
9
|
+
workflow_dispatch:
|
|
10
|
+
|
|
11
|
+
concurrency:
|
|
12
|
+
group: ${{ github.workflow }}-${{ github.ref }}
|
|
13
|
+
cancel-in-progress: false
|
|
14
|
+
|
|
15
|
+
env:
|
|
16
|
+
DOCKER_HUB_REPO: docker.io/${{ vars.DOCKER_IMAGE_NAME }}
|
|
17
|
+
GHCR_REPO: ghcr.io/${{ vars.DOCKER_IMAGE_NAME }}
|
|
18
|
+
|
|
19
|
+
jobs:
|
|
20
|
+
build-and-push:
|
|
21
|
+
runs-on: ubuntu-latest
|
|
22
|
+
permissions:
|
|
23
|
+
actions: read
|
|
24
|
+
contents: read
|
|
25
|
+
packages: write
|
|
26
|
+
id-token: write
|
|
27
|
+
attestations: write
|
|
28
|
+
steps:
|
|
29
|
+
- name: Checkout repository
|
|
30
|
+
uses: actions/checkout@v6
|
|
31
|
+
with:
|
|
32
|
+
ref: ${{ github.ref }}
|
|
33
|
+
fetch-depth: 0
|
|
34
|
+
|
|
35
|
+
- name: Set up QEMU
|
|
36
|
+
uses: docker/setup-qemu-action@v4
|
|
37
|
+
|
|
38
|
+
- name: Set up Docker Buildx
|
|
39
|
+
uses: docker/setup-buildx-action@v4
|
|
40
|
+
|
|
41
|
+
- name: Log in to Docker Hub
|
|
42
|
+
uses: docker/login-action@v4
|
|
43
|
+
with:
|
|
44
|
+
username: ${{ vars.DOCKER_HUB_USERNAME }}
|
|
45
|
+
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
|
|
46
|
+
|
|
47
|
+
- name: Log in to GHCR
|
|
48
|
+
uses: docker/login-action@v4
|
|
49
|
+
with:
|
|
50
|
+
registry: ghcr.io
|
|
51
|
+
username: ${{ github.repository_owner }}
|
|
52
|
+
password: ${{ secrets.GHCR_PAT }}
|
|
53
|
+
|
|
54
|
+
- name: Extract Docker metadata
|
|
55
|
+
id: meta
|
|
56
|
+
uses: docker/metadata-action@v6
|
|
57
|
+
with:
|
|
58
|
+
images: |
|
|
59
|
+
${{ env.DOCKER_HUB_REPO }}
|
|
60
|
+
${{ env.GHCR_REPO }}
|
|
61
|
+
tags: |
|
|
62
|
+
# Always tag with short SHA
|
|
63
|
+
type=sha,format=short,prefix=
|
|
64
|
+
# Tag with 'edge' only for main branch
|
|
65
|
+
type=edge,branch=main
|
|
66
|
+
# Branch tag for all branches except main
|
|
67
|
+
type=ref,event=branch,enable=${{ github.ref_name != 'main' }}
|
|
68
|
+
# Semver tags for releases (includes 'latest')
|
|
69
|
+
type=semver,pattern=latest
|
|
70
|
+
type=semver,pattern={{version}}
|
|
71
|
+
type=semver,pattern={{major}}.{{minor}}
|
|
72
|
+
type=semver,pattern={{major}},enable=${{ !startsWith(github.ref_name, 'v0') }}
|
|
73
|
+
|
|
74
|
+
- name: Build and push Docker image
|
|
75
|
+
id: push
|
|
76
|
+
uses: docker/build-push-action@v7
|
|
77
|
+
with:
|
|
78
|
+
context: .
|
|
79
|
+
push: true
|
|
80
|
+
provenance: false
|
|
81
|
+
sbom: false
|
|
82
|
+
platforms: linux/amd64,linux/arm64
|
|
83
|
+
tags: ${{ steps.meta.outputs.tags }}
|
|
84
|
+
labels: ${{ steps.meta.outputs.labels }}
|
|
85
|
+
cache-from: type=gha,scope=docker-build
|
|
86
|
+
cache-to: type=gha,mode=max,scope=docker-build
|
|
87
|
+
|
|
88
|
+
- name: Generate artifact attestation (Docker Hub)
|
|
89
|
+
uses: actions/attest-build-provenance@v4
|
|
90
|
+
with:
|
|
91
|
+
subject-name: ${{ env.DOCKER_HUB_REPO }}
|
|
92
|
+
subject-digest: ${{ steps.push.outputs.digest }}
|
|
93
|
+
push-to-registry: true
|
|
94
|
+
|
|
95
|
+
- name: Generate SBOM
|
|
96
|
+
uses: anchore/sbom-action@v0
|
|
97
|
+
with:
|
|
98
|
+
path: ./
|
|
99
|
+
artifact-name: sbom.cyclonedx.json
|
|
100
|
+
output-file: sbom.cyclonedx.json
|
|
101
|
+
format: cyclonedx-json
|
|
102
|
+
|
|
103
|
+
- name: Attest SBOM (Docker Hub)
|
|
104
|
+
uses: actions/attest-sbom@v4
|
|
105
|
+
with:
|
|
106
|
+
subject-name: ${{ env.DOCKER_HUB_REPO }}
|
|
107
|
+
subject-digest: ${{ steps.push.outputs.digest }}
|
|
108
|
+
sbom-path: 'sbom.cyclonedx.json'
|
|
109
|
+
push-to-registry: true
|
|
@@ -7,15 +7,25 @@ on:
|
|
|
7
7
|
release:
|
|
8
8
|
types: [published]
|
|
9
9
|
|
|
10
|
+
concurrency:
|
|
11
|
+
group: ${{ github.workflow }}-${{ github.ref }}
|
|
12
|
+
cancel-in-progress: false
|
|
13
|
+
|
|
10
14
|
permissions:
|
|
11
15
|
contents: read
|
|
12
16
|
|
|
13
17
|
jobs:
|
|
14
18
|
release-build:
|
|
15
19
|
runs-on: ubuntu-latest
|
|
20
|
+
permissions:
|
|
21
|
+
contents: read
|
|
22
|
+
id-token: write
|
|
23
|
+
attestations: write
|
|
16
24
|
|
|
17
25
|
steps:
|
|
18
|
-
- uses: actions/checkout@
|
|
26
|
+
- uses: actions/checkout@v6
|
|
27
|
+
with:
|
|
28
|
+
fetch-depth: 0
|
|
19
29
|
|
|
20
30
|
- name: Set up uv
|
|
21
31
|
uses: astral-sh/setup-uv@v7
|
|
@@ -23,7 +33,12 @@ jobs:
|
|
|
23
33
|
enable-cache: true
|
|
24
34
|
|
|
25
35
|
- name: Build release distributions
|
|
26
|
-
run: uv build
|
|
36
|
+
run: uv build --wheel --sdist
|
|
37
|
+
|
|
38
|
+
- name: Generate artifact attestation
|
|
39
|
+
uses: actions/attest-build-provenance@v4
|
|
40
|
+
with:
|
|
41
|
+
subject-path: "dist/*"
|
|
27
42
|
|
|
28
43
|
- name: Upload distributions
|
|
29
44
|
uses: actions/upload-artifact@v7
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.14
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
cff-version: 1.2.0
|
|
2
|
-
title: PyStormTracker
|
|
2
|
+
title: mwyau/PyStormTracker
|
|
3
3
|
message: "If you use this software, please cite it as below."
|
|
4
4
|
type: software
|
|
5
5
|
authors:
|
|
@@ -19,8 +19,8 @@ keywords:
|
|
|
19
19
|
- dask
|
|
20
20
|
- mpi
|
|
21
21
|
license: BSD-3-Clause
|
|
22
|
-
version: 0.3.
|
|
23
|
-
date-released: '2026-03-
|
|
22
|
+
version: 0.3.3
|
|
23
|
+
date-released: '2026-03-10'
|
|
24
24
|
preferred-citation:
|
|
25
25
|
type: article
|
|
26
26
|
authors:
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# --- Build Stage ---
|
|
2
|
+
FROM python:3.13-slim AS builder
|
|
3
|
+
|
|
4
|
+
# Prevent uv from creating a virtualenv that might be hard to move
|
|
5
|
+
ENV UV_COMPILE_BYTECODE=1 \
|
|
6
|
+
UV_LINK_MODE=copy \
|
|
7
|
+
UV_PYTHON_DOWNLOADS=0
|
|
8
|
+
|
|
9
|
+
WORKDIR /app
|
|
10
|
+
|
|
11
|
+
# Install uv
|
|
12
|
+
COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/
|
|
13
|
+
|
|
14
|
+
# 1. Copy only dependency files for better layer caching.
|
|
15
|
+
COPY pyproject.toml uv.lock ./
|
|
16
|
+
|
|
17
|
+
# 2. Install third-party dependencies first (including grib extra).
|
|
18
|
+
# Use cache mount for uv to persist downloads and build artifacts.
|
|
19
|
+
RUN --mount=type=cache,target=/root/.cache/uv \
|
|
20
|
+
uv sync --frozen --no-dev --no-install-workspace --extra grib --no-editable
|
|
21
|
+
|
|
22
|
+
# 3. Copy only necessary source files for the final installation step.
|
|
23
|
+
COPY src/ ./src/
|
|
24
|
+
COPY README.md ./
|
|
25
|
+
|
|
26
|
+
# 4. Final installation of the project package itself.
|
|
27
|
+
RUN --mount=type=cache,target=/root/.cache/uv \
|
|
28
|
+
uv sync --frozen --no-dev --extra grib --no-editable
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
# --- Runtime Stage ---
|
|
32
|
+
FROM python:3.13-slim
|
|
33
|
+
|
|
34
|
+
WORKDIR /app
|
|
35
|
+
|
|
36
|
+
# Create data directory for mounting
|
|
37
|
+
RUN mkdir /data && chmod 777 /data
|
|
38
|
+
|
|
39
|
+
# Copy the environment from the builder
|
|
40
|
+
COPY --from=builder /app/.venv /app/.venv
|
|
41
|
+
|
|
42
|
+
# Ensure the virtualenv is used by default
|
|
43
|
+
ENV PATH="/app/.venv/bin:$PATH" \
|
|
44
|
+
PYTHONUNBUFFERED=1
|
|
45
|
+
|
|
46
|
+
# Add a non-root user for security
|
|
47
|
+
RUN useradd -m pst
|
|
48
|
+
USER pst
|
|
49
|
+
|
|
50
|
+
# Volume for data persistence
|
|
51
|
+
VOLUME /data
|
|
52
|
+
|
|
53
|
+
# Default command
|
|
54
|
+
ENTRYPOINT ["stormtracker"]
|
|
55
|
+
CMD ["--help"]
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: PyStormTracker
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.3
|
|
4
4
|
Summary: A Parallel Object-Oriented Cyclone Tracker in Python
|
|
5
|
-
Project-URL: Homepage, https://
|
|
5
|
+
Project-URL: Homepage, https://pypi.org/project/PyStormTracker/
|
|
6
6
|
Project-URL: Repository, https://github.com/mwyau/PyStormTracker.git
|
|
7
7
|
Project-URL: Issues, https://github.com/mwyau/PyStormTracker/issues
|
|
8
8
|
Author-email: "Albert M. W. Yau" <albert@mwyau.com>
|
|
@@ -18,21 +18,31 @@ Classifier: Programming Language :: Python :: 3.13
|
|
|
18
18
|
Classifier: Programming Language :: Python :: 3.14
|
|
19
19
|
Classifier: Topic :: Scientific/Engineering :: Atmospheric Science
|
|
20
20
|
Requires-Python: >=3.11
|
|
21
|
-
Requires-Dist: dask>=2024.
|
|
22
|
-
Requires-Dist: distributed>=2024.
|
|
21
|
+
Requires-Dist: dask>=2024.1.0
|
|
22
|
+
Requires-Dist: distributed>=2024.1.0
|
|
23
23
|
Requires-Dist: h5netcdf>=1.0.0
|
|
24
|
-
Requires-Dist: h5py>=3.
|
|
25
|
-
Requires-Dist: numpy>=1.
|
|
26
|
-
Requires-Dist: scipy>=1.9.
|
|
24
|
+
Requires-Dist: h5py>=3.8.0
|
|
25
|
+
Requires-Dist: numpy>=1.24.0
|
|
26
|
+
Requires-Dist: scipy>=1.9.2
|
|
27
27
|
Requires-Dist: xarray>=2024.9.0
|
|
28
|
+
Provides-Extra: all
|
|
29
|
+
Requires-Dist: cfgrib>=0.9.15.1; extra == 'all'
|
|
30
|
+
Requires-Dist: eccodes>=2.43.0; extra == 'all'
|
|
31
|
+
Requires-Dist: eccodeslib>=2.43.0; extra == 'all'
|
|
32
|
+
Requires-Dist: mpi4py>=4.1.0; extra == 'all'
|
|
33
|
+
Requires-Dist: netcdf4>=1.6.1; extra == 'all'
|
|
28
34
|
Provides-Extra: docs
|
|
29
|
-
Requires-Dist: myst-parser>=
|
|
30
|
-
Requires-Dist: sphinx-rtd-theme>=
|
|
31
|
-
Requires-Dist: sphinx>=
|
|
35
|
+
Requires-Dist: myst-parser>=5.0.0; extra == 'docs'
|
|
36
|
+
Requires-Dist: sphinx-rtd-theme>=3.1.0; extra == 'docs'
|
|
37
|
+
Requires-Dist: sphinx>=9.0.4; extra == 'docs'
|
|
32
38
|
Provides-Extra: grib
|
|
33
|
-
Requires-Dist: cfgrib>=0.9.15; extra == 'grib'
|
|
39
|
+
Requires-Dist: cfgrib>=0.9.15.1; extra == 'grib'
|
|
40
|
+
Requires-Dist: eccodes>=2.43.0; extra == 'grib'
|
|
41
|
+
Requires-Dist: eccodeslib>=2.43.0; extra == 'grib'
|
|
34
42
|
Provides-Extra: mpi
|
|
35
|
-
Requires-Dist: mpi4py>=4.
|
|
43
|
+
Requires-Dist: mpi4py>=4.1.0; extra == 'mpi'
|
|
44
|
+
Provides-Extra: netcdf4
|
|
45
|
+
Requires-Dist: netcdf4>=1.6.1; extra == 'netcdf4'
|
|
36
46
|
Description-Content-Type: text/markdown
|
|
37
47
|
|
|
38
48
|
# PyStormTracker
|
|
@@ -47,30 +57,29 @@ Description-Content-Type: text/markdown
|
|
|
47
57
|
[](https://github.com/orgs/xddd/packages/container/package/pystormtracker)
|
|
48
58
|
[](https://doi.org/10.5281/zenodo.18764813)
|
|
49
59
|
|
|
50
|
-
PyStormTracker
|
|
60
|
+
**PyStormTracker** is a Python package for cyclone trajectory analysis, implementing the "Simple Tracker" algorithm described in **Yau and Chang (2020)**. It is currently being expanded to include a Python port of the adaptive constraints tracking algorithm from **Hodges (1999)** (originally in C) and the Accumulated Track Activity metrics from **Yau and Chang (2020)** (originally in Matlab).
|
|
51
61
|
|
|
52
|
-
|
|
62
|
+
Initially developed at the **National Center for Atmospheric Research (NCAR)** as part of the **2015 SIParCS** program, PyStormTracker leverages task-parallel strategies and tree reduction algorithms to efficiently and accurately process large-scale climate datasets.
|
|
53
63
|
|
|
54
64
|
## Features
|
|
55
65
|
|
|
56
|
-
- **Modern
|
|
57
|
-
- **Xarray
|
|
58
|
-
- **
|
|
59
|
-
- **Dask (Default)**: Automatically scales to all available CPU cores
|
|
60
|
-
- **MPI**:
|
|
61
|
-
- **Serial**: Standard sequential execution for
|
|
62
|
-
- **Robust Detection**:
|
|
63
|
-
- **
|
|
64
|
-
- **Standardized Output**: Results are exported to the IMILAST intercomparison format (.txt) with readable datetime strings and formatted numeric values.
|
|
66
|
+
- **Modern & Typed**: Strictly targets **Python 3.11+** with complete type hints and strict `mypy` compliance.
|
|
67
|
+
- **Xarray Native**: Leverages `xarray` for robust, high-performance coordinate-aware processing, lazy data loading, and optimized I/O.
|
|
68
|
+
- **Scalable Execution**: Supports multiple backends:
|
|
69
|
+
- **Dask (Default)**: Automatically scales to utilize all available CPU cores.
|
|
70
|
+
- **MPI**: Enables distributed execution across cluster nodes via `mpi4py`.
|
|
71
|
+
- **Serial**: Standard sequential execution for debugging or small datasets.
|
|
72
|
+
- **Robust Feature Detection**: Employs optimized $O(N)$ extrema filtering with robust handling of masked or missing data.
|
|
73
|
+
- **Interoperable Output**: Exports tracking results to the standard IMILAST intercomparison format (`.txt`) with human-readable datetime strings.
|
|
65
74
|
|
|
66
75
|
## Technical Methodology
|
|
67
76
|
|
|
68
|
-
PyStormTracker treats meteorological fields as 2D images
|
|
77
|
+
PyStormTracker treats meteorological fields as 2D images, utilizing `scipy.ndimage` for robust feature detection and tracking:
|
|
69
78
|
|
|
70
|
-
- **Local Extrema Detection**:
|
|
71
|
-
- **Intensity & Refinement**: Applies the **Laplacian operator**
|
|
72
|
-
- **Spherical Continuity**:
|
|
73
|
-
- **
|
|
79
|
+
- **Local Extrema Detection**: Employs an optimized sliding window filter to efficiently identify local minima (e.g., cyclones) or maxima (e.g., anticyclones, vorticity).
|
|
80
|
+
- **Intensity & Refinement**: Applies the discrete **Laplacian operator** to measure the "sharpness" of the field at each candidate center. This metric resolves duplicate detections, ensuring only the most physically intense point is retained when adjacent pixels are flagged.
|
|
81
|
+
- **Spherical Continuity**: Uses `mode='wrap'` for all spatial filters to correctly handle periodic boundary conditions across the Prime Meridian, allowing for seamless global tracking.
|
|
82
|
+
- **Trajectory Linking**: Connects detected centers across consecutive time steps into continuous trajectories using a nearest-neighbor heuristic linking strategy.
|
|
74
83
|
|
|
75
84
|
## Documentation
|
|
76
85
|
|
|
@@ -81,36 +90,39 @@ Full documentation, including API references and advanced usage examples, is ava
|
|
|
81
90
|
### Prerequisites
|
|
82
91
|
- Python 3.11+
|
|
83
92
|
- (Optional) OpenMPI for MPI support.
|
|
93
|
+
- **Windows Users**: Note that the `grib` optional dependency (via `eccodeslib`) currently only supports Linux and macOS.
|
|
84
94
|
|
|
85
95
|
### From PyPI (Recommended)
|
|
86
96
|
You can install the latest stable version of PyStormTracker directly from PyPI:
|
|
87
97
|
|
|
88
|
-
|
|
98
|
+
Using `pip` (standard):
|
|
89
99
|
```bash
|
|
90
|
-
|
|
100
|
+
pip install PyStormTracker
|
|
91
101
|
```
|
|
92
102
|
|
|
93
|
-
|
|
103
|
+
Using `uv` (recommended):
|
|
94
104
|
```bash
|
|
105
|
+
# For use as a CLI tool
|
|
106
|
+
uv tool install PyStormTracker
|
|
107
|
+
|
|
108
|
+
# For use as a library in your project
|
|
95
109
|
uv add PyStormTracker
|
|
96
110
|
```
|
|
97
111
|
|
|
98
112
|
### From Source
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
```
|
|
106
|
-
|
|
113
|
+
Install with `uv` (Recommended):
|
|
114
|
+
```bash
|
|
115
|
+
git clone https://github.com/mwyau/PyStormTracker.git
|
|
116
|
+
cd PyStormTracker
|
|
117
|
+
uv sync
|
|
118
|
+
```
|
|
107
119
|
|
|
108
120
|
## Usage
|
|
109
121
|
|
|
110
122
|
Once installed, you can use the `stormtracker` command directly:
|
|
111
123
|
|
|
112
124
|
```bash
|
|
113
|
-
stormtracker -i
|
|
125
|
+
stormtracker -i era5_msl_2025-2026_djf_2.5x2.5.nc -v msl -o my_tracks
|
|
114
126
|
```
|
|
115
127
|
|
|
116
128
|
### Command Line Arguments
|
|
@@ -128,17 +140,14 @@ stormtracker -i era5_msl_2.5x2.5.nc -v msl -o my_tracks
|
|
|
128
140
|
## Development
|
|
129
141
|
|
|
130
142
|
### Setup
|
|
131
|
-
Using
|
|
143
|
+
Using `uv` is the recommended way to set up your environment:
|
|
132
144
|
```bash
|
|
133
145
|
# Install dependencies and sync virtual environment
|
|
134
|
-
uv sync
|
|
135
|
-
|
|
136
|
-
# Install pre-push hooks
|
|
137
|
-
uv run pre-commit install --hook-type pre-push
|
|
146
|
+
uv sync
|
|
138
147
|
```
|
|
139
148
|
|
|
140
149
|
### Quality Control
|
|
141
|
-
Run automated checks using
|
|
150
|
+
Run automated checks using `uv run`:
|
|
142
151
|
|
|
143
152
|
**Linting & Formatting:**
|
|
144
153
|
```bash
|
|
@@ -155,7 +164,7 @@ uv run mypy src/
|
|
|
155
164
|
To keep development cycles fast, testing is tiered:
|
|
156
165
|
- **Fast Tests**: Default local runs (skips integration tests).
|
|
157
166
|
- **Integration Tests**: ONLY long-running integration/regression tests.
|
|
158
|
-
- **Full Suite**: Everything
|
|
167
|
+
- **Full Suite**: Everything.
|
|
159
168
|
|
|
160
169
|
**Run fast unit tests only (Default):**
|
|
161
170
|
```bash
|
|
@@ -167,7 +176,6 @@ uv run pytest
|
|
|
167
176
|
uv run pytest --run-integration
|
|
168
177
|
```
|
|
169
178
|
|
|
170
|
-
|
|
171
179
|
**Run everything:**
|
|
172
180
|
```bash
|
|
173
181
|
uv run pytest --run-all
|
|
@@ -177,21 +185,20 @@ uv run pytest --run-all
|
|
|
177
185
|
|
|
178
186
|
If you use this software in your research, please cite the following:
|
|
179
187
|
|
|
180
|
-
- **Yau, A. M. W.**, 2026: mwyau/PyStormTracker. *Zenodo*, https://doi.org/10.5281/zenodo.18764813.
|
|
188
|
+
- **Yau, A. M. W.**, 2026: mwyau/PyStormTracker. *Zenodo*, [https://doi.org/10.5281/zenodo.18764813](https://doi.org/10.5281/zenodo.18764813).
|
|
181
189
|
|
|
182
|
-
- **Yau, A. M. W., and E. K. M. Chang**, 2020: Finding Storm Track Activity Metrics That Are Highly Correlated with Weather Impacts. Part I: Frameworks for Evaluation and Accumulated Track Activity. *J. Climate*, **33**, 10169–10186, https://doi.org/10.1175/JCLI-D-20-0393.1.
|
|
190
|
+
- **Yau, A. M. W., and E. K. M. Chang**, 2020: Finding Storm Track Activity Metrics That Are Highly Correlated with Weather Impacts. Part I: Frameworks for Evaluation and Accumulated Track Activity. *J. Climate*, **33**, 10169–10186, [https://doi.org/10.1175/JCLI-D-20-0393.1](https://doi.org/10.1175/JCLI-D-20-0393.1).
|
|
183
191
|
|
|
184
192
|
## References
|
|
185
193
|
|
|
186
|
-
- **Yau, A. M. W., K. Paul and J. Dennis**, 2016: PyStormTracker: A Parallel Object-Oriented Cyclone Tracker in Python. *96th American Meteorological Society Annual Meeting*, New Orleans, LA. *Zenodo*, https://doi.org/10.5281/zenodo.18868625.
|
|
194
|
+
- **Yau, A. M. W., K. Paul and J. Dennis**, 2016: PyStormTracker: A Parallel Object-Oriented Cyclone Tracker in Python. *96th American Meteorological Society Annual Meeting*, New Orleans, LA. *Zenodo*, [https://doi.org/10.5281/zenodo.18868625](https://doi.org/10.5281/zenodo.18868625).
|
|
187
195
|
|
|
188
|
-
- **Neu, U., et al.**, 2013: IMILAST: A Community Effort to Intercompare Extratropical Cyclone Detection and Tracking Algorithms. *Bull. Amer. Meteor. Soc.*, **94**, 529–547, https://doi.org/10.1175/BAMS-D-11-00154.1.
|
|
189
|
-
- **IMILAST Intercomparison Protocol**: https://proclim.scnat.ch/en/activities/project_imilast/intercomparison
|
|
190
|
-
- **IMILAST Data Download**: https://proclim.scnat.ch/en/activities/project_imilast/data_download
|
|
196
|
+
- **Neu, U., et al.**, 2013: IMILAST: A Community Effort to Intercompare Extratropical Cyclone Detection and Tracking Algorithms. *Bull. Amer. Meteor. Soc.*, **94**, 529–547, [https://doi.org/10.1175/BAMS-D-11-00154.1](https://doi.org/10.1175/BAMS-D-11-00154.1).
|
|
197
|
+
- **IMILAST Intercomparison Protocol**: [https://proclim.scnat.ch/en/activities/project_imilast/intercomparison](https://proclim.scnat.ch/en/activities/project_imilast/intercomparison)
|
|
198
|
+
- **IMILAST Data Download**: [https://proclim.scnat.ch/en/activities/project_imilast/data_download](https://proclim.scnat.ch/en/activities/project_imilast/data_download)
|
|
191
199
|
|
|
192
200
|
- **Hodges, K. I.**, 1999: Adaptive Constraints for Feature Tracking. *Mon. Wea. Rev.*, **127**, 1362–1373, [https://doi.org/10.1175/1520-0493(1999)127<1362:ACFFT>2.0.CO;2](https://doi.org/10.1175/1520-0493(1999)127<1362:ACFFT>2.0.CO;2).
|
|
193
201
|
|
|
194
|
-
|
|
195
202
|
## License
|
|
196
203
|
|
|
197
204
|
This project is licensed under the BSD-3-Clause terms found in the `LICENSE` file.
|