def _classify_branch(from_bus, to_bus, line_id): if isinstance(from_bus, component_busdetails.BusDetails): if from_bus.base_voltage > 100: if from_bus.name[:3] == 'MF.' and to_bus.name[:3] == 'MF.': return component.Cable else: from_bus = from_bus.number to_bus = to_bus.number else: return component.Line if MonsterPssPy.busdat(from_bus, 'KV')[1] > 100.0: length = MonsterPssPy.brndat(from_bus, to_bus, line_id, 'LENGTH') charging = MonsterPssPy.brndat(from_bus, to_bus, line_id, 'CHARG') from_bus_name = MonsterPssPy.notona(from_bus) to_bus_name = MonsterPssPy.notona(to_bus) if from_bus_name[:3] == 'MF.' and to_bus_name[:3] == 'MF.': return component.Cable if length > 0.0: normed_charging = charging / length else: normed_charging = 0.0 tol = 0.005 if normed_charging > tol: return component.Cable else: return component.Line else: return component.Line
def _change_status(self, from_status, to_status): """ Take a two winding transformer out of service in the PSSE model. This function uses the psspy API to manually set a line in out of service. It manipulates the 'status' argument of the :func:`psspy.two_winding_data_3` function and sets it to 0. Warnings ======== If the line is a part of a multi-line section a waring is issued. In this case the same line might be tripped more than once. Raises ====== A psspy.PsseException is rasied if one tries to trip an already disconnected line. """ if self.status() == from_status: MonsterPssPy.three_wnd_imped_chng( self.from_bus.number, self.to_bus.number, self.other_bus.number, self.identificator, [MonsterPssPy._i()] * 7 + [to_status.get_index()], [] )
def _change_status(self, from_status, to_status): if self.status() == from_status: MonsterPssPy.two_winding_data( self.from_bus.number, self.to_bus.number, self.identificator, [to_status.get_index()], [] )
def get_loads(sid, bus_dict): flag = IncludeStatus.NotAddStepAndNotInService.value (bus_numbers,) = MonsterPssPy.aloadint(sid, flag=flag, string='NUMBER') load_ids = [ name.strip() for name in MonsterPssPy.aloadchar(sid, flag=flag, string='ID')[0] ] load_dict = dict() for bus_number, load_id in zip(bus_numbers, load_ids): load = component.Load( load_bus=bus_dict[bus_number].from_bus, load_id=load_id ) load_dict[load.get_sorted_short_tuple()] = load return load_dict
def get_real_power(self): buses = collections.deque(self.get_busnumbers()) pct = -1 for i in range(3): buses.rotate(1) pct_wnd = MonsterPssPy.wnddat( buses[0], buses[1], buses[2], self.identificator, 'PCTRTA' ) if pct_wnd > pct: pct = pct_wnd n_rotate = i + 1 buses.rotate(n_rotate) return MonsterPssPy.wnddt2( buses[0], buses[1], buses[2], self.identificator, 'FLOW' ).real
def _change_status(self, from_status, to_status): if self.status() == from_status and not self.msl_component: if self.msl_lines: MonsterPssPy.multi_section_line_edit( self.from_bus.number, self.to_bus.number, self.identificator, [to_status.get_index()] ) else: MonsterPssPy.branch_data( self.from_bus.number, self.to_bus.number, self.identificator, [to_status.get_index()] )
def _flow(self, *args): try: s = MonsterPssPy.brnflo(*args) except PsseBrnfloException as e: if e._ierr == 3: return [] raise return s
def set_rx(self): buses = collections.deque(self.get_busnumbers(False)) self.rx = [] for i in range(3): self.rx.append( MonsterPssPy.wnddt2(buses[0], buses[1], buses[2], self.identificator, 'RX') ) buses.rotate(-1)
def set_rate(self, file_path): buses = collections.deque(self.get_busnumbers(False)) self._rate[file_path] = [] for i in range(3): self._rate[file_path].append( MonsterPssPy.wnddat(buses[0], buses[1], buses[2], self.identificator, RATE_NAME) ) buses.rotate(-1)
def get_lines(sid, bus_dict): """ Extract line data from the PSSE case Parameters ========== include_only_in_service: Bool (optional=True) whether to include only in service line or not """ flag = IncludeStatus.NotAddStepAndNotInService.value ties = 3 (from_buses, to_buses) = MonsterPssPy.abrnint( sid, flag=flag, ties=ties, string=['FROMNUMBER', 'TONUMBER'] ) line_ids = [ name.strip() for name in MonsterPssPy.abrnchar( sid, flag=flag, ties=ties, string='ID' )[0] ] (line_length, rates, ) = MonsterPssPy.abrnreal( sid, flag=flag, ties=ties, string=['LENGTH', RATE_NAME] ) rxes = MonsterPssPy.abrncplx( sid, flag=flag, ties=ties, string='RX' ) line_dict = dict() for from_bus, to_bus, line_id, length, rate_c, rx in zip( from_buses, to_buses, line_ids, line_length, rates, rxes[0] ): comp_func = _classify_branch(from_bus, to_bus, line_id) line = comp_func( from_bus=bus_dict[from_bus].from_bus, to_bus=bus_dict[to_bus].from_bus, identificator=line_id, length=length, rate_c=rate_c, rx=rx, ) if not (line.from_bus.dummy or line.to_bus.dummy): line_dict[line.get_sorted_short_tuple()] = line return line_dict
def _change_status(self, from_status, to_status): # Do we need to scale production on generators due to droop? if len(self._scale_area): scale_area = self._scale_area else: scale_area = MonsterPssPy.aareaint(-1, 1, 'NUMBER')[0] # Get total load s_total = self.get_pq(to_status) if self.status() == to_status: if to_status == ComponentStatus.off: raise PsseBaseException('Machine component already out of service.', None) else: raise PsseBaseException('Machine component already in service.', None) try: MonsterPssPy.machine_data( self.from_bus.number, self.identificator, [ComponentStatus.off.get_index()] ) # Define SID for scale area sid = 2 MonsterPssPy.bsys(sid, numarea=len(scale_area), areas=scale_area) # Scale with (3) incremental powers, (0) ignore machine # power limits, (0) No Q changes and (2) only type 2 and 3 # buses. MonsterPssPy.scal(sid, 0, 0, [0, 0, 0, 2, 0], [0.0, s_total.real] + [0.0] * 5) except PsseBaseException: raise
def flow_amp_rate(self): args = self.get_busnumbers(True) s = self._flow(*args) if s: s_rate = self.get_rate() irate_1 = MonsterPssPy.brnmsc(*args, string='PCTRTA') irate_2 = MonsterPssPy.brnmsc(args[1], args[0], args[2], string='PCTRTA') s_rate *= max(irate_1, irate_2) / 100.0 if s_rate < abs(s.real): return complex(s.real, 0) else: return complex(s.real, math.sqrt(s_rate**2 - s.real**2)) else: return s
def status(self): _status = MonsterPssPy.tr3int( self.from_bus.number, self.to_bus.number, self.other_bus.number, self.identificator, 'STATUS' ) return self._component_status(_status)
def status(self): try: _status = MonsterPssPy.macint(self.from_bus.number, self.identificator, 'STATUS') except PsseMacintException as e: if e._ierr == 4: _status = e._value else: raise return self._component_status(_status)
def _flow(self, *branch_buses_and_id): try: s = MonsterPssPy.wnddt2(*branch_buses_and_id, string='FLOW') except PsseWnddtException as e: if e._ierr == 7: s = [] else: raise return s
def get_p_lim(self): """ Get pmin and pmax as a tuple (pmin, pmax) """ try: pmin = MonsterPssPy.macdat(self.from_bus.number, self.identificator, 'PMIN') except PsseMacdatdException as e: if e._ierr == 4: # machine offline. ignore error and continue pmin = e._value else: raise e try: pmax = MonsterPssPy.macdat(self.from_bus.number, self.identificator, 'PMAX') except PsseMacdatdException as e: if e._ierr == 4: # machine offline. ignore error and continue pmax = e._value else: raise e return (pmin, pmax)
def get_machines(sid, bus_dict): """ Extract machine data from the PSSE case Parameters ========== include_only_in_service: Bool (optional=True) whether to include only in service machines or not """ flag = IncludeStatus.NotAddStepAndNotInService.value bus_numbers = MonsterPssPy.amachint(sid, flag=flag, string='NUMBER')[0] machine_ids = [ name.strip() for name in MonsterPssPy.amachchar(sid, flag=flag, string='ID')[0] ] machine_dict = dict() for bus_number, machine_id in zip(bus_numbers, machine_ids): machine = component.Machine(bus_dict[bus_number].from_bus, machine_id) machine_dict[machine.get_sorted_short_tuple()] = machine return machine_dict
def get_pq(self, to_status=ComponentStatus.off): """ Get PQ actual machine power output from case as a complex value """ try: s_total = MonsterPssPy.macdt2(self.from_bus.number, self.identificator, 'PQ') except PsseMacdatdException as e: if to_status == ComponentStatus.on and e._ierr == 4: s_total = -e._value else: raise return s_total
def set_p(self, p): """Set active power output of machine in case as MW """ try: return MonsterPssPy.machine_chng( self.from_bus.number, self.identificator, realar1=p, ) except PsseBaseException: raise
def set_pq(self, cmplx_pq): """Set PQ value in case as actual MVA load """ try: return MonsterPssPy.load_chng( self.from_bus.number, self.identificator, realar1=cmplx_pq.real, realar2=cmplx_pq.imag ) except PsseBaseException: raise
def get_buses(sid): """ Extract bus data from the PSSE case Parameters ========== include_only_in_service: Bool (optional=True) whether to include only in service buses or not """ flag = IncludeStatus.NotAddStepAndNotInService.value bus_names = [ name.strip() for name in MonsterPssPy.abuschar( sid, flag=flag, string='NAME' )[0] ] (bus_voltages,) = MonsterPssPy.abusreal(sid, flag=flag, string='BASE') (bus_numbers, bus_areas, bus_zones, bus_dummies, bus_types) = MonsterPssPy.abusint( sid, flag=flag, string=['NUMBER', 'AREA', 'ZONE', 'DUMMY', 'TYPE'] ) bus_dummies = [bool(bus_dummy) for bus_dummy in bus_dummies] bus_dict = dict() for bus_number, bus_name, bus_voltage, bus_area, bus_zone, bus_dummy, bus_type in \ zip(bus_numbers, bus_names, bus_voltages, bus_areas, bus_zones, bus_dummies, bus_types): bus_details = component_busdetails.BusDetails( bus_number=bus_number, bus_name=bus_name, base_voltage=bus_voltage, areanum=bus_area, zonenum=bus_zone, dummy=bus_dummy, bus_type=bus_type ) bus = component_base.Bus(from_bus=bus_details) bus_dict[bus_number] = bus return bus_dict
def get_two_winding_transformers(sid, bus_dict): """ Extract two winding transformer data from the PSSE case Parameters ========== include_only_in_service: Bool (optional=True) whether to include only in service line or not """ flag = IncludeStatus.NotAddStepAndNotInService.value + 4 ties = 3 (from_buses, to_buses) = MonsterPssPy.abrnint( sid, flag=flag, ties=ties, string=['FROMNUMBER', 'TONUMBER'] ) two_winding_transformer_id = [ name.strip() for name in MonsterPssPy.abrnchar( sid, flag=flag, ties=ties, string='ID' )[0] ] rxes = MonsterPssPy.atrncplx( sid, ties=ties, flag=2, string='RXACT' ) two_winding_transformer_dict = dict() for from_bus, to_bus, two_winding_transformer_id, rx in zip( from_buses, to_buses, two_winding_transformer_id, rxes[0] ): two_winding_transformer = component.TwoWindingTransformer( from_bus=bus_dict[from_bus].from_bus, to_bus=bus_dict[to_bus].from_bus, identificator=two_winding_transformer_id, rx=rx ) two_winding_transformer_dict[ two_winding_transformer.get_sorted_short_tuple() ] = two_winding_transformer return two_winding_transformer_dict
def flow_amp_rate(self): buses = collections.deque(self.get_busnumbers(False)) flow = [] rate_a = self.get_rate() for i in range(3): branch_buses_and_id = [buses[0], buses[1], buses[2], self.identificator] s = self._flow(branch_buses_and_id) if s: i_rate = MonsterPssPy.wnddat(*branch_buses_and_id, string='PCTRTA') s_rate = rate_a[i] * i_rate / 100.0 if s_rate < abs(s.real): flow.append(complex(s.real, 0)) else: flow.append(complex(s.real, math.sqrt(s_rate**2 - s.real**2))) buses.rotate(-1) return flow
def get_msl_components(sid, bus_dict, line_dict): msl_parents = dict() msl_children = dict() flag = IncludeStatus.NotAddStepAndNotInService.value for from_bus, to_bus, identificator in MonsterPssPy.find_multisections(sid, flag=flag): try: MonsterPssPy.inimsl(from_bus, to_bus, identificator) msl_lines = [] line = component.Line( from_bus=bus_dict[from_bus].from_bus, to_bus=bus_dict[to_bus].from_bus, identificator=identificator.strip(), length=0, rate_a=-1, msl_lines=msl_lines ) msl_parents[line.get_sorted_short_tuple()] = line while True: try: ibus, jbus, ickt = MonsterPssPy.nxtmsl() from_bus = bus_dict[ibus].from_bus to_bus = bus_dict[jbus].from_bus component_func = _classify_branch(from_bus, to_bus, ickt) elem_rate_a = MonsterPssPy.brndat( ibus=ibus, jbus=jbus, ickt=ickt, string=RATE_NAME) elem_length = MonsterPssPy.brndat( ibus=ibus, jbus=jbus, ickt=ickt, string='LENGTH') rx = MonsterPssPy.brndt2( ibus=ibus, jbus=jbus, ickt=ickt, string='RX') line = component_func( from_bus=from_bus, to_bus=to_bus, identificator=ickt.strip(), length=elem_length, rate_a=elem_rate_a, rx=rx, msl_component=True ) msl_lines.append(line) msl_children[line.get_sorted_short_tuple()] = line except PsseNxtMslException: break except PsseIniMslException: pass return msl_parents, msl_children
def _change_status(self, from_status, to_status): # Do we need to scale production on generators due to droop? if len(self._scale_area): scale_area = self._scale_area else: scale_area = MonsterPssPy.aareaint(-1, 1, 'NUMBER')[0] # Get total load try: s_total = MonsterPssPy.loddt2(self.from_bus.number, self.identificator, 'TOTAL', 'ACT') except PsseLoddtException as e: if to_status == ComponentStatus.on and e._ierr == 4: s_total = -e._value else: raise if self.status() == to_status: if to_status == ComponentStatus.off: raise PsseBaseException('Load component already out of service.', None) else: raise PsseBaseException('Load component already in service.', None) try: MonsterPssPy.load_chng( self.from_bus.number, self.identificator, intgar1=to_status.get_index() ) # Define SID for scale area sid = 2 MonsterPssPy.bsys(sid, numarea=len(scale_area), areas=scale_area) # Scale with (3) incremental powers, (0) ignore machine # power limits, (0) No Q changes and (2) only type 2 and 3 # buses. MonsterPssPy.scal(sid, 0, 0, [0, 0, 0, 2, 0], [0.0, -s_total.real] + [0.0] * 5) except PsseBaseException: raise
def sensitivity(self, mainsys, dfxfile, netmod='ac', brnflowtyp='amp', transfertyp='export', dispmod=2, toln=0.3): busnr = self.get_busnumbers(False) if self._component_type == ComponentTypeEnum.ThreeWindingTransformerComponent: n = 3 else: n = 1 busnr.append(0) busnr = collections.deque(busnr) sens = [] for _ in range(n): sens.append( MonsterPssPy.sensitivity_flow_to_mw(busnr[0], busnr[1], mainsys, dfxfile, busnr[2], self.identificator, netmod=netmod, brnflowtyp=brnflowtyp, transfertyp=transfertyp, dispmod=dispmod, toln=toln)) if n == 3: busnr.rotate(-1) else: sens = sens[0] return sens
def get_full_topology(case_path_dict): MonsterPssPy.case(case_path_dict.values()[0]) monster_topology = extract_components_from_case( case_path_dict ) return monster_topology
def set_rate(self, file_path): self._rate[file_path] = MonsterPssPy.brndat(*self.get_busnumbers(True), string=RATE_NAME)
def get_real_power(self): args = self.get_busnumbers(True) + ['P'] return MonsterPssPy.brnmsc(*args)
def status(self): status = MonsterPssPy.brnint(self.from_bus.number, self.to_bus.number, self.identificator, 'STATUS') return ComponentStatus.get_enum(status)