From 266bb2e731a88fc360cfb2e8472fbd2e7b80e915 Mon Sep 17 00:00:00 2001 From: Nick Laws Date: Fri, 26 Aug 2022 09:17:45 -0600 Subject: [PATCH 01/15] cyme reader: improve warning msgs for bad load values --- ditto/readers/cyme/read.py | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/ditto/readers/cyme/read.py b/ditto/readers/cyme/read.py index 43012000..a5b42423 100644 --- a/ditto/readers/cyme/read.py +++ b/ditto/readers/cyme/read.py @@ -5714,9 +5714,7 @@ def parse_loads(self, model): try: p, q = float(settings["value1"]), float(settings["value2"]) except: - logger.warning( - "WARNING:: Skipping load on section {}".format(sectionID) - ) + logger.warning(f"Problem with load on section {sectionID} with value_type=0 (P,Q). value1={settings['value1']} value2={settings['value2']}") continue elif value_type == 1: # KVA and PF are given try: @@ -5729,9 +5727,7 @@ def parse_loads(self, model): p = kva * PF q = math.sqrt(kva ** 2 - p ** 2) except: - logger.warning( - "WARNING:: Skipping load on section {}".format(sectionID) - ) + logger.warning(f"Problem with load on section {sectionID} with value_type=1 (kVa, PF). value1={settings['value1']} value2={settings['value2']}") continue elif value_type == 2: # P and PF are given @@ -5743,16 +5739,15 @@ def parse_loads(self, model): PF /= 100.0 q = p * math.sqrt((1 - PF ** 2) / PF ** 2) else: - logger.warning("problem with PF") - logger.warning(PF) + logger.warning(f"Problem with load on section {sectionID} with value_type=2 (P, PF). value1={settings['value1']} value2={settings['value2']}") except: - logger.warning("Skipping load on section {}".format(sectionID)) + logger.warning(f"Problem with load on section {sectionID} with value_type=2 (P, PF). value1={settings['value1']} value2={settings['value2']}") continue elif value_type == 3: # AMP and PF are given # TODO logger.warning( - "WARNING:: Skipping load on section {}".format(sectionID) + "WARNING:: Skipping load on section {} because value_type=3 (AMP and PF)".format(sectionID) ) continue From 547a896cd9b5362cd8947538766e190c6ed855e6 Mon Sep 17 00:00:00 2001 From: Nick Laws Date: Fri, 26 Aug 2022 09:20:40 -0600 Subject: [PATCH 02/15] handle zero power factor in cyme reader for loads --- ditto/readers/cyme/read.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ditto/readers/cyme/read.py b/ditto/readers/cyme/read.py index a5b42423..7919193a 100644 --- a/ditto/readers/cyme/read.py +++ b/ditto/readers/cyme/read.py @@ -5733,7 +5733,9 @@ def parse_loads(self, model): try: p, PF = float(settings["value1"]), float(settings["value2"]) - if 0 <= PF <= 1: + if PF == 0: + q = 0 + elif 0 < PF <= 1: q = p * math.sqrt((1 - PF ** 2) / PF ** 2) elif 1 < PF <= 100: PF /= 100.0 From 7d968fa08179bd22003304948e6c082d658785a2 Mon Sep 17 00:00:00 2001 From: Nick Laws Date: Fri, 26 Aug 2022 09:47:11 -0600 Subject: [PATCH 03/15] handle negative power factor in cyme reader --- ditto/readers/cyme/read.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ditto/readers/cyme/read.py b/ditto/readers/cyme/read.py index 7919193a..fb20c0a6 100644 --- a/ditto/readers/cyme/read.py +++ b/ditto/readers/cyme/read.py @@ -5735,9 +5735,9 @@ def parse_loads(self, model): p, PF = float(settings["value1"]), float(settings["value2"]) if PF == 0: q = 0 - elif 0 < PF <= 1: + elif 0 < abs(PF) <= 1: q = p * math.sqrt((1 - PF ** 2) / PF ** 2) - elif 1 < PF <= 100: + elif 1 < abs(PF) <= 100: PF /= 100.0 q = p * math.sqrt((1 - PF ** 2) / PF ** 2) else: From 156a868ac23d98022028e196da4e697344b0264f Mon Sep 17 00:00:00 2001 From: Nick Laws Date: Fri, 26 Aug 2022 11:18:18 -0600 Subject: [PATCH 04/15] rm debug msg in cyme reader not sure why this is here, just printing cable sectionID --- ditto/readers/cyme/read.py | 1 - 1 file changed, 1 deletion(-) diff --git a/ditto/readers/cyme/read.py b/ditto/readers/cyme/read.py index fb20c0a6..1f4da5d8 100644 --- a/ditto/readers/cyme/read.py +++ b/ditto/readers/cyme/read.py @@ -3024,7 +3024,6 @@ def parse_lines(self, model): line_data = self.concentric_neutral_cable[settings["linecableid"]] line_data["type"] = "balanced_line" if settings["linecableid"] in self.cables: - logger.debug("cables {}".format(sectionID)) line_data = self.cables[settings["linecableid"]] line_data["type"] = "balanced_line" From 9a72c996fe82d8c3d4c019ed51ecb5c5190e8fe5 Mon Sep 17 00:00:00 2001 From: Nick Laws Date: Fri, 26 Aug 2022 13:28:29 -0600 Subject: [PATCH 05/15] cyme reader clarify parser_helper role - cannot parse a list of objects though it was taking a obj_list as input; - it would only parse the first value in the list; - now it takes in a string instead of list of string - also improved notes/documentation --- ditto/readers/cyme/read.py | 148 ++++++++++++++++++------------------- 1 file changed, 73 insertions(+), 75 deletions(-) diff --git a/ditto/readers/cyme/read.py b/ditto/readers/cyme/read.py index 1f4da5d8..aa9f9c94 100644 --- a/ditto/readers/cyme/read.py +++ b/ditto/readers/cyme/read.py @@ -328,9 +328,9 @@ def update_header_mapping(self, update): # Replace the old mapping by the new one self.header_mapping = new_mapping - def get_file_content(self, filename): + def get_file_content(self, filename: str) -> None: """ - Open the requested file and returns the content. + Open the requested file and set self.content to iter(file_pointer.readlines()) For convinience, filename can be either the full file path or: -'network': Will get the content of the network file given in the constructor @@ -719,7 +719,7 @@ def transformer_connection_configuration_mapping(self, value, winding): return res[winding] - def check_object_in_line(self, line, obj): + def check_object_in_line(self, line: str, obj: str) -> bool: """ Check if the header corresponding to object is in the given line. @@ -754,13 +754,14 @@ def check_object_in_line(self, line, obj): return np.any([x in line for x in self.header_mapping[obj]]) - def parser_helper(self, line, obj_list, attribute_list, mapping, *args, **kwargs): + def parser_helper(self, line, obj, attribute_list, mapping: dict, *args, **kwargs) -> dict: """ .. warning:: This is a helper function for the parsers. Do not use directly. - Takes as input the list of objects we want to parse as well as the list of attributes we want to extract. - Also takes the default positions of the attributes (mapping). - The function returns a list of dictionaries, where each dictionary contains the values of the desired attributes of a CYME object. + Takes as input the object we want to parse (eg. "section" maps to "[SECTION]") + as well as the list of attributes we want to extract (eg. ["sectionid", "fromnodeid", "tonodeid", "phase"]). + Also takes the default positions of the attributes (mapping), which is overwritten if "format" is found in the lines. + The function returns a dictionary of dictionaries, where each sub-dictionary contains the values of the desired attributes of a CYME object. """ if isinstance(attribute_list, list): attribute_list = np.array(attribute_list) @@ -783,11 +784,8 @@ def parser_helper(self, line, obj_list, attribute_list, mapping, *args, **kwargs result = {} - # Check the presence of headers in the given line - checks = [self.check_object_in_line(line, obj) for obj in obj_list] - - # If we have a least one - if any(checks): + # If header in line + if self.check_object_in_line(line, obj): # Get the next line next_line = next(self.content) @@ -815,7 +813,7 @@ def parser_helper(self, line, obj_list, attribute_list, mapping, *args, **kwargs # At this point, we should have the mapping for the parameters of interest # while next_line[0] not in ['[','',' ','\n','\r\n']: - while len(next_line) > 2: + while len(next_line) > 2: # blank lines separate objects in CYME .txt files if "=" not in next_line.lower(): data = next_line.split(",") @@ -824,7 +822,7 @@ def parser_helper(self, line, obj_list, attribute_list, mapping, *args, **kwargs if len(data) > 1: - while ID in result: + while ID in result: # redundant keys get *'s ID += "*" result[ID] = {} @@ -982,7 +980,7 @@ def parse_subnetwork_connections(self, model): self.subnetwork_connections.update( self.parser_helper( line, - ["subnetwork_connections"], + "subnetwork_connections", ["nodeid"], mapp_subnetwork_connections, ) @@ -1004,7 +1002,7 @@ def parse_head_nodes(self, model): headnodes = {} for line in self.content: headnodes.update( - self.parser_helper(line, ["headnodes"], ["nodeid", "networkid"], mapp) + self.parser_helper(line, "headnodes", ["nodeid", "networkid"], mapp) ) for sid, headnode in headnodes.items(): @@ -1036,11 +1034,11 @@ def parse_sources(self, model): subs = {} source_equivalents = {} - for line in self.content: + for line in self.content: # parser_helper will only loop over lines in self.content if it finds obj in the `line`; o.w. returns empty dict sources.update( self.parser_helper( line, - ["source"], + "source", ["sourceid", "nodeid", "networkid", "desiredvoltage"], mapp, ) @@ -1048,7 +1046,7 @@ def parse_sources(self, model): source_equivalents.update( self.parser_helper( line, - ["source_equivalent"], + "source_equivalent", [ "nodeid", "voltage", @@ -1078,7 +1076,7 @@ def parse_sources(self, model): for line in self.content: subs.update( self.parser_helper( - line, ["substation"], ["id", "mva", "kvll", "conn"], mapp_sub + line, "substation", ["id", "mva", "kvll", "conn"], mapp_sub ) ) if len(sources.items()) == 0: @@ -1371,7 +1369,7 @@ def parse_nodes(self, model): nodes.update( self.parser_helper( line, - ["node"], + "node", ["nodeid", "coordx", "coordy", "ratedvoltage"], mapp, **kwargs @@ -1381,7 +1379,7 @@ def parse_nodes(self, model): for line in self.content: node_connectors.update( self.parser_helper( - line, ["node_connector"], ["nodeid", "coordx", "coordy"], mapp + line, "node_connector", ["nodeid", "coordx", "coordy"], mapp ) ) @@ -1556,7 +1554,7 @@ def parse_sections(self, model): ... [SECTION] - FORMAT_section=sectionid,fromnodeid,tonodeid,phase + FORMAT_section=sectionid,fromnodeid,tonodeid,phase FORMAT_Feeder=networkid,headnodeid Feeder=feeder_1,head_feeder_1 section_1_feeder_1,node_1,node_2,ABC @@ -1947,7 +1945,7 @@ def parse_lines(self, model): self.settings, self.parser_helper( line, - ["overhead_unbalanced_line_settings"], + "overhead_unbalanced_line_settings", ["sectionid", "coordx", "coordy", "linecableid", "length"], mapp_overhead, {"type": "overhead_unbalanced"}, @@ -1964,7 +1962,7 @@ def parse_lines(self, model): self.settings, self.parser_helper( line, - ["overhead_line_settings"], + "overhead_line_settings", ["sectionid", "coordx", "coordy", "linecableid", "length"], mapp_overhead, {"type": "overhead_balanced"}, @@ -1981,7 +1979,7 @@ def parse_lines(self, model): self.settings, self.parser_helper( line, - ["overhead_byphase_settings"], + "overhead_byphase_settings", [ "sectionid", "devicenumber", @@ -2011,7 +2009,7 @@ def parse_lines(self, model): self.settings, self.parser_helper( line, - ["underground_line_settings"], + "underground_line_settings", ["sectionid", "coordx", "coordy", "linecableid", "length", "amps"], mapp_underground, {"type": "underground"}, @@ -2028,7 +2026,7 @@ def parse_lines(self, model): self.settings, self.parser_helper( line, - ["switch_settings"], + "switch_settings", ["sectionid", "coordx", "coordy", "eqid", "closedphase"], mapp_switch, {"type": "switch"}, @@ -2045,7 +2043,7 @@ def parse_lines(self, model): self.settings, self.parser_helper( line, - ["sectionalizer_settings"], + "sectionalizer_settings", ["sectionid", "coordx", "coordy", "eqid", "closedphase"], mapp_sectionalizer, {"type": "sectionalizer"}, @@ -2062,7 +2060,7 @@ def parse_lines(self, model): self.settings, self.parser_helper( line, - ["fuse_settings"], + "fuse_settings", ["sectionid", "coordx", "coordy", "eqid"], mapp_switch, # Same as switches {"type": "fuse"}, @@ -2079,7 +2077,7 @@ def parse_lines(self, model): self.settings, self.parser_helper( line, - ["recloser_settings"], + "recloser_settings", ["sectionid", "coordx", "coordy", "eqid"], mapp_switch, # Same as switches {"type": "recloser"}, @@ -2096,7 +2094,7 @@ def parse_lines(self, model): self.settings, self.parser_helper( line, - ["breaker_settings"], + "breaker_settings", ["sectionid", "coordx", "coordy", "eqid", "closedphase"], mapp_switch, # Same as switches {"type": "breaker"}, @@ -2113,7 +2111,7 @@ def parse_lines(self, model): self.settings, self.parser_helper( line, - ["network_protector_settings"], + "network_protector_settings", ["sectionid", "coordx", "coordy", "eqid", "closedphase"], mapp_switch, # Same as switches {"type": "network_protector"}, @@ -2130,7 +2128,7 @@ def parse_lines(self, model): self.settings, self.parser_helper( line, - ["section"], + "section", ["sectionid", "fromnodeid", "tonodeid", "phase"], mapp_section, ), @@ -2157,7 +2155,7 @@ def parse_lines(self, model): self.balanced_lines.update( self.parser_helper( line, - ["line"], + "line", [ "id", "phasecondid", @@ -2185,7 +2183,7 @@ def parse_lines(self, model): self.unbalanced_lines.update( self.parser_helper( line, - ["unbalanced_line"], + "unbalanced_line", [ "id", "condid_a", @@ -2225,7 +2223,7 @@ def parse_lines(self, model): self.spacings.update( self.parser_helper( line, - ["spacing_table"], + "spacing_table", [ "id", "posofcond1_x", @@ -2252,7 +2250,7 @@ def parse_lines(self, model): self.conductors.update( self.parser_helper( line, - ["conductor"], + "conductor", ["id", "diameter", "gmr", "r25", "amps", "withstandrating"], mapp_conductor, ) @@ -2267,7 +2265,7 @@ def parse_lines(self, model): self.concentric_neutral_cable.update( self.parser_helper( line, - ["concentric_neutral_cable"], + "concentric_neutral_cable", [ "id", "r1", @@ -2291,7 +2289,7 @@ def parse_lines(self, model): self.cables.update( self.parser_helper( line, - ["cable"], + "cable", ["id", "r1", "r0", "x1", "x0", "amps"], mapp_concentric_neutral_cable, ) @@ -2305,7 +2303,7 @@ def parse_lines(self, model): # self.switches.update( self.parser_helper( - line, ["switch"], ["id", "amps", "kvll"], mapp_switch_eq + line, "switch", ["id", "amps", "kvll"], mapp_switch_eq ) ) @@ -2318,7 +2316,7 @@ def parse_lines(self, model): self.fuses.update( self.parser_helper( line, - ["fuse"], + "fuse", ["id", "amps", "kvll", "interruptingrating"], mapp_network_protectors, # Same as network protectors ) @@ -2333,7 +2331,7 @@ def parse_lines(self, model): self.reclosers.update( self.parser_helper( line, - ["recloser"], + "recloser", ["id", "amps", "kvll", "interruptingrating"], mapp_network_protectors, # Same as network protectors ) @@ -2348,7 +2346,7 @@ def parse_lines(self, model): self.sectionalizers.update( self.parser_helper( line, - ["sectionalizer"], + "sectionalizer", ["id", "amps", "kvll", "interruptingrating"], mapp_sectionalizers, ) @@ -2363,7 +2361,7 @@ def parse_lines(self, model): self.breakers.update( self.parser_helper( line, - ["breaker"], + "breaker", ["id", "amps", "kvll", "interruptingrating"], mapp_network_protectors, # Same as network protectors ) @@ -2378,7 +2376,7 @@ def parse_lines(self, model): self.network_protectors.update( self.parser_helper( line, - ["network_protector"], + "network_protector", ["id", "amps", "kvll", "interruptingrating"], mapp_network_protectors, ) @@ -3876,7 +3874,7 @@ def parse_capacitors(self, model): self.settings.update( self.parser_helper( line, - ["serie_capacitor_settings"], + "serie_capacitor_settings", ["sectionid", "eqid", "coordx", "coordy"], mapp_serie_capacitor_settings, {"type": "serie"}, @@ -3892,7 +3890,7 @@ def parse_capacitors(self, model): self.settings.update( self.parser_helper( line, - ["shunt_capacitor_settings"], + "shunt_capacitor_settings", [ "sectionid", "shuntcapacitorid", @@ -3931,7 +3929,7 @@ def parse_capacitors(self, model): # self.capacitors.update( self.parser_helper( - line, ["serie_capacitor"], ["id", "reactance"], mapp_serie_capacitor + line, "serie_capacitor", ["id", "reactance"], mapp_serie_capacitor ) ) @@ -3944,7 +3942,7 @@ def parse_capacitors(self, model): self.capacitors.update( self.parser_helper( line, - ["shunt_capacitor"], + "shunt_capacitor", ["id", "kvar", "kv", "type"], mapp_shunt_capacitor, ) @@ -4311,7 +4309,7 @@ def parse_transformers(self, model): self.settings.update( self.parser_helper( line, - ["auto_transformer_settings"], + "auto_transformer_settings", [ "sectionid", "eqid", @@ -4334,7 +4332,7 @@ def parse_transformers(self, model): self.settings.update( self.parser_helper( line, - ["grounding_transformer_settings"], + "grounding_transformer_settings", ["sectionid", "equipmentid", "connectionconfiguration", "phase"], mapp_grounding_transformer_settings, {"type": "grounding_transformer"}, @@ -4350,7 +4348,7 @@ def parse_transformers(self, model): self.settings.update( self.parser_helper( line, - ["three_winding_auto_transformer_settings"], + "three_winding_auto_transformer_settings", [ "sectionid", "eqid", @@ -4377,7 +4375,7 @@ def parse_transformers(self, model): self.settings.update( self.parser_helper( line, - ["three_winding_transformer_settings"], + "three_winding_transformer_settings", [ "sectionid", "eqid", @@ -4404,7 +4402,7 @@ def parse_transformers(self, model): self.settings.update( self.parser_helper( line, - ["transformer_settings"], + "transformer_settings", [ "sectionid", "eqid", @@ -4436,7 +4434,7 @@ def parse_transformers(self, model): self.settings.update( self.parser_helper( line, - ["phase_shifter_transformer_settings"], + "phase_shifter_transformer_settings", ["sectionid", "eqid", "coordx", "coordy"], mapp_phase_shifter_transformer_settings, {"type": "phase_shifter_transformer"}, @@ -4464,7 +4462,7 @@ def parse_transformers(self, model): self.auto_transformers.update( self.parser_helper( line, - ["auto_transformer"], + "auto_transformer", [ "id", "kva", @@ -4488,7 +4486,7 @@ def parse_transformers(self, model): self.grounding_transformers.update( self.parser_helper( line, - ["grounding_transformer"], + "grounding_transformer", ["id", "ratedcapacity", "ratedvoltage", "connection_configuration"], mapp_grounding_transformer, ) @@ -4504,7 +4502,7 @@ def parse_transformers(self, model): self.three_winding_auto_transformers.update( self.parser_helper( line, - ["three_winding_auto_transformer"], + "three_winding_auto_transformer", [ "id", "primaryratedcapacity", @@ -4529,7 +4527,7 @@ def parse_transformers(self, model): self.three_winding_transformers.update( self.parser_helper( line, - ["three_winding_transformer"], + "three_winding_transformer", [ "id", "primaryratedcapacity", @@ -4553,7 +4551,7 @@ def parse_transformers(self, model): self.transformers.update( self.parser_helper( line, - ["transformer"], + "transformer", [ "id", "type", @@ -5041,7 +5039,7 @@ def parse_regulators(self, model): self.settings.update( self.parser_helper( line, - ["regulator_settings"], + "regulator_settings", [ "sectionid", "eqid", @@ -5080,7 +5078,7 @@ def parse_regulators(self, model): self.regulators.update( self.parser_helper( line, - ["regulator"], + "regulator", [ "id", "type", @@ -5358,7 +5356,7 @@ def parse_network_equivalent(self, model): self.settings, self.parser_helper( line, - ["network_equivalent_setting"], + "network_equivalent_setting", ['sectionid', 'devicenumber', 'coordx', 'coordy', 'zraa', 'zrab', 'zrac', 'zrba', 'zrbb', 'zrbc', 'zrca', 'zrcb', 'zrcc', 'zxaa', 'zxab', 'zxac', 'zxba', 'zxbb', 'zxbc', 'zxca', 'zxcb', 'zxcc', 'loadfromkwa', 'loadfromkwb', 'loadfromkwc', 'loadfromkvara', 'loadfromkvarb', 'loadfromkvarc', 'loadtokwa', 'loadtokwb', 'loadtokwc', 'loadtokvara', 'loadtokvarb', 'loadtokvarc', 'totallengtha', 'totallengthb', 'totallengthc'], mapp_network_equivalents, @@ -5376,7 +5374,7 @@ def parse_network_equivalent(self, model): self.settings, self.parser_helper( line, - ["section"], + "section", ["sectionid", "fromnodeid", "tonodeid", "phase"], mapp_section, ), @@ -5620,7 +5618,7 @@ def parse_loads(self, model): self.loads.update( self.parser_helper( line, - ["loads"], + "loads", ["sectionid", "devicenumber", "loadtype", "connection"], mapp_loads, ) @@ -5635,7 +5633,7 @@ def parse_loads(self, model): self.customer_loads.update( self.parser_helper( line, - ["customer_loads"], + "customer_loads", [ "sectionid", "devicenumber", @@ -5663,7 +5661,7 @@ def parse_loads(self, model): self.customer_class.update( self.parser_helper( line, - ["customer_class"], + "customer_class", [ "id", "constantpower", @@ -6021,7 +6019,7 @@ def parse_dg(self, model): self.converter.update( self.parser_helper( line, - ["converter"], + "converter", [ "devicenumber", "devicetype", @@ -6047,7 +6045,7 @@ def parse_dg(self, model): self.converter_settings.update( self.parser_helper( line, - ["converter_control_settings"], + "converter_control_settings", [ "devicenumber", "devicetype", @@ -6072,7 +6070,7 @@ def parse_dg(self, model): self.photovoltaic_settings.update( self.parser_helper( line, - ["photovoltaic_settings"], + "photovoltaic_settings", ["sectionid", "devicenumber", "eqphase", "ambienttemperature"], mapp_photovoltaic_settings, {"type": "photovoltaic_settings"}, @@ -6088,7 +6086,7 @@ def parse_dg(self, model): self.bess_settings.update( self.parser_helper( line, - ["bess_settings"], + "bess_settings", [ "sectionid", "devicenumber", @@ -6112,7 +6110,7 @@ def parse_dg(self, model): self.long_term_dynamics.update( self.parser_helper( line, - ["long_term_dynamics_curve_ext"], + "long_term_dynamics_curve_ext", [ "devicenumber", "devicetype", @@ -6133,7 +6131,7 @@ def parse_dg(self, model): self.dg_generation.update( self.parser_helper( line, - ["dggenerationmodel"], + "dggenerationmodel", [ "devicenumber", "devicetype", @@ -6167,7 +6165,7 @@ def parse_dg(self, model): self.bess.update( self.parser_helper( line, - ["bess"], + "bess", [ "id", "ratedstorageenergy", From 0972ee8221f12ea7d43253140a41e5b0f72353ac Mon Sep 17 00:00:00 2001 From: Nick Laws Date: Fri, 26 Aug 2022 13:45:00 -0600 Subject: [PATCH 06/15] improve warning messages from cyme reader --- ditto/readers/cyme/read.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/ditto/readers/cyme/read.py b/ditto/readers/cyme/read.py index aa9f9c94..38033866 100644 --- a/ditto/readers/cyme/read.py +++ b/ditto/readers/cyme/read.py @@ -758,7 +758,7 @@ def parser_helper(self, line, obj, attribute_list, mapping: dict, *args, **kwarg """ .. warning:: This is a helper function for the parsers. Do not use directly. - Takes as input the object we want to parse (eg. "section" maps to "[SECTION]") + Takes as input the object we want to parse (eg. "section" maps to "[SECTION]") as well as the list of attributes we want to extract (eg. ["sectionid", "fromnodeid", "tonodeid", "phase"]). Also takes the default positions of the attributes (mapping), which is overwritten if "format" is found in the lines. The function returns a dictionary of dictionaries, where each sub-dictionary contains the values of the desired attributes of a CYME object. @@ -807,7 +807,7 @@ def parser_helper(self, line, obj, attribute_list, mapping: dict, *args, **kwarg idx2 = temp[0] mapping[attribute_list[idx2]] = idx except: - pass + logger.warn(f"Unable to parse CYME line FORMAT for {obj} in {next_line}") next_line = next(self.content) @@ -860,10 +860,7 @@ def parser_helper(self, line, obj, attribute_list, mapping: dict, *args, **kwarg attribute_list = additional_attributes additional_attributes = [] except: - logger.warning( - "Attempted to apply additional attributes but failed" - ) - pass + logger.warn(f"Unable to parse additional CYME line FORMAT for {obj} in {next_line}") try: next_line = next(self.content) From e52d28c714c2c2db82e86f603ab5feaf0566cf26 Mon Sep 17 00:00:00 2001 From: Nick Laws Date: Wed, 14 Sep 2022 15:34:40 -0600 Subject: [PATCH 07/15] add Winding.base_voltage came across some transformers in CYME that have actual voltages set to something different than nominal. CYME has base voltages in TRANSFORMER SETTINGS (e.g. `primarybasevoltage`) and actual voltages in TRANSFORMER (e.g. `kvllprim`). The latter are currently set to Winding.nominal_voltage. --- ditto/models/winding.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ditto/models/winding.py b/ditto/models/winding.py index 90ed573d..7d0b422f 100644 --- a/ditto/models/winding.py +++ b/ditto/models/winding.py @@ -30,6 +30,9 @@ class Winding(DiTToHasTraits): nominal_voltage = Float( help="""The nominal voltage of the transformer winding""", default_value=None ) + base_voltage = Float( + help="""The voltage basis for per unit calculations""", default_value=None + ) voltage_limit = Float( help="""The maximum voltage allowed on the PT secondary.""", default_value=None ) From 6faea6d7deb9b798fb8cb008ddcf874024eca1d8 Mon Sep 17 00:00:00 2001 From: Nick Laws Date: Tue, 4 Oct 2022 18:32:54 -0600 Subject: [PATCH 08/15] add cyme reader test for load.nominal_voltage [FAILS] add same test to opendss reader, which passes --- tests/test_reader.py | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/tests/test_reader.py b/tests/test_reader.py index 32bddf24..424fe468 100644 --- a/tests/test_reader.py +++ b/tests/test_reader.py @@ -9,6 +9,7 @@ import os import pytest as pt from ditto.store import Store +from ditto.models.load import Load current_directory = os.path.realpath(os.path.dirname(__file__)) @@ -44,6 +45,28 @@ def test_cyme_reader(): # TODO: Log properly print(">Cyme model {model} parsed.\n".format(model=model)) + if model == "ieee_13node": + """ + test load base voltage values + the load nominal voltages are set in the system_structure_modifier.set_nominal_voltages_recur + (which is only used in the cyme reader) + the test values are what is expected from the IEEE13 openDSS model + issues: + - perhaps the default load.connection_type should by "Y"? + - all connection_types are currently set to None in this test + - currently the nominal voltages are set to the nominal_voltage of the upline transformer, + but the upline transformer nominal_voltage is (usu.) the phase to phase voltage + which is not the correct voltage to use for single phase load connected between + phase and ground. + - can we use Load and PhaseLoad attributes to correctly define the load.nominal_voltage? + """ + for load in m.iter_models(Load): + nphases = len(load.phase_loads) + if load.connection_type in ("Y", None) and nphases == 1: + assert round(load.nominal_voltage, 3) in (2400, 277) + if load.connection_type == "D": + assert round(load.nominal_voltage, 3) == 4160 + # @pt.mark.skip("Segfault occurs") def test_opendss_reader(): @@ -69,6 +92,17 @@ def test_opendss_reader(): # TODO: Log properly print(">OpenDSS model {model} parsed.\n".format(model=model)) + if model == "ieee_13node": + """ + test load base voltage values match expected values + """ + for load in m.iter_models(Load): + nphases = len(load.phase_loads) + if load.connection_type in ("Y", None) and nphases == 1: + assert round(load.nominal_voltage, 3) in (2400, 277) + if load.connection_type == "D": + assert round(load.nominal_voltage, 3) == 4160 + def test_dew_reader(): """ From ac439fdd0de84afa1f11754778ba09366246b48f Mon Sep 17 00:00:00 2001 From: Nick Laws Date: Fri, 26 Aug 2022 09:17:45 -0600 Subject: [PATCH 09/15] cyme reader: improve warning msgs for bad load values --- ditto/readers/cyme/read.py | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/ditto/readers/cyme/read.py b/ditto/readers/cyme/read.py index 43012000..a5b42423 100644 --- a/ditto/readers/cyme/read.py +++ b/ditto/readers/cyme/read.py @@ -5714,9 +5714,7 @@ def parse_loads(self, model): try: p, q = float(settings["value1"]), float(settings["value2"]) except: - logger.warning( - "WARNING:: Skipping load on section {}".format(sectionID) - ) + logger.warning(f"Problem with load on section {sectionID} with value_type=0 (P,Q). value1={settings['value1']} value2={settings['value2']}") continue elif value_type == 1: # KVA and PF are given try: @@ -5729,9 +5727,7 @@ def parse_loads(self, model): p = kva * PF q = math.sqrt(kva ** 2 - p ** 2) except: - logger.warning( - "WARNING:: Skipping load on section {}".format(sectionID) - ) + logger.warning(f"Problem with load on section {sectionID} with value_type=1 (kVa, PF). value1={settings['value1']} value2={settings['value2']}") continue elif value_type == 2: # P and PF are given @@ -5743,16 +5739,15 @@ def parse_loads(self, model): PF /= 100.0 q = p * math.sqrt((1 - PF ** 2) / PF ** 2) else: - logger.warning("problem with PF") - logger.warning(PF) + logger.warning(f"Problem with load on section {sectionID} with value_type=2 (P, PF). value1={settings['value1']} value2={settings['value2']}") except: - logger.warning("Skipping load on section {}".format(sectionID)) + logger.warning(f"Problem with load on section {sectionID} with value_type=2 (P, PF). value1={settings['value1']} value2={settings['value2']}") continue elif value_type == 3: # AMP and PF are given # TODO logger.warning( - "WARNING:: Skipping load on section {}".format(sectionID) + "WARNING:: Skipping load on section {} because value_type=3 (AMP and PF)".format(sectionID) ) continue From 718e16fd4c1c54dc2d120aebb4ab435a52093976 Mon Sep 17 00:00:00 2001 From: Nick Laws Date: Fri, 26 Aug 2022 09:20:40 -0600 Subject: [PATCH 10/15] handle zero power factor in cyme reader for loads --- ditto/readers/cyme/read.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ditto/readers/cyme/read.py b/ditto/readers/cyme/read.py index a5b42423..7919193a 100644 --- a/ditto/readers/cyme/read.py +++ b/ditto/readers/cyme/read.py @@ -5733,7 +5733,9 @@ def parse_loads(self, model): try: p, PF = float(settings["value1"]), float(settings["value2"]) - if 0 <= PF <= 1: + if PF == 0: + q = 0 + elif 0 < PF <= 1: q = p * math.sqrt((1 - PF ** 2) / PF ** 2) elif 1 < PF <= 100: PF /= 100.0 From 58ba413baa2b49585c996e6899e8f2807f67b068 Mon Sep 17 00:00:00 2001 From: Nick Laws Date: Fri, 26 Aug 2022 09:47:11 -0600 Subject: [PATCH 11/15] handle negative power factor in cyme reader --- ditto/readers/cyme/read.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ditto/readers/cyme/read.py b/ditto/readers/cyme/read.py index 7919193a..fb20c0a6 100644 --- a/ditto/readers/cyme/read.py +++ b/ditto/readers/cyme/read.py @@ -5735,9 +5735,9 @@ def parse_loads(self, model): p, PF = float(settings["value1"]), float(settings["value2"]) if PF == 0: q = 0 - elif 0 < PF <= 1: + elif 0 < abs(PF) <= 1: q = p * math.sqrt((1 - PF ** 2) / PF ** 2) - elif 1 < PF <= 100: + elif 1 < abs(PF) <= 100: PF /= 100.0 q = p * math.sqrt((1 - PF ** 2) / PF ** 2) else: From b70e8007909d278c71e7028996985e486b03ccb3 Mon Sep 17 00:00:00 2001 From: Nick Laws Date: Fri, 26 Aug 2022 11:18:18 -0600 Subject: [PATCH 12/15] rm debug msg in cyme reader not sure why this is here, just printing cable sectionID --- ditto/readers/cyme/read.py | 1 - 1 file changed, 1 deletion(-) diff --git a/ditto/readers/cyme/read.py b/ditto/readers/cyme/read.py index fb20c0a6..1f4da5d8 100644 --- a/ditto/readers/cyme/read.py +++ b/ditto/readers/cyme/read.py @@ -3024,7 +3024,6 @@ def parse_lines(self, model): line_data = self.concentric_neutral_cable[settings["linecableid"]] line_data["type"] = "balanced_line" if settings["linecableid"] in self.cables: - logger.debug("cables {}".format(sectionID)) line_data = self.cables[settings["linecableid"]] line_data["type"] = "balanced_line" From 10f9b9571e57038d794fe7b638f0c0f084f4c0bf Mon Sep 17 00:00:00 2001 From: Nick Laws Date: Fri, 26 Aug 2022 13:28:29 -0600 Subject: [PATCH 13/15] cyme reader clarify parser_helper role - cannot parse a list of objects though it was taking a obj_list as input; - it would only parse the first value in the list; - now it takes in a string instead of list of string - also improved notes/documentation --- ditto/readers/cyme/read.py | 148 ++++++++++++++++++------------------- 1 file changed, 73 insertions(+), 75 deletions(-) diff --git a/ditto/readers/cyme/read.py b/ditto/readers/cyme/read.py index 1f4da5d8..aa9f9c94 100644 --- a/ditto/readers/cyme/read.py +++ b/ditto/readers/cyme/read.py @@ -328,9 +328,9 @@ def update_header_mapping(self, update): # Replace the old mapping by the new one self.header_mapping = new_mapping - def get_file_content(self, filename): + def get_file_content(self, filename: str) -> None: """ - Open the requested file and returns the content. + Open the requested file and set self.content to iter(file_pointer.readlines()) For convinience, filename can be either the full file path or: -'network': Will get the content of the network file given in the constructor @@ -719,7 +719,7 @@ def transformer_connection_configuration_mapping(self, value, winding): return res[winding] - def check_object_in_line(self, line, obj): + def check_object_in_line(self, line: str, obj: str) -> bool: """ Check if the header corresponding to object is in the given line. @@ -754,13 +754,14 @@ def check_object_in_line(self, line, obj): return np.any([x in line for x in self.header_mapping[obj]]) - def parser_helper(self, line, obj_list, attribute_list, mapping, *args, **kwargs): + def parser_helper(self, line, obj, attribute_list, mapping: dict, *args, **kwargs) -> dict: """ .. warning:: This is a helper function for the parsers. Do not use directly. - Takes as input the list of objects we want to parse as well as the list of attributes we want to extract. - Also takes the default positions of the attributes (mapping). - The function returns a list of dictionaries, where each dictionary contains the values of the desired attributes of a CYME object. + Takes as input the object we want to parse (eg. "section" maps to "[SECTION]") + as well as the list of attributes we want to extract (eg. ["sectionid", "fromnodeid", "tonodeid", "phase"]). + Also takes the default positions of the attributes (mapping), which is overwritten if "format" is found in the lines. + The function returns a dictionary of dictionaries, where each sub-dictionary contains the values of the desired attributes of a CYME object. """ if isinstance(attribute_list, list): attribute_list = np.array(attribute_list) @@ -783,11 +784,8 @@ def parser_helper(self, line, obj_list, attribute_list, mapping, *args, **kwargs result = {} - # Check the presence of headers in the given line - checks = [self.check_object_in_line(line, obj) for obj in obj_list] - - # If we have a least one - if any(checks): + # If header in line + if self.check_object_in_line(line, obj): # Get the next line next_line = next(self.content) @@ -815,7 +813,7 @@ def parser_helper(self, line, obj_list, attribute_list, mapping, *args, **kwargs # At this point, we should have the mapping for the parameters of interest # while next_line[0] not in ['[','',' ','\n','\r\n']: - while len(next_line) > 2: + while len(next_line) > 2: # blank lines separate objects in CYME .txt files if "=" not in next_line.lower(): data = next_line.split(",") @@ -824,7 +822,7 @@ def parser_helper(self, line, obj_list, attribute_list, mapping, *args, **kwargs if len(data) > 1: - while ID in result: + while ID in result: # redundant keys get *'s ID += "*" result[ID] = {} @@ -982,7 +980,7 @@ def parse_subnetwork_connections(self, model): self.subnetwork_connections.update( self.parser_helper( line, - ["subnetwork_connections"], + "subnetwork_connections", ["nodeid"], mapp_subnetwork_connections, ) @@ -1004,7 +1002,7 @@ def parse_head_nodes(self, model): headnodes = {} for line in self.content: headnodes.update( - self.parser_helper(line, ["headnodes"], ["nodeid", "networkid"], mapp) + self.parser_helper(line, "headnodes", ["nodeid", "networkid"], mapp) ) for sid, headnode in headnodes.items(): @@ -1036,11 +1034,11 @@ def parse_sources(self, model): subs = {} source_equivalents = {} - for line in self.content: + for line in self.content: # parser_helper will only loop over lines in self.content if it finds obj in the `line`; o.w. returns empty dict sources.update( self.parser_helper( line, - ["source"], + "source", ["sourceid", "nodeid", "networkid", "desiredvoltage"], mapp, ) @@ -1048,7 +1046,7 @@ def parse_sources(self, model): source_equivalents.update( self.parser_helper( line, - ["source_equivalent"], + "source_equivalent", [ "nodeid", "voltage", @@ -1078,7 +1076,7 @@ def parse_sources(self, model): for line in self.content: subs.update( self.parser_helper( - line, ["substation"], ["id", "mva", "kvll", "conn"], mapp_sub + line, "substation", ["id", "mva", "kvll", "conn"], mapp_sub ) ) if len(sources.items()) == 0: @@ -1371,7 +1369,7 @@ def parse_nodes(self, model): nodes.update( self.parser_helper( line, - ["node"], + "node", ["nodeid", "coordx", "coordy", "ratedvoltage"], mapp, **kwargs @@ -1381,7 +1379,7 @@ def parse_nodes(self, model): for line in self.content: node_connectors.update( self.parser_helper( - line, ["node_connector"], ["nodeid", "coordx", "coordy"], mapp + line, "node_connector", ["nodeid", "coordx", "coordy"], mapp ) ) @@ -1556,7 +1554,7 @@ def parse_sections(self, model): ... [SECTION] - FORMAT_section=sectionid,fromnodeid,tonodeid,phase + FORMAT_section=sectionid,fromnodeid,tonodeid,phase FORMAT_Feeder=networkid,headnodeid Feeder=feeder_1,head_feeder_1 section_1_feeder_1,node_1,node_2,ABC @@ -1947,7 +1945,7 @@ def parse_lines(self, model): self.settings, self.parser_helper( line, - ["overhead_unbalanced_line_settings"], + "overhead_unbalanced_line_settings", ["sectionid", "coordx", "coordy", "linecableid", "length"], mapp_overhead, {"type": "overhead_unbalanced"}, @@ -1964,7 +1962,7 @@ def parse_lines(self, model): self.settings, self.parser_helper( line, - ["overhead_line_settings"], + "overhead_line_settings", ["sectionid", "coordx", "coordy", "linecableid", "length"], mapp_overhead, {"type": "overhead_balanced"}, @@ -1981,7 +1979,7 @@ def parse_lines(self, model): self.settings, self.parser_helper( line, - ["overhead_byphase_settings"], + "overhead_byphase_settings", [ "sectionid", "devicenumber", @@ -2011,7 +2009,7 @@ def parse_lines(self, model): self.settings, self.parser_helper( line, - ["underground_line_settings"], + "underground_line_settings", ["sectionid", "coordx", "coordy", "linecableid", "length", "amps"], mapp_underground, {"type": "underground"}, @@ -2028,7 +2026,7 @@ def parse_lines(self, model): self.settings, self.parser_helper( line, - ["switch_settings"], + "switch_settings", ["sectionid", "coordx", "coordy", "eqid", "closedphase"], mapp_switch, {"type": "switch"}, @@ -2045,7 +2043,7 @@ def parse_lines(self, model): self.settings, self.parser_helper( line, - ["sectionalizer_settings"], + "sectionalizer_settings", ["sectionid", "coordx", "coordy", "eqid", "closedphase"], mapp_sectionalizer, {"type": "sectionalizer"}, @@ -2062,7 +2060,7 @@ def parse_lines(self, model): self.settings, self.parser_helper( line, - ["fuse_settings"], + "fuse_settings", ["sectionid", "coordx", "coordy", "eqid"], mapp_switch, # Same as switches {"type": "fuse"}, @@ -2079,7 +2077,7 @@ def parse_lines(self, model): self.settings, self.parser_helper( line, - ["recloser_settings"], + "recloser_settings", ["sectionid", "coordx", "coordy", "eqid"], mapp_switch, # Same as switches {"type": "recloser"}, @@ -2096,7 +2094,7 @@ def parse_lines(self, model): self.settings, self.parser_helper( line, - ["breaker_settings"], + "breaker_settings", ["sectionid", "coordx", "coordy", "eqid", "closedphase"], mapp_switch, # Same as switches {"type": "breaker"}, @@ -2113,7 +2111,7 @@ def parse_lines(self, model): self.settings, self.parser_helper( line, - ["network_protector_settings"], + "network_protector_settings", ["sectionid", "coordx", "coordy", "eqid", "closedphase"], mapp_switch, # Same as switches {"type": "network_protector"}, @@ -2130,7 +2128,7 @@ def parse_lines(self, model): self.settings, self.parser_helper( line, - ["section"], + "section", ["sectionid", "fromnodeid", "tonodeid", "phase"], mapp_section, ), @@ -2157,7 +2155,7 @@ def parse_lines(self, model): self.balanced_lines.update( self.parser_helper( line, - ["line"], + "line", [ "id", "phasecondid", @@ -2185,7 +2183,7 @@ def parse_lines(self, model): self.unbalanced_lines.update( self.parser_helper( line, - ["unbalanced_line"], + "unbalanced_line", [ "id", "condid_a", @@ -2225,7 +2223,7 @@ def parse_lines(self, model): self.spacings.update( self.parser_helper( line, - ["spacing_table"], + "spacing_table", [ "id", "posofcond1_x", @@ -2252,7 +2250,7 @@ def parse_lines(self, model): self.conductors.update( self.parser_helper( line, - ["conductor"], + "conductor", ["id", "diameter", "gmr", "r25", "amps", "withstandrating"], mapp_conductor, ) @@ -2267,7 +2265,7 @@ def parse_lines(self, model): self.concentric_neutral_cable.update( self.parser_helper( line, - ["concentric_neutral_cable"], + "concentric_neutral_cable", [ "id", "r1", @@ -2291,7 +2289,7 @@ def parse_lines(self, model): self.cables.update( self.parser_helper( line, - ["cable"], + "cable", ["id", "r1", "r0", "x1", "x0", "amps"], mapp_concentric_neutral_cable, ) @@ -2305,7 +2303,7 @@ def parse_lines(self, model): # self.switches.update( self.parser_helper( - line, ["switch"], ["id", "amps", "kvll"], mapp_switch_eq + line, "switch", ["id", "amps", "kvll"], mapp_switch_eq ) ) @@ -2318,7 +2316,7 @@ def parse_lines(self, model): self.fuses.update( self.parser_helper( line, - ["fuse"], + "fuse", ["id", "amps", "kvll", "interruptingrating"], mapp_network_protectors, # Same as network protectors ) @@ -2333,7 +2331,7 @@ def parse_lines(self, model): self.reclosers.update( self.parser_helper( line, - ["recloser"], + "recloser", ["id", "amps", "kvll", "interruptingrating"], mapp_network_protectors, # Same as network protectors ) @@ -2348,7 +2346,7 @@ def parse_lines(self, model): self.sectionalizers.update( self.parser_helper( line, - ["sectionalizer"], + "sectionalizer", ["id", "amps", "kvll", "interruptingrating"], mapp_sectionalizers, ) @@ -2363,7 +2361,7 @@ def parse_lines(self, model): self.breakers.update( self.parser_helper( line, - ["breaker"], + "breaker", ["id", "amps", "kvll", "interruptingrating"], mapp_network_protectors, # Same as network protectors ) @@ -2378,7 +2376,7 @@ def parse_lines(self, model): self.network_protectors.update( self.parser_helper( line, - ["network_protector"], + "network_protector", ["id", "amps", "kvll", "interruptingrating"], mapp_network_protectors, ) @@ -3876,7 +3874,7 @@ def parse_capacitors(self, model): self.settings.update( self.parser_helper( line, - ["serie_capacitor_settings"], + "serie_capacitor_settings", ["sectionid", "eqid", "coordx", "coordy"], mapp_serie_capacitor_settings, {"type": "serie"}, @@ -3892,7 +3890,7 @@ def parse_capacitors(self, model): self.settings.update( self.parser_helper( line, - ["shunt_capacitor_settings"], + "shunt_capacitor_settings", [ "sectionid", "shuntcapacitorid", @@ -3931,7 +3929,7 @@ def parse_capacitors(self, model): # self.capacitors.update( self.parser_helper( - line, ["serie_capacitor"], ["id", "reactance"], mapp_serie_capacitor + line, "serie_capacitor", ["id", "reactance"], mapp_serie_capacitor ) ) @@ -3944,7 +3942,7 @@ def parse_capacitors(self, model): self.capacitors.update( self.parser_helper( line, - ["shunt_capacitor"], + "shunt_capacitor", ["id", "kvar", "kv", "type"], mapp_shunt_capacitor, ) @@ -4311,7 +4309,7 @@ def parse_transformers(self, model): self.settings.update( self.parser_helper( line, - ["auto_transformer_settings"], + "auto_transformer_settings", [ "sectionid", "eqid", @@ -4334,7 +4332,7 @@ def parse_transformers(self, model): self.settings.update( self.parser_helper( line, - ["grounding_transformer_settings"], + "grounding_transformer_settings", ["sectionid", "equipmentid", "connectionconfiguration", "phase"], mapp_grounding_transformer_settings, {"type": "grounding_transformer"}, @@ -4350,7 +4348,7 @@ def parse_transformers(self, model): self.settings.update( self.parser_helper( line, - ["three_winding_auto_transformer_settings"], + "three_winding_auto_transformer_settings", [ "sectionid", "eqid", @@ -4377,7 +4375,7 @@ def parse_transformers(self, model): self.settings.update( self.parser_helper( line, - ["three_winding_transformer_settings"], + "three_winding_transformer_settings", [ "sectionid", "eqid", @@ -4404,7 +4402,7 @@ def parse_transformers(self, model): self.settings.update( self.parser_helper( line, - ["transformer_settings"], + "transformer_settings", [ "sectionid", "eqid", @@ -4436,7 +4434,7 @@ def parse_transformers(self, model): self.settings.update( self.parser_helper( line, - ["phase_shifter_transformer_settings"], + "phase_shifter_transformer_settings", ["sectionid", "eqid", "coordx", "coordy"], mapp_phase_shifter_transformer_settings, {"type": "phase_shifter_transformer"}, @@ -4464,7 +4462,7 @@ def parse_transformers(self, model): self.auto_transformers.update( self.parser_helper( line, - ["auto_transformer"], + "auto_transformer", [ "id", "kva", @@ -4488,7 +4486,7 @@ def parse_transformers(self, model): self.grounding_transformers.update( self.parser_helper( line, - ["grounding_transformer"], + "grounding_transformer", ["id", "ratedcapacity", "ratedvoltage", "connection_configuration"], mapp_grounding_transformer, ) @@ -4504,7 +4502,7 @@ def parse_transformers(self, model): self.three_winding_auto_transformers.update( self.parser_helper( line, - ["three_winding_auto_transformer"], + "three_winding_auto_transformer", [ "id", "primaryratedcapacity", @@ -4529,7 +4527,7 @@ def parse_transformers(self, model): self.three_winding_transformers.update( self.parser_helper( line, - ["three_winding_transformer"], + "three_winding_transformer", [ "id", "primaryratedcapacity", @@ -4553,7 +4551,7 @@ def parse_transformers(self, model): self.transformers.update( self.parser_helper( line, - ["transformer"], + "transformer", [ "id", "type", @@ -5041,7 +5039,7 @@ def parse_regulators(self, model): self.settings.update( self.parser_helper( line, - ["regulator_settings"], + "regulator_settings", [ "sectionid", "eqid", @@ -5080,7 +5078,7 @@ def parse_regulators(self, model): self.regulators.update( self.parser_helper( line, - ["regulator"], + "regulator", [ "id", "type", @@ -5358,7 +5356,7 @@ def parse_network_equivalent(self, model): self.settings, self.parser_helper( line, - ["network_equivalent_setting"], + "network_equivalent_setting", ['sectionid', 'devicenumber', 'coordx', 'coordy', 'zraa', 'zrab', 'zrac', 'zrba', 'zrbb', 'zrbc', 'zrca', 'zrcb', 'zrcc', 'zxaa', 'zxab', 'zxac', 'zxba', 'zxbb', 'zxbc', 'zxca', 'zxcb', 'zxcc', 'loadfromkwa', 'loadfromkwb', 'loadfromkwc', 'loadfromkvara', 'loadfromkvarb', 'loadfromkvarc', 'loadtokwa', 'loadtokwb', 'loadtokwc', 'loadtokvara', 'loadtokvarb', 'loadtokvarc', 'totallengtha', 'totallengthb', 'totallengthc'], mapp_network_equivalents, @@ -5376,7 +5374,7 @@ def parse_network_equivalent(self, model): self.settings, self.parser_helper( line, - ["section"], + "section", ["sectionid", "fromnodeid", "tonodeid", "phase"], mapp_section, ), @@ -5620,7 +5618,7 @@ def parse_loads(self, model): self.loads.update( self.parser_helper( line, - ["loads"], + "loads", ["sectionid", "devicenumber", "loadtype", "connection"], mapp_loads, ) @@ -5635,7 +5633,7 @@ def parse_loads(self, model): self.customer_loads.update( self.parser_helper( line, - ["customer_loads"], + "customer_loads", [ "sectionid", "devicenumber", @@ -5663,7 +5661,7 @@ def parse_loads(self, model): self.customer_class.update( self.parser_helper( line, - ["customer_class"], + "customer_class", [ "id", "constantpower", @@ -6021,7 +6019,7 @@ def parse_dg(self, model): self.converter.update( self.parser_helper( line, - ["converter"], + "converter", [ "devicenumber", "devicetype", @@ -6047,7 +6045,7 @@ def parse_dg(self, model): self.converter_settings.update( self.parser_helper( line, - ["converter_control_settings"], + "converter_control_settings", [ "devicenumber", "devicetype", @@ -6072,7 +6070,7 @@ def parse_dg(self, model): self.photovoltaic_settings.update( self.parser_helper( line, - ["photovoltaic_settings"], + "photovoltaic_settings", ["sectionid", "devicenumber", "eqphase", "ambienttemperature"], mapp_photovoltaic_settings, {"type": "photovoltaic_settings"}, @@ -6088,7 +6086,7 @@ def parse_dg(self, model): self.bess_settings.update( self.parser_helper( line, - ["bess_settings"], + "bess_settings", [ "sectionid", "devicenumber", @@ -6112,7 +6110,7 @@ def parse_dg(self, model): self.long_term_dynamics.update( self.parser_helper( line, - ["long_term_dynamics_curve_ext"], + "long_term_dynamics_curve_ext", [ "devicenumber", "devicetype", @@ -6133,7 +6131,7 @@ def parse_dg(self, model): self.dg_generation.update( self.parser_helper( line, - ["dggenerationmodel"], + "dggenerationmodel", [ "devicenumber", "devicetype", @@ -6167,7 +6165,7 @@ def parse_dg(self, model): self.bess.update( self.parser_helper( line, - ["bess"], + "bess", [ "id", "ratedstorageenergy", From 631650e9840f45b7c3b93905807381ffdca27045 Mon Sep 17 00:00:00 2001 From: Nick Laws Date: Fri, 26 Aug 2022 13:45:00 -0600 Subject: [PATCH 14/15] improve warning messages from cyme reader --- ditto/readers/cyme/read.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/ditto/readers/cyme/read.py b/ditto/readers/cyme/read.py index aa9f9c94..38033866 100644 --- a/ditto/readers/cyme/read.py +++ b/ditto/readers/cyme/read.py @@ -758,7 +758,7 @@ def parser_helper(self, line, obj, attribute_list, mapping: dict, *args, **kwarg """ .. warning:: This is a helper function for the parsers. Do not use directly. - Takes as input the object we want to parse (eg. "section" maps to "[SECTION]") + Takes as input the object we want to parse (eg. "section" maps to "[SECTION]") as well as the list of attributes we want to extract (eg. ["sectionid", "fromnodeid", "tonodeid", "phase"]). Also takes the default positions of the attributes (mapping), which is overwritten if "format" is found in the lines. The function returns a dictionary of dictionaries, where each sub-dictionary contains the values of the desired attributes of a CYME object. @@ -807,7 +807,7 @@ def parser_helper(self, line, obj, attribute_list, mapping: dict, *args, **kwarg idx2 = temp[0] mapping[attribute_list[idx2]] = idx except: - pass + logger.warn(f"Unable to parse CYME line FORMAT for {obj} in {next_line}") next_line = next(self.content) @@ -860,10 +860,7 @@ def parser_helper(self, line, obj, attribute_list, mapping: dict, *args, **kwarg attribute_list = additional_attributes additional_attributes = [] except: - logger.warning( - "Attempted to apply additional attributes but failed" - ) - pass + logger.warn(f"Unable to parse additional CYME line FORMAT for {obj} in {next_line}") try: next_line = next(self.content) From 326b82a9e80ffd5139cee1b1df6ffb4e6ea9c722 Mon Sep 17 00:00:00 2001 From: Nick Laws Date: Tue, 4 Oct 2022 18:32:54 -0600 Subject: [PATCH 15/15] add cyme reader test for load.nominal_voltage [FAILS] add same test to opendss reader, which passes --- tests/test_reader.py | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/tests/test_reader.py b/tests/test_reader.py index 32bddf24..424fe468 100644 --- a/tests/test_reader.py +++ b/tests/test_reader.py @@ -9,6 +9,7 @@ import os import pytest as pt from ditto.store import Store +from ditto.models.load import Load current_directory = os.path.realpath(os.path.dirname(__file__)) @@ -44,6 +45,28 @@ def test_cyme_reader(): # TODO: Log properly print(">Cyme model {model} parsed.\n".format(model=model)) + if model == "ieee_13node": + """ + test load base voltage values + the load nominal voltages are set in the system_structure_modifier.set_nominal_voltages_recur + (which is only used in the cyme reader) + the test values are what is expected from the IEEE13 openDSS model + issues: + - perhaps the default load.connection_type should by "Y"? + - all connection_types are currently set to None in this test + - currently the nominal voltages are set to the nominal_voltage of the upline transformer, + but the upline transformer nominal_voltage is (usu.) the phase to phase voltage + which is not the correct voltage to use for single phase load connected between + phase and ground. + - can we use Load and PhaseLoad attributes to correctly define the load.nominal_voltage? + """ + for load in m.iter_models(Load): + nphases = len(load.phase_loads) + if load.connection_type in ("Y", None) and nphases == 1: + assert round(load.nominal_voltage, 3) in (2400, 277) + if load.connection_type == "D": + assert round(load.nominal_voltage, 3) == 4160 + # @pt.mark.skip("Segfault occurs") def test_opendss_reader(): @@ -69,6 +92,17 @@ def test_opendss_reader(): # TODO: Log properly print(">OpenDSS model {model} parsed.\n".format(model=model)) + if model == "ieee_13node": + """ + test load base voltage values match expected values + """ + for load in m.iter_models(Load): + nphases = len(load.phase_loads) + if load.connection_type in ("Y", None) and nphases == 1: + assert round(load.nominal_voltage, 3) in (2400, 277) + if load.connection_type == "D": + assert round(load.nominal_voltage, 3) == 4160 + def test_dew_reader(): """