Skip to content

Commit c02e69e

Browse files
authored
Merge pull request #42 from Open-ISP/template-energy-policy
2 parents 150cf68 + fefd3ba commit c02e69e

16 files changed

+584
-0
lines changed

src/ispypsa/iasr_table_caching/local_cache.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,11 +53,25 @@
5353
"technology_specific_lcfs",
5454
] + _GENERATOR_PROPERTY_TABLES
5555

56+
_POLICY_REQUIRED_TABLES = [
57+
"vic_renewable_target_trajectory",
58+
"qld_renewable_target_trajectory",
59+
"powering_australia_plan_trajectory",
60+
"capacity_investment_scheme_renewable_trajectory",
61+
"capacity_investment_scheme_storage_trajectory",
62+
"nsw_roadmap_storage_trajectory",
63+
"vic_storage_target_trajectory",
64+
"vic_offshore_wind_target_trajectory",
65+
"nsw_roadmap_renewable_trajectory",
66+
"tas_renewable_target_trajectory",
67+
]
68+
5669
REQUIRED_TABLES = (
5770
_NETWORK_REQUIRED_TABLES
5871
+ _GENERATORS_STORAGE_REQUIRED_SUMMARY_TABLES
5972
+ _GENERATORS_REQUIRED_PROPERTY_TABLES
6073
+ _NEW_ENTRANTS_COST_TABLES
74+
+ _POLICY_REQUIRED_TABLES
6175
)
6276

6377

