urbanopt-rnm-us 0.2.0 → 0.4.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,5 +1,5 @@
1
1
  # *********************************************************************************
2
- # URBANopt (tm), Copyright (c) 2019-2021, Alliance for Sustainable Energy, LLC, and other
2
+ # URBANopt (tm), Copyright (c) 2019-2022, Alliance for Sustainable Energy, LLC, and other
3
3
  # contributors. All rights reserved.
4
4
 
5
5
  # Redistribution and use in source and binary forms, with or without modification,
@@ -109,6 +109,7 @@ module URBANopt
109
109
  end
110
110
  end
111
111
  end
112
+
112
113
  def aggregate_consumption(file_csv, file_json, n_feature)
113
114
  feature_type = file_json['program']['building_types'][0]['building_type']
114
115
  # residential_building_types = "Single-Family Detached" #add the other types
@@ -1,5 +1,5 @@
1
1
  # *********************************************************************************
2
- # URBANopt (tm), Copyright (c) 2019-2021, Alliance for Sustainable Energy, LLC, and other
2
+ # URBANopt (tm), Copyright (c) 2019-2022, Alliance for Sustainable Energy, LLC, and other
3
3
  # contributors. All rights reserved.
4
4
 
5
5
  # Redistribution and use in source and binary forms, with or without modification,
@@ -1,5 +1,5 @@
1
1
  # *********************************************************************************
2
- # URBANopt (tm), Copyright (c) 2019-2021, Alliance for Sustainable Energy, LLC, and other
2
+ # URBANopt (tm), Copyright (c) 2019-2022, Alliance for Sustainable Energy, LLC, and other
3
3
  # contributors. All rights reserved.
4
4
 
5
5
  # Redistribution and use in source and binary forms, with or without modification,
@@ -52,11 +52,7 @@ module URBANopt
52
52
  hash[:resistance] = trafo['Low-voltage-side short-circuit resistance (ohms)'].to_f.round(2)
53
53
  hash[:reactance] = trafo['Reactance (p.u. transf)'].to_f.round(2)
54
54
  hash[:phases] = trafo['Nphases']
55
- if trafo['Nphases'] == '3'
56
- hash[:is_center_tap] = false
57
- else
58
- hash[:is_center_tap] = true
59
- end
55
+ hash[:Centertap] = trafo['Centertap']
60
56
  hash[:high_voltage] = trafo['Primary Voltage (kV)']
61
57
  hash[:low_voltage] = trafo['Secondary Voltage (kV)']
62
58
  hash[:connection] = trafo['connection']
