|
| 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 |
0 commit comments