use pvlib::singlediode::*;
fn test_params() -> Bishop88Params {
Bishop88Params::new(1.0, 9e-10, 4.0, 5000.0, 4.0)
}
#[test]
fn test_estimate_voc_positive() {
let voc = estimate_voc(1.0, 9e-10, 4.0);
assert!(voc > 50.0 && voc < 100.0, "Voc estimate should be reasonable, got {}", voc);
}
#[test]
fn test_estimate_voc_proportional_to_nnsvth() {
let voc1 = estimate_voc(1.0, 9e-10, 4.0);
let voc2 = estimate_voc(1.0, 9e-10, 8.0);
assert!((voc2 / voc1 - 2.0).abs() < 0.01, "Voc should scale linearly with nNsVth");
}
#[test]
fn test_bishop88_short_circuit() {
let p = test_params();
let out = bishop88(0.0, &p);
assert!(
(out.current - 1.0).abs() < 0.01,
"Isc should be ~photocurrent at Vd=0, got {}",
out.current
);
assert!(out.voltage < 0.0, "V should be negative at Vd=0 with Rs>0");
}
#[test]
fn test_bishop88_at_voc() {
let p = test_params();
let voc_est = estimate_voc(p.photocurrent, p.saturation_current, p.n_ns_vth);
let out = bishop88(voc_est, &p);
assert!(
out.current.abs() < 0.02,
"Current at Voc should be ~0, got {}",
out.current
);
}
#[test]
fn test_bishop88_power_positive_midpoint() {
let p = test_params();
let voc_est = estimate_voc(p.photocurrent, p.saturation_current, p.n_ns_vth);
let out = bishop88(voc_est * 0.5, &p);
assert!(out.power > 0.0, "Power should be positive at midpoint, got {}", out.power);
}
#[test]
fn test_bishop88_i_from_v_at_zero() {
let p = test_params();
let i = bishop88_i_from_v(0.0, &p);
assert!(
(i - 1.0).abs() < 0.01,
"I at V=0 should be ~Isc=1A, got {}",
i
);
}
#[test]
fn test_bishop88_i_from_v_at_voc() {
let p = test_params();
let voc = bishop88_v_from_i(0.0, &p);
let i = bishop88_i_from_v(voc, &p);
assert!(
i.abs() < 0.01,
"I at Voc should be ~0, got {}",
i
);
}
#[test]
fn test_bishop88_v_from_i_at_zero() {
let p = test_params();
let v = bishop88_v_from_i(0.0, &p);
assert!(v > 0.0, "Voc should be positive, got {}", v);
assert!(v < 100.0, "Voc should be < 100V, got {}", v);
}
#[test]
fn test_bishop88_v_from_i_at_isc() {
let p = test_params();
let v = bishop88_v_from_i(1.0, &p);
assert!(
v.abs() < 5.0,
"V at Isc should be near 0, got {}",
v
);
}
#[test]
fn test_bishop88_mpp_positive_power() {
let p = test_params();
let mpp = bishop88_mpp(&p);
assert!(mpp.power > 0.0, "MPP power should be positive, got {}", mpp.power);
assert!(mpp.current > 0.0, "MPP current should be positive, got {}", mpp.current);
assert!(mpp.voltage > 0.0, "MPP voltage should be positive, got {}", mpp.voltage);
}
#[test]
fn test_bishop88_mpp_less_than_voc() {
let p = test_params();
let mpp = bishop88_mpp(&p);
let voc = bishop88_v_from_i(0.0, &p);
assert!(
mpp.voltage < voc,
"MPP voltage ({}) should be less than Voc ({})",
mpp.voltage, voc
);
}
#[test]
fn test_bishop88_mpp_current_less_than_isc() {
let p = test_params();
let mpp = bishop88_mpp(&p);
let isc = bishop88_i_from_v(0.0, &p);
assert!(
mpp.current < isc,
"MPP current ({}) should be less than Isc ({})",
mpp.current, isc
);
}
#[test]
fn test_bishop88_mpp_is_maximum() {
let p = test_params();
let mpp = bishop88_mpp(&p);
let voc_est = estimate_voc(p.photocurrent, p.saturation_current, p.n_ns_vth);
for i in 0..100 {
let vd = voc_est * (i as f64) / 100.0;
let out = bishop88(vd, &p);
assert!(
out.power <= mpp.power + 1e-6,
"Found higher power ({}) than MPP ({}) at vd={}",
out.power, mpp.power, vd
);
}
}
#[test]
fn test_bishop88_i_from_v_roundtrip() {
let p = test_params();
let v_target = 30.0;
let i = bishop88_i_from_v(v_target, &p);
let v_back = bishop88_v_from_i(i, &p);
assert!(
(v_back - v_target).abs() < 0.01,
"Roundtrip V: expected {}, got {}",
v_target, v_back
);
}
#[test]
fn test_legacy_i_from_v() {
let i = i_from_v(0.0, 1.0, 9e-10, 4.0, 5000.0, 4.0);
assert!((i - 1.0).abs() < 0.01, "Legacy i_from_v at V=0 should give ~Isc, got {}", i);
}
#[test]
fn test_legacy_v_from_i() {
let v = v_from_i(0.0, 1.0, 9e-10, 4.0, 5000.0, 4.0);
assert!(v > 0.0, "Legacy v_from_i at I=0 should give positive Voc, got {}", v);
}
#[test]
fn test_bishop88_gradients_computed() {
let p = test_params();
let voc_est = estimate_voc(p.photocurrent, p.saturation_current, p.n_ns_vth);
let (out, grad) = bishop88_with_gradients(voc_est * 0.5, &p);
assert!(out.power > 0.0);
assert!(grad.dv_dvd > 0.0, "dv/dvd should be positive, got {}", grad.dv_dvd);
assert!(grad.di_dvd < 0.0, "di/dvd should be negative, got {}", grad.di_dvd);
}