src/ispypsa/templater/create_template.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55
from ispypsa.templater.dynamic_generator_properties import (
66
_template_generator_dynamic_properties,
77
)
8+
from ispypsa.templater.energy_policy_targets import (
9+
_template_energy_policy_targets,
10+
)
811
from ispypsa.templater.flow_paths import (
912
_template_regional_interconnectors,
1013
_template_sub_regional_flow_paths,
@@ -148,6 +151,10 @@ def create_ispypsa_inputs_template(
148151

149152
template.update(dynamic_generator_property_templates)
150153

154+
energy_policy_targets = _template_energy_policy_targets(iasr_tables, scenario)
155+
156+
template.update(energy_policy_targets)
157+
151158
return template
152159

153160

Lines changed: 241 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,241 @@
1+
import logging
2+
import re
3+
from pathlib import Path
4+
5+
import pandas as pd
6+
7+
from .mappings import _TEMPLATE_RENEWABLE_ENERGY_TARGET_MAP
8+
9+
10+
def _template_energy_policy_targets(
11+
iasr_tables: dict[str : pd.DataFrame], scenario: str
12+
) -> dict[str, pd.DataFrame]:
13+
"""Creates ISPyPSA templates for energy policy targets including NEM-wide and state-level policies.
14+
15+
Args:
16+
iasr_tables: Dict of tables from the IASR workbook that have been parsed using
17+
`isp-workbook-parser`.
18+
scenario: Scenario obtained from the model configuration
19+
20+
Returns:
21+
`dict[pd.DataFrame]`: Templates for renewable share targets, powering australia share targets (by scenario)
22+
renewable generation targets, and technology capacity targets
23+
"""
24+
logging.info("Creating templates for energy policy targets")
25+
26+
# Create templates for energy policy targets
27+
renewable_share_targets = _template_renewable_share_targets(iasr_tables)
28+
29+
power_aus_plan = iasr_tables["powering_australia_plan_trajectory"]
30+
power_aus_plan = _template_powering_australia_plan(power_aus_plan, scenario)
31+
32+
renewable_generation_targets = _template_renewable_generation_targets(iasr_tables)
33+
34+
technology_capacity_targets = _template_technology_capacity_targets(iasr_tables)
35+
36+
return {
37+
"renewable_share_targets": renewable_share_targets,
38+
"powering_australia_plan": power_aus_plan,
39+
"renewable_generation_targets": renewable_generation_targets,
40+
"technology_capacity_targets": technology_capacity_targets,
41+
}
42+
43+
44+
def _template_renewable_share_targets(
45+
iasr_tables: dict[str : pd.DataFrame],
46+
) -> pd.DataFrame:
47+
"""Creates ISPyPSA templates for renewable share targets from trajectory CSVs.
48+
Uses TEMPLATE_RENEWABLE_ENERGY_TARGET_MAP to identify files and their
49+
corresponding regions.
50+
51+
Args:
52+
iasr_tables: Dict of tables from the IASR workbook that have been parsed using
53+
`isp-workbook-parser`.
54+
55+
Returns:
56+
`pd.DataFrame`: Template containing renewable share targets with columns for
57+
financial year, region_id, policy_id, and percentage values in decimal form
58+
"""
59+
logging.info("Creating template for renewable share targets")
60+
state_renewable_share_targets = []
61+
62+
# Get mapping for this function
63+
target_files = _TEMPLATE_RENEWABLE_ENERGY_TARGET_MAP[
64+
"template_renewable_share_targets"
65+
]
66+
67+
for target in target_files:
68+
df = iasr_tables[target["csv"]]
69+
70+
df = df.melt(id_vars=df.columns[0], var_name="FY", value_name="pct")
71+
df = df[df[df.columns[0]].str.contains("share", case=False)]
72+
df["region_id"] = target["region_id"]
73+
df["policy_id"] = target["policy_id"]
74+
df["pct"] = df["pct"].astype(float)
75+
76+
state_renewable_share_targets.append(
77+
df[["FY", "region_id", "policy_id", "pct"]]
78+
)
79+
80+
merged_state_renewable_share_targets = pd.concat(
81+
state_renewable_share_targets, ignore_index=True
82+
)
83+
merged_state_renewable_share_targets["FY"] = merged_state_renewable_share_targets[
84+
"FY"
85+
].str.replace("-", "_")
86+
87+
return merged_state_renewable_share_targets
88+
89+
90+
def _template_powering_australia_plan(
91+
power_aus_plan: Path | str, scenario: str
92+
) -> pd.DataFrame:
93+
"""Creates ISPyPSA template for the Powering Australia Plan renewable share
94+
trajectories for selected scenarios.
95+
96+
Args:
97+
powering_aus: pd.DataFrame table from IASR workbook specifying Powering Australia Plan renewable share targets.
98+
scenario: Scenario obtained from the model configuration
99+
100+
Returns:
101+
`pd.DataFrame`: Template containing Powering Australia Plan targets
102+
with columns for financial year, policy_id and percentage values in
103+
decimal form for the selected scenario
104+
"""
105+
logging.info("Creating template for Powering Australia Plan")
106+
107+
# Remove rows containing "Notes" in the first column
108+
power_aus_plan = power_aus_plan[
109+
~power_aus_plan.iloc[:, 0].str.contains("Notes", case=False, na=False)
110+
]
111+
112+
# Filter for rows where the first column matches the specified scenario
113+
power_aus_plan = power_aus_plan[power_aus_plan.iloc[:, 0].eq(scenario)]
114+
115+
# Drop the first column (scenario name) to keep only year values
116+
power_aus_plan = power_aus_plan.iloc[:, 1:].reset_index(drop=True)
117+
118+
# Melt the dataframe, excluding the first column from id_vars
119+
power_aus_plan = power_aus_plan.melt(var_name="FY", value_name="pct").dropna(
120+
subset=["pct"]
121+
)
122+
123+
# Convert percentage to decimal if needed
124+
power_aus_plan["pct"] = power_aus_plan["pct"].astype(float)
125+
126+
power_aus_plan["FY"] = power_aus_plan["FY"].str.replace("-", "_")
127+
128+
# append new column which is the policy_id
129+
power_aus_plan["policy_id"] = "power_aus"
130+
return power_aus_plan
131+
132+
133+
def _template_technology_capacity_targets(
134+
iasr_tables: dict[str : pd.DataFrame],
135+
) -> pd.DataFrame:
136+
"""Creates ISPyPSA templates for technology capacity targets including
137+
CIS renewable target and storage and offshore wind trajectories.
138+
Uses TEMPLATE_RENEWABLE_ENERGY_TARGET_MAP to identify
139+
files and their corresponding regions.
140+
141+
Args:
142+
iasr_tables: Dict of tables from the IASR workbook that have been parsed using
143+
`isp-workbook-parser`.
144+
Returns:
145+
`pd.DataFrame`: Template containing technology capacity trajectories
146+
with columns for financial year, region_id and capacity in MW
147+
"""
148+
logging.info("Creating template for technology capacity targets")
149+
150+
technology_capacity_targets = []
151+
target_files = _TEMPLATE_RENEWABLE_ENERGY_TARGET_MAP[
152+
"template_technology_capacity_targets"
153+
]
154+
155+
for target in target_files:
156+
df = iasr_tables[target["csv"]]
157+
# Extract technology type from the row containing "target (MW)"
158+
target_row_mask = df.iloc[:, 0].str.contains("target", case=False) & df.iloc[
159+
:, 0
160+
].str.contains("MW", case=False)
161+
162+
target_row_idx = df.index[target_row_mask][0]
163+
# Create a new dataframe with just FY and capacity
164+
values_df = pd.DataFrame(
165+
{"FY": df.columns[1:], "capacity_mw": df.iloc[target_row_idx, 1:]}
166+
)
167+
168+
values_df["capacity_mw"] = values_df["capacity_mw"].astype(float)
169+
values_df["region_id"] = target["region_id"]
170+
values_df["policy_id"] = target["policy_id"]
171+
172+
technology_capacity_targets.append(values_df)
173+
174+
merged_technology_capacity_targets = pd.concat(
175+
technology_capacity_targets, ignore_index=True
176+
)
177+
merged_technology_capacity_targets["FY"] = merged_technology_capacity_targets[
178+
"FY"
179+
].str.replace("-", "_")
180+
181+
merged_technology_capacity_targets = merged_technology_capacity_targets.sort_values(
182+
["region_id", "policy_id", "FY"]
183+
).reset_index(drop=True)
184+
185+
return merged_technology_capacity_targets
186+
187+
188+
def _template_renewable_generation_targets(
189+
iasr_tables: dict[str : pd.DataFrame],
190+
) -> pd.DataFrame:
191+
"""Creates ISPyPSA templates for renewable generation targets.
192+
Uses TEMPLATE_RENEWABLE_ENERGY_TARGET_MAP to identify files and their corresponding regions.
193+
194+
Args:
195+
iasr_tables: Dict of tables from the IASR workbook that have been parsed using
196+
`isp-workbook-parser`.
197+
198+
Returns:
199+
`pd.DataFrame`: Template containing renewable capacity trajectories with columns for
200+
financial year, region_id and capacity in MW (converted from GWh)
201+
202+
"""
203+
logging.info("Creating template for renewable generation trajectories")
204+
205+
renewable_generation_targets = []
206+
target_files = _TEMPLATE_RENEWABLE_ENERGY_TARGET_MAP[
207+
"template_renewable_generation_targets"
208+
]
209+
210+
for target in target_files:
211+
df = iasr_tables[target["csv"]]
212+
# Check for GWh in row indices
213+
if not df.iloc[:, 0].str.contains("GWh", case=False).any():
214+
raise ValueError(f"No GWh values found in {target['csv']}.csv")
215+
216+
# if exists, remove the "Notes" row
217+
df = df[~df.iloc[:, 0].str.contains("Notes", case=False)]
218+
219+
renewable_gen_target = df.melt(
220+
id_vars=df.columns[0], var_name="FY", value_name="capacity_gwh"
221+
)
222+
223+
# Convert GWh to MWh
224+
renewable_gen_target["capacity_mwh"] = (
225+
renewable_gen_target["capacity_gwh"].astype(float) * 1000
226+
)
227+
renewable_gen_target["region_id"] = target["region_id"]
228+
renewable_gen_target["policy_id"] = target["policy_id"]
229+
renewable_generation_targets.append(
230+
renewable_gen_target[["FY", "region_id", "policy_id", "capacity_mwh"]]
231+
)
232+
233+
# Combine all dataframes
234+
merged_renewable_generation_targets = pd.concat(
235+
renewable_generation_targets, ignore_index=True
236+
)
237+
merged_renewable_generation_targets["FY"] = merged_renewable_generation_targets[
238+
"FY"
239+
].str.replace("-", "_")
240+
241+
return merged_renewable_generation_targets
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
policy_id,generator
2+
cis_generator,Large scale Solar PV
3+
cis_generator,Wind
4+
cis_generator,Wind - offshore (fixed)
5+
cis_generator,Wind - offshore (floating)
6+
cis_storage,Solar Thermal (15hrs storage)
7+
cis_storage,Pumped Hydro
8+
cis_storage,Hydrogen reciprocating engines
9+
cis_storage,Battery Storage (1hr storage)
10+
cis_storage,Battery Storage (2hrs storage)
11+
cis_storage,Battery Storage (4hrs storage)
12+
cis_storage,Battery Storage (8hrs storage)
13+
nsw_generator,Large scale Solar PV
14+
nsw_generator,Wind
15+
nsw_generator,Wind - offshore (fixed)
16+
nsw_generator,Wind - offshore (floating)
17+
nsw_generator,Solar Thermal (15hrs storage)
18+
nsw_generator,Biomass
19+
nsw_storage,Battery Storage (8hrs storage)
20+
nsw_storage,Pumped Hydro (8hrs storage)
21+
nsw_storage,Pumped Hydro (24hrs storage)
22+
nsw_storage,Pumped Hydro (48hrs storage)
23+
vic_storage,Battery Storage (1hr storage)
24+
vic_storage,Battery Storage (2hrs storage)
25+
vic_storage,Battery Storage (4hrs storage)
26+
vic_storage,Battery Storage (8hrs storage)
27+
vic_storage,Pumped Hydro (8hrs storage)
28+
vic_storage,Pumped Hydro (24hrs storage)
29+
vic_storage,Pumped Hydro (48hrs storage)
30+
vic_offshore_wind,Wind - offshore (fixed)
31+
vic_offshore_wind,Wind - offshore (floating)
32+
qret,Hydro
33+
qret,Large scale Solar PV
34+
qret,Wind
35+
qret,Biomass
36+
qret,Solar Thermal (15hrs storage)
37+
vret,Hydro
38+
vret,Large scale Solar PV
39+
vret,Wind
40+
vret,Wind - offshore (fixed)
41+
vret,Wind - offshore (floating)
42+
vret,Biomass
43+
vret,Solar Thermal (15hrs storage)
44+
tret,Hydro
45+
tret,Large scale Solar PV
46+
tret,Solar Thermal (15hrs storage)
47+
tret,Wind
48+
tret,Wind - offshore (fixed)
49+
tret,Wind - offshore (floating)
50+
power_aus,Hydro
51+
power_aus,Large scale Solar PV
52+
power_aus,Solar Thermal (15hrs storage)
53+
power_aus,Wind
54+
power_aus,Wind - offshore (fixed)
55+
power_aus,Wind - offshore (floating)

0 commit comments

Comments
 (0)