Metadata-Version: 2.4
Name: was-disaggregation
Version: 0.10.0
Summary: Forecast-conditioned gridded stochastic weather generation and daily downscaling
Author: Mandela HOUNGNIBO
License-Expression: GPL-3.0-only
Project-URL: Source, https://github.com/hmandela/was-disaggregation
Project-URL: Issues, https://github.com/hmandela/was-disaggregation/issues
Keywords: weather-generator,seasonal-forecast,downscaling,West-Africa,xarray
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Science/Research
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Scientific/Engineering :: Atmospheric Science
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: numpy>=1.24
Requires-Dist: scipy>=1.10
Requires-Dist: pandas>=2.0
Requires-Dist: xarray>=2023.6
Requires-Dist: netCDF4>=1.6
Requires-Dist: dask[array]>=2023.6
Provides-Extra: notebooks
Requires-Dist: jupyterlab>=4; extra == "notebooks"
Requires-Dist: nbformat>=5.9; extra == "notebooks"
Requires-Dist: nbclient>=0.9; extra == "notebooks"
Requires-Dist: matplotlib>=3.7; extra == "notebooks"
Provides-Extra: dask
Requires-Dist: dask[array,distributed]>=2023.6; extra == "dask"
Provides-Extra: ml
Requires-Dist: torch>=2.1; (platform_system != "Darwin" or platform_machine != "x86_64") and extra == "ml"
Requires-Dist: torch<2.3,>=2.1; (platform_system == "Darwin" and platform_machine == "x86_64") and extra == "ml"
Requires-Dist: numpy<2,>=1.24; (platform_system == "Darwin" and platform_machine == "x86_64") and extra == "ml"
Provides-Extra: test
Requires-Dist: pytest>=7.4; extra == "test"
Dynamic: license-file

# WAS Disaggregation

Forecast-conditioned stochastic weather generation and daily downscaling on
`xarray` grids, developed by Mandela HOUNGNIBO.

- PyPI distribution: `was-disaggregation`
- Python import: `was_disaggregation`
- Version: `0.10.0` (development/alpha)
- License: GPL-3.0-only; see `LICENSE`.

The distribution name was changed from the inconsistent `was-weather` metadata
in the original development archive. The `was-weather` command remains an alias
of `was-disaggregation`; Python imports use `was_disaggregation`.

## Install and develop

Install a published release with `pip`; Pixi is only needed if you choose the
repository's development and release workflow. Before 0.10.0 is published, use
the editable source installation below rather than requesting that version from
PyPI.

```bash
python -m pip install was-disaggregation
python -m pip install 'was-disaggregation[notebooks]'  # optional teaching tools
python -m pip install 'was-disaggregation[ml]'         # optional PyTorch methods
```

From a source checkout with Python 3.10 or newer and `pip`:

```bash
python -m pip install -e '.[test]'
python -m pytest -q
```

From the GitHub repository, with Pixi already installed:

```bash
git clone https://github.com/hmandela/was-disaggregation.git
cd was-disaggregation
pixi install
pixi run test
pixi run release-check
```

The default Pixi environment uses Python 3.12 on Linux x86-64, Windows x64,
macOS Intel and macOS Apple Silicon. The same committed `pixi.lock` covers all
four targets, and `.github/workflows/ci.yml` checks each one on GitHub after
the code is pushed. Additional environments
are `ml` and `notebooks`: `pixi run -e ml test` and
`pixi run -e notebooks jupyter lab`. PyTorch is imported only when a neural method
requires it. `pixi.lock` records the dependency solution; retain it in version
control when generated by `pixi install`.

For neural methods on an Intel Mac (including x86 Python under Rosetta), use
Python 3.10–3.12. The `ml` extra selects PyTorch 2.2.x and NumPy below 2 for
binary compatibility. This constraint does not apply to the statistical methods
without `ml`, or to native Apple Silicon. CI also checks neural CPU methods on
all four systems; those jobs must pass before claiming platform validation.

In `HindcastExperiment.run(n_jobs>1)`, Linux parallelizes leave-one-year-out
folds with `fork`. Windows and macOS run those folds sequentially and emit a
warning because arbitrary user-supplied fit/predict functions need not be
serializable for spawn-based workers. The results and `n_jobs=1` API remain
available on all platforms.

## Input contract

Daily observations use dimensions `(T, Y, X)` with explicit time, latitude and
longitude coordinates. Supported variables are `PRCP`, `TMIN`, `TMAX`, `HUMIN`,
`HUMAX`, `WIND` and `SOLAR`. Supply units on every variable; recognized units are
converted to these canonical units:

| Variable | Canonical unit |
| --- | --- |
| PRCP | mm d-1 |
| TMIN / TMAX | degC |
| HUMIN / HUMAX | % |
| WIND | m s-1 |
| SOLAR | MJ m-2 d-1 |

Wind force categories are not wind speeds. Daily solar input in W m-2 must be
a daily mean flux. Use `unit_overrides` with `open_observations` when a source's
metadata need correction. Grids and times of separate observation files must
align exactly. Coordinate aliases such as `time`, `lat` and `lon` are accepted.

Forecast probabilities are a `DataArray` with dimensions `(probability, Y, X)`
and labels `PB`, `PN`, `PA`. Fractions or percentages are accepted when their
sum is within 2% of 1 or 100, then normalized. Invalid triples are masked.
Constraint forecasts additionally accept labelled early/normal/late or
short/normal/long categories. Select the intended issue time explicitly when
working with a multi-time forecast file.

Months must be consecutive, for example `(7, 8, 9)` or `(12, 1, 2)`. The season
year is the year of its first month. The package uses Gregorian dates with
February 29 omitted. Native 360-day calendars require explicit preprocessing.
Incomplete observed days remain missing and cannot become zero rainfall totals.

## Generate daily weather

```python
from was_disaggregation import WeatherGenerator, open_observations, load_probabilities

obs = open_observations({"PRCP": "Daily_PRCP_1991_2024.nc"})
prob = load_probabilities("Forecast_Prob_PRCPJulAugSep_2025.nc", target=obs)
model = WeatherGenerator(
    months=(7, 8, 9), climatology=(1994, 2016), seed=42,
    spatial="distance", conditioning="mixture",
).fit(obs, prob)
generated = model.generate(year=2025, n_members=100)
generated.to_netcdf("weather_JAS_2025.nc")
obs.close()
```

Use the climatological period defining your forecast provider's categories;
`(1994, 2016)` is an example, not an inferred property of an input file.
For large domains, `generate_dask` builds a lazy tiled dataset. A command-line
example is:

```bash
pixi run was-disaggregation \
  --obs Daily_PRCP_1991_2024.nc \
  --forecast Forecast_Prob_PRCPJulAugSep_2025.nc \
  --output weather_JAS_2025.nc --year 2025 --months 7 8 9 \
  --climatology 1994 2016 --members 100 --member-batch 20 --tile 10 10
```

`examples/synthetic_smoke.py` provides a runnable example without external data.

The 2023 Houngnibo protocol can be selected explicitly (this example uses the
first rainfall generator, SRG1):

```python
model = WeatherGenerator(
    months=(7, 8, 9), climatology=(1994, 2016), seed=42,
    paper_protocol="houngnibo_2023_srg1",
).fit(obs, prob)
generated = model.generate(year=2025, n_members=100)
```

Use `houngnibo_2023_srg2` for mixed-exponential wet amounts or
`houngnibo_2023_srg3` for the three-state semi-parametric variant. If separate
seasonal probability forecasts for minimum and maximum temperature are
available, pass `temperature_probabilities={"TMIN": prob_min, "TMAX": prob_max}`
to `WeatherGenerator`, with these variables present in the observations.
The article presets fix their own rainfall conventions; their gridded spatial
component is an extension of the published station-level protocol.

For the forecast-plane rainfall mechanism of Wilks (2002), use
`WeatherGenerator(months=(7, 8, 9), climatology=(1994, 2016),
paper_protocol="wilks_2002").fit(obs, prob)`. It treats `PRCP > 0` as a wet
day, uses Gamma thresholds for historical seasonal rainfall terciles, and
estimates forecast-dependent mixed-exponential component means. The numerical
experiment in the article still requires the original observations and
forecast protocol.

