urbanopt-rnm-us 0.4.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,188 +1,477 @@
1
1
  import opendssdirect as dss
2
2
  import pandas as pd
3
+ #import matplotlib
3
4
  import matplotlib.pyplot as plt
4
5
  import numpy as np
5
6
  import sys as sys
6
7
  import math
7
8
  import networkx as nx
8
9
  import opendss_interface
10
+ import seaborn as sns
11
+ import plotly.graph_objects as go
12
+ from shapely.geometry import LineString
13
+ from shapely.geometry import Point
14
+ import geopandas as gpd
15
+
9
16
 
10
17
  class Plot_Lib:
11
- def __init__(self, folder):
12
- self.folder = folder
18
+ def __init__(self, folder,b_numeric_ids):
19
+ """Initialices the folder variables"""
20
+ self.main_folder = folder
21
+ self.folder=folder+'/Validation'
22
+ self.b_numeric_ids=b_numeric_ids
13
23
 
14
24
  def remove_terminal(self,bus):
15
- myopendss_io=opendss_interface.OpenDSS_Interface(self.folder)
25
+ """Removes the terminal from the bus name"""
26
+ myopendss_io=opendss_interface.OpenDSS_Interface(self.folder,self.b_numeric_ids)
16
27
  bus=myopendss_io.remove_terminal(bus)
17
28
  return bus
18
29
 
19
30
 
20
- def plot_hist(self,type,v_value,v_value_period,v_range,num_bins,num_periods,v_limits):
21
- output_file_full_path_fig = self.folder + '/' + type + ' Histogram (p.u.).png'
22
- output_file_full_path_csv = self.folder + '/' + type + ' Histogram (p.u.).csv'
23
- plt.figure
31
+ def plot_hist(self,subfolder,type,v_value,v_value_period,v_range,num_periods,num_bins,v_months):
32
+ """Plots an histogram"""
33
+ # Path and file name
34
+ output_file_full_path_fig = self.folder + '/' + subfolder + '/Figures/' + type + ' Histogram (p.u.).png'
35
+ output_file_full_path_csv = self.folder + '/' + subfolder + '/CSV/' + type + ' Histogram (p.u.).csv'
36
+ # New figure
37
+ # plt.figure
38
+ plt.clf()
39
+ # Activate figure grid
24
40
  plt.grid(True)
25
- v_legend=["" for _ in range(num_periods+2)]
26
- matrix=np.empty((num_bins,num_periods+2)) #Matrix for writting to file (index+periods+yearly)
27
- for j in range(num_periods):
28
- plt.xlim(v_range)
29
- v_weights = np.ones_like(v_value_period[j]) / len(v_value_period[j])
30
- counts, bins = np.histogram(v_value_period[j], range=v_range, bins=num_bins, weights=v_weights)
31
- if j==0:
32
- v_legend[0]=type
33
- matrix[:,0]=bins[:num_bins:]
34
- matrix[:,j+1]=counts
35
- v_legend[j+1]= "M"+str(j+1)
36
- plt.plot(bins[:-1]+(bins[1]-bins[0])*0.5, counts)
37
-
41
+ # Evaluate number of valid periods
42
+ num_valid_periods=0
43
+ for j in v_months:
44
+ #If data in the month
45
+ if not j==[]:
46
+ num_valid_periods=num_valid_periods+1
47
+ # Init variables
48
+ v_legend=["" for _ in range(num_valid_periods+1)]
49
+ matrix=np.empty((num_bins,num_valid_periods+1)) #Matrix for writting to file (index+periods+yearly)
50
+ # Set xlim
51
+ if v_range['display_range']:
52
+ plt.xlim(v_range['display_range'])
53
+ # Incremental index for months with data
54
+ i=0
55
+ # For each month
56
+ for j in v_months:
57
+ #If data in the month
58
+ if not j==[]:
59
+ # Set the weight variable
60
+ v_weights = np.ones_like(v_value_period[j]) / len(v_value_period[j])
61
+ # Calculate the histogram of each month
62
+ counts, bins = np.histogram(v_value_period[j], range=v_range['display_range'], bins=num_bins, weights=v_weights)
63
+ # Update matrix and legend
64
+ # In subsequent iterations
65
+ matrix[:,i]=counts
66
+ v_legend[i]="M"+str(j+1)
67
+ # Plot the month
68
+ plt.plot(bins[:-1]+(bins[1]-bins[0])*0.5, counts)
69
+ # Increment index if data
70
+ i=i+1
71
+ # Set the weight variable
38
72
  v_weights = np.ones_like(v_value) / len(v_value)
