PyStormTracker 0.2.2__tar.gz → 0.3.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. pystormtracker-0.3.0/.gitattributes +0 -0
  2. pystormtracker-0.3.0/.github/dependabot.yml +16 -0
  3. pystormtracker-0.3.0/.github/workflows/ci.yml +126 -0
  4. pystormtracker-0.3.0/.github/workflows/docker-publish.yml +70 -0
  5. {pystormtracker-0.2.2 → pystormtracker-0.3.0}/.github/workflows/python-publish.yml +3 -3
  6. {pystormtracker-0.2.2 → pystormtracker-0.3.0}/.gitignore +6 -3
  7. {pystormtracker-0.2.2 → pystormtracker-0.3.0}/.pre-commit-config.yaml +4 -6
  8. pystormtracker-0.3.0/.readthedocs.yaml +19 -0
  9. {pystormtracker-0.2.2 → pystormtracker-0.3.0}/CITATION.cff +2 -2
  10. pystormtracker-0.3.0/Dockerfile +32 -0
  11. {pystormtracker-0.2.2 → pystormtracker-0.3.0}/PKG-INFO +78 -38
  12. pystormtracker-0.3.0/README.md +149 -0
  13. pystormtracker-0.3.0/codecov.yml +13 -0
  14. pystormtracker-0.3.0/data/test/tracks/era5_msl_2.5x2.5_v0.0.2_imilast.txt +38464 -0
  15. pystormtracker-0.3.0/docs/IntercomparisonProtocol.pdf +0 -0
  16. pystormtracker-0.3.0/docs/conf.py +29 -0
  17. pystormtracker-0.3.0/docs/index.md +21 -0
  18. {pystormtracker-0.2.2 → pystormtracker-0.3.0}/pyproject.toml +60 -20
  19. pystormtracker-0.3.0/src/pystormtracker/data.py +66 -0
  20. pystormtracker-0.3.0/src/pystormtracker/models/__init__.py +6 -0
  21. {pystormtracker-0.2.2 → pystormtracker-0.3.0}/src/pystormtracker/models/center.py +25 -23
  22. pystormtracker-0.3.0/src/pystormtracker/models/grid.py +36 -0
  23. pystormtracker-0.3.0/src/pystormtracker/models/time.py +14 -0
  24. pystormtracker-0.3.0/src/pystormtracker/models/tracks.py +189 -0
  25. pystormtracker-0.3.0/src/pystormtracker/simple/detector.py +271 -0
  26. pystormtracker-0.3.0/src/pystormtracker/simple/linker.py +135 -0
  27. {pystormtracker-0.2.2 → pystormtracker-0.3.0}/src/pystormtracker/stormtracker.py +84 -84
  28. pystormtracker-0.3.0/tests/conftest.py +30 -0
  29. pystormtracker-0.3.0/tests/test_center.py +52 -0
  30. pystormtracker-0.3.0/tests/test_integration.py +197 -0
  31. pystormtracker-0.3.0/tests/test_simple_detector.py +49 -0
  32. pystormtracker-0.3.0/tests/test_simple_linker.py +54 -0
  33. pystormtracker-0.3.0/tests/test_stormtracker.py +94 -0
  34. pystormtracker-0.3.0/tests/test_tracks.py +124 -0
  35. pystormtracker-0.3.0/uv.lock +2565 -0
  36. pystormtracker-0.2.2/.gitattributes +0 -1
  37. pystormtracker-0.2.2/README.md +0 -111
  38. pystormtracker-0.2.2/notebooks/detect_center_latlon.ipynb +0 -149
  39. pystormtracker-0.2.2/notebooks/detect_center_latlon_img_0.png +0 -0
  40. pystormtracker-0.2.2/notebooks/detect_center_latlon_img_1.png +0 -0
  41. pystormtracker-0.2.2/notebooks/plot_tracks.ipynb +0 -117
  42. pystormtracker-0.2.2/notebooks/plot_tracks_img_0.png +0 -0
  43. pystormtracker-0.2.2/scripts/run_cesm.sh +0 -6
  44. pystormtracker-0.2.2/scripts/run_cesm_yellowstone.sh +0 -18
  45. pystormtracker-0.2.2/scripts/run_erai_msl.sh +0 -8
  46. pystormtracker-0.2.2/scripts/run_erai_vo.sh +0 -8
  47. pystormtracker-0.2.2/scripts/run_ncep.sh +0 -6
  48. pystormtracker-0.2.2/src/pystormtracker/models/__init__.py +0 -5
  49. pystormtracker-0.2.2/src/pystormtracker/models/grid.py +0 -40
  50. pystormtracker-0.2.2/src/pystormtracker/models/tracks.py +0 -29
  51. pystormtracker-0.2.2/src/pystormtracker/simple/detector.py +0 -315
  52. pystormtracker-0.2.2/src/pystormtracker/simple/linker.py +0 -104
  53. pystormtracker-0.2.2/tests/test_center.py +0 -48
  54. pystormtracker-0.2.2/tests/test_integration.py +0 -84
  55. pystormtracker-0.2.2/tests/test_simple_detector.py +0 -60
  56. pystormtracker-0.2.2/tests/test_simple_linker.py +0 -85
  57. pystormtracker-0.2.2/tests/test_tracks.py +0 -39
  58. {pystormtracker-0.2.2 → pystormtracker-0.3.0}/LICENSE +0 -0
  59. {pystormtracker-0.2.2 → pystormtracker-0.3.0}/src/pystormtracker/__init__.py +0 -0
  60. {pystormtracker-0.2.2 → pystormtracker-0.3.0}/src/pystormtracker/simple/__init__.py +0 -0
  61. {pystormtracker-0.2.2 → pystormtracker-0.3.0}/tests/__init__.py +0 -0
