Skip to content

Commit

Permalink
Merge branch 'dev' into features/make-varying-period-lenghts-possible
Browse files Browse the repository at this point in the history
  • Loading branch information
p-snft committed Aug 10, 2023
2 parents 3350f5a + 869b60b commit 5acd141
Show file tree
Hide file tree
Showing 24 changed files with 822 additions and 629 deletions.
1 change: 1 addition & 0 deletions AUTHORS.rst
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ Authors
* Lluis Millet
* Lennart Schürmann
* Martin Soethe
* Marie-Claire Gering
* Patrik Schönfeldt
* Pierre-François Duc
* Saeed Sayadi
Expand Down
4 changes: 4 additions & 0 deletions CITATION.cff
Original file line number Diff line number Diff line change
Expand Up @@ -162,4 +162,8 @@ authors:
family-names: Rohrer
given-names: Tobi
alias: "@tobirohrer"
-
family-names: Gering
given-names: Marie-Claire
alias: "@MaGering"
...
8 changes: 8 additions & 0 deletions docs/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,11 @@ sphinx>=1.3
sphinx-rtd-theme
setuptools
matplotlib
blinker
dill
numpy
pandas >= 1.5.3
pyomo >= 6.6.0, < 7.0
networkx
oemof.tools >= 0.4.2
oemof.network >= 0.5.0a1
1 change: 1 addition & 0 deletions docs/whatsnew/v0-5-1.rst
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ Bug fixes
* Fixed error when calling `oemof_installation_test` as console script.
* Corrected several typos in the docs.
* Periods with multiple period lengths are now supported in multi-period investment.
* Add missing 'custom_attributes' for the link component

Testing
#######
Expand Down
12 changes: 6 additions & 6 deletions examples/flexible_modelling/add_constraints.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,26 +113,26 @@ def run_add_constraints_example(solver="cbc", nologg=False):
# add the sub-model to the oemof Model instance
om.add_component("MyBlock", myblock)