39
- counts, bins = np.histogram(v_value, range=v_range, bins=num_bins, weights=v_weights)
40
- matrix[:,num_periods+1]=counts
41
- v_legend[num_periods+1]='Yearly'
73
+ # Calculate the yearly histogram
74
+ counts, bins = np.histogram(v_value, range=v_range['display_range'], bins=num_bins, weights=v_weights)
75
+ # Update matrix and legend
76
+ matrix[:,i]=counts
77
+ v_legend[i]='Yearly'
78
+ # Plot histogram
42
79
  plt.hist(bins[:-1], bins, weights=counts)
43
- plt.legend(v_legend[1:num_periods+2:])
44
- #Write line with the limits
45
- for j in range(len(v_limits)):
46
- h = plt.axvline(v_limits[j], color='r', linestyle='--')
47
- #plt.text(0.76, 120000, '1,596 buses out of limits ', fontsize = 10)
48
- plt.xlabel(type+' (p.u.)')
80
+ # Plot legend
81
+ #plt.legend(v_legend[1:num_periods+2:])
82
+ plt.legend(v_legend)
83
+ # Write line with the limits
84
+ for j in range(len(v_range['limits'])):
85
+ h = plt.axvline(v_range['limits'][j], color='r', linestyle='--')
86
+ # x,y lables
87
+ plt.xlabel(type)
49
88
  plt.ylabel('Frequency (p.u.)')
89
+ # Save to file
50
90
  plt.savefig(output_file_full_path_fig, dpi=300)
51
- plt.show()
52
- #Save to file
91
+ # Display
92
+ # plt.show()
93
+ # Save data to file
53
94
  # Write directly as a CSV file with headers on first line
54
95
  with open(output_file_full_path_csv, 'w') as fp:
55
96
  fp.write(','.join(v_legend) + '\n')
