Skip to content

Commit

Permalink
Merge pull request #982 from oemof/feature/account-for-remaining-valu…
Browse files Browse the repository at this point in the history
…es-of-multi-period-investments

Account for remaining values in multi-period models
  • Loading branch information
jokochems authored Oct 20, 2023
2 parents b5b098a + f542a7c commit 119e9cb
Show file tree
Hide file tree
Showing 51 changed files with 6,318 additions and 1,352 deletions.
5 changes: 5 additions & 0 deletions docs/usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1270,6 +1270,11 @@ Besides the `invest` variable, new variables are introduced as well. These are:
has not yet been tested.
* For now, both, the `timeindex` as well as the `timeincrement` of an energy system have to be defined since they
have to be of the same length for a multi-period model.
* You can choose whether to re-evaluate assets at the end of the optimization horizon. If you set attribute
`use_remaining_value` of the energy system to True (defaults to False), this leads to the model evaluating the
difference in the asset value at the end of the optimization horizon vs. at the time the investment was made.
The difference in value is added to or subtracted from the respective investment costs increment,
assuming assets are to be liquidated / re-evaluated at the end of the optimization horizon.
* Also please be aware, that periods correspond to years by default. You could also choose
monthly periods, but you would need to be very careful in parameterizing your energy system and your model and also,
this would mean monthly discounting (if applicable) as well as specifying your plants lifetimes in months.
Expand Down
14 changes: 14 additions & 0 deletions docs/whatsnew/v0-5-2.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,25 @@ v0.5.2 (????)
API changes
###########

* New bool attribute `use_remaining_value` of `oemof.solph.EnergySystem`

New features
############

* Allow for evaluating differences in the remaining vs. the original value
for multi-period investments.

Documentation
#############

Bug fixes
#########

* Fix handling of investment annuities and fixed costs for multi-period models:
Limit to costs that occur within the optimization horizon to prevent a
bias towards investments happening earlier in the optimization horizon.
* Fix bugs in multi-period documentation.

Testing
#######

Expand All @@ -25,3 +35,7 @@ Contributors
############

* Patrik Schönfeldt
* Johannes Kochems
* Julian Endres
* Hendrik Huyskens
* Raul Ciria Aylagas
68 changes: 51 additions & 17 deletions src/oemof/solph/_energy_system.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,10 @@ class EnergySystem(es.EnergySystem):
For a standard model, periods are not (to be) declared, i.e. None.
A list with one entry is derived, i.e. [0].
use_remaining_value : bool
If True, compare the remaining value of an investment to the
original value (only applicable for multi-period models)
kwargs
"""

Expand All @@ -71,6 +75,7 @@ def __init__(
timeincrement=None,
infer_last_interval=None,
periods=None,
use_remaining_value=False,
**kwargs,
):
# Doing imports at runtime is generally frowned upon, but should work
Expand Down Expand Up @@ -160,7 +165,8 @@ def __init__(
timeindex=timeindex, timeincrement=timeincrement, **kwargs
)

if periods is not None:
self.periods = periods
if self.periods is not None:
msg = (
"CAUTION! You specified the 'periods' attribute for your "
"energy system.\n This will lead to creating "
Expand All @@ -171,9 +177,10 @@ def __init__(
"please report them."
)
warnings.warn(msg, debugging.SuspiciousUsageWarning)
self.periods = periods
self._extract_periods_years()
self._extract_periods_matrix()
self._extract_periods_years()
self._extract_periods_matrix()
self._extract_end_year_of_optimization()
self.use_remaining_value = use_remaining_value

def _extract_periods_years(self):
"""Map years in optimization to respective period based on time indices
Expand All @@ -183,13 +190,12 @@ def _extract_periods_years(self):
start of the optimization run and starting with 0.
"""
periods_years = [0]
if self.periods is not None:
start_year = self.periods[0].min().year
for k, v in enumerate(self.periods):
if k >= 1:
periods_years.append(v.min().year - start_year)
start_year = self.periods[0].min().year
for k, v in enumerate(self.periods):
if k >= 1:
periods_years.append(v.min().year - start_year)

self.periods_years = periods_years
self.periods_years = periods_years

def _extract_periods_matrix(self):
"""Determines a matrix describing the temporal distance to each period.
Expand All @@ -200,13 +206,41 @@ def _extract_periods_matrix(self):
between each investment period to each decommissioning period.
"""
periods_matrix = []
if self.periods is not None:
period_years = np.array(self.periods_years)
for v in period_years:
row = period_years - v
row = np.where(row < 0, 0, row)
periods_matrix.append(row)
self.periods_matrix = np.array(periods_matrix)
period_years = np.array(self.periods_years)
for v in period_years:
row = period_years - v
row = np.where(row < 0, 0, row)
periods_matrix.append(row)
self.periods_matrix = np.array(periods_matrix)

def _extract_end_year_of_optimization(self):
"""Extract the end of the optimization in years
Attribute `end_year_of_optimization` of int is set.
"""
duration_last_period = self.get_period_duration(-1)
self.end_year_of_optimization = (
self.periods_years[-1] + duration_last_period
)

def get_period_duration(self, period):
"""Get duration of a period in full years
Parameters
----------
period : int
Period for which the duration in years shall be obtained
Returns
-------
int
Duration of the period
"""
return (
self.periods[period].max().year
- self.periods[period].min().year
+ 1
)


def create_time_index(
Expand Down
Loading

0 comments on commit 119e9cb

Please sign in to comment.