Skip to content

Commit

Permalink
Merge pull request #1111 from oemof/revision/remove-discounting-funct…
Browse files Browse the repository at this point in the history
…ionality

Remove internal discounting functionality
  • Loading branch information
p-snft authored Dec 17, 2024
2 parents 9be114c + 7b37fe5 commit a4e698d
Show file tree
Hide file tree
Showing 11 changed files with 93 additions and 239 deletions.
11 changes: 0 additions & 11 deletions docs/usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1201,8 +1201,6 @@ but with a few minor additions and modifications in the investment object itself
free to reinvest or choose another option to fill up the missing capacity.
* You can define an initial `age` if you have `existing` capacity. If you do not specify anything, the default value 0 will be used,
meaning your `existing` capacity has just been newly invested.
* You can define an `interest_rate` that the investor you model has, i.e. the return he desires expressed as the weighted
average osts of capital (wacc) and used for calculating annuities in the model itself.
* You also can define `fixed_costs`, i.e. costs that occur every period independent of the plants usage.
Here is an example
Expand All @@ -1218,7 +1216,6 @@ Here is an example
maximum=1000,
ep_costs=1e6,
lifetime=30,
interest_rate=0.06,
fixed_costs=100,
),
variable_costs=3,
Expand Down Expand Up @@ -1252,7 +1249,6 @@ This would mean that for investments in the particular period, these values woul
maximum=1000,
ep_costs=[1e6, 1.1e6],
lifetime=30,
interest_rate=0.06,
fixed_costs=[100, 110],
),
variable_costs=3,
Expand Down Expand Up @@ -1327,13 +1323,6 @@ Besides the `invest` variable, new variables are introduced as well. These are:
.. note::
* You can specify a `discount_rate` for the model. If you do not do so, 0.02 will be used as a default, corresponding
to sort of a social discount rate. If you work with costs in real terms, discounting is obsolete, so define
`discount_rate = 0` in that case.
* You can specify an `interest_rate` for every investment object. If you do not do so, it will be chosen the same
as the model's `discount_rate`. You could use this default to model a perfect competition administered by some sort of
social planner, but even in a social planner setting, you might want to deviate from the `discount_rate`
value and/or discriminate among technologies with different risk profiles and hence different interest requirements.
* For storage units, the `initial_content` is not allowed combined with multi-period investments.
The storage inflow and outflow are forced to zero until the storage unit is invested into.
* You can specify periods of different lengths, but the frequency of your timeindex needs to be consistent. Also,
Expand Down
2 changes: 2 additions & 0 deletions src/oemof/solph/_energy_system.py
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,8 @@ def __init__(
self._extract_periods_matrix()
self._extract_end_year_of_optimization()
self.use_remaining_value = use_remaining_value
else:
self.end_year_of_optimization = 1

def _extract_periods_years(self):
"""Map years in optimization to respective period based on time indices
Expand Down
20 changes: 2 additions & 18 deletions src/oemof/solph/_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,6 @@ class Model(po.ConcreteModel):
Solph looks for these groups in the given energy system and uses them
to create the constraints of the optimization problem.
Defaults to `Model.CONSTRAINT_GROUPS`
discount_rate : float or None
The rate used for discounting in a multi-period model.
A 2% discount rate needs to be defined as 0.02.
objective_weighting : array like (optional)
Weights used for temporal objective function
expressions. If nothing is passed, `timeincrement` will be used which
Expand Down Expand Up @@ -84,17 +81,6 @@ class Model(po.ConcreteModel):
rc : `pyomo.core.base.suffix.Suffix` or None
Store the reduced costs of the model if pyomo suffix is set to IMPORT
Note
----
* The discount rate is only applicable for a multi-period model.
* If you want to work with costs data in nominal terms,
you should specify a discount rate.
* By default, there is a discount rate of 2% in a multi-period model.
* If you want to provide your costs data in real terms,
just specify `discount_rate = 0`, i.e. effectively there will be
no discounting.
**The following basic sets are created**:
Expand Down Expand Up @@ -134,7 +120,7 @@ class Model(po.ConcreteModel):
InvestNonConvexFlowBlock,
]

def __init__(self, energysystem, discount_rate=None, **kwargs):
def __init__(self, energysystem, **kwargs):
super().__init__()

# Check root logger. Due to a problem with pyomo the building of the
Expand Down Expand Up @@ -190,9 +176,7 @@ def __init__(self, energysystem, discount_rate=None, **kwargs):
self.dual = None
self.rc = None

if discount_rate is not None:
self.discount_rate = discount_rate
elif energysystem.periods is not None:
if energysystem.periods is not None:
self._set_discount_rate_with_warning()
else:
pass
Expand Down
7 changes: 0 additions & 7 deletions src/oemof/solph/_options.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,6 @@ class Investment:
age : int, :math:`a`
Units start age, given in years at the beginning of the optimization;
only applicable for multi-period models
interest_rate : float, :math:`ir`
Interest rate for calculating annuities when investing in a particular
unit; only applicable for multi-period models.
If nothing else is specified, the interest rate is the same as the
model discount rate of the multi-period model.
fixed_costs : float or list of float, :math:`c_{fixed}(p)`
Fixed costs in each period (given in nominal terms);
only applicable for multi-period models
Expand Down Expand Up @@ -95,7 +90,6 @@ def __init__(
overall_minimum=None,
lifetime=None,
age=0,
interest_rate=0,
fixed_costs=None,
custom_attributes=None,
):
Expand All @@ -111,7 +105,6 @@ def __init__(
self.overall_minimum = overall_minimum
self.lifetime = lifetime
self.age = age
self.interest_rate = interest_rate
self.fixed_costs = sequence(fixed_costs)

for attribute in custom_attributes.keys():
Expand Down
63 changes: 19 additions & 44 deletions src/oemof/solph/components/_generic_storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -414,26 +414,18 @@ class GenericStorageBlock(ScalarBlock):
**The following parts of the objective function are created:**
*Standard model*
* :attr: `storage_costs` not 0
.. math::
\sum_{t \in \textrm{TIMEPOINTS} > 0} c_{storage}(t) \cdot E(t)
*Multi-period model*
* :attr:`fixed_costs` not None
* :attr:`fixed_costs` not 0
.. math::
\displaystyle \sum_{pp=0}^{year_{max}} E_{nom}
\cdot c_{fixed}(pp) \cdot DF^{-pp}
\cdot c_{fixed}(pp)
where:
* :math:`DF=(1+dr)` is the discount factor with discount rate :math:`dr`.
* :math:`year_{max}` denotes the last year of the optimization
where :math:`year_{max}` denotes the last year of the optimization
horizon, i.e. at the end of the last period.
""" # noqa: E501
Expand Down Expand Up @@ -585,26 +577,19 @@ def _objective_expression(self):
r"""
Objective expression for storages with no investment.
Note
----
* For standard models, this adds nothing as variable costs are
already added in the Block :py:class:`~.SimpleFlowBlock`.
* For multi-period models, fixed costs may be introduced
and added here.
* Fixed costs (will not have an impact on the actual optimisation).
* Variable costs for storage content.
"""
m = self.parent_block()

fixed_costs = 0

if m.es.periods is not None:
for n in self.STORAGES:
if valid_sequence(n.fixed_costs, len(m.PERIODS)):
fixed_costs += sum(
n.nominal_storage_capacity
* n.fixed_costs[pp]
* (1 + m.discount_rate) ** (-pp)
for pp in range(m.es.end_year_of_optimization)
)
for n in self.STORAGES:
if valid_sequence(n.fixed_costs, len(m.PERIODS)):
fixed_costs += sum(
n.nominal_storage_capacity * n.fixed_costs[pp]
for pp in range(m.es.end_year_of_optimization)
)
self.fixed_costs = Expression(expr=fixed_costs)

storage_costs = 0
Expand Down Expand Up @@ -1096,8 +1081,6 @@ class GenericInvestmentStorageBlock(ScalarBlock):
Lifetime for investments in storage capacity"
":math:`a`", "`flows[i, o].investment.age`", "
Initial age of existing capacity / energy"
":math:`ir`", "`flows[i, o].investment.interest_rate`", "
interest rate for investment"
":math:`\tau(t)`", "", "Duration of time step"
":math:`t_u`", "", "Time unit of losses :math:`\beta(t)`,
:math:`\gamma(t)`, :math:`\delta(t)` and timeincrement :math:`\tau(t)`"
Expand Down Expand Up @@ -1785,7 +1768,7 @@ def _objective_expression(self):
)
for n in self.CONVEX_INVESTSTORAGES:
lifetime = n.investment.lifetime
interest = n.investment.interest_rate
interest = 0
if interest == 0:
warn(
msg.format(m.discount_rate),
Expand All @@ -1807,7 +1790,7 @@ def _objective_expression(self):
)
investment_costs_increment = (
self.invest[n, p] * annuity * present_value_factor
) * (1 + m.discount_rate) ** (-m.es.periods_years[p])
)
remaining_value_difference = (
self._evaluate_remaining_value_difference(
m,
Expand All @@ -1825,7 +1808,7 @@ def _objective_expression(self):

for n in self.NON_CONVEX_INVESTSTORAGES:
lifetime = n.investment.lifetime
interest = n.investment.interest_rate
interest = 0
if interest == 0:
warn(
msg.format(m.discount_rate),
Expand All @@ -1848,7 +1831,7 @@ def _objective_expression(self):
investment_costs_increment = (
self.invest[n, p] * annuity * present_value_factor
+ self.invest_status[n, p] * n.investment.offset[p]
) * (1 + m.discount_rate) ** (-m.es.periods_years[p])
)
remaining_value_difference = (
self._evaluate_remaining_value_difference(
m,
Expand All @@ -1874,9 +1857,7 @@ def _objective_expression(self):
m.es.periods_years[p] + lifetime,
)
fixed_costs += sum(
self.invest[n, p]
* n.investment.fixed_costs[pp]
* (1 + m.discount_rate) ** (-pp)
self.invest[n, p] * n.investment.fixed_costs[pp]
for pp in range(
m.es.periods_years[p],
range_limit,
Expand All @@ -1891,9 +1872,7 @@ def _objective_expression(self):
m.es.end_year_of_optimization, lifetime - age
)
fixed_costs += sum(
n.investment.existing
* n.investment.fixed_costs[pp]
* (1 + m.discount_rate) ** (-pp)
n.investment.existing * n.investment.fixed_costs[pp]
for pp in range(range_limit)
)

Expand Down Expand Up @@ -1965,15 +1944,11 @@ def _evaluate_remaining_value_difference(
self.invest[n, p]
* (remaining_annuity - original_annuity)
* present_value_factor_remaining
) * (1 + m.discount_rate) ** (-end_year_of_optimization)
)
if nonconvex:
return convex_investment_costs + self.invest_status[
n, p
] * (n.investment.offset[-1] - n.investment.offset[p]) * (
1 + m.discount_rate
) ** (
-end_year_of_optimization
)
] * (n.investment.offset[-1] - n.investment.offset[p])
else:
return convex_investment_costs
else:
Expand Down
Loading

0 comments on commit a4e698d

Please sign in to comment.