def _inflow_share_rule(m, s, e, t):
def _inflow_share_rule(m, s, e, p, t):
"""pyomo rule definition: Here we can use all objects from the block or
the om object, in this case we don't need anything from the block
except the newly defined set MYFLOWS.
"""
expr = om.flow[s, e, t] >= om.flows[s, e].outflow_share[t] * sum(
om.flow[i, o, t] for (i, o) in om.FLOWS if o == e
expr = om.flow[s, e, p, t] >= om.flows[s, e].outflow_share[t] * sum(
om.flow[i, o, p, t] for (i, o) in om.FLOWS if o == e
)
return expr

myblock.inflow_share = po.Constraint(
myblock.MYFLOWS, om.TIMESTEPS, rule=_inflow_share_rule
myblock.MYFLOWS, om.TIMEINDEX, rule=_inflow_share_rule
)
# add emission constraint
myblock.emission_constr = po.Constraint(
expr=(
sum(
om.flow[i, o, t]
om.flow[i, o, p, t]
for (i, o) in myblock.COMMODITYFLOWS
for t in om.TIMESTEPS
for p, t in om.TIMEINDEX
)
<= emission_limit
)
Expand Down
128 changes: 128 additions & 0 deletions examples/flexible_modelling/saturating_storage.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
# -*- coding: utf-8 -*-

"""
General description
-------------------
Example that shows the how to implement a `GenericStorage`
that charges at reduced rates for high storage contents.
Installation requirements
-------------------------
This example requires oemof.solph (v0.5.x), install by:
pip install oemof.solph[examples]
License
-------
`MIT license <https://github.com/oemof/oemof-solph/blob/dev/LICENSE>`_
"""

import pandas as pd
from pyomo import environ as po
from matplotlib import pyplot as plt

from oemof import solph


def saturating_storage_example():
# create an energy system
idx = pd.date_range("1/1/2023", periods=100, freq="H")
es = solph.EnergySystem(timeindex=idx, infer_last_interval=False)

# power bus
bel = solph.Bus(label="bel")
es.add(bel)

es.add(
solph.components.Source(
label="source_el",
outputs={bel: solph.Flow(nominal_value=1, fix=1)},
)
)

es.add(
solph.components.Sink(
label="sink_el",
inputs={
bel: solph.Flow(
nominal_value=1,
variable_costs=1,
)
},
)
)

# Electric Storage

inflow_capacity = 0.5
full_charging_limit = 0.4
storage_capacity = 10
battery = solph.components.GenericStorage(
label="battery",
nominal_storage_capacity=storage_capacity,
inputs={bel: solph.Flow(nominal_value=inflow_capacity)},
outputs={bel: solph.Flow(variable_costs=2)},
initial_storage_level=0,
balanced=False,
loss_rate=0.0001,
)
es.add(battery)

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

def soc_limit_rule(m):
for p, ts in m.TIMEINDEX:
soc = (
m.GenericStorageBlock.storage_content[battery, ts + 1]
/ storage_capacity
)
expr = (1 - soc) / (1 - full_charging_limit) >= m.flow[
bel, battery, p, ts
] / inflow_capacity
getattr(m, "soc_limit").add((p, ts), expr)

setattr(
model,
"soc_limit",
po.Constraint(
model.TIMEINDEX,
noruleinit=True,
),
)
setattr(
model,
"soc_limit_build",
po.BuildAction(rule=soc_limit_rule),
)

# solve model
model.solve(solver="cbc")

# create result object
results = solph.processing.results(model)

plt.plot(results[(battery, None)]["sequences"], "r--", label="content")
plt.step(
20 * results[(bel, battery)]["sequences"], "b-", label="20*inflow"
)
plt.legend()
plt.grid()

plt.figure()
plt.plot(
results[(battery, None)]["sequences"][1:],
results[(bel, battery)]["sequences"][:-1],
"b-",
)
plt.grid()
plt.xlabel("Storage content")
plt.ylabel("Charging power")

plt.show()


if __name__ == "__main__":
saturating_storage_example()
Original file line number Diff line number Diff line change
Expand Up @@ -72,12 +72,7 @@ def main():

# Import data.
filename = os.path.join(os.getcwd(), "solar_generation.csv")
try:
data = pd.read_csv(filename)
except FileNotFoundError:
msg = "Data file not found: {0}. Only one value used!"
warnings.warn(msg.format(filename), UserWarning)
data = pd.DataFrame({"pv": [0.3], "wind": [0.6], "demand_el": [500]})
data = pd.read_csv(filename)

# Change the index of data to be able to select data based on the time
# range.
Expand Down Expand Up @@ -177,7 +172,7 @@ def main():
)
},
outputs={b_el_dc: solph.flows.Flow()},
conversion_factor={
conversion_factors={
b_el_dc: 0.98,
},
)
Expand All @@ -196,7 +191,7 @@ def main():
)
},
outputs={b_el_ac: solph.flows.Flow()},
conversion_factor={
conversion_factors={
b_el_ac: 0.98,
},
)
Expand Down
29 changes: 21 additions & 8 deletions src/oemof/solph/components/_generic_storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -1033,21 +1033,20 @@ def _create(self, group=None):
# ########################## CHECKS ###################################
if m.es.periods is not None:
for n in group:
error = (
error_fixed_absolute_losses = (
"For a multi-period investment model, fixed absolute"
" losses are not supported. Please remove parameter."
)
if n.fixed_losses_absolute.default != 0:
raise ValueError(error)
warning = (
raise ValueError(error_fixed_absolute_losses)
error_initial_storage_level = (
"For a multi-period model, initial_storage_level is"
" not supported.\nIt is suggested to remove that"
" parameter since it has no effect.\nstorage_content"
" will be zero, until there is some usable storage "
" capacity installed."
" not supported.\nIt needs to be removed since it"
" has no effect.\nstorage_content will be zero,"
" until there is some usable storage capacity installed."
)
if n.initial_storage_level is not None:
warn(warning, debugging.SuspiciousUsageWarning)
raise ValueError(error_initial_storage_level)

# ########################## SETS #####################################

Expand Down Expand Up @@ -1374,6 +1373,20 @@ def _old_storage_capacity_rule(block):

self.old_rule_build = BuildAction(rule=_old_storage_capacity_rule)

def _initially_empty_rule(block):
"""Ensure storage to be empty initially"""
for n in self.INVESTSTORAGES:
expr = self.storage_content[n, 0] == 0
self.initially_empty.add((n, 0), expr)

self.initially_empty = Constraint(
self.INVESTSTORAGES, m.TIMESTEPS, noruleinit=True
)

self.initially_empty_build = BuildAction(
rule=_initially_empty_rule
)

# Standard storage implementation for discrete time points
else:

Expand Down
4 changes: 4 additions & 0 deletions src/oemof/solph/components/_link.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ def __init__(
inputs=None,
outputs=None,
conversion_factors=None,
custom_attributes=None,
):
if inputs is None:
warn_if_missing_attribute(self, "inputs")
Expand All @@ -94,10 +95,13 @@ def __init__(
if conversion_factors is None:
warn_if_missing_attribute(self, "conversion_factors")
conversion_factors = {}
if custom_attributes is None:
custom_attributes = {}
super().__init__(
label=label,
inputs=inputs,
outputs=outputs,
**custom_attributes,
)
self.conversion_factors = {
k: sequence(v) for k, v in conversion_factors.items()
Expand Down
Loading

0 comments on commit 5acd141

Please sign in to comment.