Source code for relsad.loadflow.ac.bfs

import numpy as np

from relsad.network.systems.PowerNetwork import PowerNetwork
from relsad.topology.load_flow.bfs import (
    configure_bfs_load_flow_setup,
    is_cyclic,
)


[docs]def run_bfs_load_flow(network: PowerNetwork, maxit: int = 5): """ Solves the load flow with a specified number of iterations The two first septs are to set up additions topology information and to build the main structure Next, it is switched between forward sweeps (Voltage updates) and backward sweeps(load update and loss calcuation) See :doc:`/theory/bfs` for more details. Parameters ---------- network : PowerNetwork The analyzed network maxit : int The number of iterations Returns ------- network.buses : list List of network buses """ # Check if network is radial if is_cyclic(network) is True: raise Exception("The network is not radial, cannot run BFS") # Configure topology list topology_list, network.buses = configure_bfs_load_flow_setup( network.buses, network.lines ) # Run Backward Forward Sweep for _ in range(maxit): accumulate_load(topology_list) update_voltage(topology_list) return network.buses
[docs]def accumulate_load(topology_list): """ Calculates the accumulated downstream active and reactive load at all buses and calculates the active and reactive losses of lines and make an accumulated equivalent load at the buses The function returns the accumulated loads and the accumulated power losses from a branch in the system Parameters ---------- topology_list : list List containing the system topology Returns ------- p_load : float Active accumulated power load of a branch q_load : float Reactive accumulated power load of a branch p_loss : float Active accumulated power loss of a branch q_loss : float Reactive accumulated power loss of a branch """ p_load = 0.0 q_load = 0.0 p_loss = 0.0 q_loss = 0.0 for branch in reversed(topology_list): # Start on last node if len(branch) > 1: for child_branch in branch[1:]: # Do for all child branches ( p_load_child, q_load_child, p_loss_child, q_loss_child, ) = accumulate_load(child_branch) # Add accumulated powers and losses in a branch to # the node where the brancing accurs. p_load += p_load_child q_load += q_load_child p_loss += p_loss_child q_loss += q_loss_child parent_bus = branch[0] # Add local loads p_load_local, q_load_local = get_load(parent_bus) p_load += p_load_local q_load += q_load_local # Add accumulated descriptions to the branching node parent_bus.p_load_downstream = p_load parent_bus.q_load_downstream = q_load # Add line loss if parent_bus.toline: to_line = parent_bus.toline if to_line.connected: # Find the flow to the downstream bus pto = parent_bus.p_load_downstream + p_loss qto = parent_bus.q_load_downstream + q_loss # Estimate the losses of the branch to_line.ploss = ( to_line.r_pu * (pto**2 + qto**2) / parent_bus.vomag**2 ) to_line.qloss = ( to_line.x_pu * (pto**2 + qto**2) / parent_bus.vomag**2 ) p_loss += to_line.ploss q_loss += to_line.qloss # Add the losses to the downstream bus parent_bus.p_loss_downstream = p_loss parent_bus.q_loss_downstream = q_loss # Return the accumulated loads and losses from the current branch return p_load, q_load, p_loss, q_loss
# # Function for calculating the voltages and sensitivities in the single phase case #
[docs]def calc_bus_voltage_sensitivity_single_phase(fbus, tbus, tline, branch: list): """ Calculate the node voltages and sensitivities in the single phase case Parameters ---------- fbus : Bus The sending Bus element tbus : Bus The recieving Bus element tline : Line The recieving Line element branch : list List of branches in the system Returns ------- None """ vk2 = fbus.vomag**2 # Find the accumulated loads and losses flowing on the branch tpload = branch[0].p_load_downstream + branch[0].p_loss_downstream tqload = branch[0].q_load_downstream + branch[0].q_loss_downstream # Voltage calculation term2 = 2 * (tpload * tline.r_pu + tqload * tline.x_pu) term3 = ( (tpload**2 + tqload**2) * (tline.r_pu**2 + tline.x_pu**2) / fbus.vomag**2 ) # Update the bus voltage magnitude on the down-stream bus tbus.vomag = np.sqrt(vk2 - term2 + term3) # Voltage angle calculation busvoltreal = ( fbus.vomag - (tpload * tline.r_pu + tqload * tline.x_pu) / fbus.vomag ) busvoltimag = (tqload * tline.r_pu - tpload * tline.x_pu) / fbus.vomag tbus.voang = fbus.voang + np.arctan2( busvoltimag, busvoltreal ) # Update voltage angles
# # Update the voltage profile starting on the top node #
[docs]def update_voltage(topology_list): """ Update the voltage profile based on the accumulated load on each bus Parameters ---------- topology_list : list List containing the system topology Returns ------- None """ for branch in topology_list: if branch[0].toline: tline = branch[0].toline fbus = tline.fbus tbus = tline.tbus # Update voltages and sensitivities Single Phase calc_bus_voltage_sensitivity_single_phase( fbus, tbus, tline, branch ) if len(branch) > 1: for bus in branch[1:]: # Update voltages along the branches update_voltage(bus)
# # Calculates the load for the actual volage at the bus #
[docs]def get_load(bus): """ Calculates the net voltage corrected load at the bus - currently a simple ZIP model is applied. Parameters ---------- bus : Bus A Bus element Returns ------- p_load_corr : float Corrected active power load q_load_corr : float Corrected reactive power load """ # load - production [PU] relative_pload = bus.pload_pu - bus.pprod_pu relative_qload = bus.qload_pu - bus.qprod_pu p_load_corr = relative_pload * ( bus.ZIP[0] * bus.vomag**2 + bus.ZIP[1] * bus.vomag + bus.ZIP[2] ) q_load_corr = relative_qload * ( bus.ZIP[0] * bus.vomag**2 + bus.ZIP[1] * bus.vomag + bus.ZIP[2] ) return p_load_corr, q_load_corr