def test_with_nonstorage_load(self, model): """ Test load from dict with 'storage_node' key. """ m = model m.scenarios.setup() s = Storage(m, 'Storage', max_volume=100.0) l = Link(m, 'Link') data = { "type": "controlcurve", "control_curve": 0.8, "values": [10.0, 0.0], "storage_node": "Storage" } l.cost = p = load_parameter(model, data) assert isinstance(p, ControlCurveParameter) s.setup(m) # Init memory view on storage (bypasses usual `Model.setup`) si = ScenarioIndex(0, np.array([0], dtype=np.int32)) print(s.volume) assert_allclose(l.get_cost(m.timestepper.current, si), 0.0) # When storage volume changes, the cost of the link changes. s.initial_volume = 90.0 m.reset() assert_allclose(l.get_cost(m.timestepper.current, si), 10.0)
def test_with_nonstorage_load(self, model): """Test load from dict with 'storage_node' key.""" m = model s = m.nodes["Storage"] l = Link(m, "Link") # Connect the link node to the network to create a valid model o = m.nodes["Output"] s.connect(l) l.connect(o) data = { "type": "controlcurve", "control_curve": 0.8, "values": [10.0, 0.0], "storage_node": "Storage", } l.cost = p = load_parameter(model, data) assert isinstance(p, ControlCurveParameter) @assert_rec(m, l.cost) def expected_func(timestep, scenario_index): v = s.initial_volume if v >= 80.0: expected = 10.0 else: expected = 0.0 return expected for initial_volume in (90, 70): s.initial_volume = initial_volume m.run()
def test_with_nonstorage(self, model): """Test usage on non-`Storage` node.""" # Now test if the parameter is used on a non storage node m = model s = m.nodes["Storage"] l = Link(m, "Link") # Connect the link node to the network to create a valid model o = m.nodes["Output"] s.connect(l) l.connect(o) cc = ConstantParameter(model, 0.8) l.cost = ControlCurveParameter(model, s, cc, [10.0, 0.0]) @assert_rec(m, l.cost) def expected_func(timestep, scenario_index): v = s.initial_volume if v >= 80.0: expected = 10.0 else: expected = 0.0 return expected for initial_volume in (90, 70): s.initial_volume = initial_volume m.run()
def create_model(): # create a model model = Model(start="2016-01-01", end="2019-12-31", timestep=7) # create three nodes (an input, a link, and an output) A = Input(model, name="A", max_flow=10.0) B = Link(model, name="B", cost=1.0) C = Output(model, name="C", max_flow=5.0, cost=-2.0) # connect nodes A.connect(B) B.connect(C) return model
def simple_linear_model(request, solver): """ Make a simple model with a single Input and Output. Input -> Link -> Output """ model = Model(solver=solver) inpt = Input(model, name="Input") lnk = Link(model, name="Link", cost=1.0) inpt.connect(lnk) otpt = Output(model, name="Output") lnk.connect(otpt) return model
def test_with_nonstorage_load(self, model): """ Test load from dict with 'storage_node' key. """ m = model s = m.nodes['Storage'] l = Link(m, 'Link') data = { "type": "controlcurve", "control_curve": 0.8, "values": [10.0, 0.0], "storage_node": "Storage" } l.cost = p = load_parameter(model, data) assert isinstance(p, ControlCurveParameter) @assert_rec(m, l.cost) def expected_func(timestep, scenario_index): v = s.initial_volume if v >= 80.0: expected = 10.0 else: expected = 0.0 return expected for initial_volume in (90, 70): s.initial_volume = initial_volume m.run()
def test_daily_profile_control_curve(simple_linear_model): """ Test `DailyProfileControlCurveParameter` """ model = simple_linear_model s = Storage(model, 'Storage', max_volume=100.0) l = Link(model, 'Link2') data = { 'type': 'dailyprofilecontrolcurve', 'control_curves': [0.8, 0.6], 'values': [[1.0] * 366, [0.7] * np.arange(366), [0.3] * 366], 'storage_node': 'Storage' } l.max_flow = p = load_parameter(model, data) model.setup() @assert_rec(model, p) def expected_func(timestep, scenario_index): v = s.initial_volume doy = timestep.dayofyear if v >= 80.0: expected = 1.0 elif v >= 60: expected = 0.7 * (doy - 1) else: expected = 0.3 return expected for initial_volume in (90, 70, 30): s.initial_volume = initial_volume model.run()
def test_scaled_profile_nested_load(model): """ Test `ScaledProfileParameter` loading with `AggregatedParameter` """ s = Storage(model, 'Storage', max_volume=100.0) l = Link(model, 'Link') data = { 'type': 'scaledprofile', 'scale': 50.0, 'profile': { 'type': 'aggregated', 'agg_func': 'product', 'parameters': [{ 'type': 'monthlyprofile', 'values': [0.5] * 12 }, { 'type': 'monthlyprofilecontrolcurve', 'control_curves': [0.8, 0.6], 'values': [[1.0] * 12, [0.7] * np.arange(12), [0.3] * 12], 'storage_node': 'Storage' }] } } l.max_flow = p = load_parameter(model, data) p.setup(model) # Test correct aggregation is performed model.scenarios.setup() s.setup( model) # Init memory view on storage (bypasses usual `Model.setup`) s.initial_volume = 90.0 model.reset() # Set initial volume on storage si = ScenarioIndex(0, np.array([0], dtype=np.int32)) for mth in range(1, 13): ts = Timestep(datetime.datetime(2016, mth, 1), 366, 1.0) np.testing.assert_allclose(p.value(ts, si), 50.0 * 0.5 * 1.0) s.initial_volume = 70.0 model.reset() # Set initial volume on storage si = ScenarioIndex(0, np.array([0], dtype=np.int32)) for mth in range(1, 13): ts = Timestep(datetime.datetime(2016, mth, 1), 366, 1.0) np.testing.assert_allclose(p.value(ts, si), 50.0 * 0.5 * 0.7 * (mth - 1)) s.initial_volume = 30.0 model.reset() # Set initial volume on storage si = ScenarioIndex(0, np.array([0], dtype=np.int32)) for mth in range(1, 13): ts = Timestep(datetime.datetime(2016, mth, 1), 366, 1.0) np.testing.assert_allclose(p.value(ts, si), 50.0 * 0.5 * 0.3)
def test_with_nonstorage(self, model): """ Test usage on non-`Storage` node. """ # Now test if the parameter is used on a non storage node m = model m.scenarios.setup() s = Storage(m, 'Storage', max_volume=100.0) l = Link(m, 'Link') cc = ConstantParameter(0.8) l.cost = ControlCurveParameter(s, cc, [10.0, 0.0]) s.setup(m) # Init memory view on storage (bypasses usual `Model.setup`) print(s.volume) si = ScenarioIndex(0, np.array([0], dtype=np.int32)) assert_allclose(l.get_cost(m.timestepper.current, si), 0.0) # When storage volume changes, the cost of the link changes. s.initial_volume = 90.0 m.reset() print(s.volume) assert_allclose(l.get_cost(m.timestepper.current, si), 10.0)
def test_with_nonstorage(self, model): """ Test usage on non-`Storage` node. """ # Now test if the parameter is used on a non storage node m = model s = m.nodes['Storage'] l = Link(m, 'Link') cc = ConstantParameter(model, 0.8) l.cost = ControlCurveParameter(model, s, cc, [10.0, 0.0]) @assert_rec(m, l.cost) def expected_func(timestep, scenario_index): v = s.initial_volume if v >= 80.0: expected = 10.0 else: expected = 0.0 return expected for initial_volume in (90, 70): s.initial_volume = initial_volume m.run()
def test_no_scale_no_profile(self, simple_linear_model): """ No scale or profile specified """ model = simple_linear_model s = Storage(model, 'Storage', max_volume=100.0) l = Link(model, 'Link2') data = { 'type': 'monthlyprofilecontrolcurve', 'control_curves': [0.8, 0.6], 'values': [[1.0] * 12, [0.7] * np.arange(12), [0.3] * 12], 'storage_node': 'Storage' } l.max_flow = p = load_parameter(model, data) self._assert_results(model, s, p)
def test_scale_no_profile(self, simple_linear_model): """ Test `MonthlyProfileControlCurveParameter` """ model = simple_linear_model s = Storage(model, 'Storage', max_volume=100.0) l = Link(model, 'Link2') data = { 'type': 'monthlyprofilecontrolcurve', 'control_curves': [0.8, 0.6], 'values': [[1.0] * 12, [0.7] * np.arange(12), [0.3] * 12], 'storage_node': 'Storage', 'scale': 1.5 } l.max_flow = p = load_parameter(model, data) model.setup() self._assert_results(model, s, p, scale=1.5)
def test_no_scale_profile(self, model): """ No scale, but profile array specified """ s = Storage(model, 'Storage', max_volume=100.0) l = Link(model, 'Link') data = { 'type': 'monthlyprofilecontrolcurve', 'control_curves': [0.8, 0.6], 'values': [[1.0] * 12, [0.7] * np.arange(12), [0.3] * 12], 'storage_node': 'Storage', 'profile': [1.5]*12 } l.max_flow = p = load_parameter(model, data) p.setup(model) model.scenarios.setup() self._assert_results(model, s, p, scale=1.5)
def test_daily_profile_control_curve(model): """ Test `DailyProfileControlCurveParameter` """ s = Storage(model, 'Storage', max_volume=100.0) l = Link(model, 'Link') data = { 'type': 'dailyprofilecontrolcurve', 'control_curves': [0.8, 0.6], 'values': [[1.0]*366, [0.7]*np.arange(366), [0.3]*366], 'storage_node': 'Storage' } l.max_flow = p = load_parameter(model, data) p.setup(model) # Test correct aggregation is performed model.scenarios.setup() s.setup(model) # Init memory view on storage (bypasses usual `Model.setup`) s.initial_volume = 90.0 model.reset() # Set initial volume on storage si = ScenarioIndex(0, np.array([0], dtype=np.int32)) for mth in range(1, 13): ts = Timestep(datetime.datetime(2016, mth, 1), 366, 1.0) np.testing.assert_allclose(p.value(ts, si), 1.0) s.initial_volume = 70.0 model.reset() # Set initial volume on storage si = ScenarioIndex(0, np.array([0], dtype=np.int32)) for mth in range(1, 13): ts = Timestep(datetime.datetime(2016, mth, 1), 366, 1.0) doy = ts.datetime.dayofyear np.testing.assert_allclose(p.value(ts, si), 0.7*(doy - 1)) s.initial_volume = 30.0 model.reset() # Set initial volume on storage si = ScenarioIndex(0, np.array([0], dtype=np.int32)) for mth in range(1, 13): ts = Timestep(datetime.datetime(2016, mth, 1), 366, 1.0) np.testing.assert_allclose(p.value(ts, si), 0.3)
'id': link_id, 'node_1_id': node_1_id, 'node_2_id': node_2_id, 'from_slot': node_lookup_id[node_1_id]['connect_out'] - 1, 'to_slot': node_lookup_id[node_2_id]['connect_in'] - 1 } link_lookup_id[link_id] = { 'name': link['name'], 'type': link['types'][0]['name'], 'node_1_id': node_1_id, 'node_2_id': node_2_id, 'from_slot': node_lookup_id[node_1_id]['connect_out'] - 1, 'to_slot': node_lookup_id[node_2_id]['connect_in'] - 1, 'attributes': link['attributes'] } pywr_links[link_id] = Link(model, name=name) # remove unconnected (rogue) nodes from analysis connected_nodes = [] for link_id, trait in link_lookup_id.items(): connected_nodes.append(trait['node_1_id']) connected_nodes.append(trait['node_2_id']) rogue_nodes = [] for node in node_lookup_id: if node not in connected_nodes: rogue_nodes.append(node) for node in rogue_nodes: del node_lookup_id[node] # create pywr nodes dictionary with format ["name" = pywr type + 'name'] # separate non_storage dicts for greater flexibility with recording model results
def create_model(harmonic=True): # import flow timeseries for catchments flow = pd.read_csv(os.path.join('data', 'thames_stochastic_flow.gz')) flow['Date'] = flow['Date'].apply(pd.to_datetime) flow.set_index('Date', inplace=True) # resample input to weekly average flow = flow.resample('7D', how='mean') model = InspyredOptimisationModel( solver='glpk', start=flow.index[0], end=flow.index[365*10], # roughly 10 years timestep=datetime.timedelta(7), # weekly time-step ) flow_parameter = ArrayIndexedParameter(model, flow['flow'].values) catchment1 = Input(model, 'catchment1', min_flow=flow_parameter, max_flow=flow_parameter) catchment2 = Input(model, 'catchment2', min_flow=flow_parameter, max_flow=flow_parameter) reservoir1 = Storage(model, 'reservoir1', min_volume=3000, max_volume=20000, initial_volume=16000) reservoir2 = Storage(model, 'reservoir2', min_volume=3000, max_volume=20000, initial_volume=16000) if harmonic: control_curve = AnnualHarmonicSeriesParameter(model, 0.5, [0.5], [0.0], mean_upper_bounds=1.0, amplitude_upper_bounds=1.0) else: control_curve = MonthlyProfileParameter(model, np.array([0.0]*12), lower_bounds=0.0, upper_bounds=1.0) control_curve.is_variable = True controller = ControlCurveParameter(model, reservoir1, control_curve, [0.0, 10.0]) transfer = Link(model, 'transfer', max_flow=controller, cost=-500) demand1 = Output(model, 'demand1', max_flow=45.0, cost=-101) demand2 = Output(model, 'demand2', max_flow=20.0, cost=-100) river1 = Link(model, 'river1') river2 = Link(model, 'river2') # compensation flows from reservoirs compensation1 = Link(model, 'compensation1', max_flow=5.0, cost=-9999) compensation2 = Link(model, 'compensation2', max_flow=5.0, cost=-9998) terminator = Output(model, 'terminator', cost=1.0) catchment1.connect(reservoir1) catchment2.connect(reservoir2) reservoir1.connect(demand1) reservoir2.connect(demand2) reservoir2.connect(transfer) transfer.connect(reservoir1) reservoir1.connect(river1) reservoir2.connect(river2) river1.connect(terminator) river2.connect(terminator) reservoir1.connect(compensation1) reservoir2.connect(compensation2) compensation1.connect(terminator) compensation2.connect(terminator) r1 = TotalDeficitNodeRecorder(model, demand1) r2 = TotalDeficitNodeRecorder(model, demand2) r3 = AggregatedRecorder(model, [r1, r2], agg_func="mean") r3.is_objective = 'minimise' r4 = TotalFlowNodeRecorder(model, transfer) r4.is_objective = 'minimise' return model