56
- np.savetxt(fp, matrix, '%s', ',')
57
- #Deprecated with dataframes
58
- #pd_values = pd.DataFrame(matrix,index=bins)
59
- #pd_values.to_csv(output_file_full_path)
60
-
61
-
62
- def plot_losses(self,v_subs_losses_yearly,v_line_losses_yearly):
63
- output_file_full_path_fig = self.folder + '/' + 'Losses' + '.png'
64
- plt.figure
65
- plt.plot(sorted(v_subs_losses_yearly,reverse=True))
66
- plt.plot(sorted(v_line_losses_yearly,reverse=True))
67
- plt.plot(sorted(np.add(v_subs_losses_yearly,v_line_losses_yearly),reverse=True))
68
- plt.legend(['Substation losses','Line losses','Total losses'])
97
+ np.savetxt(fp, matrix, '%.7f', ',')
98
+
99
+ def plot_violin(self,subfolder,type,v_value,v_range):
100
+ """Make a figure with a violin plot"""
101
+ # Path and file name
102
+ output_file_full_path_fig = self.folder + '/' + subfolder + '/' + type + ' Violin Plot.png'
103
+ # New figure
104
+ # plt.figure
105
+ plt.clf()
106
+ # Write line with the limits
107
+ for j in range(len(v_range['limits'])):
108
+ h = plt.axhline(v_range['limits'][j], color='r', linestyle='--')
109
+ # Plot violtin
110
+ sns.violinplot(y=v_value, cut=0, color='orange')
111
+ # Strip plot (display points)
112
+ sns.stripplot(y=v_value, color='blue')
113
+ # y label
114
+ plt.ylabel(type)
115
+ # y limit
116
+ if v_range['display_range']:
117
+ plt.ylim(v_range['display_range'])
118
+ # Save figure to file
119
+ plt.savefig(output_file_full_path_fig, dpi=300)
120
+ # Display
121
+ # plt.show()
122
+
123
+ def plot_violin_two_vars(self,subfolder, type,v_value1,v_value2,v_range):
124
+ """Make a figure with a violin plot of two variables (kW/kVAr)"""
125
+ # Path and file name
126
+ output_file_full_path_fig = self.folder + '/' + subfolder + '/' + type + ' Violin Plot.png'
127
+ # New figure
128
+ # plt.figure
129
+ plt.clf()
130
+ # Init variables
131
+ v_data=[]
132
+ v_type=[]
133
+ # Write line with the limits
134
+ for j in range(len(v_range['limits'])):
135
+ h = plt.axhline(v_range['limits'][j], color='r', linestyle='--')
136
+ # Extend lists for yearly var1
137
+ l_type=['kW' for _ in v_value1]
138
+ v_data.extend(v_value1)
139
+ v_type.extend(l_type)
140
+ # Extend lists for yearly var2
141
+ l_type=['kVAr' for _ in v_value2]
142
+ v_data.extend(v_value2)
143
+ v_type.extend(l_type)
144
+ # Display violin plot
145
+ sns.violinplot(y=v_data, hue=v_type, cut=0, split=True, palette = {'kW':'blue','kVAr':'orange'})
146
+ # Set y label
147
+ plt.ylabel(type)
148
+ # Set y limit
149
+ if v_range['display_range']:
150
+ plt.ylim(v_range['display_range'])
151
+ # Save figure to file
152
+ plt.savefig(output_file_full_path_fig, dpi=300)
153
+ # Display
154
+ # plt.show()
155
+
156
+
157
+ def plot_violin_monthly(self,subfolder, type,v_value,v_value_period,v_range,num_periods,v_months):
158
+ """Make a violin plot with the monthly variation"""
159
+ # Path and file name
160
+ output_file_full_path_fig = self.folder + '/' + subfolder + '/' + type + ' Violin Plot.png'
161
+ # New figure
162
+ # plt.figure
163
+ plt.clf()
164
+ # Init variables
165
+ v_data=[]
166
+ v_month=[]
167
+ v_yearly=[]
168
+ # Write line with the limits
169
+ for j in range(len(v_range['limits'])):
170
+ h = plt.axhline(v_range['limits'][j], color='r', linestyle='--')
171
+ # Incremental index for valid months
172
+ i=0
173
+ # For each month
174
+ for j in v_months:
175
+ # If data in the month
176
+ if not j==[]:
177
+ # Extend lists for monthly
178
+ l_month=["M"+str(j+1) for _ in v_value_period[j]]
179
+ l_yearly=['Monthly' for _ in v_value_period[j]]
180
+ v_data.extend(v_value_period[j])
181
+ v_month.extend(l_month)
182
+ v_yearly.extend(l_yearly)
183
+ # Extend lists for yearly
184
+ l_month=["M"+str(j+1) for _ in v_value]
185
+ l_yearly=['Yearly' for _ in v_value]
186
+ v_data.extend(v_value)
187
+ v_month.extend(l_month)
188
+ v_yearly.extend(l_yearly)
189
+ #Increment auto-index if there is data
190
+ i=i+1
191
+ # Display violin plot
192
+ sns.violinplot(x=v_month, y=v_data, hue=v_yearly, cut=0, split=True)
193
+ # Set y label
194
+ plt.ylabel(type)
195
+ # Set y limit
196
+ if v_range['display_range']:
197
+ plt.ylim(v_range['display_range'])
198
+ # Save figure to file
199
+ plt.savefig(output_file_full_path_fig, dpi=300)
200
+ # Display
201
+ # plt.show()
202
+
203
+ def plot_violin_monthly_two_vars(self,subfolder, type,v_value1,v_value1_period,v_value2,v_value2_period,v_range,num_periods,v_months):
204
+ """Make a violin plot for two variables (kW/kVAr) with the monthly variation"""
205
+ # Path and file name
206
+ output_file_full_path_fig = self.folder + '/' + subfolder + '/' + type + ' Violin Plot.png'
207
+ # New figure
208
+ # plt.figure
209
+ plt.clf()
210
+ # Init variables
211
+ v_data=[]
212
+ v_month=[]
213
+ v_type=[]
214
+ # Incremental index for valid months
215
+ i=0
216
+ for j in v_months:
217
+ #If there is data
218
+ if not j==[]:
219
+ # Extend lists for monthly var1
220
+ l_month=["M"+str(j+1) for _ in v_value1_period[j]]
221
+ l_type=['kW' for _ in v_value1_period[j]]
222
+ v_data.extend(v_value1_period[j])
223
+ v_month.extend(l_month)
224
+ v_type.extend(l_type)
225
+ # Extend lists for monthly var2
226
+ l_month=["M"+str(j+1) for _ in v_value2_period[j]]
227
+ l_type=['kVAr' for _ in v_value2_period[j]]
228
+ v_data.extend(v_value2_period[j])
229
+ v_month.extend(l_month)
230
+ v_type.extend(l_type)
231
+ #Increment auto-index if there is data
232
+ i=i+1
233
+ # Extend lists for yearly var1
234
+ l_month=['Yearly' for _ in v_value1]
235
+ l_type=['kW' for _ in v_value1]
236
+ v_data.extend(v_value1)
237
+ v_month.extend(l_month)
238
+ v_type.extend(l_type)
239
+ # Extend lists for yearly var2
240
+ l_month=['Yearly' for _ in v_value2]
241
+ l_type=['kVAr' for _ in v_value2]
242
+ v_data.extend(v_value2)
243
+ v_month.extend(l_month)
244
+ v_type.extend(l_type)
245
+ # Show violin plot
246
+ sns.violinplot(x=v_month, y=v_data, hue=v_type, cut=0, split=True)
247
+ # Show stripplot (points) (disabled because there are too many points)
248
+ # sns.stripplot(x=v_month, y=v_data, hue=v_type, color="k", alpha=0.8)
249
+ # Set x,y lables
250
+ plt.xlabel('Month')
251
+ plt.ylabel(type)
252
+ # Set y limit
253
+ if v_range['display_range']:
254
+ plt.ylim(v_range['display_range'])
255
+ # Save figure to file
256
+ plt.savefig(output_file_full_path_fig, dpi=300)
257
+ # Display
258
+ # plt.show()
259
+
260
+ def plot_duration_curve(self,subfolder, v1_yearly,v2_yearly,b_losses,v_hours):
261
+ """Make a figure with the duration curve (yearly losses or load)"""
262
+ # New figure
263
+ # plt.figure
264
+ plt.clf()
265
+ # Display variable 1
266
+ plt.plot(v_hours,sorted(v1_yearly,reverse=True))
267
+ # Display variable 1
268
+ plt.plot(v_hours,sorted(v2_yearly,reverse=True))
269
+ # If displaying losses
270
+ if b_losses:
271
+ # Plot the added curve (lines + transformers)
272
+ plt.plot(v_hours,sorted(np.add(v1_yearly,v2_yearly),reverse=True))
273
+ # Set file path + name
274
+ output_file_full_path_fig = self.folder + '/' + subfolder + '/' + 'Losses' + '.png'
275
+ # Set legend
276
+ plt.legend(['Substation losses','Line losses','Total losses'])
277
+ # Set y label
278
+ plt.ylabel('Losses (kWh)')
279
+ else: # If displaying load
280
+ # Set file path + name
281
+ output_file_full_path_fig = self.folder + '/' + subfolder + '/' + 'Load' + '.png'
282
+ # Set legend
283
+ plt.legend(['kW','kVAr'])
284
+ # Set y label
285
+ plt.ylabel('Load')
286
+ # Set x label
69
287
  plt.xlabel('Hour (h)')
