Skip to content

Commit

Permalink
Add multi-year information to advanced concepts in the doc (#1082)
Browse files Browse the repository at this point in the history
* Add multi-year docs

* improve docs

* minor edit

* remove files

* Add error
  • Loading branch information
gnawin authored Sep 20, 2024
1 parent 5fc28a5 commit d74a797
Show file tree
Hide file tree
Showing 5 changed files with 107 additions and 0 deletions.
1 change: 1 addition & 0 deletions docs/make.jl
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ pages = [
"Standard model features" => Any[
"Unit Commitment" => joinpath("advanced_concepts", "unit_commitment.md"),
"Investment Optimization" => joinpath("advanced_concepts", "investment_optimization.md"),
"Multi-year Investments" => joinpath("advanced_concepts", "multi-year.md"),
"Reserves" => joinpath("advanced_concepts", "reserves.md"),
"Ramping" => joinpath("advanced_concepts", "ramping.md"),
"Lossless nodal DC power flows" => joinpath("advanced_concepts", "Lossless_DC_power_flow.md"),
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
53 changes: 53 additions & 0 deletions docs/src/advanced_concepts/multi-year.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# Multi-year investments

Multi-year investments refer to making investment decisions at different points in time, such that a pathway of investments can be modeled. This is particularly useful when long-term scenarios are modeled, but modeling each year is not practical. Or in a business case, investment decisions are supposed to be made in different years which has an impact on the cash flow.

There are two tutorials related to multi-year investments: [Capacity planning Tutorial](https://spine-tools.github.io/SpineOpt.jl/latest/tutorial/capacity_planning/) and [Multi-year investments](https://spine-tools.github.io/SpineOpt.jl/latest/tutorial/capacity_planning/#Multi-year-investments). This section covers the concepts of multi-year investments in SpineOpt, but we highly recommend checking out these tutorial for a more thorough understanding of how the model is set up.

## Basic idea

SpineOpt offers flexibility to the users so that different things can be modeled given specific set-ups and inputs of the model. This flexibility can be greatly illustrated by the multi-year investments modeling. We apply the same mathematical formulation for any capacity planning exercises, as shown in [Capacity planning Tutorial](https://spine-tools.github.io/SpineOpt.jl/latest/tutorial/capacity_planning/). For the multi-year model, what you would need differently from a single-year model is mainly the specification of the temporal structure, i.e., the investment period and operational period, and the rest works very much similarly to a single-year model.

## Economic representation

### Parameters
It can be tricky to put the correct cost parameters into the model since factors like discounting and end-of-lifetime effects have to be taken into account. For that purpose, SpineOpt has incorporated some dedicated parameters for economic representation. Set `use_economic_representation` to `true` will activate these paramters.

**Discounted annuities**

This factor translates the overnight costs of investment into discounted (to the discount_year) annual payments, distributed over the total lifetime of the investment. Investment payments are assumed to increase linearly over the lead-time, and decrease linearly towards the end of the economic lifetime. This is also illustrated here:

![image](figs_multi-year/discounted_annuities.png)

For this purpose, we first calculate first the fraction of payment per year (e.g. something like 0.25, 0.5, 0.75,1 over the lead time; 1 for the economic lifetime minus the lead time, and 0.75, 0.5, 0.25 and 0 for the remaining economic lifetime). Each payment fraction is then multiplied by the discounting factor of the payment year with respect to the discounting year (e.g. start of optimization).

**Salvage fraction**

As we consider (discounted and annuitized) overnight costs in the objective function, it can happen that the lifetime of a unit exceeds the model horizon. In such cases, the salvage fraction needs to be deducted from the objective function. In principle, this means, that the annuities "already paid", which exceed the modelling horizon, are recuperated.

**Discounted durations**

The discounted duration is used to discount operational costs within a certain investment period to the discount year (e.g. beginning of the optimization). If milestone years are used for investments, the discounted duration is calculated for each investment period as defined by investment temporal blocks, otherwise, it will be calculated on a yearly basis.

**Technology specific discount factors**

The technology specific discount factor can be used, if e.g. investments in a certain technology are particularly risky. The default value for this parameter is 1.

### Adaptions objective terms

When `use_economic_representation` is set to `true`:

- Investment costs are multiplied with discounted annuties conversion factor and the technological discount factor and (1 - salvage fraction).

- Operational cost terms are multiplied with the discounted duration factor.

### Additional information

More information can be found in the following files.

- [Economic representation in SpineOpt](https://github.com/Spine-project/SpineOpt.jl/files/9130471/Spine_invest.3.pdf) contains the details of the calculation of the economic prameters. Note that this document covers more concepts than what is currently available in SpineOpt (the available ones are the parameters listed above and the adaptions in the objective), the rest is under development.

- [Economic parameters calculation tool](https://github.com/Spine-project/SpineOpt.jl/files/9130469/parameter_illustration.2.xlsx) is an excel tool that you can use to calculate the economic parameters on your own.

!!! Warning
Please also note that the use of economic representation in SpineOpt does not support rolling horizon and Benders' decomposition, which warrants future improvements.
5 changes: 5 additions & 0 deletions src/data_structure/economic_structure.jl
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@
"""
function generate_economic_structure!(m; log_level=3)
use_economic_representation(model=m.ext[:spineopt].instance) || return
if !isnothing(roll_forward(model=m.ext[:spineopt].instance))
error("Using economic representation with rolling horizon is currently not supported.")
elseif model_type(model=m.ext[:spineopt].instance) === :spineopt_benders
error("Using economic representation with Benders' decomposition is currently not supported.")
end
!isempty(
[
model__default_investment_temporal_block()
Expand Down
48 changes: 48 additions & 0 deletions test/data_structure/check_economic_structure.jl
Original file line number Diff line number Diff line change
Expand Up @@ -280,4 +280,52 @@
expected_coe_obj = (1 - salvage_frac) * conv_to_disc_annuities * tech_fac * inv_cost
@test expected_coe_obj observed_coe_obj rtol = 1e-6
end
@testset "test rolling error exception" begin
_load_test_data(url_in, test_data)
discnt_year = Dict("type" => "date_time", "data" => "2020-01-01T00:00:00")
discnt_rate = 0.05
tech_discnt_rate = 0.85
use_mlstne_year = false
candidate_unts = 1
inv_cost = 2
object_parameter_values = [
["model", "instance", "discount_rate", discnt_rate],
["model", "instance", "discount_year", discnt_year],
["model", "instance", "use_milestone_years", use_mlstne_year],
["model", "instance", "use_economic_representation", true],
["model", "instance", "roll_forward", Dict("type" =>"duration","data"=>"1D")],
["unit", "unit_ab", "candidate_units", candidate_unts],
["unit", "unit_ab", "unit_investment_cost", inv_cost],
["unit", "unit_ab", "unit_discount_rate_technology_specific", tech_discnt_rate],
["unit", "unit_ab", "unit_lead_time", Dict("type" => "duration", "data" => "1Y")],
["unit", "unit_ab", "unit_investment_tech_lifetime", Dict("type" => "duration", "data" => "5Y")],
["unit", "unit_ab", "unit_investment_econ_lifetime", Dict("type" => "duration", "data" => "5Y")],
]
SpineInterface.import_data(url_in; object_parameter_values=object_parameter_values)
@test_throws ErrorException run_spineopt(url_in; optimize=false, log_level=1)
end
@testset "test Benders error exception" begin
_load_test_data(url_in, test_data)
discnt_year = Dict("type" => "date_time", "data" => "2020-01-01T00:00:00")
discnt_rate = 0.05
tech_discnt_rate = 0.85
use_mlstne_year = false
candidate_unts = 1
inv_cost = 2
object_parameter_values = [
["model", "instance", "discount_rate", discnt_rate],
["model", "instance", "discount_year", discnt_year],
["model", "instance", "use_milestone_years", use_mlstne_year],
["model", "instance", "use_economic_representation", true],
["model", "instance", "model_type", "spineopt_benders"],
["unit", "unit_ab", "candidate_units", candidate_unts],
["unit", "unit_ab", "unit_investment_cost", inv_cost],
["unit", "unit_ab", "unit_discount_rate_technology_specific", tech_discnt_rate],
["unit", "unit_ab", "unit_lead_time", Dict("type" => "duration", "data" => "1Y")],
["unit", "unit_ab", "unit_investment_tech_lifetime", Dict("type" => "duration", "data" => "5Y")],
["unit", "unit_ab", "unit_investment_econ_lifetime", Dict("type" => "duration", "data" => "5Y")],
]
SpineInterface.import_data(url_in; object_parameter_values=object_parameter_values)
@test_throws ErrorException run_spineopt(url_in; optimize=false, log_level=1)
end
end

0 comments on commit d74a797

Please sign in to comment.