# Mathematical Foundation

## Overview

PrecipGen implements the Richardson & Wright (1984) WGEN algorithm with modern enhancements for trend detection and non-stationary climate modeling.

## Core WGEN Algorithm

### Precipitation Occurrence Model

Daily precipitation occurrence follows a first-order Markov chain:

```
P(W_t | W_{t-1}) = P(W|W)  # Wet day follows wet day
P(W_t | D_{t-1}) = P(W|D)  # Wet day follows dry day
```

Where:
- `W_t` = wet day on day t
- `D_t` = dry day on day t  
- `P(W|W)` = wet-to-wet transition probability
- `P(W|D)` = dry-to-wet transition probability

### Precipitation Amount Model

When precipitation occurs, amounts follow a Gamma distribution:

```
f(x) = (x^{α-1} * e^{-x/β}) / (β^α * Γ(α))
```

Where:
- `α` = shape parameter (alpha)
- `β` = scale parameter (beta)
- `Γ(α)` = gamma function
- `x` = precipitation amount (x > 0)

### Parameter Estimation

#### Transition Probabilities

From historical data, transition probabilities are estimated as:

```python
# Count transitions
n_ww = count(wet_day[t] and wet_day[t-1])
n_wd = count(wet_day[t] and dry_day[t-1])  
n_dw = count(dry_day[t] and wet_day[t-1])
n_dd = count(dry_day[t] and dry_day[t-1])

# Calculate probabilities
p_ww = n_ww / (n_ww + n_dw)
p_wd = n_wd / (n_wd + n_dd)
```

#### Gamma Parameters

Using method of moments:

```python
wet_amounts = precipitation[precipitation > threshold]
mean_amount = wet_amounts.mean()
var_amount = wet_amounts.var()

# Method of moments estimators
beta = var_amount / mean_amount    # Scale parameter
alpha = mean_amount / beta         # Shape parameter
```

Alternative maximum likelihood estimation:

```python
from scipy.stats import gamma

# Fit gamma distribution
alpha_mle, loc, beta_mle = gamma.fit(wet_amounts, floc=0)
```

## Seasonal Parameterization

Parameters vary by month to capture seasonal patterns:

```python
for month in range(1, 13):
    month_data = data[data.index.month == month]
    params[month] = estimate_parameters(month_data)
```

Monthly parameters ensure realistic seasonal precipitation patterns.

## Trend Detection

### Sliding Window Analysis

To detect non-stationary climate patterns:

```python
window_years = 30
step_years = 1

for start_year in range(first_year, last_year - window_years + 1, step_years):
    end_year = start_year + window_years
    window_data = data[(data.index.year >= start_year) & 
                       (data.index.year < end_year)]
    
    window_params = estimate_parameters(window_data)
    # Store parameters with window center year
```

### Trend Regression

For each parameter and season, fit linear trends:

```python
from scipy.stats import linregress

# Seasonal grouping
seasons = {
    'Winter': [12, 1, 2],
    'Spring': [3, 4, 5], 
    'Summer': [6, 7, 8],
    'Fall': [9, 10, 11]
}

for season, months in seasons.items():
    for param in ['p_ww', 'p_wd', 'alpha', 'beta']:
        # Extract parameter time series
        years = window_center_years
        values = [window_params[year][season][param] for year in years]
        
        # Linear regression
        slope, intercept, r_value, p_value, std_err = linregress(years, values)
        
        # Store trend information
        trends[season][param] = {
            'slope': slope,
            'p_value': p_value,
            'significance': 'significant' if p_value < 0.05 else 'not significant'
        }
```

### Statistical Significance

Trends are tested for statistical significance:

- **Null hypothesis**: No trend (slope = 0)
- **Alternative hypothesis**: Significant trend (slope ≠ 0)
- **Test statistic**: t-statistic from linear regression
- **Significance level**: α = 0.05 (configurable)

```python
import numpy as np
from scipy import stats

def test_trend_significance(years, values, alpha=0.05):
    """Test if trend is statistically significant."""
    slope, intercept, r_value, p_value, std_err = stats.linregress(years, values)
    
    is_significant = p_value < alpha
    confidence_level = (1 - alpha) * 100
    
    return {
        'slope': slope,
        'p_value': p_value,
        'is_significant': is_significant,
        'confidence_level': f"{confidence_level}%"
    }
```

## Non-Stationary Simulation

