def build_linear_ac_sys_mat(self): """ Get the AC linear approximation matrices :return: """ A11 = -self.Yseries.imag[np.ix_(self.pqpv, self.pqpv)] A12 = self.Ybus.real[np.ix_(self.pqpv, self.pq)] A21 = -self.Yseries.real[np.ix_(self.pq, self.pqpv)] A22 = -self.Ybus.imag[np.ix_(self.pq, self.pq)] A = vstack_s([hstack_s([A11, A12]), hstack_s([A21, A22])], format="csc") # form the slack system matrix A11s = -self.Yseries.imag[np.ix_(self.ref, self.pqpv)] A12s = self.Ybus.real[np.ix_(self.ref, self.pq)] A_slack = hstack_s([A11s, A12s], format="csr") self.Asys = factorized(A) return A, A_slack
def __init__(self, Y, Ys, S, V, pv, pq, vd): """ Linearized AC power flow, solved with a linear solver :o :param Y: Admittance matrix :param Ys: Admittance matrix of the series elements :param S: Power injections vector :param V: Initial voltages :param pv: pv node indices :param pq: pq node indices :param vd: slack node indices """ # node sets self.pv = pv self.vd = vd self.pq = pq self.pvpq = r_[pv, pq] self.V = V.copy() # form the system matrix A11 = -Ys.imag[self.pvpq, :][:, self.pvpq] A12 = Y.real[self.pvpq, :][:, self.pq] A21 = -Ys.real[self.pq, :][:, self.pvpq] A22 = -Y.imag[self.pq, :][:, self.pq] self.H = vstack_s( [hstack_s([A11, A12]), hstack_s([A21, A22])], format="csr") # compose the right hand side (power vectors) self.rhs = r_[S.real[self.pvpq], S.imag[self.pq]] # declare the voltage angles self.n = self.H.shape[0] self.dx = [None] * self.n for i in range(self.n): self.dx[i] = LpVariable("dx" + str(i)) # declare the generation self.PG = list()
def LACPF_2(Y, Ys, S, Vset, pq, pv): """ Linearized AC Load Flow Args: Y: Admittance matrix Ys: Admittance matrix of the series elements S: Power injections vector of all the nodes Vset: Set voltages of all the nodes (used for the slack and PV nodes) pq: list of indices of the pq nodes pv: list of indices of the pv nodes Returns: Voltage vector and error """ pvpq = r_[pv, pq] npq = len(pq) npv = len(pv) # compose the system matrix G = Y.real B = Y.imag Gp = Ys.real Bp = Ys.imag A11 = -Bp[np.ix_(pvpq, pvpq)] A12 = G[np.ix_(pvpq, pq)] A21 = -Gp[np.ix_(pq, pvpq)] A22 = -B[np.ix_(pq, pq)] Asys = vstack_s([hstack_s([A11, A12]), hstack_s([A21, A22])], format="csc") # compose the right hand side (power vectors) rhs = r_[S.real[pvpq], S.imag[pq]] # solve the linear system x = spsolve(Asys, rhs) # compose the results vector voltages_vector = Vset.copy() # set the pv voltages va_pv = x[0:npv] vm_pv = np.abs(Vset[pv]) voltages_vector[pv] = vm_pv * np.exp(1.0j * va_pv) # set the PQ voltages va_pq = x[npv:npv + npq] vm_pq = np.ones(npq) - x[npv + npq::] voltages_vector[pq] = vm_pq * np.exp(1.0j * va_pq) # Calculate the error and check the convergence Scalc = voltages_vector * conj(Y * voltages_vector) # complex power mismatch power_mismatch = Scalc - S # ------------------------------------------------------------------------------------------------------------------ drhs = r_[power_mismatch.real[pvpq], power_mismatch.imag[pq]] dx = spsolve(Asys, drhs) # set the pv voltages va_pv = x[0:npv] + dx[0:npv] vm_pv = np.abs(Vset[pv]) voltages_vector[pv] = vm_pv * np.exp(1.0j * va_pv) # set the PQ voltages va_pq = x[npv:npv + npq] + dx[npv:npv + npq] vm_pq = np.ones(npq) - x[npv + npq::] - dx[npv + npq::] voltages_vector[pq] = vm_pq * np.exp(1.0j * va_pq) # ------------------------------------------------------------------------------------------------------------------ # concatenate error by type mismatch = r_[power_mismatch[pv].real, power_mismatch[pq].real, power_mismatch[pq].imag] # check for convergence normF = linalg.norm(mismatch, Inf) return voltages_vector, normF
def prepare_system_matrices(Ybus, Vbus, bus_idx, pqpv, pq, pv, ref): """ Prepare the system matrices :param Ybus: :param Vbus: :param pqpv: :param ref: :return: """ n_bus = len(Vbus) n_bus2 = 2 * n_bus npv = len(pv) # ################################################################################################################## # Compute the starting voltages # ################################################################################################################## # System matrix A = lil_matrix((n_bus2, n_bus2)) # lil matrices are faster to populate # Expanded slack voltages Vslack = zeros(n_bus2) # Populate A for a in pqpv: # rows for ii in range(Ybus.indptr[a], Ybus.indptr[a + 1]): # columns in sparse format b = Ybus.indices[ii] A[2 * a + 0, 2 * b + 0] = Ybus[a, b].real A[2 * a + 0, 2 * b + 1] = -Ybus[a, b].imag A[2 * a + 1, 2 * b + 0] = Ybus[a, b].imag A[2 * a + 1, 2 * b + 1] = Ybus[a, b].real # set vd elements for a in ref: A[a * 2, a * 2] = 1.0 A[a * 2 + 1, a * 2 + 1] = 1.0 Vslack[a * 2] = Vbus[a].real Vslack[a * 2 + 1] = Vbus[a].imag # Solve starting point voltages Vst_expanded = factorized(A.tocsc())(Vslack) # Invert the voltages obtained: Get the complex voltage and voltage inverse vectors Vst = Vst_expanded[2 * bus_idx] + 1j * Vst_expanded[2 * bus_idx + 1] Wst = 1.0 / Vst # ################################################################################################################## # Compute the final system matrix # ################################################################################################################## # System matrices B = lil_matrix((n_bus2, npv)) C = lil_matrix((npv, n_bus2 + npv)) for i, a in enumerate(pv): # "a" is the actual bus index # "i" is the number of the pv bus in the pv buses list B[2 * a + 0, i + 0] = Wst[a].imag B[2 * a + 1, i + 0] = Wst[a].real C[i + 0, 2 * a + 0] = Vst[a].real C[i + 0, 2 * a + 1] = Vst[a].imag Asys = vstack_s([hstack_s([A, B]), C], format="csc") return Asys, Vst, Wst
def __init__(self, circuit: MultiCircuit, voltage_band=0.1): """ Linearized AC power flow, solved with a linear solver :o :param circuit: GridCal Circuit instance """ self.vm_low = 1.0 - voltage_band self.vm_high = 1.0 + voltage_band self.load_shedding = False self.circuit = circuit self.Sbase = circuit.Sbase # node sets self.pv = circuit.power_flow_input.pv self.pq = circuit.power_flow_input.pq self.vd = circuit.power_flow_input.ref self.pvpq = r_[self.pv, self.pq] self.pvpqpq = r_[self.pv, self.pq, self.pq] Y = circuit.power_flow_input.Ybus self.B = circuit.power_flow_input.Ybus.imag Ys = circuit.power_flow_input.Yseries S = circuit.power_flow_input.Sbus self.V = circuit.power_flow_input.Vbus.copy() # form the system matrix A11 = -Ys.imag[self.pvpq, :][:, self.pvpq] A12 = Y.real[self.pvpq, :][:, self.pq] A21 = -Ys.real[self.pq, :][:, self.pvpq] A22 = -Y.imag[self.pq, :][:, self.pq] self.sys_mat = vstack_s([hstack_s([A11, A12]), hstack_s([A21, A22])], format="csr") # form the slack system matrix A11s = -Ys.imag[self.vd, :][:, self.pvpq] A12s = Y.real[self.vd, :][:, self.pq] self.sys_mat_slack = hstack_s([A11s, A12s], format="csr") # compose the right hand side (power vectors) self.rhs = r_[S.real[self.pvpq], S.imag[self.pq]] # declare the voltage increments dx self.nn = self.sys_mat.shape[0] self.nbranch = len(self.circuit.branches) self.nbus = len(self.circuit.buses) self.dx_var = [None] * self.nn self.flow_ij = [None] * self.nbranch self.flow_ji = [None] * self.nbranch self.theta_dict = dict() self.loads = np.zeros(self.nn) self.load_shed = [None] * self.nn npv = len(self.pv) npq = len(self.pq) for i in range(self.nn): if i < (npv+npq): self.dx_var[i] = LpVariable("Va" + str(i), -0.5, 0.5) self.theta_dict[self.pvpq[i]] = self.dx_var[i] # dictionary to store the angles for the pvpq nodes self.load_shed[i] = pulp.LpVariable("LoadShed_P_" + str(i), 0.0, 1e20) else: self.dx_var[i] = LpVariable("Vm" + str(i)) self.load_shed[i] = pulp.LpVariable("LoadShed_Q_" + str(i), 0.0, 1e20) # declare the slack vars self.slack_loading_ij_p = [None] * self.nbranch self.slack_loading_ji_p = [None] * self.nbranch self.slack_loading_ij_n = [None] * self.nbranch self.slack_loading_ji_n = [None] * self.nbranch if self.load_shedding: pass else: for i in range(self.nbranch): self.slack_loading_ij_p[i] = pulp.LpVariable("LoadingSlack_ij_p_" + str(i), 0, 1e20) self.slack_loading_ji_p[i] = pulp.LpVariable("LoadingSlack_ji_p_" + str(i), 0, 1e20) self.slack_loading_ij_n[i] = pulp.LpVariable("LoadingSlack_ij_n_" + str(i), 0, 1e20) self.slack_loading_ji_n[i] = pulp.LpVariable("LoadingSlack_ji_n_" + str(i), 0, 1e20) # declare the generation self.PG = list() # LP problem self.problem = None # potential errors flag self.potential_errors = False # Check if the problem was solved or not self.solved = False # LP problem restrictions saved on build and added to the problem with every load change self.s_restrictions = list() self.p_restrictions = list()
def lacpf(bus_admittances, series_admittances, complex_bus_powers, current_injections_and_extractions, bus_voltages, pq_bus_indices, pv_bus_indices): """ Linearized AC Load Flow form the article: Linearized AC Load Flow Applied to Analysis in Electric Power Systems by: P. Rossoni, W. M da Rosa and E. A. Belati Args: bus_admittances: Admittance matrix series_admittances: Admittance matrix of the series elements complex_bus_powers: Power injections vector of all the nodes bus_voltages: Set voltages of all the nodes (used for the slack and PV nodes) pq_bus_indices: list of indices of the pq nodes pv_bus_indices: list of indices of the pv nodes Returns: Voltage vector, converged?, error, calculated power and elapsed time """ start = time.time() pvpq = r_[pv_bus_indices, pq_bus_indices] npq = len(pq_bus_indices) npv = len(pv_bus_indices) # compose the system matrix # G = Y.real # B = Y.imag # Gp = Ys.real # Bp = Ys.imag A11 = -series_admittances.imag[pvpq, :][:, pvpq] A12 = bus_admittances.real[pvpq, :][:, pq_bus_indices] A21 = -series_admittances.real[pq_bus_indices, :][:, pvpq] A22 = -bus_admittances.imag[pq_bus_indices, :][:, pq_bus_indices] Asys = vstack_s([hstack_s([A11, A12]), hstack_s([A21, A22])], format="csc") # compose the right hand side (power vectors) rhs = r_[complex_bus_powers.real[pvpq], complex_bus_powers.imag[pq_bus_indices]] # solve the linear system try: x = spsolve(Asys, rhs) except Exception as e: voltages_vector = bus_voltages # Calculate the error and check the convergence s_calc = voltages_vector * conj(bus_admittances * voltages_vector) # complex power mismatch power_mismatch = s_calc - complex_bus_powers # concatenate error by type mismatch = r_[power_mismatch[pv_bus_indices].real, power_mismatch[pq_bus_indices].real, power_mismatch[pq_bus_indices].imag] # check for convergence norm_f = linalg.norm(mismatch, Inf) end = time.time() elapsed = end - start return voltages_vector, False, norm_f, s_calc, 1, elapsed # compose the results vector voltages_vector = bus_voltages.copy() # set the pv voltages va_pv = x[0:npv] vm_pv = npabs(bus_voltages[pv_bus_indices]) voltages_vector[pv_bus_indices] = vm_pv * exp(1j * va_pv) # set the PQ voltages va_pq = x[npv:npv+npq] vm_pq = ones(npq) + x[npv+npq::] voltages_vector[pq_bus_indices] = vm_pq * exp(1j * va_pq) # Calculate the error and check the convergence s_calc = voltages_vector * conj(bus_admittances * voltages_vector) # complex power mismatch power_mismatch = s_calc - complex_bus_powers # concatenate error by type mismatch = r_[power_mismatch[pv_bus_indices].real, power_mismatch[pq_bus_indices].real, power_mismatch[pq_bus_indices].imag] # check for convergence norm_f = linalg.norm(mismatch, Inf) end = time.time() elapsed = end - start return voltages_vector, True, norm_f, s_calc, 1, elapsed
def lacpf(Y, Ys, S, I, Vset, pq, pv): """ Linearized AC Load Flow form the article: Linearized AC Load Flow Applied to Analysis in Electric Power Systems by: P. Rossoni, W. M da Rosa and E. A. Belati Args: Y: Admittance matrix Ys: Admittance matrix of the series elements S: Power injections vector of all the nodes Vset: Set voltages of all the nodes (used for the slack and PV nodes) pq: list of indices of the pq nodes pv: list of indices of the pv nodes Returns: Voltage vector, converged?, error, calculated power and elapsed time """ start = time.time() pvpq = r_[pv, pq] npq = len(pq) npv = len(pv) # compose the system matrix # G = Y.real # B = Y.imag # Gp = Ys.real # Bp = Ys.imag A11 = -Ys.imag[pvpq, :][:, pvpq] A12 = Y.real[pvpq, :][:, pq] A21 = -Ys.real[pq, :][:, pvpq] A22 = -Y.imag[pq, :][:, pq] Asys = vstack_s([hstack_s([A11, A12]), hstack_s([A21, A22])], format="csc") # compose the right hand side (power vectors) rhs = r_[S.real[pvpq], S.imag[pq]] # solve the linear system x = factorized(Asys)(rhs) # compose the results vector voltages_vector = Vset.copy() # set the pv voltages va_pv = x[0:npv] vm_pv = npabs(Vset[pv]) voltages_vector[pv] = vm_pv * exp(1j * va_pv) # set the PQ voltages va_pq = x[npv:npv + npq] vm_pq = ones(npq) + x[npv + npq::] voltages_vector[pq] = vm_pq * exp(1j * va_pq) # Calculate the error and check the convergence s_calc = voltages_vector * conj(Y * voltages_vector) # complex power mismatch power_mismatch = s_calc - S # concatenate error by type mismatch = r_[power_mismatch[pv].real, power_mismatch[pq].real, power_mismatch[pq].imag] # check for convergence norm_f = linalg.norm(mismatch, Inf) end = time.time() elapsed = end - start return voltages_vector, True, norm_f, s_calc, 1, elapsed