pvlib-rust 0.1.6

A Rust port of pvlib-python: solar energy modeling toolkit
Documentation
# pvlib-rust roadmap

Items deliberately deferred past v0.1.6. They are tracked here so the
current `src/` reflects only what has been implemented and validated, and
so downstream users can see what is genuinely on the way versus marketing.

## Numerical parity (high priority)

1. ~~True DIRINT~~**shipped in 0.1.6.** Full Perez (1992) 4-D
   coefficient tensor in `src/dirint_coeffs.rs`, scalar and series APIs
   in `irradiance::{dirint, dirint_series}`.
2. ~~True `detect_clearsky` (Reno–Hansen 2016)~~**shipped in 0.1.6.**
   Windowed 5-criterion algorithm with iterative α rescaling in
   `clearsky::{detect_clearsky, detect_clearsky_detail,
   ClearSkyThresholds}`.
3. **Full `infinite_sheds.get_irradiance_poa`** — the 2-D view-factor
   primitives (`vf_row_sky_2d*`, `vf_row_ground_2d*`,
   `unshaded_ground_fraction`, `solar_projection_tangent`) are in
   `src/bifacial.rs` as of 0.1.6. What's still missing vs.
   `pvlib.bifacial.infinite_sheds` is the front-face shadow-fraction
   pipeline (`_shaded_fraction`, `_poa_sky_diffuse_pv` with split
   shaded / unshaded integrals, multi-row ground view factor
   `vf_ground_sky_2d_integ`) that composes front POA from these primitives.
4. **Perez 1990 coefficient table** — replace the 7-digit coefficients
   currently in `irradiance::perez` with the 4-digit published values
   used by `pvlib-python` (the extra digits appear fabricated and make
   bit-identical parity impossible).
5. **pvlib-python reference fixtures** — commit a pinned CSV produced
   by a specific `pvlib-python` version (e.g. 0.11.2) for a canonical
   location + weather input, and add `tests/test_python_parity.rs` that
   diffs `ModelChain` output against it with documented tolerances.
6. **Solar position refraction correction** — wire the `spa` crate's
   `pressure` and `temperature` arguments through
   `solarposition::get_solarposition`. Currently ignored, which drifts
   the apparent zenith by up to ~0.1° at low sun.

## API / ergonomics (medium priority)

7. **Unified error type** — replace the `Box<dyn Error>` returns in
   `iotools` and the `Result<_, String>` returns in `batch` with a
   `thiserror`-based `PvlibError` enum.
8. **`#[non_exhaustive]` on `WeatherSeries` / `SimulationSeries` /
   `BatchModelChain`** — once a non-breaking constructor exists, these
   should become non-exhaustive so additional outputs or config
   options do not constitute breaking changes. Adding today would
   break every test and every user that constructs `WeatherSeries`
   via struct-literal syntax.
9. **`Box<dyn Mount>``enum Mount`** — the `Mount` trait object in
   `pvsystem::Array` is a public API hazard and blocks `Clone`/`Copy`.
   A closed `enum Mount { Fixed(..), SingleAxis(..) }` covers the only
   two implementations in the crate today.
10. **`BatchModelChain::run` input validation** — return a structured
    `Err` (not `assert_eq!` / `panic`) on length-mismatched weather
    vectors. Add tilt / azimuth / capacity range checks.

## Performance (medium priority)

11. **SoA output writes in `BatchModelChain::run`** — the current
    implementation collects an 11-tuple-per-row `Vec` then transposes
    with 12 sequential `iter().map().collect()` passes. Pre-sizing the
    output vectors and writing by index eliminates the intermediate
    allocation and one full pass.
12. **Cached SPA per day / per hour** — the `spa` crate recomputes
    every per-day term (nutation, obliquity, apparent sidereal time)
    on every call. Caching these across a contiguous day in the batch
    loop is the single biggest speedup available.
13. **De-duplicate `aoi()` / hoist `to_radians()`** in Perez and
    HayDavies transposition and in the batch main loop.
14. **`with_min_len` on rayon iterators** in cheap kernels
    (`airmass_relative_batch`, `aoi_batch`, `iam_ashrae_batch`) — for
    8760 items and sub-microsecond work the fork/join overhead exceeds
    the gain.

## Ecosystem (strategic)

15. **PyO3 / maturin wheel** — expose `BatchModelChain` and the
    `*_batch` functions as a `pvlib-accel` wheel on PyPI. Accept
    `numpy.ndarray` zero-copy, release the GIL around the rayon loop.
    This is the single largest adoption lever — the pvlib user base
    lives in Python.
16. **Polars / Arrow feature** — optional `polars = ["dep:polars"]`
    and `arrow = ["dep:arrow"]` features with
    `impl From<&DataFrame> for WeatherSeries` and
    `impl Into<DataFrame> for SimulationSeries`.

## Testing (medium priority)

17. **Property-based tests**`proptest` invariants for
    `iam::physical` monotonicity, `irradiance::erbs` kt ∈ [0,1], solar
    position round-trip, inverter saturation.
18. **Edge-case suite** — polar night / polar day / equator equinox,
    DST transitions, leap-year Feb 29, year-boundary timestamps.
19. **Fuzzing**`cargo fuzz` targets for `iotools::read_tmy3` and
    `iotools::read_epw` (classic crash-surface).
20. **Coverage gate**`cargo llvm-cov` in CI with a floor (e.g.
    80 %); `src/ivtools.rs` (720 LOC) currently has no dedicated test
    file.