From 40b0c305b6cf1a908fc8c112b3222c03d68c77a3 Mon Sep 17 00:00:00 2001 From: wwang2 Date: Mon, 5 Aug 2024 15:50:11 -0600 Subject: [PATCH] improve dtrans --- ditto/modify/system_structure.py | 5 ++ ditto/readers/synergi/read.py | 48 +++++++----- ditto/writers/opendss/write.py | 127 +++++++++++++++++++------------ 3 files changed, 111 insertions(+), 69 deletions(-) diff --git a/ditto/modify/system_structure.py b/ditto/modify/system_structure.py index 8afa696f..d90ac63f 100644 --- a/ditto/modify/system_structure.py +++ b/ditto/modify/system_structure.py @@ -237,6 +237,8 @@ def set_nominal_voltages_recur(self, *args): ] ) num_phases = len(self.model[trans_name].windings[0].phase_windings) + if self.model[trans_name].is_threephaseunit == 0: + new_value = new_value *1.732 if num_phases!=3: new_value = new_value *1.732 print(f"1num_phases={num_phases}") @@ -255,6 +257,9 @@ def set_nominal_voltages_recur(self, *args): ] ) num_phases = len(self.model[trans_name].windings[0].phase_windings) + if self.model[trans_name].is_threephaseunit == 0: # wenbo added this because naming convention in synergi warehouse.mdb + new_value = new_value *1.732 + if num_phases!=3: new_value = new_value *1.732 print(f"2trans_name={trans_name}") diff --git a/ditto/readers/synergi/read.py b/ditto/readers/synergi/read.py index 58f55fd8..516323ff 100644 --- a/ditto/readers/synergi/read.py +++ b/ditto/readers/synergi/read.py @@ -172,6 +172,13 @@ def parse(self, model): NodeID = self.get_data("Node", "NodeId") NodeX = self.get_data("Node", "X") NodeY = self.get_data("Node", "Y") + + + NodeX = self.get_data("Node", "Latitude") # lat + NodeY = self.get_data("Node", "Longitude") # lon + + + ## Preferences ######## LengthUnits = self.get_data("SAI_Equ_Control", "LengthUnits") @@ -1990,16 +1997,19 @@ def parse(self, model): print("--> Parsing Loads...") load_bus_map = {} + bus_load_map = {} previous_load = None # this line is to check dubplicated load from previous load + previous_downstream_load = None for i, obj in enumerate(LoadName): # Create a Load DiTTo object api_load = Load(model) - # if obj.replace(" ", "_").lower() == '230632.0DF0'.lower(): - # breakpoint() + # if obj.replace(" ", "_").lower() == '440723.1df0'.lower(): + # print('hold') # Set the name api_load.name = "Load_" + obj.replace(" ", "_").lower() if api_load.name == previous_load: + #continue api_load.name = "Load_" + obj.replace(" ", "_").lower() + '_dup' #print(f'load_name = {api_load.name}') @@ -2122,6 +2132,15 @@ def parse(self, model): if api_load.name not in load_bus_map: load_bus_map[obj.replace(" ", "_").lower()] = [api_load.connecting_element, api_load.phase_loads] + + downstream_load = [load.p for load in api_load.phase_loads] + + if 'dup' in api_load.name: + bus_load_map[api_load.connecting_element] += np.array(downstream_load) + else: + bus_load_map[api_load.connecting_element] = downstream_load + + #previous_downstream_load = downstream_load # else: # wenbo edit: kva is not read in synergi # # Create the PhaseLoad DiTTo object @@ -2217,24 +2236,6 @@ def parse(self, model): api_load.vmin = 0.65 api_load.vmax = 1.1 - # print('phase_load.phase={}, phase_load.p={}, phase_load.q={}'.format(phase_load.phase, phase_load.p, phase_load.q)) - - # else: - # - # # if there is no load information, place a small load instead of writing zero to the load - # phase_load = PhaseLoad(model) - # - # # Set the Phase - # phase_load.phase = phase - # - # # Set P - # phase_load.p = 0.01 - # - # # Set Q - # phase_load.q = 0.01 - # - # # Add the PhaseLoad to the list - # api_load.phase_loads.append(phase_load) @@ -2280,7 +2281,12 @@ def parse(self, model): api_transformer.conn = ['wye' , 'wye'] api_transformer.kvas = api_transformer.ConnKvaPh - + # check if kvas and ConnKvaPh is enough to carry the load, then update it if needed + bus_loads = bus_load_map[api_transformer.connecting_element] + need_kvas = bus_loads + if sum(api_transformer.kvas) 0: pf_local = abs(i.power_factor) txt += " kVA={kva}".format( kva=i.active_rating / pf_local * 10**-3 @@ -1578,6 +1598,13 @@ def write_PVs(self, model): ** -3 # Set the inverter to be oversized by 10% if active rating not specified ) # DiTTo in watts + else: + # wenbo note: if no active rating, kva need to be defined still. + pf_local = 1.0 + txt += " kVA={kva}".format( + kva=i.rated_power / pf_local * 10**-3 + ) # DiTTo in watts + if hasattr(i, "rated_power") and i.rated_power is not None: txt += " Pmpp={kw}".format( kw=i.rated_power @@ -1585,16 +1612,16 @@ def write_PVs(self, model): ** -3 # Set the inverter to be oversized by 10% if active rating not specified ) # DiTTo in watts - if hasattr(i, "reactive_rating") and i.reactive_rating is not None: - if self.opendss_version >= 9: - kvarlimit_key = " kvarMax=" - else: - kvarlimit_key = " kvarlimit=" - txt += kvarlimit_key + "{kvar}".format( - kvar=i.reactive_rating - * 10 - ** -3 # Set the inverter to be oversized by 10% if active rating not specified - ) # DiTTo in watts + # if hasattr(i, "reactive_rating") and i.reactive_rating is not None: + # if self.opendss_version >= 9: + # kvarlimit_key = " kvarMax=" + # else: + # kvarlimit_key = " kvarlimit=" + # txt += kvarlimit_key + "{kvar}".format( + # kvar=i.reactive_rating + # * 10 + # ** -3 # Set the inverter to be oversized by 10% if active rating not specified + # ) # DiTTo in watts # connection type if hasattr(i, "connection_type") and i.connection_type is not None: @@ -1636,7 +1663,7 @@ def write_PVs(self, model): if hasattr(i, "control_type") and ( i.control_type is None or i.control_type == "powerfactor" ): # use powerfactor as default mode - if hasattr(i, "power_factor") and i.power_factor is not None: + if hasattr(i, "power_factor") and i.power_factor is not None and i.power_factor>0: txt += " Model=1 pf={power_factor}".format( power_factor=i.power_factor ) @@ -2086,11 +2113,14 @@ def write_loads(self, model): # Name if hasattr(i, "name") and i.name is not None: + + if i.name not in load_list: txt += "New Load." + i.name load_list.append(i.name) else: + continue txt += "New Load." + i.name + '_dup' #print(f"i.name = {i.name}") else: @@ -2145,6 +2175,7 @@ def write_loads(self, model): #print(f"i.phase_loads length = {len(i.phase_loads)}") if self.model_dtrans: if i.nominal_voltage < 300: + txt += " kV={volt}".format(volt=i.nominal_voltage * 10**-3) # Wenbo: This is added because single phase load should be L-N, not L-L elif hasattr(i, "phase_loads") and i.phase_loads is not None and len(i.phase_loads)==1: @@ -2152,7 +2183,7 @@ def write_loads(self, model): txt += " kV=0.24" self.mv_load_voltage_dict[i.name.split('Load_')[1]] = round(i.nominal_voltage * 10**-3/1.732,2) elif hasattr(i, "phase_loads") and i.phase_loads is not None and len(i.phase_loads)>1: - txt += " kV=0.208" + txt += " kV=0.416" self.mv_load_voltage_dict[i.name.split('Load_')[1]] = round(i.nominal_voltage * 10**-3,2) #txt += " kV={volt}".format(volt=i.nominal_voltage * 10**-3) else: @@ -2160,7 +2191,7 @@ def write_loads(self, model): txt += " kV={volt}".format(volt=i.nominal_voltage * 10**-3) # Wenbo: This is added because single phase load should be L-N, not L-L elif hasattr(i, "phase_loads") and i.phase_loads is not None and len(i.phase_loads)==1: - txt += " kV={volt}".format(volt=round(i.nominal_voltage * 10**-3/math.sqrt(3),2)) + txt += " kV={volt}".format(volt=round(i.nominal_voltage * 10**-3/math.sqrt(3),2)) else: txt += " kV={volt}".format(volt=i.nominal_voltage * 10**-3) @@ -4277,7 +4308,7 @@ def write_master_file(self, model): ): fp.write("Redirect {file}\n".format(file=file)) - _baseKV_list_ = list(self._baseKV_) + [0.208, 0.416] + _baseKV_list_ = list(self._baseKV_) + [0.24, 0.416] _baseKV_list_ = sorted(_baseKV_list_) fp.write("\nSet Voltagebases={}\n".format(_baseKV_list_))