79770233

Date: 2025-09-20 10:43:36
Score: 0.5
Natty:
Report link

Thank you to the answer above! In case anyone needs it, here's a full working example of solving the PF equations in GEKKO that includes all bus types (REF/Slack, PQ, and PV):

import copy
from enum import IntEnum

import numpy as np
import pandapower.networks as pn
from gekko import GEKKO
from pandapower import runpp
from pypower import idx_bus, idx_gen
from pypower.makeYbus import makeYbus
from pypower.ppoption import ppoption
from pypower.runpf import runpf


class BusType(IntEnum):
    PQ = 1
    PV = 2
    REF = 3
    ISOLATED = 4


def main():
    # load power grid data
    net = pn.case14()
    # net = pn.case30()
    # net = pn.case57()
    # net = pn.case118()
    # net = pn.case300() # @error: Max Equation Length
    runpp(net)

    if not net.converged:
        raise ValueError("Pandapower power flow did not converge.")

    ppc = copy.deepcopy(net._ppc)

    # define variables
    m = GEKKO(remote=False)

    # define variables
    nb = ppc["bus"].shape[0]

    Vm = m.Array(m.Var, nb, lb=0, value=1)
    theta = m.Array(m.Var, nb, lb=-np.pi, ub=np.pi)

    Pg = m.Array(m.Var, nb)
    Qg = m.Array(m.Var, nb)

    gen_bus_indices = ppc["gen"][:, idx_gen.GEN_BUS].astype(int)
    bus_types = ppc["bus"][:, idx_bus.BUS_TYPE].astype(int)

    # fix variables that are actually constant
    for i in range(nb):
        if bus_types[i] == BusType.REF:
            m.fix(Vm[i], val=ppc["bus"][i, idx_bus.VM])
            m.fix(theta[i], val=np.deg2rad(ppc["bus"][i, idx_bus.VA]))

        if bus_types[i] == BusType.PQ:
            if i in gen_bus_indices:
                gen_idx = np.argwhere(gen_bus_indices == i).item()
                m.fix(Pg[i], val=ppc["gen"][gen_idx, idx_gen.PG] / ppc["baseMVA"])
                m.fix(Qg[i], val=ppc["gen"][gen_idx, idx_gen.QG] / ppc["baseMVA"])
            else:
                m.fix(Pg[i], val=0)
                m.fix(Qg[i], val=0)

        if bus_types[i] == BusType.PV:
            m.fix(Vm[i], val=ppc["bus"][i, idx_bus.VM])
            if i in gen_bus_indices:
                gen_idx = np.argwhere(gen_bus_indices == i).item()
                m.fix(Pg[i], val=ppc["gen"][gen_idx, idx_gen.PG] / ppc["baseMVA"])
            else:
                m.fix(Pg[i], val=0)

    # add parameters
    Pd = ppc["bus"][:, idx_bus.PD] / ppc["baseMVA"]
    Qd = ppc["bus"][:, idx_bus.QD] / ppc["baseMVA"]

    P = Pg - Pd
    Q = Qg - Qd

    Ybus, _, _ = makeYbus(ppc["baseMVA"], ppc["bus"], ppc["branch"])

    Gbus = Ybus.real.toarray()
    Bbus = Ybus.imag.toarray()

    # active power conservation
    m.Equations(
        [
            0 == -P[i] + sum([Vm[i] * Vm[k] * (Gbus[i, k] * m.cos(theta[i] - theta[k]) + Bbus[i, k] * m.sin(theta[i] - theta[k])) for k in range(nb)]) for i in range(nb)
        ]
    )

    # reactive power conservation
    m.Equations(
        [
            0 == -Q[i] + sum([Vm[i] * Vm[k] * (Gbus[i, k] * m.sin(theta[i] - theta[k]) - Bbus[i, k] * m.cos(theta[i] - theta[k])) for k in range(nb)]) for i in range(nb)
        ]
    )

    m.options.SOLVER = 1
    m.options.RTOL = 1e-8
    m.solve(disp=True)

    theta_gekko = np.rad2deg(np.array([theta[i].value[0] for i in range(nb)]))
    Vm_gekko = np.array([Vm[i].value[0] for i in range(nb)])
    Pg_gekko = np.array([Pg[int(i)].value[0] for i in gen_bus_indices]) * ppc["baseMVA"]
    Qg_gekko = np.array([Qg[int(i)].value[0] for i in gen_bus_indices]) * ppc["baseMVA"]

    # compare with pypower solution
    ppopt = ppoption(OUT_ALL=0, VERBOSE=0)
    solved_ppc, success = runpf(ppc, ppopt)

    if success == 0:
        raise ValueError("PYPOWER power flow didn't converge successfully.")

    assert all(np.isclose(theta_gekko, solved_ppc["bus"][:, idx_bus.VA])), "Voltage angles in GEKKO don't match PYPOWER"
    assert all(np.isclose(Vm_gekko, solved_ppc["bus"][:, idx_bus.VM])), "Voltage magnitudes in GEKKO don't match PYPOWER"
    assert all(np.isclose(Pg_gekko, solved_ppc["gen"][:, idx_gen.PG])), "Generator active powers in GEKKO don't match PYPOWER"
    assert all(np.isclose(Qg_gekko, solved_ppc["gen"][:, idx_gen.QG])), "Generator reactive powers in GEKKO don't match PYPOWER"


if __name__ == "__main__":
    main()

Reasons:
  • Blacklisted phrase (0.5): Thank you
  • Long answer (-1):
  • Has code block (-0.5):
  • Self-answer (0.5):
  • Low reputation (1):
Posted by: ktor_vi