urbanopt-rnm-us 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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
-