70
- plt.ylabel('Losses (kWh)')
288
+ # Save figure to file
71
289
  plt.savefig(output_file_full_path_fig)
72
- plt.show()
290
+ # Display
291
+ # plt.show()
73
292
 
74
293
 
75
- def get_graph(self,edges):
76
- graph=nx.Graph()
294
+ def add_edges(self,graph,edges):
295
+ """Add edges to the graph"""
296
+ # Populate it with the edges
77
297
  for idx,element in enumerate(edges):
78
- graph.add_edges_from(element)
298
+ graph.add_edges_from([(element[0],element[1])])
79
299
  return graph
80
300
 
81
- def get_locations(self,graph,bus,locations=[],x_locations_levels={},parent=None,level=0,visited_buses=[]):
82
- #Add to the list of visited buses
301
+ def get_locations(self,graph,bus,locations=[],x_location_max_prev=0,parent=None,level=0,visited_buses=[]):
302
+ """Get the locations of the buses in the graph"""
303
+ # Add to the list of visited buses
83
304
  if visited_buses:
84
305
  visited_buses.append(bus)
85
306
  else:
86
307
  visited_buses=[bus]
87
- #Obtain the buses connected to this one
308
+ # Obtain the buses connected to this one
88
309
  connected_buses = list(graph.neighbors(bus))
