예제 #1
0
    def __init__(self, *args, **kwargs):
        """

        Keywords:
            control_curve - A Parameter object that can return the control curve position,
                as a percentage of fill, for the given timestep.
        """
        control_curve = pop_kwarg_parameter(kwargs, 'control_curve', None)
        above_curve_cost = kwargs.pop('above_curve_cost', None)
        cost = kwargs.pop('cost', 0.0)
        if above_curve_cost is not None:
            if control_curve is None:
                # Make a default control curve at 100% capacity
                control_curve = ConstantParameter(1.0)
            elif not isinstance(control_curve, Parameter):
                # Assume parameter is some kind of constant and coerce to ConstantParameter
                control_curve = ConstantParameter(control_curve)

            if not isinstance(cost, Parameter):
                # In the case where an above_curve_cost is given and cost is not a Parameter
                # a default cost Parameter is created.
                kwargs['cost'] = ControlCurveParameter(
                    self, control_curve, [above_curve_cost, cost])
            else:
                raise ValueError(
                    'If an above_curve_cost is given cost must not be a Parameter.'
                )
        else:
            # reinstate the given cost parameter to pass to the parent constructors
            kwargs['cost'] = cost
        super(Reservoir, self).__init__(*args, **kwargs)
예제 #2
0
파일: test_river.py 프로젝트: jetuk/pywr
def test_control_curve(solver):
    """
    Use a simple model of a Reservoir to test that a control curve
    behaves as expected.

    The control curve should alter the cost of the Reservoir when it
    is above or below a particular threshold.

    (flow = 8.0)          (max_flow = 10.0)
    Catchment -> River -> DemandCentre
                     |        ^
    (max_flow = 2.0) v        | (max_flow = 2.0)
                    Reservoir

    """
    in_flow = 8

    model = pywr.core.Model(solver=solver)
    catchment = river.Catchment(model, name="Catchment", flow=in_flow)
    lnk = river.River(model, name="River")
    catchment.connect(lnk)
    demand = pywr.core.Output(model, name="Demand", cost=-10.0, max_flow=10)
    lnk.connect(demand)
    from pywr.parameters import ConstantParameter
    control_curve = ConstantParameter(0.8)
    reservoir = river.Reservoir(model,
                                name="Reservoir",
                                max_volume=10,
                                cost=-20,
                                above_curve_cost=0.0,
                                control_curve=control_curve,
                                initial_volume=10)
    reservoir.inputs[0].max_flow = 2.0
    reservoir.outputs[0].max_flow = 2.0
    lnk.connect(reservoir)
    reservoir.connect(demand)

    model.step()
    # Reservoir is currently above control curve. 2 should be taken from the
    # reservoir
    assert (reservoir.volume == 8)
    assert (demand.flow == 10)
    # Reservoir is still at (therefore above) control curve. So 2 is still taken
    model.step()
    assert (reservoir.volume == 6)
    assert (demand.flow == 10)
    # Reservoir now below curve. Better to retain volume and divert some of the
    # inflow
    model.step()
    assert (reservoir.volume == 8)
    assert (demand.flow == 6)
    # Set the above_curve_cost function to keep filling
    from pywr.parameters.control_curves import ControlCurveParameter
    # We know what we're doing with the control_curve Parameter so unset its parent before overriding
    # the cost parameter.
    reservoir.cost = ControlCurveParameter(reservoir, control_curve,
                                           [-20.0, -20.0])
    model.step()
    assert (reservoir.volume == 10)
    assert (demand.flow == 6)
예제 #3
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")
        # 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()
예제 #4
0
def test_aggregated_storage_control_curve(three_storage_model):
    """Test using a control curve based on an aggregate storage, rather than
    a single storage.
    """
    model = three_storage_model

    # create a new supply node
    inpt = Input(model, "Input 3", cost=-1000)
    inpt.connect(model.nodes["Output 0"])
    inpt.connect(model.nodes["Output 1"])
    inpt.connect(model.nodes["Output 2"])

    # limit the flow of the new node using a control curve on the aggregate storage
    curves = [0.5]  # 50%
    values = [0, 5]
    inpt.max_flow = ControlCurveParameter(model.nodes["Total Storage"], curves,
                                          values)

    # initial storage is > 50% so flow == 0
    model.step()
    np.testing.assert_allclose(inpt.flow, 0.0)

    # set initial storage to < 50%
    storages = [node for node in model.nodes if isinstance(node, Storage)]
    for node, value in zip(storages, [0.6, 0.1, 0.1]):
        if isinstance(node, Storage):
            node.initial_volume = node.max_volume * value

    # now below the control curve, so flow is allowed
    model.reset()
    model.step()
    np.testing.assert_allclose(inpt.flow, 5.0)
예제 #5
0
    def test_with_values(self, model):
        """Test with `values` keyword argument"""
        m = model
        s = m.nodes['Storage']

        # Return 10.0 when above 0.0 when below
        s.cost = ControlCurveParameter(m, s, [0.8, 0.6], [1.0, 0.7, 0.4])
        self._assert_results(m, s)
예제 #6
0
    def test_with_parameters(self, model):
        """ Test with `parameters` keyword argument. """
        m = model
        s = m.nodes['Storage']

        # Two different control curves
        cc = [ConstantParameter(model, 0.8), ConstantParameter(model, 0.6)]
        # Three different parameters to return
        params = [
            ConstantParameter(model, 1.0), ConstantParameter(model, 0.7), ConstantParameter(model, 0.4)
        ]
        s.cost = ControlCurveParameter(model, s, cc, parameters=params)

        self._assert_results(m, s)
예제 #7
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
        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)
예제 #8
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()
예제 #9
0
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