def test_dynamic_factors_load(model): model.timestepper.end = Timestamp("2016-01-03") A = Input(model, "A", max_flow=10.0) B = Input(model, "B", max_flow=10.0) Z = Output(model, "Z", cost=-10, max_flow=10.0) A.connect(Z) B.connect(Z) DailyProfileParameter(model, np.append(np.array([3, 4]), np.ones(364)), name="factor1") data = {"name": "agg", "factors": [1, "factor1"], "nodes": ["A", "B"]} AggregatedNode.load(data, model) model.step() assert_allclose(A.flow, 2.5) assert_allclose(B.flow, 7.5) model.step() assert_allclose(A.flow, 2) assert_allclose(B.flow, 8)
def test_aggregated_node_two_factors_time_varying(model): """Nodes constrained by a time-varying ratio between flows (2 nodes)""" model.timestepper.end = Timestamp("2016-01-03") A = Input(model, "A") B = Input(model, "B", max_flow=40.0) Z = Output(model, "Z", max_flow=100, cost=-10) agg = AggregatedNode(model, "agg", [A, B]) agg.factors = [0.5, 0.5] assert_allclose(agg.factors, [0.5, 0.5]) A.connect(Z) B.connect(Z) model.setup() model.step() assert_allclose(agg.flow, 80.0) assert_allclose(A.flow, 40.0) assert_allclose(B.flow, 40.0) agg.factors = [1.0, 2.0] model.step() assert_allclose(agg.flow, 60.0) assert_allclose(A.flow, 20.0) assert_allclose(B.flow, 40.0)
def test_dynamic_factors(model): model.timestepper.end = Timestamp("2016-01-03") A = Input(model, "A", max_flow=10.0) B = Input(model, "B", max_flow=10.0) C = Input(model, "C", max_flow=10.0) Z = Output(model, "Z", cost=-10) agg = AggregatedNode(model, "agg", [A, B, C]) agg.max_flow = 10.0 factor1 = DailyProfileParameter( model, np.append(np.array([0.8, 0.3]), np.ones(364))) factor2 = DailyProfileParameter( model, np.append(np.array([0.1, 0.3]), np.ones(364))) factor3 = DailyProfileParameter( model, np.append(np.array([0.1, 0.4]), np.ones(364))) agg.factors = [factor1, factor2, factor3] A.connect(Z) B.connect(Z) C.connect(Z) model.step() assert_allclose(A.flow, 8) assert_allclose(B.flow, 1) assert_allclose(C.flow, 1) model.step() assert_allclose(A.flow, 3) assert_allclose(B.flow, 3) assert_allclose(C.flow, 4)
def test_nodes_with_str(self, simple_linear_model, tmpdir): """ Test the TablesRecorder """ from pywr.parameters import ConstantParameter model = simple_linear_model otpt = model.nodes['Output'] inpt = model.nodes['Input'] agg_node = AggregatedNode(model, 'Sum', [otpt, inpt]) p = ConstantParameter(model, 10.0, name='max_flow') inpt.max_flow = p otpt.cost = -2.0 h5file = tmpdir.join('output.h5') import tables with tables.open_file(str(h5file), 'w') as h5f: nodes = ['Output', 'Input', 'Sum'] where = "/agroup" rec = TablesRecorder(model, h5f, nodes=nodes, parameters=[p, ], where=where) model.run() for node_name in ['Output', 'Input', 'Sum', 'max_flow']: ca = h5f.get_node("/agroup/" + node_name) assert ca.shape == (365, 1) if node_name == 'Sum': np.testing.assert_allclose(ca, 20.0) else: np.testing.assert_allclose(ca, 10.0)
def test_parameters(self, simple_linear_model, tmpdir): """ Test the TablesRecorder """ from pywr.parameters import ConstantParameter model = simple_linear_model otpt = model.nodes['Output'] inpt = model.nodes['Input'] p = ConstantParameter(10.0, name='max_flow') inpt.max_flow = p agg_node = AggregatedNode(model, 'Sum', [otpt, inpt]) inpt.max_flow = 10.0 otpt.cost = -2.0 h5file = tmpdir.join('output.h5') import tables with tables.open_file(str(h5file), 'w') as h5f: rec = TablesRecorder(model, h5f, parameters=[p, ]) model.run() for node_name in model.nodes.keys(): ca = h5f.get_node('/', node_name) assert ca.shape == (365, 1) if node_name == 'Sum': np.testing.assert_allclose(ca, 20.0) else: np.testing.assert_allclose(ca, 10.0)
def test_aggregated_node_min_flow_parameter(model): """Nodes constrained by the min_flow of their AggregatedNode""" A = Input(model, "A", max_flow=20.0, cost=1) B = Input(model, "B", max_flow=20.0, cost=100) Z = Output(model, "Z", max_flow=100, cost=0) A.connect(Z) B.connect(Z) agg = AggregatedNode(model, "agg", [A, B]) agg.min_flow = ConstantParameter(model, 15.0) model.run() assert_allclose(agg.flow, 15.0) assert_allclose(A.flow, 15.0) assert_allclose(B.flow, 0.0)
def test_aggregated_node_max_flow(model): """Nodes constrained by the max_flow of their AggregatedNode""" A = Input(model, "A", max_flow=20.0, cost=1) B = Input(model, "B", max_flow=20.0, cost=2) Z = Output(model, "Z", max_flow=100, cost=-10) A.connect(Z) B.connect(Z) agg = AggregatedNode(model, "agg", [A, B]) agg.max_flow = 30.0 model.run() assert_allclose(agg.flow, 30.0) assert_allclose(A.flow, 20.0) assert_allclose(B.flow, 10.0)
def test_aggregated_node_two_factors(model): """Nodes constrained by a fixed ratio between flows (2 nodes)""" A = Input(model, "A") B = Input(model, "B", max_flow=40.0) Z = Output(model, "Z", max_flow=100, cost=-10) agg = AggregatedNode(model, "agg", [A, B]) agg.factors = [0.5, 0.5] assert_allclose(agg.factors, [0.5, 0.5]) A.connect(Z) B.connect(Z) model.run() assert_allclose(agg.flow, 80.0) assert_allclose(A.flow, 40.0) assert_allclose(B.flow, 40.0)
def test_aggregated_node_max_flow_with_weights(model, flow_weights, expected_agg_flow, expected_A_flow, expected_B_flow): """Nodes constrained by the weighted max_flow of their AggregatedNode""" A = Input(model, "A", max_flow=20.0, cost=1) B = Input(model, "B", max_flow=20.0, cost=8) Z = Output(model, "Z", max_flow=100, cost=-10) A.connect(Z) B.connect(Z) agg = AggregatedNode(model, "agg", [A, B]) agg.flow_weights = flow_weights agg.max_flow = 30.0 model.run() assert_allclose(agg.flow, expected_agg_flow) assert_allclose(A.flow, expected_A_flow) assert_allclose(B.flow, expected_B_flow)
def test_aggregated_node_max_flow_same_route(model): """Unusual case where the aggregated nodes are in the same route""" A = Input(model, "A", max_flow=20.0, cost=1) B = Input(model, "B", max_flow=20.0, cost=2) C = Input(model, "C", max_flow=50.0, cost=0) Z = Output(model, "Z", max_flow=100, cost=-10) A.connect(B) B.connect(Z) C.connect(Z) agg = AggregatedNode(model, "agg", [A, B]) agg.max_flow = 30.0 model.run() assert_allclose(agg.flow, 30.0) assert_allclose(A.flow + B.flow, 30.0)
def test_piecewise_constraint(model, flow): """Test using an aggregated node constraint in combination with a piecewise link in order to create a minimum flow constraint of the form y = mx + c, where y is the MRF, x is the upstream flow and m and c are constants. Flows are tested at 100, 200 and 300 to ensure the aggregated ratio works when there is too much to route entirely through to node 'D'. :: / -->-- X0 -->-- \ A -->-- Xo -->-- X1 -->-- Xi -->-- C \\ -->-- X2 -->-- / | Bo -->-- Bi --> D """ A = Input(model, "A", min_flow=flow, max_flow=flow) X = PiecewiseLink(model, name="X", nsteps=3, costs=[-500.0, 0, 0], max_flows=[40.0, None, None]) C = Output(model, "C") A.connect(X) X.connect(C) # create a new input inside the piecewise link which only has access # to flow travelling via the last sublink (X2) Bo = Output(model, "Bo", domain=X.sub_domain) Bi = Input(model, "Bi") D = Output(model, "D", max_flow=50, cost=-100) Bo.connect(Bi) Bi.connect(D) X.sublinks[-1].connect(Bo) agg = AggregatedNode(model, "agg", X.sublinks[1:]) agg.factors = [3.0, 1.0] model.step() assert_allclose(D.flow, min((flow - 40) * 0.25, 50.0))
def test_aggregated_node_two_factors(model): """Nodes constrained by a fixed ratio between flows (2 nodes)""" A = Input(model, "A") B = Input(model, "B", max_flow=40.0) Z = Output(model, "Z", max_flow=100, cost=-10) agg = AggregatedNode(model, "agg", [A, B]) agg.factors = [0.5, 0.5] for f in agg.factors: assert isinstance(f, ConstantParameter) assert_allclose(f.get_double_variables(), 0.5) A.connect(Z) B.connect(Z) model.run() assert_allclose(agg.flow, 80.0) assert_allclose(A.flow, 40.0) assert_allclose(B.flow, 40.0)
def test_aggregated_node_three_factors(model): """Nodes constrained by a fixed ratio between flows (3 nodes)""" A = Input(model, "A") B = Input(model, "B", max_flow=10.0) C = Input(model, "C") Z = Output(model, "Z", max_flow=100, cost=-10) agg = AggregatedNode(model, "agg", [A, B, C]) agg.factors = [0.5, 1.0, 2.0] assert_allclose(agg.factors, [0.5, 1.0, 2.0]) A.connect(Z) B.connect(Z) C.connect(Z) model.run() assert_allclose(agg.flow, 35.0) assert_allclose(A.flow, 5.0) assert_allclose(B.flow, 10.0) assert_allclose(C.flow, 20.0)
def test_routes(self, simple_linear_model, tmpdir): """ Test the TablesRecorder """ model = simple_linear_model otpt = model.nodes['Output'] inpt = model.nodes['Input'] agg_node = AggregatedNode(model, 'Sum', [otpt, inpt]) inpt.max_flow = 10.0 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, 1) np.testing.assert_allclose(flows.read(), np.ones((365, 1, 1))*10) routes = h5f.get_node('/routes') assert routes.shape[0] == 1 row = routes[0] row['start'] = "Input" row['end'] = "Output" from datetime import date, timedelta d = date(2015, 1, 1) time = h5f.get_node('/time') for i in range(len(model.timestepper)): row = time[i] assert row['year'] == d.year assert row['month'] == d.month assert row['day'] == d.day d += timedelta(1) scenarios = h5f.get_node('/scenarios') for s in model.scenarios.scenarios: row = scenarios[i] assert row['name'] == s.name assert row['size'] == s.size model.reset() model.run() time = h5f.get_node('/time') assert len(time) == len(model.timestepper)
def test_nodes(self, simple_linear_model, tmpdir): """ Test the TablesRecorder """ model = simple_linear_model otpt = model.nodes['Output'] inpt = model.nodes['Input'] agg_node = AggregatedNode(model, 'Sum', [otpt, inpt]) inpt.max_flow = 10.0 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, 1) if node_name == 'Sum': np.testing.assert_allclose(ca, 20.0) else: np.testing.assert_allclose(ca, 10.0) from datetime import date, timedelta d = date(2015, 1, 1) time = h5f.get_node('/time') for i in range(len(model.timestepper)): row = time[i] assert row['year'] == d.year assert row['month'] == d.month assert row['day'] == d.day d += timedelta(1) 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 model.reset() model.run() time = h5f.get_node('/time') assert len(time) == len(model.timestepper)
def test_parameters(self, simple_linear_model, tmpdir): """ Test the TablesRecorder """ from pywr.parameters import ConstantParameter model = simple_linear_model otpt = model.nodes['Output'] inpt = model.nodes['Input'] p = ConstantParameter(model, 10.0, name='max_flow') inpt.max_flow = p # ensure TablesRecorder can handle parameters with a / in the name p_slash = ConstantParameter(model, 0.0, name='name with a / in it') inpt.min_flow = p_slash agg_node = AggregatedNode(model, 'Sum', [otpt, inpt]) inpt.max_flow = 10.0 otpt.cost = -2.0 h5file = tmpdir.join('output.h5') import tables with tables.open_file(str(h5file), 'w') as h5f: with pytest.warns(ParameterNameWarning): rec = TablesRecorder(model, h5f, parameters=[p, p_slash]) # check parameters have been added to the component tree # this is particularly important for parameters which update their # values in `after`, e.g. DeficitParameter (see #465) assert(not model.find_orphaned_parameters()) assert(p in rec.children) assert(p_slash in rec.children) with pytest.warns(tables.NaturalNameWarning): model.run() for node_name in model.nodes.keys(): ca = h5f.get_node('/', node_name) assert ca.shape == (365, 1) if node_name == 'Sum': np.testing.assert_allclose(ca, 20.0) elif "name with a" in node_name: assert(node_name == "name with a _ in it") np.testing.assert_allclose(ca, 0.0) else: np.testing.assert_allclose(ca, 10.0)
def test_aggregated_node_three_factors(model): """Nodes constrained by a fixed ratio between flows (3 nodes)""" A = Input(model, "A") B = Input(model, "B", max_flow=10.0) C = Input(model, "C") Z = Output(model, "Z", max_flow=100, cost=-10) agg = AggregatedNode(model, "agg", [A, B, C]) agg.factors = [0.5, 1.0, 2.0] for f, v in zip(agg.factors, [0.5, 1.0, 2.0]): assert isinstance(f, ConstantParameter) assert_allclose(f.get_double_variables(), v) A.connect(Z) B.connect(Z) C.connect(Z) model.run() assert_allclose(agg.flow, 35.0) assert_allclose(A.flow, 5.0) assert_allclose(B.flow, 10.0) assert_allclose(C.flow, 20.0)
def three_storage_model(request): """ Make a simple model with three input, storage and output nodes. Also adds an `AggregatedStorage` and `AggregatedNode`. Input 0 -> Storage 0 -> Output 0 Input 1 -> Storage 1 -> Output 1 Input 2 -> Storage 2 -> Output 2 """ model = pywr.core.Model( start=pandas.to_datetime('2016-01-01'), end=pandas.to_datetime('2016-01-05'), timestep=datetime.timedelta(1), ) all_res = [] all_otpt = [] for num in range(3): inpt = Input(model, name="Input {}".format(num), max_flow=5.0 * num, cost=-1) res = Storage(model, name="Storage {}".format(num), num_outputs=1, num_inputs=1, max_volume=20, initial_volume=10 + num) otpt = Output(model, name="Output {}".format(num), max_flow=8 + num, cost=-999) inpt.connect(res) res.connect(otpt) all_res.append(res) all_otpt.append(otpt) AggregatedStorage(model, name='Total Storage', storage_nodes=all_res) AggregatedNode(model, name='Total Output', nodes=all_otpt) return model
def test_create_directory(self, simple_linear_model, tmpdir): """ Test TablesRecorder to create a new directory """ model = simple_linear_model otpt = model.nodes['Output'] inpt = model.nodes['Input'] agg_node = AggregatedNode(model, 'Sum', [otpt, inpt]) inpt.max_flow = 10.0 otpt.cost = -2.0 # Make a path with a new directory folder = tmpdir.join('outputs') h5file = folder.join('output.h5') assert(not folder.exists()) rec = TablesRecorder(model, str(h5file), create_directories=True) model.run() assert(folder.exists()) assert(h5file.exists())