89
- #Explore downstream levels
310
+ # Explore downstream levels
90
311
  x_downstream_locations=[]
91
312
  for downstream_bus in connected_buses:
92
- #Remove the terminal from the bus nmae
313
+ # Remove the terminal from the bus name
93
314
  downstream_bus=self.remove_terminal(downstream_bus)
94
- #If the bus was already visited, remove from graph (if activated this would remove loops)
95
- #if downstream_bus!=parent and downstream_bus in visited_buses and graph.has_edge(bus,downstream_bus) and b_remove_loops:
96
- #Remove self loops (possible to happen because of terminals in buses)
315
+ # If the bus was already visited, remove from graph (if activated this would remove loops)
316
+ # if downstream_bus!=parent and downstream_bus in visited_buses and graph.has_edge(bus,downstream_bus) and b_remove_loops:
317
+ # Remove self loops (possible to happen because of terminals in buses)
97
318
  if downstream_bus==bus:
98
319
  graph.remove_edge(bus,downstream_bus)
99
320
  else:
100
- #Explore downstream the graph (recursive search)
321
+ # Explore downstream the graph (recursive search)
101
322
  if downstream_bus!=parent and not downstream_bus in visited_buses:
102
- x_loc,locations=self.get_locations(graph,downstream_bus,locations,x_locations_levels,bus,level+1,visited_buses)
323
+ x_loc,locations,x_location_max_prev=self.get_locations(graph,downstream_bus,locations,x_location_max_prev,bus,level+1,visited_buses)
103
324
  x_downstream_locations.append(x_loc)
104
- #For the upper levels, it takes the average of the downstream buses
325
+ # For the upper levels, it takes the average of the downstream buses
105
326
  if x_downstream_locations:
106
327
  loc=(sum(x_downstream_locations)/len(x_downstream_locations),-level);
107
328
  else:
108
- if x_locations_levels:
109
- #Pick up location from this level or the previous ones
110
- #It is neccesary to sort it, to pick in the above for lev loop the x_next_location from the more downstream level
111
- for lev in sorted(x_locations_levels):
112
- if lev<=level:
113
- x_next_location=x_locations_levels[lev]+1
114
- #Assign location x, y
329
+ if x_location_max_prev:
330
+ # Pick up location from this level or the previous ones
331
+ # It is neccesary to sort it, to pick in the above for lev loop the x_next_location from the more downstream level
332
+ x_next_location=x_location_max_prev+1
333
+ # Assign location x, y
115
334
  loc=(x_next_location,-level)
116
335
  else:
117
- #Default position for first bus
336
+ # Default position for first bus
118
337
  loc=(1,-level)
119
- #Assign x location of this level
120
- x_locations_levels[level]=loc[0]
121
- #update locations
338
+ # Assign x location of this level
339
+ if x_location_max_prev<loc[0]:
340
+ x_location_max_prev=loc[0]
341
+ # update locations
122
342
  if locations:
123
343
  locations[bus]=loc
124
344
  else:
125
345
  locations={bus:loc}
126
- #Return x location of this bus (all locations are provided in locations argument)
127
- return loc[0],locations
128
-
129
-
130
-
131
-
132
- def plot_graph(self,edges,v_dict_voltage,v_range,v_dict_loading,v_range_loading,dict_buses_element):
133
- output_file_full_path_fig = self.folder + '/' + 'Network Violations' + '.png'
134
- #output_file_full_path_fig = folder + '/' + type + '.png'
135
- #Example
136
- graph=nx.Graph()
137
- #graph.add_edges_from([(1,2), (1,3), (1,4), (1,5), (1,2), (2,6), (6,7), (7,1)])
138
- #discard,locations=self.get_locations(graph,1)
139
- graph=self.get_graph(edges)
140
- discard,locations=self.get_locations(graph,'st_mat')
141
- #Obtain number of violations of each node
142
- cmap = plt.cm.get_cmap('jet')
346
+ # Return x location of this bus (all locations are provided in locations argument)
347
+ return loc[0],locations,x_location_max_prev
348
+
349
+
350
+ def get_dict_num_violations_v(self,graph,v_dict_voltage,v_range,v_dict_ids_buses):
351
+ """It obtains number (hours) of voltage violations of each bus"""
352
+ myopendss_io=opendss_interface.OpenDSS_Interface(self.folder,self.b_numeric_ids)
353
+ # Init dict
143
354
  dic_num_violations_v={}
