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

Remove internal discounting functionality #1111

Merged
merged 7 commits into from
Dec 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading