From 2385f7d58301c672d72a31d88d581165c332accd Mon Sep 17 00:00:00 2001 From: James Tomlinson Date: Fri, 27 Sep 2024 09:34:45 +0100 Subject: [PATCH] refactor: Tidy-up nodes and parameters modules. (#252) Some small changes to schemas to make things more consistent. Added additional types to the top-level module to allow them to be used outside of the crate. --- .../tests/models/aggregated-node1/model.json | 8 ++- pywr-schema/src/metric.rs | 55 +++++++++++++++++-- .../src/nodes/annual_virtual_storage.rs | 14 +++-- pywr-schema/src/nodes/core.rs | 34 +++++++----- pywr-schema/src/nodes/mod.rs | 23 ++++---- .../src/nodes/monthly_virtual_storage.rs | 14 +++-- .../src/nodes/river_split_with_gauge.rs | 22 +++++--- .../src/nodes/rolling_virtual_storage.rs | 44 ++++++++------- pywr-schema/src/nodes/turbine.rs | 2 +- pywr-schema/src/nodes/virtual_storage.rs | 5 +- pywr-schema/src/parameters/aggregated.rs | 4 +- pywr-schema/src/parameters/mod.rs | 46 ++++++++-------- pywr-schema/src/parameters/profiles.rs | 2 +- .../src/test_models/30-day-licence.json | 8 ++- .../src/test_models/mutual-exclusivity1.json | 8 ++- .../src/test_models/mutual-exclusivity2.json | 8 ++- .../src/test_models/mutual-exclusivity3.json | 8 ++- 17 files changed, 193 insertions(+), 112 deletions(-) diff --git a/pywr-python/tests/models/aggregated-node1/model.json b/pywr-python/tests/models/aggregated-node1/model.json index 6d08a92b..a71d07d5 100644 --- a/pywr-python/tests/models/aggregated-node1/model.json +++ b/pywr-python/tests/models/aggregated-node1/model.json @@ -49,8 +49,12 @@ }, "type": "Aggregated", "nodes": [ - "link1", - "link2" + { + "name": "link1" + }, + { + "name": "link2" + } ], "max_flow": { "type": "Constant", diff --git a/pywr-schema/src/metric.rs b/pywr-schema/src/metric.rs index 60c5c2d1..5509b5ba 100644 --- a/pywr-schema/src/metric.rs +++ b/pywr-schema/src/metric.rs @@ -253,6 +253,7 @@ impl TimeseriesReference { } } +/// A reference to a node with an optional attribute. #[derive(serde::Deserialize, serde::Serialize, Debug, Clone, JsonSchema, PywrVisitAll)] #[serde(deny_unknown_fields)] pub struct NodeReference { @@ -304,12 +305,56 @@ impl NodeReference { } } -impl From for NodeReference { +/// A reference to a node without an attribute. +#[derive(serde::Deserialize, serde::Serialize, Debug, Clone, JsonSchema, PywrVisitAll)] +pub struct SimpleNodeReference { + /// The name of the node + pub name: String, +} + +impl SimpleNodeReference { + pub fn new(name: String) -> Self { + Self { name } + } + + #[cfg(feature = "core")] + pub fn load(&self, network: &mut pywr_core::network::Network, args: &LoadArgs) -> Result { + // This is the associated node in the schema + let node = args + .schema + .get_node_by_name(&self.name) + .ok_or_else(|| SchemaError::NodeNotFound(self.name.clone()))?; + + node.create_metric(network, None, args) + } + + /// Return the default attribute of the node. + #[cfg(feature = "core")] + pub fn attribute(&self, args: &LoadArgs) -> Result { + // This is the associated node in the schema + let node = args + .schema + .get_node_by_name(&self.name) + .ok_or_else(|| SchemaError::NodeNotFound(self.name.clone()))?; + + Ok(node.default_metric()) + } + + #[cfg(feature = "core")] + pub fn node_type(&self, args: &LoadArgs) -> Result { + // This is the associated node in the schema + let node = args + .schema + .get_node_by_name(&self.name) + .ok_or_else(|| SchemaError::NodeNotFound(self.name.clone()))?; + + Ok(node.node_type()) + } +} + +impl From for SimpleNodeReference { fn from(v: String) -> Self { - NodeReference { - name: v, - attribute: None, - } + SimpleNodeReference { name: v } } } diff --git a/pywr-schema/src/nodes/annual_virtual_storage.rs b/pywr-schema/src/nodes/annual_virtual_storage.rs index a429ebc0..5f70041f 100644 --- a/pywr-schema/src/nodes/annual_virtual_storage.rs +++ b/pywr-schema/src/nodes/annual_virtual_storage.rs @@ -1,7 +1,7 @@ use crate::error::ConversionError; #[cfg(feature = "core")] use crate::error::SchemaError; -use crate::metric::Metric; +use crate::metric::{Metric, SimpleNodeReference}; #[cfg(feature = "core")] use crate::model::LoadArgs; use crate::nodes::core::StorageInitialVolume; @@ -39,7 +39,7 @@ impl Default for AnnualReset { #[serde(deny_unknown_fields)] pub struct AnnualVirtualStorageNode { pub meta: NodeMeta, - pub nodes: Vec, + pub nodes: Vec, pub factors: Option>, pub max_volume: Option, pub min_volume: Option, @@ -74,10 +74,10 @@ impl AnnualVirtualStorageNode { let indices = self .nodes .iter() - .map(|name| { + .map(|node_ref| { args.schema - .get_node_by_name(name) - .ok_or_else(|| SchemaError::NodeNotFound(name.to_string()))? + .get_node_by_name(&node_ref.name) + .ok_or_else(|| SchemaError::NodeNotFound(node_ref.name.to_string()))? .node_indices_for_constraints(network, args) }) .collect::, _>>()? @@ -188,9 +188,11 @@ impl TryFrom for AnnualVirtualStorageNode { }); }; + let nodes = v1.nodes.into_iter().map(|n| n.into()).collect(); + let n = Self { meta, - nodes: v1.nodes, + nodes, factors: v1.factors, max_volume, min_volume, diff --git a/pywr-schema/src/nodes/core.rs b/pywr-schema/src/nodes/core.rs index 0a904dda..76518114 100644 --- a/pywr-schema/src/nodes/core.rs +++ b/pywr-schema/src/nodes/core.rs @@ -1,7 +1,7 @@ use crate::error::ConversionError; #[cfg(feature = "core")] use crate::error::SchemaError; -use crate::metric::Metric; +use crate::metric::{Metric, SimpleNodeReference}; #[cfg(feature = "core")] use crate::model::LoadArgs; use crate::nodes::{NodeAttribute, NodeMeta}; @@ -725,7 +725,7 @@ pub enum Relationship { #[serde(deny_unknown_fields)] pub struct AggregatedNode { pub meta: NodeMeta, - pub nodes: Vec, + pub nodes: Vec, pub max_flow: Option, pub min_flow: Option, pub factors: Option, @@ -760,10 +760,10 @@ impl AggregatedNode { let indices = self .nodes .iter() - .map(|name| { + .map(|node_ref| { args.schema - .get_node_by_name(name) - .ok_or_else(|| SchemaError::NodeNotFound(name.to_string()))? + .get_node_by_name(&node_ref.name) + .ok_or_else(|| SchemaError::NodeNotFound(node_ref.name.to_string()))? .node_indices_for_constraints(network, args) }) .collect::, _>>()? @@ -776,11 +776,11 @@ impl AggregatedNode { let nodes: Vec> = self .nodes .iter() - .map(|name| { + .map(|node_ref| { let node = args .schema - .get_node_by_name(name) - .ok_or_else(|| SchemaError::NodeNotFound(name.to_string()))?; + .get_node_by_name(&node_ref.name) + .ok_or_else(|| SchemaError::NodeNotFound(node_ref.name.to_string()))?; node.node_indices_for_constraints(network, args) }) .collect::, _>>()?; @@ -889,9 +889,11 @@ impl TryFrom for AggregatedNode { .map(|v| v.try_into_v2_parameter(Some(&meta.name), &mut unnamed_count)) .transpose()?; + let nodes = v1.nodes.into_iter().map(|n| n.into()).collect(); + let n = Self { meta, - nodes: v1.nodes, + nodes, max_flow, min_flow, factors, @@ -904,7 +906,7 @@ impl TryFrom for AggregatedNode { #[serde(deny_unknown_fields)] pub struct AggregatedStorageNode { pub meta: NodeMeta, - pub storage_nodes: Vec, + pub storage_nodes: Vec, } impl AggregatedStorageNode { @@ -936,10 +938,10 @@ impl AggregatedStorageNode { let indices = self .storage_nodes .iter() - .map(|name| { + .map(|node_ref| { args.schema - .get_node_by_name(name) - .ok_or_else(|| SchemaError::NodeNotFound(name.to_string()))? + .get_node_by_name(&node_ref.name) + .ok_or_else(|| SchemaError::NodeNotFound(node_ref.name.to_string()))? .node_indices_for_constraints(network, args) }) .collect::, _>>()? @@ -952,7 +954,7 @@ impl AggregatedStorageNode { let nodes = self .storage_nodes .iter() - .map(|name| network.get_node_index_by_name(name, None)) + .map(|node_ref| network.get_node_index_by_name(&node_ref.name, None)) .collect::>()?; network.add_aggregated_storage_node(self.meta.name.as_str(), None, nodes)?; @@ -993,9 +995,11 @@ impl TryFrom for AggregatedStorageNode { type Error = ConversionError; fn try_from(v1: AggregatedStorageNodeV1) -> Result { + let storage_nodes = v1.storage_nodes.into_iter().map(|n| n.into()).collect(); + let n = Self { meta: v1.meta.into(), - storage_nodes: v1.storage_nodes, + storage_nodes, }; Ok(n) } diff --git a/pywr-schema/src/nodes/mod.rs b/pywr-schema/src/nodes/mod.rs index d02a9577..405f87ea 100644 --- a/pywr-schema/src/nodes/mod.rs +++ b/pywr-schema/src/nodes/mod.rs @@ -20,20 +20,18 @@ use crate::metric::Metric; #[cfg(feature = "core")] use crate::model::LoadArgs; use crate::model::PywrNetwork; -pub use crate::nodes::core::{ - AggregatedNode, AggregatedStorageNode, CatchmentNode, InputNode, LinkNode, OutputNode, StorageNode, -}; -pub use crate::nodes::delay::DelayNode; -pub use crate::nodes::river::RiverNode; -use crate::nodes::rolling_virtual_storage::RollingVirtualStorageNode; -use crate::nodes::turbine::TurbineNode; use crate::parameters::TimeseriesV1Data; use crate::visit::{VisitMetrics, VisitPaths}; -pub use annual_virtual_storage::AnnualVirtualStorageNode; -pub use loss_link::LossLinkNode; +pub use annual_virtual_storage::{AnnualReset, AnnualVirtualStorageNode}; +pub use core::{ + AggregatedNode, AggregatedStorageNode, CatchmentNode, InputNode, LinkNode, OutputNode, Relationship, + StorageInitialVolume, StorageNode, +}; +pub use delay::DelayNode; +pub use loss_link::{LossFactor, LossLinkNode}; pub use monthly_virtual_storage::MonthlyVirtualStorageNode; pub use piecewise_link::{PiecewiseLinkNode, PiecewiseLinkStep}; -pub use piecewise_storage::PiecewiseStorageNode; +pub use piecewise_storage::{PiecewiseStorageNode, PiecewiseStore}; #[cfg(feature = "core")] use pywr_core::metric::MetricF64; use pywr_schema_macros::PywrVisitAll; @@ -43,11 +41,14 @@ use pywr_v1_schema::nodes::{ use pywr_v1_schema::parameters::{ CoreParameter as CoreParameterV1, Parameter as ParameterV1, ParameterValue as ParameterValueV1, ParameterValueType, }; +pub use river::RiverNode; pub use river_gauge::RiverGaugeNode; -pub use river_split_with_gauge::RiverSplitWithGaugeNode; +pub use river_split_with_gauge::{RiverSplit, RiverSplitWithGaugeNode}; +pub use rolling_virtual_storage::{RollingVirtualStorageNode, RollingWindow}; use schemars::JsonSchema; use std::path::{Path, PathBuf}; use strum_macros::{Display, EnumDiscriminants, EnumString, IntoStaticStr, VariantNames}; +pub use turbine::{TargetType, TurbineNode}; pub use virtual_storage::VirtualStorageNode; pub use water_treatment_works::WaterTreatmentWorks; diff --git a/pywr-schema/src/nodes/monthly_virtual_storage.rs b/pywr-schema/src/nodes/monthly_virtual_storage.rs index e8d74b4f..b3255d77 100644 --- a/pywr-schema/src/nodes/monthly_virtual_storage.rs +++ b/pywr-schema/src/nodes/monthly_virtual_storage.rs @@ -1,7 +1,7 @@ use crate::error::ConversionError; #[cfg(feature = "core")] use crate::error::SchemaError; -use crate::metric::Metric; +use crate::metric::{Metric, SimpleNodeReference}; #[cfg(feature = "core")] use crate::model::LoadArgs; use crate::nodes::core::StorageInitialVolume; @@ -33,7 +33,7 @@ impl Default for NumberOfMonthsReset { #[serde(deny_unknown_fields)] pub struct MonthlyVirtualStorageNode { pub meta: NodeMeta, - pub nodes: Vec, + pub nodes: Vec, pub factors: Option>, pub max_volume: Option, pub min_volume: Option, @@ -68,10 +68,10 @@ impl MonthlyVirtualStorageNode { let indices = self .nodes .iter() - .map(|name| { + .map(|node_ref| { args.schema - .get_node_by_name(name) - .ok_or_else(|| SchemaError::NodeNotFound(name.to_string()))? + .get_node_by_name(&node_ref.name) + .ok_or_else(|| SchemaError::NodeNotFound(node_ref.name.to_string()))? .node_indices_for_constraints(network, args) }) .collect::, _>>()? @@ -179,9 +179,11 @@ impl TryFrom for MonthlyVirtualStorageNode { }); }; + let nodes = v1.nodes.into_iter().map(|n| n.into()).collect(); + let n = Self { meta, - nodes: v1.nodes, + nodes, factors: v1.factors, max_volume, min_volume, diff --git a/pywr-schema/src/nodes/river_split_with_gauge.rs b/pywr-schema/src/nodes/river_split_with_gauge.rs index d5115bd3..f9d4e068 100644 --- a/pywr-schema/src/nodes/river_split_with_gauge.rs +++ b/pywr-schema/src/nodes/river_split_with_gauge.rs @@ -12,6 +12,12 @@ use pywr_schema_macros::PywrVisitAll; use pywr_v1_schema::nodes::RiverSplitWithGaugeNode as RiverSplitWithGaugeNodeV1; use schemars::JsonSchema; +#[derive(serde::Deserialize, serde::Serialize, Clone, Debug, JsonSchema, PywrVisitAll)] +pub struct RiverSplit { + pub factor: Metric, + pub slot_name: String, +} + #[doc = svgbobdoc::transform!( /// This is used to represent a proportional split above a minimum residual flow (MRF) at a gauging station. /// @@ -40,7 +46,7 @@ pub struct RiverSplitWithGaugeNode { pub meta: NodeMeta, pub mrf: Option, pub mrf_cost: Option, - pub splits: Vec<(Metric, String)>, + pub splits: Vec, } impl RiverSplitWithGaugeNode { @@ -85,7 +91,7 @@ impl RiverSplitWithGaugeNode { let i = self .splits .iter() - .position(|(_, s)| s == slot) + .position(|split| split.slot_name == slot) .expect("Invalid slot name!"); vec![(self.meta.name.as_str(), Self::split_sub_name(i))] @@ -164,9 +170,9 @@ impl RiverSplitWithGaugeNode { network.set_node_max_flow(self.meta.name.as_str(), Self::mrf_sub_name(), value.into())?; } - for (i, (factor, _)) in self.splits.iter().enumerate() { + for (i, split) in self.splits.iter().enumerate() { // Set the factors for each split - let r = Relationship::new_proportion_factors(&[factor.load(network, args)?]); + let r = Relationship::new_proportion_factors(&[split.factor.load(network, args)?]); network.set_aggregated_node_relationship( self.meta.name.as_str(), Self::split_agg_sub_name(i).as_deref(), @@ -246,12 +252,12 @@ impl TryFrom for RiverSplitWithGaugeNode { .skip(1) .zip(v1.slot_names.into_iter().skip(1)) .map(|(f, slot_name)| { - Ok(( - f.try_into_v2_parameter(Some(&meta.name), &mut unnamed_count)?, + Ok(RiverSplit { + factor: f.try_into_v2_parameter(Some(&meta.name), &mut unnamed_count)?, slot_name, - )) + }) }) - .collect::, Self::Error>>()?; + .collect::, Self::Error>>()?; let n = Self { meta, diff --git a/pywr-schema/src/nodes/rolling_virtual_storage.rs b/pywr-schema/src/nodes/rolling_virtual_storage.rs index 462701e5..68cf1bd2 100644 --- a/pywr-schema/src/nodes/rolling_virtual_storage.rs +++ b/pywr-schema/src/nodes/rolling_virtual_storage.rs @@ -1,16 +1,15 @@ use crate::error::ConversionError; #[cfg(feature = "core")] use crate::error::SchemaError; -use crate::metric::Metric; +use crate::metric::{Metric, SimpleNodeReference}; #[cfg(feature = "core")] use crate::model::LoadArgs; -use crate::nodes::{NodeAttribute, NodeMeta}; +use crate::nodes::{NodeAttribute, NodeMeta, StorageInitialVolume}; use crate::parameters::TryIntoV2Parameter; #[cfg(feature = "core")] use pywr_core::{ derived_metric::DerivedMetric, metric::MetricF64, - node::StorageInitialVolume, timestep::TimeDomain, virtual_storage::{VirtualStorageBuilder, VirtualStorageReset}, }; @@ -72,13 +71,12 @@ impl RollingWindow { #[serde(deny_unknown_fields)] pub struct RollingVirtualStorageNode { pub meta: NodeMeta, - pub nodes: Vec, + pub nodes: Vec, pub factors: Option>, pub max_volume: Option, pub min_volume: Option, pub cost: Option, - pub initial_volume: Option, - pub initial_volume_pc: Option, + pub initial_volume: StorageInitialVolume, pub window: RollingWindow, } @@ -108,10 +106,10 @@ impl RollingVirtualStorageNode { let indices = self .nodes .iter() - .map(|name| { + .map(|node_ref| { args.schema - .get_node_by_name(name) - .ok_or_else(|| SchemaError::NodeNotFound(name.to_string()))? + .get_node_by_name(&node_ref.name) + .ok_or_else(|| SchemaError::NodeNotFound(node_ref.name.to_string()))? .node_indices_for_constraints(network, args) }) .collect::, _>>()? @@ -121,14 +119,6 @@ impl RollingVirtualStorageNode { Ok(indices) } pub fn add_to_model(&self, network: &mut pywr_core::network::Network, args: &LoadArgs) -> Result<(), SchemaError> { - let initial_volume = if let Some(iv) = self.initial_volume { - StorageInitialVolume::Absolute(iv) - } else if let Some(pc) = self.initial_volume_pc { - StorageInitialVolume::Proportional(pc) - } else { - return Err(SchemaError::MissingInitialVolume(self.meta.name.to_string())); - }; - let cost = match &self.cost { Some(v) => v.load(network, args)?.into(), None => None, @@ -155,7 +145,7 @@ impl RollingVirtualStorageNode { })?; let mut builder = VirtualStorageBuilder::new(self.meta.name.as_str(), &node_idxs) - .initial_volume(initial_volume) + .initial_volume(self.initial_volume.into()) .min_volume(min_volume) .max_volume(max_volume) .reset(reset) @@ -220,6 +210,17 @@ impl TryFrom for RollingVirtualStorageNode { .map(|v| v.try_into_v2_parameter(Some(&meta.name), &mut unnamed_count)) .transpose()?; + let initial_volume = if let Some(v) = v1.initial_volume { + StorageInitialVolume::Absolute(v) + } else if let Some(v) = v1.initial_volume_pc { + StorageInitialVolume::Proportional(v) + } else { + return Err(ConversionError::MissingAttribute { + name: meta.name, + attrs: vec!["initial_volume".to_string(), "initial_volume_pc".to_string()], + }); + }; + let window = if let Some(days) = v1.days { if let Some(days) = NonZeroUsize::new(days as usize) { RollingWindow::Days(days) @@ -245,15 +246,16 @@ impl TryFrom for RollingVirtualStorageNode { }); }; + let nodes = v1.nodes.into_iter().map(|n| n.into()).collect(); + let n = Self { meta, - nodes: v1.nodes, + nodes, factors: v1.factors, max_volume, min_volume, cost, - initial_volume: v1.initial_volume, - initial_volume_pc: v1.initial_volume_pc, + initial_volume, window, }; Ok(n) diff --git a/pywr-schema/src/nodes/turbine.rs b/pywr-schema/src/nodes/turbine.rs index f1321761..aca00b55 100644 --- a/pywr-schema/src/nodes/turbine.rs +++ b/pywr-schema/src/nodes/turbine.rs @@ -13,7 +13,7 @@ use pywr_core::{ use pywr_schema_macros::PywrVisitAll; use schemars::JsonSchema; -#[derive(serde::Deserialize, serde::Serialize, Clone, Debug, JsonSchema, PywrVisitAll)] +#[derive(serde::Deserialize, serde::Serialize, Clone, Debug, strum_macros::Display, JsonSchema, PywrVisitAll)] pub enum TargetType { // set flow derived from the hydropower target as a max_flow MaxFlow, diff --git a/pywr-schema/src/nodes/virtual_storage.rs b/pywr-schema/src/nodes/virtual_storage.rs index ca219d1f..781c2fc2 100644 --- a/pywr-schema/src/nodes/virtual_storage.rs +++ b/pywr-schema/src/nodes/virtual_storage.rs @@ -1,8 +1,7 @@ use crate::error::ConversionError; #[cfg(feature = "core")] use crate::error::SchemaError; -use crate::metric::Metric; -use crate::metric::NodeReference; +use crate::metric::{Metric, SimpleNodeReference}; #[cfg(feature = "core")] use crate::model::LoadArgs; use crate::nodes::core::StorageInitialVolume; @@ -22,7 +21,7 @@ use schemars::JsonSchema; #[serde(deny_unknown_fields)] pub struct VirtualStorageNode { pub meta: NodeMeta, - pub nodes: Vec, + pub nodes: Vec, pub factors: Option>, pub max_volume: Option, pub min_volume: Option, diff --git a/pywr-schema/src/parameters/aggregated.rs b/pywr-schema/src/parameters/aggregated.rs index 4f1f5250..6249fc3d 100644 --- a/pywr-schema/src/parameters/aggregated.rs +++ b/pywr-schema/src/parameters/aggregated.rs @@ -16,7 +16,7 @@ use schemars::JsonSchema; use std::collections::HashMap; // TODO complete these -#[derive(serde::Deserialize, serde::Serialize, Debug, Copy, Clone, JsonSchema, PywrVisitAll)] +#[derive(serde::Deserialize, serde::Serialize, Debug, Copy, Clone, strum_macros::Display, JsonSchema, PywrVisitAll)] #[serde(rename_all = "lowercase")] pub enum AggFunc { Sum, @@ -128,7 +128,7 @@ impl TryFromV1Parameter for AggregatedParameter { } // TODO complete these -#[derive(serde::Deserialize, serde::Serialize, Debug, Copy, Clone, JsonSchema, PywrVisitAll)] +#[derive(serde::Deserialize, serde::Serialize, Debug, Copy, Clone, strum_macros::Display, JsonSchema, PywrVisitAll)] #[serde(rename_all = "lowercase")] pub enum IndexAggFunc { Sum, diff --git a/pywr-schema/src/parameters/mod.rs b/pywr-schema/src/parameters/mod.rs index 0a85b6a5..93da57b5 100644 --- a/pywr-schema/src/parameters/mod.rs +++ b/pywr-schema/src/parameters/mod.rs @@ -26,37 +26,35 @@ mod thresholds; #[cfg(feature = "core")] pub use super::data_tables::LoadedTableCollection; pub use super::data_tables::TableDataRef; -pub use super::parameters::aggregated::{AggFunc, AggregatedIndexParameter, AggregatedParameter, IndexAggFunc}; -pub use super::parameters::asymmetric_switch::AsymmetricSwitchIndexParameter; -pub use super::parameters::control_curves::{ - ControlCurveIndexParameter, ControlCurveInterpolatedParameter, ControlCurveParameter, - ControlCurvePiecewiseInterpolatedParameter, -}; -pub use super::parameters::core::{ - ActivationFunction, ConstantParameter, DivisionParameter, MaxParameter, MinParameter, NegativeMaxParameter, - NegativeMinParameter, NegativeParameter, VariableSettings, -}; -pub use super::parameters::delay::DelayParameter; -pub use super::parameters::discount_factor::DiscountFactorParameter; -pub use super::parameters::indexed_array::IndexedArrayParameter; -pub use super::parameters::polynomial::Polynomial1DParameter; -pub use super::parameters::profiles::{ - DailyProfileParameter, MonthlyProfileParameter, RadialBasisFunction, RbfProfileParameter, - RbfProfileVariableSettings, UniformDrawdownProfileParameter, WeeklyProfileParameter, -}; -pub use super::parameters::python::{PythonParameter, PythonReturnType, PythonSource}; -pub use super::parameters::tables::TablesArrayParameter; -pub use super::parameters::thresholds::ParameterThresholdParameter; use crate::error::ConversionError; #[cfg(feature = "core")] use crate::error::SchemaError; use crate::metric::Metric; #[cfg(feature = "core")] use crate::model::LoadArgs; -pub use crate::parameters::hydropower::HydropowerTargetParameter; -use crate::parameters::interpolated::InterpolatedParameter; use crate::visit::{VisitMetrics, VisitPaths}; +pub use aggregated::{AggFunc, AggregatedIndexParameter, AggregatedParameter, IndexAggFunc}; +pub use asymmetric_switch::AsymmetricSwitchIndexParameter; +pub use control_curves::{ + ControlCurveIndexParameter, ControlCurveInterpolatedParameter, ControlCurveParameter, + ControlCurvePiecewiseInterpolatedParameter, +}; +pub use core::{ + ActivationFunction, ConstantParameter, DivisionParameter, MaxParameter, MinParameter, NegativeMaxParameter, + NegativeMinParameter, NegativeParameter, VariableSettings, +}; +pub use delay::DelayParameter; +pub use discount_factor::DiscountFactorParameter; +pub use hydropower::HydropowerTargetParameter; +pub use indexed_array::IndexedArrayParameter; +pub use interpolated::InterpolatedParameter; pub use offset::OffsetParameter; +pub use polynomial::Polynomial1DParameter; +pub use profiles::{ + DailyProfileParameter, MonthlyInterpDay, MonthlyProfileParameter, RadialBasisFunction, RbfProfileParameter, + RbfProfileVariableSettings, UniformDrawdownProfileParameter, WeeklyProfileParameter, +}; +pub use python::{PythonParameter, PythonReturnType, PythonSource}; #[cfg(feature = "core")] use pywr_core::{metric::MetricUsize, parameters::ParameterIndex}; use pywr_schema_macros::PywrVisitAll; @@ -68,6 +66,8 @@ use pywr_v1_schema::parameters::{ use schemars::JsonSchema; use std::path::{Path, PathBuf}; use strum_macros::{Display, EnumDiscriminants, EnumString, IntoStaticStr, VariantNames}; +pub use tables::TablesArrayParameter; +pub use thresholds::ParameterThresholdParameter; #[derive(serde::Deserialize, serde::Serialize, Debug, Clone, JsonSchema, PywrVisitAll)] pub struct ParameterMeta { diff --git a/pywr-schema/src/parameters/profiles.rs b/pywr-schema/src/parameters/profiles.rs index 661e56c4..ffac9146 100644 --- a/pywr-schema/src/parameters/profiles.rs +++ b/pywr-schema/src/parameters/profiles.rs @@ -70,7 +70,7 @@ impl TryFromV1Parameter for DailyProfileParameter { } } -#[derive(serde::Deserialize, serde::Serialize, Debug, Copy, Clone, JsonSchema, PywrVisitAll)] +#[derive(serde::Deserialize, serde::Serialize, Debug, Copy, Clone, strum_macros::Display, JsonSchema, PywrVisitAll)] pub enum MonthlyInterpDay { First, Last, diff --git a/pywr-schema/src/test_models/30-day-licence.json b/pywr-schema/src/test_models/30-day-licence.json index 062f5d73..8736cb2f 100644 --- a/pywr-schema/src/test_models/30-day-licence.json +++ b/pywr-schema/src/test_models/30-day-licence.json @@ -47,13 +47,17 @@ }, "type": "RollingVirtualStorage", "nodes": [ - "supply1" + { + "name": "supply1" + } ], "max_volume": { "type": "Constant", "value": 300 }, - "initial_volume": 0.0, + "initial_volume": { + "Proportional": 0.0 + }, "window": { "Days": 30 } diff --git a/pywr-schema/src/test_models/mutual-exclusivity1.json b/pywr-schema/src/test_models/mutual-exclusivity1.json index a437c5d5..8deb2ecd 100644 --- a/pywr-schema/src/test_models/mutual-exclusivity1.json +++ b/pywr-schema/src/test_models/mutual-exclusivity1.json @@ -71,8 +71,12 @@ }, "type": "Aggregated", "nodes": [ - "link1", - "link2" + { + "name": "link1" + }, + { + "name": "link2" + } ], "factors": { "type": "Exclusive" diff --git a/pywr-schema/src/test_models/mutual-exclusivity2.json b/pywr-schema/src/test_models/mutual-exclusivity2.json index 5112d2a3..b9ad725b 100644 --- a/pywr-schema/src/test_models/mutual-exclusivity2.json +++ b/pywr-schema/src/test_models/mutual-exclusivity2.json @@ -78,8 +78,12 @@ }, "type": "Aggregated", "nodes": [ - "wtw1", - "wtw2" + { + "name": "wtw1" + }, + { + "name": "wtw2" + } ], "factors": { "type": "Exclusive" diff --git a/pywr-schema/src/test_models/mutual-exclusivity3.json b/pywr-schema/src/test_models/mutual-exclusivity3.json index 7d817383..d2037b2f 100644 --- a/pywr-schema/src/test_models/mutual-exclusivity3.json +++ b/pywr-schema/src/test_models/mutual-exclusivity3.json @@ -91,8 +91,12 @@ }, "type": "Aggregated", "nodes": [ - "link1", - "link2" + { + "name": "link1" + }, + { + "name": "link2" + } ], "factors": { "type": "Exclusive"