Modelling of a load increase or reduction without compensation

Regarding oemof.

Hello, I would like to model a distribution grid with several companies with their own generation plants and flexible consumers. The basic structure is to use a bus as a distribution grid and to connect the individual companies, which are represented by their own bus, via a link component. The individual companies are represented as follows: A sink contains the load profile of the company minus the load profiles of the flexible consumers. A storage represents possible electricity storage facilities of the companies. A source represents a decentralised generation plant.

Some of the flexible consumers in the companies have the possibility of load shifting, which - as far as is known - can be mapped by a SinkDSM component. However, other flexible consumers only have the potential to increase or reduce the load without balancing (e.g. switching on a heating system). The idea is congestion management or grid serviceability).

Now the question is how this can be modelled. The first idea was to model a sink for the consumption of the component and another sink and source for the time series of the flexibility with the condition that the two cannot be active at the same time.

The aim is to simulate a consideration of the utilisation of the flexibilities, regardless of whether this makes sense at the moment or not, it should simply be checked how high the maximum potential would be.

Perhaps someone has another idea for the implementation or can give me some help with the parameterisation of the costs.

Many thanks in advance

If someone needs more information, I can also provide the complete code.

#*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~** Komponennten S.*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~**
#1. Bus & Verlinkung zum Netz
    bus_s = buses.Bus(label="s")
    energysystem.add(bus_s)

    energysystem.add(components.Link(
        label="s_link",
        inputs= {bus_s: solph.flows.Flow(), bus_electricity: solph.flows.Flow() },
        outputs={bus_electricity: solph.flows.Flow(), bus_s: solph.flows.Flow()},
        conversion_factors={(bus_electricity, bus_s): 1, (bus_s, bus_electricity): 1}
    ))

#2. Einspeisung und Verbraucher

    # create fixed source object representing pv power plants
    energysystem.add(
        components.Source(
            label="pv",
            outputs={
                bus_staudigel: flows.Flow(
                    fix=df_pv['pv_value'], nominal_value=1, variable_costs=0
                )
            },
        )
    )


    energysystem.add(
        components.Sink(
            label="verbrauch_s_ohne_flexibilitäten",
            inputs={bus_s: flows.Flow(fix=df_verbrauch['verbrauch'], nominal_value=1)}
        )
    )

    """energysystem.add(sinkdsm(
        label="Vorschmelze",
        inputs={bus_s: flows.Flow()},
        demand=df_lastkurve_vorschmelze['leistung'],  # Der Basis-Netzverbrauch
        capacity_up=df_flex_vorschmelze['leistung'],  # Kapazität für Erhöhung der Last (z. B. 100 kW)
        capacity_down=0,  # Kapazität für Verringerung der Last (z. B. 100 kW)
        delay_time=5,
        max_demand=1, # wenn 1 -> demand = Lastkruve
        max_capacity_up=1,  #wenn 1 -> capacity_up = Flexkurve
        max_capacity_down=1, #"
        approach="DIW",
        shed_eligibility=True,
        shift_eligibility=True,
        recovery_time_shift=0,
        shift_time=1,
        recovery_time_shed=1,
        shed_time=5,
        cost_dsm_up= 0,
        cost_dsm_down_shift=0,
        cost_dsm_down_shed=0,
    ))"""

    # Vorschmelze DSM~~~~~~~~~~~~~~optionaler Ansatz 
    energysystem.add(
        components.Sink(
            label="Demand_Vorschmelze",
            inputs={bus_s: flows.Flow(fix=df_lastkurve_vorschmelze['leistung'], nominal_value=1, variable_costs=0)}
        )
    )

    energysystem.add(
        components.Sink(
            label="Lasterhoehung",
            inputs={bus_s: flows.Flow(max=df_flex_vorschmelze['leistung'], nominal_value=1,variable_costs=0)}
        )
    )

    """energysystem.add(
        components.Source(
            label="Lastreduktion",
            outputs={
                bus_s: flows.Flow(
                    max=df_flex_vorschmelze['leistung'], nominal_value=1, variable_costs=0 #kurve lastreduktion -> fiktiv hier, da es sie nicht gibt
                )
            },
        )
    )"""

