Skip to content

Commit

Permalink
Merge pull request #240 from OSeMOSYS/issue-237
Browse files Browse the repository at this point in the history
Update Objective Cost Calculation
  • Loading branch information
willu47 authored Nov 12, 2024
2 parents c2f104c + 72744e5 commit 097fa2d
Show file tree
Hide file tree
Showing 4 changed files with 604 additions and 10 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ Changelog
Version 1.1.4
=============
- Add result calculations for ``DiscountedCapitalInvestment``, ``DiscountedCostByTechnology``, and ``DiscountedOperationalCost``
- Add result calculations for ``CapitalInvestmentStorage``, ``DiscountedCapitalInvestmentStorage``, ``DiscountedCostByStorage`` and ``DiscountedSalvageValueStorage``
- Correct ``TotalDiscountedCost`` calculation to account for storage costs

Version 1.1.3
===========================
Expand Down
21 changes: 21 additions & 0 deletions src/otoole/preprocess/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,11 @@ AnnualVariableOperatingCost:
type: result
dtype: float
default: 0
CapitalInvestmentStorage:
indices: [REGION, STORAGE, YEAR]
type: result
dtype: float
default: 0
CapitalInvestment:
indices: [REGION, TECHNOLOGY, YEAR]
type: result
Expand All @@ -342,11 +347,22 @@ Demand:
type: result
dtype: float
default: 0
DiscountedCapitalInvestmentStorage:
short_name: DiscountedCapitalInvestStorage
indices: [REGION, STORAGE, YEAR]
type: result
dtype: float
default: 0
DiscountedCapitalInvestment:
indices: [REGION, TECHNOLOGY, YEAR]
type: result
dtype: float
default: 0
DiscountedCostByStorage:
indices: [REGION, STORAGE, YEAR]
type: result
dtype: float
default: 0
DiscountedCostByTechnology:
indices: [REGION, TECHNOLOGY, YEAR]
type: result
Expand All @@ -362,6 +378,11 @@ DiscountedSalvageValue:
type: result
dtype: float
default: 0
DiscountedSalvageValueStorage:
indices: [REGION, STORAGE, YEAR]
type: result
dtype: float
default: 0
DiscountedTechnologyEmissionsPenalty:
short_name: DiscountedTechEmissionsPenalty
indices: [REGION, TECHNOLOGY, YEAR]
Expand Down
253 changes: 248 additions & 5 deletions src/otoole/results/result_package.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,14 @@ def __init__(
"AnnualTechnologyEmissionByMode": self.annual_technology_emission_by_mode,
"AnnualVariableOperatingCost": self.annual_variable_operating_cost,
"CapitalInvestment": self.capital_investment,
"CapitalInvestmentStorage": self.capital_investment_storage,
"Demand": self.demand,
"DiscountedCapitalInvestment": self.discounted_capital_investment,
"DiscountedCapitalInvestmentStorage": self.discounted_capital_investment_storage,
"DiscountedCostByStorage": self.discounted_storage_cost,
"DiscountedCostByTechnology": self.discounted_technology_cost,
"DiscountedOperationalCost": self.discounted_operational_cost,
"DiscountedSalvageValueStorage": self.discounted_salvage_value_storage,
"DiscountedTechnologyEmissionsPenalty": self.discounted_tech_emis_pen,
"ProductionByTechnology": self.production_by_technology,
"ProductionByTechnologyAnnual": self.production_by_technology_annual,
Expand Down Expand Up @@ -347,6 +351,35 @@ def capital_investment(self) -> pd.DataFrame:

return data[(data != 0).all(1)]

def capital_investment_storage(self) -> pd.DataFrame:
"""CapitalInvestmentStorage
Notes
-----
From the formulation::
r~REGION, s~STORAGE, y~YEAR,
CapitalCostStorage[r,s,y] * NewStorageCapacity[r,s,y]
~VALUE;
"""
try:
capital_cost_storage = self["CapitalCostStorage"]
new_capacity_storage = self["NewStorageCapacity"]

except KeyError as ex:
raise KeyError(self._msg("CapitalInvestmentStorage", str(ex)))

capital_investment_storage = capital_cost_storage.mul(
new_capacity_storage, fill_value=0
)

data = capital_investment_storage

if not data.empty:
data = data.groupby(by=["REGION", "STORAGE", "YEAR"]).sum()

return data[(data != 0).all(1)]

def demand(self) -> pd.DataFrame:
"""Demand
Expand Down Expand Up @@ -440,6 +473,54 @@ def discounted_capital_investment(self) -> pd.DataFrame:

return data[(data != 0).all(1)]

def discounted_capital_investment_storage(self) -> pd.DataFrame:
"""DiscountedCapitalInvestmentStorage
Notes
-----
From the formulation::
r~REGION, s~STORAGE, y~YEAR,
DiscountedCapitalInvestmentStorage[r,s,y] :=
CapitalCostStorage[r,s,y] * NewCapacity[r,t,y] / DiscountFactor[r,y]
Alternatively, can be written as::
r~REGION, s~STORAGE, y~YEAR,
DiscountedCapitalInvestmentStorage[r,s,y] := UndiscountedCapitalInvestmentStorage[r,s,y] / DiscountFactor[r,y]
"""

try:
discount_rate_storage = self["DiscountRateStorage"]
year_df = self["YEAR"].copy(deep=True)
region_df = self["REGION"].copy(deep=True)

years = year_df["VALUE"].tolist()
regions = region_df["VALUE"].tolist()
capital_investment_storage = self["CapitalInvestmentStorage"]

storages = self.get_unique_values_from_index(
[
capital_investment_storage,
],
"STORAGE",
)

except KeyError as ex:
raise KeyError(self._msg("DiscountedCapitalInvestmentStorage", str(ex)))

dfs = discount_factor_storage(
regions, storages, years, discount_rate_storage, 0.0
)

data = capital_investment_storage.div(dfs, fill_value=0.0)

if not data.empty:
data = data.groupby(by=["REGION", "STORAGE", "YEAR"]).sum()

return data[(data != 0).all(1)]

def discounted_operational_cost(self) -> pd.DataFrame:
"""DiscountedOperationalCosts
Expand Down Expand Up @@ -510,6 +591,88 @@ def discounted_operational_cost(self) -> pd.DataFrame:

return data[(data != 0).all(1)]

def discounted_storage_cost(self) -> pd.DataFrame:
"""TotalDiscountedCostByStorage
Notes
-----
From the formulation::
r~REGION, s~STORAGE, y~YEAR,
TotalDiscountedStorageCost[r,s,y]:=
(
CapitalCostStorage[r,s,y] * NewStorageCapacity[r,s,y] / DiscountFactorStorage[r,s,y] -
SalvageValueStorage[r,s,y] /
(
(1+DiscountRateStorage[r,s])^(max{yy in YEAR} max(yy)-min{yy in YEAR} min(yy)+1))
)
)
Alternatively, can be written as::
r~REGION, s~STORAGE, y~YEAR,
TotalDiscountedStorageCost[r,s,y]:=
DiscountedCapitalInvestmentStorage[r,s,y] - DiscountedSalvageValueStorage[r,s,y]
"""

try:
discounted_capital_investment_storage = self[
"DiscountedCapitalInvestmentStorage"
]
discounted_salvage_value_storage = self["DiscountedSalvageValueStorage"]

except KeyError as ex:
raise KeyError(self._msg("TotalDiscountedCostByStorage", str(ex)))

discounted_storage_costs = discounted_capital_investment_storage.sub(
discounted_salvage_value_storage, fill_value=0.0
)

data = discounted_storage_costs

if not data.empty:
data = data.groupby(by=["REGION", "STORAGE", "YEAR"]).sum()
return data[(data != 0).all(1)]

def discounted_salvage_value_storage(self) -> pd.DataFrame:
"""DiscountedSalvageValueStorage
Notes
-----
From the formulation::
DiscountedSalvageValueStorage[r,s,y] = SalvageValueStorage[r,s,y] / ((1+DiscountRateStorage[r,s])^(max{yy in YEAR} max(yy)-min{yy in YEAR} min(yy)+1)))
"""

try:
salvage_value_storage = self["SalvageValueStorage"]
discount_rate_storage = self["DiscountRateStorage"]
year_df = self["YEAR"].copy(deep=True)
region_df = self["REGION"].copy(deep=True)
storage_df = self["STORAGE"].copy(deep=True)

years = year_df["VALUE"].tolist()
regions = region_df["VALUE"].tolist()
storages = storage_df["VALUE"].tolist()

except KeyError as ex:
raise KeyError(self._msg("DiscountedSalvageValueStorage", str(ex)))

df_storage_salvage = discount_factor_storage_salvage(
regions, storages, years, discount_rate_storage
)

discounted_salvage_value_storage = salvage_value_storage.div(
df_storage_salvage, fill_value=0
)

data = discounted_salvage_value_storage

if not data.empty:
data = data.groupby(by=["REGION", "STORAGE", "YEAR"]).sum()

return data[(data != 0).all(1)]

def discounted_technology_cost(self) -> pd.DataFrame:
"""TotalDiscountedCostByTechnology
Expand Down Expand Up @@ -774,20 +937,52 @@ def total_discounted_cost(self) -> pd.DataFrame:
/ (DiscountFactorMid[r,y])
+ CapitalCost[r,t,y] * NewCapacity[r,t,y] * CapitalRecoveryFactor[r,t] * PvAnnuity[r,t] / (DiscountFactor[r,y])
+ DiscountedTechnologyEmissionsPenalty[r,t,y] - DiscountedSalvageValue[r,t,y])
+ sum{s in STORAGE}
+ sum{r in REGION, s in STORAGE, y in YEAR}
(
CapitalCostStorage[r,s,y] * NewStorageCapacity[r,s,y] / (DiscountFactorStorage[r,s,y])
- CapitalCostStorage[r,s,y] * NewStorageCapacity[r,s,y] / (DiscountFactorStorage[r,s,y]
CapitalCostStorage[r,s,y] * NewStorageCapacity[r,s,y] / (DiscountFactorStorage[r,s,y]
- SalvageValueStorage[r,s,y] / ((1+DiscountRateStorage[r,s])^(max{yy in YEAR} max(yy)-min{yy in YEAR} min(yy)+1))
)
) ~VALUE;
Alternatively, can be written as::
r~REGION, y~YEAR,
TotalDiscountedCost[r,y] :=
sum{t in TECHNOLOGY} TotalDiscountedCostByTechnology[r,t,y] + sum{s in STORAGE} TotalDiscountedStorageCost[r,s,y]
"""
try:
discounted_cost_by_technology = self["DiscountedCostByTechnology"]

except KeyError as ex:
raise KeyError(self._msg("TotalDiscountedCost", str(ex)))

data = discounted_cost_by_technology
discounted_tech = (
discounted_cost_by_technology.droplevel("TECHNOLOGY")
.reset_index()
.groupby(["REGION", "YEAR"])
.sum()
)

try:
discounted_cost_by_storage = self["DiscountedCostByStorage"]

discounted_storage = (
discounted_cost_by_storage.droplevel("STORAGE")
.reset_index()
.groupby(["REGION", "YEAR"])
.sum()
)
except KeyError as ex: # storage not always included
LOGGER.debug(ex)

discounted_storage = pd.DataFrame(
columns=["REGION", "YEAR", "VALUE"]
).set_index(["REGION", "YEAR"])

total_discounted_cost = discounted_tech.add(
discounted_storage, fill_value=0
).astype(float)

data = total_discounted_cost

if not data.empty:
data = data.groupby(by=["REGION", "YEAR"]).sum()
Expand Down Expand Up @@ -1090,3 +1285,51 @@ def discount_factor_storage(
return pd.DataFrame(
[], columns=["REGION", "STORAGE", "YEAR", "VALUE"]
).set_index(["REGION", "STORAGE", "YEAR"])


def discount_factor_storage_salvage(
regions: List,
storages: List,
years: List,
discount_rate_storage: pd.DataFrame,
) -> pd.DataFrame:
"""Discount Factor used for salvage value claculations
Arguments
---------
regions: list
storages: list
years: list
discount_rate_storage: pd.DataFrame
Notes
-----
From the formulation::
((1+DiscountRateStorage[r,s])^(1+max{yy in YEAR} max(yy)-min{yy in YEAR} min(yy)));
"""

if discount_rate_storage.empty:
raise ValueError(
"Cannot calculate discount_factor_storage_salvage due to missing discount rate"
)

if regions and years:
index = pd.MultiIndex.from_product(
[regions, storages, years], names=["REGION", "STORAGE", "YEAR"]
)
discount_fac_storage_salv = discount_rate_storage.reindex(index)

max_year = max(years)
min_year = min(years)

discount_fac_storage_salv["VALUE"] = (1 + discount_fac_storage_salv).pow(
1 + max_year - min_year
)

return discount_fac_storage_salv

else:
return pd.DataFrame(
[], columns=["REGION", "STORAGE", "YEAR", "VALUE"]
).set_index(["REGION", "STORAGE", "YEAR"])
Loading

0 comments on commit 097fa2d

Please sign in to comment.