Skip to content

Commit

Permalink
fix: Schema refs to v1 params that are converted to timeseries inputs…
Browse files Browse the repository at this point in the history
… are now updated to timeseries refs (#191)
  • Loading branch information
Batch21 authored Jun 10, 2024
1 parent b1fb691 commit 1b07603
Show file tree
Hide file tree
Showing 5 changed files with 231 additions and 7 deletions.
6 changes: 3 additions & 3 deletions pywr-schema/src/metric.rs
Original file line number Diff line number Diff line change
Expand Up @@ -297,11 +297,11 @@ impl NodeReference {
}
}

impl From<String> for NodeReference{
fn from (v: String) -> Self {
impl From<String> for NodeReference {
fn from(v: String) -> Self {
NodeReference {
name: v,
attribute: None
attribute: None,
}
}
}
Expand Down
55 changes: 52 additions & 3 deletions pywr-schema/src/model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use crate::data_tables::DataTable;
#[cfg(feature = "core")]
use crate::data_tables::LoadedTableCollection;
use crate::error::{ConversionError, SchemaError};
use crate::metric::Metric;
use crate::metric::{Metric, TimeseriesColumns, TimeseriesReference};
use crate::metric_sets::MetricSet;
use crate::nodes::NodeAndTimeseries;
use crate::outputs::Output;
Expand Down Expand Up @@ -283,14 +283,14 @@ impl PywrNetwork {
.flatten()
.collect::<Vec<_>>();

let nodes = nodes_and_ts.into_iter().map(|n| n.node).collect::<Vec<_>>();
let mut nodes = nodes_and_ts.into_iter().map(|n| n.node).collect::<Vec<_>>();

let edges = match v1.edges {
Some(edges) => edges.into_iter().map(|e| e.into()).collect(),
None => Vec::new(),
};

let parameters = if let Some(v1_parameters) = v1.parameters {
let mut parameters = if let Some(v1_parameters) = v1.parameters {
let mut unnamed_count: usize = 0;
let (parameters, param_ts_data) =
convert_parameter_v1_to_v2(v1_parameters, &mut unnamed_count, &mut errors);
Expand All @@ -300,6 +300,34 @@ impl PywrNetwork {
None
};

// closure to update a parameter ref with a timeseries ref when names match.
let update_to_ts_ref = &mut |m: &mut Metric| {
if let Metric::Parameter(p) = m {
let ts_ref = ts_data.iter().find(|ts| ts.name == Some(p.name.clone()));
if let Some(ts_ref) = ts_ref {
// The timeseries requires a name to be used as a reference
let name = match &ts_ref.name {
Some(n) => n.clone(),
None => return,
};

let cols = match (&ts_ref.column, &ts_ref.scenario) {
(Some(col), None) => TimeseriesColumns::Column(col.clone()),
(None, Some(scenario)) => TimeseriesColumns::Scenario(scenario.clone()),
(Some(_), Some(_)) => return,
(None, None) => return,
};

*m = Metric::Timeseries(TimeseriesReference::new(name, cols));
}
}
};

nodes.visit_metrics_mut(update_to_ts_ref);
if let Some(p) = parameters.as_mut() {
p.visit_metrics_mut(update_to_ts_ref)
}

let timeseries = if !ts_data.is_empty() {
let ts = convert_from_v1_data(ts_data, &v1.tables, &mut errors);
Some(ts)
Expand Down Expand Up @@ -977,6 +1005,26 @@ mod tests {
panic!("Expected an error due to missing file: {str}");
}
}

#[test]
fn test_v1_conversion() {
let v1_str = include_str!("./test_models/v1/timeseries.json");
let v1: pywr_v1_schema::PywrModel = serde_json::from_str(v1_str).unwrap();

let (v2, errors) = PywrModel::from_v1(v1);

assert_eq!(errors.len(), 0);

std::fs::write("tmp.json", serde_json::to_string_pretty(&v2).unwrap()).unwrap();

let v2_converted: serde_json::Value =
serde_json::from_str(&serde_json::to_string_pretty(&v2).unwrap()).unwrap();

let v2_expected: serde_json::Value =
serde_json::from_str(include_str!("./test_models/v1/timeseries-converted.json")).unwrap();

assert_eq!(v2_converted, v2_expected);
}
}

#[cfg(test)]
Expand All @@ -988,6 +1036,7 @@ mod core_tests {
use ndarray::{Array1, Array2, Axis};
use pywr_core::{metric::MetricF64, recorders::AssertionRecorder, solvers::ClpSolver, test_utils::run_all_solvers};
use std::path::PathBuf;
use std::str::FromStr;

fn model_str() -> &'static str {
include_str!("./test_models/simple1.json")
Expand Down
2 changes: 1 addition & 1 deletion pywr-schema/src/nodes/virtual_storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ use crate::error::ConversionError;
#[cfg(feature = "core")]
use crate::error::SchemaError;
use crate::metric::Metric;
use crate::metric::NodeReference;
#[cfg(feature = "core")]
use crate::model::LoadArgs;
use crate::nodes::core::StorageInitialVolume;
use crate::nodes::{NodeAttribute, NodeMeta};
use crate::metric::NodeReference;
use crate::parameters::TryIntoV2Parameter;
#[cfg(feature = "core")]
use pywr_core::{
Expand Down
116 changes: 116 additions & 0 deletions pywr-schema/src/test_models/v1/timeseries-converted.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
{
"metadata": {
"title": "Simple timeseries",
"description": null,
"minimum_version": null
},
"timestepper": {
"start": "2021-01-01",
"end": "2021-01-31",
"timestep": 1
},
"scenarios": null,
"network": {
"nodes": [
{
"type": "Input",
"name": "input1",
"max_flow": {
"type": "Timeseries",
"name": "inflow",
"columns": {
"type": "Column",
"name": "inflow1"
}
},
"min_flow": null,
"cost": null
},
{
"type": "Input",
"name": "input2",
"max_flow": {
"type": "Parameter",
"name": "factored_flow",
"key": null
},
"min_flow": null,
"cost": null
},
{
"type": "Link",
"name": "link1",
"max_flow": null,
"min_flow": null,
"cost": null
},
{
"type": "Output",
"name": "output1",
"max_flow": {
"type": "Parameter",
"name": "demand",
"key": null
},
"min_flow": null,
"cost": {
"type": "Constant",
"value": -10.0
}
}
],
"edges": [
{
"from_node": "input1",
"to_node": "link1"
},
{
"from_node": "input2",
"to_node": "link1"
},
{
"from_node": "link1",
"to_node": "output1"
}
],
"parameters": [
{
"type": "Constant",
"name": "demand",
"value": 100.0
},
{
"type": "Aggregated",
"name": "factored_flow",
"agg_func": "product",
"metrics": [
{
"type": "Timeseries",
"name": "inflow",
"columns": {
"type": "Column",
"name": "inflow1"
}
},
{
"type": "Constant",
"value": 0.5
}
]
}
],
"tables": null,
"timeseries": [
{
"name": "inflow",
"provider": {
"type": "Polars",
"time_col": null,
"url": "inflow.csv"
}
}
],
"metric_sets": null,
"outputs": null
}
}
59 changes: 59 additions & 0 deletions pywr-schema/src/test_models/v1/timeseries.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
{
"metadata": {
"title": "Simple timeseries"
},
"timestepper": {
"start": "2021-01-01",
"end": "2021-01-31",
"timestep": 1
},
"nodes": [
{
"name": "input1",
"type": "Input",
"max_flow": "inflow"
},
{
"name": "input2",
"type": "Input",
"max_flow": "factored_flow"
},
{
"name": "link1",
"type": "Link"
},
{
"name": "output1",
"type": "Output",
"max_flow": "demand",
"cost": -10
}
],
"edges": [
["input1", "link1"],
["input2", "link1"],
["link1", "output1"]
],
"parameters": {
"demand": {
"type": "constant",
"value": 100.0
},
"inflow": {
"type": "dataframe",
"url" : "inflow.csv",
"parse_dates": true,
"dayfirst": true,
"index_col": 0,
"column": "inflow1"
},
"factored_flow": {
"type": "aggregated",
"agg_func":"product",
"parameters": [
"inflow",
0.5
]
}
}
}

0 comments on commit 1b07603

Please sign in to comment.