File without changes
@@ -0,0 +1,16 @@
1
+ # To get started with Dependabot version updates, you'll need to specify which
2
+ # package ecosystems to update and where the package manifests are located.
3
+ # Please see the documentation for all configuration options:
4
+ # https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
5
+
6
+ version: 2
7
+ updates:
8
+ - package-ecosystem: "pip"
9
+ directory: "/"
10
+ schedule:
11
+ interval: "daily"
12
+
13
+ - package-ecosystem: "github-actions"
14
+ directory: "/"
15
+ schedule:
16
+ interval: "daily"
@@ -0,0 +1,126 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main, dev]
6
+ pull_request:
7
+ branches: [main, dev]
8
+ types: [opened, synchronize, reopened]
9
+ workflow_dispatch:
10
+
11
+ permissions:
12
+ contents: read
13
+
14
+ jobs:
15
+ prepare:
16
+ runs-on: ubuntu-latest
17
+ # Skip PR CI if the head branch is 'dev' to avoid double runs (push trigger covers it)
18
+ if: github.event_name == 'push' || github.event.pull_request.head.ref != 'dev'
19
+ outputs:
20
+ python-versions: ${{ steps.versions.outputs.value }}
21
+ steps:
22
+ - id: versions
23
+ run: echo 'value=["3.11", "3.12", "3.13", "3.14"]' >> $GITHUB_OUTPUT
24
+
25
+ ruff-lint:
26
+ runs-on: ubuntu-latest
27
+ if: github.event_name == 'push' || github.event.pull_request.head.ref != 'dev'
28
+ steps:
29
+ - uses: actions/checkout@v4
30
+ - name: Set up Python
31
+ uses: actions/setup-python@v6
32
+ with:
33
+ python-version: "3.11"
34
+ cache: "pip"
35
+ - name: Install dependencies
36
+ run: pip install ruff
37
+ - name: Lint with Ruff
38
+ run: ruff check .
39
+
40
+ ruff-format:
41
+ runs-on: ubuntu-latest
42
+ if: github.event_name == 'push' || github.event.pull_request.head.ref != 'dev'
43
+ steps:
44
+ - uses: actions/checkout@v4
45
+ - name: Set up Python
46
+ uses: actions/setup-python@v6
47
+ with:
48
+ python-version: "3.11"
49
+ cache: "pip"
50
+ - name: Install dependencies
51
+ run: pip install ruff
52
+ - name: Check formatting with Ruff
53
+ run: ruff format --check .
54
+
55
+ mypy-typecheck:
56
+ runs-on: ubuntu-latest
57
+ if: github.event_name == 'push' || github.event.pull_request.head.ref != 'dev'
58
+ steps:
59
+ - uses: actions/checkout@v4
60
+ - name: Set up Python
61
+ uses: actions/setup-python@v6
62
+ with:
63
+ python-version: "3.11"
64
+ cache: "pip"
65
+ - name: Install dependencies
66
+ run: |
67
+ python -m pip install --upgrade pip
68
+ pip install .[dev]
69
+ - name: Type check with Mypy
70
+ run: mypy src/ tests/
71
+
72
+ unit-tests:
73
+ name: unit-tests (Python ${{ matrix.python-version }})
74
+ needs: prepare
75
+ runs-on: ubuntu-latest
76
+ strategy:
77
+ matrix:
78
+ python-version: ${{ fromJSON(needs.prepare.outputs.python-versions) }}
79
+ steps:
80
+ - uses: actions/checkout@v4
81
+ - name: Set up Python ${{ matrix.python-version }}
82
+ uses: actions/setup-python@v6
83
+ with:
84
+ python-version: ${{ matrix.python-version }}
85
+ cache: "pip"
86
+ - name: Install dependencies
87
+ run: |
88
+ python -m pip install --upgrade pip
89
+ pip install .[dev]
90
+ - name: Run Unit Tests
91
+ run: |
92
+ pytest -vv
93
+ - name: Upload coverage reports to Codecov
94
+ uses: codecov/codecov-action@v5
95
+ with:
96
+ token: ${{ secrets.CODECOV_TOKEN }}
97
+
98
+ integration-tests:
99
+ name: integration-tests (Python ${{ matrix.python-version }})
100
+ needs: prepare
101
+ runs-on: ubuntu-latest
102
+ strategy:
103
+ matrix:
104
+ python-version: ${{ fromJSON(needs.prepare.outputs.python-versions) }}
105
+ steps:
106
+ - uses: actions/checkout@v4
107
+ - name: Set up Python ${{ matrix.python-version }}
108
+ uses: actions/setup-python@v6
109
+ with:
110
+ python-version: ${{ matrix.python-version }}
111
+ cache: "pip"
112
+ - name: Install MPI
113
+ run: |
114
+ sudo apt-get update
115
+ sudo apt-get install -y openmpi-bin libopenmpi-dev
116
+ - name: Install dependencies
117
+ run: |
118
+ python -m pip install --upgrade pip
119
+ pip install .[dev]
120
+ - name: Run Integration Tests
121
+ run: |
122
+ pytest -vv tests/test_integration.py --run-all
123
+ - name: Upload coverage reports to Codecov
124
+ uses: codecov/codecov-action@v5
125
+ with:
126
+ token: ${{ secrets.CODECOV_TOKEN }}
@@ -0,0 +1,70 @@
1
+ name: Docker Publish
2
+
3
+ on:
4
+ workflow_run:
5
+ workflows: ["CI"]
6
+ types: [completed]
7
+
8
+ env:
9
+ DOCKER_HUB_REPO: xddd/pystormtracker
10
+ GHCR_REPO: ghcr.io/xddd/pystormtracker
11
+
12
+ jobs:
13
+ build-and-push:
14
+ runs-on: ubuntu-latest
15
+ permissions:
16
+ contents: read
17
+ packages: write
18
+ # Only run if CI was successful AND it was a push event to main or dev
19
+ # Added head_repository check to prevent checkout of untrusted code in trusted context
20
+ if: >
21
+ github.event.workflow_run.conclusion == 'success' &&
22
+ github.event.workflow_run.head_repository.full_name == github.repository &&
23
+ (github.event.workflow_run.event == 'push' || github.event.workflow_run.event == 'workflow_dispatch') &&
24
+ (github.event.workflow_run.head_branch == 'main' || github.event.workflow_run.head_branch == 'dev')
25
+
26
+ steps:
27
+ - name: Checkout repository
28
+ # Use the commit from the completed workflow run
29
+ uses: actions/checkout@v4
30
+ with:
31
+ ref: ${{ github.event.workflow_run.head_sha }}
32
+
33
+ - name: Set up Docker Buildx
34
+ uses: docker/setup-buildx-action@v4
35
+
36
+ - name: Log in to Docker Hub
37
+ uses: docker/login-action@v4
38
+ with:
39
+ username: ${{ vars.DOCKER_HUB_USERNAME }}
40
+ password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
41
+
42
+ - name: Log in to GHCR
43
+ uses: docker/login-action@v4
44
+ with:
45
+ registry: ghcr.io
46
+ username: ${{ github.actor }}
47
+ password: ${{ secrets.GHCR_PAT }}
48
+
49
+ - name: Extract Docker metadata
50
+ id: meta
51
+ uses: docker/metadata-action@v5
52
+ with:
53
+ images: |
54
+ ${{ env.DOCKER_HUB_REPO }}
55
+ ${{ env.GHCR_REPO }}
56
+ tags: |
57
+ type=raw,value=dev,pattern=dev
58
+ type=raw,value=latest,pattern=main
59
+ type=semver,pattern={{version}},pattern=main
60
+ type=semver,pattern={{major}}.{{minor}},pattern=main
61
+
62
+ - name: Build and push Docker image
63
+ uses: docker/build-push-action@v7
64
+ with:
65
+ context: .
66
+ push: true
67
+ tags: ${{ steps.meta.outputs.tags }}
68
+ labels: ${{ steps.meta.outputs.labels }}
69
+ cache-from: type=gha
70
+ cache-to: type=gha,mode=max
@@ -17,7 +17,7 @@ jobs:
17
17
  steps:
