-
Notifications
You must be signed in to change notification settings - Fork 14
Add precomputed_load_profile generator #34
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -559,6 +559,96 @@ def __call__( | |
|
|
||
| return load_profiles | ||
|
|
||
| class PrecomputedProfile(LoadScenarioGeneratorBase): | ||
| """Reads precomputed bus-demand scenarios and returns them as (n_buses, n_scenarios, 2). | ||
|
|
||
| CSV/XLSX columns: | ||
| - load_scenario : int scenario index (0..S-1) | ||
| - load : int BUS INDEX (0..n_buses-1) in *continuous indexing* used by Network | ||
| (i.e., after mapping to 0..n_buses-1) | ||
| - p_mw, q_mvar : floats | ||
| """ | ||
|
|
||
| def __init__(self, scenario_file: str): | ||
| self.scenario_file = scenario_file | ||
|
|
||
| def _read(self) -> pd.DataFrame: | ||
| p = self.scenario_file | ||
| if p.lower().endswith((".xlsx", ".xls")): | ||
| return pd.read_excel(p) | ||
| return pd.read_csv(p) | ||
|
|
||
| def __call__( | ||
| self, | ||
| net, # type: Network | ||
| n_scenarios: int, | ||
| scenario_log: str, | ||
| max_iter: int, # unused, kept for interface compatibility | ||
| ) -> np.ndarray: | ||
| df = self._read() | ||
|
|
||
| required = {"load_scenario", "load", "p_mw", "q_mvar"} | ||
| missing = required - set(df.columns) | ||
| if missing: | ||
| raise ValueError( | ||
| f"Scenario file must contain columns {sorted(required)}; missing {sorted(missing)}. " | ||
| f"Got {list(df.columns)}" | ||
| ) | ||
|
|
||
| df["load_scenario"] = df["load_scenario"].astype(int) | ||
| df["load"] = df["load"].astype(int) | ||
|
|
||
| n_buses = int(np.asarray(net.buses).shape[0]) # or len(net.Pd) | ||
| if n_buses == 0: | ||
| raise ValueError("Network has zero buses.") | ||
|
|
||
| file_scenarios = int(df["load_scenario"].max()) + 1 | ||
| if n_scenarios > file_scenarios: | ||
| raise ValueError( | ||
| f"Requested n_scenarios={n_scenarios}, but file provides only {file_scenarios}." | ||
| ) | ||
|
|
||
| # bus index validation (continuous indices 0..n_buses-1) | ||
| if int(df["load"].min()) < 0: | ||
| raise ValueError("Scenario file contains negative bus indices in column 'load'.") | ||
| max_bus = int(df["load"].max()) | ||
| if max_bus >= n_buses: | ||
| raise ValueError( | ||
| f"Scenario file references bus index {max_bus}, but network has {n_buses} buses." | ||
| ) | ||
|
|
||
| df = df[df["load_scenario"].between(0, n_scenarios - 1)] | ||
|
|
||
| # Optional strict coverage check (comment out if you allow missing => 0) | ||
| expected = n_buses * n_scenarios | ||
| actual = df[["load_scenario", "load"]].drop_duplicates().shape[0] | ||
| if actual != expected: | ||
| raise ValueError( | ||
| f"Scenario file does not fully specify all (scenario, bus) pairs: " | ||
| f"expected {expected}, got {actual}. " | ||
| f"If you want unspecified buses to default to 0, remove this check." | ||
| ) | ||
|
|
||
| # Allocate output: (n_buses, n_scenarios, 2) | ||
| out = np.zeros((n_buses, n_scenarios, 2), dtype=float) | ||
|
|
||
| # If duplicates exist, keep last | ||
| df = df.sort_index().drop_duplicates(subset=["load_scenario", "load"], keep="last") | ||
|
||
|
|
||
| s = df["load_scenario"].to_numpy(dtype=int) | ||
| b = df["load"].to_numpy(dtype=int) | ||
| out[b, s, 0] = df["p_mw"].to_numpy(dtype=float) | ||
| out[b, s, 1] = df["q_mvar"].to_numpy(dtype=float) | ||
|
|
||
| if scenario_log: | ||
| with open(scenario_log, "a") as f: | ||
| f.write( | ||
| f"precomputed_profile: scenarios={n_scenarios}, buses={n_buses}, " | ||
| f"path={self.scenario_file}\n" | ||
| ) | ||
|
|
||
| return out | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| """ | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why drop duplicates?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll modify it to ensure strict checks