####################Bedingungen ################

    from oemof.solph import constraints


    # Überprüfen der Gruppen und Flüsse
    #print("Verfügbare Gruppen:", energysystem.groups.keys())
    #print("Verfügbare Flüsse:", list(energysystem_model.flows.keys()))

    # Constraint für Flüsse zwischen Lasterhöhung und Lastreduktion
    constraints.equate_flows(
        model=energysystem_model,
        flows1=[(energysystem.groups["s"], energysystem.groups["Lasterhoehung"])],
        flows2=[(energysystem.groups["Lastreduktion"], energysystem.groups["s"])],
        factor1=1,

 

I have now modelled it as described and also added the restriction that a component can provide either positive or negative flexibility. However, I now have the problem that the different source & sink on the bus could regulate against each other. However, this should be prevented, as the control should only take effect when a request comes from outside. In practice, a flow between two components connected to the same bus should be prevented. Does anyone have any idea how I can add this restriction?

So far, everything is understandable and the modelling concept described is appropriate.

The SinkDSM can have multiple approaches, namely “oemof”, “DLR” or “DIW” which differ in modelling details, how constraints are formulated, which additional restrictions to apply or which parameters to use to control for a certain behaviour, such as limiting the allowed delay time. Be aware that this is an experimental component, meaning the user base is not that large, there is no guarantee that it will be kept maintained “forever” and it might not be extensively unit tested - it in contrast to some components is as I introduced some unit tests for the component.

All of these approaches have in common, that they may depict load shifting or load shedding:

  • For load shifting, the energy balance needs to be levelled out after a certain amount of time steps, i.e. a delay_time (approaches: “DLR” and “DIW”) resp. within a given shift_interval (approach: “oemof”). The total energy demand after load shifting is the same one as before (assuming it to be balanced within the optimization time frame and not accounting for any numerical differences or efficiency losses)
  • For load shedding, there is the option to permanently reduce a demand without any compensating upshift.
  • Load increase without a compensation is not explicitly implemented in the component.

Thus, everything but the desired load increase could in principle be modelled, whereby for load shifting you will always have a balancing requirement. However, what you can experiment with is defining a very long delay_time resp. shift_interval. Note that this will drive up runtimes for all approaches but the “oemof” approach, as a lot of time-interlinking variables and/or constraints will be formulated.

One important thing to mention is that the component has been designed to model load shifting / shedding portfolios using a linear programming approach and not individual plants. For the latter, you might want to rather apply a mixed-integer approach, which is not contained in the framework so far.

I think, this is not necessarily a problem as you described. Once both will be attributed with (maybe small symbolic) costs, the model would not be incentivized to activate both at the same time.

I won’t go into detail on the code snippet above, but provide you with some hints on how you could approach your problem. I would recommend the following:

  • Unless you need any detailed representation, you can go for a SinkDSM with the approach “oemof” to model load shifting and shedding.
  • In case you have a recovery time, the approach “DIW” is the one of choice (parameter: recovery_time_shift which needs to be defined in time steps). Be aware that this performs worst in terms of runtimes due to a lot of time-interlinking constraints, esp. for longer “delay_times”.
  • In case you have a daily limit and/or an overall annual limit, go for the approach “DLR” (parameters: t_dayLimit and n_yearLimit_shift).
  • Concerning load increase, I’d model a regular Sink that is attached to the same company Bus as the residual planned demand of the company / assets.
  • For load decrease only, you can either use the SinkDSM component and deactivate the shifting ability by setting shift_eligibility=False or you can introduce a Source component.
  • I’d not necessarily recommend the combined Sink and Soure approach to model load shifting. This is, because I think, usually, you’ll have some form of balancing requirement in practice even if your time frame is very long. Otherwise, your model could be incentivized to reduce the load all the time which is not a realistic outcome in my opinion.

Another hint: I’d use rather small variable costs, not variable costs of 0 and/or symbolic losses in order to avoid that the model is completely agnostic of using a dedicated option. This makes your model results barely interpretable as it is up to the solver whether or not and to what extend the respective option is used.

I hope this helps.

Best regards and good luck!
Johannes