Prioritisation of supply in OEMOF

Dear OEMOF and Openmod community,

I am making a simple model that simulates a load that can only be charged by a battery that is attached to a PV source. My goal is to charge the battery whenever there is PV available. The bus of the PV source and battery can also provide to the grid in case of overproduction. For some reason, even when I set the variable costs of the battery significantly lower than the grid, the results of the model (oemof components provided below) still prioritize supply to the grid.

Any ideas on why this happens or how to solve this are very welcome!

# create EV bus
bev = buses.Bus(label="EV_bus")

# create electricity bus
bel = buses.Bus(label="electricity_bus")

# create grid
bgrid = buses.Bus(label = "grid")

# adding the buses to the energy system
energysystem.add(bev, bel, bgrid)

# create excess component for the electricity bus to allow overproduction
grid_excess = cmp.Sink(label="excess_grid", inputs={bgrid: flows.Flow(variable_costs = 10)})
energysystem.add(grid_excess)

# create converter object in order to include costs to grid
energysystem.add(
    cmp.Link(
        label="main_el_bus_to_grid",
        inputs={bel: flows.Flow(),bgrid: flows.Flow()},
        outputs={bgrid: flows.Flow(), bel: flows.Flow()},
        conversion_factors={(bel,bgrid): 0.5}
    )
)

energysystem.add(
    cmp.Source(
        label="pv",
        outputs={bel: flows.Flow(fix=data["Production"], nominal_value=1)},
    )
)

#generate data for EV demand and required storage for battery
data_EV = data_generation.generate_dataset(power = 15000 , machine_type = 'Light Barn Mover')
data_EV = data_EV[0:len(data)]
array_data_EV = data_EV['Load']
array_data_req_storage_EV = data_EV['Required Storage Content']

# create sink object representing the EV demand
# nominal_value is set to 1 because demand_el is not a normalised series
energysystem.add(
    cmp.Sink(
        label="demand",
        inputs={bev: flows.Flow(fix=array_data_EV, nominal_value=1)},
    )
)

storage = cmp.GenericStorage(
    nominal_storage_capacity=100779,
    label="storage",
    inputs={bel: flows.Flow()},
    outputs={
        bev: flows.Flow()
    },
    loss_rate=0.00,
    initial_storage_level=0.1,
    # min_storage_level = array_data_req_storage_EV,
    inflow_conversion_factor=1,
    outflow_conversion_factor=1,
    balanced = False
)

energysystem.add(storage)

The obj function in the LP file reads (for three time steps)

min
objective:
+0.8333333333333333 flow(grid_excess_bel_0_0)
+0.8333333333333333 flow(grid_excess_bel_0_1)

I have included figures to clarify. They show the two demand peaks and that the battery is only supplied when the peaks are there. But by default, the model supplies to the grid sink instead of charging the battery.
0108_EVSim_demand
0108_EVSim_soc
0108_plot_EVSim_PV_grid

Hi @TimKaasjager, thanks for bringing this up. Typically, storages are set to be balanced, so they would just be charged if the energy is needed at some point in time. If I see it correctly, you set the storage to be unbalanced and set costs to exporting energy. This way, it would be the cheapest option to use the battery as an energy dump.

Reading your code, I think you found a bug. If you want the credit, you can open an issue at Issues · oemof/oemof-solph · GitHub, otherwise I can also do so. I’ll have a deeper look into the next days.

PS: I just realised that you also wrote an email. I’ll have a closer look at the attached code during working hours tomorrow.

I ran the code you provided an found the issue: The link you add can destroy energy, as you did not set both conversion factors. The minimum change would be to add the two needed conversion_factors and set them to 1. (If the efficiency is lower, circular flows can be used to destroy energy.)

energysystem.add(
        cmp.Link(
            label="main_el_bus_to_grid",
            inputs={bel: flows.Flow(),bgrid: flows.Flow()},
            outputs={bgrid: flows.Flow(), bel: flows.Flow()},
            conversion_factors={(bel,bgrid): 1, (bgrid, bel): 1}
        )
    )

However, I would actually recommend to not use the Link. You could just add direct Flows between the buses. The following code does the same job as the above Link.

    bel.inputs[bgrid] = flows.Flow()
    bel.outputs[bgrid] = flows.Flow()

Hi @pschoen , thanks for pointing that out! It has worked. And omitting the link is an elegant fix. I haven’t quite grasped the usefulness of the Link component yet to be honest. There is one example on the documentation with cellular systems, but it does not have any kind of hierarchy or prioritization in place. So then you might as well just use the inputs and outputs syntax you proposed, I would think