From d9d636107168f2ca3094d49262c1d1f7edc20e55 Mon Sep 17 00:00:00 2001 From: James Tomlinson Date: Wed, 27 Mar 2024 09:39:52 +0000 Subject: [PATCH] refactor: Add LoadArgs to capture schema loading arguments. (#148) This reduces the number of arguments to schema loading functions, and will make maintaining those arguments easier in future. --- pywr-schema/src/model.rs | 151 ++++++--- .../src/nodes/annual_virtual_storage.rs | 53 +-- pywr-schema/src/nodes/core.rs | 247 ++------------ pywr-schema/src/nodes/delay.rs | 11 +- pywr-schema/src/nodes/loss_link.rs | 43 +-- pywr-schema/src/nodes/mod.rs | 206 ++---------- .../src/nodes/monthly_virtual_storage.rs | 53 +-- pywr-schema/src/nodes/piecewise_link.rs | 43 +-- pywr-schema/src/nodes/piecewise_storage.rs | 74 +---- pywr-schema/src/nodes/river_gauge.rs | 33 +- .../src/nodes/river_split_with_gauge.rs | 43 +-- .../src/nodes/rolling_virtual_storage.rs | 65 +--- pywr-schema/src/nodes/virtual_storage.rs | 53 +-- .../src/nodes/water_treatment_works.rs | 73 +---- pywr-schema/src/parameters/aggregated.rs | 44 +-- .../src/parameters/asymmetric_switch.rs | 33 +- pywr-schema/src/parameters/control_curves.rs | 114 +------ pywr-schema/src/parameters/core.rs | 88 +---- pywr-schema/src/parameters/delay.rs | 23 +- pywr-schema/src/parameters/discount_factor.rs | 23 +- pywr-schema/src/parameters/indexed_array.rs | 35 +- pywr-schema/src/parameters/interpolated.rs | 47 +-- pywr-schema/src/parameters/mod.rs | 302 +++--------------- pywr-schema/src/parameters/offset.rs | 28 +- pywr-schema/src/parameters/profiles.rs | 22 +- pywr-schema/src/parameters/python.rs | 81 ++--- pywr-schema/src/parameters/tables.rs | 12 +- pywr-schema/src/parameters/thresholds.rs | 33 +- 28 files changed, 378 insertions(+), 1655 deletions(-) diff --git a/pywr-schema/src/model.rs b/pywr-schema/src/model.rs index db8accaf..d4a9262f 100644 --- a/pywr-schema/src/model.rs +++ b/pywr-schema/src/model.rs @@ -136,6 +136,16 @@ pub struct Scenario { pub ensemble_names: Option>, } +#[derive(Clone)] +pub struct LoadArgs<'a> { + pub schema: &'a PywrNetwork, + pub domain: &'a ModelDomain, + pub tables: &'a LoadedTableCollection, + pub timeseries: &'a LoadedTimeseriesCollection, + pub data_path: Option<&'a Path>, + pub inter_network_transfers: &'a [PywrMultiNetworkTransfer], +} + #[derive(serde::Deserialize, serde::Serialize, Clone, Default)] pub struct PywrNetwork { pub nodes: Vec, @@ -183,20 +193,41 @@ impl PywrNetwork { } } + pub fn load_tables(&self, data_path: Option<&Path>) -> Result { + Ok(LoadedTableCollection::from_schema(self.tables.as_deref(), data_path)?) + } + + pub fn load_timeseries( + &self, + domain: &ModelDomain, + data_path: Option<&Path>, + ) -> Result { + Ok(LoadedTimeseriesCollection::from_schema( + self.timeseries.as_deref(), + domain, + data_path, + )?) + } + pub fn build_network( &self, domain: &ModelDomain, data_path: Option<&Path>, output_path: Option<&Path>, + tables: &LoadedTableCollection, + timeseries: &LoadedTimeseriesCollection, inter_network_transfers: &[PywrMultiNetworkTransfer], ) -> Result { let mut network = pywr_core::network::Network::default(); - // Load all the data tables - let tables = LoadedTableCollection::from_schema(self.tables.as_deref(), data_path)?; - - // Load all timeseries data - let timeseries = LoadedTimeseriesCollection::from_schema(self.timeseries.as_deref(), domain, data_path)?; + let args = LoadArgs { + schema: self, + domain, + tables, + timeseries, + data_path, + inter_network_transfers, + }; // Create all the nodes let mut remaining_nodes = self.nodes.clone(); @@ -205,15 +236,7 @@ impl PywrNetwork { let mut failed_nodes: Vec = Vec::new(); let n = remaining_nodes.len(); for node in remaining_nodes.into_iter() { - if let Err(e) = node.add_to_model( - &mut network, - &self, - domain, - &tables, - data_path, - inter_network_transfers, - ×eries, - ) { + if let Err(e) = node.add_to_model(&mut network, &args) { // Adding the node failed! match e { SchemaError::PywrCore(core_err) => match core_err { @@ -264,15 +287,7 @@ impl PywrNetwork { let mut failed_parameters: Vec = Vec::new(); let n = remaining_parameters.len(); for parameter in remaining_parameters.into_iter() { - if let Err(e) = parameter.add_to_model( - &mut network, - self, - domain, - &tables, - data_path, - inter_network_transfers, - ×eries, - ) { + if let Err(e) = parameter.add_to_model(&mut network, &args) { // Adding the parameter failed! match e { SchemaError::PywrCore(core_err) => match core_err { @@ -298,15 +313,7 @@ impl PywrNetwork { // Apply the inline parameters & constraints to the nodes for node in &self.nodes { - node.set_constraints( - &mut network, - self, - domain, - &tables, - data_path, - inter_network_transfers, - ×eries, - )?; + node.set_constraints(&mut network, &args)?; } // Create all of the metric sets @@ -411,7 +418,12 @@ impl PywrModel { let domain = ModelDomain::from(timestepper, scenario_collection)?; - let network = self.network.build_network(&domain, data_path, output_path, &[])?; + let tables = self.network.load_tables(data_path)?; + let timeseries = self.network.load_timeseries(&domain, data_path)?; + + let network = self + .network + .build_network(&domain, data_path, output_path, &tables, ×eries, &[])?; let model = pywr_core::models::Model::new(domain, network); @@ -619,15 +631,17 @@ impl PywrMultiNetworkModel { } let domain = ModelDomain::from(timestepper, scenario_collection)?; - let mut model = pywr_core::models::MultiNetworkModel::new(domain); - let mut schemas = Vec::with_capacity(self.networks.len()); + let mut networks = Vec::with_capacity(self.networks.len()); + let mut inter_network_transfers = Vec::new(); + let mut schemas: Vec<(PywrNetwork, LoadedTableCollection, LoadedTimeseriesCollection)> = + Vec::with_capacity(self.networks.len()); // First load all the networks // These will contain any parameters that are referenced by the inter-model transfers // Because of potential circular references, we need to load all the networks first. for network_entry in &self.networks { // Load the network itself - let network = match &network_entry.network { + let (network, schema, tables, timeseries) = match &network_entry.network { PywrNetworkRef::Path(path) => { let pth = if let Some(dp) = data_path { if path.is_relative() { @@ -640,44 +654,85 @@ impl PywrMultiNetworkModel { }; let network_schema = PywrNetwork::from_path(pth)?; + let tables = network_schema.load_tables(data_path)?; + let timeseries = network_schema.load_timeseries(&domain, data_path)?; let net = network_schema.build_network( - model.domain(), + &domain, data_path, output_path, + &tables, + ×eries, &network_entry.transfers, )?; - schemas.push(network_schema); - net + + (net, network_schema, tables, timeseries) } PywrNetworkRef::Inline(network_schema) => { + let tables = network_schema.load_tables(data_path)?; + let timeseries = network_schema.load_timeseries(&domain, data_path)?; let net = network_schema.build_network( - model.domain(), + &domain, data_path, output_path, + &tables, + ×eries, &network_entry.transfers, )?; - schemas.push(network_schema.clone()); - net + + (net, network_schema.clone(), tables, timeseries) } }; - model.add_network(&network_entry.name, network); + schemas.push((schema, tables, timeseries)); + networks.push((network_entry.name.clone(), network)); } // Now load the inter-model transfers for (to_network_idx, network_entry) in self.networks.iter().enumerate() { for transfer in &network_entry.transfers { - let from_network_idx = model.get_network_index_by_name(&transfer.from_network)?; - // Load the metric from the "from" network - let from_network = model.network_mut(from_network_idx)?; + + let (from_network_idx, from_network) = networks + .iter_mut() + .enumerate() + .find_map(|(idx, (name, net))| { + if name.as_str() == transfer.from_network.as_str() { + Some((idx, net)) + } else { + None + } + }) + .ok_or_else(|| SchemaError::NetworkNotFound(transfer.from_network.clone()))?; + // The transfer metric will fail to load if it is defined as an inter-model transfer itself. - let from_metric = transfer.metric.load(from_network, &schemas[from_network_idx], &[])?; + let (from_schema, from_tables, from_timeseries) = &schemas[from_network_idx]; - model.add_inter_network_transfer(from_network_idx, from_metric, to_network_idx, transfer.initial_value); + let args = LoadArgs { + schema: from_schema, + domain: &domain, + tables: from_tables, + timeseries: from_timeseries, + data_path, + inter_network_transfers: &[], + }; + + let from_metric = transfer.metric.load(from_network, &args)?; + + inter_network_transfers.push((from_network_idx, from_metric, to_network_idx, transfer.initial_value)); } } + // Now construct the model from the loaded components + let mut model = pywr_core::models::MultiNetworkModel::new(domain); + + for (name, network) in networks { + model.add_network(&name, network); + } + + for (from_network_idx, from_metric, to_network_idx, initial_value) in inter_network_transfers { + model.add_inter_network_transfer(from_network_idx, from_metric, to_network_idx, initial_value); + } + Ok(model) } } diff --git a/pywr-schema/src/nodes/annual_virtual_storage.rs b/pywr-schema/src/nodes/annual_virtual_storage.rs index 0cee2ce3..bafec164 100644 --- a/pywr-schema/src/nodes/annual_virtual_storage.rs +++ b/pywr-schema/src/nodes/annual_virtual_storage.rs @@ -1,19 +1,15 @@ -use crate::data_tables::LoadedTableCollection; use crate::error::{ConversionError, SchemaError}; -use crate::model::PywrMultiNetworkTransfer; +use crate::model::LoadArgs; use crate::nodes::core::StorageInitialVolume; use crate::nodes::{NodeAttribute, NodeMeta}; use crate::parameters::{DynamicFloatValue, TryIntoV2Parameter}; -use crate::timeseries::LoadedTimeseriesCollection; use pywr_core::derived_metric::DerivedMetric; use pywr_core::metric::MetricF64; -use pywr_core::models::ModelDomain; use pywr_core::node::ConstraintValue; use pywr_core::virtual_storage::VirtualStorageReset; use pywr_schema_macros::PywrNode; use pywr_v1_schema::nodes::AnnualVirtualStorageNode as AnnualVirtualStorageNodeV1; use std::collections::HashMap; -use std::path::Path; #[derive(serde::Deserialize, serde::Serialize, Clone, Debug)] pub struct AnnualReset { @@ -48,58 +44,19 @@ pub struct AnnualVirtualStorageNode { impl AnnualVirtualStorageNode { pub const DEFAULT_ATTRIBUTE: NodeAttribute = NodeAttribute::Volume; - pub fn add_to_model( - &self, - network: &mut pywr_core::network::Network, - schema: &crate::model::PywrNetwork, - domain: &ModelDomain, - tables: &LoadedTableCollection, - data_path: Option<&Path>, - inter_network_transfers: &[PywrMultiNetworkTransfer], - timeseries: &LoadedTimeseriesCollection, - ) -> Result<(), SchemaError> { + pub fn add_to_model(&self, network: &mut pywr_core::network::Network, args: &LoadArgs) -> Result<(), SchemaError> { let cost = match &self.cost { - Some(v) => v - .load( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - )? - .into(), + Some(v) => v.load(network, args)?.into(), None => ConstraintValue::Scalar(0.0), }; let min_volume = match &self.min_volume { - Some(v) => v - .load( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - )? - .into(), + Some(v) => v.load(network, args)?.into(), None => ConstraintValue::Scalar(0.0), }; let max_volume = match &self.max_volume { - Some(v) => v - .load( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - )? - .into(), + Some(v) => v.load(network, args)?.into(), None => ConstraintValue::None, }; diff --git a/pywr-schema/src/nodes/core.rs b/pywr-schema/src/nodes/core.rs index cedd3c37..6bdb0370 100644 --- a/pywr-schema/src/nodes/core.rs +++ b/pywr-schema/src/nodes/core.rs @@ -1,12 +1,9 @@ -use crate::data_tables::LoadedTableCollection; use crate::error::{ConversionError, SchemaError}; -use crate::model::PywrMultiNetworkTransfer; +use crate::model::LoadArgs; use crate::nodes::{NodeAttribute, NodeMeta}; use crate::parameters::{DynamicFloatValue, TryIntoV2Parameter}; -use crate::timeseries::LoadedTimeseriesCollection; use pywr_core::derived_metric::DerivedMetric; use pywr_core::metric::MetricF64; -use pywr_core::models::ModelDomain; use pywr_core::node::{ConstraintValue, StorageInitialVolume as CoreStorageInitialVolume}; use pywr_schema_macros::PywrNode; use pywr_v1_schema::nodes::{ @@ -15,7 +12,6 @@ use pywr_v1_schema::nodes::{ ReservoirNode as ReservoirNodeV1, StorageNode as StorageNodeV1, }; use std::collections::HashMap; -use std::path::Path; #[derive(serde::Deserialize, serde::Serialize, Clone, Default, Debug, PywrNode)] pub struct InputNode { @@ -37,49 +33,20 @@ impl InputNode { pub fn set_constraints( &self, network: &mut pywr_core::network::Network, - schema: &crate::model::PywrNetwork, - domain: &ModelDomain, - tables: &LoadedTableCollection, - data_path: Option<&Path>, - inter_network_transfers: &[PywrMultiNetworkTransfer], - timeseries: &LoadedTimeseriesCollection, + args: &LoadArgs, ) -> Result<(), SchemaError> { if let Some(cost) = &self.cost { - let value = cost.load( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - )?; + let value = cost.load(network, args)?; network.set_node_cost(self.meta.name.as_str(), None, value.into())?; } if let Some(max_flow) = &self.max_flow { - let value = max_flow.load( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - )?; + let value = max_flow.load(network, args)?; network.set_node_max_flow(self.meta.name.as_str(), None, value.into())?; } if let Some(min_flow) = &self.min_flow { - let value = min_flow.load( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - )?; + let value = min_flow.load(network, args)?; network.set_node_min_flow(self.meta.name.as_str(), None, value.into())?; } @@ -169,49 +136,20 @@ impl LinkNode { pub fn set_constraints( &self, network: &mut pywr_core::network::Network, - schema: &crate::model::PywrNetwork, - domain: &ModelDomain, - tables: &LoadedTableCollection, - data_path: Option<&Path>, - inter_network_transfers: &[PywrMultiNetworkTransfer], - timeseries: &LoadedTimeseriesCollection, + args: &LoadArgs, ) -> Result<(), SchemaError> { if let Some(cost) = &self.cost { - let value = cost.load( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - )?; + let value = cost.load(network, args)?; network.set_node_cost(self.meta.name.as_str(), None, value.into())?; } if let Some(max_flow) = &self.max_flow { - let value = max_flow.load( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - )?; + let value = max_flow.load(network, args)?; network.set_node_max_flow(self.meta.name.as_str(), None, value.into())?; } if let Some(min_flow) = &self.min_flow { - let value = min_flow.load( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - )?; + let value = min_flow.load(network, args)?; network.set_node_min_flow(self.meta.name.as_str(), None, value.into())?; } @@ -301,49 +239,20 @@ impl OutputNode { pub fn set_constraints( &self, network: &mut pywr_core::network::Network, - schema: &crate::model::PywrNetwork, - domain: &ModelDomain, - tables: &LoadedTableCollection, - data_path: Option<&Path>, - inter_network_transfers: &[PywrMultiNetworkTransfer], - timeseries: &LoadedTimeseriesCollection, + args: &LoadArgs, ) -> Result<(), SchemaError> { if let Some(cost) = &self.cost { - let value = cost.load( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - )?; + let value = cost.load(network, args)?; network.set_node_cost(self.meta.name.as_str(), None, value.into())?; } if let Some(max_flow) = &self.max_flow { - let value = max_flow.load( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - )?; + let value = max_flow.load(network, args)?; network.set_node_max_flow(self.meta.name.as_str(), None, value.into())?; } if let Some(min_flow) = &self.min_flow { - let value = min_flow.load( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - )?; + let value = min_flow.load(network, args)?; network.set_node_min_flow(self.meta.name.as_str(), None, value.into())?; } @@ -452,43 +361,14 @@ pub struct StorageNode { impl StorageNode { const DEFAULT_ATTRIBUTE: NodeAttribute = NodeAttribute::Volume; - pub fn add_to_model( - &self, - network: &mut pywr_core::network::Network, - schema: &crate::model::PywrNetwork, - domain: &ModelDomain, - tables: &LoadedTableCollection, - data_path: Option<&Path>, - inter_network_transfers: &[PywrMultiNetworkTransfer], - timeseries: &LoadedTimeseriesCollection, - ) -> Result<(), SchemaError> { + pub fn add_to_model(&self, network: &mut pywr_core::network::Network, args: &LoadArgs) -> Result<(), SchemaError> { let min_volume = match &self.min_volume { - Some(v) => v - .load( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - )? - .into(), + Some(v) => v.load(network, args)?.into(), None => ConstraintValue::Scalar(0.0), }; let max_volume = match &self.max_volume { - Some(v) => v - .load( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - )? - .into(), + Some(v) => v.load(network, args)?.into(), None => ConstraintValue::None, }; @@ -505,23 +385,10 @@ impl StorageNode { pub fn set_constraints( &self, network: &mut pywr_core::network::Network, - schema: &crate::model::PywrNetwork, - domain: &ModelDomain, - tables: &LoadedTableCollection, - data_path: Option<&Path>, - inter_network_transfers: &[PywrMultiNetworkTransfer], - timeseries: &LoadedTimeseriesCollection, + args: &LoadArgs, ) -> Result<(), SchemaError> { if let Some(cost) = &self.cost { - let value = cost.load( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - )?; + let value = cost.load(network, args)?; network.set_node_cost(self.meta.name.as_str(), None, value.into())?; } @@ -697,36 +564,15 @@ impl CatchmentNode { pub fn set_constraints( &self, network: &mut pywr_core::network::Network, - schema: &crate::model::PywrNetwork, - domain: &ModelDomain, - tables: &LoadedTableCollection, - data_path: Option<&Path>, - inter_network_transfers: &[PywrMultiNetworkTransfer], - timeseries: &LoadedTimeseriesCollection, + args: &LoadArgs, ) -> Result<(), SchemaError> { if let Some(cost) = &self.cost { - let value = cost.load( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - )?; + let value = cost.load(network, args)?; network.set_node_cost(self.meta.name.as_str(), None, value.into())?; } if let Some(flow) = &self.flow { - let value = flow.load( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - )?; + let value = flow.load(network, args)?; network.set_node_min_flow(self.meta.name.as_str(), None, value.clone().into())?; network.set_node_max_flow(self.meta.name.as_str(), None, value.into())?; } @@ -824,36 +670,15 @@ impl AggregatedNode { pub fn set_constraints( &self, network: &mut pywr_core::network::Network, - schema: &crate::model::PywrNetwork, - domain: &ModelDomain, - tables: &LoadedTableCollection, - data_path: Option<&Path>, - inter_network_transfers: &[PywrMultiNetworkTransfer], - timeseries: &LoadedTimeseriesCollection, + args: &LoadArgs, ) -> Result<(), SchemaError> { if let Some(max_flow) = &self.max_flow { - let value = max_flow.load( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - )?; + let value = max_flow.load(network, args)?; network.set_aggregated_node_max_flow(self.meta.name.as_str(), None, value.into())?; } if let Some(min_flow) = &self.min_flow { - let value = min_flow.load( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - )?; + let value = min_flow.load(network, args)?; network.set_aggregated_node_min_flow(self.meta.name.as_str(), None, value.into())?; } @@ -862,33 +687,13 @@ impl AggregatedNode { Factors::Proportion { factors } => pywr_core::aggregated_node::Factors::Proportion( factors .iter() - .map(|f| { - f.load( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - ) - }) + .map(|f| f.load(network, args)) .collect::, _>>()?, ), Factors::Ratio { factors } => pywr_core::aggregated_node::Factors::Ratio( factors .iter() - .map(|f| { - f.load( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - ) - }) + .map(|f| f.load(network, args)) .collect::, _>>()?, ), }; diff --git a/pywr-schema/src/nodes/delay.rs b/pywr-schema/src/nodes/delay.rs index 7c2fe0b3..6591446d 100644 --- a/pywr-schema/src/nodes/delay.rs +++ b/pywr-schema/src/nodes/delay.rs @@ -1,5 +1,5 @@ -use crate::data_tables::LoadedTableCollection; use crate::error::{ConversionError, SchemaError}; +use crate::model::LoadArgs; use crate::nodes::{NodeAttribute, NodeMeta}; use crate::parameters::{ConstantValue, DynamicFloatValue}; use pywr_core::metric::MetricF64; @@ -55,13 +55,18 @@ impl DelayNode { pub fn set_constraints( &self, network: &mut pywr_core::network::Network, - tables: &LoadedTableCollection, + args: &LoadArgs, ) -> Result<(), SchemaError> { // Create the delay parameter let name = format!("{}-delay", self.meta.name.as_str()); let output_idx = network.get_node_index_by_name(self.meta.name.as_str(), Self::output_sub_name())?; let metric = MetricF64::NodeInFlow(output_idx); - let p = pywr_core::parameters::DelayParameter::new(&name, metric, self.delay, self.initial_value.load(tables)?); + let p = pywr_core::parameters::DelayParameter::new( + &name, + metric, + self.delay, + self.initial_value.load(args.tables)?, + ); let delay_idx = network.add_parameter(Box::new(p))?; // Apply it as a constraint on the input node. diff --git a/pywr-schema/src/nodes/loss_link.rs b/pywr-schema/src/nodes/loss_link.rs index de7319ac..a82cebfb 100644 --- a/pywr-schema/src/nodes/loss_link.rs +++ b/pywr-schema/src/nodes/loss_link.rs @@ -1,15 +1,11 @@ -use crate::data_tables::LoadedTableCollection; use crate::error::{ConversionError, SchemaError}; -use crate::model::PywrMultiNetworkTransfer; +use crate::model::LoadArgs; use crate::nodes::{NodeAttribute, NodeMeta}; use crate::parameters::{DynamicFloatValue, TryIntoV2Parameter}; -use crate::timeseries::LoadedTimeseriesCollection; use pywr_core::metric::MetricF64; -use pywr_core::models::ModelDomain; use pywr_schema_macros::PywrNode; use pywr_v1_schema::nodes::LossLinkNode as LossLinkNodeV1; use std::collections::HashMap; -use std::path::Path; #[doc = svgbobdoc::transform!( /// This is used to represent link with losses. @@ -62,49 +58,20 @@ impl LossLinkNode { pub fn set_constraints( &self, network: &mut pywr_core::network::Network, - schema: &crate::model::PywrNetwork, - domain: &ModelDomain, - tables: &LoadedTableCollection, - data_path: Option<&Path>, - inter_network_transfers: &[PywrMultiNetworkTransfer], - timeseries: &LoadedTimeseriesCollection, + args: &LoadArgs, ) -> Result<(), SchemaError> { if let Some(cost) = &self.net_cost { - let value = cost.load( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - )?; + let value = cost.load(network, args)?; network.set_node_cost(self.meta.name.as_str(), Self::net_sub_name(), value.into())?; } if let Some(max_flow) = &self.max_net_flow { - let value = max_flow.load( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - )?; + let value = max_flow.load(network, args)?; network.set_node_max_flow(self.meta.name.as_str(), Self::net_sub_name(), value.into())?; } if let Some(min_flow) = &self.min_net_flow { - let value = min_flow.load( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - )?; + let value = min_flow.load(network, args)?; network.set_node_min_flow(self.meta.name.as_str(), Self::net_sub_name(), value.into())?; } diff --git a/pywr-schema/src/nodes/mod.rs b/pywr-schema/src/nodes/mod.rs index 91422eb9..6a393dba 100644 --- a/pywr-schema/src/nodes/mod.rs +++ b/pywr-schema/src/nodes/mod.rs @@ -12,9 +12,8 @@ mod rolling_virtual_storage; mod virtual_storage; mod water_treatment_works; -use crate::data_tables::LoadedTableCollection; use crate::error::{ConversionError, SchemaError}; -use crate::model::{PywrMultiNetworkTransfer, PywrNetwork}; +use crate::model::{LoadArgs, PywrNetwork}; pub use crate::nodes::core::{ AggregatedNode, AggregatedStorageNode, CatchmentNode, InputNode, LinkNode, OutputNode, StorageNode, }; @@ -22,14 +21,12 @@ pub use crate::nodes::delay::DelayNode; pub use crate::nodes::river::RiverNode; use crate::nodes::rolling_virtual_storage::RollingVirtualStorageNode; use crate::parameters::{DynamicFloatValue, TimeseriesV1Data}; -use crate::timeseries::LoadedTimeseriesCollection; pub use annual_virtual_storage::AnnualVirtualStorageNode; pub use loss_link::LossLinkNode; pub use monthly_virtual_storage::MonthlyVirtualStorageNode; pub use piecewise_link::{PiecewiseLinkNode, PiecewiseLinkStep}; pub use piecewise_storage::PiecewiseStorageNode; use pywr_core::metric::MetricF64; -use pywr_core::models::ModelDomain; use pywr_v1_schema::nodes::{ CoreNode as CoreNodeV1, Node as NodeV1, NodeMeta as NodeMetaV1, NodePosition as NodePositionV1, }; @@ -39,7 +36,6 @@ use pywr_v1_schema::parameters::{ pub use river_gauge::RiverGaugeNode; pub use river_split_with_gauge::RiverSplitWithGaugeNode; use std::collections::HashMap; -use std::path::Path; use strum_macros::{Display, EnumDiscriminants, EnumString, IntoStaticStr, VariantNames}; pub use virtual_storage::VirtualStorageNode; pub use water_treatment_works::WaterTreatmentWorks; @@ -337,29 +333,12 @@ impl Node { } } - pub fn add_to_model( - &self, - network: &mut pywr_core::network::Network, - schema: &PywrNetwork, - domain: &ModelDomain, - tables: &LoadedTableCollection, - data_path: Option<&Path>, - inter_network_transfers: &[PywrMultiNetworkTransfer], - timeseries: &LoadedTimeseriesCollection, - ) -> Result<(), SchemaError> { + pub fn add_to_model(&self, network: &mut pywr_core::network::Network, args: &LoadArgs) -> Result<(), SchemaError> { match self { Node::Input(n) => n.add_to_model(network), Node::Link(n) => n.add_to_model(network), Node::Output(n) => n.add_to_model(network), - Node::Storage(n) => n.add_to_model( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - ), + Node::Storage(n) => n.add_to_model(network, args), Node::Catchment(n) => n.add_to_model(network), Node::RiverGauge(n) => n.add_to_model(network), Node::LossLink(n) => n.add_to_model(network), @@ -368,180 +347,39 @@ impl Node { Node::WaterTreatmentWorks(n) => n.add_to_model(network), Node::Aggregated(n) => n.add_to_model(network), Node::AggregatedStorage(n) => n.add_to_model(network), - Node::VirtualStorage(n) => n.add_to_model( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - ), - Node::AnnualVirtualStorage(n) => n.add_to_model( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - ), + Node::VirtualStorage(n) => n.add_to_model(network, args), + Node::AnnualVirtualStorage(n) => n.add_to_model(network, args), Node::PiecewiseLink(n) => n.add_to_model(network), - Node::PiecewiseStorage(n) => n.add_to_model( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - ), + Node::PiecewiseStorage(n) => n.add_to_model(network, args), Node::Delay(n) => n.add_to_model(network), - Node::MonthlyVirtualStorage(n) => n.add_to_model( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - ), - Node::RollingVirtualStorage(n) => n.add_to_model( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - ), + Node::MonthlyVirtualStorage(n) => n.add_to_model(network, args), + Node::RollingVirtualStorage(n) => n.add_to_model(network, args), } } pub fn set_constraints( &self, network: &mut pywr_core::network::Network, - schema: &PywrNetwork, - domain: &ModelDomain, - tables: &LoadedTableCollection, - data_path: Option<&Path>, - inter_network_transfers: &[PywrMultiNetworkTransfer], - timeseries: &LoadedTimeseriesCollection, + args: &LoadArgs, ) -> Result<(), SchemaError> { match self { - Node::Input(n) => n.set_constraints( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - ), - Node::Link(n) => n.set_constraints( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - ), - Node::Output(n) => n.set_constraints( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - ), - Node::Storage(n) => n.set_constraints( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - ), - Node::Catchment(n) => n.set_constraints( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - ), - Node::RiverGauge(n) => n.set_constraints( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - ), - Node::LossLink(n) => n.set_constraints( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - ), + Node::Input(n) => n.set_constraints(network, args), + Node::Link(n) => n.set_constraints(network, args), + Node::Output(n) => n.set_constraints(network, args), + Node::Storage(n) => n.set_constraints(network, args), + Node::Catchment(n) => n.set_constraints(network, args), + Node::RiverGauge(n) => n.set_constraints(network, args), + Node::LossLink(n) => n.set_constraints(network, args), Node::River(_) => Ok(()), // No constraints on river node - Node::RiverSplitWithGauge(n) => n.set_constraints( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - ), - Node::WaterTreatmentWorks(n) => n.set_constraints( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - ), - Node::Aggregated(n) => n.set_constraints( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - ), + Node::RiverSplitWithGauge(n) => n.set_constraints(network, args), + Node::WaterTreatmentWorks(n) => n.set_constraints(network, args), + Node::Aggregated(n) => n.set_constraints(network, args), Node::AggregatedStorage(_) => Ok(()), // No constraints on aggregated storage nodes. Node::VirtualStorage(_) => Ok(()), // TODO Node::AnnualVirtualStorage(_) => Ok(()), // TODO - Node::PiecewiseLink(n) => n.set_constraints( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - ), - Node::PiecewiseStorage(n) => n.set_constraints( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - ), - Node::Delay(n) => n.set_constraints(network, tables), + Node::PiecewiseLink(n) => n.set_constraints(network, args), + Node::PiecewiseStorage(n) => n.set_constraints(network, args), + Node::Delay(n) => n.set_constraints(network, args), Node::MonthlyVirtualStorage(_) => Ok(()), // TODO Node::RollingVirtualStorage(_) => Ok(()), // TODO } diff --git a/pywr-schema/src/nodes/monthly_virtual_storage.rs b/pywr-schema/src/nodes/monthly_virtual_storage.rs index 6e4abcc3..3e93c9a8 100644 --- a/pywr-schema/src/nodes/monthly_virtual_storage.rs +++ b/pywr-schema/src/nodes/monthly_virtual_storage.rs @@ -1,19 +1,15 @@ -use crate::data_tables::LoadedTableCollection; use crate::error::{ConversionError, SchemaError}; -use crate::model::PywrMultiNetworkTransfer; +use crate::model::LoadArgs; use crate::nodes::core::StorageInitialVolume; use crate::nodes::{NodeAttribute, NodeMeta}; use crate::parameters::{DynamicFloatValue, TryIntoV2Parameter}; -use crate::timeseries::LoadedTimeseriesCollection; use pywr_core::derived_metric::DerivedMetric; use pywr_core::metric::MetricF64; -use pywr_core::models::ModelDomain; use pywr_core::node::ConstraintValue; use pywr_core::virtual_storage::VirtualStorageReset; use pywr_schema_macros::PywrNode; use pywr_v1_schema::nodes::MonthlyVirtualStorageNode as MonthlyVirtualStorageNodeV1; use std::collections::HashMap; -use std::path::Path; #[derive(serde::Deserialize, serde::Serialize, Clone, Debug)] pub struct NumberOfMonthsReset { @@ -42,58 +38,19 @@ pub struct MonthlyVirtualStorageNode { impl MonthlyVirtualStorageNode { const DEFAULT_ATTRIBUTE: NodeAttribute = NodeAttribute::Volume; - pub fn add_to_model( - &self, - network: &mut pywr_core::network::Network, - schema: &crate::model::PywrNetwork, - domain: &ModelDomain, - tables: &LoadedTableCollection, - data_path: Option<&Path>, - inter_network_transfers: &[PywrMultiNetworkTransfer], - timeseries: &LoadedTimeseriesCollection, - ) -> Result<(), SchemaError> { + pub fn add_to_model(&self, network: &mut pywr_core::network::Network, args: &LoadArgs) -> Result<(), SchemaError> { let cost = match &self.cost { - Some(v) => v - .load( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - )? - .into(), + Some(v) => v.load(network, args)?.into(), None => ConstraintValue::Scalar(0.0), }; let min_volume = match &self.min_volume { - Some(v) => v - .load( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - )? - .into(), + Some(v) => v.load(network, args)?.into(), None => ConstraintValue::Scalar(0.0), }; let max_volume = match &self.max_volume { - Some(v) => v - .load( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - )? - .into(), + Some(v) => v.load(network, args)?.into(), None => ConstraintValue::None, }; diff --git a/pywr-schema/src/nodes/piecewise_link.rs b/pywr-schema/src/nodes/piecewise_link.rs index 7ff8d364..d08e0775 100644 --- a/pywr-schema/src/nodes/piecewise_link.rs +++ b/pywr-schema/src/nodes/piecewise_link.rs @@ -1,15 +1,11 @@ -use crate::data_tables::LoadedTableCollection; use crate::error::{ConversionError, SchemaError}; -use crate::model::PywrMultiNetworkTransfer; +use crate::model::LoadArgs; use crate::nodes::{NodeAttribute, NodeMeta}; use crate::parameters::{DynamicFloatValue, TryIntoV2Parameter}; -use crate::timeseries::LoadedTimeseriesCollection; use pywr_core::metric::MetricF64; -use pywr_core::models::ModelDomain; use pywr_schema_macros::PywrNode; use pywr_v1_schema::nodes::PiecewiseLinkNode as PiecewiseLinkNodeV1; use std::collections::HashMap; -use std::path::Path; #[derive(serde::Deserialize, serde::Serialize, Clone, Debug)] pub struct PiecewiseLinkStep { @@ -65,52 +61,23 @@ impl PiecewiseLinkNode { pub fn set_constraints( &self, network: &mut pywr_core::network::Network, - schema: &crate::model::PywrNetwork, - domain: &ModelDomain, - tables: &LoadedTableCollection, - data_path: Option<&Path>, - inter_network_transfers: &[PywrMultiNetworkTransfer], - timeseries: &LoadedTimeseriesCollection, + args: &LoadArgs, ) -> Result<(), SchemaError> { for (i, step) in self.steps.iter().enumerate() { let sub_name = Self::step_sub_name(i); if let Some(cost) = &step.cost { - let value = cost.load( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - )?; + let value = cost.load(network, args)?; network.set_node_cost(self.meta.name.as_str(), sub_name.as_deref(), value.into())?; } if let Some(max_flow) = &step.max_flow { - let value = max_flow.load( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - )?; + let value = max_flow.load(network, args)?; network.set_node_max_flow(self.meta.name.as_str(), sub_name.as_deref(), value.into())?; } if let Some(min_flow) = &step.min_flow { - let value = min_flow.load( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - )?; + let value = min_flow.load(network, args)?; network.set_node_min_flow(self.meta.name.as_str(), sub_name.as_deref(), value.into())?; } } diff --git a/pywr-schema/src/nodes/piecewise_storage.rs b/pywr-schema/src/nodes/piecewise_storage.rs index 753ef60b..1fb6765d 100644 --- a/pywr-schema/src/nodes/piecewise_storage.rs +++ b/pywr-schema/src/nodes/piecewise_storage.rs @@ -1,17 +1,13 @@ -use crate::data_tables::LoadedTableCollection; use crate::error::SchemaError; -use crate::model::PywrMultiNetworkTransfer; +use crate::model::LoadArgs; use crate::nodes::{NodeAttribute, NodeMeta}; use crate::parameters::DynamicFloatValue; -use crate::timeseries::LoadedTimeseriesCollection; use pywr_core::derived_metric::DerivedMetric; use pywr_core::metric::MetricF64; -use pywr_core::models::ModelDomain; use pywr_core::node::{ConstraintValue, StorageInitialVolume}; use pywr_core::parameters::VolumeBetweenControlCurvesParameter; use pywr_schema_macros::PywrNode; use std::collections::HashMap; -use std::path::Path; #[derive(serde::Deserialize, serde::Serialize, Clone, Debug)] pub struct PiecewiseStore { @@ -69,26 +65,9 @@ impl PiecewiseStorageNode { Some("agg-store") } - pub fn add_to_model( - &self, - network: &mut pywr_core::network::Network, - schema: &crate::model::PywrNetwork, - domain: &ModelDomain, - tables: &LoadedTableCollection, - data_path: Option<&Path>, - inter_network_transfers: &[PywrMultiNetworkTransfer], - timeseries: &LoadedTimeseriesCollection, - ) -> Result<(), SchemaError> { + pub fn add_to_model(&self, network: &mut pywr_core::network::Network, args: &LoadArgs) -> Result<(), SchemaError> { // These are the min and max volume of the overall node - let max_volume = self.max_volume.load( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - )?; + let max_volume = self.max_volume.load(network, args)?; let mut store_node_indices = Vec::new(); @@ -97,28 +76,12 @@ impl PiecewiseStorageNode { // The volume of this step is the proportion between the last control curve // (or zero if first) and this control curve. let lower = if i > 0 { - Some(self.steps[i - 1].control_curve.load( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - )?) + Some(self.steps[i - 1].control_curve.load(network, args)?) } else { None }; - let upper = step.control_curve.load( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - )?; + let upper = step.control_curve.load(network, args)?; let max_volume_parameter = VolumeBetweenControlCurvesParameter::new( format!("{}-{}-max-volume", self.meta.name, Self::step_sub_name(i).unwrap()).as_str(), @@ -153,15 +116,7 @@ impl PiecewiseStorageNode { // The volume of this store the remain proportion above the last control curve let lower = match self.steps.last() { - Some(step) => Some(step.control_curve.load( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - )?), + Some(step) => Some(step.control_curve.load(network, args)?), None => None, }; @@ -212,26 +167,13 @@ impl PiecewiseStorageNode { pub fn set_constraints( &self, network: &mut pywr_core::network::Network, - schema: &crate::model::PywrNetwork, - domain: &ModelDomain, - tables: &LoadedTableCollection, - data_path: Option<&Path>, - inter_network_transfers: &[PywrMultiNetworkTransfer], - timeseries: &LoadedTimeseriesCollection, + args: &LoadArgs, ) -> Result<(), SchemaError> { for (i, step) in self.steps.iter().enumerate() { let sub_name = Self::step_sub_name(i); if let Some(cost) = &step.cost { - let value = cost.load( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - )?; + let value = cost.load(network, args)?; network.set_node_cost(self.meta.name.as_str(), sub_name.as_deref(), value.into())?; } } diff --git a/pywr-schema/src/nodes/river_gauge.rs b/pywr-schema/src/nodes/river_gauge.rs index 4c17517a..16aba56a 100644 --- a/pywr-schema/src/nodes/river_gauge.rs +++ b/pywr-schema/src/nodes/river_gauge.rs @@ -1,15 +1,11 @@ -use crate::data_tables::LoadedTableCollection; use crate::error::{ConversionError, SchemaError}; -use crate::model::PywrMultiNetworkTransfer; +use crate::model::LoadArgs; use crate::nodes::{NodeAttribute, NodeMeta}; use crate::parameters::{DynamicFloatValue, TryIntoV2Parameter}; -use crate::timeseries::LoadedTimeseriesCollection; use pywr_core::metric::MetricF64; -use pywr_core::models::ModelDomain; use pywr_schema_macros::PywrNode; use pywr_v1_schema::nodes::RiverGaugeNode as RiverGaugeNodeV1; use std::collections::HashMap; -use std::path::Path; #[doc = svgbobdoc::transform!( /// This is used to represent a minimum residual flow (MRF) at a gauging station. @@ -55,37 +51,16 @@ impl RiverGaugeNode { pub fn set_constraints( &self, network: &mut pywr_core::network::Network, - schema: &crate::model::PywrNetwork, - domain: &ModelDomain, - tables: &LoadedTableCollection, - data_path: Option<&Path>, - inter_network_transfers: &[PywrMultiNetworkTransfer], - timeseries: &LoadedTimeseriesCollection, + args: &LoadArgs, ) -> Result<(), SchemaError> { // MRF applies as a maximum on the MRF node. if let Some(cost) = &self.mrf_cost { - let value = cost.load( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - )?; + let value = cost.load(network, args)?; network.set_node_cost(self.meta.name.as_str(), Self::mrf_sub_name(), value.into())?; } if let Some(mrf) = &self.mrf { - let value = mrf.load( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - )?; + let value = mrf.load(network, args)?; network.set_node_max_flow(self.meta.name.as_str(), Self::mrf_sub_name(), value.into())?; } diff --git a/pywr-schema/src/nodes/river_split_with_gauge.rs b/pywr-schema/src/nodes/river_split_with_gauge.rs index dbad4a35..a86aa338 100644 --- a/pywr-schema/src/nodes/river_split_with_gauge.rs +++ b/pywr-schema/src/nodes/river_split_with_gauge.rs @@ -1,17 +1,13 @@ -use crate::data_tables::LoadedTableCollection; use crate::error::{ConversionError, SchemaError}; -use crate::model::PywrMultiNetworkTransfer; +use crate::model::LoadArgs; use crate::nodes::{NodeAttribute, NodeMeta}; use crate::parameters::{DynamicFloatValue, TryIntoV2Parameter}; -use crate::timeseries::LoadedTimeseriesCollection; use pywr_core::aggregated_node::Factors; use pywr_core::metric::MetricF64; -use pywr_core::models::ModelDomain; use pywr_core::node::NodeIndex; use pywr_schema_macros::PywrNode; use pywr_v1_schema::nodes::RiverSplitWithGaugeNode as RiverSplitWithGaugeNodeV1; use std::collections::HashMap; -use std::path::Path; #[doc = svgbobdoc::transform!( /// This is used to represent a proportional split above a minimum residual flow (MRF) at a gauging station. @@ -86,51 +82,22 @@ impl RiverSplitWithGaugeNode { pub fn set_constraints( &self, network: &mut pywr_core::network::Network, - schema: &crate::model::PywrNetwork, - domain: &ModelDomain, - tables: &LoadedTableCollection, - data_path: Option<&Path>, - inter_network_transfers: &[PywrMultiNetworkTransfer], - timeseries: &LoadedTimeseriesCollection, + args: &LoadArgs, ) -> Result<(), SchemaError> { // MRF applies as a maximum on the MRF node. if let Some(cost) = &self.mrf_cost { - let value = cost.load( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - )?; + let value = cost.load(network, args)?; network.set_node_cost(self.meta.name.as_str(), Self::mrf_sub_name(), value.into())?; } if let Some(mrf) = &self.mrf { - let value = mrf.load( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - )?; + let value = mrf.load(network, args)?; network.set_node_max_flow(self.meta.name.as_str(), Self::mrf_sub_name(), value.into())?; } for (i, (factor, _)) in self.splits.iter().enumerate() { // Set the factors for each split - let factors = Factors::Proportion(vec![factor.load( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - )?]); + let factors = Factors::Proportion(vec![factor.load(network, args)?]); network.set_aggregated_node_factors( self.meta.name.as_str(), Self::split_agg_sub_name(i).as_deref(), diff --git a/pywr-schema/src/nodes/rolling_virtual_storage.rs b/pywr-schema/src/nodes/rolling_virtual_storage.rs index f03044c4..aa6de857 100644 --- a/pywr-schema/src/nodes/rolling_virtual_storage.rs +++ b/pywr-schema/src/nodes/rolling_virtual_storage.rs @@ -1,12 +1,9 @@ -use crate::data_tables::LoadedTableCollection; use crate::error::{ConversionError, SchemaError}; -use crate::model::PywrMultiNetworkTransfer; +use crate::model::LoadArgs; use crate::nodes::{NodeAttribute, NodeMeta}; use crate::parameters::{DynamicFloatValue, TryIntoV2Parameter}; -use crate::timeseries::LoadedTimeseriesCollection; use pywr_core::derived_metric::DerivedMetric; use pywr_core::metric::MetricF64; -use pywr_core::models::ModelDomain; use pywr_core::node::{ConstraintValue, StorageInitialVolume}; use pywr_core::timestep::TimeDomain; use pywr_core::virtual_storage::VirtualStorageReset; @@ -14,7 +11,6 @@ use pywr_schema_macros::PywrNode; use pywr_v1_schema::nodes::RollingVirtualStorageNode as RollingVirtualStorageNodeV1; use std::collections::HashMap; use std::num::NonZeroUsize; -use std::path::Path; /// The length of the rolling window. /// @@ -81,16 +77,7 @@ pub struct RollingVirtualStorageNode { impl RollingVirtualStorageNode { const DEFAULT_ATTRIBUTE: NodeAttribute = NodeAttribute::Volume; - pub fn add_to_model( - &self, - network: &mut pywr_core::network::Network, - schema: &crate::model::PywrNetwork, - domain: &ModelDomain, - tables: &LoadedTableCollection, - data_path: Option<&Path>, - inter_network_transfers: &[PywrMultiNetworkTransfer], - timeseries: &LoadedTimeseriesCollection, - ) -> Result<(), SchemaError> { + 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 { @@ -100,47 +87,17 @@ impl RollingVirtualStorageNode { }; let cost = match &self.cost { - Some(v) => v - .load( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - )? - .into(), + Some(v) => v.load(network, args)?.into(), None => ConstraintValue::Scalar(0.0), }; let min_volume = match &self.min_volume { - Some(v) => v - .load( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - )? - .into(), + Some(v) => v.load(network, args)?.into(), None => ConstraintValue::Scalar(0.0), }; let max_volume = match &self.max_volume { - Some(v) => v - .load( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - )? - .into(), + Some(v) => v.load(network, args)?.into(), None => ConstraintValue::None, }; @@ -152,12 +109,12 @@ impl RollingVirtualStorageNode { // The rolling licence never resets let reset = VirtualStorageReset::Never; - let timesteps = self - .window - .as_timesteps(domain.time()) - .ok_or_else(|| SchemaError::InvalidRollingWindow { - name: self.meta.name.clone(), - })?; + let timesteps = + self.window + .as_timesteps(args.domain.time()) + .ok_or_else(|| SchemaError::InvalidRollingWindow { + name: self.meta.name.clone(), + })?; network.add_virtual_storage_node( self.meta.name.as_str(), diff --git a/pywr-schema/src/nodes/virtual_storage.rs b/pywr-schema/src/nodes/virtual_storage.rs index d2c591c0..be34ff57 100644 --- a/pywr-schema/src/nodes/virtual_storage.rs +++ b/pywr-schema/src/nodes/virtual_storage.rs @@ -1,19 +1,15 @@ -use crate::data_tables::LoadedTableCollection; use crate::error::{ConversionError, SchemaError}; -use crate::model::PywrMultiNetworkTransfer; +use crate::model::LoadArgs; use crate::nodes::core::StorageInitialVolume; use crate::nodes::{NodeAttribute, NodeMeta}; use crate::parameters::{DynamicFloatValue, TryIntoV2Parameter}; -use crate::timeseries::LoadedTimeseriesCollection; use pywr_core::derived_metric::DerivedMetric; use pywr_core::metric::MetricF64; -use pywr_core::models::ModelDomain; use pywr_core::node::ConstraintValue; use pywr_core::virtual_storage::VirtualStorageReset; use pywr_schema_macros::PywrNode; use pywr_v1_schema::nodes::VirtualStorageNode as VirtualStorageNodeV1; use std::collections::HashMap; -use std::path::Path; #[derive(serde::Deserialize, serde::Serialize, Clone, Default, Debug, PywrNode)] pub struct VirtualStorageNode { @@ -30,58 +26,19 @@ pub struct VirtualStorageNode { impl VirtualStorageNode { const DEFAULT_ATTRIBUTE: NodeAttribute = NodeAttribute::Volume; - pub fn add_to_model( - &self, - network: &mut pywr_core::network::Network, - schema: &crate::model::PywrNetwork, - domain: &ModelDomain, - tables: &LoadedTableCollection, - data_path: Option<&Path>, - inter_network_transfers: &[PywrMultiNetworkTransfer], - timeseries: &LoadedTimeseriesCollection, - ) -> Result<(), SchemaError> { + pub fn add_to_model(&self, network: &mut pywr_core::network::Network, args: &LoadArgs) -> Result<(), SchemaError> { let cost = match &self.cost { - Some(v) => v - .load( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - )? - .into(), + Some(v) => v.load(network, args)?.into(), None => ConstraintValue::Scalar(0.0), }; let min_volume = match &self.min_volume { - Some(v) => v - .load( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - )? - .into(), + Some(v) => v.load(network, args)?.into(), None => ConstraintValue::Scalar(0.0), }; let max_volume = match &self.max_volume { - Some(v) => v - .load( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - )? - .into(), + Some(v) => v.load(network, args)?.into(), None => ConstraintValue::None, }; diff --git a/pywr-schema/src/nodes/water_treatment_works.rs b/pywr-schema/src/nodes/water_treatment_works.rs index c1c847f5..49836252 100644 --- a/pywr-schema/src/nodes/water_treatment_works.rs +++ b/pywr-schema/src/nodes/water_treatment_works.rs @@ -1,16 +1,12 @@ -use crate::data_tables::LoadedTableCollection; use crate::error::SchemaError; -use crate::model::PywrMultiNetworkTransfer; +use crate::model::LoadArgs; use crate::nodes::{NodeAttribute, NodeMeta}; use crate::parameters::DynamicFloatValue; -use crate::timeseries::LoadedTimeseriesCollection; use num::Zero; use pywr_core::aggregated_node::Factors; use pywr_core::metric::MetricF64; -use pywr_core::models::ModelDomain; use pywr_schema_macros::PywrNode; use std::collections::HashMap; -use std::path::Path; #[doc = svgbobdoc::transform!( /// A node used to represent a water treatment works (WTW) with optional losses. @@ -108,64 +104,27 @@ impl WaterTreatmentWorks { pub fn set_constraints( &self, network: &mut pywr_core::network::Network, - schema: &crate::model::PywrNetwork, - domain: &ModelDomain, - tables: &LoadedTableCollection, - data_path: Option<&Path>, - inter_network_transfers: &[PywrMultiNetworkTransfer], - timeseries: &LoadedTimeseriesCollection, + args: &LoadArgs, ) -> Result<(), SchemaError> { if let Some(cost) = &self.cost { - let value = cost.load( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - )?; + let value = cost.load(network, args)?; network.set_node_cost(self.meta.name.as_str(), Self::net_sub_name(), value.into())?; } if let Some(max_flow) = &self.max_flow { - let value = max_flow.load( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - )?; + let value = max_flow.load(network, args)?; network.set_node_max_flow(self.meta.name.as_str(), Self::net_sub_name(), value.into())?; } if let Some(min_flow) = &self.min_flow { - let value = min_flow.load( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - )?; + let value = min_flow.load(network, args)?; network.set_node_min_flow(self.meta.name.as_str(), Self::net_sub_name(), value.into())?; } // soft min flow constraints; This typically applies a negative cost upto a maximum // defined by the `soft_min_flow` if let Some(cost) = &self.soft_min_flow_cost { - let value = cost.load( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - )?; + let value = cost.load(network, args)?; network.set_node_cost( self.meta.name.as_str(), Self::net_soft_min_flow_sub_name(), @@ -173,15 +132,7 @@ impl WaterTreatmentWorks { )?; } if let Some(min_flow) = &self.soft_min_flow { - let value = min_flow.load( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - )?; + let value = min_flow.load(network, args)?; network.set_node_max_flow( self.meta.name.as_str(), Self::net_soft_min_flow_sub_name(), @@ -192,15 +143,7 @@ impl WaterTreatmentWorks { if let Some(loss_factor) = &self.loss_factor { // Handle the case where we a given a zero loss factor // The aggregated node does not support zero loss factors so filter them here. - let lf = match loss_factor.load( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - )? { + let lf = match loss_factor.load(network, args)? { MetricF64::Constant(f) => { if f.is_zero() { None diff --git a/pywr-schema/src/parameters/aggregated.rs b/pywr-schema/src/parameters/aggregated.rs index 1e6bbd2f..019fa6fe 100644 --- a/pywr-schema/src/parameters/aggregated.rs +++ b/pywr-schema/src/parameters/aggregated.rs @@ -1,19 +1,15 @@ -use crate::data_tables::LoadedTableCollection; use crate::error::{ConversionError, SchemaError}; -use crate::model::PywrMultiNetworkTransfer; +use crate::model::LoadArgs; use crate::parameters::{ DynamicFloatValue, DynamicFloatValueType, DynamicIndexValue, IntoV2Parameter, ParameterMeta, TryFromV1Parameter, TryIntoV2Parameter, }; -use crate::timeseries::LoadedTimeseriesCollection; -use pywr_core::models::ModelDomain; use pywr_core::parameters::ParameterIndex; use pywr_v1_schema::parameters::{ AggFunc as AggFuncV1, AggregatedIndexParameter as AggregatedIndexParameterV1, AggregatedParameter as AggregatedParameterV1, IndexAggFunc as IndexAggFuncV1, }; use std::collections::HashMap; -use std::path::Path; // TODO complete these #[derive(serde::Deserialize, serde::Serialize, Debug, Copy, Clone)] @@ -95,27 +91,12 @@ impl AggregatedParameter { pub fn add_to_model( &self, network: &mut pywr_core::network::Network, - schema: &crate::model::PywrNetwork, - domain: &ModelDomain, - tables: &LoadedTableCollection, - data_path: Option<&Path>, - inter_network_transfers: &[PywrMultiNetworkTransfer], - timeseries: &LoadedTimeseriesCollection, + args: &LoadArgs, ) -> Result, SchemaError> { let metrics = self .metrics .iter() - .map(|v| { - v.load( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - ) - }) + .map(|v| v.load(network, args)) .collect::, _>>()?; let p = pywr_core::parameters::AggregatedParameter::new(&self.meta.name, &metrics, self.agg_func.into()); @@ -213,27 +194,12 @@ impl AggregatedIndexParameter { pub fn add_to_model( &self, network: &mut pywr_core::network::Network, - schema: &crate::model::PywrNetwork, - domain: &ModelDomain, - tables: &LoadedTableCollection, - data_path: Option<&Path>, - inter_network_transfers: &[PywrMultiNetworkTransfer], - timeseries: &LoadedTimeseriesCollection, + args: &LoadArgs, ) -> Result, SchemaError> { let parameters = self .parameters .iter() - .map(|v| { - v.load( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - ) - }) + .map(|v| v.load(network, args)) .collect::, _>>()?; let p = pywr_core::parameters::AggregatedIndexParameter::new(&self.meta.name, parameters, self.agg_func.into()); diff --git a/pywr-schema/src/parameters/asymmetric_switch.rs b/pywr-schema/src/parameters/asymmetric_switch.rs index eede9cda..33d75a35 100644 --- a/pywr-schema/src/parameters/asymmetric_switch.rs +++ b/pywr-schema/src/parameters/asymmetric_switch.rs @@ -1,15 +1,11 @@ -use crate::data_tables::LoadedTableCollection; use crate::error::{ConversionError, SchemaError}; -use crate::model::PywrMultiNetworkTransfer; +use crate::model::LoadArgs; use crate::parameters::{ DynamicFloatValueType, DynamicIndexValue, IntoV2Parameter, ParameterMeta, TryFromV1Parameter, TryIntoV2Parameter, }; -use crate::timeseries::LoadedTimeseriesCollection; -use pywr_core::models::ModelDomain; use pywr_core::parameters::ParameterIndex; use pywr_v1_schema::parameters::AsymmetricSwitchIndexParameter as AsymmetricSwitchIndexParameterV1; use std::collections::HashMap; -use std::path::Path; #[derive(serde::Deserialize, serde::Serialize, Debug, Clone)] pub struct AsymmetricSwitchIndexParameter { @@ -30,31 +26,10 @@ impl AsymmetricSwitchIndexParameter { pub fn add_to_model( &self, network: &mut pywr_core::network::Network, - schema: &crate::model::PywrNetwork, - domain: &ModelDomain, - tables: &LoadedTableCollection, - data_path: Option<&Path>, - inter_network_transfers: &[PywrMultiNetworkTransfer], - timeseries: &LoadedTimeseriesCollection, + args: &LoadArgs, ) -> Result, SchemaError> { - let on_index_parameter = self.on_index_parameter.load( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - )?; - let off_index_parameter = self.off_index_parameter.load( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - )?; + let on_index_parameter = self.on_index_parameter.load(network, args)?; + let off_index_parameter = self.off_index_parameter.load(network, args)?; let p = pywr_core::parameters::AsymmetricSwitchIndexParameter::new( &self.meta.name, diff --git a/pywr-schema/src/parameters/control_curves.rs b/pywr-schema/src/parameters/control_curves.rs index 09a2c1ec..d7b7e8da 100644 --- a/pywr-schema/src/parameters/control_curves.rs +++ b/pywr-schema/src/parameters/control_curves.rs @@ -1,12 +1,9 @@ -use crate::data_tables::LoadedTableCollection; use crate::error::{ConversionError, SchemaError}; -use crate::model::PywrMultiNetworkTransfer; +use crate::model::LoadArgs; use crate::nodes::NodeAttribute; use crate::parameters::{ DynamicFloatValue, IntoV2Parameter, NodeReference, ParameterMeta, TryFromV1Parameter, TryIntoV2Parameter, }; -use crate::timeseries::LoadedTimeseriesCollection; -use pywr_core::models::ModelDomain; use pywr_core::parameters::ParameterIndex; use pywr_v1_schema::parameters::{ ControlCurveIndexParameter as ControlCurveIndexParameterV1, @@ -14,7 +11,6 @@ use pywr_v1_schema::parameters::{ ControlCurveParameter as ControlCurveParameterV1, ControlCurvePiecewiseInterpolatedParameter as ControlCurvePiecewiseInterpolatedParameterV1, }; -use std::path::Path; #[derive(serde::Deserialize, serde::Serialize, Debug, Clone)] pub struct ControlCurveInterpolatedParameter { @@ -29,45 +25,20 @@ impl ControlCurveInterpolatedParameter { pub fn add_to_model( &self, network: &mut pywr_core::network::Network, - schema: &crate::model::PywrNetwork, - domain: &ModelDomain, - tables: &LoadedTableCollection, - data_path: Option<&Path>, - inter_network_transfers: &[PywrMultiNetworkTransfer], - timeseries: &LoadedTimeseriesCollection, + args: &LoadArgs, ) -> Result, SchemaError> { - let metric = self.storage_node.load(network, schema)?; + let metric = self.storage_node.load(network, args)?; let control_curves = self .control_curves .iter() - .map(|cc| { - cc.load( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - ) - }) + .map(|cc| cc.load(network, args)) .collect::>()?; let values = self .values .iter() - .map(|val| { - val.load( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - ) - }) + .map(|val| val.load(network, args)) .collect::>()?; let p = pywr_core::parameters::ControlCurveInterpolatedParameter::new( @@ -153,29 +124,14 @@ impl ControlCurveIndexParameter { pub fn add_to_model( &self, network: &mut pywr_core::network::Network, - schema: &crate::model::PywrNetwork, - domain: &ModelDomain, - tables: &LoadedTableCollection, - data_path: Option<&Path>, - inter_network_transfers: &[PywrMultiNetworkTransfer], - timeseries: &LoadedTimeseriesCollection, + args: &LoadArgs, ) -> Result, SchemaError> { - let metric = self.storage_node.load(network, schema)?; + let metric = self.storage_node.load(network, args)?; let control_curves = self .control_curves .iter() - .map(|cc| { - cc.load( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - ) - }) + .map(|cc| cc.load(network, args)) .collect::>()?; let p = pywr_core::parameters::ControlCurveIndexParameter::new(&self.meta.name, metric, control_curves); @@ -275,45 +231,20 @@ impl ControlCurveParameter { pub fn add_to_model( &self, network: &mut pywr_core::network::Network, - schema: &crate::model::PywrNetwork, - domain: &ModelDomain, - tables: &LoadedTableCollection, - data_path: Option<&Path>, - inter_network_transfers: &[PywrMultiNetworkTransfer], - timeseries: &LoadedTimeseriesCollection, + args: &LoadArgs, ) -> Result, SchemaError> { - let metric = self.storage_node.load(network, schema)?; + let metric = self.storage_node.load(network, args)?; let control_curves = self .control_curves .iter() - .map(|cc| { - cc.load( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - ) - }) + .map(|cc| cc.load(network, args)) .collect::>()?; let values = self .values .iter() - .map(|val| { - val.load( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - ) - }) + .map(|val| val.load(network, args)) .collect::>()?; let p = pywr_core::parameters::ControlCurveParameter::new(&self.meta.name, metric, control_curves, values); @@ -390,29 +321,14 @@ impl ControlCurvePiecewiseInterpolatedParameter { pub fn add_to_model( &self, network: &mut pywr_core::network::Network, - schema: &crate::model::PywrNetwork, - domain: &ModelDomain, - tables: &LoadedTableCollection, - data_path: Option<&Path>, - inter_network_transfers: &[PywrMultiNetworkTransfer], - timeseries: &LoadedTimeseriesCollection, + args: &LoadArgs, ) -> Result, SchemaError> { - let metric = self.storage_node.load(network, schema)?; + let metric = self.storage_node.load(network, args)?; let control_curves = self .control_curves .iter() - .map(|cc| { - cc.load( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - ) - }) + .map(|cc| cc.load(network, args)) .collect::>()?; let values = match &self.values { diff --git a/pywr-schema/src/parameters/core.rs b/pywr-schema/src/parameters/core.rs index 957f775a..2a50ed3b 100644 --- a/pywr-schema/src/parameters/core.rs +++ b/pywr-schema/src/parameters/core.rs @@ -1,19 +1,15 @@ -use crate::data_tables::LoadedTableCollection; use crate::error::{ConversionError, SchemaError}; -use crate::model::PywrMultiNetworkTransfer; +use crate::model::LoadArgs; use crate::parameters::{ ConstantValue, DynamicFloatValue, DynamicFloatValueType, IntoV2Parameter, ParameterMeta, TryFromV1Parameter, TryIntoV2Parameter, }; -use crate::timeseries::LoadedTimeseriesCollection; -use pywr_core::models::ModelDomain; 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, }; use std::collections::HashMap; -use std::path::Path; /// Activation function or transformation to apply to variable value. /// @@ -170,9 +166,9 @@ impl ConstantParameter { pub fn add_to_model( &self, network: &mut pywr_core::network::Network, - tables: &LoadedTableCollection, + args: &LoadArgs, ) -> Result, SchemaError> { - let p = pywr_core::parameters::ConstantParameter::new(&self.meta.name, self.value.load(tables)?); + let p = pywr_core::parameters::ConstantParameter::new(&self.meta.name, self.value.load(args.tables)?); Ok(network.add_parameter(Box::new(p))?) } } @@ -222,22 +218,9 @@ impl MaxParameter { pub fn add_to_model( &self, network: &mut pywr_core::network::Network, - schema: &crate::model::PywrNetwork, - domain: &ModelDomain, - tables: &LoadedTableCollection, - data_path: Option<&Path>, - inter_network_transfers: &[PywrMultiNetworkTransfer], - timeseries: &LoadedTimeseriesCollection, + args: &LoadArgs, ) -> Result, SchemaError> { - let idx = self.parameter.load( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - )?; + let idx = self.parameter.load(network, args)?; let threshold = self.threshold.unwrap_or(0.0); let p = pywr_core::parameters::MaxParameter::new(&self.meta.name, idx, threshold); @@ -304,31 +287,10 @@ impl DivisionParameter { pub fn add_to_model( &self, network: &mut pywr_core::network::Network, - schema: &crate::model::PywrNetwork, - domain: &ModelDomain, - tables: &LoadedTableCollection, - data_path: Option<&Path>, - inter_network_transfers: &[PywrMultiNetworkTransfer], - timeseries: &LoadedTimeseriesCollection, + args: &LoadArgs, ) -> Result, SchemaError> { - let n = self.numerator.load( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - )?; - let d = self.denominator.load( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - )?; + let n = self.numerator.load(network, args)?; + let d = self.denominator.load(network, args)?; let p = pywr_core::parameters::DivisionParameter::new(&self.meta.name, n, d); Ok(network.add_parameter(Box::new(p))?) @@ -392,22 +354,9 @@ impl MinParameter { pub fn add_to_model( &self, network: &mut pywr_core::network::Network, - schema: &crate::model::PywrNetwork, - domain: &ModelDomain, - tables: &LoadedTableCollection, - data_path: Option<&Path>, - inter_network_transfers: &[PywrMultiNetworkTransfer], - timeseries: &LoadedTimeseriesCollection, + args: &LoadArgs, ) -> Result, SchemaError> { - let idx = self.parameter.load( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - )?; + let idx = self.parameter.load(network, args)?; let threshold = self.threshold.unwrap_or(0.0); let p = pywr_core::parameters::MinParameter::new(&self.meta.name, idx, threshold); @@ -456,22 +405,9 @@ impl NegativeParameter { pub fn add_to_model( &self, network: &mut pywr_core::network::Network, - schema: &crate::model::PywrNetwork, - domain: &ModelDomain, - tables: &LoadedTableCollection, - data_path: Option<&Path>, - inter_network_transfers: &[PywrMultiNetworkTransfer], - timeseries: &LoadedTimeseriesCollection, + args: &LoadArgs, ) -> Result, SchemaError> { - let idx = self.parameter.load( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - )?; + let idx = self.parameter.load(network, args)?; let p = pywr_core::parameters::NegativeParameter::new(&self.meta.name, idx); Ok(network.add_parameter(Box::new(p))?) diff --git a/pywr-schema/src/parameters/delay.rs b/pywr-schema/src/parameters/delay.rs index 102ffbc3..1ddb3675 100644 --- a/pywr-schema/src/parameters/delay.rs +++ b/pywr-schema/src/parameters/delay.rs @@ -1,12 +1,8 @@ -use crate::data_tables::LoadedTableCollection; use crate::error::SchemaError; -use crate::model::PywrMultiNetworkTransfer; +use crate::model::LoadArgs; use crate::parameters::{DynamicFloatValue, DynamicFloatValueType, ParameterMeta}; -use crate::timeseries::LoadedTimeseriesCollection; -use pywr_core::models::ModelDomain; use pywr_core::parameters::ParameterIndex; use std::collections::HashMap; -use std::path::Path; /// A parameter that delays a value from the network by a number of time-steps. #[derive(serde::Deserialize, serde::Serialize, Debug, Clone)] @@ -35,22 +31,9 @@ impl DelayParameter { pub fn add_to_model( &self, network: &mut pywr_core::network::Network, - schema: &crate::model::PywrNetwork, - domain: &ModelDomain, - tables: &LoadedTableCollection, - data_path: Option<&Path>, - inter_network_transfers: &[PywrMultiNetworkTransfer], - timeseries: &LoadedTimeseriesCollection, + args: &LoadArgs, ) -> Result, SchemaError> { - let metric = self.metric.load( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - )?; + let metric = self.metric.load(network, args)?; let p = pywr_core::parameters::DelayParameter::new(&self.meta.name, metric, self.delay, self.initial_value); Ok(network.add_parameter(Box::new(p))?) } diff --git a/pywr-schema/src/parameters/discount_factor.rs b/pywr-schema/src/parameters/discount_factor.rs index 339c2cbe..2f91389f 100644 --- a/pywr-schema/src/parameters/discount_factor.rs +++ b/pywr-schema/src/parameters/discount_factor.rs @@ -1,14 +1,10 @@ -use crate::data_tables::LoadedTableCollection; use crate::error::SchemaError; -use crate::model::PywrMultiNetworkTransfer; +use crate::model::LoadArgs; use crate::parameters::{DynamicFloatValue, DynamicFloatValueType, IntoV2Parameter, ParameterMeta, TryFromV1Parameter}; -use crate::timeseries::LoadedTimeseriesCollection; use crate::ConversionError; -use pywr_core::models::ModelDomain; use pywr_core::parameters::ParameterIndex; use pywr_v1_schema::parameters::DiscountFactorParameter as DiscountFactorParameterV1; use std::collections::HashMap; -use std::path::Path; /// A parameter that returns the current discount factor for a given time-step. #[derive(serde::Deserialize, serde::Serialize, Debug, Clone)] @@ -36,22 +32,9 @@ impl DiscountFactorParameter { pub fn add_to_model( &self, network: &mut pywr_core::network::Network, - schema: &crate::model::PywrNetwork, - domain: &ModelDomain, - tables: &LoadedTableCollection, - data_path: Option<&Path>, - inter_network_transfers: &[PywrMultiNetworkTransfer], - timeseries: &LoadedTimeseriesCollection, + args: &LoadArgs, ) -> Result, SchemaError> { - let discount_rate = self.discount_rate.load( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - )?; + let discount_rate = self.discount_rate.load(network, args)?; let p = pywr_core::parameters::DiscountFactorParameter::new(&self.meta.name, discount_rate, self.base_year); Ok(network.add_parameter(Box::new(p))?) } diff --git a/pywr-schema/src/parameters/indexed_array.rs b/pywr-schema/src/parameters/indexed_array.rs index e49a4e3c..6315ccd0 100644 --- a/pywr-schema/src/parameters/indexed_array.rs +++ b/pywr-schema/src/parameters/indexed_array.rs @@ -1,16 +1,12 @@ -use crate::data_tables::LoadedTableCollection; use crate::error::{ConversionError, SchemaError}; -use crate::model::PywrMultiNetworkTransfer; +use crate::model::LoadArgs; use crate::parameters::{ DynamicFloatValue, DynamicFloatValueType, DynamicIndexValue, IntoV2Parameter, ParameterMeta, TryFromV1Parameter, TryIntoV2Parameter, }; -use crate::timeseries::LoadedTimeseriesCollection; -use pywr_core::models::ModelDomain; use pywr_core::parameters::ParameterIndex; use pywr_v1_schema::parameters::IndexedArrayParameter as IndexedArrayParameterV1; use std::collections::HashMap; -use std::path::Path; #[derive(serde::Deserialize, serde::Serialize, Debug, Clone)] pub struct IndexedArrayParameter { @@ -38,37 +34,14 @@ impl IndexedArrayParameter { pub fn add_to_model( &self, network: &mut pywr_core::network::Network, - schema: &crate::model::PywrNetwork, - domain: &ModelDomain, - tables: &LoadedTableCollection, - data_path: Option<&Path>, - inter_network_transfers: &[PywrMultiNetworkTransfer], - timeseries: &LoadedTimeseriesCollection, + args: &LoadArgs, ) -> Result, SchemaError> { - let index_parameter = self.index_parameter.load( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - )?; + let index_parameter = self.index_parameter.load(network, args)?; let metrics = self .metrics .iter() - .map(|v| { - v.load( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - ) - }) + .map(|v| v.load(network, args)) .collect::, _>>()?; let p = pywr_core::parameters::IndexedArrayParameter::new(&self.meta.name, index_parameter, &metrics); diff --git a/pywr-schema/src/parameters/interpolated.rs b/pywr-schema/src/parameters/interpolated.rs index b1914b54..696f0747 100644 --- a/pywr-schema/src/parameters/interpolated.rs +++ b/pywr-schema/src/parameters/interpolated.rs @@ -1,20 +1,16 @@ -use crate::data_tables::LoadedTableCollection; use crate::error::SchemaError; -use crate::model::PywrMultiNetworkTransfer; +use crate::model::LoadArgs; use crate::parameters::{ DynamicFloatValue, DynamicFloatValueType, IntoV2Parameter, MetricFloatReference, MetricFloatValue, NodeReference, ParameterMeta, TryFromV1Parameter, TryIntoV2Parameter, }; -use crate::timeseries::LoadedTimeseriesCollection; use crate::ConversionError; -use pywr_core::models::ModelDomain; use pywr_core::parameters::ParameterIndex; use pywr_v1_schema::parameters::{ InterpolatedFlowParameter as InterpolatedFlowParameterV1, InterpolatedVolumeParameter as InterpolatedVolumeParameterV1, }; use std::collections::HashMap; -use std::path::Path; /// A parameter that interpolates a value to a function with given discrete data points. /// @@ -54,22 +50,9 @@ impl InterpolatedParameter { pub fn add_to_model( &self, network: &mut pywr_core::network::Network, - schema: &crate::model::PywrNetwork, - domain: &ModelDomain, - tables: &LoadedTableCollection, - data_path: Option<&Path>, - inter_network_transfers: &[PywrMultiNetworkTransfer], - timeseries: &LoadedTimeseriesCollection, + args: &LoadArgs, ) -> Result, SchemaError> { - let x = self.x.load( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - )?; + let x = self.x.load(network, args)?; // Sense check the points if self.xp.len() != self.fp.len() { @@ -82,32 +65,12 @@ impl InterpolatedParameter { let xp = self .xp .iter() - .map(|p| { - p.load( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - ) - }) + .map(|p| p.load(network, args)) .collect::, _>>()?; let fp = self .fp .iter() - .map(|p| { - p.load( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - ) - }) + .map(|p| p.load(network, args)) .collect::, _>>()?; let points = xp.into_iter().zip(fp).collect::>(); diff --git a/pywr-schema/src/parameters/mod.rs b/pywr-schema/src/parameters/mod.rs index 2c0a8b49..1dbb3044 100644 --- a/pywr-schema/src/parameters/mod.rs +++ b/pywr-schema/src/parameters/mod.rs @@ -44,21 +44,20 @@ pub use super::parameters::python::{PythonModule, PythonParameter, PythonReturnT pub use super::parameters::tables::TablesArrayParameter; pub use super::parameters::thresholds::ParameterThresholdParameter; use crate::error::{ConversionError, SchemaError}; -use crate::model::PywrMultiNetworkTransfer; +use crate::model::LoadArgs; use crate::nodes::NodeAttribute; use crate::parameters::core::DivisionParameter; use crate::parameters::interpolated::InterpolatedParameter; -use crate::timeseries::LoadedTimeseriesCollection; pub use offset::OffsetParameter; use pywr_core::metric::{MetricF64, MetricUsize}; -use pywr_core::models::{ModelDomain, MultiNetworkTransferIndex}; +use pywr_core::models::MultiNetworkTransferIndex; use pywr_core::parameters::{ParameterIndex, ParameterType}; use pywr_v1_schema::parameters::{ CoreParameter, DataFrameParameter as DataFrameParameterV1, ExternalDataRef as ExternalDataRefV1, Parameter as ParameterV1, ParameterMeta as ParameterMetaV1, ParameterValue as ParameterValueV1, ParameterVec, TableIndex as TableIndexV1, TableIndexEntry as TableIndexEntryV1, }; -use std::path::{Path, PathBuf}; +use std::path::PathBuf; #[derive(serde::Deserialize, serde::Serialize, Debug, Clone)] pub struct ParameterMeta { @@ -236,183 +235,34 @@ impl Parameter { pub fn add_to_model( &self, network: &mut pywr_core::network::Network, - schema: &crate::model::PywrNetwork, - domain: &ModelDomain, - tables: &LoadedTableCollection, - data_path: Option<&Path>, - inter_network_transfers: &[PywrMultiNetworkTransfer], - timeseries: &LoadedTimeseriesCollection, + args: &LoadArgs, ) -> Result { let ty = match self { - Self::Constant(p) => ParameterType::Parameter(p.add_to_model(network, tables)?), - Self::ControlCurveInterpolated(p) => ParameterType::Parameter(p.add_to_model( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - )?), - Self::Aggregated(p) => ParameterType::Parameter(p.add_to_model( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - )?), - Self::AggregatedIndex(p) => ParameterType::Index(p.add_to_model( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - )?), - Self::AsymmetricSwitchIndex(p) => ParameterType::Index(p.add_to_model( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - )?), - Self::ControlCurvePiecewiseInterpolated(p) => ParameterType::Parameter(p.add_to_model( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - )?), - Self::ControlCurveIndex(p) => ParameterType::Index(p.add_to_model( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - )?), - Self::ControlCurve(p) => ParameterType::Parameter(p.add_to_model( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - )?), - Self::DailyProfile(p) => ParameterType::Parameter(p.add_to_model(network, tables)?), - Self::IndexedArray(p) => ParameterType::Parameter(p.add_to_model( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - )?), - Self::MonthlyProfile(p) => ParameterType::Parameter(p.add_to_model(network, tables)?), - Self::WeeklyProfile(p) => ParameterType::Parameter(p.add_to_model(network, tables)?), - Self::UniformDrawdownProfile(p) => ParameterType::Parameter(p.add_to_model(network, tables)?), - Self::Max(p) => ParameterType::Parameter(p.add_to_model( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - )?), - Self::Min(p) => ParameterType::Parameter(p.add_to_model( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - )?), - Self::Negative(p) => ParameterType::Parameter(p.add_to_model( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - )?), + Self::Constant(p) => ParameterType::Parameter(p.add_to_model(network, args)?), + Self::ControlCurveInterpolated(p) => ParameterType::Parameter(p.add_to_model(network, args)?), + Self::Aggregated(p) => ParameterType::Parameter(p.add_to_model(network, args)?), + Self::AggregatedIndex(p) => ParameterType::Index(p.add_to_model(network, args)?), + Self::AsymmetricSwitchIndex(p) => ParameterType::Index(p.add_to_model(network, args)?), + Self::ControlCurvePiecewiseInterpolated(p) => ParameterType::Parameter(p.add_to_model(network, args)?), + Self::ControlCurveIndex(p) => ParameterType::Index(p.add_to_model(network, args)?), + Self::ControlCurve(p) => ParameterType::Parameter(p.add_to_model(network, args)?), + Self::DailyProfile(p) => ParameterType::Parameter(p.add_to_model(network, args)?), + Self::IndexedArray(p) => ParameterType::Parameter(p.add_to_model(network, args)?), + Self::MonthlyProfile(p) => ParameterType::Parameter(p.add_to_model(network, args)?), + Self::WeeklyProfile(p) => ParameterType::Parameter(p.add_to_model(network, args)?), + Self::UniformDrawdownProfile(p) => ParameterType::Parameter(p.add_to_model(network, args)?), + Self::Max(p) => ParameterType::Parameter(p.add_to_model(network, args)?), + Self::Min(p) => ParameterType::Parameter(p.add_to_model(network, args)?), + Self::Negative(p) => ParameterType::Parameter(p.add_to_model(network, args)?), Self::Polynomial1D(p) => ParameterType::Parameter(p.add_to_model(network)?), - Self::ParameterThreshold(p) => ParameterType::Index(p.add_to_model( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - )?), - Self::TablesArray(p) => ParameterType::Parameter(p.add_to_model(network, domain, data_path)?), - Self::Python(p) => p.add_to_model( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - )?, - Self::Delay(p) => ParameterType::Parameter(p.add_to_model( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - )?), - Self::Division(p) => ParameterType::Parameter(p.add_to_model( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - )?), - Self::Offset(p) => ParameterType::Parameter(p.add_to_model( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - )?), - Self::DiscountFactor(p) => ParameterType::Parameter(p.add_to_model( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - )?), - Self::Interpolated(p) => ParameterType::Parameter(p.add_to_model( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - )?), + Self::ParameterThreshold(p) => ParameterType::Index(p.add_to_model(network, args)?), + Self::TablesArray(p) => ParameterType::Parameter(p.add_to_model(network, args)?), + Self::Python(p) => p.add_to_model(network, args)?, + Self::Delay(p) => ParameterType::Parameter(p.add_to_model(network, args)?), + Self::Division(p) => ParameterType::Parameter(p.add_to_model(network, args)?), + Self::Offset(p) => ParameterType::Parameter(p.add_to_model(network, args)?), + Self::DiscountFactor(p) => ParameterType::Parameter(p.add_to_model(network, args)?), + Self::Interpolated(p) => ParameterType::Parameter(p.add_to_model(network, args)?), Self::RbfProfile(p) => ParameterType::Parameter(p.add_to_model(network)?), }; @@ -712,13 +562,10 @@ pub struct NodeReference { } impl NodeReference { - pub fn load( - &self, - network: &mut pywr_core::network::Network, - schema: &crate::model::PywrNetwork, - ) -> Result { + pub fn load(&self, network: &mut pywr_core::network::Network, args: &LoadArgs) -> Result { // This is the associated node in the schema - let node = schema + let node = args + .schema .get_node_by_name(&self.name) .ok_or_else(|| SchemaError::NodeNotFound(self.name.clone()))?; @@ -737,14 +584,9 @@ pub enum MetricFloatReference { impl MetricFloatReference { /// Load the metric definition into a `Metric` containing the appropriate internal references. - pub fn load( - &self, - network: &mut pywr_core::network::Network, - schema: &crate::model::PywrNetwork, - inter_network_transfers: &[PywrMultiNetworkTransfer], - ) -> Result { + pub fn load(&self, network: &mut pywr_core::network::Network, args: &LoadArgs) -> Result { match self { - Self::Node(node_ref) => node_ref.load(network, schema), + Self::Node(node_ref) => node_ref.load(network, args), Self::Parameter { name, key } => { match key { Some(key) => { @@ -762,7 +604,7 @@ impl MetricFloatReference { } Self::InterNetworkTransfer { name } => { // Find the matching inter model transfer - match inter_network_transfers.iter().position(|t| &t.name == name) { + match args.inter_network_transfers.iter().position(|t| &t.name == name) { Some(idx) => Ok(MetricF64::InterNetworkTransfer(MultiNetworkTransferIndex(idx))), None => Err(SchemaError::InterNetworkTransferNotFound(name.to_string())), } @@ -808,18 +650,9 @@ pub enum MetricFloatValue { impl MetricFloatValue { /// Load the metric definition into a `Metric` containing the appropriate internal references. - pub fn load( - &self, - network: &mut pywr_core::network::Network, - schema: &crate::model::PywrNetwork, - domain: &ModelDomain, - tables: &LoadedTableCollection, - data_path: Option<&Path>, - inter_network_transfers: &[PywrMultiNetworkTransfer], - timeseries: &LoadedTimeseriesCollection, - ) -> Result { + pub fn load(&self, network: &mut pywr_core::network::Network, args: &LoadArgs) -> Result { match self { - Self::Reference(reference) => Ok(reference.load(network, schema, inter_network_transfers)?), + Self::Reference(reference) => Ok(reference.load(network, args)?), Self::InlineParameter { definition } => { // This inline parameter could already have been loaded on a previous attempt // Let's see if exists first. @@ -835,7 +668,7 @@ impl MetricFloatValue { } Err(_) => { // An error retrieving a parameter with this name; assume it needs creating. - match definition.add_to_model(network, schema, domain, tables, data_path, inter_network_transfers, timeseries)? { + match definition.add_to_model(network, args)? { ParameterType::Parameter(idx) => Ok(MetricF64::ParameterValue(idx)), ParameterType::Index(_) => Err(SchemaError::UnexpectedParameterType(format!( "Found index parameter of type '{}' with name '{}' where an float parameter was expected.", @@ -854,10 +687,12 @@ impl MetricFloatValue { Self::Timeseries(ts_ref) => { let param_idx = match &ts_ref.columns { TimeseriesColumns::Scenario(scenario) => { - timeseries.load_df(network, ts_ref.name.as_ref(), domain, scenario.as_str())? + args.timeseries + .load_df(network, ts_ref.name.as_ref(), &args.domain, scenario.as_str())? } TimeseriesColumns::Column(col) => { - timeseries.load_column(network, ts_ref.name.as_ref(), col.as_str())? + args.timeseries + .load_column(network, ts_ref.name.as_ref(), col.as_str())? } }; Ok(MetricF64::ParameterValue(param_idx)) @@ -878,12 +713,7 @@ impl ParameterIndexValue { pub fn load( &self, network: &mut pywr_core::network::Network, - schema: &crate::model::PywrNetwork, - domain: &ModelDomain, - tables: &LoadedTableCollection, - data_path: Option<&Path>, - inter_network_transfers: &[PywrMultiNetworkTransfer], - timeseries: &LoadedTimeseriesCollection, + args: &LoadArgs, ) -> Result, SchemaError> { match self { Self::Reference(name) => { @@ -892,7 +722,7 @@ impl ParameterIndexValue { } Self::Inline(parameter) => { // Inline parameter needs to be added - match parameter.add_to_model(network, schema, domain, tables, data_path, inter_network_transfers, timeseries)? { + match parameter.add_to_model(network, args)? { ParameterType::Index(idx) => Ok(idx), ParameterType::Parameter(_) => Err(SchemaError::UnexpectedParameterType(format!( "Found float parameter of type '{}' with name '{}' where an index parameter was expected.", @@ -932,27 +762,10 @@ impl DynamicFloatValue { Self::Constant(ConstantValue::Literal(v)) } - pub fn load( - &self, - network: &mut pywr_core::network::Network, - schema: &crate::model::PywrNetwork, - domain: &ModelDomain, - tables: &LoadedTableCollection, - data_path: Option<&Path>, - inter_network_transfers: &[PywrMultiNetworkTransfer], - timeseries: &LoadedTimeseriesCollection, - ) -> Result { + pub fn load(&self, network: &mut pywr_core::network::Network, args: &LoadArgs) -> Result { let parameter_ref = match self { - DynamicFloatValue::Constant(v) => MetricF64::Constant(v.load(tables)?), - DynamicFloatValue::Dynamic(v) => v.load( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - )?, + DynamicFloatValue::Constant(v) => MetricF64::Constant(v.load(args.tables)?), + DynamicFloatValue::Dynamic(v) => v.load(network, args)?, }; Ok(parameter_ref) } @@ -1029,27 +842,10 @@ impl DynamicIndexValue { } /// - pub fn load( - &self, - network: &mut pywr_core::network::Network, - schema: &crate::model::PywrNetwork, - domain: &ModelDomain, - tables: &LoadedTableCollection, - data_path: Option<&Path>, - inter_network_transfers: &[PywrMultiNetworkTransfer], - timeseries: &LoadedTimeseriesCollection, - ) -> Result { + pub fn load(&self, network: &mut pywr_core::network::Network, args: &LoadArgs) -> Result { let parameter_ref = match self { - DynamicIndexValue::Constant(v) => MetricUsize::Constant(v.load(tables)?), - DynamicIndexValue::Dynamic(v) => MetricUsize::IndexParameterValue(v.load( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - )?), + DynamicIndexValue::Constant(v) => MetricUsize::Constant(v.load(args.tables)?), + DynamicIndexValue::Dynamic(v) => MetricUsize::IndexParameterValue(v.load(network, args)?), }; Ok(parameter_ref) } diff --git a/pywr-schema/src/parameters/offset.rs b/pywr-schema/src/parameters/offset.rs index 4e74dfee..c8b3d92c 100644 --- a/pywr-schema/src/parameters/offset.rs +++ b/pywr-schema/src/parameters/offset.rs @@ -1,13 +1,8 @@ -use crate::data_tables::LoadedTableCollection; +use crate::error::SchemaError; +use crate::model::LoadArgs; use crate::parameters::{ConstantValue, DynamicFloatValue, DynamicFloatValueType, ParameterMeta}; -use crate::timeseries::LoadedTimeseriesCollection; use pywr_core::parameters::ParameterIndex; - -use crate::error::SchemaError; -use crate::model::PywrMultiNetworkTransfer; -use pywr_core::models::ModelDomain; use std::collections::HashMap; -use std::path::Path; /// A parameter that returns a fixed delta from another metric. /// @@ -51,24 +46,11 @@ impl OffsetParameter { pub fn add_to_model( &self, network: &mut pywr_core::network::Network, - schema: &crate::model::PywrNetwork, - domain: &ModelDomain, - tables: &LoadedTableCollection, - data_path: Option<&Path>, - inter_network_transfers: &[PywrMultiNetworkTransfer], - timeseries: &LoadedTimeseriesCollection, + args: &LoadArgs, ) -> Result, SchemaError> { - let idx = self.metric.load( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - )?; + let idx = self.metric.load(network, args)?; - let p = pywr_core::parameters::OffsetParameter::new(&self.meta.name, idx, self.offset.load(tables)?); + let p = pywr_core::parameters::OffsetParameter::new(&self.meta.name, idx, self.offset.load(args.tables)?); Ok(network.add_parameter(Box::new(p))?) } } diff --git a/pywr-schema/src/parameters/profiles.rs b/pywr-schema/src/parameters/profiles.rs index 78bc1c89..e9c011b6 100644 --- a/pywr-schema/src/parameters/profiles.rs +++ b/pywr-schema/src/parameters/profiles.rs @@ -1,5 +1,5 @@ -use crate::data_tables::LoadedTableCollection; use crate::error::{ConversionError, SchemaError}; +use crate::model::LoadArgs; use crate::parameters::{ ConstantFloatVec, ConstantValue, DynamicFloatValueType, IntoV2Parameter, ParameterMeta, TryFromV1Parameter, }; @@ -30,9 +30,9 @@ impl DailyProfileParameter { pub fn add_to_model( &self, network: &mut pywr_core::network::Network, - tables: &LoadedTableCollection, + args: &LoadArgs, ) -> Result, SchemaError> { - let values = &self.values.load(tables)?[..366]; + let values = &self.values.load(args.tables)?[..366]; let p = pywr_core::parameters::DailyProfileParameter::new(&self.meta.name, values.try_into().expect("")); Ok(network.add_parameter(Box::new(p))?) } @@ -100,9 +100,9 @@ impl MonthlyProfileParameter { pub fn add_to_model( &self, network: &mut pywr_core::network::Network, - tables: &LoadedTableCollection, + args: &LoadArgs, ) -> Result, SchemaError> { - let values = &self.values.load(tables)?[..12]; + let values = &self.values.load(args.tables)?[..12]; let p = pywr_core::parameters::MonthlyProfileParameter::new( &self.meta.name, values.try_into().expect(""), @@ -174,18 +174,18 @@ impl UniformDrawdownProfileParameter { pub fn add_to_model( &self, network: &mut pywr_core::network::Network, - tables: &LoadedTableCollection, + args: &LoadArgs, ) -> Result, SchemaError> { let reset_day = match &self.reset_day { - Some(v) => v.load(tables)? as u32, + Some(v) => v.load(args.tables)? as u32, None => 1, }; let reset_month = match &self.reset_month { - Some(v) => v.load(tables)? as u32, + Some(v) => v.load(args.tables)? as u32, None => 1, }; let residual_days = match &self.residual_days { - Some(v) => v.load(tables)? as u8, + Some(v) => v.load(args.tables)? as u8, None => 0, }; @@ -544,11 +544,11 @@ impl WeeklyProfileParameter { pub fn add_to_model( &self, network: &mut pywr_core::network::Network, - tables: &LoadedTableCollection, + args: &LoadArgs, ) -> Result, SchemaError> { let p = pywr_core::parameters::WeeklyProfileParameter::new( &self.meta.name, - WeeklyProfileValues::try_from(self.values.load(tables)?.as_slice()).map_err( + WeeklyProfileValues::try_from(self.values.load(args.tables)?.as_slice()).map_err( |err: WeeklyProfileError| SchemaError::LoadParameter { name: self.meta.name.to_string(), error: err.to_string(), diff --git a/pywr-schema/src/parameters/python.rs b/pywr-schema/src/parameters/python.rs index e7597085..1ad5a4af 100644 --- a/pywr-schema/src/parameters/python.rs +++ b/pywr-schema/src/parameters/python.rs @@ -1,16 +1,14 @@ -use crate::data_tables::{make_path, LoadedTableCollection}; +use crate::data_tables::make_path; use crate::error::SchemaError; -use crate::model::PywrMultiNetworkTransfer; +use crate::model::LoadArgs; use crate::parameters::{DynamicFloatValue, DynamicFloatValueType, DynamicIndexValue, ParameterMeta}; -use crate::timeseries::LoadedTimeseriesCollection; use pyo3::prelude::PyModule; use pyo3::types::{PyDict, PyTuple}; use pyo3::{IntoPy, PyErr, PyObject, Python, ToPyObject}; -use pywr_core::models::ModelDomain; use pywr_core::parameters::{ParameterType, PyParameter}; use serde_json::Value; use std::collections::HashMap; -use std::path::{Path, PathBuf}; +use std::path::PathBuf; #[derive(serde::Deserialize, serde::Serialize, Debug, Clone)] #[serde(rename_all = "lowercase")] @@ -137,12 +135,7 @@ impl PythonParameter { pub fn add_to_model( &self, network: &mut pywr_core::network::Network, - schema: &crate::model::PywrNetwork, - domain: &ModelDomain, - tables: &LoadedTableCollection, - data_path: Option<&Path>, - inter_network_transfers: &[PywrMultiNetworkTransfer], - timeseries: &LoadedTimeseriesCollection, + args: &LoadArgs, ) -> Result { pyo3::prepare_freethreaded_python(); @@ -150,7 +143,7 @@ impl PythonParameter { let module = match &self.module { PythonModule::Module(module) => PyModule::import(py, module.as_str()), PythonModule::Path(original_path) => { - let path = &make_path(original_path, data_path); + let path = &make_path(original_path, args.data_path); let code = std::fs::read_to_string(path).expect("Could not read Python code from path."); let file_name = path.file_name().unwrap().to_str().unwrap(); let module_name = path.file_stem().unwrap().to_str().unwrap(); @@ -163,7 +156,7 @@ impl PythonParameter { }) .map_err(|e: PyErr| SchemaError::PythonError(e.to_string()))?; - let args = Python::with_gil(|py| { + let py_args = Python::with_gil(|py| { PyTuple::new(py, self.args.iter().map(|arg| try_json_value_into_py(py, arg).unwrap())).into_py(py) }); @@ -181,20 +174,7 @@ impl PythonParameter { let metrics = match &self.metrics { Some(metrics) => metrics .iter() - .map(|(k, v)| { - Ok(( - k.to_string(), - v.load( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - )?, - )) - }) + .map(|(k, v)| Ok((k.to_string(), v.load(network, args)?))) .collect::, SchemaError>>()?, None => HashMap::new(), }; @@ -202,25 +182,12 @@ impl PythonParameter { let indices = match &self.indices { Some(indices) => indices .iter() - .map(|(k, v)| { - Ok(( - k.to_string(), - v.load( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - )?, - )) - }) + .map(|(k, v)| Ok((k.to_string(), v.load(network, args)?))) .collect::, SchemaError>>()?, None => HashMap::new(), }; - let p = PyParameter::new(&self.meta.name, object, args, kwargs, &metrics, &indices); + let p = PyParameter::new(&self.meta.name, object, py_args, kwargs, &metrics, &indices); let pt = match self.return_type { PythonReturnType::Float => ParameterType::Parameter(network.add_parameter(Box::new(p))?), @@ -235,7 +202,7 @@ impl PythonParameter { #[cfg(test)] mod tests { use crate::data_tables::LoadedTableCollection; - use crate::model::PywrNetwork; + use crate::model::{LoadArgs, PywrNetwork}; use crate::parameters::python::PythonParameter; use crate::timeseries::LoadedTimeseriesCollection; use pywr_core::models::ModelDomain; @@ -272,9 +239,17 @@ mod tests { let mut network = Network::default(); let tables = LoadedTableCollection::from_schema(None, None).unwrap(); let ts = LoadedTimeseriesCollection::default(); - param - .add_to_model(&mut network, &schema, &domain, &tables, None, &[], &ts) - .unwrap(); + + let args = LoadArgs { + schema: &schema, + data_path: None, + tables: &tables, + timeseries: &ts, + domain: &domain, + inter_network_transfers: &[], + }; + + param.add_to_model(&mut network, &args).unwrap(); assert!(network.get_parameter_by_name("my-float-parameter").is_ok()); } @@ -308,9 +283,17 @@ mod tests { let mut network = Network::default(); let tables = LoadedTableCollection::from_schema(None, None).unwrap(); let ts = LoadedTimeseriesCollection::default(); - param - .add_to_model(&mut network, &schema, &domain, &tables, None, &[], &ts) - .unwrap(); + + let args = LoadArgs { + schema: &schema, + data_path: None, + tables: &tables, + timeseries: &ts, + domain: &domain, + inter_network_transfers: &[], + }; + + param.add_to_model(&mut network, &args).unwrap(); assert!(network.get_index_parameter_by_name("my-int-parameter").is_ok()); } diff --git a/pywr-schema/src/parameters/tables.rs b/pywr-schema/src/parameters/tables.rs index 5e9da5d8..c1ac4597 100644 --- a/pywr-schema/src/parameters/tables.rs +++ b/pywr-schema/src/parameters/tables.rs @@ -1,11 +1,11 @@ use crate::error::{ConversionError, SchemaError}; +use crate::model::LoadArgs; use crate::parameters::{DynamicFloatValueType, IntoV2Parameter, ParameterMeta, TryFromV1Parameter}; use ndarray::s; -use pywr_core::models::ModelDomain; use pywr_core::parameters::ParameterIndex; use pywr_v1_schema::parameters::TablesArrayParameter as TablesArrayParameterV1; use std::collections::HashMap; -use std::path::{Path, PathBuf}; +use std::path::PathBuf; #[derive(serde::Deserialize, serde::Serialize, Debug, Clone)] pub struct TablesArrayParameter { @@ -31,13 +31,12 @@ impl TablesArrayParameter { pub fn add_to_model( &self, network: &mut pywr_core::network::Network, - domain: &ModelDomain, - data_path: Option<&Path>, + args: &LoadArgs, ) -> Result, SchemaError> { // 1. Load the file from the HDF5 file (NB this is not Pandas format). // Handle the case of an optional data path with a relative url. - let pth = if let Some(dp) = data_path { + let pth = if let Some(dp) = args.data_path { if self.url.is_relative() { dp.join(&self.url) } else { @@ -62,7 +61,8 @@ impl TablesArrayParameter { // 3. Create an ArrayParameter using the loaded array. if let Some(scenario) = &self.scenario { - let scenario_group_index = domain + let scenario_group_index = args + .domain .scenarios() .group_index(scenario) .ok_or(SchemaError::ScenarioGroupNotFound(scenario.to_string()))?; diff --git a/pywr-schema/src/parameters/thresholds.rs b/pywr-schema/src/parameters/thresholds.rs index 509f33ef..7328c4fb 100644 --- a/pywr-schema/src/parameters/thresholds.rs +++ b/pywr-schema/src/parameters/thresholds.rs @@ -1,17 +1,13 @@ -use crate::data_tables::LoadedTableCollection; use crate::error::{ConversionError, SchemaError}; -use crate::model::PywrMultiNetworkTransfer; +use crate::model::LoadArgs; use crate::parameters::{ DynamicFloatValue, DynamicFloatValueType, IntoV2Parameter, ParameterMeta, TryFromV1Parameter, TryIntoV2Parameter, }; -use crate::timeseries::LoadedTimeseriesCollection; -use pywr_core::models::ModelDomain; use pywr_core::parameters::ParameterIndex; use pywr_v1_schema::parameters::{ ParameterThresholdParameter as ParameterThresholdParameterV1, Predicate as PredicateV1, }; use std::collections::HashMap; -use std::path::Path; #[derive(serde::Deserialize, serde::Serialize, Debug, Clone, Copy)] pub enum Predicate { @@ -73,31 +69,10 @@ impl ParameterThresholdParameter { pub fn add_to_model( &self, network: &mut pywr_core::network::Network, - schema: &crate::model::PywrNetwork, - domain: &ModelDomain, - tables: &LoadedTableCollection, - data_path: Option<&Path>, - inter_network_transfers: &[PywrMultiNetworkTransfer], - timeseries: &LoadedTimeseriesCollection, + args: &LoadArgs, ) -> Result, SchemaError> { - let metric = self.parameter.load( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - )?; - let threshold = self.threshold.load( - network, - schema, - domain, - tables, - data_path, - inter_network_transfers, - timeseries, - )?; + let metric = self.parameter.load(network, args)?; + let threshold = self.threshold.load(network, args)?; let p = pywr_core::parameters::ThresholdParameter::new( &self.meta.name,