355
+ # For each node
144
356
  for node in graph:
145
- #Truncate list to limits
146
- dic_num_violations_v[node]=0
147
- for idx2,value in enumerate(v_dict_voltage[node]):
148
- if value<v_range[0] or value>=v_range[1]:
149
- dic_num_violations_v[node]=dic_num_violations_v[node]+1
150
- #Make colormap
357
+ # Get dict of violations
358
+ if (self.b_numeric_ids):
359
+ dic_num_violations_v[node]=myopendss_io.get_num_violations(v_dict_voltage[v_dict_ids_buses[node]],v_range,node,None)
360
+ else:
361
+ dic_num_violations_v[node]=myopendss_io.get_num_violations(v_dict_voltage[node],v_range,node,None)
362
+ return dic_num_violations_v
363
+
364
+
365
+ def get_dict_num_violations_l(self,graph,dict_buses_element,v_dict_loading,v_range_loading,v_dict_ids_buses):
366
+ """It obtains number (hours) of violations of each branch"""
367
+ # Obtain number of violations (hours) of each branch
368
+ myopendss_io=opendss_interface.OpenDSS_Interface(self.folder,self.b_numeric_ids)
369
+ # Init dict
370
+ dic_num_violations_l={}
371
+ # For each granch
372
+ for edge in graph.edges():
373
+ # Obtain name from bus1 to bus2
374
+ if not self.b_numeric_ids:
375
+ bus1to2=self.remove_terminal(edge[0])+'-->'+self.remove_terminal(edge[1])
376
+ else:
377
+ bus1to2=self.remove_terminal(v_dict_ids_buses[edge[0]])+'-->'+self.remove_terminal(v_dict_ids_buses[edge[1]])
378
+ # Obtain name from bus2 to bus1
379
+ if not self.b_numeric_ids:
380
+ bus2to1=self.remove_terminal(edge[1])+'-->'+self.remove_terminal(edge[0])
381
+ else:
382
+ bus2to1=self.remove_terminal(v_dict_ids_buses[edge[1]])+'-->'+self.remove_terminal(v_dict_ids_buses[edge[0]])
383
+ # If bus1 to bus2 is the one that exists in the dictionary
384
+ if bus1to2 in dict_buses_element:
385
+ # Set element name
386
+ element=dict_buses_element[bus1to2]
387
+ # Evaluate number of violations
388
+ dic_num_violations_l[edge]=myopendss_io.get_num_violations(v_dict_loading[element],v_range_loading,element,None)
389
+ elif bus2to1 in dict_buses_element: #If bus2 to bus1 is the one that exists in the dictionary
390
+ # Set element name
391
+ element=dict_buses_element[bus2to1]
392
+ # Evaluate number of violations
393
+ dic_num_violations_l[edge]=myopendss_io.get_num_violations(v_dict_loading[element],v_range_loading,element,None)
394
+ else: # If the element does not exist in the dictionary, set number of violations to zero
395
+ dic_num_violations_l[edge]=0
396
+ return dic_num_violations_l
397
+
398
+
399
+
400
+ def plot_graph(self,subfolder,my_closed_edges,my_open_edges,v_dict_voltage,v_range,v_dict_loading,v_range_loading,dict_buses_element,v_dict_buses_ids,v_dict_ids_buses):
401
+ """Plot a graph with the hierarchical representation of the network"""
402
+ # Path and file name
403
+ output_file_full_path_fig = self.folder + '/' + subfolder + '/' + 'Network Violations' + '.png'
404
+ # New figure
405
+ # plt.figure
406
+ plt.clf()
407
+ # Close the previous fig
408
+ plt.close()
409
+ # Create empty graph
410
+ mygraph=nx.Graph()
411
+ # Commented graph example
412
+ # mygraph.add_edges_from([(1,2), (1,3), (1,4), (1,5), (1,2), (2,6), (6,7), (7,1)])
413
+ # discard,locations=self.get_locations(mygraph,1)
414
+ # Populate the graph with the closed edges
415
+ mygraph=self.add_edges(mygraph,my_closed_edges)
416
+ mygraph_only_closed_edges=mygraph.copy()
417
+ # Populate the graph with the open edges
418
+ mygraph=self.add_edges(mygraph,my_open_edges)
419
+ # Get the locations of the buses
420
+ # WARNING: Locations are extracter from closed edges. This requires that there are no disconnected buses (e.g. two open switches in series)
421
+ if self.b_numeric_ids:
422
+ discard,locations,x_location_max_prev=self.get_locations(mygraph_only_closed_edges,str(0)) #function get_all_buses_ids sets slack bus to str(0)
423
+ else:
424
+ discard,locations,x_location_max_prev=self.get_locations(mygraph_only_closed_edges,'st_mat') #RNM-US specific (the slack bus of the distribution system is always "st_mat")
425
+ # Create colormap
426
+ cmap = plt.cm.get_cmap('jet')
427
+ # Take subset of colormap to avoid dark colors (with overlap with text)
428
+ cmap = cmap.from_list('trunc({n},{a:.3f},{b:.3f})'.format(n=cmap.name, a=0.15, b=0.75),cmap(np.linspace(0.25, 0.75, 120)))
429
+ # Init colormap variable
151
430
  color_map_v=[]
