The `OffsetConverter` is broken in the new implementation
Closed this issue · 4 comments
Describe the bug
With the current formulation, the OffsetConverter
can produce the outputs at the level of each respective offset
without consuming anything from the input.
I am preparing a PR to solve the issue.
To Reproduce
Consider the following example of an electrolyzer producing hydrogen and heat. The electrolyzer follows a fixed hydrogen demand, a slack source is available in case the demand cannot be supplied due to part load restrictions.
from oemof import solph
es = solph.EnergySystem(timeindex=solph.create_time_index(year=2023, number=5))
eta_h2_min = 0.6 # efficiency at minimal operation point
eta_h2_max = 0.5 # efficiency at nominal operation point
P_in_min = 20 # absolute minimal output power
P_in_max = 100 # absolute nominal output power
# calculate limits of input power flow
P_out_min_h2 = P_in_min * eta_h2_min
P_out_max_h2 = P_in_max * eta_h2_max
# calculate coefficients of input-output line equation
slope_h2 = (P_out_max_h2 - P_out_min_h2) / (P_in_max - P_in_min)
offset_h2 = (P_out_max_h2 - slope_h2 * P_in_max) / P_out_max_h2
eta_heat_min = 0.3 # efficiency at minimal operation point
eta_heat_max = 0.4 # efficiency at nominal operation point
# calculate limits of input power flow
P_out_min = P_in_min * eta_heat_min
P_out_max = P_in_max * eta_heat_max
# calculate coefficients of input-output line equation
slope_heat = (P_out_max - P_out_min) / (P_in_max - P_in_min)
offset_heat = (P_out_max - slope_heat * P_in_max) / P_out_max
b_h2 = solph.Bus("hydrogen bus")
b_heat = solph.Bus("heat bus")
b_el = solph.Bus("electricity bus")
b_electrolyzer_h2 = solph.Bus("electrolyzer virtual input for h2")
b_electrolyzer_heat = solph.Bus("electrolyzer virtual input for heat")
source_el = solph.components.Source("electricity import", outputs={b_el: solph.Flow()})
source_slack_h2 = solph.components.Source("hydrogen slack", outputs={b_h2: solph.Flow(variable_costs=1000)})
sink_heat = solph.components.Sink("heat export", inputs={b_heat: solph.Flow()})
sink_h2 = solph.components.Sink("hydrogen export", inputs={b_h2: solph.Flow(fix=[10, 20, 30, 40, 50, 60], nominal_value=1)})
electrolyzer = solph.components.OffsetConverter(
label="electrolyzer",
inputs={b_el: solph.Flow(nominal_value=P_in_max)},
outputs={
b_heat: solph.Flow(
nominal_value=P_out_max,
nonconvex=solph.NonConvex(),
min=P_out_min / P_out_max
),
b_h2: solph.Flow(
nominal_value=P_out_max_h2,
nonconvex=solph.NonConvex()
)
},
coefficients={
b_heat: [offset_heat, slope_heat],
b_h2: [offset_h2, slope_h2],
}
)
es.add(
b_el, b_heat, b_h2,
source_el, sink_h2, sink_heat, source_slack_h2,
electrolyzer
)
om = solph.Model(es)
om.solve("gurobi")
result = solph.views.convert_keys_to_strings(om.results())
result[("electrolyzer", "hydrogen bus")]["sequences"]
The minimum hydrogen production should be at 12
, but due to the offset it is at 2.5
.
flow | status | status_nominal | |
---|---|---|---|
2023-01-01 00:00:00 | 2.5 | 1 | 50 |
2023-01-01 01:00:00 | 20 | 1 | 50 |
2023-01-01 02:00:00 | 30 | 1 | 50 |
2023-01-01 03:00:00 | 40 | 1 | 50 |
2023-01-01 04:00:00 | 50 | 1 | 50 |
2023-01-01 05:00:00 | 50 | 1 | 50 |
The reason for that is, that the status_nominal
is multiplied with the output binary variable instead of the input binary variable.
Never mind, I think my example is just missing the min
attribute of the H2 output Flow. Still, I would like to make the cleanup with enforcing a single NonConvex
flow as the reference for all other flow's slopes and offsets.
Never mind, I think my example is just missing the
min
attribute of the H2 output Flow. Still, I would like to make the cleanup with enforcing a singleNonConvex
flow as the reference for all other flow's slopes and offsets.
Does that mean this issue is closed, then?
Does that mean this issue is closed, then?
Kind of yes, the linked pull requests is still valid. Now it is more of a complete rework of the OffsetConverter
and might close #1010 instead?
So, this was not a real bug, only the API was very inviting to put unreasonable parameters. I will close this, as it can be seen as a duplicate of the "mislieading documentation" issue.