@@ -0,0 +1,51 @@
1
+ import opendssdirect as dss
2
+ import pandas as pd
3
+ import matplotlib.pyplot as plt
4
+ import numpy as np
5
+ import sys as sys
6
+ import math
7
+ import networkx as nx
8
+ import opendss_interface
9
+ import plot_lib
10
+
11
+ class Validation:
12
+ def __init__(self, folder):
13
+ self.folder = folder
14
+
15
+ def main_validation(self):
16
+ master_file_full_path = folder + '/dss_files/' + 'Master.dss'
17
+ start_index = 0
18
+ num_periods=12
19
+ end_index = 8760
20
+ v_range_voltage=(0.9, 1.1)
21
+ v_limits_voltage=[0.95,1.05]
22
+ v_range_loading=(0,1.3)
23
+ v_limits_loading=[1]
24
+ v_range_show_all=(0,0)
25
+
26
+ #For tests
27
+ #end_index = 24
28
+ #v_range_voltage=(0.975, 1.025)
29
+
30
+ myopendss_io=opendss_interface.OpenDSS_Interface(folder)
31
+ v_dict_voltage,v_voltage_yearly,v_voltage_period,v_power_yearly,v_power_period,v_dict_loading,v_loading_yearly,v_loading_period,v_dict_losses,v_subs_losses_yearly,v_line_losses_yearly,dict_buses_element=myopendss_io.solve_powerflow_iteratively(num_periods,start_index,end_index,master_file_full_path,v_range_voltage,v_range_loading)
32
+ myopendss_io.write_dict(v_dict_voltage,v_range_show_all,'Voltages (p.u.)','Buses')
33
+ myopendss_io.write_dict(v_dict_voltage,v_range_voltage,'Voltage Violations (p.u.)','Buses')
34
+ myopendss_io.write_dict(v_dict_loading,v_range_show_all,'Loading (p.u.)','Branches')
35
+ myopendss_io.write_dict(v_dict_loading,v_range_loading,'Loading Violations (p.u.)','Branches')
36
+ myopendss_io.write_dict(v_dict_losses,v_range_show_all,'Losses','Branches')
37
+ edges=myopendss_io.get_edges()
38
+ myplot_lib=plot_lib.Plot_Lib(folder)
39
+ myplot_lib.plot_hist('Voltage',v_voltage_yearly,v_voltage_period,v_range_voltage,40,num_periods,v_limits_voltage)
40
+ myplot_lib.plot_hist('Loading',v_loading_yearly,v_loading_period,v_range_loading,80,num_periods,v_limits_loading)
41
+ myplot_lib.plot_losses(v_subs_losses_yearly,v_line_losses_yearly)
42
+ myplot_lib.plot_graph(edges,v_dict_voltage,v_range_voltage,v_dict_loading,v_range_loading,dict_buses_element)
43
+
44
+
45
+
46
+ if __name__ == "__main__":
47
+ #Example to run it in command window
48
+ #python main_validation.py files
49
+ folder = sys.argv[1]
50
+ valid=Validation(folder)
51
+ valid.main_validation()
@@ -0,0 +1,234 @@
1
+ import opendssdirect as dss
2
+ import pandas as pd
3
+ import matplotlib.pyplot as plt
4
+ import numpy as np
5
+ import sys as sys
6
+ import math
7
+ import networkx as nx
8
+
9
+ class OpenDSS_Interface:
10
+ def __init__(self, folder):
11
+ self.folder = folder
12
+
13
+ def remove_terminal(self,bus):
14
+ if isinstance(bus,str):
15
+ return bus.split('.')[0]
16
+ else:
17
+ return bus
18
+
19
+
20
+ def extract_period(self,v_value_i,v_value_period,i,end_index,num_periods):
21
+ for j in range(num_periods):
22
+ if (i<=end_index*j/num_periods):
23
+ v_value_period[j].extend(v_value_i)
24
+ break
25
+ return v_value_period
26
+
27
+ def add_to_dictionary(self,v_dict_voltage,dict_voltage_i):
28
+ for idx,name in enumerate(dict_voltage_i):
29
+ if name in v_dict_voltage: #if not empty
30
+ v_dict_voltage[name].append(dict_voltage_i[name])
31
+ else:
32
+ v_dict_voltage[name]=[dict_voltage_i[name]]
33
+
34
+ def dss_run_command(self,command):
35
+ output=dss.run_command(command)
36
+ if (len(output)>0):
37
+ print(output)
38
+
39
+
40
+ def get_all_voltage(self):
41
+ """Computes over and under voltages for all buses"""
42
+ bus_names = dss.Circuit.AllBusNames()
43
+ dict_voltage = {}
44
+ v_voltage = [0 for _ in range(len(bus_names))]
45
+ for idx,b in enumerate(bus_names):
46
+ dss.Circuit.SetActiveBus(b)
47
+ vang = dss.Bus.puVmagAngle()
48
+ if len(vang[::2]) > 0:
49
+ vmag = sum(vang[::2])/(len(vang)/2)
50
+ else:
51
+ vmag = 0
52
+ dict_voltage[b] = vmag
53
+ v_voltage[idx]=vmag
54
+
55
+ return dict_voltage,v_voltage
56
+
57
+ def get_all_power(self):
58
+ """Computes power in all circuits"""
59
+ circuit_names = dss.Circuit.AllElementNames()
60
+ dict_power = {}
61
+ v_power = [0 for _ in range(len(circuit_names))]
62
+ for idx,b in enumerate(circuit_names):
63
+ dss.Circuit.SetActiveElement(b)
64
+ power = dss.CktElement.Powers()
65
+ if len(power[::2]) > 0:
66
+ poweravg = sum(power[::2])/(len(power)/2)
67
+ else:
68
+ poweravg = 0
69
+ dict_power[b] = poweravg
70
+ v_power[idx]=poweravg
71
+
72
+ return dict_power,v_power
73
+
74
+
75
+ def get_all_loading(self):
76
+ """Computes loading in all circuits"""
77
+ circuit_names = dss.Circuit.AllElementNames()
78
+ dict_loading = {}
79
+ dict_buses_element={} #Associate the element to the buses (this has the inconvenient that only associates one element to each pair of buses)
80
+ v_loading = [0 for _ in range(len(circuit_names))]
81
+ for idx,element in enumerate(circuit_names):
82
+ dss.Circuit.SetActiveElement(element)
83
+ #only if it is a branch (two buses)
84
+ buses = dss.CktElement.BusNames()
85
+ if (len(buses)>=2):
86
+ current = dss.CktElement.CurrentsMagAng()
87
+ num_terminals=dss.CktElement.NumTerminals()
88
+ if len(current[::2]) > 0:
89
+ #currentmag = sum(current[::2])/len(current[::2])
90
+ #Take only the average of 1 terminal (discarding phases so every 2)
91
+ lenc=len(current[::2])
92
+ stop=round(2*lenc/num_terminals)
93
+ #print(current[:stop:2])
94
+ currentmag = sum(current[:stop:2])/lenc
95
+ else:
96
+ currentmag = 0
97
+ currentmag = current[0]
98
+ nominal_current = dss.CktElement.NormalAmps()
99
+ #Transformers have applied a 1.1 factor in the calculation of NormalAmps
100
+ #See library that OpenDSSdirect uses in https://github.com/dss-extensions/dss_capi/blob/master/src/PDElements/Transformer.pas
101
+ #in particular line code AmpRatings[i] := 1.1 * kVARatings[i] / Fnphases / Vfactor;
102
+ if (element.startswith("Transformer")):
103
+ nominal_current=nominal_current/1.1
104
+ if (nominal_current>0):
105
+ dict_loading[element] = currentmag/nominal_current
106
+ v_loading[idx]=currentmag/nominal_current
107
+ bus1to2=self.remove_terminal(buses[0])+'-->'+self.remove_terminal(buses[1])
108
+ dict_buses_element[bus1to2]=element
109
+
110
+ return dict_loading,v_loading,dict_buses_element
111
+
112
+ def get_all_losses(self):
113
+ """Computes losses in all circuits"""
114
+ circuit_names = dss.Circuit.AllElementNames()
115
+ dict_losses = {}
116
+ v_losses = [0 for _ in range(len(circuit_names))]
117
+ total_losses=0
118
+ for idx,element in enumerate(circuit_names):
119
+ dss.Circuit.SetActiveElement(element)
120
+ #only if it is a branch (two buses)
121
+ buses = dss.CktElement.BusNames()
122
+ if (len(buses)>=2):
123
+ #next if check discards vsources
124
+ nominal_current = dss.CktElement.NormalAmps()
125
+ if (nominal_current>0):
126
+ losses = dss.CktElement.Losses()
127
+ if len(losses) ==2:
128
+ lossesavg = (losses[0]) #Verify this is correct, if not abs() they range from + to -, [0] to take active losses
129
+ else:
130
+ print("error - not correctly reading losses")
131
+ lossesavg=0
132
+ #Convert to kW. This function is the exeption that return losses in W
133
+ lossesavg=lossesavg/1000
134
+ dict_losses[element] = lossesavg
135
+ v_losses[idx]=lossesavg
136
+
137
+ return dict_losses,total_losses
138
+
139
+ def get_total_subs_losses(self):
140
+ """Computes total substation losses"""
141
+ return dss.Circuit.SubstationLosses()[0] #Real part
142
+
143
+ def get_total_line_losses(self):
144
+ """Computes total line losses"""
145
+ return dss.Circuit.LineLosses()[0] #Real part
146
+
147
+ def get_edges(self):
148
+ edges=[]
149
+ circuit_names = dss.Circuit.AllElementNames()
150
+ for idx,element in enumerate(circuit_names):
151
+ dss.Circuit.SetActiveElement(element)
152
+ buses = dss.CktElement.BusNames()
153
+ #Only if it is a branch
154
+ if (len(buses)>=2): #There can be 3 buses in single-phase center-tap transformer, in this case the two last ones are equals (different terminals only) and we can take just the 2 first ones
155
+ #remove terminal from the bus name (everything to the right of point)
156
+ edges.append([(self.remove_terminal(buses[0]),self.remove_terminal(buses[1]))])
157
+ return edges
158
+
159
+ def write_dict(self,v_dict,v_range,type,component):
160
+ output_file_full_path = self.folder + '/' + type + '_' + component + '.csv'
161
+ # Write directly as a CSV file with headers on first line
162
+ with open(output_file_full_path, 'w') as fp:
163
+ #Header: ID, hours (consider adding day, month in future)
164
+ for idx,name in enumerate(v_dict):
165
+ fp.write('Hour,'+','.join(str(idx2) for idx2,value in enumerate(v_dict[name])) + '\n')
166
+ break
167
+ #Write matrix
168
+ for idx,name in enumerate(v_dict):
169
+ #Truncate list to limits
170
+ truncated_values=[]
171
+ for idx2,value in enumerate(v_dict[name]):
172
+ if value<v_range[0] or value>=v_range[1]:
173
+ truncated_values.append(str(value))
174
+ else:
175
+ truncated_values.append("")
176
+ fp.write(name+','+','.join(truncated_values)+'\n')
177
+ #truncated_values=[i for i, lower, upper in zip(v_dict_voltage[name], [v_range_voltage[1]]*len(v_dict_voltage[name]), [v_range_voltage[1]]*len(v_dict_voltage[name])) if i <lower or i>upper]
178
+ #fp.write(name+','+','.join(map(str,v_dict_voltage[name]))+'\n')
179
+
180
+
181
+ def solve_powerflow_iteratively(self,num_periods,start_index,end_index,location,v_range_voltage,v_range_loading):
182
+ #Por flow solving mode
183
+ self.dss_run_command("Clear")
184
+ self.dss_run_command('Redirect '+location)
185
+ self.dss_run_command("solve mode = snap")
186
+ self.dss_run_command("Set mode=yearly stepsize=1h number=1")
187
+ #Init vectors
188
+ v_voltage_yearly=[]
189
+ v_voltage_period=[[] for _ in range(num_periods)]
190
+ v_power_yearly=[]
191
+ v_power_period=[[] for _ in range(num_periods)]
192
+ v_loading_yearly=[]
193
+ v_loading_period=[[] for _ in range(num_periods)]
194
+ v_subs_losses_yearly=[]
195
+ v_line_losses_yearly=[]
196
+ v_dict_voltage={}
197
+ v_dict_loading={}
198
+ v_dict_losses={}
199
+ #Additional initializations
200
+ my_range=range(start_index,end_index,1)
201
+ old_percentage_str="" #Variable for tracking progress
202
+ for i in my_range:
203
+ #Solve power flow
204
+ self.dss_run_command("Solve")
205
+ #Get voltages
206
+ dict_voltage_i, v_voltage_i = self.get_all_voltage()
207
+ self.add_to_dictionary(v_dict_voltage,dict_voltage_i)
208
+ v_voltage_yearly.extend(v_voltage_i)
209
+ self.extract_period(v_voltage_i,v_voltage_period,i,end_index,num_periods)
210
+ #Get power
211
+ dict_power_i, v_power_i = self.get_all_power()
212
+ v_power_yearly.extend(v_power_i)
213
+ v_power_period=self.extract_period(v_power_i,v_power_period,i,end_index,num_periods)
214
+ #Get loading
215
+ dict_loading_i, v_loading_i,dict_buses_element = self.get_all_loading()
216
+ self.add_to_dictionary(v_dict_loading,dict_loading_i)
217
+ v_loading_yearly.extend(v_loading_i)
218
+ v_loading_period=self.extract_period(v_loading_i,v_loading_period,i,end_index,num_periods)
219
+ #Get dict losses
220
+ dict_losses_i, v_losses_i = self.get_all_losses()
221
+ self.add_to_dictionary(v_dict_losses,dict_losses_i)
222
+ #Get losses
223
+ subs_losses_i = self.get_total_subs_losses()
224
+ v_subs_losses_yearly.append(subs_losses_i)
225
+ line_losses_i = self.get_total_line_losses()
226
+ v_line_losses_yearly.append(line_losses_i)
227
+ #Print progress
228
+ #percentage_str="{:.0f}".format(100*i/end_index)+"%"
229
+ #if (percentage_str!=old_percentage_str):
230
+ # print(percentage_str)
231
+ #old_percentage_str=percentage_str
232
+ return v_dict_voltage,v_voltage_yearly,v_voltage_period,v_power_yearly,v_power_period,v_dict_loading,v_loading_yearly,v_loading_period,v_dict_losses,v_subs_losses_yearly,v_line_losses_yearly,dict_buses_element
233
+
234
+
@@ -0,0 +1,222 @@
1
+ import opendssdirect as dss
2
+ import pandas as pd
3
+ import matplotlib.pyplot as plt
4
+ import numpy as np
5
+ import sys as sys
6
+ import math
7
+ import networkx as nx
8
+ import opendss_interface
9
+
10
+ class Plot_Lib:
11
+ def __init__(self, folder):
12
+ self.folder = folder
13
+
14
+ def remove_terminal(self,bus):
15
+ myopendss_io=opendss_interface.OpenDSS_Interface(self.folder)
16
+ bus=myopendss_io.remove_terminal(bus)
17
+ return bus
18
+
19
+
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
24
+ 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
+
38
+ 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'
42
+ 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.)')
49
+ plt.ylabel('Frequency (p.u.)')
50
+ plt.savefig(output_file_full_path_fig, dpi=300)
51
+ plt.show()
52
+ #Save to file
53
+ # Write directly as a CSV file with headers on first line
54
+ with open(output_file_full_path_csv, 'w') as fp:
55
+ 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'])
69
+ plt.xlabel('Hour (h)')
70
+ plt.ylabel('Losses (kWh)')
71
+ plt.savefig(output_file_full_path_fig)
72
+ plt.show()
73
+
74
+
75
+ def get_graph(self,edges):
76
+ graph=nx.Graph()
77
+ for idx,element in enumerate(edges):
78
+ graph.add_edges_from(element)
79
+ return graph
80
+
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
83
+ if visited_buses:
84
+ visited_buses.append(bus)
85
+ else:
86
+ visited_buses=[bus]
87
+ #Obtain the buses connected to this one
88
+ connected_buses = list(graph.neighbors(bus))
89
+ #Explore downstream levels
90
+ x_downstream_locations=[]
91
+ for downstream_bus in connected_buses:
92
+ #Remove the terminal from the bus nmae
93
+ 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)
97
+ if downstream_bus==bus:
98
+ graph.remove_edge(bus,downstream_bus)
99
+ else:
100
+ #Explore downstream the graph (recursive search)
101
+ 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)
103
+ x_downstream_locations.append(x_loc)
104
+ #For the upper levels, it takes the average of the downstream buses
105
+ if x_downstream_locations:
106
+ loc=(sum(x_downstream_locations)/len(x_downstream_locations),-level);
107
+ 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
115
+ loc=(x_next_location,-level)
116
+ else:
117
+ #Default position for first bus
118
+ loc=(1,-level)
119
+ #Assign x location of this level
120
+ x_locations_levels[level]=loc[0]
121
+ #update locations
122
+ if locations:
123
+ locations[bus]=loc
124
+ else:
125
+ 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')
143
+ dic_num_violations_v={}
144
+ 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
151
+ color_map_v=[]
152
+ max_violations_v=max(dic_num_violations_v.values())
153
+ for node in graph:
154
+ if max_violations_v==0:
155
+ intensity=0
156
+ else:
157
+ intensity=dic_num_violations_v[node]/max_violations_v
158
+ #color_map_v.append(cmap_nodes(intensity))
159
+ 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=[]
178
+ max_violations_l=max(dic_num_violations_l.values())
179
+ for edge in graph.edges():
180
+ if max_violations_l==0:
181
+ intensity=0
182
+ else:
183
+ intensity=dic_num_violations_l[edge]/max_violations_l
184
+ #color_map_v.append(cmap_nodes(intensity))
185
+ color_map_l.append(intensity)
186
+ #Obtain min and max of x locations
187
+ max_x=0
188
+ max_y=0
189
+ for idx,name in enumerate(locations):
190
+ if (max_x<locations[name][0]):
191
+ max_x=locations[name][0]
192
+ if (max_y<-locations[name][1]):
193
+ max_y=-locations[name][1]
194
+ #plt.figure(figsize=(max_x,max_y))
195
+ unitary_size=0.5
196
+ ratio=16/9
197
+ maximum=max(max_x,max_y)*unitary_size
198
+ 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)
202
+ num_ticks_v=5
203
+ ticks_v = np.linspace(0, 1, num_ticks_v)
204
+ labels_v = np.linspace(0, max_violations_v, num_ticks_v)
205
+ cbar=plt.colorbar(nodes,ticks=ticks_v)
206
+ cbar.ax.set_yticklabels(["{:4.2f}".format(i) for i in labels_v]) # add the labels
207
+ cbar.set_label("Voltage violations (h)", fontsize=10, y=0.5, rotation=90)
208
+ cbar.ax.yaxis.set_label_position('left')
209
+ num_ticks_l=5
210
+ ticks_l = np.linspace(0, 1, num_ticks_l)
211
+ labels_l = np.linspace(0, max_violations_l, num_ticks_l)
212
+ cbar=plt.colorbar(edges,ticks=ticks_l)
213
+ cbar.ax.set_yticklabels(["{:4.2f}".format(i) for i in labels_l]) # add the labels
214
+ cbar.set_label("Thermal limit violations (h)", fontsize=10, y=0.5, rotation=90)
215
+ cbar.ax.yaxis.set_label_position('left')
216
+ plt.axis('off')
217
+ wm = plt.get_current_fig_manager()
218
+ wm.window.state('zoomed')
219
+ plt.savefig(output_file_full_path_fig, dpi=300)
220
+ plt.show()
221
+
222
+
@@ -0,0 +1,75 @@
1
+ # *********************************************************************************
2
+ # URBANopt (tm), Copyright (c) 2019-2022, Alliance for Sustainable Energy, LLC, and other
3
+ # contributors. All rights reserved.
4
+
5
+ # Redistribution and use in source and binary forms, with or without modification,
6
+ # are permitted provided that the following conditions are met:
7
+
8
+ # Redistributions of source code must retain the above copyright notice, this list
9
+ # of conditions and the following disclaimer.
10
+
11
+ # Redistributions in binary form must reproduce the above copyright notice, this
12
+ # list of conditions and the following disclaimer in the documentation and/or other
13
+ # materials provided with the distribution.
14
+
15
+ # Neither the name of the copyright holder nor the names of its contributors may be
16
+ # used to endorse or promote products derived from this software without specific
17
+ # prior written permission.
18
+
19
+ # Redistribution of this software, without modification, must refer to the software
20
+ # by the same designation. Redistribution of a modified version of this software
21
+ # (i) may not refer to the modified version by the same designation, or by any
22
+ # confusingly similar designation, and (ii) must refer to the underlying software
23
+ # originally provided by Alliance as "URBANopt". Except to comply with the foregoing,
24
+ # the term "URBANopt", or any confusingly similar designation may not be used to
25
+ # refer to any modified version of this software or any modified version of the
26
+ # underlying software originally provided by Alliance without the prior written
27
+ # consent of Alliance.
28
+
29
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
30
+ # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
31
+ # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
32
+ # IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
33
+ # INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
34
+ # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
35
+ # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
36
+ # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
37
+ # OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
38
+ # OF THE POSSIBILITY OF SUCH DAMAGE.
39
+ # *********************************************************************************
40
+
41
+ require 'urbanopt/rnm/logger'
42
+
43
+ module URBANopt
44
+ module RNM
45
+ # Class for OpenDSS validation (runs python script)
46
+ class Validation
47
+ ##
48
+ # Initialize attributes: ++run directory+
49
+ ##
50
+ # [parameters:]
51
+ # * +rnm_dirname+ - _String_ - name of RNM-US directory that will contain the input files (within the scenario directory)
52
+ def initialize(rnm_full_path)
53
+ # absolute path
54
+ @rnm_full_path = rnm_full_path
55
+ @opendss_full_path=File.join(@rnm_full_path,'results/OpenDSS')
56
+ if !Dir.exist?(@opendss_full_path)
57
+ puts 'Error: folder does not exist'+@rnm_full_path
58
+ end
59
+ end
60
+
61
+
62
+ ##
63
+ # Run validation
64
+ ##
65
+ def run_validation()
66
+ puts "Initating OpenDSS validation in folder"
67
+ puts @opendss_full_path
68
+ puts "This can take some minutes"
69
+ #puts `python ./lib/urbanopt/rnm/validation/main_validation.py #{@rnm_full_path}`
70
+ log=`python ./lib/urbanopt/rnm/validation/main_validation.py #{@opendss_full_path}`
71
+ puts log
72
+ end
73
+ end
74
+ end
75
+ end
@@ -1,5 +1,5 @@
1
1
  # *********************************************************************************
2
- # URBANopt (tm), Copyright (c) 2019-2021, Alliance for Sustainable Energy, LLC, and other
2
+ # URBANopt (tm), Copyright (c) 2019-2022, Alliance for Sustainable Energy, LLC, and other
3
3
  # contributors. All rights reserved.
4
4
 
5
5
  # Redistribution and use in source and binary forms, with or without modification,
@@ -40,6 +40,6 @@
40
40
 
41
41
  module URBANopt
42
42
  module RNM
43
- VERSION = '0.2.0'.freeze
43
+ VERSION = '0.4.0'.freeze
44
44
  end
45
45
  end
@@ -1,5 +1,5 @@
1
1
  # *********************************************************************************
2
- # URBANopt (tm), Copyright (c) 2019-2021, Alliance for Sustainable Energy, LLC, and other
2
+ # URBANopt (tm), Copyright (c) 2019-2022, Alliance for Sustainable Energy, LLC, and other
3
3
  # contributors. All rights reserved.
4
4
 
5
5
  # Redistribution and use in source and binary forms, with or without modification,