diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..1e997ed6 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,4 @@ +* text=auto +*.csv text eol=lf +*.sh text eol=lf +*.bat text eol=crlf diff --git a/pywr-core/Cargo.toml b/pywr-core/Cargo.toml index 0fdc225f..e2d515af 100644 --- a/pywr-core/Cargo.toml +++ b/pywr-core/Cargo.toml @@ -45,6 +45,7 @@ rand = "0.8.5" rand_distr = "0.4.3" rand_chacha = "0.3.1" dyn-clone = "1.0.16" +serde = { version = "1.0.197", features = ["derive"] } [dev-dependencies] criterion = "0.5" diff --git a/pywr-core/src/network.rs b/pywr-core/src/network.rs index d32e24e2..6cd77fd8 100644 --- a/pywr-core/src/network.rs +++ b/pywr-core/src/network.rs @@ -377,7 +377,7 @@ impl Network { // Setup recorders for (recorder, internal_state) in self.recorders.iter().zip(recorder_internal_states) { - recorder.finalise(metric_set_states, internal_state)?; + recorder.finalise(self, metric_set_states, internal_state)?; } Ok(()) diff --git a/pywr-core/src/recorders/aggregator/mod.rs b/pywr-core/src/recorders/aggregator/mod.rs index 7f9b9bba..97eda1a8 100644 --- a/pywr-core/src/recorders/aggregator/mod.rs +++ b/pywr-core/src/recorders/aggregator/mod.rs @@ -265,6 +265,11 @@ impl PeriodValue { pub fn new(start: NaiveDateTime, duration: PywrDuration, value: T) -> Self { Self { start, duration, value } } + + /// The end of the period. + pub fn end(&self) -> NaiveDateTime { + self.duration + self.start + } } impl PeriodValue> { diff --git a/pywr-core/src/recorders/csv.rs b/pywr-core/src/recorders/csv.rs index bdd357e2..c39500b6 100644 --- a/pywr-core/src/recorders/csv.rs +++ b/pywr-core/src/recorders/csv.rs @@ -4,6 +4,8 @@ use crate::network::Network; use crate::recorders::metric_set::MetricSetIndex; use crate::scenario::ScenarioIndex; use crate::state::State; +use chrono::NaiveDateTime; +use serde::{Deserialize, Serialize}; use std::any::Any; use std::fs::File; use std::ops::Deref; @@ -11,7 +13,7 @@ use std::path::PathBuf; /// Output the values from a [`MetricSet`] to a CSV file. #[derive(Clone, Debug)] -pub struct CSVRecorder { +pub struct CsvWideFmtOutput { meta: RecorderMeta, filename: PathBuf, metric_set_idx: MetricSetIndex, @@ -21,7 +23,7 @@ struct Internal { writer: csv::Writer, } -impl CSVRecorder { +impl CsvWideFmtOutput { pub fn new>(name: &str, filename: P, metric_set_idx: MetricSetIndex) -> Self { Self { meta: RecorderMeta::new(name), @@ -29,9 +31,48 @@ impl CSVRecorder { metric_set_idx, } } + + fn write_values( + &self, + metric_set_states: &[Vec], + internal: &mut Internal, + ) -> Result<(), PywrError> { + let mut row = Vec::new(); + + // Iterate through all scenario's state + for ms_scenario_states in metric_set_states.iter() { + let metric_set_state = ms_scenario_states + .get(*self.metric_set_idx.deref()) + .ok_or(PywrError::MetricSetIndexNotFound(self.metric_set_idx))?; + + if let Some(current_values) = metric_set_state.current_values() { + let values = current_values + .iter() + .map(|v| format!("{:.2}", v.value)) + .collect::>(); + + // If the row is empty, add the start time + if row.is_empty() { + row.push(current_values.first().unwrap().start.to_string()) + } + + row.extend(values); + } + } + + // Only write + if row.len() > 1 { + internal + .writer + .write_record(row) + .map_err(|e| PywrError::CSVError(e.to_string()))?; + } + + Ok(()) + } } -impl Recorder for CSVRecorder { +impl Recorder for CsvWideFmtOutput { fn meta(&self) -> &RecorderMeta { &self.meta } @@ -110,7 +151,7 @@ impl Recorder for CSVRecorder { fn save( &self, - timestep: &Timestep, + _timestep: &Timestep, _scenario_indices: &[ScenarioIndex], _network: &Network, _state: &[State], @@ -125,44 +166,159 @@ impl Recorder for CSVRecorder { None => panic!("No internal state defined when one was expected! :("), }; - let mut row = vec![timestep.date.to_string()]; - - // Iterate through all of the scenario's state - for ms_scenario_states in metric_set_states.iter() { - let metric_set_state = ms_scenario_states - .get(*self.metric_set_idx.deref()) - .ok_or(PywrError::MetricSetIndexNotFound(self.metric_set_idx))?; + self.write_values(metric_set_states, internal)?; - if let Some(current_values) = metric_set_state.current_values() { - let values = current_values - .iter() - .map(|v| format!("{:.2}", v.value)) - .collect::>(); + Ok(()) + } - row.extend(values); + fn finalise( + &self, + _network: &Network, + metric_set_states: &[Vec], + internal_state: &mut Option>, + ) -> Result<(), PywrError> { + // This will leave the internal state with a `None` because we need to take + // ownership of the file handle in order to close it. + match internal_state.take() { + Some(mut internal) => { + if let Some(internal) = internal.downcast_mut::() { + self.write_values(metric_set_states, internal)?; + Ok(()) + } else { + panic!("Internal state did not downcast to the correct type! :("); + } } + None => panic!("No internal state defined when one was expected! :("), + } + } +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct CsvLongFmtRecord { + time_start: NaiveDateTime, + time_end: NaiveDateTime, + scenario_index: usize, + metric_set: String, + name: String, + sub_name: String, + attribute: String, + value: f64, +} + +/// Output the values from a several [`MetricSet`]s to a CSV file in long format. +/// +/// The long format contains a row for each value produced by the metric set. This is useful +/// for analysis in tools like R or Python which can easily read long format data. +/// +#[derive(Clone, Debug)] +pub struct CsvLongFmtOutput { + meta: RecorderMeta, + filename: PathBuf, + metric_set_indices: Vec, +} + +impl CsvLongFmtOutput { + pub fn new>(name: &str, filename: P, metric_set_indices: &[MetricSetIndex]) -> Self { + Self { + meta: RecorderMeta::new(name), + filename: filename.into(), + metric_set_indices: metric_set_indices.to_vec(), } + } - // Only write - if row.len() > 1 { - internal - .writer - .write_record(row) - .map_err(|e| PywrError::CSVError(e.to_string()))?; + fn write_values( + &self, + network: &Network, + metric_set_states: &[Vec], + internal: &mut Internal, + ) -> Result<(), PywrError> { + // Iterate through all the scenario's state + for (scenario_idx, ms_scenario_states) in metric_set_states.iter().enumerate() { + for metric_set_idx in self.metric_set_indices.iter() { + let metric_set_state = ms_scenario_states + .get(*metric_set_idx.deref()) + .ok_or(PywrError::MetricSetIndexNotFound(*metric_set_idx))?; + + if let Some(current_values) = metric_set_state.current_values() { + let metric_set = network.get_metric_set(*metric_set_idx)?; + + for (metric, value) in metric_set.iter_metrics().zip(current_values.iter()) { + let name = metric.name(network)?.to_string(); + let sub_name = metric + .sub_name(network)? + .map_or_else(|| "".to_string(), |s| s.to_string()); + let attribute = metric.attribute().to_string(); + + let record = CsvLongFmtRecord { + time_start: value.start, + time_end: value.end(), + scenario_index: scenario_idx, + metric_set: metric_set.name().to_string(), + name, + sub_name, + attribute, + value: value.value, + }; + + internal + .writer + .serialize(record) + .map_err(|e| PywrError::CSVError(e.to_string()))?; + } + } + } } + + Ok(()) + } +} + +impl Recorder for CsvLongFmtOutput { + fn meta(&self) -> &RecorderMeta { + &self.meta + } + fn setup(&self, _domain: &ModelDomain, _network: &Network) -> Result>, PywrError> { + let writer = csv::Writer::from_path(&self.filename).map_err(|e| PywrError::CSVError(e.to_string()))?; + + let internal = Internal { writer }; + + Ok(Some(Box::new(internal))) + } + + fn save( + &self, + _timestep: &Timestep, + _scenario_indices: &[ScenarioIndex], + network: &Network, + _state: &[State], + metric_set_states: &[Vec], + internal_state: &mut Option>, + ) -> Result<(), PywrError> { + let internal = match internal_state { + Some(internal) => match internal.downcast_mut::() { + Some(pa) => pa, + None => panic!("Internal state did not downcast to the correct type! :("), + }, + None => panic!("No internal state defined when one was expected! :("), + }; + + self.write_values(network, metric_set_states, internal)?; + Ok(()) } fn finalise( &self, - _metric_set_states: &[Vec], + network: &Network, + metric_set_states: &[Vec], internal_state: &mut Option>, ) -> Result<(), PywrError> { // This will leave the internal state with a `None` because we need to take // ownership of the file handle in order to close it. match internal_state.take() { - Some(internal) => { - if let Ok(_internal) = internal.downcast::() { + Some(mut internal) => { + if let Some(internal) = internal.downcast_mut::() { + self.write_values(network, metric_set_states, internal)?; Ok(()) } else { panic!("Internal state did not downcast to the correct type! :("); diff --git a/pywr-core/src/recorders/hdf.rs b/pywr-core/src/recorders/hdf.rs index ccb6dbe3..79ae9535 100644 --- a/pywr-core/src/recorders/hdf.rs +++ b/pywr-core/src/recorders/hdf.rs @@ -134,6 +134,7 @@ impl Recorder for HDF5Recorder { fn finalise( &self, + _network: &Network, _metric_set_states: &[Vec], internal_state: &mut Option>, ) -> Result<(), PywrError> { diff --git a/pywr-core/src/recorders/memory.rs b/pywr-core/src/recorders/memory.rs index da66c695..697e1ee3 100644 --- a/pywr-core/src/recorders/memory.rs +++ b/pywr-core/src/recorders/memory.rs @@ -260,6 +260,7 @@ impl Recorder for MemoryRecorder { fn finalise( &self, + _network: &Network, metric_set_states: &[Vec], internal_state: &mut Option>, ) -> Result<(), PywrError> { diff --git a/pywr-core/src/recorders/mod.rs b/pywr-core/src/recorders/mod.rs index 76977225..35920324 100644 --- a/pywr-core/src/recorders/mod.rs +++ b/pywr-core/src/recorders/mod.rs @@ -5,7 +5,6 @@ mod memory; mod metric_set; mod py; -pub use self::csv::CSVRecorder; use crate::metric::{MetricF64, MetricUsize}; use crate::models::ModelDomain; use crate::network::Network; @@ -14,6 +13,7 @@ use crate::state::State; use crate::timestep::Timestep; use crate::PywrError; pub use aggregator::{AggregationFrequency, AggregationFunction, Aggregator}; +pub use csv::{CsvLongFmtOutput, CsvLongFmtRecord, CsvWideFmtOutput}; use float_cmp::{approx_eq, ApproxEq, F64Margin}; pub use hdf::HDF5Recorder; pub use memory::{Aggregation, AggregationError, MemoryRecorder}; @@ -87,6 +87,7 @@ pub trait Recorder: Send + Sync { } fn finalise( &self, + _network: &Network, _metric_set_states: &[Vec], _internal_state: &mut Option>, ) -> Result<(), PywrError> { diff --git a/pywr-schema/src/error.rs b/pywr-schema/src/error.rs index a458878d..916578f9 100644 --- a/pywr-schema/src/error.rs +++ b/pywr-schema/src/error.rs @@ -39,8 +39,8 @@ pub enum SchemaError { PythonError(String), #[error("hdf5 error: {0}")] HDF5Error(String), - #[error("csv error: {0}")] - CSVError(String), + #[error("Missing metric set: {0}")] + MissingMetricSet(String), #[error("unexpected parameter type: {0}")] UnexpectedParameterType(String), #[error("mismatch in the length of data provided. expected: {expected}, found: {found}")] diff --git a/pywr-schema/src/outputs/csv.rs b/pywr-schema/src/outputs/csv.rs index 221f1d40..675976df 100644 --- a/pywr-schema/src/outputs/csv.rs +++ b/pywr-schema/src/outputs/csv.rs @@ -1,12 +1,39 @@ use crate::error::SchemaError; -use pywr_core::recorders::CSVRecorder; +use pywr_core::recorders::{CsvLongFmtOutput, CsvWideFmtOutput, Recorder}; use std::path::{Path, PathBuf}; +#[derive(serde::Deserialize, serde::Serialize, Debug, Clone, Default)] +#[serde(rename_all = "lowercase")] +pub enum CsvFormat { + Wide, + #[default] + Long, +} + +#[derive(serde::Deserialize, serde::Serialize, Debug, Clone)] +#[serde(untagged)] +pub enum CsvMetricSet { + Single(String), + Multiple(Vec), +} + +/// Output data to a CSV file. +/// +/// This output will write the output data to a CSV file. The output data is written in either +/// wide or long format. The wide format will write each metric to a separate column, while the +/// long format will write each metric to a separate row. The wide format is useful for small +/// numbers of metrics or scenarios, while the long format is useful for large numbers of metrics +/// or scenarios. For more details see the [`CsvLongFmtOutput`] and [`CsvWideFmtOutput`] types. +/// +/// The long format supports either a single metric set or a list of metric sets. However, +/// the wide format only supports a single metric set. +/// #[derive(serde::Deserialize, serde::Serialize, Debug, Clone)] pub struct CsvOutput { - name: String, - filename: PathBuf, - metric_set: String, + pub name: String, + pub filename: PathBuf, + pub format: CsvFormat, + pub metric_set: CsvMetricSet, } impl CsvOutput { @@ -20,10 +47,32 @@ impl CsvOutput { _ => self.filename.to_path_buf(), }; - let metric_set_idx = network.get_metric_set_index_by_name(&self.metric_set)?; - let recorder = CSVRecorder::new(&self.name, filename, metric_set_idx); + let recorder: Box = match self.format { + CsvFormat::Wide => match &self.metric_set { + CsvMetricSet::Single(metric_set) => { + let metric_set_idx = network.get_metric_set_index_by_name(metric_set)?; + Box::new(CsvWideFmtOutput::new(&self.name, filename, metric_set_idx)) + } + CsvMetricSet::Multiple(_) => { + return Err(SchemaError::MissingMetricSet( + "Wide format CSV output requires a single `metric_set`".to_string(), + )) + } + }, + CsvFormat::Long => { + let metric_set_indices = match &self.metric_set { + CsvMetricSet::Single(metric_set) => vec![network.get_metric_set_index_by_name(metric_set)?], + CsvMetricSet::Multiple(metric_sets) => metric_sets + .iter() + .map(|ms| network.get_metric_set_index_by_name(ms)) + .collect::, _>>()?, + }; + + Box::new(CsvLongFmtOutput::new(&self.name, filename, &metric_set_indices)) + } + }; - network.add_recorder(Box::new(recorder))?; + network.add_recorder(recorder)?; Ok(()) } @@ -40,10 +89,34 @@ mod tests { include_str!("../test_models/csv1.json") } + fn csv1_outputs_long_str() -> &'static str { + include_str!("../test_models/csv1-outputs-long.csv") + } + + fn csv1_outputs_wide_str() -> &'static str { + include_str!("../test_models/csv1-outputs-wide.csv") + } + fn csv2_str() -> &'static str { include_str!("../test_models/csv2.json") } + fn csv2_outputs_long_str() -> &'static str { + include_str!("../test_models/csv2-outputs-long.csv") + } + + fn csv2_outputs_wide_str() -> &'static str { + include_str!("../test_models/csv2-outputs-wide.csv") + } + + fn csv3_str() -> &'static str { + include_str!("../test_models/csv3.json") + } + + fn csv3_outputs_long_str() -> &'static str { + include_str!("../test_models/csv3-outputs-long.csv") + } + #[test] fn test_schema() { let data = csv1_str(); @@ -51,7 +124,7 @@ mod tests { assert_eq!(schema.network.nodes.len(), 3); assert_eq!(schema.network.edges.len(), 2); - assert!(schema.network.outputs.is_some_and(|o| o.len() == 1)); + assert!(schema.network.outputs.is_some_and(|o| o.len() == 2)); } #[test] @@ -65,9 +138,16 @@ mod tests { model.run::(&ClpSolverSettings::default()).unwrap(); - // After model run there should be an output file. - let expected_path = temp_dir.path().join("outputs.csv"); - assert!(expected_path.exists()); + // After model run there should be two output files. + let expected_long_path = temp_dir.path().join("outputs-long.csv"); + assert!(expected_long_path.exists()); + let long_content = std::fs::read_to_string(&expected_long_path).unwrap(); + assert_eq!(&long_content, csv1_outputs_long_str()); + + let expected_wide_path = temp_dir.path().join("outputs-wide.csv"); + assert!(expected_wide_path.exists()); + let wide_content = std::fs::read_to_string(&expected_wide_path).unwrap(); + assert_eq!(&wide_content, csv1_outputs_wide_str()); } #[test] @@ -81,8 +161,32 @@ mod tests { model.run::(&ClpSolverSettings::default()).unwrap(); - // After model run there should be an output file. - let expected_path = temp_dir.path().join("outputs.csv"); - assert!(expected_path.exists()); + // After model run there should be two output files. + let expected_long_path = temp_dir.path().join("outputs-long.csv"); + assert!(expected_long_path.exists()); + let long_content = std::fs::read_to_string(&expected_long_path).unwrap(); + assert_eq!(&long_content, csv2_outputs_long_str()); + + let expected_wide_path = temp_dir.path().join("outputs-wide.csv"); + assert!(expected_wide_path.exists()); + let wide_content = std::fs::read_to_string(&expected_wide_path).unwrap(); + assert_eq!(&wide_content, csv2_outputs_wide_str()); + } + + #[test] + fn test_csv3_run() { + let data = csv3_str(); + let schema = PywrModel::from_str(data).unwrap(); + + let temp_dir = TempDir::new().unwrap(); + + let model = schema.build_model(None, Some(temp_dir.path())).unwrap(); + + model.run::(&ClpSolverSettings::default()).unwrap(); + + let expected_long_path = temp_dir.path().join("outputs-long.csv"); + assert!(expected_long_path.exists()); + let long_content = std::fs::read_to_string(&expected_long_path).unwrap(); + assert_eq!(&long_content, csv3_outputs_long_str()); } } diff --git a/pywr-schema/src/test_models/csv1-outputs-long.csv b/pywr-schema/src/test_models/csv1-outputs-long.csv new file mode 100644 index 00000000..53fbccf5 --- /dev/null +++ b/pywr-schema/src/test_models/csv1-outputs-long.csv @@ -0,0 +1,366 @@ +time_start,time_end,scenario_index,metric_set,name,sub_name,attribute,value +2015-01-01T00:00:00,2015-01-02T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-01-02T00:00:00,2015-01-03T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-01-03T00:00:00,2015-01-04T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-01-04T00:00:00,2015-01-05T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-01-05T00:00:00,2015-01-06T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-01-06T00:00:00,2015-01-07T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-01-07T00:00:00,2015-01-08T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-01-08T00:00:00,2015-01-09T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-01-09T00:00:00,2015-01-10T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-01-10T00:00:00,2015-01-11T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-01-11T00:00:00,2015-01-12T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-01-12T00:00:00,2015-01-13T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-01-13T00:00:00,2015-01-14T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-01-14T00:00:00,2015-01-15T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-01-15T00:00:00,2015-01-16T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-01-16T00:00:00,2015-01-17T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-01-17T00:00:00,2015-01-18T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-01-18T00:00:00,2015-01-19T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-01-19T00:00:00,2015-01-20T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-01-20T00:00:00,2015-01-21T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-01-21T00:00:00,2015-01-22T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-01-22T00:00:00,2015-01-23T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-01-23T00:00:00,2015-01-24T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-01-24T00:00:00,2015-01-25T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-01-25T00:00:00,2015-01-26T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-01-26T00:00:00,2015-01-27T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-01-27T00:00:00,2015-01-28T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-01-28T00:00:00,2015-01-29T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-01-29T00:00:00,2015-01-30T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-01-30T00:00:00,2015-01-31T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-01-31T00:00:00,2015-02-01T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-02-01T00:00:00,2015-02-02T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-02-02T00:00:00,2015-02-03T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-02-03T00:00:00,2015-02-04T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-02-04T00:00:00,2015-02-05T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-02-05T00:00:00,2015-02-06T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-02-06T00:00:00,2015-02-07T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-02-07T00:00:00,2015-02-08T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-02-08T00:00:00,2015-02-09T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-02-09T00:00:00,2015-02-10T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-02-10T00:00:00,2015-02-11T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-02-11T00:00:00,2015-02-12T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-02-12T00:00:00,2015-02-13T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-02-13T00:00:00,2015-02-14T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-02-14T00:00:00,2015-02-15T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-02-15T00:00:00,2015-02-16T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-02-16T00:00:00,2015-02-17T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-02-17T00:00:00,2015-02-18T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-02-18T00:00:00,2015-02-19T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-02-19T00:00:00,2015-02-20T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-02-20T00:00:00,2015-02-21T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-02-21T00:00:00,2015-02-22T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-02-22T00:00:00,2015-02-23T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-02-23T00:00:00,2015-02-24T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-02-24T00:00:00,2015-02-25T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-02-25T00:00:00,2015-02-26T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-02-26T00:00:00,2015-02-27T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-02-27T00:00:00,2015-02-28T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-02-28T00:00:00,2015-03-01T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-03-01T00:00:00,2015-03-02T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-03-02T00:00:00,2015-03-03T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-03-03T00:00:00,2015-03-04T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-03-04T00:00:00,2015-03-05T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-03-05T00:00:00,2015-03-06T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-03-06T00:00:00,2015-03-07T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-03-07T00:00:00,2015-03-08T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-03-08T00:00:00,2015-03-09T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-03-09T00:00:00,2015-03-10T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-03-10T00:00:00,2015-03-11T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-03-11T00:00:00,2015-03-12T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-03-12T00:00:00,2015-03-13T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-03-13T00:00:00,2015-03-14T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-03-14T00:00:00,2015-03-15T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-03-15T00:00:00,2015-03-16T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-03-16T00:00:00,2015-03-17T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-03-17T00:00:00,2015-03-18T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-03-18T00:00:00,2015-03-19T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-03-19T00:00:00,2015-03-20T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-03-20T00:00:00,2015-03-21T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-03-21T00:00:00,2015-03-22T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-03-22T00:00:00,2015-03-23T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-03-23T00:00:00,2015-03-24T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-03-24T00:00:00,2015-03-25T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-03-25T00:00:00,2015-03-26T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-03-26T00:00:00,2015-03-27T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-03-27T00:00:00,2015-03-28T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-03-28T00:00:00,2015-03-29T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-03-29T00:00:00,2015-03-30T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-03-30T00:00:00,2015-03-31T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-03-31T00:00:00,2015-04-01T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-04-01T00:00:00,2015-04-02T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-04-02T00:00:00,2015-04-03T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-04-03T00:00:00,2015-04-04T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-04-04T00:00:00,2015-04-05T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-04-05T00:00:00,2015-04-06T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-04-06T00:00:00,2015-04-07T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-04-07T00:00:00,2015-04-08T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-04-08T00:00:00,2015-04-09T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-04-09T00:00:00,2015-04-10T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-04-10T00:00:00,2015-04-11T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-04-11T00:00:00,2015-04-12T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-04-12T00:00:00,2015-04-13T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-04-13T00:00:00,2015-04-14T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-04-14T00:00:00,2015-04-15T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-04-15T00:00:00,2015-04-16T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-04-16T00:00:00,2015-04-17T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-04-17T00:00:00,2015-04-18T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-04-18T00:00:00,2015-04-19T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-04-19T00:00:00,2015-04-20T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-04-20T00:00:00,2015-04-21T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-04-21T00:00:00,2015-04-22T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-04-22T00:00:00,2015-04-23T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-04-23T00:00:00,2015-04-24T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-04-24T00:00:00,2015-04-25T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-04-25T00:00:00,2015-04-26T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-04-26T00:00:00,2015-04-27T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-04-27T00:00:00,2015-04-28T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-04-28T00:00:00,2015-04-29T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-04-29T00:00:00,2015-04-30T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-04-30T00:00:00,2015-05-01T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-05-01T00:00:00,2015-05-02T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-05-02T00:00:00,2015-05-03T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-05-03T00:00:00,2015-05-04T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-05-04T00:00:00,2015-05-05T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-05-05T00:00:00,2015-05-06T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-05-06T00:00:00,2015-05-07T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-05-07T00:00:00,2015-05-08T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-05-08T00:00:00,2015-05-09T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-05-09T00:00:00,2015-05-10T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-05-10T00:00:00,2015-05-11T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-05-11T00:00:00,2015-05-12T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-05-12T00:00:00,2015-05-13T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-05-13T00:00:00,2015-05-14T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-05-14T00:00:00,2015-05-15T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-05-15T00:00:00,2015-05-16T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-05-16T00:00:00,2015-05-17T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-05-17T00:00:00,2015-05-18T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-05-18T00:00:00,2015-05-19T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-05-19T00:00:00,2015-05-20T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-05-20T00:00:00,2015-05-21T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-05-21T00:00:00,2015-05-22T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-05-22T00:00:00,2015-05-23T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-05-23T00:00:00,2015-05-24T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-05-24T00:00:00,2015-05-25T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-05-25T00:00:00,2015-05-26T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-05-26T00:00:00,2015-05-27T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-05-27T00:00:00,2015-05-28T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-05-28T00:00:00,2015-05-29T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-05-29T00:00:00,2015-05-30T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-05-30T00:00:00,2015-05-31T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-05-31T00:00:00,2015-06-01T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-06-01T00:00:00,2015-06-02T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-06-02T00:00:00,2015-06-03T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-06-03T00:00:00,2015-06-04T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-06-04T00:00:00,2015-06-05T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-06-05T00:00:00,2015-06-06T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-06-06T00:00:00,2015-06-07T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-06-07T00:00:00,2015-06-08T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-06-08T00:00:00,2015-06-09T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-06-09T00:00:00,2015-06-10T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-06-10T00:00:00,2015-06-11T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-06-11T00:00:00,2015-06-12T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-06-12T00:00:00,2015-06-13T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-06-13T00:00:00,2015-06-14T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-06-14T00:00:00,2015-06-15T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-06-15T00:00:00,2015-06-16T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-06-16T00:00:00,2015-06-17T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-06-17T00:00:00,2015-06-18T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-06-18T00:00:00,2015-06-19T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-06-19T00:00:00,2015-06-20T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-06-20T00:00:00,2015-06-21T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-06-21T00:00:00,2015-06-22T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-06-22T00:00:00,2015-06-23T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-06-23T00:00:00,2015-06-24T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-06-24T00:00:00,2015-06-25T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-06-25T00:00:00,2015-06-26T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-06-26T00:00:00,2015-06-27T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-06-27T00:00:00,2015-06-28T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-06-28T00:00:00,2015-06-29T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-06-29T00:00:00,2015-06-30T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-06-30T00:00:00,2015-07-01T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-07-01T00:00:00,2015-07-02T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-07-02T00:00:00,2015-07-03T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-07-03T00:00:00,2015-07-04T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-07-04T00:00:00,2015-07-05T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-07-05T00:00:00,2015-07-06T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-07-06T00:00:00,2015-07-07T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-07-07T00:00:00,2015-07-08T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-07-08T00:00:00,2015-07-09T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-07-09T00:00:00,2015-07-10T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-07-10T00:00:00,2015-07-11T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-07-11T00:00:00,2015-07-12T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-07-12T00:00:00,2015-07-13T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-07-13T00:00:00,2015-07-14T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-07-14T00:00:00,2015-07-15T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-07-15T00:00:00,2015-07-16T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-07-16T00:00:00,2015-07-17T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-07-17T00:00:00,2015-07-18T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-07-18T00:00:00,2015-07-19T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-07-19T00:00:00,2015-07-20T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-07-20T00:00:00,2015-07-21T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-07-21T00:00:00,2015-07-22T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-07-22T00:00:00,2015-07-23T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-07-23T00:00:00,2015-07-24T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-07-24T00:00:00,2015-07-25T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-07-25T00:00:00,2015-07-26T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-07-26T00:00:00,2015-07-27T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-07-27T00:00:00,2015-07-28T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-07-28T00:00:00,2015-07-29T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-07-29T00:00:00,2015-07-30T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-07-30T00:00:00,2015-07-31T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-07-31T00:00:00,2015-08-01T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-08-01T00:00:00,2015-08-02T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-08-02T00:00:00,2015-08-03T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-08-03T00:00:00,2015-08-04T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-08-04T00:00:00,2015-08-05T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-08-05T00:00:00,2015-08-06T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-08-06T00:00:00,2015-08-07T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-08-07T00:00:00,2015-08-08T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-08-08T00:00:00,2015-08-09T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-08-09T00:00:00,2015-08-10T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-08-10T00:00:00,2015-08-11T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-08-11T00:00:00,2015-08-12T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-08-12T00:00:00,2015-08-13T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-08-13T00:00:00,2015-08-14T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-08-14T00:00:00,2015-08-15T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-08-15T00:00:00,2015-08-16T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-08-16T00:00:00,2015-08-17T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-08-17T00:00:00,2015-08-18T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-08-18T00:00:00,2015-08-19T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-08-19T00:00:00,2015-08-20T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-08-20T00:00:00,2015-08-21T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-08-21T00:00:00,2015-08-22T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-08-22T00:00:00,2015-08-23T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-08-23T00:00:00,2015-08-24T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-08-24T00:00:00,2015-08-25T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-08-25T00:00:00,2015-08-26T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-08-26T00:00:00,2015-08-27T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-08-27T00:00:00,2015-08-28T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-08-28T00:00:00,2015-08-29T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-08-29T00:00:00,2015-08-30T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-08-30T00:00:00,2015-08-31T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-08-31T00:00:00,2015-09-01T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-09-01T00:00:00,2015-09-02T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-09-02T00:00:00,2015-09-03T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-09-03T00:00:00,2015-09-04T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-09-04T00:00:00,2015-09-05T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-09-05T00:00:00,2015-09-06T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-09-06T00:00:00,2015-09-07T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-09-07T00:00:00,2015-09-08T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-09-08T00:00:00,2015-09-09T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-09-09T00:00:00,2015-09-10T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-09-10T00:00:00,2015-09-11T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-09-11T00:00:00,2015-09-12T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-09-12T00:00:00,2015-09-13T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-09-13T00:00:00,2015-09-14T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-09-14T00:00:00,2015-09-15T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-09-15T00:00:00,2015-09-16T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-09-16T00:00:00,2015-09-17T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-09-17T00:00:00,2015-09-18T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-09-18T00:00:00,2015-09-19T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-09-19T00:00:00,2015-09-20T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-09-20T00:00:00,2015-09-21T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-09-21T00:00:00,2015-09-22T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-09-22T00:00:00,2015-09-23T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-09-23T00:00:00,2015-09-24T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-09-24T00:00:00,2015-09-25T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-09-25T00:00:00,2015-09-26T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-09-26T00:00:00,2015-09-27T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-09-27T00:00:00,2015-09-28T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-09-28T00:00:00,2015-09-29T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-09-29T00:00:00,2015-09-30T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-09-30T00:00:00,2015-10-01T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-10-01T00:00:00,2015-10-02T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-10-02T00:00:00,2015-10-03T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-10-03T00:00:00,2015-10-04T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-10-04T00:00:00,2015-10-05T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-10-05T00:00:00,2015-10-06T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-10-06T00:00:00,2015-10-07T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-10-07T00:00:00,2015-10-08T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-10-08T00:00:00,2015-10-09T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-10-09T00:00:00,2015-10-10T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-10-10T00:00:00,2015-10-11T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-10-11T00:00:00,2015-10-12T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-10-12T00:00:00,2015-10-13T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-10-13T00:00:00,2015-10-14T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-10-14T00:00:00,2015-10-15T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-10-15T00:00:00,2015-10-16T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-10-16T00:00:00,2015-10-17T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-10-17T00:00:00,2015-10-18T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-10-18T00:00:00,2015-10-19T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-10-19T00:00:00,2015-10-20T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-10-20T00:00:00,2015-10-21T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-10-21T00:00:00,2015-10-22T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-10-22T00:00:00,2015-10-23T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-10-23T00:00:00,2015-10-24T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-10-24T00:00:00,2015-10-25T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-10-25T00:00:00,2015-10-26T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-10-26T00:00:00,2015-10-27T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-10-27T00:00:00,2015-10-28T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-10-28T00:00:00,2015-10-29T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-10-29T00:00:00,2015-10-30T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-10-30T00:00:00,2015-10-31T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-10-31T00:00:00,2015-11-01T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-11-01T00:00:00,2015-11-02T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-11-02T00:00:00,2015-11-03T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-11-03T00:00:00,2015-11-04T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-11-04T00:00:00,2015-11-05T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-11-05T00:00:00,2015-11-06T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-11-06T00:00:00,2015-11-07T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-11-07T00:00:00,2015-11-08T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-11-08T00:00:00,2015-11-09T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-11-09T00:00:00,2015-11-10T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-11-10T00:00:00,2015-11-11T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-11-11T00:00:00,2015-11-12T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-11-12T00:00:00,2015-11-13T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-11-13T00:00:00,2015-11-14T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-11-14T00:00:00,2015-11-15T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-11-15T00:00:00,2015-11-16T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-11-16T00:00:00,2015-11-17T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-11-17T00:00:00,2015-11-18T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-11-18T00:00:00,2015-11-19T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-11-19T00:00:00,2015-11-20T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-11-20T00:00:00,2015-11-21T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-11-21T00:00:00,2015-11-22T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-11-22T00:00:00,2015-11-23T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-11-23T00:00:00,2015-11-24T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-11-24T00:00:00,2015-11-25T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-11-25T00:00:00,2015-11-26T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-11-26T00:00:00,2015-11-27T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-11-27T00:00:00,2015-11-28T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-11-28T00:00:00,2015-11-29T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-11-29T00:00:00,2015-11-30T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-11-30T00:00:00,2015-12-01T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-12-01T00:00:00,2015-12-02T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-12-02T00:00:00,2015-12-03T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-12-03T00:00:00,2015-12-04T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-12-04T00:00:00,2015-12-05T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-12-05T00:00:00,2015-12-06T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-12-06T00:00:00,2015-12-07T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-12-07T00:00:00,2015-12-08T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-12-08T00:00:00,2015-12-09T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-12-09T00:00:00,2015-12-10T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-12-10T00:00:00,2015-12-11T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-12-11T00:00:00,2015-12-12T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-12-12T00:00:00,2015-12-13T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-12-13T00:00:00,2015-12-14T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-12-14T00:00:00,2015-12-15T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-12-15T00:00:00,2015-12-16T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-12-16T00:00:00,2015-12-17T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-12-17T00:00:00,2015-12-18T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-12-18T00:00:00,2015-12-19T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-12-19T00:00:00,2015-12-20T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-12-20T00:00:00,2015-12-21T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-12-21T00:00:00,2015-12-22T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-12-22T00:00:00,2015-12-23T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-12-23T00:00:00,2015-12-24T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-12-24T00:00:00,2015-12-25T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-12-25T00:00:00,2015-12-26T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-12-26T00:00:00,2015-12-27T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-12-27T00:00:00,2015-12-28T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-12-28T00:00:00,2015-12-29T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-12-29T00:00:00,2015-12-30T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-12-30T00:00:00,2015-12-31T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-12-31T00:00:00,2016-01-01T00:00:00,0,nodes,demand1,,inflow,10.0 diff --git a/pywr-schema/src/test_models/csv1-outputs-wide.csv b/pywr-schema/src/test_models/csv1-outputs-wide.csv new file mode 100644 index 00000000..27dce68a --- /dev/null +++ b/pywr-schema/src/test_models/csv1-outputs-wide.csv @@ -0,0 +1,369 @@ +node,demand1 +sub-node, +attribute,inflow +global-scenario-index,0 +2015-01-01 00:00:00,10.00 +2015-01-02 00:00:00,10.00 +2015-01-03 00:00:00,10.00 +2015-01-04 00:00:00,10.00 +2015-01-05 00:00:00,10.00 +2015-01-06 00:00:00,10.00 +2015-01-07 00:00:00,10.00 +2015-01-08 00:00:00,10.00 +2015-01-09 00:00:00,10.00 +2015-01-10 00:00:00,10.00 +2015-01-11 00:00:00,10.00 +2015-01-12 00:00:00,10.00 +2015-01-13 00:00:00,10.00 +2015-01-14 00:00:00,10.00 +2015-01-15 00:00:00,10.00 +2015-01-16 00:00:00,10.00 +2015-01-17 00:00:00,10.00 +2015-01-18 00:00:00,10.00 +2015-01-19 00:00:00,10.00 +2015-01-20 00:00:00,10.00 +2015-01-21 00:00:00,10.00 +2015-01-22 00:00:00,10.00 +2015-01-23 00:00:00,10.00 +2015-01-24 00:00:00,10.00 +2015-01-25 00:00:00,10.00 +2015-01-26 00:00:00,10.00 +2015-01-27 00:00:00,10.00 +2015-01-28 00:00:00,10.00 +2015-01-29 00:00:00,10.00 +2015-01-30 00:00:00,10.00 +2015-01-31 00:00:00,10.00 +2015-02-01 00:00:00,10.00 +2015-02-02 00:00:00,10.00 +2015-02-03 00:00:00,10.00 +2015-02-04 00:00:00,10.00 +2015-02-05 00:00:00,10.00 +2015-02-06 00:00:00,10.00 +2015-02-07 00:00:00,10.00 +2015-02-08 00:00:00,10.00 +2015-02-09 00:00:00,10.00 +2015-02-10 00:00:00,10.00 +2015-02-11 00:00:00,10.00 +2015-02-12 00:00:00,10.00 +2015-02-13 00:00:00,10.00 +2015-02-14 00:00:00,10.00 +2015-02-15 00:00:00,10.00 +2015-02-16 00:00:00,10.00 +2015-02-17 00:00:00,10.00 +2015-02-18 00:00:00,10.00 +2015-02-19 00:00:00,10.00 +2015-02-20 00:00:00,10.00 +2015-02-21 00:00:00,10.00 +2015-02-22 00:00:00,10.00 +2015-02-23 00:00:00,10.00 +2015-02-24 00:00:00,10.00 +2015-02-25 00:00:00,10.00 +2015-02-26 00:00:00,10.00 +2015-02-27 00:00:00,10.00 +2015-02-28 00:00:00,10.00 +2015-03-01 00:00:00,10.00 +2015-03-02 00:00:00,10.00 +2015-03-03 00:00:00,10.00 +2015-03-04 00:00:00,10.00 +2015-03-05 00:00:00,10.00 +2015-03-06 00:00:00,10.00 +2015-03-07 00:00:00,10.00 +2015-03-08 00:00:00,10.00 +2015-03-09 00:00:00,10.00 +2015-03-10 00:00:00,10.00 +2015-03-11 00:00:00,10.00 +2015-03-12 00:00:00,10.00 +2015-03-13 00:00:00,10.00 +2015-03-14 00:00:00,10.00 +2015-03-15 00:00:00,10.00 +2015-03-16 00:00:00,10.00 +2015-03-17 00:00:00,10.00 +2015-03-18 00:00:00,10.00 +2015-03-19 00:00:00,10.00 +2015-03-20 00:00:00,10.00 +2015-03-21 00:00:00,10.00 +2015-03-22 00:00:00,10.00 +2015-03-23 00:00:00,10.00 +2015-03-24 00:00:00,10.00 +2015-03-25 00:00:00,10.00 +2015-03-26 00:00:00,10.00 +2015-03-27 00:00:00,10.00 +2015-03-28 00:00:00,10.00 +2015-03-29 00:00:00,10.00 +2015-03-30 00:00:00,10.00 +2015-03-31 00:00:00,10.00 +2015-04-01 00:00:00,10.00 +2015-04-02 00:00:00,10.00 +2015-04-03 00:00:00,10.00 +2015-04-04 00:00:00,10.00 +2015-04-05 00:00:00,10.00 +2015-04-06 00:00:00,10.00 +2015-04-07 00:00:00,10.00 +2015-04-08 00:00:00,10.00 +2015-04-09 00:00:00,10.00 +2015-04-10 00:00:00,10.00 +2015-04-11 00:00:00,10.00 +2015-04-12 00:00:00,10.00 +2015-04-13 00:00:00,10.00 +2015-04-14 00:00:00,10.00 +2015-04-15 00:00:00,10.00 +2015-04-16 00:00:00,10.00 +2015-04-17 00:00:00,10.00 +2015-04-18 00:00:00,10.00 +2015-04-19 00:00:00,10.00 +2015-04-20 00:00:00,10.00 +2015-04-21 00:00:00,10.00 +2015-04-22 00:00:00,10.00 +2015-04-23 00:00:00,10.00 +2015-04-24 00:00:00,10.00 +2015-04-25 00:00:00,10.00 +2015-04-26 00:00:00,10.00 +2015-04-27 00:00:00,10.00 +2015-04-28 00:00:00,10.00 +2015-04-29 00:00:00,10.00 +2015-04-30 00:00:00,10.00 +2015-05-01 00:00:00,10.00 +2015-05-02 00:00:00,10.00 +2015-05-03 00:00:00,10.00 +2015-05-04 00:00:00,10.00 +2015-05-05 00:00:00,10.00 +2015-05-06 00:00:00,10.00 +2015-05-07 00:00:00,10.00 +2015-05-08 00:00:00,10.00 +2015-05-09 00:00:00,10.00 +2015-05-10 00:00:00,10.00 +2015-05-11 00:00:00,10.00 +2015-05-12 00:00:00,10.00 +2015-05-13 00:00:00,10.00 +2015-05-14 00:00:00,10.00 +2015-05-15 00:00:00,10.00 +2015-05-16 00:00:00,10.00 +2015-05-17 00:00:00,10.00 +2015-05-18 00:00:00,10.00 +2015-05-19 00:00:00,10.00 +2015-05-20 00:00:00,10.00 +2015-05-21 00:00:00,10.00 +2015-05-22 00:00:00,10.00 +2015-05-23 00:00:00,10.00 +2015-05-24 00:00:00,10.00 +2015-05-25 00:00:00,10.00 +2015-05-26 00:00:00,10.00 +2015-05-27 00:00:00,10.00 +2015-05-28 00:00:00,10.00 +2015-05-29 00:00:00,10.00 +2015-05-30 00:00:00,10.00 +2015-05-31 00:00:00,10.00 +2015-06-01 00:00:00,10.00 +2015-06-02 00:00:00,10.00 +2015-06-03 00:00:00,10.00 +2015-06-04 00:00:00,10.00 +2015-06-05 00:00:00,10.00 +2015-06-06 00:00:00,10.00 +2015-06-07 00:00:00,10.00 +2015-06-08 00:00:00,10.00 +2015-06-09 00:00:00,10.00 +2015-06-10 00:00:00,10.00 +2015-06-11 00:00:00,10.00 +2015-06-12 00:00:00,10.00 +2015-06-13 00:00:00,10.00 +2015-06-14 00:00:00,10.00 +2015-06-15 00:00:00,10.00 +2015-06-16 00:00:00,10.00 +2015-06-17 00:00:00,10.00 +2015-06-18 00:00:00,10.00 +2015-06-19 00:00:00,10.00 +2015-06-20 00:00:00,10.00 +2015-06-21 00:00:00,10.00 +2015-06-22 00:00:00,10.00 +2015-06-23 00:00:00,10.00 +2015-06-24 00:00:00,10.00 +2015-06-25 00:00:00,10.00 +2015-06-26 00:00:00,10.00 +2015-06-27 00:00:00,10.00 +2015-06-28 00:00:00,10.00 +2015-06-29 00:00:00,10.00 +2015-06-30 00:00:00,10.00 +2015-07-01 00:00:00,10.00 +2015-07-02 00:00:00,10.00 +2015-07-03 00:00:00,10.00 +2015-07-04 00:00:00,10.00 +2015-07-05 00:00:00,10.00 +2015-07-06 00:00:00,10.00 +2015-07-07 00:00:00,10.00 +2015-07-08 00:00:00,10.00 +2015-07-09 00:00:00,10.00 +2015-07-10 00:00:00,10.00 +2015-07-11 00:00:00,10.00 +2015-07-12 00:00:00,10.00 +2015-07-13 00:00:00,10.00 +2015-07-14 00:00:00,10.00 +2015-07-15 00:00:00,10.00 +2015-07-16 00:00:00,10.00 +2015-07-17 00:00:00,10.00 +2015-07-18 00:00:00,10.00 +2015-07-19 00:00:00,10.00 +2015-07-20 00:00:00,10.00 +2015-07-21 00:00:00,10.00 +2015-07-22 00:00:00,10.00 +2015-07-23 00:00:00,10.00 +2015-07-24 00:00:00,10.00 +2015-07-25 00:00:00,10.00 +2015-07-26 00:00:00,10.00 +2015-07-27 00:00:00,10.00 +2015-07-28 00:00:00,10.00 +2015-07-29 00:00:00,10.00 +2015-07-30 00:00:00,10.00 +2015-07-31 00:00:00,10.00 +2015-08-01 00:00:00,10.00 +2015-08-02 00:00:00,10.00 +2015-08-03 00:00:00,10.00 +2015-08-04 00:00:00,10.00 +2015-08-05 00:00:00,10.00 +2015-08-06 00:00:00,10.00 +2015-08-07 00:00:00,10.00 +2015-08-08 00:00:00,10.00 +2015-08-09 00:00:00,10.00 +2015-08-10 00:00:00,10.00 +2015-08-11 00:00:00,10.00 +2015-08-12 00:00:00,10.00 +2015-08-13 00:00:00,10.00 +2015-08-14 00:00:00,10.00 +2015-08-15 00:00:00,10.00 +2015-08-16 00:00:00,10.00 +2015-08-17 00:00:00,10.00 +2015-08-18 00:00:00,10.00 +2015-08-19 00:00:00,10.00 +2015-08-20 00:00:00,10.00 +2015-08-21 00:00:00,10.00 +2015-08-22 00:00:00,10.00 +2015-08-23 00:00:00,10.00 +2015-08-24 00:00:00,10.00 +2015-08-25 00:00:00,10.00 +2015-08-26 00:00:00,10.00 +2015-08-27 00:00:00,10.00 +2015-08-28 00:00:00,10.00 +2015-08-29 00:00:00,10.00 +2015-08-30 00:00:00,10.00 +2015-08-31 00:00:00,10.00 +2015-09-01 00:00:00,10.00 +2015-09-02 00:00:00,10.00 +2015-09-03 00:00:00,10.00 +2015-09-04 00:00:00,10.00 +2015-09-05 00:00:00,10.00 +2015-09-06 00:00:00,10.00 +2015-09-07 00:00:00,10.00 +2015-09-08 00:00:00,10.00 +2015-09-09 00:00:00,10.00 +2015-09-10 00:00:00,10.00 +2015-09-11 00:00:00,10.00 +2015-09-12 00:00:00,10.00 +2015-09-13 00:00:00,10.00 +2015-09-14 00:00:00,10.00 +2015-09-15 00:00:00,10.00 +2015-09-16 00:00:00,10.00 +2015-09-17 00:00:00,10.00 +2015-09-18 00:00:00,10.00 +2015-09-19 00:00:00,10.00 +2015-09-20 00:00:00,10.00 +2015-09-21 00:00:00,10.00 +2015-09-22 00:00:00,10.00 +2015-09-23 00:00:00,10.00 +2015-09-24 00:00:00,10.00 +2015-09-25 00:00:00,10.00 +2015-09-26 00:00:00,10.00 +2015-09-27 00:00:00,10.00 +2015-09-28 00:00:00,10.00 +2015-09-29 00:00:00,10.00 +2015-09-30 00:00:00,10.00 +2015-10-01 00:00:00,10.00 +2015-10-02 00:00:00,10.00 +2015-10-03 00:00:00,10.00 +2015-10-04 00:00:00,10.00 +2015-10-05 00:00:00,10.00 +2015-10-06 00:00:00,10.00 +2015-10-07 00:00:00,10.00 +2015-10-08 00:00:00,10.00 +2015-10-09 00:00:00,10.00 +2015-10-10 00:00:00,10.00 +2015-10-11 00:00:00,10.00 +2015-10-12 00:00:00,10.00 +2015-10-13 00:00:00,10.00 +2015-10-14 00:00:00,10.00 +2015-10-15 00:00:00,10.00 +2015-10-16 00:00:00,10.00 +2015-10-17 00:00:00,10.00 +2015-10-18 00:00:00,10.00 +2015-10-19 00:00:00,10.00 +2015-10-20 00:00:00,10.00 +2015-10-21 00:00:00,10.00 +2015-10-22 00:00:00,10.00 +2015-10-23 00:00:00,10.00 +2015-10-24 00:00:00,10.00 +2015-10-25 00:00:00,10.00 +2015-10-26 00:00:00,10.00 +2015-10-27 00:00:00,10.00 +2015-10-28 00:00:00,10.00 +2015-10-29 00:00:00,10.00 +2015-10-30 00:00:00,10.00 +2015-10-31 00:00:00,10.00 +2015-11-01 00:00:00,10.00 +2015-11-02 00:00:00,10.00 +2015-11-03 00:00:00,10.00 +2015-11-04 00:00:00,10.00 +2015-11-05 00:00:00,10.00 +2015-11-06 00:00:00,10.00 +2015-11-07 00:00:00,10.00 +2015-11-08 00:00:00,10.00 +2015-11-09 00:00:00,10.00 +2015-11-10 00:00:00,10.00 +2015-11-11 00:00:00,10.00 +2015-11-12 00:00:00,10.00 +2015-11-13 00:00:00,10.00 +2015-11-14 00:00:00,10.00 +2015-11-15 00:00:00,10.00 +2015-11-16 00:00:00,10.00 +2015-11-17 00:00:00,10.00 +2015-11-18 00:00:00,10.00 +2015-11-19 00:00:00,10.00 +2015-11-20 00:00:00,10.00 +2015-11-21 00:00:00,10.00 +2015-11-22 00:00:00,10.00 +2015-11-23 00:00:00,10.00 +2015-11-24 00:00:00,10.00 +2015-11-25 00:00:00,10.00 +2015-11-26 00:00:00,10.00 +2015-11-27 00:00:00,10.00 +2015-11-28 00:00:00,10.00 +2015-11-29 00:00:00,10.00 +2015-11-30 00:00:00,10.00 +2015-12-01 00:00:00,10.00 +2015-12-02 00:00:00,10.00 +2015-12-03 00:00:00,10.00 +2015-12-04 00:00:00,10.00 +2015-12-05 00:00:00,10.00 +2015-12-06 00:00:00,10.00 +2015-12-07 00:00:00,10.00 +2015-12-08 00:00:00,10.00 +2015-12-09 00:00:00,10.00 +2015-12-10 00:00:00,10.00 +2015-12-11 00:00:00,10.00 +2015-12-12 00:00:00,10.00 +2015-12-13 00:00:00,10.00 +2015-12-14 00:00:00,10.00 +2015-12-15 00:00:00,10.00 +2015-12-16 00:00:00,10.00 +2015-12-17 00:00:00,10.00 +2015-12-18 00:00:00,10.00 +2015-12-19 00:00:00,10.00 +2015-12-20 00:00:00,10.00 +2015-12-21 00:00:00,10.00 +2015-12-22 00:00:00,10.00 +2015-12-23 00:00:00,10.00 +2015-12-24 00:00:00,10.00 +2015-12-25 00:00:00,10.00 +2015-12-26 00:00:00,10.00 +2015-12-27 00:00:00,10.00 +2015-12-28 00:00:00,10.00 +2015-12-29 00:00:00,10.00 +2015-12-30 00:00:00,10.00 +2015-12-31 00:00:00,10.00 diff --git a/pywr-schema/src/test_models/csv1.json b/pywr-schema/src/test_models/csv1.json index 4e612d2c..e1b0189f 100644 --- a/pywr-schema/src/test_models/csv1.json +++ b/pywr-schema/src/test_models/csv1.json @@ -62,7 +62,15 @@ { "name": "my-outputs", "type": "CSV", - "filename": "outputs.csv", + "format": "long", + "filename": "outputs-long.csv", + "metric_set": "nodes" + }, + { + "name": "my-outputs", + "type": "CSV", + "format": "wide", + "filename": "outputs-wide.csv", "metric_set": "nodes" } ] diff --git a/pywr-schema/src/test_models/csv2-outputs-long.csv b/pywr-schema/src/test_models/csv2-outputs-long.csv new file mode 100644 index 00000000..16377ed1 --- /dev/null +++ b/pywr-schema/src/test_models/csv2-outputs-long.csv @@ -0,0 +1,13 @@ +time_start,time_end,scenario_index,metric_set,name,sub_name,attribute,value +2015-01-01T00:00:00,2015-02-01T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-02-01T00:00:00,2015-03-01T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-03-01T00:00:00,2015-04-01T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-04-01T00:00:00,2015-05-01T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-05-01T00:00:00,2015-06-01T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-06-01T00:00:00,2015-07-01T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-07-01T00:00:00,2015-08-01T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-08-01T00:00:00,2015-09-01T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-09-01T00:00:00,2015-10-01T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-10-01T00:00:00,2015-11-01T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-11-01T00:00:00,2015-12-01T00:00:00,0,nodes,demand1,,inflow,10.0 +2015-12-01T00:00:00,2015-12-31T00:00:00,0,nodes,demand1,,inflow,10.0 diff --git a/pywr-schema/src/test_models/csv2-outputs-wide.csv b/pywr-schema/src/test_models/csv2-outputs-wide.csv new file mode 100644 index 00000000..796abe35 --- /dev/null +++ b/pywr-schema/src/test_models/csv2-outputs-wide.csv @@ -0,0 +1,16 @@ +node,demand1 +sub-node, +attribute,inflow +global-scenario-index,0 +2015-01-01 00:00:00,10.00 +2015-02-01 00:00:00,10.00 +2015-03-01 00:00:00,10.00 +2015-04-01 00:00:00,10.00 +2015-05-01 00:00:00,10.00 +2015-06-01 00:00:00,10.00 +2015-07-01 00:00:00,10.00 +2015-08-01 00:00:00,10.00 +2015-09-01 00:00:00,10.00 +2015-10-01 00:00:00,10.00 +2015-11-01 00:00:00,10.00 +2015-12-01 00:00:00,10.00 diff --git a/pywr-schema/src/test_models/csv2.json b/pywr-schema/src/test_models/csv2.json index 5fd64c8f..4f30763d 100644 --- a/pywr-schema/src/test_models/csv2.json +++ b/pywr-schema/src/test_models/csv2.json @@ -50,14 +50,14 @@ "metric_sets": [ { "name": "nodes", - "aggregator": { - "freq": { - "type": "Monthly" - }, - "func": { - "type": "Mean" - } + "aggregator": { + "freq": { + "type": "Monthly" }, + "func": { + "type": "Mean" + } + }, "metrics": [ { "type": "Default", @@ -70,7 +70,15 @@ { "name": "monthly-avg-outputs", "type": "CSV", - "filename": "outputs.csv", + "format": "long", + "filename": "outputs-long.csv", + "metric_set": "nodes" + }, + { + "name": "monthly-avg-outputs", + "type": "CSV", + "format": "wide", + "filename": "outputs-wide.csv", "metric_set": "nodes" } ] diff --git a/pywr-schema/src/test_models/csv3-outputs-long.csv b/pywr-schema/src/test_models/csv3-outputs-long.csv new file mode 100644 index 00000000..92d49863 --- /dev/null +++ b/pywr-schema/src/test_models/csv3-outputs-long.csv @@ -0,0 +1,14 @@ +time_start,time_end,scenario_index,metric_set,name,sub_name,attribute,value +2015-01-01T00:00:00,2015-02-01T00:00:00,0,nodes-monthly-mean,demand1,,inflow,10.0 +2015-02-01T00:00:00,2015-03-01T00:00:00,0,nodes-monthly-mean,demand1,,inflow,10.0 +2015-03-01T00:00:00,2015-04-01T00:00:00,0,nodes-monthly-mean,demand1,,inflow,10.0 +2015-04-01T00:00:00,2015-05-01T00:00:00,0,nodes-monthly-mean,demand1,,inflow,10.0 +2015-05-01T00:00:00,2015-06-01T00:00:00,0,nodes-monthly-mean,demand1,,inflow,10.0 +2015-06-01T00:00:00,2015-07-01T00:00:00,0,nodes-monthly-mean,demand1,,inflow,10.0 +2015-07-01T00:00:00,2015-08-01T00:00:00,0,nodes-monthly-mean,demand1,,inflow,10.0 +2015-08-01T00:00:00,2015-09-01T00:00:00,0,nodes-monthly-mean,demand1,,inflow,10.0 +2015-09-01T00:00:00,2015-10-01T00:00:00,0,nodes-monthly-mean,demand1,,inflow,10.0 +2015-10-01T00:00:00,2015-11-01T00:00:00,0,nodes-monthly-mean,demand1,,inflow,10.0 +2015-11-01T00:00:00,2015-12-01T00:00:00,0,nodes-monthly-mean,demand1,,inflow,10.0 +2015-12-01T00:00:00,2015-12-31T00:00:00,0,nodes-monthly-mean,demand1,,inflow,10.0 +2015-01-01T00:00:00,2015-12-31T00:00:00,0,nodes-annual-mean,demand1,,inflow,10.0 diff --git a/pywr-schema/src/test_models/csv3.json b/pywr-schema/src/test_models/csv3.json new file mode 100644 index 00000000..7a2c36b7 --- /dev/null +++ b/pywr-schema/src/test_models/csv3.json @@ -0,0 +1,99 @@ +{ + "metadata": { + "title": "Simple 1", + "description": "A very simple example.", + "minimum_version": "0.1" + }, + "timestepper": { + "start": "2015-01-01", + "end": "2015-12-31", + "timestep": 1 + }, + "network": { + "nodes": [ + { + "name": "supply1", + "type": "Input", + "max_flow": 15 + }, + { + "name": "link1", + "type": "Link" + }, + { + "name": "demand1", + "type": "Output", + "max_flow": { + "type": "Parameter", + "name": "demand" + }, + "cost": -10 + } + ], + "edges": [ + { + "from_node": "supply1", + "to_node": "link1" + }, + { + "from_node": "link1", + "to_node": "demand1" + } + ], + "parameters": [ + { + "name": "demand", + "type": "Constant", + "value": 10.0 + } + ], + "metric_sets": [ + { + "name": "nodes-monthly-mean", + "aggregator": { + "freq": { + "type": "Monthly" + }, + "func": { + "type": "Mean" + } + }, + "metrics": [ + { + "type": "Default", + "node": "demand1" + } + ] + }, + { + "name": "nodes-annual-mean", + "aggregator": { + "freq": { + "type": "Annual" + }, + "func": { + "type": "Mean" + } + }, + "metrics": [ + { + "type": "Default", + "node": "demand1" + } + ] + } + ], + "outputs": [ + { + "name": "monthly-avg-outputs", + "type": "CSV", + "format": "long", + "filename": "outputs-long.csv", + "metric_set": [ + "nodes-monthly-mean", + "nodes-annual-mean" + ] + } + ] + } +}