urbanopt-rnm-us 0.3.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,528 @@
1
+ import opendssdirect as dss
2
+ import pandas as pd
3
+ #import matplotlib
4
+ import matplotlib.pyplot as plt
5
+ import numpy as np
6
+ import sys as sys
7
+ import math
8
+ import networkx as nx
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
+
16
+
17
+ class Plot_Lib:
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
23
+
24
+ def remove_terminal(self,bus):
25
+ """Removes the terminal from the bus name"""
26
+ myopendss_io=opendss_interface.OpenDSS_Interface(self.folder,self.b_numeric_ids)
27
+ bus=myopendss_io.remove_terminal(bus)
28
+ return bus
29
+
30
+
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
40
+ plt.grid(True)
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
72
+ v_weights = np.ones_like(v_value) / len(v_value)
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
79
+ plt.hist(bins[:-1], bins, weights=counts)
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)
88
+ plt.ylabel('Frequency (p.u.)')
89
+ # Save to file
90
+ plt.savefig(output_file_full_path_fig, dpi=300)
91
+ # Display
92
+ # plt.show()
93
+ # Save data to file
94
+ # Write directly as a CSV file with headers on first line
95
+ with open(output_file_full_path_csv, 'w') as fp:
96
+ fp.write(','.join(v_legend) + '\n')
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
287
+ plt.xlabel('Hour (h)')
288
+ # Save figure to file
289
+ plt.savefig(output_file_full_path_fig)
290
+ # Display
291
+ # plt.show()
292
+
293
+
294
+ def add_edges(self,graph,edges):
295
+ """Add edges to the graph"""
296
+ # Populate it with the edges
297
+ for idx,element in enumerate(edges):
298
+ graph.add_edges_from([(element[0],element[1])])
299
+ return graph
300
+
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
304
+ if visited_buses:
305
+ visited_buses.append(bus)
306
+ else:
307
+ visited_buses=[bus]
308
+ # Obtain the buses connected to this one
309
+ connected_buses = list(graph.neighbors(bus))
310
+ # Explore downstream levels
311
+ x_downstream_locations=[]
312
+ for downstream_bus in connected_buses:
313
+ # Remove the terminal from the bus name
314
+ downstream_bus=self.remove_terminal(downstream_bus)
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)
318
+ if downstream_bus==bus:
319
+ graph.remove_edge(bus,downstream_bus)
320
+ else:
321
+ # Explore downstream the graph (recursive search)
322
+ if downstream_bus!=parent and not downstream_bus in 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)
324
+ x_downstream_locations.append(x_loc)
325
+ # For the upper levels, it takes the average of the downstream buses
326
+ if x_downstream_locations:
327
+ loc=(sum(x_downstream_locations)/len(x_downstream_locations),-level);
328
+ else:
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
334
+ loc=(x_next_location,-level)
335
+ else:
336
+ # Default position for first bus
337
+ loc=(1,-level)
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
342
+ if locations:
343
+ locations[bus]=loc
344
+ else:
345
+ locations={bus:loc}
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
354
+ dic_num_violations_v={}
355
+ # For each node
356
+ for node in graph:
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
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
434
+ max_violations_v=max(dic_num_violations_v.values())
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
443
+ if max_violations_v==0:
444
+ intensity=0
445
+ else:
446
+ intensity=dic_num_violations_v[node]/max_violations_v
447
+ # Add the intensity of the bus to the colormap
448
+ color_map_v.append(intensity)
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
455
+ max_violations_l=max(dic_num_violations_l.values())
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
464
+ if max_violations_l==0:
465
+ intensity=0
466
+ else:
467
+ intensity=dic_num_violations_l[edge]/max_violations_l
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
475
+ #Obtain min and max of x locations
476
+ max_x=0
477
+ max_y=0
478
+ for idx,name in enumerate(locations):
479
+ if (max_x<locations[name][0]):
480
+ max_x=locations[name][0]
481
+ if (max_y<-locations[name][1]):
482
+ max_y=-locations[name][1]
483
+ # Define the size of the figure
484
+ unitary_size=0.5
485
+ ratio=16/9
486
+ maximum=max(max_x,max_y)*unitary_size
487
+ if (maximum>40):
488
+ maximum=40
489
+ plt.figure(figsize=(maximum*ratio,maximum))
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
499
+ num_ticks_v=5
500
+ ticks_v = np.linspace(0, 1, num_ticks_v)
501
+ labels_v = np.linspace(0, max_violations_v, num_ticks_v)
502
+ cbar=plt.colorbar(nodes,ticks=ticks_v)
503
+ cbar.ax.set_yticklabels(["{:4.2f}".format(i) for i in labels_v]) # add the labels
504
+ cbar.set_label("Voltage violations (h)", fontsize=10, y=0.5, rotation=90)
505
+ cbar.ax.yaxis.set_label_position('left')
506
+ num_ticks_l=5
507
+ ticks_l = np.linspace(0, 1, num_ticks_l)
508
+ labels_l = np.linspace(0, max_violations_l, num_ticks_l)
509
+ cbar=plt.colorbar(edges,ticks=ticks_l)
510
+ cbar.ax.set_yticklabels(["{:4.2f}".format(i) for i in labels_l]) # add the labels
511
+ cbar.set_label("Thermal limit violations (h)", fontsize=10, y=0.5, rotation=90)
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
516
+ plt.axis('off')
517
+ # Maximize figure
518
+ wm = plt.get_current_fig_manager()
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
526
+ plt.savefig(output_file_full_path_fig, dpi=300)
527
+ # Display
528
+ plt.show()