def test_initial_volume(self): """Test RollingVirtualStorage node behaviour when initial volume is less than max volume""" model = pywr.core.Model(timestep="1D") inpt = Input(model, "Input", max_flow=0.0) lnk = Link(model, "Link") inpt.connect(lnk) otpt = Output(model, "Output", max_flow=10, cost=-10.0) lnk.connect(otpt) vs = pywr.core.RollingVirtualStorage(model, "Licence", [lnk], timesteps=3, initial_volume=70.0, max_volume=100.0) model.setup() assert_allclose(vs.volume, [70.0], atol=1e-7) model.step() assert_allclose(otpt.flow, [0.0], atol=1e-7) assert_allclose(vs.volume, [85.0], atol=1e-7) model.step() assert_allclose(otpt.flow, [0.0], atol=1e-7) assert_allclose(vs.volume, [100.0], atol=1e-7) model.step() assert_allclose(otpt.flow, [0.0], atol=1e-7) assert_allclose(vs.volume, [100.0], atol=1e-7)
def test_virtual_storage_duplicate_route(): """ Test the VirtualStorage node """ model = pywr.core.Model() inpt = Input(model, "Input", max_flow=20) lnk = Link(model, "Link") inpt.connect(lnk) otpt = Output(model, "Output", max_flow=10, cost=-10.0) lnk.connect(otpt) vs = pywr.core.VirtualStorage(model, "Licence", [lnk, otpt], factors=[0.5, 1.0], initial_volume=10.0, max_volume=10.0) model.setup() assert_allclose(vs.volume, [10], atol=1e-7) model.step() assert_allclose(otpt.flow, [10 / 1.5], atol=1e-7) assert_allclose(vs.volume, [0], atol=1e-7) model.step() assert_allclose(otpt.flow, [0], atol=1e-7) assert_allclose(vs.volume, [0], atol=1e-7)
def __init__(self, model, name, *args, **kwargs): demand = kwargs.pop('demand', ConstantParameter(model, 0)) supply = kwargs.pop('supply', ConstantParameter(model, 0)) headroom = kwargs.pop('headroom', ConstantParameter(model, 0)) super().__init__(model, name, *args, **kwargs) self.supply_node = Input(model, name=f'{name}-supply', parent=self) self.demand_node = Output(model, name=f'{name}-demand', parent=self) self.supply_node.max_flow = MaxParameter(model, supply) demand_headroom = AggregatedParameter(model, parameters=[demand, headroom], agg_func='sum') self.demand_node.max_flow = MaxParameter(model, demand_headroom) self.demand_node.cost = -100 self.supply_node.connect(self) self.connect(self.demand_node) # Track deficits deficit_param = DeficitParameter(model, self.demand_node, name=f'{name}-deficit_param') NumpyArrayParameterRecorder(model, deficit_param, name=f'__{name}-demand__:deficit', temporal_agg_func='sum')
def test_virtual_storage(solver): """ Test the VirtualStorage node """ model = pywr.core.Model(solver=solver) inpt = Input(model, "Input", max_flow=20) lnk = Link(model, "Link") inpt.connect(lnk) otpt = Output(model, "Output", max_flow=10, cost=-10.0) lnk.connect(otpt) vs = pywr.core.VirtualStorage(model, "Licence", [lnk], initial_volume=10.0, max_volume=10.0) model.setup() assert_allclose(vs.volume, [10], atol=1e-7) model.step() assert_allclose(otpt.flow, [10], atol=1e-7) assert_allclose(vs.volume, [0], atol=1e-7) model.step() assert_allclose(otpt.flow, [0], atol=1e-7) assert_allclose(vs.volume, [0], atol=1e-7)
def test_transfer2(pywr_solver, size): """Test a simple transfer model. """ model = Model() Scenario(model, name='test', size=size) demands = [9.47, 7.65, 9.04, 9.56, 9.44] supply = [4.74, 6.59, 12.04, 8.81, 11.75] for i, (s, d) in enumerate(zip(supply, demands)): s = Input(model, name=f'supply-{i}', max_flow=s) lnk = Link(model, name=f'wtw-{i}') d = Output(model, name=f'demand-{i}', max_flow=d, cost=-10.0) s.connect(lnk) lnk.connect(d) transfer04 = Link(model, name='transfer-04', max_flow=15.36, cost=1.0) model.nodes['wtw-0'].connect(transfer04) transfer04.connect(model.nodes['wtw-4']) model.setup() model.step() expected_supply = [4.74, 6.59, 9.04, 8.81, 9.44] for i, expected in enumerate(expected_supply): assert_allclose(model.nodes[f'supply-{i}'].flow, [expected] * size) assert_allclose(transfer04.flow, [0.0] * size, atol=1e-8)
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=10.0) C = Output(model, name="C", max_flow=5.0, cost=-20.0) # connect the nodes together A.connect(B) B.connect(C) return model
def test_transfer(pywr_solver, size): """Test a simple transfer model. """ model = Model() Scenario(model, name='test', size=size) supply1 = Input(model, name='supply-1', max_flow=5.0) wtw1 = Link(model, name='wtw-1') demand1 = Output(model, name='demand-1', max_flow=10.0, cost=-10.0) supply1.connect(wtw1) wtw1.connect(demand1) supply2 = Input(model, name='supply-2', max_flow=15.0) wtw2 = Link(model, name='wtw-2') demand2 = Output(model, name='demand-2', max_flow=10.0, cost=-10.0) supply2.connect(wtw2) wtw2.connect(demand2) transfer21 = Link(model, name='transfer-12', max_flow=2.0, cost=1.0) wtw2.connect(transfer21) transfer21.connect(wtw1) model.setup() model.step() assert_allclose(supply1.flow, [5.0] * size) assert_allclose(demand1.flow, [7.0] * size) assert_allclose(supply2.flow, [12.0] * size) assert_allclose(demand2.flow, [10.0] * size) assert_allclose(transfer21.flow, [2.0] * size)
def test_run(self, from_json): """Test RollingVirtualStorage node behaviour.""" if from_json: model = load_model('rolling_virtual_storage.json') vs = model.nodes['Licence'] otpt = model.nodes['Output'] else: model = pywr.core.Model() inpt = Input(model, "Input", max_flow=20) lnk = Link(model, "Link") inpt.connect(lnk) otpt = Output(model, "Output", max_flow=15, cost=-10.0) lnk.connect(otpt) vs = pywr.core.RollingVirtualStorage(model, "Licence", [lnk], days=3, initial_volume=30.0, max_volume=30.0) model.setup() assert_allclose(vs.volume, [30], atol=1e-7) model.step() assert_allclose(otpt.flow, [15], atol=1e-7) assert_allclose(vs.volume, [15], atol=1e-7) model.step() assert_allclose(otpt.flow, [15], atol=1e-7) assert_allclose(vs.volume, [0], atol=1e-7) model.step() assert_allclose(otpt.flow, [0], atol=1e-7) # End of third day the flow from the first day is return to the licence assert_allclose(vs.volume, [15], atol=1e-7) model.step() assert_allclose(otpt.flow, [15], atol=1e-7) assert_allclose(vs.volume, [15], atol=1e-7)
def test_run_weekly(self): """Test RollingVirtualStorage node behaviour with a weekly timestep.""" model = pywr.core.Model(timestep='7D') inpt = Input(model, "Input", max_flow=20) lnk = Link(model, "Link") inpt.connect(lnk) otpt = Output(model, "Output", max_flow=10, cost=-10.0) lnk.connect(otpt) vs = pywr.core.RollingVirtualStorage(model, "Licence", [lnk], timesteps=3, initial_volume=100.0, max_volume=100.0) model.setup() assert_allclose(vs.volume, [100.0], atol=1e-7) model.step() assert_allclose(otpt.flow, [10.0], atol=1e-7) assert_allclose(vs.volume, [30.0], atol=1e-7) model.step() assert_allclose(otpt.flow, [30.0 / 7], atol=1e-7) assert_allclose(vs.volume, [0], atol=1e-7) model.step() assert_allclose(otpt.flow, [0], atol=1e-7) # End of third day the flow from the first day is return to the licence assert_allclose(vs.volume, [70.0], atol=1e-7) model.step() assert_allclose(otpt.flow, [10.0], atol=1e-7) assert_allclose(vs.volume, [30.0], atol=1e-7)
class WaterResourceZonePR19(Link): """ A general node to represent a WRZ from the PR19 WRP tables. """ class Schema(NodeSchema): # The main attributes are not validated (i.e. `Raw`) # They could be many different things. demand = fields.ParameterField() supply = fields.ParameterField() headroom = fields.ParameterField() def __init__(self, model, name, *args, **kwargs): demand = kwargs.pop('demand', ConstantParameter(model, 0)) supply = kwargs.pop('supply', ConstantParameter(model, 0)) headroom = kwargs.pop('headroom', ConstantParameter(model, 0)) super().__init__(model, name, *args, **kwargs) self.supply_node = Input(model, name=f'{name}-supply', parent=self) self.demand_node = Output(model, name=f'{name}-demand', parent=self) self.supply_node.max_flow = MaxParameter(model, supply) demand_headroom = AggregatedParameter(model, parameters=[demand, headroom], agg_func='sum') self.demand_node.max_flow = MaxParameter(model, demand_headroom) self.demand_node.cost = -100 self.supply_node.connect(self) self.connect(self.demand_node) # Track deficits deficit_param = DeficitParameter(model, self.demand_node, name=f'{name}-deficit_param') NumpyArrayParameterRecorder(model, deficit_param, name=f'__{name}-demand__:deficit', temporal_agg_func='sum')
def test_max_vol_param_zero_initial_vol(self): """Test RollingVirtualStorage node behaviour when initial volume is zero""" model = pywr.core.Model(timestep="1D") inpt = Input(model, "Input", max_flow=0.0) lnk = Link(model, "Link") inpt.connect(lnk) otpt = Output(model, "Output", max_flow=10, cost=-10.0) lnk.connect(otpt) mx_vol = ConstantParameter(model, 100) pywr.core.RollingVirtualStorage( model, "Licence", [lnk], timesteps=3, initial_volume=0.0, initial_volume_pc=0.0, max_volume=mx_vol, ) with pytest.raises(ValueError): model.setup()
def _make_weather_nodes(self, model, weather, cost): if not isinstance(self.area, Parameter): raise ValueError( 'Weather nodes can only be created if an area Parameter is given.' ) rainfall = weather['rainfall'].astype(np.float64) rainfall_param = MonthlyProfileParameter(model, rainfall) evaporation = weather['evaporation'].astype(np.float64) evaporation_param = MonthlyProfileParameter(model, evaporation) # Assume rainfall/evap is mm/day # Need to convert: # Mm2 -> m2 # mm/day -> m/day # m3/day -> Mm3/day # TODO allow this to be configured const = ConstantParameter(model, 1e6 * 1e-3 * 1e-6) # Create the flow parameters multiplying area by rate of rainfall/evap rainfall_flow_param = AggregatedParameter( model, [rainfall_param, const, self.area], agg_func='product') evaporation_flow_param = AggregatedParameter( model, [evaporation_param, const, self.area], agg_func='product') # Create the nodes to provide the flows rainfall_node = Input(model, '{}.rainfall'.format(self.name), parent=self) rainfall_node.max_flow = rainfall_flow_param rainfall_node.cost = cost evporation_node = Output(model, '{}.evaporation'.format(self.name), parent=self) evporation_node.max_flow = evaporation_flow_param evporation_node.cost = cost rainfall_node.connect(self) self.connect(evporation_node) self.rainfall_node = rainfall_node self.evaporation_node = evporation_node # Finally record these flows self.rainfall_recorder = NumpyArrayNodeRecorder( model, rainfall_node, name=f'__{rainfall_node.name}__:rainfall') self.evaporation_recorder = NumpyArrayNodeRecorder( model, evporation_node, name=f'__{evporation_node.name}__:evaporation')
def _make_rainfall_node(self, model, rainfall, cost): if not isinstance(self.area, Parameter): log.warning( 'Weather nodes can only be created if an area Parameter is given.' ) return if rainfall is None: try: rainfall_param = load_parameter(model, f'__{self.name}__:rainfall') except KeyError: log.warning( f"Please speficy a rainfall or a weather on node {self.name}" ) return elif isinstance(rainfall, pd.DataFrame) or isinstance( rainfall, pd.Series): #assume it's a dataframe rainfall = rainfall.astype(np.float64) rainfall_param = MonthlyProfileParameter(model, rainfall) else: rainfall_param = rainfall # Create the flow parameters multiplying area by rate of rainfall/evap rainfall_flow_param = AggregatedParameter( model, [rainfall_param, self.const, self.area], agg_func='product') # Create the nodes to provide the flows rainfall_node = Input(model, '{}.rainfall'.format(self.name), parent=self) rainfall_node.max_flow = rainfall_flow_param rainfall_node.cost = cost rainfall_node.connect(self) self.rainfall_node = rainfall_node # Finally record these flows self.rainfall_recorder = NumpyArrayNodeRecorder( model, rainfall_node, name=f'__{rainfall_node.name}__:rainfall')
def test_reservoir_circle(): """ Issue #140. A model with a circular route, from a reservoir Input back around to it's own Output. Demand ^ | Reservoir <- Pumping | ^ v | Compensation | | | v | Catchment -> River 1 -> River 2 ----> MRFA -> Waste | ^ |---> MRFB ----| """ model = Model() catchment = Input(model, "catchment", max_flow=500, min_flow=500) reservoir = Storage(model, "reservoir", max_volume=10000, initial_volume=5000) demand = Output(model, "demand", max_flow=50, cost=-100) pumping_station = Link(model, "pumping station", max_flow=100, cost=-10) river1 = Link(model, "river1") river2 = Link(model, "river2") compensation = Link(model, "compensation", cost=600) mrfA = Link(model, "mrfA", cost=-500, max_flow=50) mrfB = Link(model, "mrfB") waste = Output(model, "waste") catchment.connect(river1) river1.connect(river2) river2.connect(mrfA) river2.connect(mrfB) mrfA.connect(waste) mrfB.connect(waste) river2.connect(pumping_station) pumping_station.connect(reservoir) reservoir.connect(compensation) compensation.connect(river1) reservoir.connect(demand) model.check() model.setup() # not limited by mrf, pump capacity is constraint model.step() assert_allclose(catchment.flow, 500) assert_allclose(waste.flow, 400) assert_allclose(compensation.flow, 0) assert_allclose(pumping_station.flow, 100) assert_allclose(demand.flow, 50) # limited by mrf catchment.min_flow = catchment.max_flow = 100 model.step() assert_allclose(waste.flow, 50) assert_allclose(compensation.flow, 0) assert_allclose(pumping_station.flow, 50) assert_allclose(demand.flow, 50) # reservoir can support mrf, but doesn't need to compensation.cost = 200 model.step() assert_allclose(waste.flow, 50) assert_allclose(compensation.flow, 0) assert_allclose(pumping_station.flow, 50) assert_allclose(demand.flow, 50) # reservoir supporting mrf catchment.min_flow = catchment.max_flow = 0 model.step() assert_allclose(waste.flow, 50) assert_allclose(compensation.flow, 50) assert_allclose(pumping_station.flow, 0) assert_allclose(demand.flow, 50)