1+ from pathlib import Path
2+
3+ from loguru import logger
4+ import pandas as pd
5+ import click
6+
7+ from pypsse .common import EXPORTS_SETTINGS_FILENAME , SIMULATION_SETTINGS_FILENAME
8+ from pypsse .models import ExportFileOptions , SimulationSettings
9+ from pypsse .simulator import Simulator
10+
11+ @click .argument (
12+ "project-path" ,
13+ )
14+ @click .option (
15+ "-s" ,
16+ "--simulations-file" ,
17+ required = False ,
18+ default = SIMULATION_SETTINGS_FILENAME ,
19+ show_default = True ,
20+ help = "scenario toml file to run (over rides default)" ,
21+ )
22+ @click .option (
23+ "-e" ,
24+ "--export-file-path" ,
25+ required = False ,
26+ default = "./filtered_results.csv" ,
27+ show_default = True ,
28+ help = "path for exporting filtered results" ,
29+ )
30+ @click .option (
31+ "-l" ,
32+ "--load-filter" ,
33+ help = "applies load filters if set to true" ,
34+ is_flag = True ,
35+ default = False ,
36+ show_default = True ,
37+ )
38+ @click .option (
39+ '--load/--no-load' ,
40+ required = False ,
41+ show_default = True ,
42+ help = "filter by load [bool]" ,
43+ )
44+ @click .option (
45+ "-g" ,
46+ "--gen-filter" ,
47+ help = "applies generator filters if set to true" ,
48+ is_flag = True ,
49+ default = False ,
50+ show_default = True ,
51+ )
52+ @click .option (
53+ '--generation/--no-generation' ,
54+ required = False ,
55+ show_default = True ,
56+ help = "filter by generation [bool]" ,
57+ )
58+ @click .option (
59+ '--comp-load/--no-comp-load' ,
60+ required = False ,
61+ show_default = True ,
62+ help = "filter by composite load models [bool]" ,
63+ )
64+ @click .option (
65+ "-b" ,
66+ "--apply-bounds" ,
67+ help = "applies load and generation limit bounds if set to true" ,
68+ is_flag = True ,
69+ default = False ,
70+ show_default = True ,
71+ )
72+ @click .option (
73+ '--load-bounds' ,
74+ required = False ,
75+ show_default = True ,
76+ default = "0/10000" ,
77+ help = "bounds for load [example 10/100]" ,
78+ )
79+ @click .option (
80+ '--gen-bounds' ,
81+ required = False ,
82+ show_default = True ,
83+ default = "0/10000" ,
84+ help = "bounds for generation " ,
85+ )
86+ @click .command ()
87+ def explore (project_path , simulations_file , export_file_path , load_filter , load , gen_filter , generation , apply_bounds , comp_load , load_bounds , gen_bounds ):
88+ """Runs a valid PyPSSE simulation."""
89+
90+ export_file_path = Path (export_file_path )
91+
92+ file_path = Path (project_path ) / simulations_file
93+ msg = "Simulation file not found. Use -s to choose a valid settings file"
94+ "if its name differs from the default file name."
95+ assert file_path .exists (), msg
96+ x = Simulator .from_setting_files (file_path )
97+ buses = set (x .raw_data .buses )
98+ quantities = {
99+ 'Loads' : ['MVA' , 'FmA' , 'FmB' , 'FmC' , 'FmD' , 'Fel' , 'PFel' ],
100+ 'Induction_generators' : ['MVA' ],
101+ 'Machines' : ['MVA' , 'PERCENT' ],
102+ }
103+ results = x .sim .read_subsystems (quantities , buses )
104+ had_comp_models = False
105+ if "Loads_FmA" in results :
106+ had_comp_models = True
107+
108+ load_dict = {}
109+ bus_load_real = {}
110+ bus_load_imag = {}
111+ is_comp_load = {}
112+ for bus , ld_id in x .raw_data .loads :
113+ if bus not in load_dict :
114+ load_dict [bus ] = []
115+ bus_load_real [bus ] = 0
116+ bus_load_imag [bus ] = 0
117+ is_comp_load = []
118+
119+ if had_comp_models :
120+ is_comp = True if f'{ bus } _{ ld_id } ' in results ["Loads_FmA" ] else False
121+ else :
122+ is_comp = False
123+ is_comp_load .append (is_comp )
124+ load_dict [bus ].append (ld_id )
125+ key = f"{ ld_id } _{ bus } " if len (ld_id ) == 1 else f"{ ld_id } _{ bus } "
126+ bus_load_real [bus ] += results ["Loads_MVA" ][key ].real
127+ bus_load_imag [bus ] += results ["Loads_MVA" ][key ].imag
128+
129+ generator_dict = {}
130+ bus_gen = {}
131+ for bus , gen_id in x .raw_data .generators :
132+ if bus not in load_dict :
133+ generator_dict [bus ] = []
134+ bus_gen [bus ] = 0
135+ key = f"{ gen_id } _{ bus } " if len (gen_id ) == 1 else f"{ gen_id } _{ bus } "
136+ generator_dict [bus ].append (gen_id )
137+ bus_gen [bus ] += results ["Machines_MVA" ][key ]
138+
139+ results = {
140+ "bus" : [],
141+ "has load" : [],
142+ "is load comp" : [],
143+ "total P load [MW]" : [],
144+ "total Q load [MVar]" : [],
145+ "has generation" : [],
146+ "total generation [MVA]" : [],
147+ }
148+ for bus in x .raw_data .buses :
149+ results ["bus" ].append (bus )
150+ results ["has load" ].append (True if bus in load_dict else False )
151+ results ["is load comp" ].append (is_comp_load [bus ] if bus in is_comp_load else False )
152+ results ["total P load [MW]" ].append (bus_load_real [bus ] if bus in bus_load_real else 0 )
153+ results ["total Q load [MVar]" ].append (bus_load_imag [bus ] if bus in bus_load_imag else 0 )
154+ results ["has generation" ].append (True if bus in generator_dict else False )
155+ results ["total generation [MVA]" ].append (bus_gen [bus ] if bus in bus_gen else 0 )
156+
157+
158+ results = pd .DataFrame (results )
159+ if filter :
160+ if load_filter and load :
161+ results = results [results ["has load" ] == True ]
162+ elif load_filter and not load :
163+ results = results [results ["has load" ] == False ]
164+
165+ if gen_filter and generation :
166+ results = results [results ["has generation" ] == True ]
167+ elif gen_filter and not generation :
168+ results = results [results ["has generation" ] == False ]
169+
170+ if load_filter and comp_load :
171+ results = results [results ["is load comp" ] == True ]
172+ elif load_filter and not comp_load :
173+ results = results [results ["is load comp" ] == False ]
174+
175+ load_lower , load_upper = [float (x ) for x in load_bounds .split ("/" )]
176+ gen_lower , gen_upper = [float (x ) for x in gen_bounds .split ("/" )]
177+ if apply_bounds :
178+ results = results [(results ["total P load [MW]" ] >= load_lower ) & (results ["total P load [MW]" ] <= load_upper )]
179+ results = results [(results ["total generation [MVA]" ] >= gen_lower ) & (results ["total generation [MVA]" ] <= gen_upper )]
180+
181+ results .to_csv (export_file_path )
182+
183+
184+
185+
186+
0 commit comments