18
18
  - uses: actions/checkout@v4
19
19
 
20
- - uses: actions/setup-python@v5
20
+ - uses: actions/setup-python@v6
21
21
  with:
22
22
  python-version: "3.x"
23
23
 
@@ -27,7 +27,7 @@ jobs:
27
27
  python -m build
28
28
 
29
29
  - name: Upload distributions
30
- uses: actions/upload-artifact@v4
30
+ uses: actions/upload-artifact@v7
31
31
  with:
32
32
  name: release-dists
33
33
  path: dist/
@@ -54,7 +54,7 @@ jobs:
54
54
  echo "version=${VERSION#v}" >> $GITHUB_OUTPUT
55
55
 
56
56
  - name: Retrieve release distributions
57
- uses: actions/download-artifact@v4
57
+ uses: actions/download-artifact@v8
58
58
  with:
59
59
  name: release-dists
60
60
  path: dist/
@@ -64,7 +64,10 @@ target/
64
64
 
65
65
  # netCDF files
66
66
  *.nc
67
- !data/test/*.nc
68
67
 
69
- # CSV files
70
- *.csv
68
+ # TXT track files
69
+ *.txt
70
+ !data/test/tracks/*.txt
71
+
72
+ # VSCode
73
+ .vscode/
@@ -1,6 +1,6 @@
1
1
  repos:
2
2
  - repo: https://github.com/pre-commit/pre-commit-hooks
3
- rev: v4.5.0
3
+ rev: v5.0.0
4
4
  hooks:
5
5
  - id: trailing-whitespace
6
6
  - id: end-of-file-fixer
@@ -9,16 +9,14 @@ repos:
9
9
  - id: debug-statements
10
10
 
11
11
  - repo: https://github.com/astral-sh/ruff-pre-commit
12
- rev: v0.3.4
12
+ rev: v0.9.9
13
13
  hooks:
14
- # Run the linter.
15
14
  - id: ruff
16
15
  args: [ --fix ]
17
- # Run the formatter.
18
16
  - id: ruff-format
19
17
 
20
18
  - repo: https://github.com/pre-commit/mirrors-mypy
21
- rev: v1.9.0
19
+ rev: v1.15.0
22
20
  hooks:
23
21
  - id: mypy
24
- additional_dependencies: [ types-all ]
22
+ additional_dependencies: [numpy, pooch, scipy, pytest, pandas, xarray, dask, distributed, h5netcdf]
@@ -0,0 +1,19 @@
1
+ # Read the Docs configuration file
2
+ # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
3
+
4
+ version: 2
5
+
6
+ build:
7
+ os: ubuntu-24.04
8
+ tools:
9
+ python: "3.14"
10
+
11
+ python:
12
+ install:
13
+ - method: pip
14
+ path: .
15
+ extra_requirements:
16
+ - dev
17
+
18
+ sphinx:
19
+ configuration: docs/conf.py
@@ -19,8 +19,8 @@ keywords:
19
19
  - dask
20
20
  - mpi
21
21
  license: BSD-3-Clause
22
- version: 0.2.2
23
- date-released: '2026-03-04'
22
+ version: 0.3.0
23
+ date-released: '2026-03-08'
24
24
  preferred-citation:
25
25
  type: article
26
26
  authors:
@@ -0,0 +1,32 @@
1
+ # Use Python 3.14 slim as the base image
2
+ FROM python:3.14-slim
3
+
4
+ # Install system dependencies for MPI and building packages
5
+ RUN apt-get update && apt-get install -y --no-install-recommends \
6
+ build-essential \
7
+ libopenmpi-dev \
8
+ openmpi-bin \
9
+ && rm -rf /var/lib/apt/lists/*
10
+
11
+ # Set working directory
12
+ WORKDIR /app
13
+
14
+ # Create data directory for mounting
15
+ RUN mkdir /data && chmod 777 /data
16
+
17
+ # Copy the project files
18
+ COPY . .
19
+
20
+ # Install the package and its dependencies
21
+ RUN pip install --no-cache-dir .
22
+
23
+ # Add a non-root user for security
24
+ RUN useradd -m pst
25
+ USER pst
26
+
27
+ # Volume for data persistence
28
+ VOLUME /data
29
+
30
+ # Default command
31
+ ENTRYPOINT ["stormtracker"]
32
+ CMD ["--help"]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: PyStormTracker
3
- Version: 0.2.2
3
+ Version: 0.3.0
4
4
  Summary: A Parallel Object-Oriented Cyclone Tracker in Python
5
5
  Project-URL: Homepage, https://github.com/mwyau/PyStormTracker
6
6
  Project-URL: Repository, https://github.com/mwyau/PyStormTracker.git
@@ -13,57 +13,72 @@ Classifier: Development Status :: 4 - Beta
13
13
  Classifier: Intended Audience :: Science/Research
14
14
  Classifier: License :: OSI Approved :: BSD License
15
15
  Classifier: Programming Language :: Python :: 3
16
- Classifier: Programming Language :: Python :: 3.10
17
16
  Classifier: Programming Language :: Python :: 3.11
18
17
  Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Programming Language :: Python :: 3.14
19
20
  Classifier: Topic :: Scientific/Engineering :: Atmospheric Science
20
- Requires-Python: >=3.10
21
- Requires-Dist: dask>=2024.2.0
22
- Requires-Dist: distributed>=2024.2.0
23
- Requires-Dist: netcdf4>=1.6.5
24
- Requires-Dist: numpy>=1.24.0
25
- Requires-Dist: scipy>=1.10.0
21
+ Requires-Python: >=3.11
22
+ Requires-Dist: numpy>=1.23.0
23
+ Requires-Dist: scipy>=1.9.0
24
+ Requires-Dist: xarray[accel,io,parallel]>=2024.9.0
26
25
  Provides-Extra: dev
27
- Requires-Dist: mpi4py>=3.1.5; extra == 'dev'
28
- Requires-Dist: mypy>=1.9.0; extra == 'dev'
29
- Requires-Dist: netcdf4>=1.6.5; extra == 'dev'
26
+ Requires-Dist: mpi4py>=4.0.0; extra == 'dev'
27
+ Requires-Dist: mypy>=1.15.0; extra == 'dev'
28
+ Requires-Dist: myst-parser>=2.0.0; extra == 'dev'
30
29
  Requires-Dist: pandas>=2.2.0; extra == 'dev'
31
- Requires-Dist: pre-commit>=3.7.0; extra == 'dev'
30
+ Requires-Dist: pre-commit>=4.0.0; extra == 'dev'
31
+ Requires-Dist: pytest-cov>=5.0.0; extra == 'dev'
32
32
  Requires-Dist: pytest>=8.1.1; extra == 'dev'
33
- Requires-Dist: ruff>=0.3.4; extra == 'dev'
33
+ Requires-Dist: ruff>=0.9.0; extra == 'dev'
34
+ Requires-Dist: sphinx-rtd-theme>=2.0.0; extra == 'dev'
35
+ Requires-Dist: sphinx>=7.2.0; extra == 'dev'
34
36
  Description-Content-Type: text/markdown
35
37
 
36
38
  # PyStormTracker
37
39
 
40
+ [![CI](https://github.com/mwyau/PyStormTracker/actions/workflows/ci.yml/badge.svg)](https://github.com/mwyau/PyStormTracker/actions/workflows/ci.yml)
41
+ [![Documentation Status](https://readthedocs.org/projects/pystormtracker/badge/?version=latest)](https://pystormtracker.readthedocs.io/en/latest/?badge=latest)
42
+ [![codecov](https://codecov.io/github/mwyau/PyStormTracker/graph/badge.svg?token=JmTabGA3cq)](https://codecov.io/github/mwyau/PyStormTracker)
43
+ [![PyPI version](https://img.shields.io/pypi/v/PyStormTracker)](https://pypi.org/project/PyStormTracker/)
44
+ ![Python versions](https://img.shields.io/pypi/pyversions/PyStormTracker)
45
+ ![License](https://img.shields.io/pypi/l/PyStormTracker)
46
+ [![Docker](https://img.shields.io/badge/docker-xddd%2Fpystormtracker-blue?logo=docker)](https://hub.docker.com/r/xddd/pystormtracker)
47
+ [![GHCR](https://img.shields.io/badge/ghcr.io-xddd%2Fpystormtracker-blue?logo=github)](https://github.com/orgs/xddd/packages/container/package/pystormtracker)
38
48
  [![DOI](https://zenodo.org/badge/36328800.svg)](https://doi.org/10.5281/zenodo.18764813)
39
49
 
40
50
  PyStormTracker provides the implementation of the "Simple Tracker" algorithm used for cyclone trajectory analysis in **Yau and Chang (2020)**. It was originally developed at the **National Center for Atmospheric Research (NCAR)** as part of the **2015 Summer Internships in Parallel Computational Science (SIParCS)** program, utilizing a task-parallel strategy with temporal decomposition and a tree reduction algorithm to process large climate datasets.
41
51
 
42
52
  ## Features
43
53
 
44
- - **Modern Python 3 Support**: Fully migrated from Python 2 with comprehensive type hints.
45
- - **Flexible Data Support**: Works with `netCDF4` and handles various coordinate naming conventions (`lat`/`lon` vs `latitude`/`longitude`).
54
+ - **Modern Python Support**: Strictly targets **Python 3.11+** with comprehensive type hints and 100% strict `mypy` compliance.
55
+ - **Xarray Integrated**: Leverages `xarray` with optimized I/O, acceleration, and parallel extras for robust, high-performance coordinate-aware processing and lazy data loading.
46
56
  - **Parallel Backends**:
47
57
  - **Dask (Default)**: Automatically scales to all available CPU cores on local machines.
48
58
  - **MPI**: Supports distributed execution via `mpi4py`.
49
59
  - **Serial**: Standard sequential execution for smaller datasets or debugging.
50
- - **Robust Detection**: Handles masked/missing data correctly and includes automated unit/integration tests.
51
- - **User-Friendly Output**: Results are exported directly to CSV with readable datetime strings and formatted numeric values.
60
+ - **Robust Detection**: Optimized $O(N)$ extrema filtering and robust handling of masked/missing data.
61
+ - **CI/CD Integrated**: Automated linting, type-checking, and code coverage reporting via GitHub Actions.
62
+ - **Standardized Output**: Results are exported to the IMILAST intercomparison format (.txt) with readable datetime strings and formatted numeric values.
52
63
 
53
64
  ## Technical Methodology
54
65
 
55
66
  PyStormTracker treats meteorological fields as 2D images and leverages `scipy.ndimage` for robust feature detection:
56
67
 
57
- - **Local Extrema Detection**: Uses `generic_filter` with a sliding window (default 5x5) to identify local minima (cyclones) or maxima (anticyclones/vorticity).
68
+ - **Local Extrema Detection**: Uses an optimized sliding window filter to identify local minima (cyclones) or maxima (anticyclones/vorticity).
58
69
  - **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.
59
70
  - **Spherical Continuity**: Utilizes `mode='wrap'` for all filters to correctly handle periodic boundaries across the Prime Meridian, enabling seamless tracking across the entire globe.
60
71
  - **Heuristic Linking**: Implements a nearest-neighbor linking strategy to connect detected centers into trajectories across successive time steps.
61
72
 
73
+ ## Documentation
74
+
75
+ Full documentation, including API references and advanced usage examples, is available at [pystormtracker.readthedocs.io](https://pystormtracker.readthedocs.io/).
76
+
62
77
  ## Installation
63
78
 
64
79
  ### Prerequisites
65
- - Python 3.10+
66
- - (Optional) MS-MPI or OpenMPI for MPI support.
80
+ - Python 3.11+
81
+ - (Optional) OpenMPI for MPI support.
67
82
 
68
83
  ### From PyPI (Recommended)
69
84
  You can install the latest stable version of PyStormTracker directly from PyPI:
@@ -78,9 +93,10 @@ pip install PyStormTracker
78
93
  cd PyStormTracker
79
94
  ```
80
95
 
81
- 2. Install the package in editable mode:
96
+ 2. Install with **uv** (Recommended):
82
97
  ```bash
83
- pip install -e .
98
+ uv sync --extra dev
99
+ uv run pre-commit install --hook-type pre-push
84
100
  ```
85
101
 
86
102
  ## Usage
@@ -88,7 +104,7 @@ pip install PyStormTracker
88
104
  Once installed, you can use the `stormtracker` command directly:
89
105
 
90
106
  ```bash
91
- stormtracker -i data/test/slp.2012.nc -v slp -o my_tracks
107
+ stormtracker -i era5_msl_2.5x2.5.nc -v msl -o my_tracks
92
108
  ```
93
109
 
94
110
  ### Command Line Arguments
@@ -96,50 +112,74 @@ stormtracker -i data/test/slp.2012.nc -v slp -o my_tracks
96
112
  | Argument | Short | Description |
97
113
  | :--- | :--- | :--- |
98
114
  | `--input` | `-i` | **Required.** Path to the input NetCDF file. |
99
- | `--var` | `-v` | **Required.** Variable name to track (e.g., `slp`, `vo`). |
100
- | `--output` | `-o` | **Required.** Path to the output CSV file (appends `.csv` if missing). |
115
+ | `--var` | `-v` | **Required.** Variable name to track (e.g., `msl`, `vo`). |
116
+ | `--output` | `-o` | **Required.** Path to the output track file (appends `.txt` if missing). |
101
117
  | `--num` | `-n` | Number of time steps to process. |
102
118
  | `--mode` | `-m` | `min` (default) for low pressure, `max` for vorticity/high pressure. |
103
119
  | `--backend` | `-b` | `dask` (default), `serial`, or `mpi`. |
104
120
  | `--workers` | `-w` | Number of Dask workers (defaults to CPU core count). |
105
121
 
106
- ### Examples
122
+ ## Development
107
123
 
108
- **Run with Dask (Auto-detected cores):**
124
+ ### Setup
125
+ Using **uv** is the recommended way to set up your environment:
109
126
  ```bash
110
- stormtracker -i input.nc -v slp -o tracks
127
+ # Install dependencies and sync virtual environment
128
+ uv sync --extra dev
129
+
130
+ # Install pre-push hooks
131
+ uv run pre-commit install --hook-type pre-push
111
132
  ```
112
133
 
113
- **Run with MPI (Distributed):**
134
+ ### Quality Control
135
+ Run automated checks using **uv run**:
136
+
137
+ **Linting & Formatting:**
114
138
  ```bash
115
- mpiexec -n 4 stormtracker -i input.nc -v slp -o tracks -b mpi
139
+ uv run ruff check . --fix
140
+ uv run ruff format .
116
141
  ```
117
142
 
118
- ## Project Structure
143
+ **Type Checking:**
144
+ ```bash
145
+ uv run mypy src/
146
+ ```
119
147
 
120
- - `src/pystormtracker/models/`: Data structures (`Center`, `Grid`, `Tracks`).
121
- - `src/pystormtracker/simple/`: Implementation of the Simple Tracker logic (`SimpleDetector`, `SimpleLinker`).
122
- - `src/pystormtracker/stormtracker.py`: CLI orchestration and parallel backends.
148
+ ### Tiered Testing
149
+ To keep development cycles fast, testing is tiered:
150
+ - **Fast Tests**: Default local runs (skips slow tests).
151
+ - **Slow Tests**: ONLY long-running integration/regression tests.
152
+ - **Full Suite**: Everything (used in CI).
123
153
 
124
- ## Testing
154
+ **Run fast unit tests only (Default):**
155
+ ```bash
156
+ uv run pytest
157
+ ```
125
158
 
126
- Run the full test suite (unit and integration tests) using `pytest`:
159
+ **Run ONLY slow/integration tests:**
160
+ ```bash
161
+ uv run pytest --run-slow
162
+ ```
127
163
 
164
+ **Run everything:**
128
165
  ```bash
129
- pytest
166
+ uv run pytest --run-all
130
167
  ```
131
168
 
132
169
  ## Citations
133
170
 
134
171
  If you use this software in your research, please cite the following:
135
172
 
136
- - **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.
173
+ - **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.
137
174
 
138
175
  - **Yau, A. M. W.**, 2026: mwyau/PyStormTracker. *Zenodo*, https://doi.org/10.5281/zenodo.18764813.
139
176
 
140
177
  ## References
141
178
 
142
179
  - **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.
180
+ - **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.
181
+ - **IMILAST Intercomparison Protocol**: https://proclim.scnat.ch/en/activities/project_imilast/intercomparison
182
+ - **IMILAST Data Download**: https://proclim.scnat.ch/en/activities/project_imilast/data_download
143
183
 
144
184
  ## License
145
185