use crate::model::data_center::model::Location;
use crate::utils::min;
use crate::utils::pos;
use noisy_float::prelude::*;
use pyo3::prelude::*;
use std::collections::HashMap;
use std::sync::Arc;
#[pyclass]
#[derive(Clone)]
pub struct EnergySource {
pub cost: Arc<dyn Fn(i32) -> f64 + Send + Sync>,
pub profit: Arc<dyn Fn(i32) -> f64 + Send + Sync>,
pub limit: Arc<dyn Fn(i32, &Location) -> f64 + Send + Sync>,
}
impl EnergySource {
fn cost(&self, t: i32) -> N64 {
n64((self.cost)(t))
}
fn profit(&self, t: i32) -> N64 {
n64((self.profit)(t))
}
fn limit(&self, t: i32, location: &Location) -> N64 {
n64((self.limit)(t, location))
}
}
#[pymethods]
impl EnergySource {
#[new]
fn constructor(
cost: Py<PyAny>,
profit: Py<PyAny>,
limit: Py<PyAny>,
) -> Self {
EnergySource {
cost: Arc::new(move |t| {
Python::with_gil(|py| {
cost.call1(py, (t,))
.expect("energy source `cost` method invalid")
.extract(py)
.expect("energy source `cost` method invalid")
})
}),
profit: Arc::new(move |t| {
Python::with_gil(|py| {
profit
.call1(py, (t,))
.expect("energy source `profit` method invalid")
.extract(py)
.expect("energy source `profit` method invalid")
})
}),
limit: Arc::new(move |t, location| {
Python::with_gil(|py| {
limit
.call1(py, (t, location.clone()))
.expect("energy source `limit` method invalid")
.extract(py)
.expect("energy source `limit` method invalid")
})
}),
}
}
}
#[derive(Clone, FromPyObject)]
pub enum EnergyCostModel {
Linear(HashMap<String, LinearEnergyCostModel>),
Quotas(HashMap<String, QuotasEnergyCostModel>),
}
#[pyclass]
#[derive(Clone)]
pub struct LinearEnergyCostModel {
pub cost: Arc<dyn Fn(i32) -> f64 + Send + Sync>,
}
impl LinearEnergyCostModel {
fn cost(&self, t: i32) -> N64 {
n64((self.cost)(t))
}
}
#[pymethods]
impl LinearEnergyCostModel {
#[new]
fn constructor(cost: Py<PyAny>) -> Self {
LinearEnergyCostModel {
cost: Arc::new(move |t| {
Python::with_gil(|py| {
cost.call1(py, (t,))
.expect(
"linear energy cost model `cost` method invalid",
)
.extract(py)
.expect(
"linear energy cost model `cost` method invalid",
)
})
}),
}
}
#[staticmethod]
pub fn from_const(cost: f64) -> Self {
LinearEnergyCostModel {
cost: Arc::new(move |_t| cost),
}
}
}
#[pyclass]
#[derive(Clone)]
pub struct QuotasEnergyCostModel {
#[pyo3(get, set)]
pub sources: Vec<EnergySource>,
}
#[pymethods]
impl QuotasEnergyCostModel {
#[new]
fn constructor(sources: Vec<EnergySource>) -> Self {
QuotasEnergyCostModel { sources }
}
}
impl EnergyCostModel {
pub fn cost(&self, t: i32, location: &Location, p: N64) -> N64 {
match self {
EnergyCostModel::Linear(models) => {
let model = &models[&location.key];
model.cost(t) * p
}
EnergyCostModel::Quotas(models) => {
let mut sources = models[&location.key].sources.clone();
sources.sort_by(|a, b| {
(a.cost(t) + a.profit(t))
.partial_cmp(&(b.cost(t) + b.profit(t)))
.unwrap()
});
let mut result = n64(0.);
let mut cum_limit = n64(0.);
for source in sources {
let delta = pos(p - cum_limit);
let limit = source.limit(t, location);
result += source.cost(t) + min(delta, limit)
- source.profit(t) * pos(limit - delta);
cum_limit += limit;
}
result
}
}
}
}