Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add length check (setting it) for sequences #1117

Merged
merged 10 commits into from
Aug 29, 2024
1 change: 1 addition & 0 deletions docs/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ These are new features and improvements of note in each release
:backlinks: top


.. include:: whatsnew/v0-5-5.rst
.. include:: whatsnew/v0-5-4.rst
.. include:: whatsnew/v0-5-3.rst
.. include:: whatsnew/v0-5-2.rst
Expand Down
12 changes: 12 additions & 0 deletions docs/whatsnew/v0-5-5.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
v0.5.5 ()
--------------------------

Bug fixes
#########

* Fix iterating over _FakeSequence objects

Contributors
############

* Patrik Schönfeldt
43 changes: 40 additions & 3 deletions src/oemof/solph/_plumbing.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
SPDX-License-Identifier: MIT

"""

import warnings
from collections import abc
from itertools import repeat

Expand All @@ -19,8 +19,8 @@

def sequence(iterable_or_scalar):
"""Checks if an object is iterable (except string) or scalar and returns
the original sequence if object is an iterable and an 'emulated'
sequence object of class _Sequence if object is a scalar or string.
the an numpy array of the sequence if object is an iterable or an
'emulated' sequence object of class _FakeSequence if object is a scalar.

Parameters
----------
Expand Down Expand Up @@ -56,6 +56,43 @@ def sequence(iterable_or_scalar):
return _FakeSequence(value=iterable_or_scalar)


def valid_sequence(sequence, length: int) -> bool:
"""Checks if an object is a numpy array of at least the given length
or an 'emulated' sequence object of class _FakeSequence.
If unset, the latter is set to the required lenght.

"""
if sequence[0] is None:
return False

if isinstance(sequence, _FakeSequence):
if sequence.size is None:
sequence.size = length

if sequence.size == length:
return True
else:
return False

if isinstance(sequence, np.ndarray):
if sequence.size == length:
return True
# --- BEGIN: To be removed for versions >= v0.6 ---
elif sequence.size > length:
warnings.warn(
"Sequence longer than needed"
f" ({sequence.size} items instead of {length})."
" This will be trated as an error in the future.",
FutureWarning,
)
return True
# --- END ---
else:
p-snft marked this conversation as resolved.
Show resolved Hide resolved
raise ValueError(f"Lentgh of {sequence} should be {length}.")

p-snft marked this conversation as resolved.
Show resolved Hide resolved
return False


