def test_multiple_scenarios(self, simple_linear_model, tmpdir): """ Test the TablesRecorder """ from pywr.parameters import ConstantScenarioParameter model = simple_linear_model scA = Scenario(model, name='A', size=4) scB = Scenario(model, name='B', size=2) otpt = model.nodes['Output'] inpt = model.nodes['Input'] inpt.max_flow = ConstantScenarioParameter(model, scA, [10, 20, 30, 40]) otpt.max_flow = ConstantScenarioParameter(model, scB, [20, 40]) otpt.cost = -2.0 h5file = tmpdir.join('output.h5') import tables with tables.open_file(str(h5file), 'w') as h5f: rec = TablesRecorder(model, h5f) model.run() for node_name in model.nodes.keys(): ca = h5f.get_node('/', node_name) assert ca.shape == (365, 4, 2) np.testing.assert_allclose(ca[0, ...], [[10, 10], [20, 20], [20, 30], [20, 40]]) scenarios = h5f.get_node('/scenarios') for i, s in enumerate(model.scenarios.scenarios): row = scenarios[i] assert row['name'] == s.name.encode('utf-8') assert row['size'] == s.size
def test_routes_multiple_scenarios(self, simple_linear_model, tmpdir): """ Test the TablesRecorder """ from pywr.parameters import ConstantScenarioParameter model = simple_linear_model scA = Scenario(model, name='A', size=4) scB = Scenario(model, name='B', size=2) otpt = model.nodes['Output'] inpt = model.nodes['Input'] inpt.max_flow = ConstantScenarioParameter(model, scA, [10, 20, 30, 40]) otpt.max_flow = ConstantScenarioParameter(model, scB, [20, 40]) otpt.cost = -2.0 h5file = tmpdir.join('output.h5') import tables with tables.open_file(str(h5file), 'w') as h5f: rec = TablesRecorder(model, h5f, routes_flows='flows') model.run() flows = h5f.get_node('/flows') assert flows.shape == (365, 1, 4, 2) np.testing.assert_allclose(flows[0, 0], [[10, 10], [20, 20], [20, 30], [20, 40]])
def test_two_scenarios(simple_linear_model, ): """Basic test of Scenario functionality""" model = simple_linear_model # Convenience renaming scenario_input = Scenario(model, 'Inflow', size=2) model.nodes["Input"].max_flow = ConstantScenarioParameter( scenario_input, [5.0, 10.0]) scenario_outflow = Scenario(model, 'Outflow', size=2) model.nodes["Output"].max_flow = ConstantScenarioParameter( scenario_outflow, [3.0, 8.0]) model.nodes["Output"].cost = -2.0 # add numpy recorders to input and output nodes NumpyArrayNodeRecorder(model, model.nodes["Input"], "input") NumpyArrayNodeRecorder(model, model.nodes["Output"], "output") expected_node_results = { "Input": [3.0, 5.0, 3.0, 8.0], "Link": [3.0, 5.0, 3.0, 8.0], "Output": [3.0, 5.0, 3.0, 8.0], } assert_model(model, expected_node_results) model.run() # combine recorder outputs to a single dataframe df = model.to_dataframe() assert (df.shape == (365, 2 * 2 * 2)) assert_allclose(df["input", 0, 0].iloc[0], 3.0) assert_allclose(df["input", 0, 1].iloc[0], 5.0) assert_allclose(df["input", 1, 0].iloc[0], 3.0) assert_allclose(df["input", 1, 1].iloc[0], 8.0)
def test_scenario_storage(): """Test the behaviour of Storage nodes with multiple scenarios The model defined has two inflow scenarios: 5 and 10. It is expected that the volume in the storage node should increase at different rates in the two scenarios. """ model = Model() i = Input(model, 'input', max_flow=999) s = Storage(model, 'storage', num_inputs=1, num_outputs=1, max_volume=1000, initial_volume=500) o = Output(model, 'output', max_flow=999) scenario_input = Scenario(model, 'Inflow', size=2) i.min_flow = ConstantScenarioParameter(model, scenario_input, [5.0, 10.0]) i.connect(s) s.connect(o) s_rec = NumpyArrayStorageRecorder(model, s) model.run() assert_allclose(i.flow, [5, 10]) assert_allclose(s_rec.data[0], [505, 510]) assert_allclose(s_rec.data[1], [510, 520])
def test_annual_count_index_parameter_recorder(simple_storage_model): """ Test AnnualCountIndexParameterRecord The test sets uses a simple reservoir model with different inputs that trigger a control curve failure after different numbers of years. """ from pywr.parameters import ConstantScenarioParameter, ConstantParameter from pywr.parameters.control_curves import ControlCurveIndexParameter model = simple_storage_model scenario = Scenario(model, 'A', size=2) # Simulate 5 years model.timestepper.start = '2015-01-01' model.timestepper.end = '2019-12-31' # Control curve parameter param = ControlCurveIndexParameter(model, model.nodes['Storage'], ConstantParameter(model, 0.25)) # Storage model has a capacity of 20, but starts at 10 Ml # Demand is roughly 2 Ml/d per year # First ensemble balances the demand # Second ensemble should fail during 3rd year demand = 2.0 / 365 model.nodes['Input'].max_flow = ConstantScenarioParameter(model, scenario, [demand, 0]) model.nodes['Output'].max_flow = demand # Create the recorder with a threshold of 1 rec = AnnualCountIndexParameterRecorder(model, param, 1) model.run() # We expect no failures in the first ensemble, but 3 out of 5 in the second assert_allclose(rec.values(), [0, 3])
def test_aggregated_storage_scenarios(three_storage_model): """ Test `AggregatedScenario` correctly sums over scenarios. """ from pywr.core import Scenario from pywr.parameters import ConstantScenarioParameter m = three_storage_model sc = Scenario(m, 'A', size=5) agg_stg = m.nodes['Total Storage'] stgs = [m.nodes['Storage {}'.format(num)] for num in range(3)] # Setup scenario inpt1 = m.nodes['Input 1'].max_flow = ConstantScenarioParameter( sc, range(sc.size)) m.setup() m.step() # Finally check volume is summed correctly np.testing.assert_allclose(agg_stg.volume, np.sum(s.volume for s in stgs)) current_pc = np.sum(s.volume for s in stgs) / (np.sum(s.max_volume for s in stgs)) np.testing.assert_allclose(agg_stg.current_pc, current_pc) np.testing.assert_allclose(agg_stg.flow, np.sum(s.flow for s in stgs)) m.step() # Finally check volume is summed correctly np.testing.assert_allclose(agg_stg.volume, np.sum(s.volume for s in stgs)) current_pc = np.sum(s.volume for s in stgs) / (np.sum(s.max_volume for s in stgs)) np.testing.assert_allclose(agg_stg.current_pc, current_pc) np.testing.assert_allclose(agg_stg.flow, np.sum(s.flow for s in stgs))
def test_scenario_two_parameter(simple_linear_model, ): """Basic test of Scenario functionality""" model = simple_linear_model # Convenience renaming scenario_input = Scenario(model, 'Inflow', size=2) model.nodes["Input"].max_flow = ConstantScenarioParameter(model, scenario_input, [5.0, 10.0]) model.nodes["Output"].max_flow = ConstantScenarioParameter(model, scenario_input, [8.0, 3.0]) model.nodes["Output"].cost = -2.0 expected_node_results = { "Input": [5.0, 3.0], "Link": [5.0, 3.0], "Output": [5.0, 3.0], } assert_model(model, expected_node_results)
def test_parameter_constant_scenario(simple_linear_model): """ Test ConstantScenarioParameter """ model = simple_linear_model # Add two scenarios scA = Scenario(model, 'Scenario A', size=2) scB = Scenario(model, 'Scenario B', size=5) p = ConstantScenarioParameter(model, scB, np.arange(scB.size, dtype=np.float64)) model.setup() ts = model.timestepper.current # Now ensure the appropriate value is returned for the Scenario B indices. for i, (a, b) in enumerate(itertools.product(range(scA.size), range(scB.size))): si = ScenarioIndex(i, np.array([a, b], dtype=np.int32)) np.testing.assert_allclose(p.value(ts, si), float(b))
def test_two_scenarios(simple_linear_model, ): """Basic test of Scenario functionality""" model = simple_linear_model # Convenience renaming scenario_input = Scenario(model, 'Inflow', size=2) model.nodes["Input"].max_flow = ConstantScenarioParameter( model, scenario_input, [5.0, 10.0]) scenario_outflow = Scenario(model, 'Outflow', size=2, ensemble_names=['High', 'Low']) model.nodes["Output"].max_flow = ConstantScenarioParameter( model, scenario_outflow, [3.0, 8.0]) model.nodes["Output"].cost = -2.0 # Check ensemble names are provided in the multi-index index = model.scenarios.multiindex assert index.levels[0].name == 'Inflow' assert index.levels[1].name == 'Outflow' assert np.all(index.levels[1] == ['High', 'Low']) # add numpy recorders to input and output nodes NumpyArrayNodeRecorder(model, model.nodes["Input"], "input") NumpyArrayNodeRecorder(model, model.nodes["Output"], "output") expected_node_results = { "Input": [3.0, 5.0, 3.0, 8.0], "Link": [3.0, 5.0, 3.0, 8.0], "Output": [3.0, 5.0, 3.0, 8.0], } assert_model(model, expected_node_results) model.run() # combine recorder outputs to a single dataframe df = model.to_dataframe() assert (df.shape == (365, 2 * 2 * 2)) assert_allclose(df["input", 0, 'High'].iloc[0], 3.0) assert_allclose(df["input", 0, 'Low'].iloc[0], 5.0) assert_allclose(df["input", 1, 'High'].iloc[0], 3.0) assert_allclose(df["input", 1, 'Low'].iloc[0], 8.0)
def test_user_scenarios(self, simple_linear_model, tmpdir): """ Test the TablesRecorder with user defined scenario subset """ from pywr.parameters import ConstantScenarioParameter model = simple_linear_model scA = Scenario(model, name='A', size=4) scB = Scenario(model, name='B', size=2) # Use first and last combinations model.scenarios.user_combinations = [[0, 0], [3, 1]] otpt = model.nodes['Output'] inpt = model.nodes['Input'] inpt.max_flow = ConstantScenarioParameter(model, scA, [10, 20, 30, 40]) otpt.max_flow = ConstantScenarioParameter(model, scB, [20, 40]) otpt.cost = -2.0 h5file = tmpdir.join('output.h5') import tables with tables.open_file(str(h5file), 'w') as h5f: rec = TablesRecorder(model, h5f) model.run() for node_name in model.nodes.keys(): ca = h5f.get_node('/', node_name) assert ca.shape == (365, 2) np.testing.assert_allclose(ca[0, ...], [10, 40]) # check combinations table exists combinations = h5f.get_node('/scenario_combinations') for i, comb in enumerate(model.scenarios.user_combinations): row = combinations[i] assert row['A'] == comb[0] assert row['B'] == comb[1]
def test_cache_scenarios(simple_model): """ Test caching across scenarios. """ model = simple_model scen = Scenario(model, 'Scenario A', size=2) param = ConstantScenarioParameter(scen, [10, 20]) inpt = model.nodes["input"] inpt.max_flow = CachedParameter(param) model.run() assert_allclose(inpt.flow, [10.0, 20.0])
def cyclical_storage_model(simple_storage_model): """ Extends simple_storage_model to have a cyclical boundary condition """ from pywr.parameters import AnnualHarmonicSeriesParameter, ConstantScenarioParameter m = simple_storage_model s = Scenario(m, name='Scenario A', size=3) m.timestepper.end = '2017-12-31' m.timestepper.delta = 5 inpt = m.nodes['Input'] inpt.max_flow = AnnualHarmonicSeriesParameter(m, 5, [0.1, 0.0, 0.25], [0.0, 0.0, 0.0]) otpt = m.nodes['Output'] otpt.max_flow = ConstantScenarioParameter(m, s, [5, 6, 2]) return m
def test_max(self, model): # Add two scenarios scA = Scenario(model, 'Scenario A', size=2) scB = Scenario(model, 'Scenario B', size=5) values = np.arange(366, dtype=np.float64) p1 = DailyProfileParameter(values) p2 = ConstantScenarioParameter(scB, np.arange(scB.size, dtype=np.float64)) p = AggregatedParameter([p1, p2], agg_func='max') p.setup(model) for ts in model.timestepper: month = ts.datetime.month day = ts.datetime.day iday = int((datetime.datetime(2016, month, day) - datetime.datetime(2016, 1, 1)).days) for i in range(scB.size): si = ScenarioIndex(i, np.array([0, i], dtype=np.int32)) np.testing.assert_allclose(p.value(ts, si), max(values[iday], i))
def test_aggregated_node_scenarios(three_storage_model): """ Test `AggregatedScenario` correctly sums over scenarios. """ from pywr.core import Scenario from pywr.parameters import ConstantScenarioParameter m = three_storage_model sc = Scenario(m, 'A', size=5) agg_otpt = m.nodes['Total Output'] otpts = [m.nodes['Output {}'.format(num)] for num in range(3)] # Setup scenario inpt1 = m.nodes['Input 1'].max_flow = ConstantScenarioParameter(m, sc, range(sc.size)) m.setup() m.step() # Finally check volume is summed correctly np.testing.assert_allclose(agg_otpt.flow, sum(o.flow for o in otpts)) m.step() # Finally check volume is summed correctly np.testing.assert_allclose(agg_otpt.flow, sum(o.flow for o in otpts))
def test_min(self, model): # Add two scenarios scA = Scenario(model, 'Scenario A', size=2) scB = Scenario(model, 'Scenario B', size=5) values = np.arange(366, dtype=np.float64) p1 = DailyProfileParameter(values) p2 = ConstantScenarioParameter(scB, np.arange(scB.size, dtype=np.float64)) p = AggregatedParameter([ p1, ], agg_func='min') p.add(p2) p.setup(model) for ts in model.timestepper: iday = ts.datetime.dayofyear - 1 for i in range(scB.size): si = ScenarioIndex(i, np.array([0, i], dtype=np.int32)) np.testing.assert_allclose(p.value(ts, si), min(values[iday], i))
def test_agg(self, simple_linear_model, agg_func): model = simple_linear_model model.timestepper.delta = 15 scenarioA = Scenario(model, "Scenario A", size=2) scenarioB = Scenario(model, "Scenario B", size=5) values = np.arange(366, dtype=np.float64) p1 = DailyProfileParameter(model, values) p2 = ConstantScenarioParameter(model, scenarioB, np.arange(scenarioB.size, dtype=np.float64)) p = AggregatedParameter(model, [p1, p2], agg_func=agg_func) func = TestAggregatedParameter.funcs[agg_func] @assert_rec(model, p) def expected_func(timestep, scenario_index): x = p1.get_value(scenario_index) y = p2.get_value(scenario_index) return func(np.array([x,y])) model.run()
def test_routes_user_scenarios(self, simple_linear_model, tmpdir): """ Test the TablesRecorder with user defined scenario subset """ from pywr.parameters import ConstantScenarioParameter model = simple_linear_model scA = Scenario(model, name='A', size=4) scB = Scenario(model, name='B', size=2) # Use first and last combinations model.scenarios.user_combinations = [[0, 0], [3, 1]] otpt = model.nodes['Output'] inpt = model.nodes['Input'] inpt.max_flow = ConstantScenarioParameter(model, scA, [10, 20, 30, 40]) otpt.max_flow = ConstantScenarioParameter(model, scB, [20, 40]) otpt.cost = -2.0 h5file = tmpdir.join('output.h5') import tables with tables.open_file(str(h5file), 'w') as h5f: rec = TablesRecorder(model, h5f, routes_flows='flows') model.run() flows = h5f.get_node('/flows') assert flows.shape == (365, 1, 2) np.testing.assert_allclose(flows[0, 0], [10, 40]) # check combinations table exists combinations = h5f.get_node('/scenario_combinations') for i, comb in enumerate(model.scenarios.user_combinations): row = combinations[i] assert row['A'] == comb[0] assert row['B'] == comb[1] # This part of the test requires IPython (see `pywr.notebook`) pytest.importorskip("IPython") # triggers a skip of the test if IPython not found. from pywr.notebook.sankey import routes_to_sankey_links links = routes_to_sankey_links(str(h5file), 'flows') # Value is mean of 10 and 40 link = links[0] assert link['source'] == 'Input' assert link['target'] == 'Output' np.testing.assert_allclose(link['value'], 25.0) links = routes_to_sankey_links(str(h5file), 'flows', scenario_slice=0) link = links[0] assert link['source'] == 'Input' assert link['target'] == 'Output' np.testing.assert_allclose(link['value'], 10.0) links = routes_to_sankey_links(str(h5file), 'flows', scenario_slice=1, time_slice=0) link = links[0] assert link['source'] == 'Input' assert link['target'] == 'Output' np.testing.assert_allclose(link['value'], 40.0)