diff --git a/ditto/models/load.py b/ditto/models/load.py index 8dfae3c6..6247e0b4 100644 --- a/ditto/models/load.py +++ b/ditto/models/load.py @@ -124,6 +124,12 @@ class Load(DiTToHasTraits): default_value=False, ) + # TODO: should this be in phase load + is_grounded = Bool( + help="""Flag that indicates whether load is grounded or not""", + default_value=False, + ) + # Modification: Nicolas (July 2018) is_center_tap = Bool( help="""Flag that indicates whether the element is a center tap load or not.""", diff --git a/ditto/modify/system_structure.py b/ditto/modify/system_structure.py index b607c28a..c7ea4de4 100644 --- a/ditto/modify/system_structure.py +++ b/ditto/modify/system_structure.py @@ -65,7 +65,7 @@ def __init__(self, model, *args): 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)) + raise ValueError("Multiple sourcebus found: {srcs}".format(srcs=srcs)) else: source = srcs[0] @@ -840,24 +840,19 @@ def center_tap_load_preprocessing(self): # Try to get the corresponding DiTTo object by name # Note: This should work if set_names() has been called before... # If it fails, raise an error... - try: - t_obj = self.model[t_name] - # Get the phases and clean - _phases = np.array( + t_obj = self.model[t_name] + # Get the phases and clean + _phases = np.array( + [ [ - [ - phase_winding.phase - for phase_winding in winding.phase_windings - ] - for winding in t_obj.windings + phase_winding.phase + for phase_winding in winding.phase_windings ] - ) - _phases = np.unique(_phases.flatten()) - phases.append(_phases) - except: - raise ValueError( - "Unable to retrieve DiTTo object with name {}".format(t_name) - ) + for winding in t_obj.windings + ] + ) + _phases = np.unique(_phases.flatten()) + phases.append(_phases) # Now, we have all the transformers and their phases # The next step is to loop over the loads, modify their phases according to the upstream transformer phase diff --git a/ditto/readers/abstract_reader.py b/ditto/readers/abstract_reader.py index cfcf1325..2bf5330b 100644 --- a/ditto/readers/abstract_reader.py +++ b/ditto/readers/abstract_reader.py @@ -1,4 +1,4 @@ -# coding: utf8 +# -*- coding: utf-8 -*- from __future__ import absolute_import, division, print_function from builtins import super, range, zip, round, map @@ -149,9 +149,9 @@ def convert_to_meters(self, quantity, unit, **kwargs): if unit.lower() == "mi": if inverse: - return quantity / 1609.34 + return quantity / 1609.344 else: - return 1609.34 * quantity + return 1609.344 * quantity elif unit.lower() == "km": if inverse: @@ -258,9 +258,9 @@ def convert_from_meters(self, quantity, unit, **kwargs): if unit.lower() == "mi": if inverse: - return quantity / 0.000621371 + return quantity * 1609.344 else: - return 0.000621371 * quantity + return quantity / 1609.344 elif unit.lower() == "km": if inverse: @@ -658,9 +658,7 @@ def get_phase_impedances( ].concentric_neutral_outside_diameter if wire_list[i].concentric_neutral_resistance is None: - concentric_neutral_resistance_meters = ( - 0.000269 - ) # Aluminium wire resistivity per meter for 4/0 wire (Nexans) + concentric_neutral_resistance_meters = 0.000269 # Aluminium wire resistivity per meter for 4/0 wire (Nexans) self.logger.warning( "Warning - using default concentric_neutral_resistance of " + str(concentric_neutral_resistance_meters) @@ -671,9 +669,7 @@ def get_phase_impedances( ].concentric_neutral_resistance if wire_list[i].resistance is None: - resistance_meters = ( - 0.000269 - ) # Aluminium wire resistivity per meter for 4/0 wire (Nexans) + resistance_meters = 0.000269 # Aluminium wire resistivity per meter for 4/0 wire (Nexans) self.logger.warning( "Warning - using default resistance of " + str(resistance_meters) diff --git a/ditto/readers/opendss/read.py b/ditto/readers/opendss/read.py index 8bd84ee3..89383c64 100644 --- a/ditto/readers/opendss/read.py +++ b/ditto/readers/opendss/read.py @@ -271,27 +271,27 @@ def set_nominal_voltages(self, model): # Set the active bus dss.Circuit.SetActiveBus(bus_name) # Set the nominal voltage of the corresponding node in the DiTTo Model - try: - model[bus_name.lower()].nominal_voltage = ( - dss.Bus.kVBase() * math.sqrt(3) * 10 ** 3 - ) # DiTTo in volts - except: - print("Could not set nominal voltage for bus {b}".format(b=bus_name)) - pass + model[bus_name.lower()].nominal_voltage = ( + dss.Bus.kVBase() * math.sqrt(3) * 10 ** 3 + ) # DiTTo in volts for obj in model.models: if hasattr(obj, "nominal_voltage") and obj.nominal_voltage is None: # If the object has a connecting_element attribute if hasattr(obj, "connecting_element"): try: - obj.nominal_voltage = model[ - obj.connecting_element - ].nominal_voltage + if model[obj.connecting_element].nominal_voltage is not None: + obj.nominal_voltage = model[ + obj.connecting_element + ].nominal_voltage except: pass elif hasattr(obj, "from_element"): try: - obj.nominal_voltage = model[obj.from_element].nominal_voltage + if model[obj.from_element].nominal_voltage is not None: + obj.nominal_voltage = model[ + obj.from_element + ].nominal_voltage except: pass elif isinstance(obj, PowerTransformer) or isinstance(obj, Regulator): @@ -306,9 +306,10 @@ def set_nominal_voltages(self, model): and obj.windings[x].nominal_voltage is None ): try: - obj.windings[x].nominal_voltage = model[ - mapp[x] - ].nominal_voltage + if model[mapp[x]].nominal_voltage is not None: + obj.windings[x].nominal_voltage = model[ + mapp[x] + ].nominal_voltage except: pass @@ -590,6 +591,8 @@ def parse_nodes(self, model, **kwargs): # Loop over the lines to get the phases for name, data in lines.items(): + if not data["enabled"]: + continue # Parse bus1 data if "." in data["bus1"]: temp = data["bus1"].split(".") @@ -803,9 +806,8 @@ def parse_lines(self, model): # Skip Line object if disabled and not a switch # (Otherwise it could mean that the switch is open) - if not data["Switch"] and not data["enabled"]: + if not data["enabled"]: continue - api_line = Line(model) api_line.feeder_name = self.source_name @@ -857,6 +859,7 @@ def parse_lines(self, model): pass if line_unit.lower() not in ["ft", "mi", "m", "km", "kft", "cm", "in"]: + # raise NotImplementedError("OpenDSS line length is none") line_unit = u"km" # length @@ -945,76 +948,71 @@ def parse_lines(self, model): Rmatrix = None Xmatrix = None + # print(Rmatrix) # Matrices are in Ohms per some unit distance which is the unit defined in the line if line_unit is not None and Rmatrix is not None and Xmatrix is not None: - try: - if ( - isinstance(Rmatrix, list) - and len(Rmatrix) == 1 - and "|" in Rmatrix[0] - ): - rowsR = Rmatrix[0].split("|") - rowsR = list(map(lambda x: x.strip(), rowsR)) - new_Rmatrix = [] - for rowR in rowsR: - new_Rmatrix.append([]) - new_Rmatrix[-1] += list( - map( - lambda x: self.convert_to_meters( - float(x.strip()), line_unit, inverse=True - ), - rowR.split(" "), - ) - ) - new_Rmatrix = self.symmetrize(new_Rmatrix) - else: - new_Rmatrix = list( + if ( + isinstance(Rmatrix, list) + and len(Rmatrix) == 1 + and "|" in Rmatrix[0] + ): + rowsR = Rmatrix[0].split("|") + rowsR = list(map(lambda x: x.strip(), rowsR)) + new_Rmatrix = [] + for rowR in rowsR: + new_Rmatrix.append([]) + new_Rmatrix[-1] += list( map( - lambda x: self.convert_to_meters( - float(x), line_unit, inverse=True - ), - Rmatrix, + lambda x: float(x.strip()), + rowR.split(" "), ) ) + new_Rmatrix = self.symmetrize(new_Rmatrix) + else: + new_Rmatrix = list( + map( + lambda x: float(x.strip()), + Rmatrix, + ) + ) - if ( - isinstance(Xmatrix, list) - and len(Xmatrix) == 1 - and "|" in Xmatrix[0] - ): - rowsX = Xmatrix[0].split("|") - rowsX = list(map(lambda x: x.strip(), rowsX)) - new_Xmatrix = [] - for rowX in rowsX: - new_Xmatrix.append([]) - new_Xmatrix[-1] += list( - map( - lambda x: self.convert_to_meters( - float(x.strip()), line_unit, inverse=True - ), - rowX.split(" "), - ) - ) - new_Xmatrix = self.symmetrize(new_Xmatrix) - else: - new_Xmatrix = list( + if ( + isinstance(Xmatrix, list) + and len(Xmatrix) == 1 + and "|" in Xmatrix[0] + ): + rowsX = Xmatrix[0].split("|") + rowsX = list(map(lambda x: x.strip(), rowsX)) + new_Xmatrix = [] + for rowX in rowsX: + new_Xmatrix.append([]) + new_Xmatrix[-1] += list( map( lambda x: self.convert_to_meters( - float(x), line_unit, inverse=True + float(x.strip()), line_unit, inverse=True ), - Xmatrix, + rowX.split(" "), ) ) - new_Rmatrix = np.array(new_Rmatrix) - new_Xmatrix = np.array(new_Xmatrix) - Z = new_Rmatrix + 1j * new_Xmatrix - if Z.ndim == 1: - Z = [Z.tolist()] - else: - Z = Z.tolist() - api_line.impedance_matrix = Z - except: - pass + new_Xmatrix = self.symmetrize(new_Xmatrix) + else: + new_Xmatrix = list( + map( + lambda x: self.convert_to_meters( + float(x), line_unit, inverse=True + ), + Xmatrix, + ) + ) + new_Rmatrix = np.array(new_Rmatrix) + new_Xmatrix = np.array(new_Xmatrix) + Z = new_Rmatrix + 1j * new_Xmatrix + if Z.ndim == 1: + Z = [Z.tolist()] + else: + Z = Z.tolist() + # print(new_Rmatrix) + api_line.impedance_matrix = Z if "cmatrix" in data: Cmatrix = data["cmatrix"] @@ -1789,10 +1787,7 @@ def parse_transformers(self, model): pass # phase - try: - phase_windings[p].phase = self.phase_mapping(b1_phases[p]) - except: - pass + phase_windings[p].phase = self.phase_mapping(b1_phases[p]) regulators = dss.utils.class_to_dataframe("RegControl") for reg_name, reg_data in regulators.items(): @@ -2455,6 +2450,11 @@ def parse_loads(self, model): # Get the actual phase numbers (in the connection data) try: + if ".0" in data["bus1"]: + api_load.is_grounded = True + else: + api_load.is_grounded = False + if "." in data["bus1"]: temp = data["bus1"].split(".") bus = temp[0] @@ -2528,8 +2528,14 @@ def parse_loads(self, model): pass # Phase Loads - kW /= float(len(phases)) # Load assumed balanced - kva /= float(len(phases)) # Load assumed balanced + # TODO: is_grounded is required here + + if api_load.is_grounded: + kW /= float(len(phases) - 1) # Load assumed balanced + kva /= float(len(phases) - 1) # Load assumed balanced + else: + kW /= float(len(phases)) # Load assumed balanced + kva /= float(len(phases)) # Load assumed balanced _phase_loads = [] @@ -2538,42 +2544,24 @@ def parse_loads(self, model): _phase_loads.append(PhaseLoad(model)) _phase_loads[i].phase = self.phase_mapping(p) - # Case one: KW and pf - if kW is not None and pf is not None: + # Try to get the model + if load_model is not None: + _phase_loads[i].model = load_model + + if load_model == 1: + _phase_loads[i].p = kW * 10 ** 3 # DiTT0 in watts + _phase_loads[i].q = ( + kW * 10 ** 3 * np.sqrt(1 - pf ** 2) + ) / pf # DiTT0 in var + elif kW is not None and pf is not None: + # Case one: KW and pf _phase_loads[i].p = kW * 10 ** 3 # DiTT0 in watts _phase_loads[i].q = ( kW * 10 ** 3 * np.sqrt(1 - pf ** 2) ) / pf # DiTT0 in var - - # Case two: kvar and pf - elif kva is not None and pf is not None: - # Handle the special case where pf=1 - if pf == 1: - # in this case, pure reactive power - _phase_loads[i].p = 0.0 - else: - _phase_loads[i].p = (pf * kvar * 10 ** 3) / np.sqrt( - 1 - pf ** 2 - ) # DiTT0 in watts - _phase_loads[i].q = kva * 10 ** 3 # DiTT0 in var - - # Case three kW and kvar - elif kW is not None and kva is not None: - _phase_loads[i].p = kW * 10 ** 3 # DiTT0 in Watts - _phase_loads[i].q = kvar * 10 ** 3 # DiTT0 in var - - # Try to get the model - try: - _model = int(data["model"]) - except: - _model = None - pass - - if load_model is not None: - _phase_loads[i].model = load_model # ZIPV model (model==8) - if _model == 8: + if load_model == 8: # Try to get the ZIPV coefficients try: ZIPV = list(map(lambda x: float(x), data["ZIPV"].split())) diff --git a/ditto/writers/abstract_writer.py b/ditto/writers/abstract_writer.py index 90591126..bc1927c5 100644 --- a/ditto/writers/abstract_writer.py +++ b/ditto/writers/abstract_writer.py @@ -1,4 +1,4 @@ -# coding: utf8 +# -*- coding: utf-8 -*- from __future__ import absolute_import, division, print_function from builtins import super, range, zip, round, map @@ -113,9 +113,9 @@ def convert_from_meters(self, quantity, unit, **kwargs): if unit.lower() == "mi": if inverse: - return quantity / 0.000621371 + return quantity * 1609.344 else: - return 0.000621371 * quantity + return quantity / 1609.344 elif unit.lower() == "km": if inverse: diff --git a/ditto/writers/ephasor/template.xlsx b/ditto/writers/ephasor/template.xlsx new file mode 100644 index 00000000..2f9ab33b Binary files /dev/null and b/ditto/writers/ephasor/template.xlsx differ diff --git a/ditto/writers/ephasor/write.py b/ditto/writers/ephasor/write.py index 7ca05f28..20ed6e82 100644 --- a/ditto/writers/ephasor/write.py +++ b/ditto/writers/ephasor/write.py @@ -1,9 +1,10 @@ +# -*- coding: utf-8 -*- from __future__ import absolute_import, division, print_function from builtins import super, range, zip, round, map import os import math import logging - +import shutil import numpy as np import pandas as pd @@ -282,12 +283,11 @@ def line(self): if hasattr(line, "name") and line.name is not None: logger.debug("New Line." + line.name) - obj_dict["ID"][index] = line.name + obj_dict["ID"][index] = "line_" + line.name else: obj_dict["ID"][index] = "None" units = "mi" - result = "temp " logger.debug("Type " + str(line.line_type)) for wire in line.wires: @@ -362,6 +362,7 @@ def line(self): # Rmatrix, Xmatrix, and Cmatrix # Can also be defined through the linecodes (check linecodes_flag) if hasattr(line, "impedance_matrix") and line.impedance_matrix is not None: + print("using impedance matrix") # Use numpy arrays since it is much easier for complex numbers try: zz = line.impedance_matrix @@ -375,7 +376,6 @@ def line(self): ) ) - result += "Rmatrix=(" logger.debug(R.shape) for rc, row in enumerate(R): @@ -384,41 +384,14 @@ def line(self): num_str = str(ec + 1) + str(rc + 1) if num_str in valid: name = "r" + num_str + " (ohm/Mile)" - obj_dict[name][index] = self.convert_from_meters( - np.real(elt), units, inverse=True - ) - result += "{e} ".format( - e=self.convert_from_meters( - np.real(elt), units, inverse=True - ) - ) - result += "| " - result = result[:-2] # Remove the last "| " since we do not need it - result += ") " - - result += "Xmatrix=(" + obj_dict[name][index] = np.real(elt) for rc, row in enumerate(X): for ec, elt in enumerate(row): num_str = str(ec + 1) + str(rc + 1) if num_str in valid: name = "x" + num_str + " (ohm/Mile)" - obj_dict[name][index] = self.convert_from_meters( - np.real(elt), units, inverse=True - ) - - # B = 1 /(np.real(R[rc,ec]) + np.imag(elt)) - # B = 1 / Z[rc,ec] - # name = 'b' + num_str + ' (uS/Mile)' - # obj_dict[name][index] = self.convert_from_meters(np.imag(B), units, inverse=False) - result += "{e} ".format( - e=self.convert_from_meters( - np.real(elt), units, inverse=True - ) - ) - result += "| " - result = result[:-2] # Remove the last "| " since we do not need it - result += ") " + obj_dict[name][index] = np.real(elt) else: logger.debug("no matrix") @@ -428,7 +401,6 @@ def line(self): ): C = np.array(line.capacitance_matrix) kf2mil = 0.189394 - result += "Bmatrix=(" for rc, row in enumerate(C): # Should only be a real matrix for ec, elt in enumerate(row): num_str = str(ec + 1) + str(rc + 1) @@ -443,18 +415,8 @@ def line(self): # -.602 siemens per mile # convet to micor se B = 0 - # if np.imag(elt) != 0: - # B = 1 / self.convert_from_meters( - # np.imag(elt), units, inverse=True - # )*1e3*2*60*math.pi if elt != 0: - B = np.real( - self.convert_from_meters(elt, units, inverse=True) - * 1e-3 - * 2 - * 60 - * math.pi - ) + B = np.real(elt * 1e-3 * 2 * 60 * math.pi) # import pdb;pdb.set_trace() logger.debug("Siemens line units ", B) @@ -462,36 +424,7 @@ def line(self): # B = np.imag(B) * 0.000621371 * 1e6 logger.debug("done", B) - # B = self.convert_from_meters(np.imag(B), units, inverse=True) - # print B - # B = -np.real(elt) / (np.real(elt)**2 + np.imag(elt)) - # B = -np.imag(elt) / (np.real(elt)**2 + np.imag(elt)**2) - # print self.convert_from_meters(B, units, inverse=True) - # B = - np.imag(elt) / np.abs(elt)**2 - # print self.convert_from_meters(B, units, inverse=True) - # B = - self.convert_from_meters(np.imag(elt), units, inverse=True) / self.convert_from_meters(np.abs(elt), units, inverse=True) ** 2 - # print B - # B = self.convert_from_meters(B, units, inverse=True) - # print name, self.convert_from_meters(B, units, inverse=False) - # B= elt - # B / kf2mil * 2.65 - # B = B/1000 * 2.65 - # obj_dict[name][index] = B - # print B - # print line.name - # exit(0) obj_dict[name][index] = B - result += "{e} ".format( - e=self.convert_from_meters( - np.real(elt), units, inverse=True - ) - ) - - result += "| " - result = result[:-2] # Remove the last "| " since we do not need it - result += ") " - - result += "Cmatrix=(" for rc, row in enumerate(C): for ec, elt in enumerate(row): num_str = str(ec + 1) + str(rc + 1) @@ -508,9 +441,7 @@ def line(self): B = np.real(elt) * line.length logger.debug("line units ", B) - B = self.convert_from_meters( - np.real(B), units, inverse=False - ) + B = np.real(B) B = B * 1e6 # print "Siemens line units ", B /(2.6526) # obj_dict[name][index] = B /(2.6526) @@ -529,25 +460,6 @@ def line(self): # print line.name # exit(0) - # obj_dict[name][index]=self.convert_from_meters(np.real(elt), units, inverse=True) * 2.65 - result += "{e} ".format( - e=self.convert_from_meters( - np.real(elt), units, inverse=True - ) - ) - result += "| " - result = result[:-2] # Remove the last "| " since we do not need it - result += ") " - - # # Ampacity - # if hasattr(line.wires[0], 'ampacity') and line.wires[0].ampacity is not None: - # result += ' normamps={na}'.format(na=line.wires[0].ampacity) - # - # # Emergency ampacity - # if hasattr(line.wires[0], 'ampacity_emergency') and line.wires[0].ampacity_emergency is not None: - # result += ' emergamps={emer}'.format(emer=line.wires[0].ampacity_emergency) - - logger.debug(result) df1 = pd.DataFrame(obj_dict) df1 = df1[ [ @@ -600,38 +512,41 @@ def transformer(self): :rtype: dataframe """ - obj_dict = { - "ID": [], - "Num Phases": [], - "W1Bus A": [], - "W1Bus B": [], - "W1Bus C": [], - "W1V (kV)": [], - "W1S_base (kVA)": [], - "W1R (pu)": [], - "W1Conn. type": [], - "W2Bus A": [], - "W2Bus B": [], - "W2Bus C": [], - "W2V (kV)": [], - "W2S_base (kVA)": [], - "W2R (pu)": [], - "W2Conn. type": [], - "Mutual Impedance": [], - "Tap A": [], - "Tap B": [], - "Tap C": [], - "Lowest Tap": [], - "Highest Tap": [], - "Min Range (%)": [], - "Max Range (%)": [], - "X (pu)": [], - "Z0 leakage(pu)": [], - "Z1 leakage(pu)": [], - "X0/R0": [], - "X1/R1": [], - "No Load Loss(kW)": [], - } + + column_names = [ + "ID", + "Num Phases", + "W1Bus A", + "W1Bus B", + "W1Bus C", + "W1V (kV)", + "W1S_base (kVA)", + "W1R (pu)", + "W1Conn. type", + "W2Bus A", + "W2Bus B", + "W2Bus C", + "W2V (kV)", + "W2S_base (kVA)", + "W2R (pu)", + "W2Conn. type", + "Mutual Impedance", + "Tap A", + "Tap B", + "Tap C", + "Lowest Tap", + "Highest Tap", + "Min Range (%)", + "Max Range (%)", + "X (pu)", + "Z0 leakage(pu)", + "Z1 leakage(pu)", + "X0/R0", + "X1/R1", + "No Load Loss(kW)", + ] + + obj_dict = {c: [] for c in column_names} ### Check for a sperated 3 phase transformer. Line to netural or whatever index = -1 @@ -664,7 +579,7 @@ def transformer(self): "name": i.name, "i": index, "kv1": i.windings[0].nominal_voltage, - "Tap " + pp: i.windings[0].phase_windings[0].tap_position, + "Tap " + pp: 0, # i.windings[0].phase_windings[0].tap_position, "kva": i.windings[0].rated_power, } else: @@ -697,7 +612,7 @@ def transformer(self): logger.debug(index) if hasattr(i, "name") and i.name is not None: logger.debug("New Transformer." + i.name) - obj_dict["ID"][index] = i.name + obj_dict["ID"][index] = "transformer_" + i.name else: obj_dict["ID"][index] = "None" @@ -710,9 +625,9 @@ def transformer(self): if len(i.windings) >= 2: logger.debug("here", i.reactances, i.reactances[0], i.name) - obj_dict["X (pu)"][index] = i.reactances[ - 0 - ] # TODO check currently opendss reads in reactances is defined as [value1, value2, ...] for each winding type. May need to change. + obj_dict["X (pu)"][index] = ( + i.reactances[0] / 100 + ) # TODO check currently opendss reads in reactances is defined as [value1, value2, ...] for each winding type. May need to change. if hasattr(i, "windings") and i.windings is not None: N_phases = [] for winding_num, winding in enumerate(i.windings): @@ -774,6 +689,8 @@ def transformer(self): phases = ["A", "B", "C"] N_phases.append(len(winding.phase_windings)) for pw in winding.phase_windings: + if pw.phase is None: + continue if winding_num == 0: obj_dict["W1Bus " + phases[phase_cnt]][index] = ( i.from_element + "_" + pw.phase.lower() @@ -832,51 +749,57 @@ def transformer(self): obj_dict["Min Range (%)"][index] = 10 obj_dict["Max Range (%)"][index] = 10 obj_dict["Mutual Impedance"][index] = int(0) - obj_dict["Num Phases"][index] = int(N_phases[0]) - obj_dict["Z0 leakage(pu)"][index] = int(0) - obj_dict["Z1 leakage(pu)"][index] = int(0) - obj_dict["X0/R0"][index] = int(0) - obj_dict["X1/R1"][index] = int(0) - obj_dict["No Load Loss(kW)"][index] = int(0) + if int(N_phases[0]) != 1: + obj_dict["Num Phases"][index] = 3 + else: + obj_dict["Num Phases"][index] = 1 + obj_dict["Z0 leakage(pu)"][index] = None + obj_dict["Z1 leakage(pu)"][index] = None + obj_dict["X0/R0"][index] = None + obj_dict["X1/R1"][index] = None + obj_dict["No Load Loss(kW)"][index] = None df4 = pd.DataFrame(obj_dict) logger.debug("df4") logger.debug(df4) - df4 = df4[ - [ - "ID", - "Num Phases", - "W1Bus A", - "W1Bus B", - "W1Bus C", - "W1V (kV)", - "W1S_base (kVA)", - "W1R (pu)", - "W1Conn. type", - "W2Bus A", - "W2Bus B", - "W2Bus C", - "W2V (kV)", - "W2S_base (kVA)", - "W2R (pu)", - "W2Conn. type", - "Mutual Impedance", - "Tap A", - "Tap B", - "Tap C", - "Lowest Tap", - "Highest Tap", - "Min Range (%)", - "Max Range (%)", - "X (pu)", - "Z0 leakage(pu)", - "Z1 leakage(pu)", - "X0/R0", - "X1/R1", - "No Load Loss(kW)", - ] - ] + df4 = df4[column_names] + + # df4.columns = [ + # [ + # "", + # "", + # "winding From", + # "", + # "", + # "", + # "", + # "", + # "", + # "winding To", + # "", + # "", + # "", + # "", + # "", + # "", + # " ", + # "", + # "", + # "", + # "", + # "", + # "", + # "", + # "", + # "", + # "", + # "", + # "", + # "", + # ], + # column_names, + # ] return df4 @@ -932,7 +855,7 @@ def source(self): # transformer.to_element + "_" + pw.phase.lower() # ) # else: # just a powersource object - obj_dict["ID"][index] = i.name + obj_dict["ID"][index] = "powersource_" + i.name obj_dict["Angle (deg)"][index] = i.phase_angle obj_dict["V (kV)"][index] = i.nominal_voltage / 1000 if i.emergency_power is not None: @@ -1027,13 +950,12 @@ def bus(self): ) # PAG No generators so all nodes are PQ buses else: obj_dict["Type"].append("PQ") - if "a" in letter_phases: obj_dict["Angle (deg)"].append(0) if "b" in letter_phases: - obj_dict["Angle (deg)"].append(120) - if "c" in letter_phases: obj_dict["Angle (deg)"].append(-120) + if "c" in letter_phases: + obj_dict["Angle (deg)"].append(120) # if len(letter_phases) == 1: # obj_dict["Angle (deg)"].append(0) @@ -1078,6 +1000,48 @@ def bus(self): logger.debug(obj_dict) return df9 + def capacitor(self): + """ + Create capacitors + """ + obj_dict = { + "Bus1": [], + "Bus2": [], + "Bus3": [], + "ID": [], + "P1(kW)": [], + "Q1(kVAr)": [], + "P2 (kW)": [], + "Q2 (kVAr)": [], + "P3(kW)": [], + "Q3 (kVAr)": [], + "kV (ph-gr RMS)": [], + "Status1": [], + "Status2": [], + "Status3": [], + } + for i, capacitor in enumerate(self._capacitors): + capacitor + + # df3 = pd.DataFrame(obj_dict) + # df3 = df3[[ + # "Bus1" + # "Bus2" + # "Bus3" + # "ID" + # "P1(kW)" + # "Q1(kVAr)" + # "P2 (kW)" + # "Q2 (kVAr)" + # "P3(kW)" + # "Q3 (kVAr)" + # "kV (ph-gr RMS)" + # "Status1" + # "Status2" + # "Status3" + # ]] + # return df3 + def load(self): """Create loads @@ -1132,17 +1096,15 @@ def load(self): or i.name[-1].lower() == "b" or i.name[-1].lower() == "c" ): - n_name = i.name[0:-1] - if not n_name in load_dict: + n_name = i.name + if n_name not in load_dict: for key, value in obj_dict.items(): value.append(None) index += 1 load_dict[n_name] = index - if "load_" == n_name[0:5]: - obj_dict["ID"][index] = n_name[5:] - else: - obj_dict["ID"][index] = n_name - + if not n_name.startswith("load_"): + n_name = f"load_{n_name}" + obj_dict["ID"][index] = n_name else: index = load_dict[n_name] else: @@ -1150,15 +1112,22 @@ def load(self): value.append(None) index += 1 if "load_" == i.name[0:5]: - obj_dict["ID"][index] = i.name[5:] + obj_dict["ID"][index] = "load_" + i.name[5:] else: - obj_dict["ID"][index] = i.name + obj_dict["ID"][index] = "load_" + i.name if hasattr(i, "nominal_voltage") and i.nominal_voltage is not None: # logger.debug(' nominal_voltage {nv};'.format(nv=i.nominal_voltage)) - obj_dict["V (kV)"][index] = ( - i.nominal_voltage / 1000.0 - ) # template 1_6 says L-L kV + # ephasorsim only accepts phase to phase values + # opendss reader reads this value incorrectly if there are three phases + if i.connection_type == "Y" and len(i.phase_loads) <= 2: + obj_dict["V (kV)"][index] = ( + i.nominal_voltage / 1000.0 * math.sqrt(3) + ) # template 1_6 says L-L kV + else: + obj_dict["V (kV)"][index] = ( + i.nominal_voltage / 1000.0 + ) # template 1_6 says L-L kV phase_cnt = 1 if hasattr(i, "phase_loads") and i.phase_loads is not None: @@ -1293,7 +1262,7 @@ def load(self): obj_dict["Type"][index] = "ZIP" phase_cnt += 1 - obj_dict["Bandwidth (pu)"][index] = 0.02 # this doesn't work right + obj_dict["Bandwidth (pu)"][index] = 0.2 # this doesn't work right if i.connection_type == "Y": obj_dict["Conn. type"][index] = "wye" @@ -1337,7 +1306,7 @@ def load(self): return df3 def write(self, m, **kwargs): - """ Write model to file + """Write model to file >>> write, model @@ -1359,56 +1328,64 @@ def write(self, m, **kwargs): self._powersources = [i for i in self.m.models if isinstance(i, PowerSource)] self._photovoltaics = [i for i in self.m.models if isinstance(i, Photovoltaic)] - modifier = system_structure_modifier(self.m) - modifier.terminals_to_phases() + # modifier = system_structure_modifier(self.m) + # modifier.terminals_to_phases() df7 = self.source() df1 = self.line() df2 = self.switch() df3 = self.load() df4 = self.transformer() + df5 = self.capacitor() df9 = self.bus() - writer = pd.ExcelWriter( - os.path.join(self.output_path, self.output_name + ".xlsx"), - engine="xlsxwriter", + excel_file_name = os.path.join(self.output_path, self.output_name + ".xlsx") + shutil.copy( + os.path.join(os.path.dirname(os.path.realpath(__file__)), "template.xlsx"), + excel_file_name, ) - df7.to_excel(writer, "Vsource 3-phase", index=False) - # workbook = writer.book - worksheet = writer.sheets["Vsource 3-phase"] - worksheet.set_column(0, 25, 16) - - df9.to_excel(writer, "Bus", index=False) - workbook = writer.book - worksheet = writer.sheets["Bus"] - - worksheet.set_column(0, 3, 16) - - df1.to_excel(writer, "Multiphase Line", index=False) - # workbook = writer.book - worksheet = writer.sheets["Multiphase Line"] - - worksheet.set_column(0, 25, 16) - df2.to_excel(writer, "Switch", index=False) - # workbook = writer.book - worksheet = writer.sheets["Switch"] - - worksheet.set_column(0, 25, 16) - - df4.to_excel(writer, "Multiphase Transformer", index=False) - # workbook = writer.book - worksheet = writer.sheets["Multiphase Transformer"] - - worksheet.set_column(0, 19, 16) - - df3.to_excel(writer, "Multiphase Load", index=False) - # workbook = writer.book - worksheet = writer.sheets["Multiphase Load"] - - worksheet.set_column(0, 19, 16) - - writer.save() + # print("Running bus") + append_df_to_excel(excel_file_name, df9, sheet_name="Bus") + # print("Running vsource") + append_df_to_excel(excel_file_name, df7, sheet_name="Vsource 3-phase") + # print("Running Multiphase") + append_df_to_excel(excel_file_name, df1, sheet_name="Multiphase Line") + # print("Running Switch") + append_df_to_excel(excel_file_name, df2, sheet_name="Switch") + # print("Running Multiphase") + append_df_to_excel(excel_file_name, df4, sheet_name="Multiphase Transformer") + # print("Running Load") + append_df_to_excel(excel_file_name, df3, sheet_name="Multiphase Load") + + +# https://stackoverflow.com/questions/20219254/how-to-write-to-an-existing-excel-file-without-overwriting-data-using-pandas +def append_df_to_excel( + filename, + df, + sheet_name="Sheet1", +): + """ + Append a DataFrame [df] to existing Excel file [filename] + into [sheet_name] Sheet. + If [filename] doesn't exist, then this function will create it. + + Parameters: + filename : File path or existing ExcelWriter + (Example: '/path/to/file.xlsx') + df : dataframe to save to workbook + sheet_name : Name of sheet which will contain DataFrame. + (default: 'Sheet1') + + Returns: None + """ + with pd.ExcelWriter( + filename, + mode="a", + engine="openpyxl", + if_sheet_exists="replace", + ) as writer: + df.to_excel(writer, sheet_name=sheet_name) if __name__ == "__main__": diff --git a/setup.py b/setup.py index fa5936af..876b681e 100644 --- a/setup.py +++ b/setup.py @@ -24,7 +24,7 @@ with open(os.path.join(here, "ditto", "version.py"), encoding="utf-8") as f: version = f.read() -version = version.split()[2].strip('"').strip("'") +version = "0.2.0" test_requires = [ "backports.tempfile", @@ -44,7 +44,7 @@ dew_requires = [numpy_dependency, "xlrd"] gridlabd_requires = ["croniter", numpy_dependency] cyme_requires = [numpy_dependency] -ephasor_requires = [numpy_dependency, "pandas"] +ephasor_requires = [numpy_dependency, "pandas", "openpxyl"] synergi_requires = [ numpy_dependency, "pandas_access", @@ -109,15 +109,16 @@ def run(self): "Programming Language :: Python :: 3.6", ], test_suite="tests", - install_requires=["click", "future", "networkx", "six", "traitlets", "json_tricks"], + install_requires=[ + "click", + "future", + "networkx", + "six", + "traitlets==4.3.3", + "json_tricks", + ], extras_require={ - "all": extras_requires - + opendss_requires - + dew_requires - + gridlabd_requires - + ephasor_requires - + cyme_requires - + synergi_requires, + "all": extras_requires + opendss_requires + dew_requires + gridlabd_requires + ephasor_requires + cyme_requires + synergi_requires, "extras": extras_requires, "cyme": cyme_requires, "dew": dew_requires,