def convert_line_to_transformer(self, line: Line): """ Convert a line to Transformer :param line: Line instance :return: Nothing """ transformer = Transformer2W(bus_from=line.bus_from, bus_to=line.bus_to, name='Transformer', active=line.active, rate=line.rate, r=line.R, x=line.X, b=line.B, active_prof=line.active_prof, rate_prof=line.rate_prof) # add device to the circuit self.circuit.add_transformer2w(transformer) # add device to the schematic transformer.graphic_obj = self.add_api_transformer(transformer) # update position transformer.graphic_obj.fromPort.update() transformer.graphic_obj.toPort.update() # delete the line from the circuit self.circuit.delete_line(line) # delete from the schematic self.diagramScene.removeItem(line.graphic_obj)
def convert_branch(branch: Branch): """ :param branch: :return: """ if branch.branch_type == BranchType.Line: return Line(bus_from=branch.bus_from, bus_to=branch.bus_to, name=branch.name, r=branch.R, x=branch.X, b=branch.B, rate=branch.rate, active=branch.active, tolerance=branch.tolerance, cost=branch.Cost, mttf=branch.mttf, mttr=branch.mttr, r_fault=branch.r_fault, x_fault=branch.x_fault, fault_pos=branch.fault_pos, length=branch.length, temp_base=branch.temp_base, temp_oper=branch.temp_oper, alpha=branch.alpha, rate_prof=branch.rate_prof, Cost_prof=branch.Cost_prof, active_prof=branch.active_prof, temp_oper_prof=branch.temp_oper_prof) elif branch.branch_type == BranchType.Transformer: return Transformer2W(bus_from=branch.bus_from, bus_to=branch.bus_to, name=branch.name, r=branch.R, x=branch.X, b=branch.B, rate=branch.rate, active=branch.active, tolerance=branch.tolerance, cost=branch.Cost, mttf=branch.mttf, mttr=branch.mttr, tap=branch.tap_module, shift_angle=branch.angle, vset=branch.vset, bus_to_regulated=branch.bus_to_regulated, temp_base=branch.temp_base, temp_oper=branch.temp_oper, alpha=branch.alpha, template=branch.template, rate_prof=branch.rate_prof, Cost_prof=branch.Cost_prof, active_prof=branch.active_prof, temp_oper_prof=branch.temp_oper_prof) else: return branch
def add_transformer(self, branch: Transformer2W): """ Add branch to the schematic :param branch: Branch object """ terminal_from = branch.bus_from.graphic_obj.terminal terminal_to = branch.bus_to.graphic_obj.terminal graphic_obj = TransformerGraphicItem(terminal_from, terminal_to, self.diagramScene, branch=branch) graphic_obj.diagramScene.circuit = self.circuit # add pointer to the circuit terminal_from.hosting_connections.append(graphic_obj) terminal_to.hosting_connections.append(graphic_obj) graphic_obj.redraw() branch.graphic_obj = graphic_obj
def scene_mouse_release_event(self, event): """ Finalize the branch creation if its drawing ends in a terminal @param event: @return: """ # Clear or finnish the started connection: if self.started_branch: pos = event.scenePos() items = self.diagramScene.items(pos) # get the item (the terminal) at the mouse position for item in items: if type(item) is TerminalItem: # connect only to terminals if item.parent is not self.started_branch.fromPort.parent: # forbid connecting to itself self.started_branch.setToPort(item) item.hosting_connections.append(self.started_branch) self.started_branch.bus_to = item.parent if self.started_branch.bus_from.api_object.is_dc != self.started_branch.bus_to.api_object.is_dc: # different DC status -> VSC name = 'VSC ' + str(len(self.circuit.vsc_devices) + 1) obj = VSC(bus_from=self.started_branch.bus_from.api_object, bus_to=self.started_branch.bus_to.api_object, name=name) obj.graphic_obj = VscGraphicItem(fromPort=self.started_branch.fromPort, toPort=self.started_branch.toPort, diagramScene=self.diagramScene, branch=obj) elif self.started_branch.bus_from.api_object.is_dc and self.started_branch.bus_to.api_object.is_dc: # both buses are DC name = 'Dc line ' + str(len(self.circuit.dc_lines) + 1) obj = DcLine(bus_from=self.started_branch.bus_from.api_object, bus_to=self.started_branch.bus_to.api_object, name=name) obj.graphic_obj = DcLineGraphicItem(fromPort=self.started_branch.fromPort, toPort=self.started_branch.toPort, diagramScene=self.diagramScene, branch=obj) else: # Same DC status -> line / trafo v1 = self.started_branch.bus_from.api_object.Vnom v2 = self.started_branch.bus_to.api_object.Vnom if abs(v1 - v2) > 1.0: name = 'Transformer ' + str(len(self.circuit.transformers2w) + 1) obj = Transformer2W(bus_from=self.started_branch.bus_from.api_object, bus_to=self.started_branch.bus_to.api_object, name=name) obj.graphic_obj = TransformerGraphicItem(fromPort=self.started_branch.fromPort, toPort=self.started_branch.toPort, diagramScene=self.diagramScene, branch=obj) else: name = 'Line ' + str(len(self.circuit.lines) + 1) obj = Line(bus_from=self.started_branch.bus_from.api_object, bus_to=self.started_branch.bus_to.api_object, name=name) obj.graphic_obj = LineGraphicItem(fromPort=self.started_branch.fromPort, toPort=self.started_branch.toPort, diagramScene=self.diagramScene, branch=obj) # add the new object to the circuit self.circuit.add_branch(obj) # update the connection placement obj.graphic_obj.fromPort.update() obj.graphic_obj.toPort.update() # set the connection placement obj.graphic_obj.setZValue(-1) # if self.started_branch.toPort is None: self.started_branch.remove_widget() # release this pointer self.started_branch = None
def test_basic(): """ Basic GridCal test, also useful for a basic tutorial. In this case the magnetizing branch of the transformers is neglected by inputting 1e-20 excitation current and iron core losses. The results are identical to ETAP's, which always uses this assumption in balanced load flow calculations. """ test_name = "test_basic" grid = MultiCircuit(name=test_name) S_base = 100 # MVA grid.Sbase = S_base grid.time_profile = None grid.logger = Logger() # Create buses POI = Bus( name="POI", vnom=100, # kV is_slack=True) grid.add_bus(POI) B_C3 = Bus(name="B_C3", vnom=10) # kV grid.add_bus(B_C3) B_MV_M32 = Bus(name="B_MV_M32", vnom=10) # kV grid.add_bus(B_MV_M32) B_LV_M32 = Bus(name="B_LV_M32", vnom=0.6) # kV grid.add_bus(B_LV_M32) # Create voltage controlled generators (or slack, a.k.a. swing) UT = Generator(name="Utility") UT.bus = POI grid.add_generator(POI, UT) # Create static generators (with fixed power factor) M32 = StaticGenerator( name="M32", P=4.2, # MW Q=0.0j) # MVAr M32.bus = B_LV_M32 grid.add_static_generator(B_LV_M32, M32) # Create transformer types s = 5 # MVA z = 8 # % xr = 40 SS = TransformerType( name="SS", hv_nominal_voltage=100, # kV lv_nominal_voltage=10, # kV nominal_power=s, copper_losses=complex_impedance(z, xr).real * s * 1000 / S_base, iron_losses=1e-20, no_load_current=1e-20, short_circuit_voltage=z) grid.add_transformer_type(SS) s = 5 # MVA z = 6 # % xr = 20 PM = TransformerType( name="PM", hv_nominal_voltage=10, # kV lv_nominal_voltage=0.6, # kV nominal_power=s, copper_losses=complex_impedance(z, xr).real * s * 1000 / S_base, iron_losses=1e-20, no_load_current=1e-20, short_circuit_voltage=z) grid.add_transformer_type(PM) # Create branches X_C3 = Transformer2W(bus_from=POI, bus_to=B_C3, name="X_C3", template=SS) grid.add_transformer2w(X_C3) C_M32 = Transformer2W(bus_from=B_C3, bus_to=B_MV_M32, name="C_M32", r=0.784, x=0.174) grid.add_transformer2w(C_M32) X_M32 = Transformer2W(bus_from=B_MV_M32, bus_to=B_LV_M32, name="X_M32", template=PM) grid.add_transformer2w(X_M32) # Apply templates (device types) grid.apply_all_branch_types() print("Buses:") for i, b in enumerate(grid.buses): print(f" - bus[{i}]: {b}") print() options = PowerFlowOptions(SolverType.NR, verbose=True, initialize_with_existing_solution=True, multi_core=True, control_q=ReactivePowerControlMode.Direct, tolerance=1e-6, max_iter=99) power_flow = PowerFlowDriver(grid, options) power_flow.run() approx_volt = [round(100 * abs(v), 1) for v in power_flow.results.voltage] solution = [ 100.0, 99.6, 102.7, 102.9 ] # Expected solution from GridCal and ETAP 16.1.0, for reference print() print(f"Test: {test_name}") print(f"Results: {approx_volt}") print(f"Solution: {solution}") print() print("Generators:") for g in grid.get_generators(): print(f" - Generator {g}: q_min={g.Qmin}pu, q_max={g.Qmax}pu") print() print("Branches:") branches = grid.get_branches() for b in branches: print(f" - {b}:") print(f" R = {round(b.R, 4)} pu") print(f" X = {round(b.X, 4)} pu") print(f" X/R = {round(b.X/b.R, 1)}") print(f" G = {round(b.G, 4)} pu") print(f" B = {round(b.B, 4)} pu") print() print("Transformer types:") for t in grid.transformer_types: print( f" - {t}: Copper losses={int(t.Pcu)}kW, Iron losses={int(t.Pfe)}kW, SC voltage={t.Vsc}%" ) print() print("Losses:") for i in range(len(branches)): print( f" - {branches[i]}: losses={1000*round(power_flow.results.losses[i], 3)} kVA" ) print() equal = True for i in range(len(approx_volt)): if approx_volt[i] != solution[i]: equal = False assert equal
def test_pv_1(): """ Voltage controlled generator test, also useful for a basic tutorial. In this case the generator M32 regulates the voltage at a setpoint of 1.025 pu, and the slack bus (POI) regulates it at 1.0 pu. The transformers' magnetizing branch losses are considered, but their voltage regulators aren't. """ test_name = "test_pv_1" grid = MultiCircuit(name=test_name) Sbase = 100 # MVA grid.Sbase = Sbase grid.time_profile = None grid.logger = Logger() # Create buses POI = Bus( name="POI", vnom=100, # kV is_slack=True) grid.add_bus(POI) B_MV_M32 = Bus(name="B_MV_M32", vnom=10) # kV grid.add_bus(B_MV_M32) B_LV_M32 = Bus(name="B_LV_M32", vnom=0.6) # kV grid.add_bus(B_LV_M32) # Create voltage controlled generators (or slack, a.k.a. swing) UT = Generator(name="Utility") UT.bus = POI grid.add_generator(POI, UT) M32 = Generator(name="M32", active_power=4.2, voltage_module=1.025, Qmin=-2.5, Qmax=2.5) M32.bus = B_LV_M32 grid.add_generator(B_LV_M32, M32) # Create transformer types s = 100 # MVA z = 8 # % xr = 40 SS = TransformerType( name="SS", hv_nominal_voltage=100, # kV lv_nominal_voltage=10, # kV nominal_power=s, copper_losses=complex_impedance(z, xr).real * s * 1000 / Sbase, iron_losses=125, # kW no_load_current=0.5, # % short_circuit_voltage=z) grid.add_transformer_type(SS) s = 5 # MVA z = 6 # % xr = 20 PM = TransformerType( name="PM", hv_nominal_voltage=10, # kV lv_nominal_voltage=0.6, # kV nominal_power=s, copper_losses=complex_impedance(z, xr).real * s * 1000 / Sbase, iron_losses=6.25, # kW no_load_current=0.5, # % short_circuit_voltage=z) grid.add_transformer_type(PM) # Create branches X_C3 = Transformer2W(bus_from=POI, bus_to=B_MV_M32, name="X_C3", template=SS) grid.add_branch(X_C3) X_M32 = Transformer2W(bus_from=B_MV_M32, bus_to=B_LV_M32, name="X_M32", template=PM) grid.add_branch(X_M32) # Apply templates (device types) grid.apply_all_branch_types() print("Buses:") for i, b in enumerate(grid.buses): print(f" - bus[{i}]: {b}") print() options = PowerFlowOptions(SolverType.LM, verbose=True, initialize_with_existing_solution=True, multi_core=True, control_q=ReactivePowerControlMode.Direct, tolerance=1e-6, max_iter=99) power_flow = PowerFlowDriver(grid, options) power_flow.run() approx_volt = [round(100 * abs(v), 1) for v in power_flow.results.voltage] solution = [ 100.0, 100.1, 102.5 ] # Expected solution from GridCal and ETAP 16.1.0, for reference print() print(f"Test: {test_name}") print(f"Results: {approx_volt}") print(f"Solution: {solution}") print() print("Generators:") for g in grid.get_generators(): print(f" - Generator {g}: q_min={g.Qmin}pu, q_max={g.Qmax}pu") print() print("Branches:") branches = grid.get_branches() for b in branches: print(f" - {b}:") print(f" R = {round(b.R, 4)} pu") print(f" X = {round(b.X, 4)} pu") print(f" X/R = {round(b.X / b.R, 1)}") print(f" G = {round(b.G, 4)} pu") print(f" B = {round(b.B, 4)} pu") print() print("Transformer types:") for t in grid.transformer_types: print( f" - {t}: Copper losses={int(t.Pcu)}kW, Iron losses={int(t.Pfe)}kW, SC voltage={t.Vsc}%" ) print() print("Losses:") for i in range(len(branches)): print( f" - {branches[i]}: losses={1000 * round(power_flow.results.losses[i], 3)} kVA" ) print() equal = True for i in range(len(approx_volt)): if approx_volt[i] != solution[i]: equal = False assert equal