Skip to content

Commit

Permalink
feat: Implement NegativeMaxParameter and NegativeMinParameter (#79)
Browse files Browse the repository at this point in the history
  • Loading branch information
s-simoncelli authored Apr 15, 2024
1 parent 8444418 commit b3d743e
Show file tree
Hide file tree
Showing 6 changed files with 233 additions and 5 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,4 @@ tracing = { version = "0.1", features = ["log"] }
csv = "1.1"
hdf5 = { git = "https://github.com/aldanor/hdf5-rust.git", package = "hdf5", features = ["static", "zlib"] }
pywr-v1-schema = { git = "https://github.com/pywr/pywr-schema/", tag = "v0.11.0", package = "pywr-schema" }
chrono = { version = "0.4.34" }
chrono = { version = "0.4.34" }
4 changes: 4 additions & 0 deletions pywr-core/src/parameters/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ mod interpolated;
mod max;
mod min;
mod negative;
mod negativemax;
mod negativemin;
mod offset;
mod polynomial;
mod profiles;
Expand Down Expand Up @@ -49,6 +51,8 @@ pub use interpolated::InterpolatedParameter;
pub use max::MaxParameter;
pub use min::MinParameter;
pub use negative::NegativeParameter;
pub use negativemax::NegativeMaxParameter;
pub use negativemin::NegativeMinParameter;
pub use offset::OffsetParameter;
pub use polynomial::Polynomial1DParameter;
pub use profiles::{
Expand Down
40 changes: 40 additions & 0 deletions pywr-core/src/parameters/negativemax.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
use crate::metric::MetricF64;
use crate::network::Network;
use crate::parameters::{Parameter, ParameterMeta};
use crate::scenario::ScenarioIndex;
use crate::state::{ParameterState, State};
use crate::timestep::Timestep;
use crate::PywrError;

pub struct NegativeMaxParameter {
meta: ParameterMeta,
metric: MetricF64,
threshold: f64,
}

impl NegativeMaxParameter {
pub fn new(name: &str, metric: MetricF64, threshold: f64) -> Self {
Self {
meta: ParameterMeta::new(name),
metric,
threshold,
}
}
}

impl Parameter<f64> for NegativeMaxParameter {
fn meta(&self) -> &ParameterMeta {
&self.meta
}
fn compute(
&self,
_timestep: &Timestep,
_scenario_index: &ScenarioIndex,
network: &Network,
state: &State,
_internal_state: &mut Option<Box<dyn ParameterState>>,
) -> Result<f64, PywrError> {
let x = -self.metric.get_value(network, state)?;
Ok(x.max(self.threshold))
}
}
40 changes: 40 additions & 0 deletions pywr-core/src/parameters/negativemin.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
use crate::metric::MetricF64;
use crate::network::Network;
use crate::parameters::{Parameter, ParameterMeta};
use crate::scenario::ScenarioIndex;
use crate::state::{ParameterState, State};
use crate::timestep::Timestep;
use crate::PywrError;

pub struct NegativeMinParameter {
meta: ParameterMeta,
metric: MetricF64,
threshold: f64,
}

impl NegativeMinParameter {
pub fn new(name: &str, metric: MetricF64, threshold: f64) -> Self {
Self {
meta: ParameterMeta::new(name),
metric,
threshold,
}
}
}

impl Parameter<f64> for NegativeMinParameter {
fn meta(&self) -> &ParameterMeta {
&self.meta
}
fn compute(
&self,
_timestep: &Timestep,
_scenario_index: &ScenarioIndex,
network: &Network,
state: &State,
_internal_state: &mut Option<Box<dyn ParameterState>>,
) -> Result<f64, PywrError> {
let x = -self.metric.get_value(network, state)?;
Ok(x.min(self.threshold))
}
}
135 changes: 134 additions & 1 deletion pywr-schema/src/parameters/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ use crate::parameters::{
use pywr_core::parameters::ParameterIndex;
use pywr_v1_schema::parameters::{
ConstantParameter as ConstantParameterV1, DivisionParameter as DivisionParameterV1, MaxParameter as MaxParameterV1,
MinParameter as MinParameterV1, NegativeParameter as NegativeParameterV1,
MinParameter as MinParameterV1, NegativeMaxParameter as NegativeMaxParameterV1,
NegativeMinParameter as NegativeMinParameterV1, NegativeParameter as NegativeParameterV1,
};
use std::collections::HashMap;

Expand Down Expand Up @@ -430,3 +431,135 @@ impl TryFromV1Parameter<NegativeParameterV1> for NegativeParameter {
Ok(p)
}
}

/// This parameter takes the maximum of the negative of a metric and a constant value (threshold).
///
/// # Arguments
///
/// * `metric` - The metric value to compare with the float.
/// * `threshold` - The threshold value to compare against the given parameter. Default to 0.0.
///
/// # Examples
///
/// ```json
/// {
/// "type": "NegativeMax",
/// "metric": {
/// "type": "MonthlyProfile",
/// "values": [-1, -4, 5, 9, 1, 5, 10, 8, 11, 9, 11 ,12]
/// },
/// "threshold": 2
/// }
/// ```
/// In January this parameter returns 2, in February 4.
///
#[derive(serde::Deserialize, serde::Serialize, Debug, Clone)]
pub struct NegativeMaxParameter {
#[serde(flatten)]
pub meta: ParameterMeta,
pub metric: Metric,
pub threshold: Option<f64>,
}

impl NegativeMaxParameter {
pub fn node_references(&self) -> HashMap<&str, &str> {
HashMap::new()
}

pub fn add_to_model(
&self,
network: &mut pywr_core::network::Network,
args: &LoadArgs,
) -> Result<ParameterIndex<f64>, SchemaError> {
let idx = self.metric.load(network, args)?;
let threshold = self.threshold.unwrap_or(0.0);

let p = pywr_core::parameters::NegativeMaxParameter::new(&self.meta.name, idx, threshold);
Ok(network.add_parameter(Box::new(p))?)
}
}

impl TryFromV1Parameter<NegativeMaxParameterV1> for NegativeMaxParameter {
type Error = ConversionError;

fn try_from_v1_parameter(
v1: NegativeMaxParameterV1,
parent_node: Option<&str>,
unnamed_count: &mut usize,
) -> Result<Self, Self::Error> {
let meta: ParameterMeta = v1.meta.into_v2_parameter(parent_node, unnamed_count);
let parameter = v1.parameter.try_into_v2_parameter(Some(&meta.name), unnamed_count)?;
let p = Self {
meta,
metric: parameter,
threshold: v1.threshold,
};
Ok(p)
}
}

/// This parameter takes the minimum of the negative of a metric and a constant value (threshold).
///
/// # Arguments
///
/// * `metric` - The metric value to compare with the float.
/// * `threshold` - The threshold value to compare against the given parameter. Default to 0.0.
///
/// # Examples
///
/// ```json
/// {
/// "type": "NegativeMin",
/// "metric": {
/// "type": "MonthlyProfile",
/// "values": [-1, -4, 5, 9, 1, 5, 10, 8, 11, 9, 11 ,12]
/// },
/// "threshold": 2
/// }
/// ```
/// In January this parameter returns 1, in February 2.
///
#[derive(serde::Deserialize, serde::Serialize, Debug, Clone)]
pub struct NegativeMinParameter {
#[serde(flatten)]
pub meta: ParameterMeta,
pub metric: Metric,
pub threshold: Option<f64>,
}

impl NegativeMinParameter {
pub fn node_references(&self) -> HashMap<&str, &str> {
HashMap::new()
}

pub fn add_to_model(
&self,
network: &mut pywr_core::network::Network,
args: &LoadArgs,
) -> Result<ParameterIndex<f64>, SchemaError> {
let idx = self.metric.load(network, args)?;
let threshold = self.threshold.unwrap_or(0.0);

let p = pywr_core::parameters::NegativeMinParameter::new(&self.meta.name, idx, threshold);
Ok(network.add_parameter(Box::new(p))?)
}
}

impl TryFromV1Parameter<NegativeMinParameterV1> for NegativeMinParameter {
type Error = ConversionError;

fn try_from_v1_parameter(
v1: NegativeMinParameterV1,
parent_node: Option<&str>,
unnamed_count: &mut usize,
) -> Result<Self, Self::Error> {
let meta: ParameterMeta = v1.meta.into_v2_parameter(parent_node, unnamed_count);
let parameter = v1.parameter.try_into_v2_parameter(Some(&meta.name), unnamed_count)?;
let p = Self {
meta,
metric: parameter,
threshold: v1.threshold,
};
Ok(p)
}
}
17 changes: 14 additions & 3 deletions pywr-schema/src/parameters/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ pub use super::parameters::control_curves::{
ControlCurvePiecewiseInterpolatedParameter,
};
pub use super::parameters::core::{
ActivationFunction, ConstantParameter, MaxParameter, MinParameter, NegativeParameter, VariableSettings,
ActivationFunction, ConstantParameter, MaxParameter, MinParameter, NegativeMaxParameter, NegativeMinParameter,
NegativeParameter, VariableSettings,
};
pub use super::parameters::delay::DelayParameter;
pub use super::parameters::discount_factor::DiscountFactorParameter;
Expand Down Expand Up @@ -160,6 +161,8 @@ pub enum Parameter {
Max(MaxParameter),
Min(MinParameter),
Negative(NegativeParameter),
NegativeMax(NegativeMaxParameter),
NegativeMin(NegativeMinParameter),
Polynomial1D(Polynomial1DParameter),
ParameterThreshold(ParameterThresholdParameter),
TablesArray(TablesArrayParameter),
Expand Down Expand Up @@ -201,6 +204,8 @@ impl Parameter {
Self::DiscountFactor(p) => p.meta.name.as_str(),
Self::Interpolated(p) => p.meta.name.as_str(),
Self::RbfProfile(p) => p.meta.name.as_str(),
Self::NegativeMax(p) => p.meta.name.as_str(),
Self::NegativeMin(p) => p.meta.name.as_str(),
}
}

Expand Down Expand Up @@ -249,6 +254,8 @@ impl Parameter {
Self::DiscountFactor(p) => pywr_core::parameters::ParameterType::Parameter(p.add_to_model(network, args)?),
Self::Interpolated(p) => pywr_core::parameters::ParameterType::Parameter(p.add_to_model(network, args)?),
Self::RbfProfile(p) => pywr_core::parameters::ParameterType::Parameter(p.add_to_model(network)?),
Self::NegativeMax(p) => pywr_core::parameters::ParameterType::Parameter(p.add_to_model(network, args)?),
Self::NegativeMin(p) => pywr_core::parameters::ParameterType::Parameter(p.add_to_model(network, args)?),
};

Ok(ty)
Expand Down Expand Up @@ -435,8 +442,6 @@ impl TryFromV1Parameter<ParameterV1> for ParameterOrTimeseries {
CoreParameter::InterpolatedFlow(p) => {
Parameter::Interpolated(p.try_into_v2_parameter(parent_node, unnamed_count)?).into()
}
CoreParameter::NegativeMax(_) => todo!("Implement NegativeMaxParameter"),
CoreParameter::NegativeMin(_) => todo!("Implement NegativeMinParameter"),
CoreParameter::HydropowerTarget(_) => todo!("Implement HydropowerTargetParameter"),
CoreParameter::WeeklyProfile(p) => {
Parameter::WeeklyProfile(p.try_into_v2_parameter(parent_node, unnamed_count)?).into()
Expand All @@ -460,6 +465,12 @@ impl TryFromV1Parameter<ParameterV1> for ParameterOrTimeseries {
CoreParameter::RbfProfile(p) => {
Parameter::RbfProfile(p.try_into_v2_parameter(parent_node, unnamed_count)?).into()
}
CoreParameter::NegativeMax(p) => {
Parameter::NegativeMax(p.try_into_v2_parameter(parent_node, unnamed_count)?).into()
}
CoreParameter::NegativeMin(p) => {
Parameter::NegativeMin(p.try_into_v2_parameter(parent_node, unnamed_count)?).into()
}
},
ParameterV1::Custom(p) => {
println!("Custom parameter: {:?} ({})", p.meta.name, p.ty);
Expand Down

0 comments on commit b3d743e

Please sign in to comment.