quantmet-tools 0.4.4__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.
- quantmet_tools-0.4.4/CHANGELOG.md +296 -0
- quantmet_tools-0.4.4/CITATION.cff +31 -0
- quantmet_tools-0.4.4/LICENSE +9 -0
- quantmet_tools-0.4.4/MANIFEST.in +1 -0
- quantmet_tools-0.4.4/PKG-INFO +461 -0
- quantmet_tools-0.4.4/README.md +412 -0
- quantmet_tools-0.4.4/pyproject.toml +89 -0
- quantmet_tools-0.4.4/quantmet/__init__.py +10 -0
- quantmet_tools-0.4.4/quantmet/conservation.py +200 -0
- quantmet_tools-0.4.4/quantmet/constants.py +69 -0
- quantmet_tools-0.4.4/quantmet/dct_spectra.py +105 -0
- quantmet_tools-0.4.4/quantmet/degdays.py +386 -0
- quantmet_tools-0.4.4/quantmet/ensemble.py +120 -0
- quantmet_tools-0.4.4/quantmet/epflux.py +306 -0
- quantmet_tools-0.4.4/quantmet/gill.py +286 -0
- quantmet_tools-0.4.4/quantmet/helmholtz.py +161 -0
- quantmet_tools-0.4.4/quantmet/packets.py +51 -0
- quantmet_tools-0.4.4/quantmet/pv.py +143 -0
- quantmet_tools-0.4.4/quantmet/qbo.py +112 -0
- quantmet_tools-0.4.4/quantmet/stats.py +368 -0
- quantmet_tools-0.4.4/quantmet/tc.py +320 -0
- quantmet_tools-0.4.4/quantmet/tc_detect.c +116 -0
- quantmet_tools-0.4.4/quantmet/tc_detect.py +181 -0
- quantmet_tools-0.4.4/quantmet/tem.py +360 -0
- quantmet_tools-0.4.4/quantmet/thermo.py +860 -0
- quantmet_tools-0.4.4/quantmet/verify.py +425 -0
- quantmet_tools-0.4.4/quantmet/waf.py +598 -0
- quantmet_tools-0.4.4/quantmet/wk_filter.py +202 -0
- quantmet_tools-0.4.4/quantmet_tools.egg-info/PKG-INFO +461 -0
- quantmet_tools-0.4.4/quantmet_tools.egg-info/SOURCES.txt +51 -0
- quantmet_tools-0.4.4/quantmet_tools.egg-info/dependency_links.txt +1 -0
- quantmet_tools-0.4.4/quantmet_tools.egg-info/requires.txt +29 -0
- quantmet_tools-0.4.4/quantmet_tools.egg-info/top_level.txt +1 -0
- quantmet_tools-0.4.4/setup.cfg +4 -0
- quantmet_tools-0.4.4/tests/test_conservation.py +88 -0
- quantmet_tools-0.4.4/tests/test_dct_spectra.py +59 -0
- quantmet_tools-0.4.4/tests/test_degdays.py +217 -0
- quantmet_tools-0.4.4/tests/test_ensemble.py +69 -0
- quantmet_tools-0.4.4/tests/test_epflux.py +143 -0
- quantmet_tools-0.4.4/tests/test_gill.py +100 -0
- quantmet_tools-0.4.4/tests/test_helmholtz.py +119 -0
- quantmet_tools-0.4.4/tests/test_packets.py +19 -0
- quantmet_tools-0.4.4/tests/test_pv.py +55 -0
- quantmet_tools-0.4.4/tests/test_qbo.py +63 -0
- quantmet_tools-0.4.4/tests/test_stats.py +167 -0
- quantmet_tools-0.4.4/tests/test_tc.py +56 -0
- quantmet_tools-0.4.4/tests/test_tc_detect.py +92 -0
- quantmet_tools-0.4.4/tests/test_tem.py +193 -0
- quantmet_tools-0.4.4/tests/test_thermo.py +395 -0
- quantmet_tools-0.4.4/tests/test_verify.py +281 -0
- quantmet_tools-0.4.4/tests/test_waf.py +164 -0
- quantmet_tools-0.4.4/tests/test_waf_divergence.py +385 -0
- quantmet_tools-0.4.4/tests/test_wk_filter.py +89 -0
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.4.4 — 2026-10-28
|
|
4
|
+
|
|
5
|
+
### Changed
|
|
6
|
+
|
|
7
|
+
- **Distribution name**: the package is published on PyPI as `quantmet-tools`, the repository's name
|
|
8
|
+
(`quantmet` is too close to an existing project's name for PyPI). The import name is still `quantmet`:
|
|
9
|
+
`pip install quantmet-tools`, then `import quantmet`. The package code is unchanged.
|
|
10
|
+
|
|
11
|
+
## 0.4.3 — 2026-10-17
|
|
12
|
+
|
|
13
|
+
### Fixed
|
|
14
|
+
|
|
15
|
+
- **`quantmet.tc_detect`**: a non-finite sea-level pressure is never a candidate, and a NaN anywhere in a candidate's
|
|
16
|
+
annulus voids it. Before, both comparisons let NaN through, so one masked cell made every point within the ring
|
|
17
|
+
radius a "candidate" (a 5 x 5 NaN block on a flat 1-degree field returned the 4,096-candidate cap). The kernel is
|
|
18
|
+
rebuilt automatically (its cache name hashes the C source).
|
|
19
|
+
- **`quantmet.tem`** (`tem_terms`, `residual_streamfunction`): the static-stability floor now treats a neutral or
|
|
20
|
+
unstable layer as marginally stable (th_p <= -1e-7 K/Pa), as `quantmet.epflux` does; before, a positive th_p
|
|
21
|
+
passed through and flipped the sign of psi_e there. No change where theta decreases with pressure.
|
|
22
|
+
|
|
23
|
+
### Changed
|
|
24
|
+
|
|
25
|
+
- **One set of physical constants** (`quantmet.constants`) in every module. Omega was 7.292e-5 (epflux, tem,
|
|
26
|
+
wk_filter), 7.2921e-5 (pv) and 7.292115e-5 (constants, conservation); kappa 0.2854 (epflux) and 0.2857 (tem, pv);
|
|
27
|
+
g 9.81 (wk_filter); Gill's beta 2.28e-11 against 2 Omega / a = 2.289e-11. Effects are at the 0.1 % level or
|
|
28
|
+
below (the E-P flux uses kappa through theta' / theta_p, whose ratio barely depends on it); Gill's deformation
|
|
29
|
+
radius shrinks 0.2 %.
|
|
30
|
+
- **`quantmet.tc_detect`** raises a `RuntimeError` naming the missing compiler (and the `CC` variable) when the
|
|
31
|
+
kernel cannot be built, instead of a bare `FileNotFoundError`.
|
|
32
|
+
- **`quantmet.tc.recurvature`** docstring states what the code returns: the last point without eastward motion
|
|
33
|
+
before the motion first reaches `east`, for a track that moved westward earlier.
|
|
34
|
+
- **CI** tests Python 3.13 as well.
|
|
35
|
+
|
|
36
|
+
## Unreleased infrastructure (now part of 0.4.3)
|
|
37
|
+
|
|
38
|
+
Project infrastructure only; the package code is unchanged.
|
|
39
|
+
|
|
40
|
+
- **Documentation site** at [scorvec.com/quantmet-tools](https://scorvec.com/quantmet-tools/) (MkDocs, built strictly
|
|
41
|
+
and deployed from `main` by `docs.yml`): the overview, quick starts, numerical notes and install sections of the
|
|
42
|
+
README, and one API page per module with every docstring shown exactly as written. `pip install -e ".[docs]"`
|
|
43
|
+
installs the tools.
|
|
44
|
+
- **PyPI**: `release.yml` builds the sdist and wheel on every change, checks them (`twine check --strict`, an import
|
|
45
|
+
of the installed wheel in a clean environment) and publishes a GitHub release to PyPI by Trusted Publishing; the
|
|
46
|
+
release tag must match `quantmet.__version__`.
|
|
47
|
+
- **Packaging metadata**: SPDX license expression (`license = "MIT"`, setuptools >= 77), supported-Python and audience
|
|
48
|
+
classifiers, documentation and issue-tracker URLs; the sdist also carries the changelog and `CITATION.cff`.
|
|
49
|
+
- **`CITATION.cff`**, a security policy (`SECURITY.md`), a `pre-commit` configuration running the CI ruff checks, and
|
|
50
|
+
Dependabot for the workflow actions and the Python dependencies.
|
|
51
|
+
|
|
52
|
+
## 0.4.2 — 2026-10-15
|
|
53
|
+
|
|
54
|
+
### Leap years
|
|
55
|
+
|
|
56
|
+
- **`quantmet.waf.lowpass_anomaly`** calls `clim` with the 1-based day on the 366-slot calendar
|
|
57
|
+
(`quantmet.degdays.doy366` + 1: 29 Feb = 60, 1 March = 61 in every year). It used the plain day of the year, so in
|
|
58
|
+
a common year every day from 1 March was read from the climatology slot of the day before. Climatologies indexed
|
|
59
|
+
by the plain day of the year must be rebuilt on the 366-slot calendar.
|
|
60
|
+
- **`quantmet.degdays.doy366`** docstring: how to fill slot 59 (29 Feb) and the cyclic phase on 366 slots.
|
|
61
|
+
- **Tests:** 7 new (179 in all): `doy366` over 1896–2104 (one calendar day per slot, the century rule) and on
|
|
62
|
+
sub-daily, pre-1970 and Python dates; local days through 29 Feb; `lowpass_anomaly` on a 366-slot climatology in a
|
|
63
|
+
common and a leap year; calendar-day lags of `acf_inflation`, the window of `paired` and the same-valid-time pairs
|
|
64
|
+
of `cycle_offset` across 29 Feb.
|
|
65
|
+
|
|
66
|
+
### Maintenance (no numerical change)
|
|
67
|
+
|
|
68
|
+
- **`quantmet.tc_detect`**: the compiled kernel is cached with `functools.cache` (tests reset it with
|
|
69
|
+
`tc_detect._lib.cache_clear()`), and the shared library is named `.so` on every platform but Windows (`.dll`).
|
|
70
|
+
- **Rounding**: `int(np.rint(x))` where values may be NumPy scalars (`ensemble.cycle_offset`, `tc.phase_series`);
|
|
71
|
+
`np.rint` rounds half to even exactly like `round`.
|
|
72
|
+
- **Code style**: ruff lint and format (configuration in `pyproject.toml`), checked in CI.
|
|
73
|
+
- **Docs**: README install notes without platform specifics; the full MIT license text.
|
|
74
|
+
|
|
75
|
+
## 0.4.1 — 2026-10-12
|
|
76
|
+
|
|
77
|
+
### `quantmet.waf`: one truncated-divergence method on every grid (#1)
|
|
78
|
+
|
|
79
|
+
- **The inconsistency.** With `div_lmax`, `tn01_flux` on a closed grid (the 360° column repeated, e.g. the extended
|
|
80
|
+
Driscoll–Healy grids of `quantmet.helmholtz`) set the masked cells' divergence to zero before the spectral
|
|
81
|
+
truncation; on an open grid it truncated the divergence as computed. Both zero-fill the masked flux, so the step at
|
|
82
|
+
the mask edge is the same. Its centred difference lands half in the first masked cell and half in the first valid
|
|
83
|
+
one, and only the closed path dropped the masked half. `wx`, `wy` and the mask were identical, but the truncated
|
|
84
|
+
divergence was not: 8.5 % rms and 11–25 % of the maximum apart on real 250 hPa AIFS-ENS input (leads 0–10 d,
|
|
85
|
+
20–80°), and 37 % rms on a synthetic field with large mask edges.
|
|
86
|
+
- **New `div_method` keyword**, one implementation for both layouts:
|
|
87
|
+
- `"zero"` (default): the 0.4.0 closed-grid behaviour, reproduced bit for bit;
|
|
88
|
+
- `"as_computed"`: the 0.4.0 open-grid behaviour, reproduced bit for bit;
|
|
89
|
+
- `"taper"`: the flux is ramped to zero with a cos² weight over `taper_deg` (default 5°) of great-circle distance
|
|
90
|
+
from the mask (`mask_taper`), its divergence is taken spectrally (`sh_divergence`), then masked.
|
|
91
|
+
|
|
92
|
+
**Open-grid results change** unless `div_method="as_computed"` is passed. Without `div_lmax` nothing changes.
|
|
93
|
+
- **How the default was chosen.** `tests/test_waf_divergence.py` (run it as a script for the table). Setup: extended
|
|
94
|
+
DH2 grid at 1.41°, T15, 20 paired realisations. Values are medians of the relative rms in the valid cells.
|
|
95
|
+
|
|
96
|
+
| case | zero | as_computed | taper 5° |
|
|
97
|
+
|---|---|---|---|
|
|
98
|
+
| (a) known divergence, W = ∇χ + k×∇ψ (T12): error | **0.241** | 0.307 | 0.348 |
|
|
99
|
+
| (a′) divergence concentrated inside the masked caps: error | **0.469** | 0.680 | 0.713 |
|
|
100
|
+
| (b) divergence-free W = k×∇ψ: spurious divergence | **0.085** | 0.163 | 0.183 |
|
|
101
|
+
| (c) real TN01 flux, westerlies falling to zero across a band, rim wind direction ±1.5 m/s: leakage | 0.055 | 0.039 | **0.012** |
|
|
102
|
+
| (c′) the same around a weak-wind cap | 0.007 | 0.005 | **0.002** |
|
|
103
|
+
|
|
104
|
+
- `"zero"` was best in every realisation of (a), (a′) and (b).
|
|
105
|
+
- `"taper"` was worst there, and worse the wider the ramp (2° → 8°): it keeps the whole edge term W·∇w, now inside
|
|
106
|
+
the valid ramp. It was best in (c), where all three stay at a few % of the signal.
|
|
107
|
+
- With rougher (T30) fields, (a) ties between zero and as_computed (0.35 vs 0.34), while (b) still halves.
|
|
108
|
+
- A realistic check used the live 250 hPa flux, evaluated everywhere, as the known field, scored on the real mask
|
|
109
|
+
over 11 leads:
|
|
110
|
+
- (a): 0.164 / 0.190 / 0.245;
|
|
111
|
+
- (b), on its rotational part: 0.060 / 0.118 / 0.146;
|
|
112
|
+
- zero was best at every lead;
|
|
113
|
+
- (c) on the real basic state: 0.033 / 0.031 / 0.027.
|
|
114
|
+
- **New helpers.**
|
|
115
|
+
- `sh_divergence`: divergence on a Driscoll–Healy grid from the weak form D_lm = −(1/4π)∮ W·∇Y_lm dΩ, so the flux
|
|
116
|
+
is not differenced; exact to round-off for band-limited fields; leading dimensions allowed.
|
|
117
|
+
- `mask_taper`: cos² edge weight from the great-circle distance to the mask (scipy KD-tree; the last mask is
|
|
118
|
+
cached).
|
|
119
|
+
- `masked_divergence`: the divergence stage for any masked flux.
|
|
120
|
+
- `DIV_METHODS` and `TAPER_DEG`.
|
|
121
|
+
- **`sh_truncate`** accepts leading dimensions, and an extended grid with only the −90° row or only the 360° column
|
|
122
|
+
(0.4.0 misread both).
|
|
123
|
+
- **Docs.** The horizontal TN01 flux is homogeneous of degree zero in (U, V): its magnitude does not grow as |U| → 0,
|
|
124
|
+
but its direction becomes ill-conditioned (a ±1.5 m/s wind error turns it by ~27° at 3 m/s).
|
|
125
|
+
- **Tests:** 9 new (172 in all).
|
|
126
|
+
|
|
127
|
+
## 0.4.0 — 2026-10-12
|
|
128
|
+
|
|
129
|
+
### New modules
|
|
130
|
+
- `quantmet.verify` — forecast verification: `crps_ensemble` (weighted empirical CRPS, O(m log m)), `crps_fair`
|
|
131
|
+
(Ferro 2014; unbiased at any ensemble size; member `weights` and `groups` for a weighted multi-model mixture, with
|
|
132
|
+
the within-model correction W_k²/(W_k² − Σw²) and one-member groups as point masses), `crps_gaussian` (closed form,
|
|
133
|
+
EMOS), `ranks` / `rank_histogram` / `pit` (ties spread uniformly, seedable), `brier_score`, `brier_decomposition`
|
|
134
|
+
(reliability, resolution, uncertainty plus the Stephenson et al. 2008 within-bin terms, so the identity is exact),
|
|
135
|
+
`reliability_curve`, `diebold_mariano` (Newey–West Bartlett HAC after AR(1) prewhitening, Andrews 1991 bandwidth,
|
|
136
|
+
≥ h − 1 lags, Harvey–Leybourne–Newbold correction with t(n − 1) p-values; size 5–6 % at a nominal 5 % on
|
|
137
|
+
persistent equal-skill losses, where the naive test rejects 35 %), and `paired`, which aligns two dated series on
|
|
138
|
+
the dates both legs have and returns the day count with the window it was meant to cover.
|
|
139
|
+
- `quantmet.thermo` — moist thermodynamics on SI inputs (K, Pa, kg/kg, RH in %): saturation vapour pressure over
|
|
140
|
+
water and ice (Magnus with Alduchov–Eskridge 1996 coefficients, Bolton 1980, Murphy–Koop 2005), dewpoint / frost
|
|
141
|
+
point, the full set of pressure-aware RH ↔ dewpoint ↔ specific humidity ↔ mixing ratio conversions, saturation
|
|
142
|
+
mixing ratio and specific humidity, dewpoint depression, vapour pressure deficit, Kirchhoff latent heats;
|
|
143
|
+
potential temperature (dry, or the exact moist Poisson exponent), Exner, virtual and virtual potential temperature,
|
|
144
|
+
density temperature with condensate, air density, equivalent temperature, θ_e (Bolton eq. 39 pseudo-adiabatic and
|
|
145
|
+
Emanuel's reversible form), saturation θ_e, θ_l (Betts 1973), θ_il (Tripoli & Cotton 1981), θ_w (Davies-Jones 2008);
|
|
146
|
+
Stull (2011) wet bulb with its validity mask and the psychrometric (isobaric) wet bulb at any pressure; the exact
|
|
147
|
+
LCL (Romps 2017, reproducing his published value) and Bolton's; dry and moist static energy, specific enthalpy with
|
|
148
|
+
a stated reference state, the pseudo-adiabatic lapse rate and an RK4 pseudo-adiabat; column integrals 0 → p_s on
|
|
149
|
+
pressure or model levels (precipitable water, column vapour / liquid / ice, column MSE, column enthalpy) with
|
|
150
|
+
below-ground levels ignored; ln p-mean layer temperature and hypsometric thickness; the NWS heat index (simple
|
|
151
|
+
formula first, Rothfusz with both adjustments only above 80 °F, tested against the NWS chart) and the NWS 2001
|
|
152
|
+
wind chill (tested against the NWS chart); °C/°F/K and m/s–kt–mph helpers.
|
|
153
|
+
- `quantmet.degdays` — `midpoint_dd`, `integral_dd` (sample mean, or the exact integral of the piecewise-linear
|
|
154
|
+
curve), `degree_hours` by time-of-day block, `dd_from_extremes` (daily extremes plus a normalised diurnal shape),
|
|
155
|
+
`expected_dd` (Gaussian temperature, Thom 1954), `weighted_dd` (kink per location, then weight; strict about
|
|
156
|
+
missing locations), `local_days` / `day_length_hours` (DST-safe local calendar days from UTC data via zoneinfo,
|
|
157
|
+
incomplete days dropped), `doy366` (29 Feb in its own slot) and `fit_balance_points` (two-hinge change-point fit
|
|
158
|
+
of load on temperature with optional extra regressors, batched over the hinge grid).
|
|
159
|
+
- `quantmet.constants` — one SI constant set (R_d 287.04, R_v 461.5, c_pd 1004.7, c_pv 1846, c_l 4218, c_i 2106,
|
|
160
|
+
L_v0 2.501e6, L_f0 3.337e5, g 9.80665, ε = R_d/R_v), used by `thermo`; older modules keep their own constants.
|
|
161
|
+
|
|
162
|
+
### Packaging
|
|
163
|
+
- Version 0.4.0; description and keywords cover the new modules. No new dependencies (`zoneinfo` is standard
|
|
164
|
+
library; Windows needs the `tzdata` package for the time-zone database).
|
|
165
|
+
|
|
166
|
+
## 0.3.1 — 2026-10-12
|
|
167
|
+
|
|
168
|
+
Bug fixes (all four reproduced, each now pinned by a test that fails on 0.3.0):
|
|
169
|
+
|
|
170
|
+
- `quantmet.conservation.column_energy` — the `total` was ∫(c_pT + Φ + Lq) dp/g, which counts the pressure work
|
|
171
|
+
twice (∫Φ dp/g = Φ_s p_s/g + ∫R T dp/g): a hydrostatic isothermal column (250 K, p_s = 1000 hPa) gave
|
|
172
|
+
3.286e9 J/m² against the true 2.561e9 (+28 %). `total` is now the total energy I + P + LH + K: with the new `phis`
|
|
173
|
+
argument (surface geopotential) ∫(c_pT + Lq + k) dp/g + Φ_s p_s/g (Trenberth 1997; exact for that column), without
|
|
174
|
+
it ∫(c_vT + Φ + Lq + k) dp/g (Peixoto & Oort 1992; the same for a hydrostatic column, −0.26 % there from the
|
|
175
|
+
top layer's log-singular Φ). New components `cvT` and `PhiS`; `cpT`, `Phi`, `Lq`, `KE` unchanged. New constants
|
|
176
|
+
`RD`, `CV`.
|
|
177
|
+
- `quantmet.dct_spectra.ke_spectrum` — wavelengths were labelled one wavenumber too long and the longest bin was
|
|
178
|
+
dropped: exact DCT modes of 192, 96, 48 and 24 km came out at 256, 109.7, 51.2 and 24.8 km. Now
|
|
179
|
+
`wavelengths = res_km * min(shape) / k` for k = 1 … n and every bin is returned (one more than before), so
|
|
180
|
+
slopes, `effective_resolution` and `fidelity_resolution` change. The docstring now says the method assumes one
|
|
181
|
+
grid spacing in both directions.
|
|
182
|
+
- `quantmet.helmholtz` — interpolation to the Gauss–Legendre grid had no longitude wrap, so on 1.5° and 2.5° grids at
|
|
183
|
+
lmax = 120 (and `quantmet.tc.irrotational_on_grid` at 2.5°, lmax = 106) every GLQ column past the last input
|
|
184
|
+
longitude was NaN and χ / ψ came out 100 % NaN. The input is now padded with a column at lon ± 360 and, if the grid
|
|
185
|
+
stops short of the poles, with pole rows (zonal wavenumber 1 of the nearest row, the only part of a wind component
|
|
186
|
+
that survives at a pole); repeated 360° columns are dropped. Longitude derivatives (divergence, vorticity,
|
|
187
|
+
`irrotational_wind`, `nondivergent_wind`) are periodic centred differences on global grids instead of
|
|
188
|
+
np.gradient's one-sided ones at the seam (seam-column error in u_χ 0.012 → 0.002 m/s on the test field).
|
|
189
|
+
- `quantmet.tc_detect` (C kernel) — the ring test was centred one column WEST of the candidate (the summed-area table
|
|
190
|
+
is built on a padded field whose column j + ring_px + 1 holds source column j, but the boxes started at j): on a
|
|
191
|
+
background gradient the depth was off by one column's worth of it. Boxes now start at j + 1. The kernel source
|
|
192
|
+
hash changes, so the cached library is rebuilt on first use. Latitude and longitude spacings may now differ (ring,
|
|
193
|
+
local-minimum and wind windows are converted with dlat for rows and dlon for columns; the kernel takes separate
|
|
194
|
+
row/column half-widths).
|
|
195
|
+
|
|
196
|
+
Other changes:
|
|
197
|
+
|
|
198
|
+
- `quantmet.epflux.ep_flux` accepts leading axes, (..., plev, lat, lon) → (..., plev, lat), one flux per member (0.3.0
|
|
199
|
+
raised); `below_ground_share` takes (..., lat, lon). The module docstring now says the static stability is the
|
|
200
|
+
zonal-mean profile by default (it still said global-mean).
|
|
201
|
+
- `quantmet.tc_detect` docstrings: candidates are local minima with no LOWER neighbour (ties count), not strict
|
|
202
|
+
minima; the annulus is square in degrees.
|
|
203
|
+
- `__version__` is the single source of the version (pyproject reads it); 0.3.0 shipped with `__version__ = "0.2.0"`.
|
|
204
|
+
- `examples/aifs_spectral_fidelity` — rerun with the fixed spectra (table, figure and npz regenerated; the 0.3.0
|
|
205
|
+
numbers are listed there), a caveat that one res_km (21.3 km, dx at 40°N) was used for a 0.25° box whose dy is
|
|
206
|
+
27.8 km, and fetching from ECMWF's Google Cloud mirror (`source="google"`, one parameter per request).
|
|
207
|
+
- README: the "runs in production daily" and "every module tested" claims made accurate (tests for `dct_spectra`
|
|
208
|
+
added); column-energy numerical note.
|
|
209
|
+
|
|
210
|
+
## 0.3.0 — 2026-10-02
|
|
211
|
+
|
|
212
|
+
### New modules
|
|
213
|
+
- `quantmet.pv` — Ertel PV on pressure levels (`ertel_pv`), the dynamic tropopause (`dynamic_tropopause`: upward
|
|
214
|
+
search, interpolation in PV, top-level fill where the column never reaches the threshold) and isentropic PV
|
|
215
|
+
(`on_isentrope`).
|
|
216
|
+
- `quantmet.packets` — Rossby wave-packet envelope (`packet_envelope`, Zimin et al. 2003) and `band_mean`.
|
|
217
|
+
- `quantmet.tc` — ECMWF tropical-cyclone track BUFR decoding for every ensemble member (`decode_tracks`; control =
|
|
218
|
+
member 0, deterministic subset excluded), `recurvature`, the outflow-jet metric (`irrotational_on_grid`,
|
|
219
|
+
`pv_advection`, `outflow_index`; Archambault et al. 2013), Hart (2003) phase space (`hart_parameters`,
|
|
220
|
+
`phase_series`) and Evans & Hart (2003) ET timing (`et_times`); helpers `at`, `within`, `great_circle_km`.
|
|
221
|
+
- `quantmet.ensemble` — ensemble `sensitivity` (Torn & Hakim) and `group_composite` with FDR masking (via
|
|
222
|
+
`quantmet.stats`), and `cycle_offset`, a no-lookahead estimator of systematic 00Z/12Z cycle offsets.
|
|
223
|
+
- `quantmet.conservation` — dry-air mass, implied evaporation, column total energy, relative and mass AAM, mountain
|
|
224
|
+
and friction torques, with exact 0 → p_s pressure-level integration and hemispheric splits.
|
|
225
|
+
|
|
226
|
+
### Changed
|
|
227
|
+
- `quantmet.epflux` — static stability from the ZONAL-MEAN theta by default (`theta_p="global"` restores the
|
|
228
|
+
earlier global profile); vertical derivatives in ln p on the actual levels; below-ground masking (`psfc`,
|
|
229
|
+
`bg_frac`, `below_ground_share`; members may pass `(u, v, t, psfc)`); NaN-aware latitude smoothing
|
|
230
|
+
(`smooth_deg`). Existing calls keep working; their numbers change through the new default stability and the
|
|
231
|
+
ln-p derivative.
|
|
232
|
+
|
|
233
|
+
### Packaging
|
|
234
|
+
- `tc` and `all` extras; version 0.3.0.
|
|
235
|
+
|
|
236
|
+
## 0.2.0 — 2026-09-28
|
|
237
|
+
|
|
238
|
+
### New modules
|
|
239
|
+
- `quantmet.tem` — transformed-Eulerian-mean diagnostics on pressure levels:
|
|
240
|
+
`cospectrum` / `eddy_covariances` (by zonal wavenumber, Nyquist term not
|
|
241
|
+
doubled), `tem_terms` (fhat, v\*, ω\*, full primitive-equation E–P flux and
|
|
242
|
+
its divergence, total and by wave group), `residual_streamfunction`,
|
|
243
|
+
`residual_velocities`, `downward_control`, `fill_tropics`, `upwelling_w`,
|
|
244
|
+
`eddy_heat_flux` (by wavenumber, band mean, poleward-positive).
|
|
245
|
+
- `quantmet.waf` — Takaya–Nakamura (2001) wave-activity flux on the sphere
|
|
246
|
+
(`tn01_flux`) and on a Cartesian grid (`tn01_flux_cartesian`), the
|
|
247
|
+
per-member ensemble mean with sign agreement (`ensemble_tn01_flux`),
|
|
248
|
+
`lead_smooth`, `lowpass_anomaly` (low-passed basic state),
|
|
249
|
+
`streamfunction_anomaly` (via `quantmet.helmholtz`) and `sh_truncate`.
|
|
250
|
+
Periodic longitude differences; the cross term uses the plain mixed angle
|
|
251
|
+
derivative of TN01 eq. 38 (no metric term). Masking thresholds are
|
|
252
|
+
arguments.
|
|
253
|
+
- `quantmet.gill` — Gill (1980) model by sparse direct solve with c, β, ε,
|
|
254
|
+
sponge and smoothing exposed; `heating_from_ssta` (total-SST threshold),
|
|
255
|
+
`column_heating` and `heating_from_precip` (latent heat, 28.9 W/m² per
|
|
256
|
+
mm/day, documented first-baroclinic projection), `rossby_decay_rate`
|
|
257
|
+
(exact damped n = 1 root), `to_dimensional`, `band_mean`.
|
|
258
|
+
- `quantmet.qbo` — `zero_line`, the QBO zero-wind-line index with the dip
|
|
259
|
+
rule, censoring and hemispheric mirroring.
|
|
260
|
+
- `quantmet.stats` — `fdr_adjust` / `benjamini_hochberg` (NaN-aware),
|
|
261
|
+
`prais_winsten` (AR(1) GLS with segment breaks) and `segment_starts`,
|
|
262
|
+
`acf_inflation` (Bartlett window, optional daily grid), `label_events` and
|
|
263
|
+
`event_bootstrap`, `partial_corr_test` (Freedman–Lane permutation,
|
|
264
|
+
bootstrap CI, F test), `compare_r`, `auc`.
|
|
265
|
+
|
|
266
|
+
### Changed
|
|
267
|
+
- `quantmet.epflux`: new `charney_drazin_uc` and `charney_drazin_excess`
|
|
268
|
+
(latitude-only smoothing, leading member/lead axes carried through).
|
|
269
|
+
Provenance link updated to stratosphere.html.
|
|
270
|
+
- `quantmet.wk_filter`: `(time, ..., lon)` input with any number of middle
|
|
271
|
+
dimensions; custom bands as dicts (ER bounds solved for any wavenumber
|
|
272
|
+
range); `pad_end` / `ramp` soft landing at the end of the record; `dt` for
|
|
273
|
+
sub-daily sampling; new `wave_mask`. Two-dimensional results are
|
|
274
|
+
bit-identical to 0.1.0. The time-Nyquist row is never kept.
|
|
275
|
+
- `quantmet.tc_detect`: the 0.1.0 wrapper called a `detect` symbol the C
|
|
276
|
+
kernel does not define (the kernel exports `detect_step`), so
|
|
277
|
+
`detect_candidates` failed on first use; it now binds `detect_step` with
|
|
278
|
+
explicit argtypes and exposes its search band, pressure ceiling, local-min
|
|
279
|
+
window and optional wind maximum. The library is built into a per-user
|
|
280
|
+
cache directory with the platform's shared-library suffix, named by a
|
|
281
|
+
hash of the C source, and renamed into place atomically.
|
|
282
|
+
- `quantmet.helmholtz`: docstring documents the accuracy (∝ lmax^-1.7) and
|
|
283
|
+
the extended output grid.
|
|
284
|
+
- `import quantmet` exposes `__version__` and imports nothing else.
|
|
285
|
+
|
|
286
|
+
### Packaging
|
|
287
|
+
- `[build-system]`, version 0.2.0, license/authors/URLs, package data for
|
|
288
|
+
`tc_detect.c`, extras `helmholtz` and `waf` (pyshtools + xarray) and `test`.
|
|
289
|
+
- pytest suite in `tests/` (analytic cases throughout) and a GitHub Actions
|
|
290
|
+
workflow: Python 3.10–3.12 with core dependencies only, plus a job with
|
|
291
|
+
the extras.
|
|
292
|
+
- Committed `__pycache__` removed; `.gitignore` added.
|
|
293
|
+
|
|
294
|
+
## 0.1.0
|
|
295
|
+
|
|
296
|
+
- `dct_spectra`, `helmholtz`, `wk_filter`, `tc_detect`; `epflux` added later.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
cff-version: 1.2.0
|
|
2
|
+
message: "If you use quantmet in published work, please cite it as below."
|
|
3
|
+
title: "quantmet: quantitative meteorology tools"
|
|
4
|
+
type: software
|
|
5
|
+
authors:
|
|
6
|
+
- family-names: Corvec
|
|
7
|
+
given-names: Shawn
|
|
8
|
+
repository-code: "https://github.com/scorvec/quantmet-tools"
|
|
9
|
+
url: "https://scorvec.com/quantmet-tools/"
|
|
10
|
+
license: MIT
|
|
11
|
+
version: "0.4.4"
|
|
12
|
+
date-released: "2026-10-28"
|
|
13
|
+
abstract: >-
|
|
14
|
+
A Python toolkit for diagnosing atmospheric model output: transformed-Eulerian-mean and Eliassen-Palm
|
|
15
|
+
diagnostics, downward control, the Takaya-Nakamura wave-activity flux, the Gill model, the QBO zero-wind line,
|
|
16
|
+
Wheeler-Kiladis equatorial-wave filtering, Helmholtz decomposition, kinetic-energy spectra, potential vorticity and
|
|
17
|
+
the dynamic tropopause, Rossby wave packets, tropical-cyclone diagnostics and detection, ensemble sensitivity,
|
|
18
|
+
global conservation budgets, significance tests, forecast verification, moist thermodynamics and degree days.
|
|
19
|
+
keywords:
|
|
20
|
+
- meteorology
|
|
21
|
+
- atmospheric dynamics
|
|
22
|
+
- stratosphere
|
|
23
|
+
- Eliassen-Palm flux
|
|
24
|
+
- transformed Eulerian mean
|
|
25
|
+
- wave activity flux
|
|
26
|
+
- equatorial waves
|
|
27
|
+
- potential vorticity
|
|
28
|
+
- tropical cyclones
|
|
29
|
+
- forecast verification
|
|
30
|
+
- thermodynamics
|
|
31
|
+
- degree days
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Shawn Corvec
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
6
|
+
|
|
7
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
8
|
+
|
|
9
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
include CHANGELOG.md CITATION.cff
|