241 lines
7.0 KiB
Markdown
241 lines
7.0 KiB
Markdown
# Feature: PID Controller
|
||
|
||
## Overview
|
||
|
||
The PID (Proportional-Integral-Derivative) step size controller is an advanced adaptive time-stepping controller that provides better stability and efficiency than the basic PI controller, especially for difficult or oscillatory problems.
|
||
|
||
**Key Characteristics:**
|
||
- Three-term control: proportional, integral, and derivative components
|
||
- More stable than PI for challenging problems
|
||
- Standard in production ODE solvers
|
||
- Can prevent oscillatory step size behavior
|
||
|
||
## Why This Feature Matters
|
||
|
||
- **Robustness**: Handles difficult problems that cause PI controller to oscillate
|
||
- **Industry standard**: Used in MATLAB, Sundials, and other production solvers
|
||
- **Minimal overhead**: Small computational cost for significant stability improvement
|
||
- **Smooth stepping**: Reduces erratic step size changes
|
||
|
||
## Dependencies
|
||
|
||
- None (extends current controller infrastructure)
|
||
|
||
## Implementation Approach
|
||
|
||
### Mathematical Formulation
|
||
|
||
The PID controller determines the next step size based on error estimates from the current and previous steps:
|
||
|
||
```
|
||
h_{n+1} = h_n * (ε_n)^(-β₁) * (ε_{n-1})^(-β₂) * (ε_{n-2})^(-β₃)
|
||
```
|
||
|
||
Where:
|
||
- ε_i = error estimate at step i (normalized by tolerance)
|
||
- β₁ = proportional coefficient (typically ~0.3 to 0.5)
|
||
- β₂ = integral coefficient (typically ~0.04 to 0.1)
|
||
- β₃ = derivative coefficient (typically ~0.01 to 0.05)
|
||
|
||
Standard formula (Hairer & Wanner):
|
||
```
|
||
h_{n+1} = h_n * safety * (ε_n)^(-β₁/(k+1)) * (ε_{n-1})^(-β₂/(k+1)) * (h_n/h_{n-1})^(-β₃/(k+1))
|
||
```
|
||
|
||
Where k is the order of the method.
|
||
|
||
### Advantages Over PI
|
||
|
||
- **PI controller**: Uses only current and previous error (2 terms)
|
||
- **PID controller**: Also uses rate of change of error (3 terms)
|
||
- **Result**: Anticipates trends, prevents overshoot
|
||
|
||
### Implementation Design
|
||
|
||
```rust
|
||
pub struct PIDController {
|
||
// Coefficients
|
||
pub beta1: f64, // Proportional
|
||
pub beta2: f64, // Integral
|
||
pub beta3: f64, // Derivative
|
||
|
||
// Constraints
|
||
pub factor_min: f64, // qmax inverse
|
||
pub factor_max: f64, // qmin inverse
|
||
pub h_max: f64,
|
||
pub safety_factor: f64,
|
||
|
||
// State (error history)
|
||
pub err_old: f64, // ε_{n-1}
|
||
pub err_older: f64, // ε_{n-2}
|
||
pub h_old: f64, // h_{n-1}
|
||
|
||
// Next step guess
|
||
pub next_step_guess: TryStep,
|
||
}
|
||
```
|
||
|
||
## Implementation Tasks
|
||
|
||
### Core Controller
|
||
|
||
- [ ] Define `PIDController` struct
|
||
- [ ] Add beta1, beta2, beta3 coefficients
|
||
- [ ] Add constraint fields (factor_min, factor_max, h_max, safety)
|
||
- [ ] Add state fields (err_old, err_older, h_old)
|
||
- [ ] Add next_step_guess field
|
||
|
||
- [ ] Implement `Controller<D>` trait
|
||
- [ ] `determine_step()` method
|
||
- [ ] Handle first step (no history)
|
||
- [ ] Handle second step (partial history)
|
||
- [ ] Full PID formula for subsequent steps
|
||
- [ ] Apply safety factor and limits
|
||
- [ ] Update error history
|
||
- [ ] Return TryStep::Accepted or NotYetAccepted
|
||
|
||
- [ ] Constructor methods
|
||
- [ ] `new()` with all parameters
|
||
- [ ] `default()` with standard coefficients
|
||
- [ ] `for_order()` - scale coefficients by method order
|
||
|
||
- [ ] Helper methods
|
||
- [ ] `reset()` - clear history (for algorithm switching)
|
||
- [ ] Update state after accepted/rejected steps
|
||
|
||
### Standard Coefficient Sets
|
||
|
||
Different coefficient sets for different problem classes:
|
||
|
||
- [ ] **Default (H312)**:
|
||
- β₁ = 1/4, β₂ = 1/4, β₃ = 0
|
||
- Actually a PI controller with specific tuning
|
||
- Good general-purpose choice
|
||
|
||
- [ ] **H211**:
|
||
- β₁ = 1/6, β₂ = 1/6, β₃ = 0
|
||
- More conservative
|
||
|
||
- [ ] **Full PID (Gustafsson)**:
|
||
- β₁ = 0.49/(k+1)
|
||
- β₂ = 0.34/(k+1)
|
||
- β₃ = 0.10/(k+1)
|
||
- True PID behavior
|
||
|
||
### Integration
|
||
|
||
- [ ] Export PIDController in prelude
|
||
- [ ] Update Problem to accept any Controller trait
|
||
- [ ] Examples using PID controller
|
||
|
||
### Testing
|
||
|
||
- [ ] **Comparison test: Smooth problem**
|
||
- [ ] Run exponential decay with PI and PID
|
||
- [ ] Both should perform similarly
|
||
- [ ] Verify PID doesn't hurt performance
|
||
|
||
- [ ] **Oscillatory problem test**
|
||
- [ ] Problem that causes PI to oscillate step sizes
|
||
- [ ] Example: y'' + ω²y = 0 with varying ω
|
||
- [ ] PID should have smoother step size evolution
|
||
- [ ] Plot step size vs time for both
|
||
|
||
- [ ] **Step rejection handling**
|
||
- [ ] Verify history updated correctly after rejection
|
||
- [ ] Doesn't blow up or get stuck
|
||
|
||
- [ ] **Reset test**
|
||
- [ ] Algorithm switching scenario
|
||
- [ ] Verify reset() clears history appropriately
|
||
|
||
- [ ] **Coefficient tuning test**
|
||
- [ ] Try different β values
|
||
- [ ] Verify stability bounds
|
||
- [ ] Document which work best for which problems
|
||
|
||
### Benchmarking
|
||
|
||
- [ ] Add PID option to existing benchmarks
|
||
- [ ] Compare step count and function evaluations vs PI
|
||
- [ ] Measure overhead (should be negligible)
|
||
|
||
### Documentation
|
||
|
||
- [ ] Docstring explaining PID control
|
||
- [ ] When to prefer PID over PI
|
||
- [ ] Coefficient selection guidance
|
||
- [ ] Example comparing PI and PID behavior
|
||
|
||
## Testing Requirements
|
||
|
||
### Oscillatory Test Problem
|
||
|
||
Problem designed to expose step size oscillation:
|
||
|
||
```rust
|
||
// Prothero-Robinson equation
|
||
// y' = λ(y - φ(t)) + φ'(t)
|
||
// where φ(t) = sin(ωt), λ << 0 (stiff), ω moderate
|
||
//
|
||
// This problem can cause step size oscillation with PI
|
||
```
|
||
|
||
Expected: PID should maintain more stable step sizes.
|
||
|
||
### Step Size Stability Metric
|
||
|
||
Track standard deviation of log(h_i/h_{i-1}) over the integration:
|
||
- PI controller: may have σ > 0.5
|
||
- PID controller: should have σ < 0.3
|
||
|
||
## References
|
||
|
||
1. **PID Controllers for ODE**:
|
||
- Gustafsson, K., Lundh, M., and Söderlind, G. (1988)
|
||
- "A PI stepsize control for the numerical solution of ordinary differential equations"
|
||
- BIT Numerical Mathematics, 28, 270-287
|
||
|
||
2. **Implementation Details**:
|
||
- Hairer, E., Nørsett, S.P., and Wanner, G. (1993)
|
||
- "Solving Ordinary Differential Equations I", Section II.4
|
||
- PID controller discussion
|
||
|
||
3. **Coefficient Selection**:
|
||
- Söderlind, G. (2002)
|
||
- "Automatic Control and Adaptive Time-Stepping"
|
||
- Numerical Algorithms, 31, 281-310
|
||
|
||
4. **Julia Implementation**:
|
||
- `OrdinaryDiffEq.jl/lib/OrdinaryDiffEqCore/src/integrators/controllers.jl`
|
||
- Look for `PIDController`
|
||
|
||
## Complexity Estimate
|
||
|
||
**Effort**: Small (3-5 hours)
|
||
- Straightforward extension of PI controller
|
||
- Main work is getting coefficients right
|
||
- Testing requires careful problem selection
|
||
|
||
**Risk**: Low
|
||
- Well-understood algorithm
|
||
- Minimal code changes
|
||
- Easy to validate
|
||
|
||
## Success Criteria
|
||
|
||
- [ ] Implements full PID formula correctly
|
||
- [ ] Handles first/second step bootstrap
|
||
- [ ] Shows improved stability on oscillatory test problem
|
||
- [ ] Performance similar to PI on smooth problems
|
||
- [ ] Error history management correct after rejections
|
||
- [ ] Documentation complete with usage examples
|
||
- [ ] Coefficient sets match literature values
|
||
|
||
## Future Enhancements
|
||
|
||
- [ ] Automatic coefficient selection based on problem characteristics
|
||
- [ ] More sophisticated controllers (H0211b, predictive)
|
||
- [ ] Limiter functions to prevent extreme changes
|
||
- [ ] Per-algorithm default coefficients
|