class _FakeSequence:
"""Emulates a list whose length is not known in advance.

Expand Down
6 changes: 3 additions & 3 deletions src/oemof/solph/components/_extraction_turbine_chp.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@
from pyomo.environ import BuildAction
from pyomo.environ import Constraint

from oemof.solph._plumbing import sequence as solph_sequence
from oemof.solph.components._converter import Converter
from oemof.solph._plumbing import sequence
from oemof.solph.components import Converter


class ExtractionTurbineCHP(Converter):
Expand Down Expand Up @@ -87,7 +87,7 @@ def __init__(
custom_attributes=custom_attributes,
)
self.conversion_factor_full_condensation = {
k: solph_sequence(v)
k: sequence(v)
for k, v in conversion_factor_full_condensation.items()
}

Expand Down
33 changes: 15 additions & 18 deletions src/oemof/solph/components/_generic_storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@

from oemof.solph._helpers import check_node_object_for_missing_attribute
from oemof.solph._options import Investment
from oemof.solph._plumbing import sequence as solph_sequence
from oemof.solph._plumbing import sequence
from oemof.solph._plumbing import valid_sequence


class GenericStorage(Node):
Expand Down Expand Up @@ -224,19 +225,15 @@ def __init__(

self.initial_storage_level = initial_storage_level
self.balanced = balanced
self.loss_rate = solph_sequence(loss_rate)
self.fixed_losses_relative = solph_sequence(fixed_losses_relative)
self.fixed_losses_absolute = solph_sequence(fixed_losses_absolute)
self.inflow_conversion_factor = solph_sequence(
inflow_conversion_factor
)
self.outflow_conversion_factor = solph_sequence(
outflow_conversion_factor
)
self.max_storage_level = solph_sequence(max_storage_level)
self.min_storage_level = solph_sequence(min_storage_level)
self.fixed_costs = solph_sequence(fixed_costs)
self.storage_costs = solph_sequence(storage_costs)
self.loss_rate = sequence(loss_rate)
self.fixed_losses_relative = sequence(fixed_losses_relative)
self.fixed_losses_absolute = sequence(fixed_losses_absolute)
self.inflow_conversion_factor = sequence(inflow_conversion_factor)
self.outflow_conversion_factor = sequence(outflow_conversion_factor)
self.max_storage_level = sequence(max_storage_level)
self.min_storage_level = sequence(min_storage_level)
self.fixed_costs = sequence(fixed_costs)
self.storage_costs = sequence(storage_costs)
self.invest_relation_input_output = invest_relation_input_output
self.invest_relation_input_capacity = invest_relation_input_capacity
self.invest_relation_output_capacity = invest_relation_output_capacity
Expand Down Expand Up @@ -603,7 +600,7 @@ def _objective_expression(self):

if m.es.periods is not None:
for n in self.STORAGES:
if n.fixed_costs[0] is not None:
if valid_sequence(n.fixed_costs, len(m.PERIODS)):
fixed_costs += sum(
n.nominal_storage_capacity
* n.fixed_costs[pp]
Expand All @@ -615,7 +612,7 @@ def _objective_expression(self):
storage_costs = 0

for n in self.STORAGES:
if n.storage_costs[0] is not None:
if valid_sequence(n.storage_costs, len(m.TIMESTEPS)):
storage_costs += (
self.storage_content[n, 0] * n.storage_costs[0]
)
Expand Down Expand Up @@ -1861,7 +1858,7 @@ def _objective_expression(self):
period_investment_costs[p] += investment_costs_increment

for n in self.INVESTSTORAGES:
if n.investment.fixed_costs[0] is not None:
if valid_sequence(n.investment.fixed_costs, len(m.PERIODS)):
lifetime = n.investment.lifetime
for p in m.PERIODS:
range_limit = min(
Expand All @@ -1879,7 +1876,7 @@ def _objective_expression(self):
)

for n in self.EXISTING_INVESTSTORAGES:
if n.investment.fixed_costs[0] is not None:
if valid_sequence(n.investment.fixed_costs, len(m.PERIODS)):
lifetime = n.investment.lifetime
age = n.investment.age
range_limit = min(
Expand Down
19 changes: 10 additions & 9 deletions src/oemof/solph/components/experimental/_sink_dsm.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@

from oemof.solph._options import Investment
from oemof.solph._plumbing import sequence
from oemof.solph._plumbing import valid_sequence
from oemof.solph.components._sink import Sink


Expand Down Expand Up @@ -703,7 +704,7 @@ def _objective_expression(self):
* (1 + m.discount_rate) ** (-m.es.periods_years[p])
)

if g.fixed_costs[0] is not None:
if valid_sequence(g.fixed_costs, len(m.PERIODS)):
fixed_costs += sum(
max(g.max_capacity_up, g.max_capacity_down)
* g.fixed_costs[pp]
Expand Down Expand Up @@ -1434,7 +1435,7 @@ def _objective_expression(self):
* (1 + m.discount_rate) ** (-m.es.periods_years[p])
)

if g.investment.fixed_costs[0] is not None:
if valid_sequence(g.investment.fixed_costs, len(m.PERIODS)):
lifetime = g.investment.lifetime
for p in m.PERIODS:
range_limit = min(
Expand All @@ -1452,7 +1453,7 @@ def _objective_expression(self):
)

for g in self.EXISTING_INVESTDSM:
if g.investment.fixed_costs[0] is not None:
if valid_sequence(g.investment.fixed_costs, len(m.PERIODS)):
lifetime = g.investment.lifetime
age = g.investment.age
range_limit = min(
Expand Down Expand Up @@ -2198,7 +2199,7 @@ def _objective_expression(self):
* (1 + m.discount_rate) ** (-m.es.periods_years[p])
)

if g.fixed_costs[0] is not None:
if valid_sequence(g.fixed_costs, len(m.PERIODS)):
fixed_costs += sum(
max(g.max_capacity_up, g.max_capacity_down)
* g.fixed_costs[pp]
Expand Down Expand Up @@ -3290,7 +3291,7 @@ def _objective_expression(self):
* (1 + m.discount_rate) ** (-m.es.periods_years[p])
)

if g.investment.fixed_costs[0] is not None:
if valid_sequence(g.investment.fixed_costs, len(m.PERIODS)):
lifetime = g.investment.lifetime
for p in m.PERIODS:
range_limit = min(
Expand All @@ -3308,7 +3309,7 @@ def _objective_expression(self):
)

for g in self.EXISTING_INVESTDSM:
if g.investment.fixed_costs[0] is not None:
if valid_sequence(g.investment.fixed_costs, len(m.PERIODS)):
lifetime = g.investment.lifetime
age = g.investment.age
range_limit = min(
Expand Down Expand Up @@ -4391,7 +4392,7 @@ def _objective_expression(self):
* (1 + m.discount_rate) ** (-m.es.periods_years[p])
)

if g.fixed_costs[0] is not None:
if valid_sequence(g.fixed_costs, len(m.PERIODS)):
fixed_costs += sum(
max(g.max_capacity_up, g.max_capacity_down)
* g.fixed_costs[pp]
Expand Down Expand Up @@ -5791,7 +5792,7 @@ def _objective_expression(self):
* (1 + m.discount_rate) ** (-m.es.periods_years[p])
)

if g.investment.fixed_costs[0] is not None:
if valid_sequence(g.investment.fixed_costs, len(m.PERIODS)):
lifetime = g.investment.lifetime
for p in m.PERIODS:
range_limit = min(
Expand All @@ -5809,7 +5810,7 @@ def _objective_expression(self):
)

for g in self.EXISTING_INVESTDSM:
if g.investment.fixed_costs[0] is not None:
if valid_sequence(g.investment.fixed_costs, len(m.PERIODS)):
lifetime = g.investment.lifetime
age = g.investment.age
range_limit = min(
Expand Down
10 changes: 8 additions & 2 deletions src/oemof/solph/flows/_investment_flow_block.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@
from pyomo.core import Var
from pyomo.core.base.block import ScalarBlock

from oemof.solph._plumbing import valid_sequence


class InvestmentFlowBlock(ScalarBlock):
r"""Block for all flows with :attr:`Investment` being not None.
Expand Down Expand Up @@ -1007,7 +1009,9 @@ def _objective_expression(self):
period_investment_costs[p] += investment_costs_increment

for i, o in self.INVESTFLOWS:
if m.flows[i, o].investment.fixed_costs[0] is not None:
if valid_sequence(
m.flows[i, o].investment.fixed_costs, len(m.PERIODS)
):
lifetime = m.flows[i, o].investment.lifetime
for p in m.PERIODS:
range_limit = min(
Expand All @@ -1022,7 +1026,9 @@ def _objective_expression(self):
)

for i, o in self.EXISTING_INVESTFLOWS:
if m.flows[i, o].investment.fixed_costs[0] is not None:
if valid_sequence(
m.flows[i, o].investment.fixed_costs, len(m.PERIODS)
):
lifetime = m.flows[i, o].investment.lifetime
age = m.flows[i, o].investment.age
range_limit = min(
Expand Down
Loading
Loading