|
1 | | -# PvLib-Rust Changelog |
| 1 | +# pvlib-rust Changelog |
| 2 | + |
| 3 | +## [0.1.6] - 2026-04-20 |
| 4 | + |
| 5 | +### Changed |
| 6 | +- **License** changed from MIT to Apache-2.0. Prior releases remain available under MIT. |
| 7 | +- **`CHANGELOG.md`** entries for 0.1.1 through 0.1.4 rewritten in neutral engineering tone. The original entries used marketing language ("Immune by Design", "Ultimate 10x Performance Sweep") that misrepresented the scope of the work. |
| 8 | +- **`reqwest` is now an optional dependency** behind the `pvgis` feature (on by default). Users who do not need PVGIS/SAM HTTP helpers can build with `default-features = false` to drop the TLS/hyper/tokio dependency tree. |
| 9 | +- **`reqwest`** uses `rustls-tls` instead of the default system TLS stack, to simplify static-binary builds. |
| 10 | +- **Build profile**: `[profile.release]` now sets `lto = "fat"`, `codegen-units = 1`, `panic = "abort"`, `strip = "debuginfo"`. |
| 11 | + |
| 12 | +### Added |
| 13 | +- Crate-level rustdoc in `src/lib.rs` with runnable Quick Start example. |
| 14 | +- Top-level re-exports (`pvlib::Location`, `pvlib::BatchModelChain`, `pvlib::WeatherSeries`, `pvlib::SimulationSeries`, `pvlib::PVSystem`, `pvlib::ModelChain`). |
| 15 | +- `Location::try_new` — validates `latitude ∈ [-90, 90]`, `longitude ∈ [-180, 180]`, finite altitude. |
| 16 | +- `criterion` dev-dependency and `benches/batch_tmy.rs` for reproducible TMY-year timing. |
| 17 | +- `.github/workflows/ci.yml` with fmt/clippy/test/doc/wasm matrix. |
| 18 | +- `examples/annual_tmy.rs` — runnable end-to-end TMY simulation. |
| 19 | +- `ROADMAP.md` — enumerates pending work (true DIRINT coefficient table, Reno–Hansen `detect_clearsky`, `infinite_sheds` port, PyO3/maturin wheel, Polars integration, proptest suite, pvlib-python numerical-parity fixtures). |
| 20 | +- `#[non_exhaustive]` on public configuration structs and model-selection enums that are expected to grow (`WeatherSeries`, `SimulationSeries`, `BatchModelChain`, `DCModel`, `ACModel`, `AOIModel`, `TemperatureModel`, `TranspositionModel`, `LossesModel`, `SpectralModel`). Users who previously constructed these via `..` record syntax must migrate to constructors/builders. |
| 21 | +- `#[must_use]` on aggregate accessors (`SimulationSeries::total_energy_wh`, `peak_power`, `capacity_factor`) and expensive constructors. |
| 22 | +- MSRV declared: `rust-version = "1.85"` (required by `edition = "2024"`). |
| 23 | + |
| 24 | +### Fixed |
| 25 | +- `src/albedo.rs:inland_water_dvoracek` no longer panics on unknown `surface_condition`; returns `f64::NAN` and emits a one-time warning. A new `inland_water_dvoracek_try` returns `Option<f64>` for callers that want to detect this explicitly. |
| 26 | +- `src/iotools.rs` PVGIS/SAM HTTP calls now use a shared `reqwest::blocking::Client` with a 30-second timeout. Previously unbounded (`reqwest::blocking::get`) and could hang indefinitely on stalled connections. |
| 27 | +- `src/iotools.rs` TMY3/EPW readers now use bounds-checked field access; malformed rows produce a structured error instead of panicking with an OOB index. |
| 28 | +- `src/pvsystem.rs` — `trait Mount` now requires `Send + Sync`, so `Array` / `PVSystem` / `ModelChain` are safe to share across threads (`Arc<ModelChain>` in an async handler, parallel iteration, etc.). |
| 29 | +- `src/irradiance.rs:erbs` — explicit guard returns `(0.0, 0.0)` on non-finite `ghi` or `zenith` instead of propagating NaN. |
| 30 | +- `src/irradiance.rs:disc` — guards against NaN airmass producing NaN DNI below the zenith cutoff. |
| 31 | +- `src/iam.rs:physical` — output clamped to `[0.0, 1.0]` to match `pvlib.iam.physical` semantics and avoid super-unit IAMs at grazing angles from floating-point drift. |
| 32 | +- `src/modelchain.rs` — `ACModel::Sandia` and `ACModel::ADR` now return an explicit `unimplemented!("…")` error instead of silently delegating to PVWatts. The variants are retained for API stability but callers get a clear message. |
| 33 | +- Hot-path day-of-year computation (`BatchModelChain::run`, `ModelChain::run_model_from_weather`, `ModelChain::run_model_from_poa`) now uses `chrono::Datelike::ordinal()` instead of `format("%j").to_string().parse::<i32>().unwrap_or(1)`. Two heap allocations per timestep removed. |
| 34 | + |
| 35 | +### Full ports (algorithms that were previously labelled approximations) |
| 36 | +- **`irradiance::dirint`** is now a faithful port of `pvlib.irradiance.dirint`, including the full 6×6×7×5 Perez (1992) coefficient tensor (lives in the private `dirint_coeffs` module). Signature changed from |
| 37 | + `fn dirint(ghi, zenith, dew_point, pressure, dni_extra) -> (f64, f64)` to |
| 38 | + `fn dirint(ghi, zenith, day_of_year, pressure_pa, temp_dew_c: Option<f64>) -> f64` (returns DNI). A new |
| 39 | + `irradiance::dirint_series(&[ghi], &[zenith], &[doy], pressure_pa, temp_dew: Option<&[f64]>, use_delta_kt_prime: bool) -> Vec<f64>` |
| 40 | + propagates Δkt′ temporal persistence (Perez eqns 2/3). `clearness_index_zenith_independent` and `precipitable_water_from_dew_point` are now public helpers. |
| 41 | +- **`clearsky::detect_clearsky`** is now a faithful port of the Reno–Hansen (2016) windowed 5-criterion algorithm with iterative α rescaling. Signature changed from `fn detect_clearsky(ghi: f64, clearsky_ghi: f64, _window_length: usize) -> bool` to |
| 42 | + `fn detect_clearsky(measured: &[f64], clearsky: &[f64], sample_interval_minutes: f64, thresholds: ClearSkyThresholds) -> Vec<bool>`. |
| 43 | + New types: `ClearSkyThresholds` (with `::default()` matching Reno–Hansen 2016 and `::from_sample_interval()` matching Jordan–Hansen 2023 Table 1) and `ClearSkyDetectionResult`; `detect_clearsky_detail` returns clear-sample flags plus the fitted α and iteration count. |
| 44 | +- **`bifacial`** replaces the single linear `(1 − exp(−h/pitch)) · GHI · albedo` approximation with the real Marion 2017 / Mikofski 2019 2-D view-factor primitives: `vf_row_sky_2d`, `vf_row_sky_2d_integ`, `vf_row_ground_2d`, `vf_row_ground_2d_integ`, `solar_projection_tangent`, `unshaded_ground_fraction`, plus a composed rear-irradiance helper `rear_irradiance_sheds`. `get_irradiance_infinite_sheds` is retained (same signature, `height` / `pitch` now ignored) and now computes rear irradiance via those real view factors. The remaining gap vs. `pvlib.bifacial.infinite_sheds.get_irradiance_poa` is the front-face shadow-fraction pipeline, still in `ROADMAP.md`. |
| 45 | +- README speed comparison softened: pvlib-python vectorized path is typically sub-second for a TMY year; the 500–1250× headline was based on a non-vectorized reference and has been replaced with a more representative range. |
| 46 | + |
| 47 | +## [0.1.5] - 2026-03-28 |
| 48 | +- Minor fixes and Cargo.lock update. |
2 | 49 |
|
3 | 50 | ## [0.1.4] - 2026-03-28 |
| 51 | + |
4 | 52 | ### Added |
5 | | -- **Batch API Improvements** — 7 new features based on real-world production consumer feedback: |
6 | | - - `solar_elevation` field in `SimulationSeries` (eliminates `90 - zenith` boilerplate) |
7 | | - - `solar_position_batch_utc()` — accepts `NaiveDateTime` (UTC) directly, no `DateTime<Tz>` needed |
8 | | - - `WeatherSeries::from_utc()` — construct weather input from UTC timestamps + timezone string |
9 | | - - `with_auto_decomposition(true)` — auto Erbs GHI→DNI/DHI when DNI/DHI are zero or NaN |
10 | | - - `with_bifacial(factor, albedo)` — rear-side gain at DC level, capped at 25% |
11 | | - - `with_system_losses(0.14)` — flat DC derating, clamped to [0.0, 1.0] |
12 | | -- **NaN Robustness** — NaN DNI/DHI from upstream Python/Polars pipelines treated as missing values, triggering auto-decomposition when enabled |
13 | | -- **10 new edge-case tests** covering NaN propagation, loss clamping, bifacial zero-POA safety, extreme parameters |
| 53 | +- **Batch API extensions**: |
| 54 | + - `solar_elevation` field on `SimulationSeries` (removes `90 - zenith` boilerplate at call sites). |
| 55 | + - `solar_position_batch_utc()` — accepts `&[NaiveDateTime]` (UTC). |
| 56 | + - `WeatherSeries::from_utc()` — constructs weather input from UTC timestamps plus an IANA timezone string. |
| 57 | + - `with_auto_decomposition(true)` — runs Erbs GHI→DNI/DHI when both DNI and DHI are zero or NaN. |
| 58 | + - `with_bifacial(bifaciality_factor, ground_albedo)` — rear-side gain applied at the DC level, clamped at 25%. |
| 59 | + - `with_system_losses(fraction)` — flat DC derating, clamped to `[0.0, 1.0]`. |
| 60 | +- NaN-in-NaN-out semantics documented for DNI/DHI upstream of `BatchModelChain::run`. |
| 61 | +- 10 edge-case tests covering NaN propagation, loss clamping, and bifacial zero-POA safety. |
14 | 62 |
|
15 | 63 | ### Fixed |
16 | | -- Bifacial gain moved from post-AC to pre-inverter (DC level) — correct physics modeling |
17 | | -- Zero clippy warnings (`cargo clippy -D warnings` passes clean) |
18 | | -- 337 total tests (up from 319) |
| 64 | +- Bifacial gain moved from post-AC to pre-inverter (DC level). |
| 65 | +- Clean `cargo clippy -D warnings` pass. |
19 | 66 |
|
20 | 67 | ## [0.1.3] - 2026-03-22 |
21 | | -### Added |
22 | | -- **Performance & Architectural Certification**: Handled the "Ultimate 10x Performance Sweep". Scraped 10 major historical architectural bottlenecks from `pvlib-python` (including Pandas Memory Initialization locks, Numba JIT cache-leaking, timezone object-casting lags, and transcendental broadcasting limits). |
23 | | -- **Core Engine Validation**: `PvLib-Rust` mathematically verifies its superiority: |
24 | | - - Horner's method is natively utilized in the `DISC` model, preventing iterative `pow()` function degradation algorithmically resolved in Python `#1180`. |
25 | | - - Rayon's `par_iter()` maps contiguous `f64` slice blocks identically to Python's hard-fought Numba vectorizations, natively bypassing the `pandas.Series` index-alignment hash-lookup lag (#1887). |
26 | | - - True zero-cost abstraction: Compiled statically via LLVM, avoiding `pvlib.use_numba` dynamic memory-leak cache crashes (#401) entirely. |
27 | | - - Memory mapping generates strictly presized contiguous Rust vectors, bypassing compounding runtime allocations known in `pvlib-python` scaling pipelines (#502). |
| 68 | + |
| 69 | +### Changed |
| 70 | +- Audit of `pvlib-python` performance-related GitHub issues and checked which do not apply to the Rust port due to language-level differences (no Numba cache, no pandas index alignment, native f64 parallel-iter). No algorithmic changes. |
28 | 71 |
|
29 | 72 | ## [0.1.2] - 2026-03-22 |
30 | | -### Added |
31 | | -- **Mathematical Audit Certification**: Ran a 3-phase autonomous deep-scraping agent against the `pvlib-python` GitHub issue tracker. Verified `PvLib-Rust` algorithmically against 18 historic, highly complex mathematical flaws patched in the Python repository. |
32 | | -- **Audit Findings**: |
33 | | - - **Immune by Design**: `PvLib-Rust` utilizes native Rust paradigms mapping such as `f64::clamp()` to rigorously guard trigonometric boundaries. The codebase is confirmed immune to: |
34 | | - - `acos(1.000002)` floating-point `NaN` crashes. |
35 | | - - Physical IAM backwards-illumination `NaN` overflows ($AOI > 90^\circ$). |
36 | | - - AOI sign-mirroring errors involving `abs(cos_aoi)`. |
37 | | - - Haurwitz clearsky exponential blowouts near sunset ($\cos \theta \to 0$). |
38 | | - - Townsend Snow $\gamma$ parameter negative square roots. |
39 | | - - **Immune by Structural Safety**: Abstracting to `BatchModelChain` cleanly isolates inverter logic, mitigating Python's double-clipping AC generic-loss defects. |
40 | | - - **Continuous Integrations**: Rust port relies heavily on analytical formulations (Anderson & Mikofski 2020 single-axis geometries and exact 2D algebraic sheds) bypassing previous Python issues such as trapezoidal sky view factor noise or tilted tracking axis backtracking errors. |
| 73 | + |
| 74 | +### Changed |
| 75 | +- Audit of `pvlib-python` numerical-correctness issues; verified existing Rust implementations already guard the relevant edge cases via `f64::clamp` on trigonometric inputs. No algorithmic changes. |
| 76 | + |
| 77 | +### Notes |
| 78 | +- Previous wording in this entry (e.g., "Immune by Design", "Mathematical Audit Certification") overstated the scope of the work and has been replaced with the neutral summary above. |
41 | 79 |
|
42 | 80 | ## [0.1.1] - 2026-03-21 |
| 81 | + |
43 | 82 | ### Fixed |
44 | | -- Fixed critical `zip` lengths panic vulnerabilities inside the ` rayon` batch module processing iterators by establishing strict parallel mapping lengths. |
45 | | -- Clamped cross-axis tracking geometric denominators inside `shading.rs` to stop division-by-zero occurrences when sun aligns perpendicularly to the tracking grid. |
46 | | -- Guarded `snow.rs` bounds against Townsend thermal sliding parameter collapses. |
| 83 | +- `zip` length mismatches inside the `rayon` batch module now enforce equal lengths. |
| 84 | +- Division-by-zero guard in `shading.rs` cross-axis geometry when the sun is perpendicular to the tracking axis. |
| 85 | +- Bounds guard in `snow.rs` Townsend thermal-sliding parameter. |
47 | 86 |
|
48 | 87 | ## [0.1.0] - 2026-03-21 |
| 88 | + |
49 | 89 | ### Added |
50 | | -- Initial port released. |
51 | | -- Implemented core phenomenological models: IAM, Tracking, Atmosphere, PVSystem, SolarPosition, and Rayon parallel Batch processing. |
| 90 | +- Initial port: core modules (IAM, tracking, atmosphere, PVSystem, solar position) plus a rayon-parallel batch API. |
0 commit comments