Open nailend opened 9 months ago
Hello @nailend,
I see what you are aiming for. Maybe I have not fully understood it, but wouldn't what I just commented for #987 maybe also address and potentially even solve this problem?
timeincrement
would be 1, right?objective_weighting
of 10, I think the current implementation would already cover it, wouldn't it?discount_rate
, i.e. you can set it to 0 in case your discount rate reflects only inflation rates. (There are some discussions about proper "social" discount rates, but this is what I think makes sense and use for my analyses.) Or if you have nominal cost values, you could pass a time series of present values of your costs in hourly resolution which you have to calculate in a preprocessing step. Of course, the latter is somewhat hacky, so I see why you want to address this inside the code base.For the suggested adjustment in the variable_costs
calculation, I see no problems except for some additional code duplication.
For the adjustment of the method get_period_duration()
, I fear that we might run into trouble for the last period. If its length was only one year, let's assume its year 60, we would have range(60, 60)
, so an empty object. This now is problematic for investments in the last period as this range object is also needed for iteration for the investment annuities and the fixed costs and used in an annuity routine which cannot deal with a duration of 0, see #982.
Long story short: One should think of a safe(r) way for get_period_duration()
, the remainder sounds reasonable.
- If you use one representative year to represent 10 years,
timeincrement
would be 1, right?
yes!
- If you then explicitly specified an
objective_weighting
of 10, I think the current implementation would already cover it, wouldn't it?
I guess, I haven't fully understood, what the effect of changing 'objective_weighting means for the discount rate.
- Of course, you have to take care of proper discounting. ... ... the latter is somewhat hacky, so I see why you want to address this inside the code base.
Exactly!
For the suggested adjustment in the
variable_costs
calculation, I see no problems except for some additional code duplication. For the adjustment of the methodget_period_duration()
, I fear that we might run into trouble for the last period. If its length was only one year, let's assume its year 60, we would haverange(60, 60)
, so an empty object. This now is problematic for investments in the last period as this range object is also needed for iteration for the investment annuities and the fixed costs and used in an annuity routine which cannot deal with a duration of 0, see #982.
I am not sure which range()
you are referring to, but in calculation of the variable_costs
the range should be range(0,1)
and therefore just add variable costs for the last year without discounting it. (I have adapted the function slightly to be able to use -1
for selecting the last period). With this, I also don't see any complications with the investment_flow_block
. Maybe you can link the range()
you are referring to?
@jokochems I think I mixed up the if conditions. In the last period, your former code will be used, so there should be no problem if there was none before. Just in all other periods, I changed the method.
Sorry for the confusion!
I don't really get the issue. Can't you e.g. just use six representative years, each standing for ten years of your optimisation horizon? (If the issue is resolved, please close it.)
I don't really get the issue. Can't you e.g. just use six representative years, each standing for ten years of your optimisation horizon? (If the issue is resolved, please close it.)
That's exactly what I want to do, but currently the variable costs for the implicit years would not be accounted for. You can probably also do this with the objective_weighting
, but I am not sure how to derive the correct value for six years and the discounting (6 * (1 + m.discount_rate) ** (-1) * (1 + m.discount_rate) ** (-2) .... * (1 + m.discount_rate) ** (-6)
?)
I would also see this as a frequently used feature for long term multi-period-investments and therefore useful. It's not that much of code changes, is it?
Currently, no costs would be considered automatically for the implicit years. Probably, this is also the most transparent way to work. Disregarding the current implementation, I would suggest the following solution:
CAPEX:
OPEX:
In my opinion, the way to implement this is to disable automatic discounting for the OPEX. This would be useful anyway, as you often get prognosis for future energy costs discounted to today's values.
Currently, no costs would be considered automatically for the implicit years. Probably, this is also the most transparent way to work. Disregarding the current implementation, I would suggest the following solution:
CAPEX:
- Increase the discount rate, e.g. from 0.02 to 0.22 (1.22 = 1.02^10).
- Give lifetimes in multiples of 10 years, so that the deprecation matches.
OPEX:
- Calculate the discounted prices for every year.
- Use sums over the 10 year periods for optimisation.
In my opinion, the way to implement this is to disable automatic discounting for the OPEX. This would be useful anyway, as you often get prognosis for future energy costs discounted to today's values.
Hi, @p-snft. Thank you. I do see your point and I think, I commented similar stuff above.
I have some remarks:
oemof.tools
, but I think, there might be a use case as long-term simulations blow up computation times and unless you have a big cluster computing node at hand, you might go for something with reduced complexity, e.g. using representative years.discount_rate
attribute to 0.discount_rate
rather than doing extensive precalculationsAlso, I like your ideas on structural improvements and am looking forward towards the discussions at the next dev meeting. Thank you!
I am not sure which
range()
you are referring to, but in calculation of thevariable_costs
the range should berange(0,1)
and therefore just add variable costs for the last year without discounting it. (I have adapted the function slightly to be able to use-1
for selecting the last period). With this, I also don't see any complications with theinvestment_flow_block
. Maybe you can link therange()
you are referring to?
I meant the ones to control for fixed costs being limited to the optimization horizon, for instance here: https://github.com/oemof/oemof-solph/blob/119e9cbab35f8289fcde3e0539486d53dd7e99bb/src/oemof/solph/flows/_investment_flow_block.py#L1025C29-L1028C30
The other remark was on the newly introduced end_year_of_optimization
attribute for an energy system and the problem if this is the same as the start year of the last period.
But I think, this has been resolved as you altered the implementation of get_period_duration
. I think, it even makes more sense as you defined it now - thank you. :-)
The yet implemented multi-period-investment was designed by the assumption to provide time series for every year (equivalent period) within the optimization horizon. Due to a long optimization horizon (~60 years), we need to use representative years/time series for the years within a multi-year-period.
Fortunately, @jokochems well-designed approach already facilitates most of this. Currently, only the flow costs (
variable
andfixed
) for the flows are not handled accordingly. This could be done by adjusting values for theobjective weighting
but might collide with its use in the TSAM integration (as discussed in #987) which we also want to use.Therefore, I propose the boolean-flag
use_representative_year_in_multi_year_periods
forsolph.EnergySystem
. This would lead to a further condition at the calculation for thevariable_costs
, here.In case it's True, variable costs would be calculated like this:
The variable costs for the representative/explicit year (with timeseries provided) would then be discounted and used for the implicit years of the period (without timeseries provided).
Further
get_period_duration()
from #982 would need some adaption for multi-year-periods as well: