From a541e77c7157c3d8aa6f1f022181422f4732cab5 Mon Sep 17 00:00:00 2001 From: Stefano Simonelli <16114781+s-simoncelli@users.noreply.github.com> Date: Mon, 18 Dec 2023 06:03:30 +0000 Subject: [PATCH] Implemented NegativeMaxParameter and NegativeMinParameter --- pywr-core/src/parameters/mod.rs | 5 ++ pywr-core/src/parameters/negativemax.rs | 45 +++++++++++ pywr-core/src/parameters/negativemin.rs | 45 +++++++++++ pywr-schema/src/parameters/core.rs | 100 ++++++++++++++++++++++++ pywr-schema/src/parameters/mod.rs | 13 ++- 5 files changed, 207 insertions(+), 1 deletion(-) create mode 100644 pywr-core/src/parameters/negativemax.rs create mode 100644 pywr-core/src/parameters/negativemin.rs diff --git a/pywr-core/src/parameters/mod.rs b/pywr-core/src/parameters/mod.rs index 1c724963..70d1fca0 100644 --- a/pywr-core/src/parameters/mod.rs +++ b/pywr-core/src/parameters/mod.rs @@ -14,6 +14,8 @@ mod interpolated; mod max; mod min; mod negative; +mod negativemax; +mod negativemin; mod offset; mod polynomial; mod profiles; @@ -22,6 +24,7 @@ mod rhai; pub mod simple_wasm; mod threshold; mod vector; +mod flow; use std::any::Any; // Re-imports @@ -50,6 +53,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::{ diff --git a/pywr-core/src/parameters/negativemax.rs b/pywr-core/src/parameters/negativemax.rs new file mode 100644 index 00000000..41e2a7a8 --- /dev/null +++ b/pywr-core/src/parameters/negativemax.rs @@ -0,0 +1,45 @@ +use crate::metric::Metric; +use crate::model::Model; +use crate::parameters::{Parameter, ParameterMeta}; +use crate::scenario::ScenarioIndex; +use std::any::Any; + +use crate::state::State; +use crate::timestep::Timestep; +use crate::PywrError; + +pub struct NegativeMaxParameter { + meta: ParameterMeta, + metric: Metric, + threshold: f64, +} + +impl NegativeMaxParameter { + pub fn new(name: &str, metric: Metric, threshold: f64) -> Self { + Self { + meta: ParameterMeta::new(name), + metric, + threshold, + } + } +} + +impl Parameter for NegativeMaxParameter { + fn as_any_mut(&mut self) -> &mut dyn Any { + self + } + fn meta(&self) -> &ParameterMeta { + &self.meta + } + fn compute( + &self, + _timestep: &Timestep, + _scenario_index: &ScenarioIndex, + model: &Model, + state: &State, + _internal_state: &mut Option>, + ) -> Result { + let x = -self.metric.get_value(model, state)?; + Ok(x.max(self.threshold)) + } +} diff --git a/pywr-core/src/parameters/negativemin.rs b/pywr-core/src/parameters/negativemin.rs new file mode 100644 index 00000000..ac75b561 --- /dev/null +++ b/pywr-core/src/parameters/negativemin.rs @@ -0,0 +1,45 @@ +use crate::metric::Metric; +use crate::model::Model; +use crate::parameters::{Parameter, ParameterMeta}; +use crate::scenario::ScenarioIndex; +use std::any::Any; + +use crate::state::State; +use crate::timestep::Timestep; +use crate::PywrError; + +pub struct NegativeMinParameter { + meta: ParameterMeta, + metric: Metric, + threshold: f64, +} + +impl NegativeMinParameter { + pub fn new(name: &str, metric: Metric, threshold: f64) -> Self { + Self { + meta: ParameterMeta::new(name), + metric, + threshold, + } + } +} + +impl Parameter for NegativeMinParameter { + fn as_any_mut(&mut self) -> &mut dyn Any { + self + } + fn meta(&self) -> &ParameterMeta { + &self.meta + } + fn compute( + &self, + _timestep: &Timestep, + _scenario_index: &ScenarioIndex, + model: &Model, + state: &State, + _internal_state: &mut Option>, + ) -> Result { + let x = -self.metric.get_value(model, state)?; + Ok(x.min(self.threshold)) + } +} diff --git a/pywr-schema/src/parameters/core.rs b/pywr-schema/src/parameters/core.rs index 2c4e4288..0e06d295 100644 --- a/pywr-schema/src/parameters/core.rs +++ b/pywr-schema/src/parameters/core.rs @@ -446,3 +446,103 @@ impl TryFromV1Parameter for NegativeParameter { Ok(p) } } + +/// This parameter takes the maximum of the negative of a Parameter and a constant value (threshold). +/// +/// # Arguments +/// +/// * `parameter` - The parameter to compare with the float. +/// * `threshold` - The threshold value to compare against the given parameter. Default to 0.0. +/// +/// # Examples +/// +/// ```json +/// { +/// "type": "NegativeMax", +/// "parameter": { +/// "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 parameter: DynamicFloatValue, + pub threshold: Option, +} + +impl NegativeMaxParameter { + pub fn node_references(&self) -> HashMap<&str, &str> { + HashMap::new() + } + + pub fn add_to_model( + &self, + model: &mut pywr_core::model::Model, + tables: &LoadedTableCollection, + data_path: Option<&Path>, + ) -> Result { + let idx = self.parameter.load(model, tables, data_path)?; + let threshold = self.threshold.unwrap_or(0.0); + + let p = pywr_core::parameters::NegativeMaxParameter::new(&self.meta.name, idx, threshold); + Ok(model.add_parameter(Box::new(p))?) + } +} + +// TODO NegativeMaxParameter from v1 + +/// This parameter takes the minimum of the negative of a Parameter and a constant value (threshold). +/// +/// # Arguments +/// +/// * `parameter` - The parameter to compare with the float. +/// * `threshold` - The threshold value to compare against the given parameter. Default to 0.0. +/// +/// # Examples +/// +/// ```json +/// { +/// "type": "NegativeMin", +/// "parameter": { +/// "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 parameter: DynamicFloatValue, + pub threshold: Option, +} + +impl NegativeMinParameter { + pub fn node_references(&self) -> HashMap<&str, &str> { + HashMap::new() + } + + pub fn add_to_model( + &self, + model: &mut pywr_core::model::Model, + tables: &LoadedTableCollection, + data_path: Option<&Path>, + ) -> Result { + let idx = self.parameter.load(model, tables, data_path)?; + let threshold = self.threshold.unwrap_or(0.0); + + let p = pywr_core::parameters::NegativeMinParameter::new(&self.meta.name, idx, threshold); + Ok(model.add_parameter(Box::new(p))?) + } +} + +// TODO NegativeMinParameter from v1 diff --git a/pywr-schema/src/parameters/mod.rs b/pywr-schema/src/parameters/mod.rs index 0795f8a8..2004e942 100644 --- a/pywr-schema/src/parameters/mod.rs +++ b/pywr-schema/src/parameters/mod.rs @@ -31,7 +31,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; @@ -157,6 +158,8 @@ pub enum Parameter { Max(MaxParameter), Min(MinParameter), Negative(NegativeParameter), + NegativeMax(NegativeMaxParameter), + NegativeMin(NegativeMinParameter), Polynomial1D(Polynomial1DParameter), ParameterThreshold(ParameterThresholdParameter), TablesArray(TablesArrayParameter), @@ -199,6 +202,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(), } } @@ -232,6 +237,8 @@ impl Parameter { Self::DiscountFactor(p) => p.node_references(), Self::Interpolated(p) => p.node_references(), Self::RbfProfile(p) => p.node_references(), + Self::NegativeMax(p) => p.node_references(), + Self::NegativeMin(p) => p.node_references(), } } @@ -282,6 +289,8 @@ impl Parameter { Self::DiscountFactor(_) => "DiscountFactor", Self::Interpolated(_) => "Interpolated", Self::RbfProfile(_) => "RbfProfile", + Self::NegativeMax(_) => "NegativeMax", + Self::NegativeMin(_) => "NegativeMin", } } @@ -320,6 +329,8 @@ impl Parameter { Self::DiscountFactor(p) => ParameterType::Parameter(p.add_to_model(model, tables, data_path)?), Self::Interpolated(p) => ParameterType::Parameter(p.add_to_model(model, tables, data_path)?), Self::RbfProfile(p) => ParameterType::Parameter(p.add_to_model(model)?), + Self::NegativeMax(p) => ParameterType::Parameter(p.add_to_model(model, tables, data_path)?), + Self::NegativeMin(p) => ParameterType::Parameter(p.add_to_model(model, tables, data_path)?), }; Ok(ty)