### Trend-Adjusted Parameters

For non-stationary simulation, parameters evolve with time:

```python
def get_trend_adjusted_params(base_params, trends, target_year, base_year=2000):
    """Adjust parameters for target year based on trends."""
    adjusted_params = base_params.copy()
    year_offset = target_year - base_year
    
    for season in trends:
        for param in trends[season]:
            slope = trends[season][param]['slope']
            if trends[season][param]['is_significant']:
                adjusted_params[season][param] += slope * year_offset
    
    return adjusted_params
```

### Bounded Parameter Adjustment

Ensure parameters remain within valid ranges:

```python
def bound_parameters(params):
    """Ensure parameters are within valid bounds."""
    # Transition probabilities: [0, 1]
    params['p_ww'] = np.clip(params['p_ww'], 0.0, 1.0)
    params['p_wd'] = np.clip(params['p_wd'], 0.0, 1.0)
    
    # Gamma parameters: > 0
    params['alpha'] = max(params['alpha'], 0.01)
    params['beta'] = max(params['beta'], 0.01)
    
    return params
```

## Validation Metrics

### Statistical Alignment

Synthetic data should match historical statistics:

```python
def validate_synthetic_data(historical, synthetic):
    """Validate synthetic data against historical."""
    metrics = {}
    
    # Monthly means
    hist_monthly = historical.groupby(historical.index.month).mean()
    synth_monthly = synthetic.groupby(synthetic.index.month).mean()
    metrics['monthly_mean_error'] = np.mean(np.abs(hist_monthly - synth_monthly))
    
    # Monthly standard deviations  
    hist_std = historical.groupby(historical.index.month).std()
    synth_std = synthetic.groupby(synthetic.index.month).std()
    metrics['monthly_std_error'] = np.mean(np.abs(hist_std - synth_std))
    
    # Wet day frequency
    hist_wet_freq = (historical > 0.1).mean()
    synth_wet_freq = (synthetic > 0.1).mean()
    metrics['wet_day_freq_error'] = abs(hist_wet_freq - synth_wet_freq)
    
    # Dry spell analysis
    hist_dry_spells = calculate_dry_spell_distribution(historical)
    synth_dry_spells = calculate_dry_spell_distribution(synthetic)
    metrics['dry_spell_ks_stat'] = ks_test(hist_dry_spells, synth_dry_spells)
    
    return metrics
```

### Goodness-of-Fit Tests

Statistical tests for model validation:

```python
from scipy.stats import kstest, anderson

def goodness_of_fit_tests(wet_amounts):
    """Test if wet amounts follow gamma distribution."""
    # Fit gamma distribution
    alpha, loc, beta = gamma.fit(wet_amounts, floc=0)
    
    # Kolmogorov-Smirnov test
    ks_stat, ks_p = kstest(wet_amounts, lambda x: gamma.cdf(x, alpha, scale=beta))
    
    # Anderson-Darling test
    ad_stat, ad_critical, ad_significance = anderson(wet_amounts, dist='gamma')
    
    return {
        'ks_statistic': ks_stat,
        'ks_p_value': ks_p,
        'ad_statistic': ad_stat,
        'gamma_params': {'alpha': alpha, 'beta': beta}
    }
```

## Implementation Notes

### Numerical Stability

- Use log-space calculations for gamma function evaluation
- Apply parameter bounds to prevent numerical issues
- Handle edge cases (all dry periods, extreme values)

### Performance Optimization

- Vectorized operations using NumPy
- Efficient sliding window implementation
- Parallel processing for batch operations

### Quality Assurance

- Comprehensive unit tests for all mathematical functions
- Property-based testing for statistical properties
- Benchmark against known reference implementations

## References

1. Richardson, C.W. and Wright, D.A., 1984. WGEN: A model for generating daily weather variables. US Department of Agriculture, Agricultural Research Service.

2. Wilks, D.S., 2011. Statistical methods in the atmospheric sciences (Vol. 100). Academic press.

3. Semenov, M.A. and Barrow, E.M., 1997. Use of a stochastic weather generator in the development of climate change scenarios. Climatic change, 35(4), pp.397-414.

4. Chen, J., Brissette, F.P. and Lucas-Picher, P., 2014. Assessing the limits of bias-correcting climate model outputs for climate change impact studies. Journal of Geophysical Research: Atmospheres, 119(2), pp.1123-1136.