## Methods and interpretation

| Family | Main entry points | Interpretation |
| --- | --- | --- |
| Parametric | `WeatherGenerator`, `fit_srg3`, `generate_dask` | Wet/dry or three-state Markov occurrence, Gamma/mixed-exponential/kernel rainfall, multivariate transformed residuals and spatial kernels |
| Historical resampling | `ApipattanavisGenerator`, `ForecastAnalogGenerator`, `ForecastSchaakeGenerator` | Daily three-state Markov + shared multivariate KNN, forecast-weighted years/blocks and rank templates |
| Multiple attributes | `SeasonalConstraint`, `SeasonalTotal`, `OnsetDate`, `CessationDate`, `MaxDrySpell` | MRE/Croley year weighting with soft or feasible exact constraints and member-selection diagnostics |
| GLM | `GLMWeatherGenerator`, `BayesianGLMWeatherGenerator`, `CoefficientGP` | Daily GLM, posterior hierarchical GP sampling, monthly joint temperature covariance |
| Dynamical | `DailyBiasCorrector`, `DynamicalDownscaler`, `NHMM`, `NHSMM`, `minimum_divergence_selection` | LOCI and quantile correction, latent states, ECC and MDSS dependence reordering |
| Neural | `GenerativeDownscaler` | Experimental scoring-rule, conditional GAN, EDM/residual diffusion and flow-matching variants requiring PyTorch |
| Verification | `HindcastExperiment`, `daily_scores`, `reliability_table`, `rank_histogram` | Refitting experiments and ensemble diagnostics |

These are implemented variants inspired by the literature named in module
docstrings. They are not certified reproductions of every cited method.
The 0.10.0 changes, mathematical derivations and remaining methodological
differences are mapped in [`docs/AUDIT_MATHEMATIQUE_0.10.0_FR.md`](docs/AUDIT_MATHEMATIQUE_0.10.0_FR.md).
The article-to-code mapping, implemented corrections and unreproduced
components are listed in [`docs/TRACEABILITE_SCIENTIFIQUE_0.10.0_FR.md`](docs/TRACEABILITE_SCIENTIFIQUE_0.10.0_FR.md).
Each scientific module and its principal methods include original authors,
article titles, DOI/primary links and the scope of the implementation.
Software authorship is separate from those scientific references.
The English Master 1 practical is [`notebooks/Mathematical_Validation_0_10_0_EN.ipynb`](notebooks/Mathematical_Validation_0_10_0_EN.ipynb).
Conditioning daily parameters or drawing parameter classes does not guarantee
that generated seasonal totals exactly reproduce PB/PN/PA frequencies. In
particular, automatic mixture shrinkage is an approximate interpolation of
fitted parameters. Finite Markov-reward moments are exact for latent first-order
occurrence; run-length occurrence and amount feedback require simulation.
MRE/Croley constraints use historical weights: `soft` penalizes
deviations, while `exact` enforces feasible category equalities and rejects
infeasible targets. Inspect achieved probabilities and effective sample sizes.

Cross-validation must fit corrections, scalers, predictors and model parameters
using training data only. The climatology defining externally supplied forecast
categories is a separate explicit choice. Validate independent hindcasts against
observed seasonal totals, wet-day frequency, spells, extremes, temporal/spatial
dependence, variable bounds and reliability before operational use. The included
regressions check software behavior; no regional skill claim is inferred from
synthetic tests.

## Publication

See [`docs/RELEASE_0.10.0_FR.md`](docs/RELEASE_0.10.0_FR.md) for GitHub, TestPyPI
and PyPI steps with pip/Miniconda or optional Pixi. `docs/PUBLICATION_PIXI_FR.md`
is the archived guide for 0.8.0. Tokens are entered at the local prompt and
are never written into this source archive. Change
`was_disaggregation/_version.py` before each new release; PyPI does not allow
reusing a previously uploaded artifact name.
