Start and shutdown costs

Costs for change in operational status

General description

Example that illustrates how to model startup and shutdown costs attributed to a binary flow.

Code

Download source code: startup_shutdown.py

Click to display code

import matplotlib.pyplot as plt
import pandas as pd

from oemof import solph


def main(optimize=True):
    demand_el = [
        0,
        0,
        0,
        1,
        1,
        1,
        0,
        0,
        1,
        1,
        1,
        0,
        0,
        1,
        1,
        1,
        0,
        0,
        1,
        1,
        1,
        1,
        0,
        0,
    ]
    # create an energy system
    idx = solph.create_time_index(2017, number=len(demand_el))
    es = solph.EnergySystem(timeindex=idx, infer_last_interval=False)

    # power bus and components
    bel = solph.Bus(label="bel")

    demand_el = solph.components.Sink(
        label="demand_el",
        inputs={bel: solph.Flow(fix=demand_el, nominal_capacity=10)},
    )

    # pp1 and pp2 are competing to serve overall 12 units load at lowest cost
    # summed costs for pp1 = 12 * 10 * 10.25 = 1230
    # summed costs for pp2 = 4*5 + 4*5 + 12 * 10 * 10 = 1240
    # => pp1 serves the load despite of higher variable costs since
    #    the start and shutdown costs of pp2 change its marginal costs
    pp1 = solph.components.Source(
        label="power_plant1",
        outputs={bel: solph.Flow(nominal_capacity=10, variable_costs=10.25)},
    )

    # shutdown costs only work in combination with a minimum load
    # since otherwise the status variable is "allowed" to be active i.e.
    # it permanently has a value of one which does not allow to set the shutdown
    # variable which is set to one if the status variable changes from one to zero
    pp2 = solph.components.Source(
        label="power_plant2",
        outputs={
            bel: solph.Flow(
                nominal_capacity=10,
                minimum=0.5,
                maximum=1.0,
                variable_costs=10,
                nonconvex=solph.NonConvex(startup_costs=5, shutdown_costs=5),
            )
        },
    )
    es.add(bel, demand_el, pp1, pp2)

    if optimize is False:
        return es

    # create an optimization problem and solve it
    om = solph.Model(es)

    # debugging
    # om.write('problem.lp', io_options={'symbolic_solver_labels': True})

    # solve model
    results = om.solve(solver="cbc", solve_kwargs={"tee": True})

    # plot electrical bus
    to_bus = pd.DataFrame(
        {
            source.label: results["flow"][(source, bel)]
            for source, target in results["flow"].columns
            if target == bel
        }
    )
    from_bus = pd.DataFrame(
        {
            target.label: results["flow"][(bel, target)] * -1
            for source, target in results["flow"].columns
            if source == bel
        }
    )
    data = pd.concat([from_bus, to_bus], axis=1)
    ax = data.plot(kind="line", drawstyle="steps-post", grid=True, rot=0)
    ax.set_xlabel("Hour")
    ax.set_ylabel("P (MW)")
    plt.show()


if __name__ == "__main__":
    main()

Installation requirements

This example requires oemof.solph (at least v0.6.4), install by:

pip install oemof.solph>=0.6.4

License

MIT license