431
+ # Get number of voltage valiations
432
+ dic_num_violations_v=self.get_dict_num_violations_v(mygraph,v_dict_voltage,v_range,v_dict_ids_buses)
433
+ # Obtain the maximum number of voltage violations in a bus
152
434
  max_violations_v=max(dic_num_violations_v.values())
153
- for node in graph:
435
+ # if no violations, use only one color in the colormap
436
+ if max_violations_v==0:
437
+ cmap_v = cmap.from_list('trunc({n},{a:.3f},{b:.3f})'.format(n=cmap.name, a=0, b=0),cmap(np.linspace(0, 0, 2)))
438
+ else:
439
+ cmap_v = cmap
440
+ # Creater a colormap (color_map_v) o colour the buses according to their number of violations
441
+ for node in mygraph:
442
+ # If there are no violations, set all intensities to zero
154
443
  if max_violations_v==0:
155
444
  intensity=0
156
445
  else:
157
446
  intensity=dic_num_violations_v[node]/max_violations_v
158
- #color_map_v.append(cmap_nodes(intensity))
447
+ # Add the intensity of the bus to the colormap
159
448
  color_map_v.append(intensity)
160
- #Obtain number of violations of each branch
161
- dic_num_violations_l={}
162
- for edge in graph.edges():
163
- #Truncate list to limits
164
- dic_num_violations_l[edge]=0
165
- #bus1to2=self.remove_terminal(edge[0])+'-->'+self.remove_terminal(edge[1])
166
- #bus2to1=self.remove_terminal(edge[1])+'-->'+self.remove_terminal(edge[0])
167
- bus1to2=self.remove_terminal(edge[0])+'-->'+self.remove_terminal(edge[1])
168
- bus2to1=self.remove_terminal(edge[1])+'-->'+self.remove_terminal(edge[0])
169
- if bus1to2 in dict_buses_element:
170
- element=dict_buses_element[bus1to2]
171
- else:
172
- element=dict_buses_element[bus2to1]
173
- for idx2,value in enumerate(v_dict_loading[element]):
174
- if value<v_range_loading[0] or value>=v_range_loading[1]:
175
- dic_num_violations_l[edge]=dic_num_violations_l[edge]+1
176
- #Make colormap
177
- color_map_l=[]
449
+ # Init colormap variables
450
+ color_map_l_closed=[]
451
+ color_map_l_open=[]
452
+ # Get number of loading valiations
453
+ dic_num_violations_l=self.get_dict_num_violations_l(mygraph,dict_buses_element,v_dict_loading,v_range_loading,v_dict_ids_buses)
454
+ # Obtain the maximum number of loading violations in a branch
178
455
  max_violations_l=max(dic_num_violations_l.values())
