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.
Files changed (43) hide show
  1. pystormtracker-0.3.3/.github/workflows/ci.yml +183 -0
  2. pystormtracker-0.3.3/.github/workflows/docker-publish.yml +109 -0
  3. {pystormtracker-0.3.1 → pystormtracker-0.3.3}/.github/workflows/python-publish.yml +17 -2
  4. pystormtracker-0.3.3/.python-version +1 -0
  5. {pystormtracker-0.3.1 → pystormtracker-0.3.3}/CITATION.cff +3 -3
  6. pystormtracker-0.3.3/Dockerfile +55 -0
  7. {pystormtracker-0.3.1 → pystormtracker-0.3.3}/PKG-INFO +62 -55
  8. {pystormtracker-0.3.1 → pystormtracker-0.3.3}/README.md +40 -43
  9. {pystormtracker-0.3.1 → pystormtracker-0.3.3}/pyproject.toml +38 -20
  10. pystormtracker-0.3.3/src/pystormtracker/__init__.py +11 -0
  11. {pystormtracker-0.3.1 → pystormtracker-0.3.3}/src/pystormtracker/simple/detector.py +3 -0
  12. {pystormtracker-0.3.1 → pystormtracker-0.3.3}/uv.lock +282 -120
  13. pystormtracker-0.3.1/.github/workflows/ci.yml +0 -119
  14. pystormtracker-0.3.1/.github/workflows/docker-publish.yml +0 -88
  15. pystormtracker-0.3.1/.pre-commit-config.yaml +0 -23
  16. pystormtracker-0.3.1/Dockerfile +0 -35
  17. pystormtracker-0.3.1/src/pystormtracker/__init__.py +0 -4
  18. {pystormtracker-0.3.1 → pystormtracker-0.3.3}/.github/dependabot.yml +0 -0
  19. {pystormtracker-0.3.1 → pystormtracker-0.3.3}/.gitignore +0 -0
  20. {pystormtracker-0.3.1 → pystormtracker-0.3.3}/.readthedocs.yaml +0 -0
  21. {pystormtracker-0.3.1 → pystormtracker-0.3.3}/LICENSE +0 -0
  22. {pystormtracker-0.3.1 → pystormtracker-0.3.3}/codecov.yml +0 -0
  23. {pystormtracker-0.3.1 → pystormtracker-0.3.3}/data/test/tracks/era5_msl_2.5x2.5_v0.0.2_imilast.txt +0 -0
  24. {pystormtracker-0.3.1 → pystormtracker-0.3.3}/docs/IntercomparisonProtocol.pdf +0 -0
  25. {pystormtracker-0.3.1 → pystormtracker-0.3.3}/docs/conf.py +0 -0
  26. {pystormtracker-0.3.1 → pystormtracker-0.3.3}/docs/index.md +0 -0
  27. {pystormtracker-0.3.1 → pystormtracker-0.3.3}/src/pystormtracker/models/__init__.py +0 -0
  28. {pystormtracker-0.3.1 → pystormtracker-0.3.3}/src/pystormtracker/models/center.py +0 -0
  29. {pystormtracker-0.3.1 → pystormtracker-0.3.3}/src/pystormtracker/models/grid.py +0 -0
  30. {pystormtracker-0.3.1 → pystormtracker-0.3.3}/src/pystormtracker/models/time.py +0 -0
  31. {pystormtracker-0.3.1 → pystormtracker-0.3.3}/src/pystormtracker/models/tracks.py +0 -0
  32. {pystormtracker-0.3.1 → pystormtracker-0.3.3}/src/pystormtracker/simple/__init__.py +0 -0
  33. {pystormtracker-0.3.1 → pystormtracker-0.3.3}/src/pystormtracker/simple/linker.py +0 -0
  34. {pystormtracker-0.3.1 → pystormtracker-0.3.3}/src/pystormtracker/stormtracker.py +0 -0
  35. {pystormtracker-0.3.1 → pystormtracker-0.3.3}/tests/__init__.py +0 -0
  36. {pystormtracker-0.3.1 → pystormtracker-0.3.3}/tests/conftest.py +0 -0
  37. {pystormtracker-0.3.1 → pystormtracker-0.3.3}/tests/data_utils.py +0 -0
  38. {pystormtracker-0.3.1 → pystormtracker-0.3.3}/tests/test_center.py +0 -0
  39. {pystormtracker-0.3.1 → pystormtracker-0.3.3}/tests/test_integration.py +0 -0
  40. {pystormtracker-0.3.1 → pystormtracker-0.3.3}/tests/test_simple_detector.py +0 -0
  41. {pystormtracker-0.3.1 → pystormtracker-0.3.3}/tests/test_simple_linker.py +0 -0
  42. {pystormtracker-0.3.1 → pystormtracker-0.3.3}/tests/test_stormtracker.py +0 -0
  43. {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@v4
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.1
23
- date-released: '2026-03-08'
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.1
3
+ Version: 0.3.3
4
4
  Summary: A Parallel Object-Oriented Cyclone Tracker in Python
5
- Project-URL: Homepage, https://pystormtracker.readthedocs.io/
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.0.0
22
- Requires-Dist: distributed>=2024.0.0
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.0.0
25
- Requires-Dist: numpy>=1.23.0
26
- Requires-Dist: scipy>=1.9.0
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>=2.0.0; extra == 'docs'
30
- Requires-Dist: sphinx-rtd-theme>=2.0.0; extra == 'docs'
31
- Requires-Dist: sphinx>=7.2.0; extra == 'docs'
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.0.0; extra == 'mpi'
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
  [![GHCR](https://img.shields.io/badge/ghcr.io-xddd%2Fpystormtracker-blue?logo=github)](https://github.com/orgs/xddd/packages/container/package/pystormtracker)
48
58
  [![DOI](https://zenodo.org/badge/36328800.svg)](https://doi.org/10.5281/zenodo.18764813)
49
59
 
50
- PyStormTracker implements the "Simple Tracker" algorithm for cyclone trajectory analysis in **Yau and Chang (2020)**. Work is currently in progress to port the adaptive constraints tracking algorithm in **Hodges (1999)** from C to Python, and the Accumulated Track Activity metrics in **Yau and Chang (2020)** from Matlab to Python.
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
- Originally developed at the **National Center for Atmospheric Research (NCAR)** during the **2015 SIParCS** program, this package utilizes task-parallel strategies and tree reduction algorithms to efficiently process large climate datasets.
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 Python Support**: Strictly targets **Python 3.11+** with comprehensive type hints and 100% strict `mypy` compliance.
57
- - **Xarray Integrated**: Leverages `xarray` with optimized I/O, acceleration, and parallel extras for robust, high-performance coordinate-aware processing and lazy data loading.
58
- - **Parallel Backends**:
59
- - **Dask (Default)**: Automatically scales to all available CPU cores on local machines.
60
- - **MPI**: Supports distributed execution via `mpi4py`.
61
- - **Serial**: Standard sequential execution for smaller datasets or debugging.
62
- - **Robust Detection**: Optimized $O(N)$ extrema filtering and robust handling of masked/missing data.
63
- - **CI/CD Integrated**: Automated linting, type-checking, and code coverage reporting via GitHub Actions.
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 and leverages `scipy.ndimage` for robust feature detection:
77
+ PyStormTracker treats meteorological fields as 2D images, utilizing `scipy.ndimage` for robust feature detection and tracking:
69
78
 
70
- - **Local Extrema Detection**: Uses an optimized sliding window filter to identify local minima (cyclones) or maxima (anticyclones/vorticity).
71
- - **Intensity & Refinement**: Applies the **Laplacian operator** (`laplace`) to measure the "sharpness" of the field at each detected center point. This is used to resolve duplicates and ensure only the most physically intense point is kept when multiple adjacent pixels are flagged.
72
- - **Spherical Continuity**: Utilizes `mode='wrap'` for all filters to correctly handle periodic boundaries across the Prime Meridian, enabling seamless tracking across the entire globe.
73
- - **Heuristic Linking**: Implements a nearest-neighbor linking strategy to connect detected centers into trajectories across successive time steps.
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
- For use as a CLI tool:
98
+ Using `pip` (standard):
89
99
  ```bash
90
- uv tool install PyStormTracker
100
+ pip install PyStormTracker
91
101
  ```
92
102
 
93
- For use as a library in your project:
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
- 2. Install with **uv** (Recommended):
100
- ```bash
101
- git clone https://github.com/mwyau/PyStormTracker.git
102
- cd PyStormTracker
103
- uv sync --group dev
104
- uv run pre-commit install --hook-type pre-push
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 era5_msl_2.5x2.5.nc -v msl -o my_tracks
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 **uv** is the recommended way to set up your environment:
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 --group dev
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 **uv run**:
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 (used in CI).
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.