use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LmpComponents {
pub bus_id: usize,
pub energy: f64,
pub congestion: f64,
pub loss: f64,
pub lmp: f64,
}
impl LmpComponents {
pub fn compute(
bus_id: usize,
lambda: f64,
gsf: &[f64],
shadow_prices: &[f64],
mlf: f64,
) -> Self {
let congestion: f64 = gsf
.iter()
.zip(shadow_prices.iter())
.map(|(&g, &mu)| g * mu)
.sum();
let loss = mlf * lambda;
let lmp = lambda + congestion + loss;
Self {
bus_id,
energy: lambda,
congestion,
loss,
lmp,
}
}
}
pub fn compute_lmps(
lambda: f64,
gsf: &[Vec<f64>],
shadow_prices: &[f64],
mlfs: &[f64],
) -> Vec<LmpComponents> {
let n_buses = gsf.first().map(|r| r.len()).unwrap_or(0);
(0..n_buses)
.map(|b| {
let gsf_row: Vec<f64> = gsf
.iter()
.map(|row| if b < row.len() { row[b] } else { 0.0 })
.collect();
let mlf = if b < mlfs.len() { mlfs[b] } else { 0.0 };
LmpComponents::compute(b, lambda, &gsf_row, shadow_prices, mlf)
})
.collect()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_lmp_uncongested_equals_lambda() {
let lambda = 40.0;
let gsf = vec![vec![0.5, -0.3, 0.1], vec![-0.2, 0.4, 0.1]];
let shadow_prices = vec![0.0, 0.0];
let mlfs = vec![0.0, 0.0, 0.0];
let lmps = compute_lmps(lambda, &gsf, &shadow_prices, &mlfs);
for lmp in &lmps {
assert!(
(lmp.lmp - lambda).abs() < 1e-10,
"Uncongested LMP should equal lambda: {:.4}",
lmp.lmp
);
}
}
#[test]
fn test_lmp_congestion_component() {
let lambda = 40.0;
let gsf = vec![vec![0.5, 0.3]];
let shadow_prices = vec![10.0];
let mlfs = vec![0.0, 0.0];
let lmps = compute_lmps(lambda, &gsf, &shadow_prices, &mlfs);
assert!((lmps[0].congestion - 5.0).abs() < 1e-10);
assert!((lmps[1].congestion - 3.0).abs() < 1e-10);
}
#[test]
fn test_lmp_components_sum() {
let lmp_comp = LmpComponents::compute(0, 35.0, &[0.3, -0.1], &[5.0, 2.0], 0.02);
let expected = 35.0 + 0.3 * 5.0 + (-0.1) * 2.0 + 0.02 * 35.0;
assert!((lmp_comp.lmp - expected).abs() < 1e-8);
}
}