179
- for edge in graph.edges():
456
+ # if no violations, use only one color in the colormap
457
+ if max_violations_l==0:
458
+ cmap_l = cmap.from_list('trunc({n},{a:.3f},{b:.3f})'.format(n=cmap.name, a=0, b=0.01),cmap(np.linspace(0, 0.01, 2)))
459
+ else:
460
+ cmap_l = cmap
461
+ # Creater a colormap (color_map_l) o colour the closed branches according to their number of violations
462
+ for edge in mygraph_only_closed_edges.edges():
463
+ # If there are no violations, set all intensities to zero
180
464
  if max_violations_l==0:
181
465
  intensity=0
182
466
  else:
183
467
  intensity=dic_num_violations_l[edge]/max_violations_l
184
- #color_map_v.append(cmap_nodes(intensity))
185
- color_map_l.append(intensity)
468
+ # Add the intensity of the branch to the colormap
469
+ color_map_l_closed.append(intensity)
470
+ # Creater a colormap for the open branches
471
+ for edge in mygraph.edges():
472
+ # Add the intensity of the branch to the colormap
473
+ if not edge in mygraph_only_closed_edges.edges():
474
+ color_map_l_open.append(0) #As it is a different graph, order may be different, but it does not matter because they are all zero
186
475
  #Obtain min and max of x locations
187
476
  max_x=0
188
477
  max_y=0
@@ -191,14 +480,22 @@ class Plot_Lib:
191
480
  max_x=locations[name][0]
192
481
  if (max_y<-locations[name][1]):
193
482
  max_y=-locations[name][1]
194
- #plt.figure(figsize=(max_x,max_y))
483
+ # Define the size of the figure
195
484
  unitary_size=0.5
196
485
  ratio=16/9
197
486
  maximum=max(max_x,max_y)*unitary_size
487
+ if (maximum>40):
488
+ maximum=40
198
489
  plt.figure(figsize=(maximum*ratio,maximum))
199
- nodes = nx.draw_networkx_nodes(graph, pos=locations, node_color=color_map_v, cmap=cmap)
200
- edges=nx.draw_networkx_edges(graph,pos=locations, edge_color=color_map_l,width=4,edge_cmap=cmap)
201
- nx.draw_networkx_labels(graph, pos=locations)
490
+ # Set transparency parameter
491
+ myalpha=0.6
492
+ # Draw the nodes
493
+ nodes = nx.draw_networkx_nodes(mygraph, pos=locations, node_color=color_map_v, cmap=cmap_v,alpha=myalpha)
494
+ # Draw the closed edges (solid lines)
495
+ edges=nx.draw_networkx_edges(mygraph_only_closed_edges,pos=locations, edge_color=color_map_l_closed,width=4,edge_cmap=cmap_l,alpha=myalpha)
496
+ # Show the buses names
497
+ nx.draw_networkx_labels(mygraph, pos=locations,font_size=8)
498
+ # Make the ticks, lables, and colorbar
202
499
  num_ticks_v=5
203
500
  ticks_v = np.linspace(0, 1, num_ticks_v)
204
501
  labels_v = np.linspace(0, max_violations_v, num_ticks_v)
@@ -213,10 +510,19 @@ class Plot_Lib:
213
510
  cbar.ax.set_yticklabels(["{:4.2f}".format(i) for i in labels_l]) # add the labels
214
511
  cbar.set_label("Thermal limit violations (h)", fontsize=10, y=0.5, rotation=90)
215
512
  cbar.ax.yaxis.set_label_position('left')
513
+ # Draw the open edges (dashed lines)
514
+ edges=nx.draw_networkx_edges(mygraph,edgelist=my_open_edges,pos=locations, edge_color=color_map_l_open,width=4,edge_cmap=cmap_l,style='--',alpha=myalpha)
515
+ # Don't display the axis in the figure
216
516
  plt.axis('off')
517
+ # Maximize figure
217
518
  wm = plt.get_current_fig_manager()
218
- wm.window.state('zoomed')
519
+ backend_name = plt.get_backend()
520
+ # this won't work on mac but should work on windows?
521
+ # macosx does not have a "window" attribute
522
+ if backend_name.lower() != 'macosx':
523
+ wm.window.state('zoomed')
524
+
525
+ # Save the figure to file
219
526
  plt.savefig(output_file_full_path_fig, dpi=300)
527
+ # Display
220
528
  plt.show()
221
-
222
-