def apply(cls, stack, model, *args, **kwargs): if 'filename' in kwargs: filename = kwargs['filename'] #If the file does not exist, do nothing and return the input model if not os.path.exists(filename): return model # If the file is empty also do nothing and return the input model try: df = pd.read_csv(filename) except pd.errors.EmptyDataError: logging.warning("Empty Dataframe loaded for {}".format(filename)) return model #Create a CSV reader csv_reader = CSVReader() m2 = Store() csv_reader.parse(m2, filename) #Create a Modifier object modifier = Modifier() #Merge the two models new_model = modifier.merge(model, m2) #Return the new model return new_model
def apply(cls, stack, model, load_path): loads = Store() reader = CsvReader() reader.parse(loads, load_path) modifier = Modifier() intermediate_model = modifier.merge(model, loads) return intermediate_model
def apply(cls, stack, model, *args, **kwargs): rnm_name = None setpoint = None if 'rnm_name' in kwargs: rnm_name = kwargs['rnm_name'] if 'setpoint' in kwargs: setpoint = kwargs['setpoint'] for m in model.models: if isinstance(m, PowerTransformer) and hasattr( m, 'name') and rnm_name.lower() in m.name: api_regulator = Regulator(model) api_regulator.name = 'reg_' + m.name api_regulator.from_element = m.from_element api_regulator.to_element = m.to_element api_regulator.positions = m.positions windings = [] if m.windings is not None and len(m.windings) > 0: for w in m.windings: winding = Winding(model) winding.nominal_voltage = w.nominal_voltage winding.voltage_type = w.voltage_type winding.resistance = w.resistance winding.rated_power = w.rated_power phase_windings = [] if w.phase_windings is not None and len( w.phase_windings) > 0: for p in w.phase_windings: phase_winding = PhaseWinding(model) phase_winding.phase = p.phase phase_windings.append(phase_winding) winding.phase_windings = phase_windings windings.append(winding) api_regulator.windings = windings api_regulator.ltc = 0 api_regulator.is_subtation = 0 api_regulator.pt_ratio = float( m.windings[0].nominal_voltage) / 120.0 / (3**0.5) api_regulator.ct_ratio = 100.0 api_regulator.bandwidth = 2.0 if setpoint is not None: api_regulator.setpoint = setpoint modifier = Modifier() modifier.delete_element(model, m) return model
def apply(cls, stack, model, load_path, base_dir=None): if base_dir and (not os.path.exists(load_path)): load_path = os.path.join(base_dir,load_path) dtypes = {'Load.positions[0].elevation': float, 'Load.rooftop_area': float, 'Load.num_levels': float, 'Load.num_users': float, 'Load.timeseries[0].interval': float, 'Load.timeseries[0].loaded': int} loads = Store() reader = CsvReader() reader.parse(loads, load_path, dtypes=dtypes) modifier = Modifier() intermediate_model = modifier.merge(model, loads) return intermediate_model
def apply(cls, stack, model, sub_internals): reduced_model = model modifier = Modifier() csv_reader = CsvReader() for sub in os.listdir(sub_internals): to_delete = Store() to_delete2 = Store() to_delete3 = Store() sub_lines = os.path.join(sub_internals, sub, 'internals_lines.csv') csv_reader.parse(to_delete, sub_lines) sub_nodes = os.path.join(sub_internals, sub, 'internals_nodes.csv') csv_reader.parse(to_delete2, sub_nodes) to_delete = modifier.add(to_delete, to_delete2) sub_transformers = os.path.join(sub_internals, sub, 'internals_transformers.csv') csv_reader.parse(to_delete3, sub_transformers) to_delete = modifier.add(to_delete, to_delete3) reduced_model = modifier.delete(reduced_model, to_delete) substation = Store() final_model = reduced_model for sub in os.listdir(sub_internals): odss_reader = OpenDSSReader() sub_master = os.path.join(sub_internals, sub, 'master.dss') odss_reader.build_opendssdirect(sub_master) sub_bus_coords = os.path.join(sub_internals, sub, 'Buscoords.dss') odss_reader.set_dss_file_names({'Nodes': sub_bus_coords}) odss_reader.parse(substation, verbose=True) final_model = modifier.add(final_model, substation) return final_model
def apply(cls, stack, model, random_percent=None, scale_factor=None, timeseries_path=None): if timeseries_path is not None: scale_factor_model = Store() reader = CsvReader() reader.parse(sc_factor_model, scaling_path) modifier = Modifier() intermediate_model = modifier.merge(model, scale_factor_model) return intermediate_model if random_percent == None: random_percent = 100 if scale_factor is not None: scale_factor_model = Store() modifier = Modifier() total_loads = 0 for obj_name in model.model_names: if isinstance(model.model_names[obj_name], Load): total_loads = total_loads + 1 num_selected = int(random_percent / 100.0 * total_loads) to_select = [1 for i in range(num_selected) ] + [0 for i in range(total_loads - num_selected)] shuffle(to_select) load_cnt = 0 for obj_name in model.model_names: if isinstance(model.model_names[obj_name], Load) and to_select[load_cnt] == 1: tmp_load = Load(scale_factor_model) tmp_timeseries = Timeseries(scale_factor_model) tmp_timeseries.scale_factor = scale_factor tmp_load.name = obj_name tmp_load.timeseries = [tmp_timeseries] modifier.set_attributes(model, model.model_names[obj_name], tmp_load) model.set_names() return model else: logger.warn('no scaling factor set') return model
def apply(cls, stack, model, *args, **kwargs): cutoff_length = None if 'cutoff_length' in kwargs: cutoff_length = kwargs['cutoff_length'] if cutoff_length is None: return model from_element_cnt = {} for i in model.models: if isinstance(i,Line) and i.length is not None and i.length >cutoff_length and i.feeder_name is not None and i.feeder_name != 'subtransmission': # Option 1: put a switch at one of the intermediate nodes 1/10 of the way down the line if i.positions is not None and len(i.positions) >10: node1 = Node(model) node2 = Node(model) pos = int(len(i.positions)/10) node1.is_substation_connection = 0 node1.feeder_name = i.feeder_name node1.substation_name = i.substation_name node1.nominal_voltage = i.nominal_voltage if i.from_element in from_element_cnt: from_element_cnt[i.from_element]=from_element_cnt[i.from_element]+1 else: from_element_cnt[i.from_element] = 1 node1.name = i.from_element+'_b1_'+str(from_element_cnt[i.from_element]) node1.positions = [i.positions[pos]] node1_phases = [] for p in i.wires: if p.phase is not None and p.phase in ['A','B','C','N']: node1_phases.append(Unicode(p.phase)) node1.phases = node1_phases node2.is_substation_connection = 0 node2.feeder_name = i.feeder_name node2.substation_name = i.substation_name node2.nominal_voltage = i.nominal_voltage node2.name = i.from_element+'_b2_'+str(from_element_cnt[i.from_element]) node2.positions = [i.positions[pos+1]] node2_phases = [] for p in i.wires: if p.phase is not None and p.phase in ['A','B','C','N']: node2_phases.append(Unicode(p.phase)) node2.phases = node2_phases modifier = Modifier() switch_break = modifier.copy(model,i) switch_break.name = i.name+'_disconnect' switch_break.length = 1 #1 meter switch_break.positions = [] switch_break.from_element = node1.name switch_break.to_element = node2.name switch_break.is_switch = True for wire in switch_break.wires: wire.is_open=False second_stretch = modifier.copy(model,i) second_stretch.name = i.name+'_cont' second_stretch.positions = i.positions[pos+2:] second_stretch.from_element = node2.name second_stretch.length = 9/10.0*i.length # rough estimate based on number of positions i.to_element = node1.name i.length = 1/10.0*i.length-1 if pos>0: i.positions = i.positions[:pos-1] else: i.positions = [] return model
def apply(cls, stack, model, feeder_file, output_substation_folder, base_dir=None, readme_file=None, substation_folder=None): # Format is max number of feeders, list of substation numbers subs_4kv = [(4, [5]), (8, [13]), (12, [14])] subs_25kv = [(6, [11]), (12, [15])] subs_1247kv = [(1, [1]), (2, [8]), (3, [2]), (4, [4, 10]), (6, [9]), (8, [3, 7]), (12, [6]), (16, [12])] sub_list = None logger.debug("Starting add_cyme_substations") if base_dir and (not os.path.exists(feeder_file)): feeder_file = os.path.join(base_dir, feeder_file) if substation_folder == None: substation_folder = os.path.join(os.path.dirname(__file__), 'resources') elif base_dir and (not os.path.exists(output_substation_folder)): output_substation_folder = os.path.join(base_dir, output_substation_folder) transformer_config = 'Y' #Default is Delta-Wye. If specified we set it to be wye-wye if readme_file is not None and os.path.exists(readme_file): f = open(readme_file, 'r') info = f.readline().split() suffix = info[1][:-2] config = info[5] if config == 'delta-delta': transformer_config = 'D' kv = int(float(suffix) * 1000) if suffix == '12.47': suffix = '1247' suffix = '_' + suffix if kv == 4000: sub_list = subs_4kv elif kv == 25000: sub_list = subs_25kv else: sub_list = subs_1247kv else: kv = 12470 suffix = '_1247' sub_list = subs_1247kv from_cyme_layer_dir = None # first look in stack for layer in stack: if layer.name == 'From CYME': from_cyme_layer_dir = layer.layer_dir break # then try this layer's library directory if from_cyme_layer_dir is None: from_cyme_layer_dir = os.path.join( os.path.dirname(os.path.dirname(__file__)), 'from_cyme') if not os.path.exists(from_cyme_layer_dir): msg = "Cannot find the 'From CYME' layer." logger.error(msg) raise Exception(msg) logger.debug("Building the model network") srcs = [] for obj in model.models: if isinstance(obj, PowerSource) and obj.is_sourcebus == 1: srcs.append(obj.name) srcs = np.unique(srcs) if len(srcs) == 0: raise ValueError('No PowerSource object found in the model.') elif len(srcs) > 1: raise ValueError( 'Mupltiple sourcebus found: {srcs}'.format(srcs=srcs)) else: source = srcs[0] logger.debug("Identifying the Source Bus as {src}".format(src=source)) ''' Substation nodes have the form ***_1247. (or _4 or _25 for 4kv and 25kv systems) The boundary of the substation is ***_69 on the high side. The boundary of the substation in a node with no x on the low side A feeder defines these boundaries. e.g. IHS0_1247->IDT706 is a substation of IHS0_1247 with a boundary of IDT706 We remove everything between the high and low boundaries when updating a substation ''' model.build_networkx( source) # Used to remove internal edges in substation df = pd.read_csv( feeder_file, ' ' ) #The RNM file specifying which feeders belong to which substation substation_boundaries_low = {} substation_boundaries_high = {} substation_transformers = {} substation_names = {} for index, row in df.iterrows(): substation = row.iloc[1].lower() feeder = row.iloc[2].lower() transformer = row.iloc[3].lower() transformer = 'tr' + transformer[ 6:] #The prefix is slightly different with the feeder.txt file buses = feeder.split('->') bus2 = buses[1] bus1 = buses[0] if substation in substation_boundaries_low: substation_boundaries_low[substation].add( bus2 + '-' + bus1 + 'x' ) #Set the first node to be the location with one x sign as sometimes there isn't one with xx substation_names[bus2 + '-' + bus1 + 'x'] = substation else: substation_boundaries_low[substation] = set( [bus2 + '-' + bus1 + 'x']) substation_names[bus2 + '-' + bus1 + 'x'] = substation if substation not in substation_boundaries_high: substation_boundaries_high[substation] = set([ substation.replace(suffix, '_69') ]) # All substations are from 69kV if substation not in substation_transformers: substation_transformers[substation] = set([transformer]) print(substation_names) for sub in substation_boundaries_low.keys( ): #sub is the name of the substation and substation_boundaries_low[sub] is a set of all the connected feeders logger.debug("Building to_delete and modifier") to_delete = Store() modifier = Modifier() logger.info( "Processing substation {}. There are {} in total.".format( sub, len(substation_boundaries_low))) all_boundaries = substation_boundaries_low[sub].union( substation_boundaries_high[sub]) # TODO: do a search from the high boundary to the low boundary and include everything inside #get_internal_nodes(substation_boundaries_high[sub],substation_boundaries_low[sub]) feeder_names = list( substation_boundaries_low[sub] ) # Feeder point of connection to the substation internal_nodes = [ i for i in model.model_names if sub in i and isinstance(model[i], Node) ] internal_edges = [ i for i in model.model_names if sub in i and (isinstance(model[i], Line) or isinstance(model[i], PowerTransformer)) ] tmp_include_edges = [] for i in internal_edges: if model[i].from_element in feeder_names: internal_nodes.append(model[i].from_element) if model[i].to_element in feeder_names: internal_nodes.append(model[i].to_element) if 'mv' in model[i].to_element and 'mv' in model[ i].from_element: tmp_include_edges.append( i ) #Used to address MV loads attached directly to the substation for i in tmp_include_edges: internal_edges.remove(i) high_boundary = list(substation_boundaries_high[sub])[ 0] #Should only be one high side boundary point in the set internal_nodes.append(high_boundary) """ #import pdb;pdb.set_trace() all_nodes.append(sub+'-'+high_boundary+'xxx') all_nodes.append(sub+'-'+high_boundary+'xx') all_nodes.append(sub+'-'+high_boundary+'x') all_nodes.append(sub) for low_val in substation_boundaries_low[sub]: all_nodes.append(low_val) # End at these nodes. No reclosers after these. last_node = low_val+'-'+sub+'x' last_node_rev = sub+'-'+low_val+'x' if last_node in model.model_names: all_nodes.append(last_node) #The connection point if last_node_rev in model.model_names: all_nodes.append(last_node_rev) #The connection point if the name is backwards. Dypically with UMV nodes if last_node+'x' in model.model_names: all_nodes.append(last_node+'x') #This extra node often exists if last_node+'_p' in model.model_names: all_nodes.append(last_node+'_p') #This extra node sometimes exists if last_node+'xx' in model.model_names: all_nodes.append(last_node+'xx') #This extra node sometimes exists too """ all_nodes_set = set(internal_nodes) #internal_edges = model.get_internal_edges(all_nodes_set) #import pdb;pdb.set_trace() # These attribuetes will be used to inform which substation is selected rated_power = None emergency_power = None loadloss = None noloadloss = None reactance = None lat = None long = None transformer = list(substation_transformers[sub])[ 0] # Assume only one transformer per substation in RNM # Currently using CYME reader which has different names for the transformers to the feeders.txt file # reactance = model[transformer].reactances[0] # Assume the same for all windings in a substation # loadloss = model[transformer].loadloss # noloadloss = model[transformer].noload_loss # rated_power = model[transformer].windings[0].rated_power # Assume the same for all windings in a substation # emergency_power = model[transformer].windings[0].emergency_power # Assume the same for all windings in a substation try: lat = model[sub].positions[0].lat long = model[sub].positions[0].long except: raise ValueError('{} missing position elements'.format( model[sub].name)) # import pdb;pdb.set_trace() # Mark the internals of the substation for deletion for n in all_nodes_set: if not n in model.model_names: continue # is_endpoint = False # for key in substation_boundaries_low: # if n in substation_boundaries_low[key]: #Don't delete the boundaries of the substation # is_endpoint = True # break # if is_endpoint: # continue obj_name = type(model[n]).__name__ base_obj = globals()[obj_name](to_delete) base_obj.name = n for e in internal_edges: if not e in model.model_names: continue if model[ e].from_element in feeder_names: #Don't remove edge from bus2-bus1-x to the first distribution transformer continue obj_name = type(model[e]).__name__ base_obj = globals()[obj_name](to_delete) base_obj.name = e num_model_feeders = len(feeder_names) not_allocated = True low_voltage = kv high_voltage = 69000 #import pdb;pdb.set_trace() # Read the CYME models random.seed(0) for element in sub_list: # Insert extra logic here to determine which one to use if num_model_feeders > element[0]: continue sub_file = 'sb' + str(element[1][random.randint( 0, len(element[1]) - 1)]) print(sub_file) sub_model = Store() reader = CymeReader(data_folder_path=os.path.join( os.path.dirname(__file__), 'resources', sub_file)) reader.parse(sub_model) sub_model.set_names() # Set the nominal voltages within the substation using the cyme model transformer and source voltage modifier = system_structure_modifier(sub_model) modifier.set_nominal_voltages_recur() # Determine which nodes in the CYME model connect feeders/HV all_substation_connections = [] available_feeders = 0 for i in sub_model.models: if isinstance(i, Node) and hasattr( i, 'is_substation_connection' ) and i.is_substation_connection == 1: all_substation_connections.append(i) i.setpoint = 1.03 try: if i.nominal_voltage == low_voltage: available_feeders += 1 except: raise ValueError( "Nominal Voltages not set correctly in substation" ) logger.info( "Processing substation {}. There are {} feeders available and {} feeders in RNM" .format(sub, available_feeders, num_model_feeders)) if available_feeders >= num_model_feeders: boundry_map = {} feeder_cnt = 0 ref_lat = 0 ref_long = 0 for i in sub_model.models: # Remove the source node. This is no longer needed if isinstance(i, PowerSource): sub_model.remove_element(i) # Set a random node as the reference node if isinstance(i, Node) and hasattr( i, 'positions' ) and len(i.positions) > 0 and hasattr( i.positions[0], 'lat' ) and i.positions[0].lat is not None and hasattr( i.positions[0], 'long') and i.positions[0].long is not None: ref_lat = i.positions[0].lat ref_long = i.positions[0].long substation_name = substation_names[feeder_names[0].lower()] for i in sub_model.models: # Remove feeder name from substation elements. This is normally set automatically when reading from CYME if hasattr(i, 'feeder_name'): i.feeder_name = '' if hasattr(i, 'substation_name'): i.substation_name = substation_name if hasattr(i, 'is_substation'): i.is_substation = True if isinstance( i, PowerTransformer ) and transformer_config == 'D' and i.windings is not None and len( i.windings) == 2 and i.windings[1] is not None: i.windings[1].connection_type = 'D' # Assign feeder names to the endpoints of the substation if isinstance(i, Node) and hasattr( i, 'is_substation_connection' ) and i.is_substation_connection == 1: if hasattr( i, 'nominal_voltage' ) and i.nominal_voltage is not None and i.nominal_voltage >= high_voltage: ######### USE no_feeders.txt to inform how many connection points there should be. Skip this substation if the number isn't correct-->>>> #### Need to discuss with Carlos boundry_map[i.name] = high_boundary i.setpoint = 1.0 i.name = high_boundary #TODO: issue of multiple high voltage inputs needs to be addressed #i.feeder_name = 'subtransmission' i.substation_name = substation_name i.is_substation = False elif hasattr( i, 'nominal_voltage' ) and i.nominal_voltage is not None and i.nominal_voltage < high_voltage: feeder_cnt += 1 if feeder_cnt <= num_model_feeders: endpoint = feeder_names[feeder_cnt - 1].split('-')[0] if 'mv' in endpoint: #The node names for these are reversed for some reason boundry_map[ i. name] = substation_name + '-' + endpoint + 'x' i.name = substation_name + '-' + endpoint + 'x' if i.name in all_nodes_set: all_nodes_set.remove(i.name) else: boundry_map[i.name] = feeder_names[ feeder_cnt - 1] i.name = feeder_names[feeder_cnt - 1].lower() i.substation_name = substation_name i.is_substation = False i.feeder_name = i.substation_name + '->' + endpoint #import pdb;pdb.set_trace() if i.substation_name + '->' + endpoint in model.model_names: # Set the Feedermetadata headnode to be the correct name. model[i.substation_name + '->' + endpoint].headnode = i.name else: i.name = str( sub_file + '_' + sub + '_' + i.name ).lower( ) #ie. No feeders assigned to this berth so using the substation identifiers else: raise ValueError( "Nominal Voltages not set correctly in substation" ) elif hasattr(i, 'name') and (not isinstance( i, Feeder_metadata)): i.name = str(sub_file + '_' + sub + '_' + i.name).lower() if isinstance(i, Regulator) and hasattr( i, 'connected_transformer' ) and i.connected_transformer is not None: i.connected_transformer = str( sub_file + '_' + sub + '_' + i.connected_transformer).lower() if hasattr( i, 'from_element') and i.from_element is not None: if i.from_element in boundry_map: i.from_element = boundry_map[i.from_element] else: i.from_element = str(sub_file + '_' + sub + '_' + i.from_element).lower() if hasattr(i, 'to_element') and i.to_element is not None: if i.to_element in boundry_map: i.to_element = boundry_map[i.to_element] else: i.to_element = str(sub_file + '_' + sub + '_' + i.to_element).lower() if hasattr( i, 'positions' ) and i.positions is not None and len(i.positions) > 0: # import pdb;pdb.set_trace() if ref_long == 0 and ref_lat == 0: logger.warning( "Warning: Reference co-ords are (0,0)") scale_factor = 1 if element[ 0] >= 12: # The larger substations were created with a strange scale factor scale_factor = 1 / 50.0 i.positions[0].lat = scale_factor * 7 * ( i.positions[0].lat - ref_lat) + lat i.positions[0].long = scale_factor * 10 * ( i.positions[0].long - ref_long) + long if len(i.positions) > 1: for k in range(1, len(i.positions)): i.positions[k].lat = scale_factor * 7 * ( i.positions[k].lat - ref_lat) + lat i.positions[k].long = scale_factor * 10 * ( i.positions[k].long - ref_long) + long #import pdb;pdb.set_trace() not_allocated = False sub_model.set_names() to_delete.set_names() reduced_model = modifier.delete(model, to_delete) logger.info("Adding model from {} to model".format( substation_folder)) #import pdb;pdb.set_trace() model = modifier.add( reduced_model, sub_model ) #Is it a problem to be modifying the model directly? break if not_allocated: raise ValueError( 'Substation too small. {num} feeders needed. Exiting...'. format(num=num_model_feeders)) model.set_names() logger.debug("Returning {!r}".format(model)) return model
def apply(cls, stack, model, feeder_file, output_substation_folder, base_dir=None, substation_folder=None): logger.debug("Starting add_substations") if base_dir and (not os.path.exists(feeder_file)): feeder_file = os.path.join(base_dir,feeder_file) if base_dir and (not os.path.exists(output_substation_folder)): output_substation_folder = os.path.join(base_dir,output_substation_folder) if substation_folder == None: substation_folder = os.path.join(os.path.dirname(__file__),'resources') # Need to load OpenDSS files later. Make sure we can find the required layer. from_opendss_layer_dir = None # first look in stack for layer in stack: if layer.name == 'From OpenDSS': from_opendss_layer_dir = layer.layer_dir break # then try this layer's library directory if from_opendss_layer_dir is None: from_opendss_layer_dir = os.path.join(os.path.dirname(os.path.dirname(__file__)),'from_opendss') if not os.path.exists(from_opendss_layer_dir): msg = "Cannot find the 'From OpenDSS' layer." logger.error(msg) raise Exception(msg) logger.debug("Building the model network") model.build_networkx(source=None) # Used to remove internal edges in substation df = pd.read_csv(feeder_file,' ') #The RNM file specifying which feeders belong to which substation substations = {} for index,row in df.iterrows(): substation = row.iloc[1] feeder = row.iloc[2] buses = feeder.split('->') bus1 = buses[1] bus2 = buses[0] if bus1[0:4].lower() == 'ucmv': # Not swapped if MV load connected to it bus1 = buses[0] bus2 = buses[1] adjusted_feeder = bus1+'->'+bus2 #In the feeder file bus1 and bus2 are swapped if substation in substations: substations[substation].add(adjusted_feeder) else: substations[substation]=set([adjusted_feeder]) logger.debug("Building to_delete and modifier") to_delete = Store() modifier = Modifier() for sub in substations: #sub is the name of the substation and substations[sub] is a set of all the connected feeders logger.debug("Processing substation {}. There are {} in total.".format(sub,len(substations))) all_nodes = [] subname = sub.replace('.','') subname = subname.lower() all_nodes.append(subname) hv_subname = subname+'->'+subname.replace('1247','69')+'_s' all_nodes.append(hv_subname) sourcenode = hv_subname+'_s' #Source point of connection to the substation all_nodes.append(sourcenode) feeder_names = [] # Feeder point of connection to the substation rated_power = None emergency_power = None loadloss = None noloadloss = None reactance = None for feeder in substations[sub]: feeder_name = feeder.replace('.','')+'_s' feeder_name = feeder_name.lower() feeder_names.append(feeder_name) all_nodes.append(feeder_name) all_nodes_set = set(all_nodes) internal_edges = model.get_internal_edges(all_nodes_set) for n in all_nodes_set: obj_name = type(model[n]).__name__ base_obj = globals()[obj_name](to_delete) base_obj.name = n for e in internal_edges: obj_name = type(model[e]).__name__ if obj_name == 'PowerTransformer': reactance = model[e].reactances[0] # Assume the same for all windings in a substation loadloss = model[e].loadloss noloadloss = model[e].noload_loss rated_power = model[e].windings[0].rated_power # Assume the same for all windings in a substation emergency_power = model[e].windings[0].emergency_power # Assume the same for all windings in a substationr base_obj = globals()[obj_name](to_delete) base_obj.name = e num_model_feeders = len(substations[sub]) not_allocated = True # Write the substation files to disk. These are then read and added for sub_file in os.listdir(substation_folder): # Important these must be listed in increasing order if len(pd.read_csv(substation_folder+'/%s/feeders.csv'%sub_file))>= num_model_feeders: generic_source = list(pd.read_csv(substation_folder+'/%s/source.csv'%sub_file)['source']) generic_feeders = list(pd.read_csv(substation_folder+'/%s/feeders.csv'%sub_file)['feeders'])[:num_model_feeders] #Select the first n feeder bays of the substation as required generic_nodes = list(pd.read_csv(substation_folder+'/%s/all_nodes.csv'%sub_file)['node']) generic_substation_fp = open(substation_folder+'/%s/%s.dss'%(sub_file,sub_file),'r') generic_substation_dss = generic_substation_fp.read() generic_substation_fp.close() substation_dss = generic_substation_dss.replace(generic_source[0],'%s'%sourcenode) # Replace source node for i in range(len(feeder_names)): substation_dss = substation_dss.replace(generic_feeders[i],'%s'%feeder_names[i]) # Replace feeder nodes # TODO: do this in a better way. for i in range(len(generic_nodes)): #Replace any remaining nodes that haven't been changed yet. Unallocated feeder heads are managed here substation_dss = substation_dss.replace(generic_nodes[i]+ ' ','%s_%s_%s '%(sub_file,subname,generic_nodes[i])) substation_dss = substation_dss.replace(generic_nodes[i]+ '.','%s_%s_%s.'%(sub_file,subname,generic_nodes[i])) substation_dss = substation_dss.replace('Line.','Line.%s_%s_'%(sub_file,subname)) substation_dss = substation_dss.replace('LineCode.','LineCode.%s_%s_'%(sub_file,subname)) substation_dss = substation_dss.replace('Capacitor.','Capacitor.%s_%s_'%(sub_file,subname)) substation_dss = substation_dss.replace('CapControl.','CapControl.%s_%s_'%(sub_file,subname)) substation_dss = substation_dss.replace('Monitor.','Monitor.%s_%s_'%(sub_file,subname)) substation_dss = substation_dss.replace('Relay.','Relay.%s_%s_'%(sub_file,subname)) substation_dss = substation_dss.replace('Transformer.','Transformer.%s_%s_'%(sub_file,subname)) substation_dss = substation_dss.replace('transformer=','transformer=%s_%s_'%(sub_file,subname)) substation_dss = substation_dss.replace('Regcontrol.','Regcontrol.%s_%s_'%(sub_file,subname)) # TODO: WARNING: This is a total hack to replace the substation attributes and should not be used long-term. # This is very specific to the substations used in dataset3 and is very case sensative. substation_dss = substation_dss.replace('kvas=(30000, 30000)','kvas=(%f, %f)'%(rated_power,rated_power)) substation_dss = substation_dss.replace('kvas=(25000, 25000)','kvas=(%f, %f)'%(rated_power/3.0,rated_power/3.0)) substation_dss = substation_dss.replace('%noloadloss=0.12','%noloadloss={noll}'.format(noll=noloadloss)) substation_dss = substation_dss.replace('%loadloss=0.1','%loadloss={ll}'.format(ll=loadloss)) substation_dss = substation_dss.replace('XHL=0.1','XHL=%f'%(reactance)) if not os.path.isdir(output_substation_folder+'/%s'%subname): os.makedirs(output_substation_folder+'/%s'%subname) substation_output = open(output_substation_folder+'/%s/substation.dss'%(subname),'w') substation_output.write(substation_dss) substation_output.close() buscoords = open(output_substation_folder+'/%s/Buscoords.dss'%subname,'w') buscoords.close() masterfile_fp = open(substation_folder+'/%s/master.dss'%sub_file,'r') masterfile_dss = masterfile_fp.read() masterfile_fp.close() masterfile_dss = masterfile_dss.replace('SourceBus',sourcenode) master_output = open(output_substation_folder+'/%s/master.dss'%subname,'w') master_output.write(masterfile_dss) master_output.close() #shutil.copyfile(substation_folder+'/%s/master.dss'%sub_file,output_substation_folder+'/%s/master.dss'%subname) not_allocated = False break if not_allocated: raise('Substation too small. %d feeders needed. Exiting...'%(num_model_feeders)) logger.debug("Creating reduced and final models") reduced_model = modifier.delete(model, to_delete) final_model = reduced_model from_opendss_layer = Layer(from_opendss_layer_dir) from_opendss_layer.args.mode = ArgMode.USE from_opendss_layer.kwargs.mode = ArgMode.USE for p, dirnames, filenames in os.walk(output_substation_folder): for sub_folder in dirnames: logger.debug("Processing output_substation_folder/{}".format(sub_folder)) from_opendss_layer.args[0] = os.path.join(output_substation_folder,sub_folder,'master.dss') from_opendss_layer.args[1] = os.path.join(output_substation_folder, sub_folder, 'Buscoords.dss') from_opendss_layer.kwargs['read_power_source'] = False s = Stack() from_opendss_layer.run_layer(s) substation_model = s.model logger.debug("Adding model from {} to final_model".format(sub_folder)) final_model = modifier.add(final_model, substation_model) break logger.debug("Returning {!r}".format(final_model)) return final_model