Exemple #1
0
def optimal_power_flow_energy_reserve(*args):
    casedata = args[0] # Target power flow modelling
    beta = args[1] # The reserve level
    mpc = loadcase(casedata) # Import the power flow modelling
    ## convert to internal indexing
    mpc = ext2int(mpc)
    baseMVA, bus, gen, branch,gencost = mpc["baseMVA"], mpc["bus"], mpc["gen"], mpc["branch"],mpc["gencost"] #

    nb = shape(mpc['bus'])[0]  ## number of buses
    nl = shape(mpc['branch'])[0]  ## number of branches
    ng = shape(mpc['gen'])[0]  ## number of dispatchable injections

    ## Formualte the
    stat = branch[:, BR_STATUS]  ## ones at in-service branches
    b = stat / branch[:, BR_X]  ## series susceptance
    tap = ones(nl)  ## default tap ratio = 1
    i = find(branch[:, TAP])  ## indices of non-zero tap ratios
    tap[i] = branch[i, TAP]  ## assign non-zero tap ratios

    ## build connection matrix Cft = Cf - Ct for line and from - to buses
    f = branch[:, F_BUS]  ## list of "from" buses
    t = branch[:, T_BUS]  ## list of "to" buses
    i = r_[range(nl), range(nl)]  ## double set of row indices
    ## connection matrix
    Cft = sparse((r_[ones(nl), -ones(nl)], (i, r_[f, t])), (nl, nb))

    ## build Bf such that Bf * Va is the vector of real branch powers injected
    ## at each branch's "from" bus
    Bf = sparse((r_[b, -b], (i, r_[f, t])), shape=(nl, nb))  ## = spdiags(b, 0, nl, nl) * Cft

    ## build Bbus
    Bbus = Cft.T * Bf
    # The distribution factor
    Distribution_factor = sparse(Bf*inv(Bbus))

    Cg = sparse((ones(ng), (gen[:, GEN_BUS], arange(ng))), (nb, ng)) # Sparse index generation method is different from the way of matlab
    Cd = sparse((ones(nb), (bus[:, BUS_I], arange(nb))), (nb, nb)) # Sparse index load

    Pd = sum(bus[:,PD]) # Total power demand

    # Formulate the problem
    lb = concatenate((gen[:,PMIN],zeros(ng))) # extend the
    ub = concatenate((gen[:,PMAX],gen[:,PMAX]))
    Aeq = sparse(concatenate((ones(ng),zeros(ng))))
    beq = [Pd]

    Aineq = sparse(hstack([Distribution_factor * Cg,zeros((nl,ng))]))
    Aineq = vstack([Aineq, -Aineq])
    # The ramp reserve requirement
    Aineq = vstack([Aineq, sparse((r_[ones(ng), ones(ng)], (r_[arange(ng), arange(ng)], r_[arange(ng), ng+arange(ng)])), (ng, 2*ng))])
    Aineq = vstack([Aineq, sparse((r_[-ones(ng), ones(ng)], (r_[arange(ng), arange(ng)], r_[arange(ng), ng+arange(ng)])), (ng, 2*ng))])
    bineq = concatenate((branch[:, RATE_A] + Distribution_factor * Cd * bus[:, PD], branch[:, RATE_A] - Distribution_factor * Cd * bus[:, PD]))
    bineq = concatenate((bineq, gen[:, PMAX]))
    bineq = concatenate((bineq, -gen[:, PMIN]))
    c = concatenate((gencost[:,5],zeros(ng)))
    Q = diag(concatenate((gencost[:,4],zeros(ng))))
    (Pg,obj) = miqp_gurobi(c = c,Q = Q, Aeq = Aeq, beq = beq, A = Aineq, b = bineq, xmin = lb,xmax = ub)
    obj =  obj + sum(gencost[:,6])
    return Pg, obj
def draw_network(fignr=754):
	from matplotlib.pyplot import figure, show
	import networkx as nx

	casedata = get_topology()
	ppc = casedata
	ppopt = ppoption(PF_ALG=2)
	ppc = ext2int(ppc)
	figure(fignr)
	g = nx.Graph()
	i = ppc['bus'][:, BUS_I].astype(int)
	g.add_nodes_from(i, bgcolor='green')
	#nx.draw_networkx_nodes(g,pos=nx.spring_layout(g))
	fr = ppc['branch'][:, F_BUS].astype(int)
	to = ppc['branch'][:, T_BUS].astype(int)
	g.add_edges_from(zip(fr, to), color='magenta')
	nx.draw(g, with_labels=True, node_size=1000,node_color='skyblue',width=0.5)
	show()
def draw_network(fignr=754):
    from matplotlib.pyplot import figure, show
    import networkx as nx

    casedata = get_topology()
    ppc = casedata
    ppopt = ppoption(PF_ALG=2)
    ppc = ext2int(ppc)
    figure(fignr)
    g = nx.Graph()
    i = ppc['bus'][:, BUS_I].astype(int)
    g.add_nodes_from(i, bgcolor='green')
    #nx.draw_networkx_nodes(g,pos=nx.spring_layout(g))
    fr = ppc['branch'][:, F_BUS].astype(int)
    to = ppc['branch'][:, T_BUS].astype(int)
    g.add_edges_from(zip(fr, to), color='magenta')
    nx.draw(g,
            with_labels=True,
            node_size=1000,
            node_color='skyblue',
            width=0.5)
    show()
Exemple #4
0
def run(mpc):
    """
    Gurobi based optimal power flow modelling and solution
    :param mpc: The input case of optimal power flow
    :return: obtained solution
    """
    # Data format
    from pypower.idx_brch import F_BUS, T_BUS, BR_R, BR_X, TAP, SHIFT, BR_STATUS, RATE_A
    from pypower.idx_cost import MODEL, NCOST, PW_LINEAR, COST, POLYNOMIAL
    from pypower.idx_bus import BUS_TYPE, REF, VA, VM, PD, GS, VMAX, VMIN, BUS_I, QD
    from pypower.idx_gen import GEN_BUS, VG, PG, QG, PMAX, PMIN, QMAX, QMIN
    from pypower.ext2int import ext2int

    mpc = ext2int(mpc)
    baseMVA, bus, gen, branch, gencost = mpc["baseMVA"], mpc["bus"], mpc[
        "gen"], mpc["branch"], mpc["gencost"]

    nb = shape(mpc['bus'])[0]  # number of buses
    nl = shape(mpc['branch'])[0]  # number of branches
    ng = shape(mpc['gen'])[0]  # number of dispatchable injections
    f = branch[:, F_BUS]  ## list of "from" buses
    t = branch[:, T_BUS]  ## list of "to" buses
    i = range(nl)  ## double set of row indices
    # Connection matrix
    Cf = sparse((ones(nl), (i, f)), (nl, nb))
    Ct = sparse((ones(nl), (i, t)), (nl, nb))
    Cg = sparse((ones(ng), (gen[:, GEN_BUS], range(ng))), (nb, ng))
    Branch_R = branch[:, BR_R]
    Branch_X = branch[:, BR_X]
    Cf = Cf.T
    Ct = Ct.T
    # Obtain the boundary information

    Slmax = branch[:, RATE_A] / baseMVA
    Pij_l = -Slmax
    Qij_l = -Slmax
    Iij_l = zeros(nl)
    Vm_l = turn_to_power(bus[:, VMIN], 2)
    Pg_l = gen[:, PMIN] / baseMVA
    Qg_l = gen[:, QMIN] / baseMVA

    Pij_u = Slmax
    Qij_u = Slmax
    Iij_u = Slmax
    Vm_u = turn_to_power(bus[:, VMAX], 2)
    Pg_u = 2 * gen[:, PMAX] / baseMVA
    Qg_u = 2 * gen[:, QMAX] / baseMVA
    lx = concatenate([Pij_l, Qij_l, Iij_l, Vm_l, Pg_l, Qg_l])
    ux = concatenate([Pij_u, Qij_u, Iij_u, Vm_u, Pg_u, Qg_u])
    model = Model("OPF")
    # Define the decision variables
    x = {}
    nx = 3 * nl + nb + 2 * ng
    for i in range(nx):
        x[i] = model.addVar(lb=lx[i], ub=ux[i], vtype=GRB.CONTINUOUS)

    # Add system level constraints
    Aeq_p = hstack([
        Ct - Cf,
        zeros((nb, nl)), -diag(Ct * Branch_R) * Ct,
        zeros((nb, nb)), Cg,
        zeros((nb, ng))
    ])
    beq_p = bus[:, PD] / baseMVA
    # Add constraints for each sub system
    Aeq_q = hstack([
        zeros((nb, nl)), Ct - Cf, -diag(Ct * Branch_X) * Ct,
        zeros((nb, nb)),
        zeros((nb, ng)), Cg
    ])
    beq_q = bus[:, QD] / baseMVA
    Aeq_KVL = hstack([
        -2 * diags(Branch_R), -2 * diags(Branch_X),
        diags(turn_to_power(Branch_R, 2)) + diags(turn_to_power(Branch_X, 2)),
        Cf.T - Ct.T,
        zeros((nl, 2 * ng))
    ])
    beq_KVL = zeros(nl)

    Aeq = vstack([Aeq_p, Aeq_q, Aeq_KVL])
    Aeq = Aeq.todense()
    beq = concatenate([beq_p, beq_q, beq_KVL])
    neq = len(beq)

    for i in range(neq):
        expr = 0
        for j in range(nx):
            expr += x[j] * Aeq[i, j]
        model.addConstr(lhs=expr, sense=GRB.EQUAL, rhs=beq[i])

    for i in range(nl):
        model.addConstr(x[i] * x[i] + x[i + nl] * x[i + nl] <=
                        x[i + 2 * nl] * x[f[i] + 3 * nl],
                        name='"rc{0}"'.format(i))

    obj = 0
    for i in range(ng):
        obj += gencost[i, 4] * x[i + 3 * nl + nb] * x[
            i + 3 * nl + nb] * baseMVA * baseMVA + gencost[i, 5] * x[
                i + 3 * nl + nb] * baseMVA + gencost[i, 6]

    model.setObjective(obj)
    model.Params.OutputFlag = 0
    model.Params.LogToConsole = 0
    model.Params.DisplayInterval = 1
    model.optimize()

    xx = []
    for v in model.getVars():
        xx.append(v.x)

    obj = obj.getValue()

    Pij = xx[0:nl]
    Qij = xx[nl + 0:2 * nl]
    Iij = xx[2 * nl:3 * nl]
    Vi = xx[3 * nl:3 * nl + nb]
    Pg = xx[3 * nl + nb:3 * nl + nb + ng]
    Qg = xx[3 * nl + nb + ng:3 * nl + nb + 2 * ng]

    # for i in range(nl):  # branch indexing exchange
    #     if branch[i, F_BUS] > branch[i, T_BUS]:
    #         temp = branch[i, F_BUS]
    #         branch[i, F_BUS] = branch[i, T_BUS]
    #         branch[i, T_BUS] = temp

    f = branch[:, F_BUS]  ## list of "from" buses
    t = branch[:, T_BUS]  ## list of "to" buses
    # i = range(nl)  ## double set of row indices
    area = ancestor_children_generation(f, t, range(nb))
    Pi = Cg * Pg - bus[:, PD] / baseMVA
    Qi = Cg * Qg - bus[:, QD] / baseMVA

    for i in range(nb):
        # If the bus is the root bus, only the children information is required.
        if len(area[i]["Ai"]) == 0:
            print(i)
            expr = 0
            for j in range(len(area[i]["Cbranch"][0])):
                expr += Pij[area[i]["Cbranch"][0][j]]

            print(expr - Pi[i])

            expr = 0
            for j in range(len(area[i]["Cbranch"][0])):
                expr += Qij[area[i]["Cbranch"][0][j]]

            print(expr - Qi[i])

        elif len(area[i]["Cbranch"]) == 0:  # This bus is the lead node
            print(i)
            print(Pij[area[i]["Abranch"][0][0]] -
                  Iij[area[i]["Abranch"][0][0]] *
                  Branch_R[area[i]["Abranch"][0][0]] + Pi[i])

            print(Qij[area[i]["Abranch"][0][0]] -
                  Iij[area[i]["Abranch"][0][0]] *
                  Branch_X[area[i]["Abranch"][0][0]] + Qi[i])

            print(Vi[int(area[i]["Ai"][0])] - Vi[i] -
                  2 * Branch_R[area[i]["Abranch"][0][0]] *
                  Pij[area[i]["Abranch"][0][0]] -
                  2 * Branch_X[area[i]["Abranch"][0][0]] *
                  Qij[area[i]["Abranch"][0][0]] +
                  Iij[area[i]["Abranch"][0][0]] *
                  (Branch_R[area[i]["Abranch"][0][0]]**2 +
                   Branch_X[area[i]["Abranch"][0][0]]**2))

            print(
                Pij[area[i]["Abranch"][0][0]] * Pij[area[i]["Abranch"][0][0]] +
                Qij[area[i]["Abranch"][0][0]] * Qij[area[i]["Abranch"][0][0]]
                <= Vi[int(area[i]["Ai"][0])] * Iij[area[i]["Abranch"][0][0]])

        else:
            print(i)
            expr = 0
            for j in range(len(area[i]["Cbranch"][0])):
                expr += Pij[area[i]["Cbranch"][0][j]]
            print(Pij[area[i]["Abranch"][0][0]] -
                  Iij[area[i]["Abranch"][0][0]] *
                  Branch_R[area[i]["Abranch"][0][0]] + Pi[i] - expr)

            expr = 0
            for j in range(len(area[i]["Cbranch"][0])):
                expr += Qij[area[i]["Cbranch"][0][j]]

            print(Qij[area[i]["Abranch"][0][0]] -
                  Iij[area[i]["Abranch"][0][0]] *
                  Branch_X[area[i]["Abranch"][0][0]] + Qi[i] - expr)

            print(Vi[int(area[i]["Ai"][0])] - Vi[i] -
                  2 * Branch_R[area[i]["Abranch"][0][0]] *
                  Pij[area[i]["Abranch"][0][0]] -
                  2 * Branch_X[area[i]["Abranch"][0][0]] *
                  Qij[area[i]["Abranch"][0][0]] +
                  Iij[area[i]["Abranch"][0][0]] *
                  (Branch_R[area[i]["Abranch"][0][0]]**2 +
                   Branch_X[area[i]["Abranch"][0][0]]**2))
            print(
                Pij[area[i]["Abranch"][0][0]] * Pij[area[i]["Abranch"][0][0]] +
                Qij[area[i]["Abranch"][0][0]] * Qij[area[i]["Abranch"][0][0]]
                <= Vi[int(area[i]["Ai"][0])] * Iij[area[i]["Abranch"][0][0]])
    obj = 0
    for i in range(ng):
        print(Pg[i] - Pi[int(gen[i, GEN_BUS])] -
              bus[int(gen[i, GEN_BUS]), PD] / baseMVA)
        print(Qg[i] - Qi[int(gen[i, GEN_BUS])] -
              bus[int(gen[i, GEN_BUS]), QD] / baseMVA)
        print(int(gen[i, GEN_BUS]))
        obj += gencost[i, 4] * Pg[i] * Pg[i] * baseMVA * baseMVA + gencost[
            i, 5] * Pg[i] * baseMVA + gencost[i, 6]

    # Connection matrix
    Cg = sparse((ones(ng), (gen[:, GEN_BUS], range(ng))), (nb, ng))
    Branch_R = branch[:, BR_R]
    Branch_X = branch[:, BR_X]

    # Obtain the boundary information

    Slmax = branch[:, RATE_A] / baseMVA

    Pij_l = -Slmax
    Qij_l = -Slmax
    Iij_l = zeros(nl)
    Vm_l = turn_to_power(bus[:, VMIN], 2)
    Pg_l = gen[:, PMIN] / baseMVA
    Qg_l = gen[:, QMIN] / baseMVA
    Pi_l = -bus[:, PD] / baseMVA + Cg * Pg_l / baseMVA
    Qi_l = -bus[:, QD] / baseMVA + Cg * Qg_l / baseMVA

    Pij_u = Slmax
    Qij_u = Slmax
    Iij_u = Slmax
    Vm_u = turn_to_power(bus[:, VMAX], 2)
    Pg_u = 2 * gen[:, PMAX] / baseMVA
    Qg_u = 2 * gen[:, QMAX] / baseMVA
    Pi_u = -bus[:, PD] / baseMVA + Cg * Pg_u  # Boundary error
    Qi_u = -bus[:, QD] / baseMVA + Cg * Qg_u  # Boundary error

    model = Model("OPF")
    # Define the decision variables, compact set
    Pij = {}
    Qij = {}
    Iij = {}
    Vi = {}
    Pg = {}
    Qg = {}
    Pi = {}
    Qi = {}

    for i in range(nl):
        Pij[i] = model.addVar(lb=Pij_l[i],
                              ub=Pij_u[i],
                              vtype=GRB.CONTINUOUS,
                              name="Pij{0}".format(i))
        Qij[i] = model.addVar(lb=Qij_l[i],
                              ub=Qij_u[i],
                              vtype=GRB.CONTINUOUS,
                              name="Qij{0}".format(i))
        Iij[i] = model.addVar(lb=Iij_l[i],
                              ub=Iij_u[i],
                              vtype=GRB.CONTINUOUS,
                              name="Iij{0}".format(i))

    for i in range(nb):
        Vi[i] = model.addVar(lb=Vm_l[i],
                             ub=Vm_u[i],
                             vtype=GRB.CONTINUOUS,
                             name="V{0}".format(i))

    for i in range(ng):
        Pg[i] = model.addVar(lb=Pg_l[i],
                             ub=Pg_u[i],
                             vtype=GRB.CONTINUOUS,
                             name="Pg{0}".format(i))
        Qg[i] = model.addVar(lb=Qg_l[i],
                             ub=Qg_u[i],
                             vtype=GRB.CONTINUOUS,
                             name="Qg{0}".format(i))
    for i in range(nb):
        Pi[i] = model.addVar(lb=Pi_l[i],
                             ub=Pi_u[i],
                             vtype=GRB.CONTINUOUS,
                             name="Pi{0}".format(i))
        Qi[i] = model.addVar(lb=Qi_l[i],
                             ub=Qi_u[i],
                             vtype=GRB.CONTINUOUS,
                             name="Qi{0}".format(i))
    # For each area, before decomposition
    # Add system level constraints
    for i in range(nb):
        # If the bus is the root bus, only the children information is required.
        if len(area[i]["Ai"]) == 0:
            expr = 0
            for j in range(len(area[i]["Cbranch"][0])):
                expr += Pij[area[i]["Cbranch"][0][j]]

            model.addConstr(lhs=expr - Pi[i], sense=GRB.EQUAL, rhs=0)

            expr = 0
            for j in range(len(area[i]["Cbranch"][0])):
                expr += Qij[area[i]["Cbranch"][0][j]]

            model.addConstr(lhs=expr - Qi[i], sense=GRB.EQUAL, rhs=0)

        elif len(area[i]["Cbranch"]) == 0:  # This bus is the lead node
            model.addConstr(lhs=Pij[area[i]["Abranch"][0][0]] -
                            Iij[area[i]["Abranch"][0][0]] *
                            Branch_R[area[i]["Abranch"][0][0]] + Pi[i],
                            sense=GRB.EQUAL,
                            rhs=0)
            model.addConstr(lhs=Qij[area[i]["Abranch"][0][0]] -
                            Iij[area[i]["Abranch"][0][0]] *
                            Branch_X[area[i]["Abranch"][0][0]] + Qi[i],
                            sense=GRB.EQUAL,
                            rhs=0)

            model.addConstr(lhs=Vi[int(area[i]["Ai"][0])] - Vi[i] -
                            2 * Branch_R[area[i]["Abranch"][0][0]] *
                            Pij[area[i]["Abranch"][0][0]] -
                            2 * Branch_X[area[i]["Abranch"][0][0]] *
                            Qij[area[i]["Abranch"][0][0]] +
                            Iij[area[i]["Abranch"][0][0]] *
                            (Branch_R[area[i]["Abranch"][0][0]]**2 +
                             Branch_X[area[i]["Abranch"][0][0]]**2),
                            sense=GRB.EQUAL,
                            rhs=0)

            model.addConstr(
                Pij[area[i]["Abranch"][0][0]] * Pij[area[i]["Abranch"][0][0]] +
                Qij[area[i]["Abranch"][0][0]] * Qij[area[i]["Abranch"][0][0]]
                <= Vi[area[i]["Ai"][0]] * Iij[area[i]["Abranch"][0][0]],
                name="rc{0}".format(i))
        else:
            expr = 0
            for j in range(len(area[i]["Cbranch"][0])):
                expr += Pij[area[i]["Cbranch"][0][j]]
            model.addConstr(lhs=Pij[area[i]["Abranch"][0][0]] -
                            Iij[area[i]["Abranch"][0][0]] *
                            Branch_R[area[i]["Abranch"][0][0]] + Pi[i] - expr,
                            sense=GRB.EQUAL,
                            rhs=0)
            expr = 0
            for j in range(len(area[i]["Cbranch"][0])):
                expr += Qij[area[i]["Cbranch"][0][j]]

            model.addConstr(lhs=Qij[area[i]["Abranch"][0][0]] -
                            Iij[area[i]["Abranch"][0][0]] *
                            Branch_X[area[i]["Abranch"][0][0]] + Qi[i] - expr,
                            sense=GRB.EQUAL,
                            rhs=0)

            model.addConstr(lhs=Vi[int(area[i]["Ai"][0])] - Vi[i] -
                            2 * Branch_R[area[i]["Abranch"][0][0]] *
                            Pij[area[i]["Abranch"][0][0]] -
                            2 * Branch_X[area[i]["Abranch"][0][0]] *
                            Qij[area[i]["Abranch"][0][0]] +
                            Iij[area[i]["Abranch"][0][0]] *
                            (Branch_R[area[i]["Abranch"][0][0]]**2 +
                             Branch_X[area[i]["Abranch"][0][0]]**2),
                            sense=GRB.EQUAL,
                            rhs=0)

            model.addConstr(
                Pij[area[i]["Abranch"][0][0]] * Pij[area[i]["Abranch"][0][0]] +
                Qij[area[i]["Abranch"][0][0]] * Qij[area[i]["Abranch"][0][0]]
                <= Vi[area[i]["Ai"][0]] * Iij[area[i]["Abranch"][0][0]],
                name="rc{0}".format(i))
    obj = 0
    for i in range(ng):
        model.addConstr(lhs=Pg[i] - Pi[int(gen[i, GEN_BUS])],
                        sense=GRB.EQUAL,
                        rhs=bus[int(gen[i, GEN_BUS]), PD] / baseMVA)
        model.addConstr(lhs=Qg[i] - Qi[int(gen[i, GEN_BUS])],
                        sense=GRB.EQUAL,
                        rhs=bus[int(gen[i, GEN_BUS]), QD] / baseMVA)
        obj += gencost[i, 4] * Pg[i] * Pg[i] * baseMVA * baseMVA + gencost[
            i, 5] * Pg[i] * baseMVA + gencost[i, 6]

    model.setObjective(obj)
    model.Params.OutputFlag = 0
    model.Params.LogToConsole = 0
    model.Params.DisplayInterval = 1
    model.optimize()

    Pij = []
    Qij = []
    Iij = []
    Vi = []
    Pg = []
    Qg = []
    Pi = []
    Qi = []

    for i in range(nl):
        Pij.append(model.getVarByName("Pij{0}".format(i)).X)
        Qij.append(model.getVarByName("Qij{0}".format(i)).X)
        Iij.append(model.getVarByName("Iij{0}".format(i)).X)

    for i in range(nb):
        Vi.append(model.getVarByName("V{0}".format(i)).X)
        Pi.append(model.getVarByName("Pi{0}".format(i)).X)
        Qi.append(model.getVarByName("Qi{0}".format(i)).X)

    for i in range(ng):
        Pg.append(model.getVarByName("Pg{0}".format(i)).X)
        Qg.append(model.getVarByName("Qg{0}".format(i)).X)

    obj = obj.getValue()

    primal_residual = []

    for i in range(nl):
        primal_residual.append(Pij[i] * Pij[i] + Qij[i] * Qij[i] -
                               Iij[i] * Vi[int(f[i])])

    return obj, primal_residual
Exemple #5
0
def Ybus(ppc):
    ppc = ext2int(ppc)
    baseMVA, bus, gen, branch = ppc["baseMVA"], ppc["bus"], ppc["gen"], ppc[
        "branch"]
    Ybus, Yf, Yt = makeYbus(baseMVA, bus, branch)
    return Ybus
Exemple #6
0
def run_sim(ppc, elements, dynopt=None, events=None, recorder=None):
    """
    Run a time-domain simulation
    
    Inputs:
        ppc         PYPOWER load flow case
        elements    Dictionary of dynamic model objects (machines, controllers, etc) with Object ID as key
        events      Events object
        recorder    Recorder object (empty)
    
    Outputs:
        recorder    Recorder object (with data)
    """

    #########
    # SETUP #
    #########

    # Get version information
    ver = pydyn_ver()
    print('PYPOWER-Dynamics ' + ver['Version'] + ', ' + ver['Date'])

    # Program options
    if dynopt:
        h = dynopt['h']
        t_sim = dynopt['t_sim']
        max_err = dynopt['max_err']
        max_iter = dynopt['max_iter']
        verbose = dynopt['verbose']
    else:
        # Default program options
        h = 0.01  # step length (s)
        t_sim = 5  # simulation time (s)
        max_err = 0.0001  # Maximum error in network iteration (voltage mismatches)
        max_iter = 25  # Maximum number of network iterations
        verbose = False

    if dynopt['sample_period']:
        sample_rate = max(int(dynopt['sample_period'] / h) - 1, 0)
    else:
        sample_rate = 0

    # Make lists of current injection sources (generators, external grids, etc) and controllers
    sources = []
    controllers = []
    for element in elements.values():
        if element.__module__ in [
                'pydyn.sym_order6a', 'pydyn.sym_order6b', 'pydyn.sym_order4',
                'pydyn.ext_grid', 'pydyn.vsc_average', 'pydyn.asym_1cage',
                'pydyn.asym_2cage'
        ]:
            sources.append(element)

        if element.__module__ == 'pydyn.controller':
            controllers.append(element)

    # Set up interfaces
    interfaces = init_interfaces(elements)
    interfaces0 = init_interfaces0(elements)

    # find events
    events_controllers = []

    # find blocks that create events in controllers
    for element_id in elements.keys():
        element = elements[element_id]
        if element.__module__ == 'pydyn.controller':
            for line in element.equations:
                if line[1] == 'EVENT':
                    new_event = [element_id, line[0], line[2]] + line[3:]
                    events_controllers.append(new_event)

    ##################
    # INITIALISATION #
    ##################
    print('Initialising models...')

    if not verbose:
        ppopt = ppoption(VERBOSE=0, OUT_ALL=0)
    else:
        ppopt = ppoption()
        #print('not verbose')

    # Run power flow and update bus voltages and angles in PYPOWER case object
    results, success = runpf(ppc, ppopt)
    ppc["bus"][:, VM] = results["bus"][:, VM]
    ppc["bus"][:, VA] = results["bus"][:, VA]

    # Build Ybus matrix
    ppc_int = ext2int(ppc)
    baseMVA, bus, branch = ppc_int["baseMVA"], ppc_int["bus"], ppc_int[
        "branch"]
    Ybus, Yf, Yt = makeYbus(baseMVA, bus, branch)

    # Build modified Ybus matrix
    try:
        Ybus = mod_Ybus(Ybus, elements, bus, ppc_int['gen'], baseMVA)
    except:
        bp()
        Ybus = mod_Ybus(Ybus, elements, bus, ppc_int['gen'], baseMVA)

    # Calculate initial voltage phasors
    v0 = bus[:, VM] * (np.cos(np.radians(bus[:, VA])) +
                       1j * np.sin(np.radians(bus[:, VA])))

    # Initialise sources from load flow
    for source in sources:
        if source.__module__ in ['pydyn.asym_1cage', 'pydyn.asym_2cage']:
            # Asynchronous machine
            source_bus = ppc_int['bus'][source.bus_no, 0].astype(np.int64)
            v_source = v0[source_bus]
            source.initialise(v_source, 0)
        else:
            # Generator or VSC
            source_bus = ppc_int['gen'][source.gen_no, 0].astype(np.int64)
            S_source = np.complex(results["gen"][source.gen_no, 1] / baseMVA,
                                  results["gen"][source.gen_no, 2] / baseMVA)
            v_source = v0[source_bus]
            source.initialise(v_source, S_source)

    # initialise bus
    elements['bus'] = bus_int(ppc)
    elements['sys_matrices'] = sys_matrices_int(ppc)
    #elements['branch'] = ppc['branch']

    # Do we need interfaces0?
    # Interface controllers and machines (for initialisation)
    #for intf in interfaces:
    for k in range(len(interfaces)):
        intf = interfaces[k]
        intf0 = interfaces0[k]
        int_type = intf[0]
        var_name = intf0[1]
        source_var = intf[1]
        source_id = intf[2]
        dest_var = intf[3]
        dest_id = intf[4]
        if int_type == 'OUTPUT':
            # If an output, interface in the reverse direction for initialisation
            #intf[2].signals[var_name] = intf[3].signals[var_name]
            #if (intf0[2] != source_id) or (var_name != source_var) or (var_name != dest_var):
            #    bp()
            elements[source_id].signals[source_var] = elements[
                dest_id].signals[dest_var]
        else:
            # Inputs are interfaced in normal direction during initialisation
            #intf[3].signals[var_name] = intf[2].signals[var_name]
            #if (intf0[3] != dest_id)  or (var_name != source_var) or (var_name != dest_var):
            #    bp()
            elements[dest_id].signals[dest_var] = elements[source_id].signals[
                source_var]

        #try:
        #    element_source.signals[ var_name_source ] = element_dest.signals[ var_name_dest ]
        #except:
        #    bp()

    # Initialise controllers
    for controller in controllers:
        controller.initialise()

    #############
    # MAIN LOOP #
    #############

    sample_age = 0

    if events == None:
        print('Warning: no events!')

    # Factorise Ybus matrix
    Ybus_inv = splu(Ybus)

    y1 = []
    v_prev = v0
    print('Simulating...')
    for t in range(int(t_sim / h) + 1):
        if np.mod(t, 1 / h) == 0 and verbose:
            print('t=' + str(t * h) + 's')

        # Interface controllers and machines
        #for intf in interfaces:
        for k in range(len(interfaces)):
            intf = interfaces[k]
            intf0 = interfaces0[k]

            var_name = intf0[1]
            source_var = intf[1]
            source_id = intf[2]
            dest_var = intf[3]
            dest_id = intf[4]
            #if var_name_dest not in element_dest.signals.keys():
            #bp()
            #element_dest.signals[ var_name_dest ] = element_source.signals[ var_name_source ]

            #if (intf0[2] != source_id) or (var_name != source_var) or (var_name != dest_var) or (intf0[3] != dest_id):
            #    bp()

            elements[dest_id].signals[dest_var] = elements[source_id].signals[
                source_var]
            #intf[3].signals[var_name] = intf[2].signals[var_name]

        # Solve differential equations
        for j in range(4):
            # Solve step of differential equations
            for element in elements.values():
                try:
                    element.solve_step(h, j)
                except:
                    bp()
                    element.solve_step(h, j)

            # Interface with network equations
            v_prev = solve_network(sources, v_prev, Ybus_inv, ppc_int,
                                   len(bus), max_err, max_iter)

        # check for events
        for event_c in events_controllers:
            new_event = None
            ctrl = event_c[0]
            ctrl_var = event_c[2]
            var_result = event_c[1]
            condition = elements[ctrl].signals[ctrl_var]
            if condition >= 1:
                #event_type = event_c[0]
                #node = event_c[1]
                new_event = [np.round(t * h, 5)] + event_c[3:]
                #print(new_event)
                try:
                    events.event_stack.append(new_event)
                    elements[ctrl].signals[var_result] = 1.0
                    #bp()
                except:
                    bp()
            else:
                elements[ctrl].signals[var_result] = 0.0

        if sample_age < sample_rate:
            sample_age += 1
        else:
            sample_age = 0
            if recorder != None:
                # Record signals or states
                recorder.record_variables(t * h, elements)

        if events != None:
            #if new_event != None:
            #    bp()
            # Check event stack
            ppc, refactorise = events.handle_events(np.round(t * h, 5),
                                                    elements, ppc, baseMVA)

            if refactorise == True:
                # Rebuild Ybus from new ppc_int
                ppc_int = ext2int(ppc)
                baseMVA, bus, branch = ppc_int["baseMVA"], ppc_int[
                    "bus"], ppc_int["branch"]
                Ybus, Yf, Yt = makeYbus(baseMVA, bus, branch)

                # Rebuild modified Ybus
                Ybus = mod_Ybus(Ybus, elements, bus, ppc_int['gen'], baseMVA)

                # Refactorise Ybus
                Ybus_inv = splu(Ybus)

                # Solve network equations
                v_prev = solve_network(sources, v_prev, Ybus_inv, ppc_int,
                                       len(bus), max_err, max_iter)

        # update the voltage in 'bus' matrix
        ppc['bus'][:, VM] = abs(v_prev)
        ppc['bus'][:, VA] = 2 * np.arctan(v_prev.imag /
                                          (abs(v_prev) + v_prev.real))

        # update the system matrices
        elements['bus'].update(ppc)
        elements['sys_matrices'].update(ppc)

        #bp()

    return recorder
def run(mpc):
    """
    Gurobi based optimal power flow modelling and solution
    :param mpc: The input case of optimal power flow
    :return: obtained solution
    """
    mpc = ext2int(mpc)
    baseMVA, bus, gen, branch, gencost = mpc["baseMVA"], mpc["bus"], mpc[
        "gen"], mpc["branch"], mpc["gencost"]

    nb = shape(mpc['bus'])[0]  # number of buses
    nl = shape(mpc['branch'])[0]  # number of branches
    ng = shape(mpc['gen'])[0]  # number of dispatchable injections
    f = branch[:, F_BUS]  ## list of "from" buses
    t = branch[:, T_BUS]  ## list of "to" buses

    # Modify the bus information
    Branch_R = branch[:, BR_R]
    Branch_X = branch[:, BR_X]
    Slmax = branch[:, RATE_A] / baseMVA
    gen[:, PMAX] = gen[:, PMAX] / baseMVA
    gen[:, PMIN] = gen[:, PMIN] / baseMVA
    gen[:, QMAX] = gen[:, QMAX] / baseMVA
    gen[:, QMIN] = gen[:, QMIN] / baseMVA
    gencost[:, 4] = gencost[:, 4] * baseMVA * baseMVA
    gencost[:, 5] = gencost[:, 5] * baseMVA
    bus[:, PD] = bus[:, PD] / baseMVA
    bus[:, QD] = bus[:, QD] / baseMVA

    area = ancestor_children_generation(f, t, nb, Branch_R, Branch_X, Slmax,
                                        gen, bus, gencost, baseMVA)
    # Initialize algorithm for each sub area
    # 1) For each area, for self observation
    # x:=[Pg,Qg,pi,qi,Pi,Qi,Vi,Ii]
    # y:=[pi,qi,Pi,Qi,Vi,Ii,Pij,Qij,Vij,Iij]
    f = f.tolist()
    t = t.tolist()
    for i in range(nb):
        area[i]["PG"] = (area[i]["PGMAX"] + area[i]["PGMIN"]) / 2
        area[i]["QG"] = (area[i]["QGMIN"] + area[i]["QGMAX"]) / 2
        # Observation of x
        area[i]["pi"] = area[i]["PG"] - area[i]["PD"]
        area[i]["qi"] = area[i]["QG"] - area[i]["QD"]
        area[i]["Pi"] = area[i]["pi"]
        area[i]["Qi"] = area[i]["qi"]
        area[i]["Vi"] = (area[i]["VMIN"] + area[i]["VMAX"]) / 2
        if area[i]["TYPE"] != "ROOT":
            area[i]["Ii"] = (area[i]["Pi"]**2 +
                             area[i]["Qi"]**2) / area[i]["Vi"]
        # The self observation
        area[i]["pi_y"] = area[i]["pi"]
        area[i]["qi_y"] = area[i]["qi"]
        if area[i]["TYPE"] != "ROOT":
            area[i]["Vi_y"] = area[i]["Vi"]
            area[i]["Ii_y"] = area[i]["Ii"]
            area[i]["Pi_y"] = area[i]["Pi"]
            area[i]["Qi_y"] = area[i]["Qi"]
        # The multipliers
        area[i]["mu_pi"] = area[i]["pi"] - area[i]["pi_y"]
        area[i]["mu_qi"] = area[i]["qi"] - area[i]["qi_y"]
        if area[i]["TYPE"] != "ROOT":
            area[i]["mu_Vi"] = area[i]["Vi"] - area[i]["Vi_y"]
            area[i]["mu_Ii"] = area[i]["Ii"] - area[i]["Ii_y"]
            area[i]["mu_Pi"] = area[i]["Pi"] - area[i]["Pi_y"]
            area[i]["mu_Qi"] = area[i]["Qi"] - area[i]["Qi_y"]
        area[i]["COST"] = 0
        # Spread the information to the observatory

    observatory = []
    # Store the voltage of parent bus and children power flow information
    # 1)The ancestor bus voltage information is stored in the observotory
    # 2)The children bus power and current information is stored in the observotory
    for i in range(nl):
        temp = {}
        temp["Vij_x"] = area[int(f[i])]["Vi"]
        temp["Pij_x"] = area[int(t[i])]["Pi"]
        temp["Qij_x"] = area[int(t[i])]["Qi"]
        temp["Iij_x"] = area[int(t[i])]["Ii"]
        temp["Vij_y"] = area[int(f[i])]["Vi"]
        temp["Pij_y"] = area[int(t[i])]["Pi"]
        temp["Qij_y"] = area[int(t[i])]["Qi"]
        temp["Iij_y"] = area[int(t[i])]["Ii"]
        temp["mu_Vij"] = 0
        temp["mu_Pij"] = 0
        temp["mu_Qij"] = 0
        temp["mu_Iij"] = 0
        observatory.append(temp)
    # Begin the iteration,
    Gap = 1000
    Gap_index = []
    Dual_gap_index = []
    Obj_index = []
    k = 0
    kmax = 10000
    ru = 700
    # The iteration
    mu = 5
    t = 2
    while k <= kmax and Gap > 0.0001 * 2:
        observatory0 = deepcopy(observatory)
        area0 = deepcopy(area)
        for i in range(nb):
            (area, observatory) = sub_problem(area, observatory, i, ru / 2)

        # # multiplier update
        # for i in range(nb):
        #     area[i]["mu_pi"] += ru * (area[i]["pi"] - area[i]["pi_y"])
        #     area[i]["mu_qi"] += ru * (area[i]["qi"] - area[i]["qi_y"])
        #     if area[i]["TYPE"] != "ROOT":
        #         area[i]["mu_Vi"] += ru * (area[i]["Vi"] - area[i]["Vi_y"])
        #         area[i]["mu_Ii"] += ru * (area[i]["Ii"] - area[i]["Ii_y"])
        #         area[i]["mu_Pi"] += ru * (area[i]["Pi"] - area[i]["Pi_y"])
        #         area[i]["mu_Qi"] += ru * (area[i]["Qi"] - area[i]["Qi_y"])
        # for i in range(nl):
        #     observatory[i]["mu_Vij"] += ru * (observatory[i]["Vij_x"] - observatory[i]["Vij_y"])
        #     observatory[i]["mu_Pij"] += ru * (observatory[i]["Pij_x"] - observatory[i]["Pij_y"])
        #     observatory[i]["mu_Qij"] += ru * (observatory[i]["Qij_x"] - observatory[i]["Qij_y"])
        #     observatory[i]["mu_Iij"] += ru * (observatory[i]["Iij_x"] - observatory[i]["Iij_y"])

        # Calculate the gap
        gap = 0
        for i in range(nb):
            gap += (area[i]["pi"] - area[i]["pi_y"])**2
            gap += (area[i]["qi"] - area[i]["qi_y"])**2
            if area[i]["TYPE"] != "ROOT":
                gap += (area[i]["Vi"] - area[i]["Vi_y"])**2
                gap += (area[i]["Ii"] - area[i]["Ii_y"])**2
                gap += (area[i]["Pi"] - area[i]["Pi_y"])**2
                gap += (area[i]["Qi"] - area[i]["Qi_y"])**2
        for i in range(nl):
            gap += (observatory[i]["Vij_x"] - observatory[i]["Vij_y"])**2
            gap += (observatory[i]["Pij_x"] - observatory[i]["Pij_y"])**2
            gap += (observatory[i]["Qij_x"] - observatory[i]["Qij_y"])**2
            gap += (observatory[i]["Iij_x"] - observatory[i]["Iij_y"])**2

        dual_gap = 0
        for i in range(nb):
            dual_gap += (area0[i]["pi_y"] - area[i]["pi_y"])**2
            dual_gap += (area0[i]["qi_y"] - area[i]["qi_y"])**2
            if area[i]["TYPE"] != "ROOT":
                dual_gap += (area0[i]["Vi_y"] - area[i]["Vi_y"])**2
                dual_gap += (area0[i]["Ii_y"] - area[i]["Ii_y"])**2
                dual_gap += (area0[i]["Pi_y"] - area[i]["Pi_y"])**2
                dual_gap += (area0[i]["Qi_y"] - area[i]["Qi_y"])**2
        for i in range(nl):
            dual_gap += (observatory0[i]["Vij_y"] - observatory[i]["Vij_y"])**2
            dual_gap += (observatory0[i]["Pij_y"] - observatory[i]["Pij_y"])**2
            dual_gap += (observatory0[i]["Qij_y"] - observatory[i]["Qij_y"])**2
            dual_gap += (observatory0[i]["Iij_y"] - observatory[i]["Iij_y"])**2

        # if dual_gap * mu < gap:
        #     ru = ru * t
        # if gap * mu < dual_gap:
        #     ru = ru / t

        Gap = sqrt(gap)
        Gap_index.append(Gap)
        Dual_gap_index.append(sqrt(dual_gap))
        obj = 0
        for i in range(nb):
            obj += area[i]["COST"]
        k = k + 1
        print(k)
        print(Gap)
        print(obj)
        print(sqrt(dual_gap))
    # compute the objective function
    obj = 0
    for i in range(nb):
        obj += area[i]["COST"]
    Gap_index = array(Gap_index)
    Dual_gap_index = array(Dual_gap_index)
    plt.figure(1)
    plt.subplot(211)
    plt.plot(Gap_index)
    plt.ylabel('Primal residual')

    plt.subplot(212)
    plt.plot(Dual_gap_index)
    plt.ylabel('Dual residual')

    plt.show()
    return obj, area
def run(mpc):
    """
    Gurobi based optimal power flow modelling and solution
    :param mpc: The input case of optimal power flow
    :return: obtained solution
    """
    mpc = ext2int(mpc)
    baseMVA, bus, gen, branch, gencost = mpc["baseMVA"], mpc["bus"], mpc["gen"], mpc["branch"], mpc["gencost"]

    nb = shape(mpc['bus'])[0]  # number of buses
    nl = shape(mpc['branch'])[0]  # number of branches
    ng = shape(mpc['gen'])[0]  # number of dispatchable injections
    f = branch[:, F_BUS]  ## list of "from" buses
    t = branch[:, T_BUS]  ## list of "to" buses
    i = range(nl)  ## double set of row indices
    # Connection matrix
    Cf = sparse((ones(nl), (i, f)), (nl, nb))
    Ct = sparse((ones(nl), (i, t)), (nl, nb))
    Cg = sparse((ones(ng), (gen[:, GEN_BUS], range(ng))), (nb, ng))
    # Connection matrix
    Cg = sparse((ones(ng), (gen[:, GEN_BUS], range(ng))), (nb, ng))
    Branch_R = branch[:, BR_R]
    Branch_X = branch[:, BR_X]
    Slmax = branch[:, RATE_A] / baseMVA
    gen[:, PMAX] = gen[:, PMAX] / baseMVA
    gen[:, PMIN] = gen[:, PMIN] / baseMVA
    gen[:, QMAX] = gen[:, QMAX] / baseMVA
    gen[:, QMIN] = gen[:, QMIN] / baseMVA
    gencost[:, 4] = gencost[:, 4] * baseMVA * baseMVA
    gencost[:, 5] = gencost[:, 5] * baseMVA
    bus[:, PD] = bus[:, PD] / baseMVA
    bus[:, QD] = bus[:, QD] / baseMVA
    area = ancestor_children_generation(f, t, nb, Branch_R, Branch_X, Slmax, gen, bus, gencost, baseMVA)
    M = inf
    # Formulate the centralized optimization problem according to the information provided by area
    model = Model("OPF")

    # Define the decision variables, compact set
    # X variables
    Pi_x = {}
    Qi_x = {}
    Ii_x = {}
    Vi_x = {}
    pi_x = {}
    qi_x = {}
    Pg = {}
    Qg = {}

    # Y variables
    # Part 1), self observation
    Pii_y = {}
    Qii_y = {}
    Iii_y = {}
    Vii_y = {}
    pii_y = {}
    qii_y = {}
    # Part 2), to the ancestor
    Pij_y = {}
    Qij_y = {}
    Iij_y = {}
    # Part 3), to the children. The definition is in accordance with the sequence of lines
    Vij_y = {}  # For the given branch

    obj = 0
    for i in range(nb):  # The iteration from each bus
        Pi_x[i] = model.addVar(lb=-area[i]["SMAX"], ub=area[i]["SMAX"], vtype=GRB.CONTINUOUS,
                               name="Pi_x{0}".format(i))
        Qi_x[i] = model.addVar(lb=-area[i]["SMAX"], ub=area[i]["SMAX"], vtype=GRB.CONTINUOUS,
                               name="Qi_x{0}".format(i))
        Ii_x[i] = model.addVar(lb=-area[i]["SMAX"], ub=area[i]["SMAX"], vtype=GRB.CONTINUOUS,
                               name="Ii_x{0}".format(i))
        Vi_x[i] = model.addVar(lb=area[i]["VMIN"], ub=area[i]["VMAX"], vtype=GRB.CONTINUOUS,
                               name="Vi_x{0}".format(i))

        pi_x[i] = model.addVar(lb=-M, ub=M, vtype=GRB.CONTINUOUS, name="pi_x{0}".format(i))
        qi_x[i] = model.addVar(lb=-M, ub=M, vtype=GRB.CONTINUOUS, name="qi_x{0}".format(i))

        Pg[i] = model.addVar(lb=area[i]["PGMIN"], ub=area[i]["PGMAX"], vtype=GRB.CONTINUOUS, name="Pgi{0}".format(i))
        Qg[i] = model.addVar(lb=area[i]["QGMIN"], ub=area[i]["QGMAX"], vtype=GRB.CONTINUOUS, name="Qgi{0}".format(i))

        Pii_y[i] = model.addVar(lb=-M, ub=M, vtype=GRB.CONTINUOUS, name="Pii_y{0}".format(i))
        Qii_y[i] = model.addVar(lb=-M, ub=M, vtype=GRB.CONTINUOUS, name="Qii_y{0}".format(i))
        Iii_y[i] = model.addVar(lb=-M, ub=M, vtype=GRB.CONTINUOUS, name="Iii_y{0}".format(i))
        Vii_y[i] = model.addVar(lb=-M, ub=M, vtype=GRB.CONTINUOUS, name="Vii_y{0}".format(i))
        pii_y[i] = model.addVar(lb=-M, ub=M, vtype=GRB.CONTINUOUS, name="pii_y{0}".format(i))
        qii_y[i] = model.addVar(lb=-M, ub=M, vtype=GRB.CONTINUOUS, name="qii_y{0}".format(i))
        # For each branch, the following observation variables should be introduced
        # According to the sequence of lines

    for i in range(nl):
        Pij_y[i] = model.addVar(lb=-M, ub=M, vtype=GRB.CONTINUOUS, name="Pij_y{0}".format(i))
        Qij_y[i] = model.addVar(lb=-M, ub=M, vtype=GRB.CONTINUOUS, name="Qij_y{0}".format(i))
        Iij_y[i] = model.addVar(lb=-M, ub=M, vtype=GRB.CONTINUOUS, name="Iij_y{0}".format(i))
        Vij_y[i] = model.addVar(lb=-M, ub=M, vtype=GRB.CONTINUOUS, name="Vij_y{0}".format(i))

    for i in range(nb):
        # Add constrain for each bus
        model.addConstr(Pg[i] - pi_x[i] == area[i]["PD"])
        model.addConstr(Qg[i] - qi_x[i] == area[i]["QD"])
        model.addConstr(Pi_x[i] * Pi_x[i] + Qi_x[i] * Qi_x[i] <= Ii_x[i] * Vi_x[i])

        # Update the objective function
        obj += area[i]["a"] * Pg[i] * Pg[i] + area[i]["b"] * Pg[i] + area[i]["c"]
        # Add constrain for the observation of each bus
        # 1)Constrain for KCL equations
        # 2)Constrain for KVL equations
        if area[i]["TYPE"] == "ROOT":
            # Only KCL equation is required
            expr = 0
            for j in range(len(area[i]["Ci"])):
                expr += Pij_y[area[i]["Cbranch"][j]] - Iij_y[area[i]["Cbranch"][j]] * Branch_R[area[i]["Cbranch"][j]]
            model.addConstr(pii_y[i] + expr == 0)

            expr = 0
            for j in range(len(area[i]["Ci"])):
                expr += Qij_y[area[i]["Cbranch"][j]] - Iij_y[area[i]["Cbranch"][j]] * Branch_X[area[i]["Cbranch"][j]]
            model.addConstr(qii_y[i] + expr == 0)

        elif area[i]["TYPE"] == "LEAF":  # Only KCL equation is required
            model.addConstr(pii_y[i] - Pii_y[i] == 0)
            model.addConstr(qii_y[i] - Qii_y[i] == 0)
            model.addConstr(
                Vij_y[area[i]["Abranch"]] - Vii_y[i] + 2 * area[i]["BR_R"] * Pii_y[i] + 2 * area[i]["BR_X"] * Qii_y[i] -
                Iii_y[i] * (area[i]["BR_R"] ** 2 + area[i]["BR_X"] ** 2) == 0)
        else:
            expr = 0
            for j in range(len(area[i]["Ci"])):
                expr += Pij_y[area[i]["Cbranch"][j]] - Iij_y[area[i]["Cbranch"][j]] * Branch_R[area[i]["Cbranch"][j]]
            model.addConstr(pii_y[i] - Pii_y[i] + expr == 0)

            expr = 0
            for j in range(len(area[i]["Ci"])):
                expr += Qij_y[area[i]["Cbranch"][j]] - Iij_y[area[i]["Cbranch"][j]] * Branch_X[area[i]["Cbranch"][j]]
            model.addConstr(qii_y[i] - Qii_y[i] + expr == 0)
            model.addConstr(
                Vij_y[area[i]["Abranch"]] - Vii_y[i] + 2 * area[i]["BR_R"] * Pii_y[i] + 2 * area[i]["BR_X"] * Qii_y[i] -
                Iii_y[i] * (area[i]["BR_R"] ** 2 + area[i]["BR_X"] ** 2) == 0)

        # Formulate consensus constraints
        # Add constraints
        # The introduction of Xii is to formulate the closed form of the solution
        model.addConstr(Pii_y[i] == Pi_x[i])
        model.addConstr(Qii_y[i] == Qi_x[i])
        model.addConstr(Vii_y[i] == Vi_x[i])
        model.addConstr(Iii_y[i] == Ii_x[i])
        model.addConstr(pii_y[i] == pi_x[i])
        model.addConstr(qii_y[i] == qi_x[i])
        # For each branch
    for i in range(nl): # which stands for the observatory for each line; The observatory constraints
        model.addConstr(Vij_y[i] == Vi_x[f[i]])
        model.addConstr(Pij_y[i] == Pi_x[t[i]])
        model.addConstr(Qij_y[i] == Qi_x[t[i]])
        model.addConstr(Iij_y[i] == Ii_x[t[i]])
    # from the perspective of nodes
    # for i in range(nb):
    #     if area[i]["nChildren"] != 0:
    #         for j in range(area[i]["nChildren"]):
    #             model.addConstr(Vi_x[i] == Vij_y[area[i]["Cbranch"][j]])
    #     if area[i]["TYPE"] != "ROOT":
    #         model.addConstr(Pi_x[i] == Pij_y[area[i]["Abranch"]])
    #         model.addConstr(Qi_x[i] == Qij_y[area[i]["Abranch"]])
    #         model.addConstr(Ii_x[i] == Iij_y[area[i]["Abranch"]])
    model.setObjective(obj)
    model.Params.OutputFlag = 1
    model.Params.LogToConsole = 1
    model.Params.DisplayInterval = 1
    model.optimize()

    Pi = []
    Qi = []
    Ii = []
    Vi = []
    pi = []
    qi = []
    pg = []
    qg = []

    for i in range(nb):
        Pi.append(model.getVarByName("Pi_x{0}".format(i)).X)
        Qi.append(model.getVarByName("Pi_x{0}".format(i)).X)
        Ii.append(model.getVarByName("Ii_x{0}".format(i)).X)
        Vi.append(model.getVarByName("Vi_x{0}".format(i)).X)
        pi.append(model.getVarByName("pi_x{0}".format(i)).X)
        qi.append(model.getVarByName("qi_x{0}".format(i)).X)
        pg.append(model.getVarByName("Pgi{0}".format(i)).X)
        qg.append(model.getVarByName("Qgi{0}".format(i)).X)

    obj = obj.getValue()

    primal_residual = []

    for i in range(nb):
        primal_residual.append(Pi[i] * Pi[i] + Qi[i] * Qi[i] - Ii[i] * Vi[i])

    return obj, primal_residual
Exemple #9
0
def run(mpc):
    """
    Gurobi based optimal power flow modelling and solution
    :param mpc: The input case of optimal power flow
    :return: obtained solution
    """
    # Data format
    from pypower.idx_brch import F_BUS, T_BUS, BR_R, BR_X, TAP, SHIFT, BR_STATUS, RATE_A
    from pypower.idx_cost import MODEL, NCOST, PW_LINEAR, COST, POLYNOMIAL
    from pypower.idx_bus import BUS_TYPE, REF, VA, VM, PD, GS, VMAX, VMIN, BUS_I, QD
    from pypower.idx_gen import GEN_BUS, VG, PG, QG, PMAX, PMIN, QMAX, QMIN
    from pypower.ext2int import ext2int

    mpc = ext2int(mpc)
    baseMVA, bus, gen, branch, gencost = mpc["baseMVA"], mpc["bus"], mpc[
        "gen"], mpc["branch"], mpc["gencost"]

    nb = shape(mpc['bus'])[0]  ## number of buses
    nl = shape(mpc['branch'])[0]  ## number of branches
    ng = shape(mpc['gen'])[0]  ## number of dispatchable injections

    f = branch[:, F_BUS]  ## list of "from" buses
    t = branch[:, T_BUS]  ## list of "to" buses
    i = range(nl)  ## double set of row indices
    # Connection matrix
    Cf = sparse((ones(nl), (i, f)), (nl, nb))
    Ct = sparse((ones(nl), (i, t)), (nl, nb))
    Cg = sparse((ones(ng), (gen[:, GEN_BUS], range(ng))), (nb, ng))
    Branch_R = branch[:, BR_R]
    Branch_X = branch[:, BR_X]
    Cf = Cf.T
    Ct = Ct.T
    # Obtain the boundary information

    Slmax = branch[:, RATE_A] / baseMVA
    Pij_l = -Slmax
    Qij_l = -Slmax
    Iij_l = zeros(nl)
    Vm_l = turn_to_power(bus[:, VMIN], 2)
    Pg_l = gen[:, PMIN] / baseMVA
    Qg_l = gen[:, QMIN] / baseMVA

    Pij_u = Slmax
    Qij_u = Slmax
    Iij_u = Slmax
    Vm_u = turn_to_power(bus[:, VMAX], 2)
    Pg_u = 2 * gen[:, PMAX] / baseMVA
    Qg_u = 2 * gen[:, QMAX] / baseMVA
    lx = concatenate([Pij_l, Qij_l, Iij_l, Vm_l, Pg_l, Qg_l])
    ux = concatenate([Pij_u, Qij_u, Iij_u, Vm_u, Pg_u, Qg_u])
    model = Model("OPF")
    # Define the decision variables
    x = {}
    nx = 3 * nl + nb + 2 * ng
    for i in range(nx):
        x[i] = model.addVar(lb=lx[i], ub=ux[i], vtype=GRB.CONTINUOUS)

    # Add system level constraints
    Aeq_p = hstack([
        Ct - Cf,
        zeros((nb, nl)), -diag(Ct * Branch_R) * Ct,
        zeros((nb, nb)), Cg,
        zeros((nb, ng))
    ])
    beq_p = bus[:, PD] / baseMVA
    # Add constraints for each sub system
    Aeq_q = hstack([
        zeros((nb, nl)), Ct - Cf, -diag(Ct * Branch_X) * Ct,
        zeros((nb, nb)),
        zeros((nb, ng)), Cg
    ])
    beq_q = bus[:, QD] / baseMVA
    Aeq_KVL = hstack([
        -2 * diags(Branch_R), -2 * diags(Branch_X),
        diags(turn_to_power(Branch_R, 2)) + diags(turn_to_power(Branch_X, 2)),
        Cf.T - Ct.T,
        zeros((nl, 2 * ng))
    ])
    beq_KVL = zeros(nl)

    Aeq = vstack([Aeq_p, Aeq_q, Aeq_KVL])
    Aeq = Aeq.todense()
    beq = concatenate([beq_p, beq_q, beq_KVL])
    neq = len(beq)

    for i in range(neq):
        expr = 0
        for j in range(nx):
            expr += x[j] * Aeq[i, j]
        model.addConstr(lhs=expr, sense=GRB.EQUAL, rhs=beq[i])

    for i in range(nl):
        model.addConstr(x[i] * x[i] + x[i + nl] * x[i + nl] <=
                        x[i + 2 * nl] * x[f[i] + 3 * nl],
                        name='"rc{0}"'.format(i))

    obj = 0
    for i in range(ng):
        obj += gencost[i, 4] * x[i + 3 * nl + nb] * x[
            i + 3 * nl + nb] * baseMVA * baseMVA + gencost[i, 5] * x[
                i + 3 * nl + nb] * baseMVA + gencost[i, 6]

    model.setObjective(obj)
    model.Params.OutputFlag = 0
    model.Params.LogToConsole = 0
    model.Params.DisplayInterval = 1
    model.optimize()

    xx = []
    for v in model.getVars():
        xx.append(v.x)

    obj = obj.getValue()

    Pij = xx[0:nl]
    Qij = xx[nl + 0:2 * nl]
    Iij = xx[2 * nl:3 * nl]
    Vi = xx[3 * nl:3 * nl + nb]
    Pg = xx[3 * nl + nb:3 * nl + nb + ng]
    Qg = xx[3 * nl + nb + ng:3 * nl + nb + 2 * ng]

    primal_residual = []

    for i in range(nl):
        primal_residual.append(Pij[i] * Pij[i] + Qij[i] * Qij[i] -
                               Iij[i] * Vi[int(f[i])])

    return xx, obj, primal_residual
Exemple #10
0
def run(mpc):
    """
    Gurobi based optimal power flow modelling and solution
    :param mpc: The input case of optimal power flow
    :return: obtained solution
    """
    mpc = ext2int(mpc)
    baseMVA, bus, gen, branch, gencost = mpc["baseMVA"], mpc["bus"], mpc[
        "gen"], mpc["branch"], mpc["gencost"]

    nb = shape(mpc['bus'])[0]  # number of buses
    nl = shape(mpc['branch'])[0]  # number of branches
    ng = shape(mpc['gen'])[0]  # number of dispatchable injections
    f = branch[:, F_BUS]  ## list of "from" buses
    t = branch[:, T_BUS]  ## list of "to" buses
    i = range(nl)  ## double set of row indices
    # Connection matrix
    Cf = sparse((ones(nl), (i, f)), (nl, nb))
    Ct = sparse((ones(nl), (i, t)), (nl, nb))
    Cg = sparse((ones(ng), (gen[:, GEN_BUS], range(ng))), (nb, ng))
    # Connection matrix
    Cg = sparse((ones(ng), (gen[:, GEN_BUS], range(ng))), (nb, ng))
    Branch_R = branch[:, BR_R]
    Branch_X = branch[:, BR_X]
    Slmax = branch[:, RATE_A] / baseMVA
    gen[:, PMAX] = gen[:, PMAX] / baseMVA
    gen[:, PMIN] = gen[:, PMIN] / baseMVA
    gen[:, QMAX] = gen[:, QMAX] / baseMVA
    gen[:, QMIN] = gen[:, QMIN] / baseMVA
    gencost[:, 4] = gencost[:, 4] * baseMVA * baseMVA
    gencost[:, 5] = gencost[:, 5] * baseMVA
    bus[:, PD] = bus[:, PD] / baseMVA
    bus[:, QD] = bus[:, QD] / baseMVA
    area = ancestor_children_generation(f, t, nb, Branch_R, Branch_X, Slmax,
                                        gen, bus, gencost, baseMVA)
    # Formulate the centralized optimization problem according to the information provided by area
    # Generate the initial value
    # 1) For the x-update
    Pi_x0 = [0] * nb
    Qi_x0 = [0] * nb
    Ii_x0 = [1] * nb
    Vi_x0 = [0] * nb
    pi_x0 = [0] * nb
    qi_x0 = [0] * nb
    Pg0 = [0] * nb
    Qg0 = [0] * nb
    # 2) For the y-update
    Pi_y0 = [0] * nb
    Qi_y0 = [0] * nb
    Ii_y0 = [0] * nb
    Vi_y0 = [1] * nb
    pi_y0 = [0] * nb
    qi_y0 = [0] * nb
    Pij_y0 = [0] * nl
    Qij_y0 = [0] * nl
    Iij_y0 = [0] * nl
    Vij_y0 = [0] * nl
    # 3) The multiplier part
    mu_Pi = [0] * nb
    mu_Qi = [0] * nb
    mu_Ii = [0] * nb
    mu_Vi = [0] * nb
    mu_pi = [0] * nb
    mu_qi = [0] * nb
    mu_Pij = [0] * nl
    mu_Qij = [0] * nl
    mu_Iij = [0] * nl
    mu_Vij = [0] * nl
    f = f.tolist()
    t = t.tolist()
    for i in range(nl):
        f[i] = int(f[i])
        t[i] = int(t[i])
    for i in range(nl):
        Pij_y0[i] = Pi_x0[t[i]]
        Qij_y0[i] = Qi_x0[t[i]]
        Iij_y0[i] = Ii_x0[t[i]]
        Vij_y0[i] = Vi_x0[f[i]]
    Gap = 1000
    Gap_index = []
    k = 0
    kmax = 10000
    ru = 1000
    half_ru = ru / 2
    # The iteration
    while k <= kmax and Gap > 0.001:
        # Y variables
        # Part 1), self observation
        modelY = Model("Yupdate")
        Pi_y = {}
        Qi_y = {}
        Ii_y = {}
        Vi_y = {}
        pi_y = {}
        qi_y = {}
        # Part 2), to the ancestor
        Pij_y = {}
        Qij_y = {}
        Iij_y = {}
        # Part 3), to the children. The definition is in accordance with the sequence of lines
        Vij_y = {}  # For the given branch
        for i in range(nb):  # The iteration from each bus
            Pi_y[i] = modelY.addVar(lb=-inf,
                                    ub=inf,
                                    vtype=GRB.CONTINUOUS,
                                    name="Pi_y{0}".format(i))
            Qi_y[i] = modelY.addVar(lb=-inf,
                                    ub=inf,
                                    vtype=GRB.CONTINUOUS,
                                    name="Qi_y{0}".format(i))
            Ii_y[i] = modelY.addVar(lb=-inf,
                                    ub=inf,
                                    vtype=GRB.CONTINUOUS,
                                    name="Ii_y{0}".format(i))
            Vi_y[i] = modelY.addVar(lb=-inf,
                                    ub=inf,
                                    vtype=GRB.CONTINUOUS,
                                    name="Vi_y{0}".format(i))
            pi_y[i] = modelY.addVar(lb=-inf,
                                    ub=inf,
                                    vtype=GRB.CONTINUOUS,
                                    name="pi_y{0}".format(i))
            qi_y[i] = modelY.addVar(lb=-inf,
                                    ub=inf,
                                    vtype=GRB.CONTINUOUS,
                                    name="qi_y{0}".format(i))

        for i in range(nl):  # The information stored in the observatory
            Pij_y[i] = modelY.addVar(lb=-inf,
                                     ub=inf,
                                     vtype=GRB.CONTINUOUS,
                                     name="Pij_y{0}".format(i))
            Qij_y[i] = modelY.addVar(lb=-inf,
                                     ub=inf,
                                     vtype=GRB.CONTINUOUS,
                                     name="Qij_y{0}".format(i))
            Iij_y[i] = modelY.addVar(lb=-inf,
                                     ub=inf,
                                     vtype=GRB.CONTINUOUS,
                                     name="Iij_y{0}".format(i))
            Vij_y[i] = modelY.addVar(lb=-inf,
                                     ub=inf,
                                     vtype=GRB.CONTINUOUS,
                                     name="Vij_y{0}".format(i))

        for i in range(nb):
            # Add constrain for the observation of each bus
            # 1)Constrain for KCL equations
            # 2)Constrain for KVL equations
            if area[i]["TYPE"] == "ROOT":
                # Only KCL equation is required
                expr = 0
                for j in range(area[i]["nCi"]):
                    expr += Pij_y[area[i]["Cbranch"][j]] - Iij_y[
                        area[i]["Cbranch"][j]] * area[i]["BR_R_C"][j]
                modelY.addConstr(pi_y[i] + expr == 0)

                expr = 0
                for j in range(area[i]["nCi"]):
                    expr += Qij_y[area[i]["Cbranch"][j]] - Iij_y[
                        area[i]["Cbranch"][j]] * area[i]["BR_X_C"][j]
                modelY.addConstr(qi_y[i] + expr == 0)

            elif area[i]["TYPE"] == "LEAF":  # Only KCL equation is required
                modelY.addConstr(pi_y[i] - Pi_y[i] == 0)
                modelY.addConstr(qi_y[i] - Qi_y[i] == 0)
                modelY.addConstr(
                    Vij_y[area[i]["Abranch"]] - Vi_y[i] +
                    2 * area[i]["BR_R"] * Pi_y[i] +
                    2 * area[i]["BR_X"] * Qi_y[i] - Ii_y[i] *
                    (area[i]["BR_R"]**2 + area[i]["BR_X"]**2) == 0)
            else:
                expr = 0
                for j in range(area[i]["nCi"]):
                    expr += Pij_y[area[i]["Cbranch"][j]] - Iij_y[
                        area[i]["Cbranch"][j]] * area[i]["BR_R_C"][j]
                modelY.addConstr(pi_y[i] - Pi_y[i] + expr == 0)

                expr = 0
                for j in range(area[i]["nCi"]):
                    expr += Qij_y[area[i]["Cbranch"][j]] - Iij_y[
                        area[i]["Cbranch"][j]] * area[i]["BR_X_C"][j]
                modelY.addConstr(qi_y[i] - Qi_y[i] + expr == 0)
                modelY.addConstr(
                    Vij_y[area[i]["Abranch"]] - Vi_y[i] +
                    2 * area[i]["BR_R"] * Pi_y[i] +
                    2 * area[i]["BR_X"] * Qi_y[i] - Ii_y[i] *
                    (area[i]["BR_R"]**2 + area[i]["BR_X"]**2) == 0)
        objY = 0
        for i in range(nb):
            objY += mu_Pi[i] * Pi_y[i] + half_ru * (Pi_y[i] - Pi_x0[i]) * (Pi_y[i] - Pi_x0[i]) + \
                    mu_Qi[i] * Qi_y[i] + half_ru * (Qi_y[i] - Qi_x0[i]) * (Qi_y[i] - Qi_x0[i]) + \
                    mu_Ii[i] * Ii_y[i] + half_ru * (Ii_y[i] - Ii_x0[i]) * (Ii_y[i] - Ii_x0[i]) + \
                    mu_Vi[i] * Vi_y[i] + half_ru * (Vi_y[i] - Vi_x0[i]) * (Vi_y[i] - Vi_x0[i]) + \
                    mu_pi[i] * pi_y[i] + half_ru * (pi_y[i] - pi_x0[i]) * (pi_y[i] - pi_x0[i]) + \
                    mu_qi[i] * qi_y[i] + half_ru * (qi_y[i] - qi_x0[i]) * (qi_y[i] - qi_x0[i])
        for i in range(nl):
            objY += mu_Pij[i] * Pij_y[i] + half_ru * (Pij_y[i] - Pi_x0[t[i]]) * (Pij_y[i] - Pi_x0[t[i]]) + \
                    mu_Qij[i] * Qij_y[i] + half_ru * (Qij_y[i] - Qi_x0[t[i]]) * (Qij_y[i] - Qi_x0[t[i]]) + \
                    mu_Iij[i] * Iij_y[i] + half_ru * (Iij_y[i] - Ii_x0[t[i]]) * (Iij_y[i] - Ii_x0[t[i]]) + \
                    mu_Vij[i] * Vij_y[i] + half_ru * (Vij_y[i] - Vi_x0[f[i]]) * (Vij_y[i] - Vi_x0[f[i]])

        modelY.setObjective(objY)
        modelY.Params.OutputFlag = 0
        modelY.Params.LogToConsole = 0
        modelY.Params.DisplayInterval = 1
        modelY.Params.LogFile = ""
        modelY.optimize()
        for i in range(nb):
            Pi_y0[i] = modelY.getVarByName("Pi_y{0}".format(i)).X
            Qi_y0[i] = modelY.getVarByName("Qi_y{0}".format(i)).X
            Ii_y0[i] = modelY.getVarByName("Ii_y{0}".format(i)).X
            Vi_y0[i] = modelY.getVarByName("Vi_y{0}".format(i)).X
            pi_y0[i] = modelY.getVarByName("pi_y{0}".format(i)).X
            qi_y0[i] = modelY.getVarByName("qi_y{0}".format(i)).X

        for i in range(nl):
            Pij_y0[i] = modelY.getVarByName("Pij_y{0}".format(i)).X
            Qij_y0[i] = modelY.getVarByName("Qij_y{0}".format(i)).X
            Iij_y0[i] = modelY.getVarByName("Iij_y{0}".format(i)).X
            Vij_y0[i] = modelY.getVarByName("Vij_y{0}".format(i)).X
        del modelY

        # The sub problems
        modelX = Model("Xupdate")
        # Define the decision variables, compact set
        # X variables
        Pi_x = {}
        Qi_x = {}
        Ii_x = {}
        Vi_x = {}
        pi_x = {}
        qi_x = {}
        Pg = {}
        Qg = {}

        objX = 0
        for i in range(nb):  # The iteration from each bus
            Pi_x[i] = modelX.addVar(lb=-area[i]["SMAX"],
                                    ub=area[i]["SMAX"],
                                    vtype=GRB.CONTINUOUS,
                                    name="Pi_x{0}".format(i))
            Qi_x[i] = modelX.addVar(lb=-area[i]["SMAX"],
                                    ub=area[i]["SMAX"],
                                    vtype=GRB.CONTINUOUS,
                                    name="Qi_x{0}".format(i))
            Ii_x[i] = modelX.addVar(lb=-area[i]["SMAX"],
                                    ub=area[i]["SMAX"],
                                    vtype=GRB.CONTINUOUS,
                                    name="Ii_x{0}".format(i))
            Vi_x[i] = modelX.addVar(lb=area[i]["VMIN"],
                                    ub=area[i]["VMAX"],
                                    vtype=GRB.CONTINUOUS,
                                    name="Vi_x{0}".format(i))

            pi_x[i] = modelX.addVar(lb=-inf,
                                    ub=inf,
                                    vtype=GRB.CONTINUOUS,
                                    name="pi_x{0}".format(i))
            qi_x[i] = modelX.addVar(lb=-inf,
                                    ub=inf,
                                    vtype=GRB.CONTINUOUS,
                                    name="qi_x{0}".format(i))

            Pg[i] = modelX.addVar(lb=area[i]["PGMIN"],
                                  ub=area[i]["PGMAX"],
                                  vtype=GRB.CONTINUOUS,
                                  name="Pgi{0}".format(i))
            Qg[i] = modelX.addVar(lb=area[i]["QGMIN"],
                                  ub=area[i]["QGMAX"],
                                  vtype=GRB.CONTINUOUS,
                                  name="Qgi{0}".format(i))
            # For each branch, the following observation variables should be introduced
            # According to the sequence of line

        for i in range(nb):
            # Add constrain for each bus
            modelX.addConstr(Pg[i] - pi_x[i] == area[i]["PD"])
            modelX.addConstr(Qg[i] - qi_x[i] == area[i]["QD"])
            modelX.addConstr(
                Pi_x[i] * Pi_x[i] + Qi_x[i] * Qi_x[i] <= Ii_x[i] * Vi_x[i])

        for i in range(nb):
            # Update the objective function
            objX += area[i]["a"] * Pg[i] * Pg[i] + area[i]["b"] * Pg[i] + area[i]["c"] - \
                    mu_Pi[i] * Pi_x[i] + half_ru * (Pi_x[i] - Pi_y0[i]) * (Pi_x[i] - Pi_y0[i]) - \
                    mu_Qi[i] * Qi_x[i] + half_ru * (Qi_x[i] - Qi_y0[i]) * (Qi_x[i] - Qi_y0[i]) - \
                    mu_Ii[i] * Ii_x[i] + half_ru * (Ii_x[i] - Ii_y0[i]) * (Ii_x[i] - Ii_y0[i]) - \
                    mu_Vi[i] * Vi_x[i] + half_ru * (Vi_x[i] - Vi_y0[i]) * (Vi_x[i] - Vi_y0[i]) - \
                    mu_pi[i] * pi_x[i] + half_ru * (pi_x[i] - pi_y0[i]) * (pi_x[i] - pi_y0[i]) - \
                    mu_qi[i] * qi_x[i] + half_ru * (qi_x[i] - qi_y0[i]) * (qi_x[i] - qi_y0[i])

        for i in range(nl):
            objX += -mu_Pij[i] * Pi_x[t[i]] + half_ru * (Pi_x[t[i]] - Pij_y0[i]) * (Pi_x[t[i]] - Pij_y0[i]) \
                    - mu_Qij[i] * Qi_x[t[i]] + half_ru * (Qi_x[t[i]] - Qij_y0[i]) * (Qi_x[t[i]] - Qij_y0[i]) \
                    - mu_Iij[i] * Ii_x[t[i]] + half_ru * (Ii_x[t[i]] - Iij_y0[i]) * (Ii_x[t[i]] - Iij_y0[i]) \
                    - mu_Vij[i] * Vi_x[f[i]] + half_ru * (Vi_x[f[i]] - Vij_y0[i]) * (Vi_x[f[i]] - Vij_y0[i])

        modelX.setObjective(objX)
        modelX.Params.OutputFlag = 0
        modelX.Params.LogToConsole = 0
        modelX.Params.DisplayInterval = 1
        modelX.Params.LogFile = ""
        modelX.optimize()
        for i in range(nb):
            Pi_x0[i] = modelX.getVarByName("Pi_x{0}".format(i)).X
            Qi_x0[i] = modelX.getVarByName("Qi_x{0}".format(i)).X
            Ii_x0[i] = modelX.getVarByName("Ii_x{0}".format(i)).X
            Vi_x0[i] = modelX.getVarByName("Vi_x{0}".format(i)).X
            pi_x0[i] = modelX.getVarByName("pi_x{0}".format(i)).X
            qi_x0[i] = modelX.getVarByName("qi_x{0}".format(i)).X
            Pg0[i] = modelX.getVarByName("Pgi{0}".format(i)).X
            Qg0[i] = modelX.getVarByName("Qgi{0}".format(i)).X
        del modelX

        # Update mutiplier
        for i in range(nb):
            mu_Pi[i] += ru * (Pi_y0[i] - Pi_x0[i])
            mu_Qi[i] += ru * (Qi_y0[i] - Qi_x0[i])
            mu_Ii[i] += ru * (Ii_y0[i] - Ii_x0[i])
            mu_Vi[i] += ru * (Vi_y0[i] - Vi_x0[i])
            mu_pi[i] += ru * (pi_y0[i] - pi_x0[i])
            mu_qi[i] += ru * (qi_y0[i] - qi_x0[i])
        for i in range(nl):
            mu_Pij[i] += ru * (Pij_y0[i] - Pi_x0[t[i]])
            mu_Qij[i] += ru * (Qij_y0[i] - Qi_x0[t[i]])
            mu_Iij[i] += ru * (Iij_y0[i] - Ii_x0[t[i]])
            mu_Vij[i] += ru * (Vij_y0[i] - Vi_x0[f[i]])

        # Update residual
        residual = 0
        for i in range(nb):
            residual += abs(Pi_y0[i] - Pi_x0[i])
            residual += abs(Qi_y0[i] - Qi_x0[i])
            residual += abs(Ii_y0[i] - Ii_x0[i])
            residual += abs(Vi_y0[i] - Vi_x0[i])
            residual += abs(pi_y0[i] - pi_x0[i])
            residual += abs(qi_y0[i] - qi_x0[i])
        for i in range(nl):
            residual += abs(Pij_y0[i] - Pi_x0[t[i]])
            residual += abs(Qij_y0[i] - Qi_x0[t[i]])
            residual += abs(Iij_y0[i] - Ii_x0[t[i]])
            residual += abs(Vij_y0[i] - Vi_x0[f[i]])

        obj = 0
        for i in range(nb):
            obj += area[i]["a"] * Pg0[i] * Pg0[i] + area[i]["b"] * Pg0[
                i] + area[i]["c"]
        Gap = residual
        k = k + 1
        print(k)
        print(Gap)
        print(obj)
    obj = 0
    for i in range(nb):
        obj += area[i]["a"] * Pg0[i] * Pg0[i] + area[i]["b"] * Pg0[i] + area[
            i]["c"]

    primal_residual = []
    for i in range(nb):
        primal_residual.append(Pi_x0[i] * Pi_x0[i] + Qi_x0[i] * Qi_x0[i] -
                               Ii_x0[i] * Vi_x0[i])

    return obj, primal_residual
Exemple #11
0
def runpf(casedata=None, ppopt=None, fname='', solvedcase=''):
    """Runs a power flow.

    Runs a power flow [full AC Newton's method by default] and optionally
    returns the solved values in the data matrices, a flag which is C{True} if
    the algorithm was successful in finding a solution, and the elapsed
    time in seconds. All input arguments are optional. If C{casename} is
    provided it specifies the name of the input data file or dict
    containing the power flow data. The default value is 'case9'.

    If the ppopt is provided it overrides the default PYPOWER options
    vector and can be used to specify the solution algorithm and output
    options among other things. If the 3rd argument is given the pretty
    printed output will be appended to the file whose name is given in
    C{fname}. If C{solvedcase} is specified the solved case will be written
    to a case file in PYPOWER format with the specified name. If C{solvedcase}
    ends with '.mat' it saves the case as a MAT-file otherwise it saves it
    as a Python-file.

    If the C{ENFORCE_Q_LIMS} options is set to C{True} [default is false] then
    if any generator reactive power limit is violated after running the AC
    power flow, the corresponding bus is converted to a PQ bus, with Qg at
    the limit, and the case is re-run. The voltage magnitude at the bus
    will deviate from the specified value in order to satisfy the reactive
    power limit. If the reference bus is converted to PQ, the first
    remaining PV bus will be used as the slack bus for the next iteration.
    This may result in the real power output at this generator being
    slightly off from the specified values.

    Enforcing of generator Q limits inspired by contributions from Mu Lin,
    Lincoln University, New Zealand (1/14/05).

    @author: Ray Zimmerman (PSERC Cornell)
    """
    ## default arguments
    if casedata is None:
        casedata = join(dirname(__file__), 'case9')
    ppopt = ppoption(ppopt)

    ## options
    verbose = ppopt["VERBOSE"]
    qlim = ppopt["ENFORCE_Q_LIMS"]  ## enforce Q limits on gens?
    dc = ppopt["PF_DC"]  ## use DC formulation?

    ## read data
    ppc = loadcase(casedata)

    ## add zero columns to branch for flows if needed
    if ppc["branch"].shape[1] < QT:
        ppc["branch"] = c_[ppc["branch"],
                           zeros((ppc["branch"].shape[0],
                                  QT - ppc["branch"].shape[1] + 1))]

    ## convert to internal indexing
    ppc = ext2int(ppc)
    baseMVA, bus, gen, branch = \
        ppc["baseMVA"], ppc["bus"], ppc["gen"], ppc["branch"]

    ## get bus index lists of each type of bus
    ref, pv, pq = bustypes(bus, gen)

    ## generator info
    on = find(gen[:, GEN_STATUS] > 0)  ## which generators are on?
    gbus = gen[on, GEN_BUS].astype(int)  ## what buses are they at?

    ##-----  run the power flow  -----
    t0 = time()
    if verbose > 0:
        v = ppver('all')
        stdout.write('PYPOWER Version %s, %s' % (v["Version"], v["Date"]))

    if dc:  # DC formulation
        if verbose:
            stdout.write(' -- DC Power Flow\n')

        ## initial state
        Va0 = bus[:, VA] * (pi / 180)

        ## build B matrices and phase shift injections
        B, Bf, Pbusinj, Pfinj = makeBdc(baseMVA, bus, branch)

        ## compute complex bus power injections [generation - load]
        ## adjusted for phase shifters and real shunts
        Pbus = makeSbus(baseMVA, bus,
                        gen).real - Pbusinj - bus[:, GS] / baseMVA

        ## "run" the power flow
        Va = dcpf(B, Pbus, Va0, ref, pv, pq)

        ## update data matrices with solution
        branch[:, [QF, QT]] = zeros((branch.shape[0], 2))
        branch[:, PF] = (Bf * Va + Pfinj) * baseMVA
        branch[:, PT] = -branch[:, PF]
        bus[:, VM] = ones(bus.shape[0])
        bus[:, VA] = Va * (180 / pi)
        ## update Pg for slack generator (1st gen at ref bus)
        ## (note: other gens at ref bus are accounted for in Pbus)
        ##      Pg = Pinj + Pload + Gs
        ##      newPg = oldPg + newPinj - oldPinj
        refgen = zeros(len(ref), dtype=int)
        for k in range(len(ref)):
            temp = find(gbus == ref[k])
            refgen[k] = on[temp[0]]
        gen[refgen,
            PG] = gen[refgen, PG] + (B[ref, :] * Va - Pbus[ref]) * baseMVA

        success = 1
    else:  ## AC formulation
        alg = ppopt['PF_ALG']
        if verbose > 0:
            if alg == 1:
                solver = 'Newton'
            elif alg == 2:
                solver = 'fast-decoupled, XB'
            elif alg == 3:
                solver = 'fast-decoupled, BX'
            elif alg == 4:
                solver = 'Gauss-Seidel'
            else:
                solver = 'unknown'
            print(' -- AC Power Flow (%s)\n' % solver)

        ## initial state
        # V0    = ones(bus.shape[0])            ## flat start
        V0 = bus[:, VM] * exp(1j * pi / 180 * bus[:, VA])
        V0[gbus] = gen[on, VG] / abs(V0[gbus]) * V0[gbus]

        if qlim:
            ref0 = ref  ## save index and angle of
            Varef0 = bus[ref0, VA]  ##   original reference bus(es)
            limited = []  ## list of indices of gens @ Q lims
            fixedQg = zeros(gen.shape[0])  ## Qg of gens at Q limits

        repeat = True
        while repeat:
            ## build admittance matrices
            Ybus, Yf, Yt = makeYbus(baseMVA, bus, branch)

            ## compute complex bus power injections [generation - load]
            Sbus = makeSbus(baseMVA, bus, gen)

            ## run the power flow
            alg = ppopt["PF_ALG"]
            if alg == 1:
                V, success, _ = newtonpf(Ybus, Sbus, V0, ref, pv, pq, ppopt)
            elif alg == 2 or alg == 3:
                Bp, Bpp = makeB(baseMVA, bus, branch, alg)
                V, success, _ = fdpf(Ybus, Sbus, V0, Bp, Bpp, ref, pv, pq,
                                     ppopt)
            elif alg == 4:
                V, success, _ = gausspf(Ybus, Sbus, V0, ref, pv, pq, ppopt)
            else:
                stderr.write('Only Newton'
                             's method, fast-decoupled, and '
                             'Gauss-Seidel power flow algorithms currently '
                             'implemented.\n')

            ## update data matrices with solution
            bus, gen, branch = pfsoln(baseMVA, bus, gen, branch, Ybus, Yf, Yt,
                                      V, ref, pv, pq)

            if qlim:  ## enforce generator Q limits
                ## find gens with violated Q constraints
                gen_status = gen[:, GEN_STATUS] > 0
                qg_max_lim = gen[:, QG] > gen[:, QMAX]
                qg_min_lim = gen[:, QG] < gen[:, QMIN]

                mx = find(gen_status & qg_max_lim)
                mn = find(gen_status & qg_min_lim)

                if len(mx) > 0 or len(
                        mn) > 0:  ## we have some Q limit violations
                    # No PV generators
                    if len(pv) == 0:
                        if verbose:
                            if len(mx) > 0:
                                print(
                                    'Gen %d [only one left] exceeds upper Q limit : INFEASIBLE PROBLEM\n'
                                    % mx + 1)
                            else:
                                print(
                                    'Gen %d [only one left] exceeds lower Q limit : INFEASIBLE PROBLEM\n'
                                    % mn + 1)

                        success = 0
                        break

                    ## one at a time?
                    if qlim == 2:  ## fix largest violation, ignore the rest
                        k = argmax(r_[gen[mx, QG] - gen[mx, QMAX],
                                      gen[mn, QMIN] - gen[mn, QG]])
                        if k > len(mx):
                            mn = mn[k - len(mx)]
                            mx = []
                        else:
                            mx = mx[k]
                            mn = []

                    if verbose and len(mx) > 0:
                        for i in range(len(mx)):
                            print('Gen ' + str(mx[i] + 1) +
                                  ' at upper Q limit, converting to PQ bus\n')

                    if verbose and len(mn) > 0:
                        for i in range(len(mn)):
                            print('Gen ' + str(mn[i] + 1) +
                                  ' at lower Q limit, converting to PQ bus\n')

                    ## save corresponding limit values
                    fixedQg[mx] = gen[mx, QMAX]
                    fixedQg[mn] = gen[mn, QMIN]
                    mx = r_[mx, mn].astype(int)

                    ## convert to PQ bus
                    gen[mx, QG] = fixedQg[mx]  ## set Qg to binding
                    for i in range(
                            len(mx)
                    ):  ## [one at a time, since they may be at same bus]
                        gen[mx[i],
                            GEN_STATUS] = 0  ## temporarily turn off gen,
                        bi = gen[mx[i], GEN_BUS]  ## adjust load accordingly,
                        bus[bi, [PD, QD]] = (bus[bi, [PD, QD]] -
                                             gen[mx[i], [PG, QG]])

                    if len(ref) > 1 and any(bus[gen[mx, GEN_BUS],
                                                BUS_TYPE] == REF):
                        raise ValueError('Sorry, PYPOWER cannot enforce Q '
                                         'limits for slack buses in systems '
                                         'with multiple slacks.')

                    bus[gen[mx, GEN_BUS].astype(int),
                        BUS_TYPE] = PQ  ## & set bus type to PQ

                    ## update bus index lists of each type of bus
                    ref_temp = ref
                    ref, pv, pq = bustypes(bus, gen)
                    if verbose and ref != ref_temp:
                        print('Bus %d is new slack bus\n' % ref)

                    limited = r_[limited, mx].astype(int)
                else:
                    repeat = 0  ## no more generator Q limits violated
            else:
                repeat = 0  ## don't enforce generator Q limits, once is enough

        if qlim and len(limited) > 0:
            ## restore injections from limited gens [those at Q limits]
            gen[limited, QG] = fixedQg[limited]  ## restore Qg value,
            for i in range(
                    len(limited
                        )):  ## [one at a time, since they may be at same bus]
                bi = gen[limited[i], GEN_BUS]  ## re-adjust load,
                bus[bi,
                    [PD, QD]] = bus[bi, [PD, QD]] + gen[limited[i], [PG, QG]]
                gen[limited[i], GEN_STATUS] = 1  ## and turn gen back on

            if ref != ref0:
                ## adjust voltage angles to make original ref bus correct
                bus[:, VA] = bus[:, VA] - bus[ref0, VA] + Varef0

    ppc["et"] = time() - t0
    ppc["success"] = success

    ##-----  output results  -----
    ## convert back to original bus numbering & print results
    ppc["bus"], ppc["gen"], ppc["branch"] = bus, gen, branch
    results = int2ext(ppc)

    ## zero out result fields of out-of-service gens & branches
    if len(results["order"]["gen"]["status"]["off"]) > 0:
        results["gen"][ix_(results["order"]["gen"]["status"]["off"],
                           [PG, QG])] = 0

    if len(results["order"]["branch"]["status"]["off"]) > 0:
        results["branch"][ix_(results["order"]["branch"]["status"]["off"],
                              [PF, QF, PT, QT])] = 0

    if fname:
        fd = None
        try:
            fd = open(fname, "a")
        except Exception as detail:
            stderr.write("Error opening %s: %s.\n" % (fname, detail))
        finally:
            if fd is not None:
                printpf(results, fd, ppopt)
                fd.close()
    else:
        printpf(results, stdout, ppopt)

    ## save solved case
    if solvedcase:
        savecase(solvedcase, results)

    return results, success
def run_sim(ppc, elements, dynopt=None, events=None, recorder=None):
    """
    Run a time-domain simulation
    
    Inputs:
        ppc         PYPOWER load flow case
        elements    Dictionary of dynamic model objects (machines, controllers, etc) with Object ID as key
        events      Events object
        recorder    Recorder object (empty)
    
    Outputs:
        recorder    Recorder object (with data)
    """

    #########
    # SETUP #
    #########

    # Get version information
    ver = pydyn_ver()
    print('PYPOWER-Dynamics ' + ver['Version'] + ', ' + ver['Date'])

    # Program options
    if dynopt:
        h = dynopt['h']
        t_sim = dynopt['t_sim']
        max_err = dynopt['max_err']
        max_iter = dynopt['max_iter']
        verbose = dynopt['verbose']
    else:
        # Default program options
        h = 0.01  # step length (s)
        t_sim = 5  # simulation time (s)
        max_err = 0.0001  # Maximum error in network iteration (voltage mismatches)
        max_iter = 25  # Maximum number of network iterations
        verbose = False

    # Make lists of current injection sources (generators, external grids, etc) and controllers
    sources = []
    controllers = []
    for element in elements.values():
        if element.__module__ in [
                'pydyn.sym_order6a', 'pydyn.sym_order6b', 'pydyn.sym_order4',
                'pydyn.ext_grid', 'pydyn.vsc_average', 'pydyn.asym_1cage',
                'pydyn.asym_2cage'
        ]:
            sources.append(element)

        if element.__module__ == 'pydyn.controller':
            controllers.append(element)

    # Set up interfaces
    interfaces = init_interfaces(elements)

    ##################
    # INITIALISATION #
    ##################
    print('Initialising models...')

    # Run power flow and update bus voltages and angles in PYPOWER case object
    results, success = runpf(ppc)
    ppc["bus"][:, VM] = results["bus"][:, VM]
    ppc["bus"][:, VA] = results["bus"][:, VA]

    # Build Ybus matrix
    ppc_int = ext2int(ppc)
    baseMVA, bus, branch = ppc_int["baseMVA"], ppc_int["bus"], ppc_int[
        "branch"]
    Ybus, Yf, Yt = makeYbus(baseMVA, bus, branch)

    # Build modified Ybus matrix
    Ybus = mod_Ybus(Ybus, elements, bus, ppc_int['gen'], baseMVA)

    # Calculate initial voltage phasors
    v0 = bus[:, VM] * (np.cos(np.radians(bus[:, VA])) +
                       1j * np.sin(np.radians(bus[:, VA])))

    # Initialise sources from load flow
    for source in sources:
        if source.__module__ in ['pydyn.asym_1cage', 'pydyn.asym_2cage']:
            # Asynchronous machine
            source_bus = int(ppc_int['bus'][source.bus_no, 0])
            v_source = v0[source_bus]
            source.initialise(v_source, 0)
        else:
            # Generator or VSC
            source_bus = int(ppc_int['gen'][source.gen_no, 0])
            S_source = np.complex(results["gen"][source.gen_no, 1] / baseMVA,
                                  results["gen"][source.gen_no, 2] / baseMVA)
            v_source = v0[source_bus]
            source.initialise(v_source, S_source)

    # Interface controllers and machines (for initialisation)
    for intf in interfaces:
        int_type = intf[0]
        var_name = intf[1]
        if int_type == 'OUTPUT':
            # If an output, interface in the reverse direction for initialisation
            intf[2].signals[var_name] = intf[3].signals[var_name]
        else:
            # Inputs are interfaced in normal direction during initialisation
            intf[3].signals[var_name] = intf[2].signals[var_name]

    # Initialise controllers
    for controller in controllers:
        controller.initialise()

    #############
    # MAIN LOOP #
    #############

    if events == None:
        print('Warning: no events!')

    # Factorise Ybus matrix
    Ybus_inv = splu(Ybus)

    y1 = []
    v_prev = v0
    print('Simulating...')
    for t in range(int(t_sim / h) + 1):
        if np.mod(t, 1 / h) == 0:
            print('t=' + str(t * h) + 's')

        # Interface controllers and machines
        for intf in interfaces:
            var_name = intf[1]
            intf[3].signals[var_name] = intf[2].signals[var_name]

        # Solve differential equations
        for j in range(4):
            # Solve step of differential equations
            for element in elements.values():
                element.solve_step(h, j)

            # Interface with network equations
            v_prev = solve_network(sources, v_prev, Ybus_inv, ppc_int,
                                   len(bus), max_err, max_iter)

        if recorder != None:
            # Record signals or states
            recorder.record_variables(t * h, elements)

        if events != None:
            # Check event stack
            ppc, refactorise = events.handle_events(np.round(t * h, 5),
                                                    elements, ppc, baseMVA)

            if refactorise == True:
                # Rebuild Ybus from new ppc_int

                prev_ybus = Ybus.todense().copy()

                ppc_int = ext2int(ppc)
                baseMVA, bus, branch = ppc_int["baseMVA"], ppc_int[
                    "bus"], ppc_int["branch"]
                Ybus, Yf, Yt = makeYbus(baseMVA, bus, branch)

                # Rebuild modified Ybus
                Ybus = mod_Ybus(Ybus, elements, bus, ppc_int['gen'], baseMVA)

                ybus_delta = np.array(Ybus - prev_ybus)

                # Refactorise Ybus
                Ybus_inv = splu(Ybus)

                # Solve network equations
                v_prev = solve_network(sources, v_prev, Ybus_inv, ppc_int,
                                       len(bus), max_err, max_iter)

    return recorder
Exemple #13
0
fnameWhole = "Net1_UKGDS_60.m"
convert_mcase(fname)
from Net1_UKGDS_60_subgrid import Net1_UKGDS_60_ as net
from Net1_UKGDS_60 import Net1_UKGDS_60_ as netWhole

t_f = 96
# time variation
t_ges = 1440  # all time in min
delta_t = 15  # time intervals in min
time = np.arange(delta_t, t_ges + delta_t, delta_t)

casedataWhole = netWhole()
convert_to_python_indices(casedataWhole)
ppc = casedataWhole
ppopt = ppoption(PF_ALG=2)
ppc = ext2int(ppc)
baseMVA, bus, gen, branch = ppc["baseMVA"], ppc["bus"], ppc["gen"], ppc[
    "branch"]
baseMVA = 1000
Ybus, Yf, Yt = makeYbus(baseMVA, bus, branch)  # nodal admittance matrix Ybus
condition = np.hstack((1, np.arange(16, 27, 1)))
Yws = Ybus[np.ix_(condition, condition)]

#Yws = Y
t_f = 96
slack_idx = 0
p0 = 1e-3
iterstop = 50
accuracy = 1e-9
roundY = None
Exemple #14
0
def solveropfnlp_2(ppc, solver="ipopt"):
    if solver == "ipopt":
        opt = SolverFactory("ipopt", executable="/home/iso/PycharmProjects/opfLC_python3/Python3/py_solvers/ipopt-linux64/ipopt")
    if solver == "bonmin":
        opt = SolverFactory("bonmin", executable="/home/iso/PycharmProjects/opfLC_python3/Python3/py_solvers/bonmin-linux64/bonmin")
    if solver == "knitro":
        opt = SolverFactory("knitro", executable="D:/ICT/Artelys/Knitro 10.2.1/knitroampl/knitroampl")

    ppc = ext2int(ppc)      # convert to continuous indexing starting from 0

    # Gather information about the system
    # =============================================================
    baseMVA, bus, gen, branch = \
        ppc["baseMVA"], ppc["bus"], ppc["gen"], ppc["branch"]

    nb = bus.shape[0]       # number of buses
    ng = gen.shape[0]       # number of generators
    nl = branch.shape[0]    # number of lines

    # generator buses
    gb = tolist(np.array(gen[:, GEN_BUS]).astype(int))

    sb = find((bus[:, BUS_TYPE] == REF))    # slack bus index
    fr = branch[:, F_BUS].astype(int)       # from bus indices
    to = branch[:, T_BUS].astype(int)       # to bus indices

    tr = branch[:, TAP]     # transformation ratios
    tr[find(tr == 0)] = 1   # set to 1 transformation ratios that are 0

    r = branch[:, BR_R]     # branch resistances
    x = branch[:, BR_X]     # branch reactances
    b = branch[:, BR_B]     # branch susceptances

    start_time = time.clock()

    # Admittance matrix computation
    # =============================================================
    y = makeYbus(baseMVA, bus, branch)[0]   # admittance matrix

    yk = 1./(r+x*1j)                        # branch admittance
    yft = yk + 0.5j*b                       # branch admittance + susceptance
    gk = yk.real                            # branch resistance
    yk = yk/tr                              # include /tr in yk

    # Optimization
    # =============================================================
    branch[find(branch[:, RATE_A] == 0), RATE_A] = 9999     # set undefined Sflow limit to 9999
    Smax = branch[:, RATE_A] / baseMVA                      # Max. Sflow

    # Power demand parameters
    Pd = bus[:, PD] / baseMVA
    Qd = bus[:, QD] / baseMVA

    # Max and min Pg and Qg
    Pg_max = zeros(nb)
    Pg_max[gb] = gen[:, PMAX] / baseMVA
    Pg_min = zeros(nb)
    Pg_min[gb] = gen[:, PMIN] / baseMVA
    Qg_max = zeros(nb)
    Qg_max[gb] = gen[:, QMAX] / baseMVA
    Qg_min = zeros(nb)
    Qg_min[gb] = gen[:, QMIN] / baseMVA

    # Vmax and Vmin vectors
    Vmax = bus[:, VMAX]
    Vmin = bus[:, VMIN]

    vm = bus[:, VM]
    va = bus[:, VA]*pi/180

    # create a new optimization model
    model = ConcreteModel()

    # Define sets
    # ------------
    model.bus = Set(ordered=True, initialize=range(nb))     # Set of all buses
    model.gen = Set(ordered=True, initialize=gb)                # Set of buses with generation
    model.line = Set(ordered=True, initialize=range(nl))    # Set of all lines

    # Define variables
    # -----------------
    # Voltage magnitudes vector (vm)
    model.vm = Var(model.bus)

    # Voltage angles vector (va)
    model.va = Var(model.bus)

    # Reactive power generation, synchronous machines(SM) (Qg)
    model.Qg = Var(model.gen)
    Qg0 = zeros(nb)
    Qg0[gb] = gen[:, QG]/baseMVA

    # Active power generation, synchronous machines(SM) (Pg)
    model.Pg = Var(model.gen)
    Pg0 = zeros(nb)
    Pg0[gb] = gen[:, PG] / baseMVA

    # Active and reactive power from at all branches
    model.Pf = Var(model.line)
    model.Qf = Var(model.line)

    # Active and reactive power to at all branches
    model.Pt = Var(model.line)
    model.Qt = Var(model.line)

    # Warm start the problem
    # ------------------------
    for i in range(nb):
        model.vm[i] = vm[i]
        model.va[i] = va[i]
        if i in gb:
            model.Pg[i] = Pg0[i]
            model.Qg[i] = Qg0[i]
    for i in range(nl):
        model.Pf[i] = vm[fr[i]] ** 2 * abs(yft[i]) / (tr[i] ** 2) * np.cos(-ang(yft[i])) -\
                      vm[fr[i]] * vm[to[i]] * abs(yk[i]) * np.cos(va[fr[i]] - va[to[i]] - ang(yk[i]))
        model.Qf[i] = vm[fr[i]] ** 2 * abs(yft[i]) / (tr[i] ** 2) * np.sin(-ang(yft[i])) -\
                      vm[fr[i]] * vm[to[i]] * abs(yk[i]) * np.sin(va[fr[i]] - va[to[i]] - ang(yk[i]))
        model.Pt[i] = vm[to[i]] ** 2 * abs(yft[i]) * np.cos(-ang(yft[i])) -\
                      vm[to[i]] * vm[fr[i]] * abs(yk[i]) * np.cos(va[to[i]] - va[fr[i]] - ang(yk[i]))
        model.Qt[i] = vm[to[i]] ** 2 * abs(yft[i]) * np.sin(-ang(yft[i])) -\
                      vm[to[i]] * vm[fr[i]] * abs(yk[i]) * np.sin(va[to[i]] - va[fr[i]] - ang(yk[i]))

    # Define constraints
    # ----------------------------

    # Equalities:
    # ------------

    # Active power flow equalities
    def powerflowact(model, i):
        if i in gb:
            return model.Pg[i]-Pd[i] == sum(model.vm[i]*model.vm[j]*abs(y[i, j]) *
                                            cos(model.va[i] - model.va[j] - ang(y[i, j])) for j in range(nb))
        else:
            return sum(model.vm[i]*model.vm[j]*abs(y[i, j]) * cos(model.va[i] - model.va[j] -
                                                                  ang(y[i, j])) for j in range(nb)) == -Pd[i]

    model.const1 = Constraint(model.bus, rule=powerflowact)

    # Reactive power flow equalities
    def powerflowreact(model, i):
        if i in gb:
            return model.Qg[i]-Qd[i] == sum(model.vm[i]*model.vm[j]*abs(y[i, j]) *
                                            sin(model.va[i] - model.va[j] - ang(y[i, j])) for j in range(nb))
        else:
            return sum(model.vm[i]*model.vm[j]*abs(y[i, j]) * sin(model.va[i] - model.va[j] -
                                                                  ang(y[i, j])) for j in range(nb)) == -Qd[i]

    model.const2 = Constraint(model.bus, rule=powerflowreact)

    # Active power from
    def pfrom(model, i):
        return model.Pf[i] == model.vm[fr[i]] ** 2 * abs(yft[i]) / (tr[i] ** 2) * np.cos(-ang(yft[i])) - \
                              model.vm[fr[i]] * model.vm[to[i]] * abs(yk[i]) * \
                              cos(model.va[fr[i]] - model.va[to[i]] - ang(yk[i]))

    model.const3 = Constraint(model.line, rule=pfrom)

    # Reactive power from
    def qfrom(model, i):
        return model.Qf[i] == model.vm[fr[i]] ** 2 * abs(yft[i]) / (tr[i] ** 2) * np.sin(-ang(yft[i])) - \
                              model.vm[fr[i]] * model.vm[to[i]] * abs(yk[i]) * \
                              sin(model.va[fr[i]] - model.va[to[i]] - ang(yk[i]))

    model.const4 = Constraint(model.line, rule=qfrom)

    # Active power to
    def pto(model, i):
        return model.Pt[i] == model.vm[to[i]] ** 2 * abs(yft[i]) * np.cos(-ang(yft[i])) - \
                              model.vm[to[i]] * model.vm[fr[i]] * abs(yk[i]) * \
                              cos(model.va[to[i]] - model.va[fr[i]] - ang(yk[i]))

    model.const5 = Constraint(model.line, rule=pto)

    # Reactive power to
    def qto(model, i):
        return model.Qt[i] == model.vm[to[i]] ** 2 * abs(yft[i]) * np.sin(-ang(yft[i])) - \
                              model.vm[to[i]] * model.vm[fr[i]] * abs(yk[i]) * \
                              sin(model.va[to[i]] - model.va[fr[i]] - ang(yk[i]))

    model.const6 = Constraint(model.line, rule=qto)

    # Slack bus phase angle
    model.const7 = Constraint(expr=model.va[sb[0]] == 0)

    # Inequalities:
    # ----------------

    # Active power generator limits Pg_min <= Pg <= Pg_max
    def genplimits(model, i):
        return Pg_min[i] <= model.Pg[i] <= Pg_max[i]

    model.const8 = Constraint(model.gen, rule=genplimits)

    # Reactive power generator limits Qg_min <= Qg <= Qg_max
    def genqlimits(model, i):
        return Qg_min[i] <= model.Qg[i] <= Qg_max[i]

    model.const9 = Constraint(model.gen, rule=genqlimits)

    # Voltage constraints ( Vmin <= V <= Vmax )
    def vlimits(model, i):
        return Vmin[i] <= model.vm[i] <= Vmax[i]

    model.const10 = Constraint(model.bus, rule=vlimits)

    # Sfrom line limit
    def sfrommax(model, i):
        return model.Pf[i]**2 + model.Qf[i]**2 <= Smax[i]**2

    model.const11 = Constraint(model.line, rule=sfrommax)

    # Sto line limit
    def stomax(model, i):
        return model.Pt[i]**2 + model.Qt[i]**2 <= Smax[i]**2

    model.const12 = Constraint(model.line, rule=stomax)

    # Set objective function
    # ------------------------
    def obj_fun(model):
        return sum(gk[i] * ((model.vm[fr[i]] / tr[i])**2 + model.vm[to[i]]**2 -
                         2/tr[i] * model.vm[fr[i]] * model.vm[to[i]] *
                         cos(model.va[fr[i]] - model.va[to[i]])) for i in range(nl))

    model.obj = Objective(rule=obj_fun, sense=minimize)

    mt = time.clock() - start_time                  # Modeling time

    # Execute solve command with the selected solver
    # ------------------------------------------------
    start_time = time.clock()
    results = opt.solve(model, tee=True)
    et = time.clock() - start_time                  # Elapsed time
    print(results)

    # Update the case info with the optimized variables
    # ==================================================
    for i in range(nb):
        bus[i, VM] = model.vm[i].value              # Bus voltage magnitudes
        bus[i, VA] = model.va[i].value*180/pi       # Bus voltage angles
    # Include Pf - Qf - Pt - Qt in the branch matrix
    branchsol = zeros((nl, 17))
    branchsol[:, :-4] = branch
    for i in range(nl):
        branchsol[i, PF] = model.Pf[i].value * baseMVA
        branchsol[i, QF] = model.Qf[i].value * baseMVA
        branchsol[i, PT] = model.Pt[i].value * baseMVA
        branchsol[i, QT] = model.Qt[i].value * baseMVA
    # Update gen matrix variables
    for i in range(ng):
        gen[i, PG] = model.Pg[gb[i]].value * baseMVA
        gen[i, QG] = model.Qg[gb[i]].value * baseMVA
        gen[i, VG] = bus[gb[i], VM]
    # Convert to external (original) numbering and save case results
    ppc = int2ext(ppc)
    ppc['bus'][:, 1:] = bus[:, 1:]
    branchsol[:, 0:2] = ppc['branch'][:, 0:2]
    ppc['branch'] = branchsol
    ppc['gen'][:, 1:] = gen[:, 1:]
    ppc['obj'] = value(obj_fun(model))
    ppc['ploss'] = value(obj_fun(model)) * baseMVA
    ppc['et'] = et
    ppc['mt'] = mt
    ppc['success'] = 1

    # ppc solved case is returned
    return ppc
def run(mpc):
    """
    Gurobi based optimal power flow modelling and solution
    :param mpc: The input case of optimal power flow
    :return: obtained solution
    """
    # Data format
    from pypower.idx_brch import F_BUS, T_BUS, BR_R, BR_X, TAP, SHIFT, BR_STATUS, RATE_A
    from pypower.idx_cost import MODEL, NCOST, PW_LINEAR, COST, POLYNOMIAL
    from pypower.idx_bus import BUS_TYPE, REF, VA, VM, PD, GS, VMAX, VMIN, BUS_I, QD
    from pypower.idx_gen import GEN_BUS, VG, PG, QG, PMAX, PMIN, QMAX, QMIN
    from pypower.ext2int import ext2int

    mpc = ext2int(mpc)
    baseMVA, bus, gen, branch, gencost = mpc["baseMVA"], mpc["bus"], mpc[
        "gen"], mpc["branch"], mpc["gencost"]

    nb = shape(mpc['bus'])[0]  # number of buses
    nl = shape(mpc['branch'])[0]  # number of branches
    ng = shape(mpc['gen'])[0]  # number of dispatchable injections

    for i in range(nl):  # branch indexing exchange
        if branch[i, F_BUS] > branch[i, T_BUS]:
            temp = branch[i, F_BUS]
            branch[i, F_BUS] = branch[i, T_BUS]
            branch[i, T_BUS] = temp

    f = branch[:, F_BUS]  ## list of "from" buses
    t = branch[:, T_BUS]  ## list of "to" buses
    area = ancestor_children_generation(f, t, range(nb))

    # Connection matrix
    Cg = sparse((ones(ng), (gen[:, GEN_BUS], range(ng))), (nb, ng))
    Branch_R = branch[:, BR_R]
    Branch_X = branch[:, BR_X]

    # Obtain the boundary information

    Slmax = branch[:, RATE_A] / baseMVA

    Pij_l = -Slmax
    Qij_l = -Slmax
    Iij_l = zeros(nl)
    Vm_l = turn_to_power(bus[:, VMIN], 2)
    Pg_l = gen[:, PMIN] / baseMVA
    Qg_l = gen[:, QMIN] / baseMVA
    Pi_l = -bus[:, PD] / baseMVA + Cg * Pg_l
    Qi_l = -bus[:, QD] / baseMVA + Cg * Qg_l

    Pij_u = Slmax
    Qij_u = Slmax
    Iij_u = Slmax
    Vm_u = turn_to_power(bus[:, VMAX], 2)
    Pg_u = 2 * gen[:, PMAX] / baseMVA
    Qg_u = 2 * gen[:, QMAX] / baseMVA
    Pi_u = -bus[:, PD] / baseMVA + Cg * Pg_u
    Qi_u = -bus[:, QD] / baseMVA + Cg * Qg_u
    #
    model = Model("OPF")
    # Define the decision variables, compact set
    Pij = {}
    Qij = {}
    Iij = {}
    Vi = {}
    Pg = {}
    Qg = {}
    Pi = {}
    Qi = {}

    for i in range(nl):
        Pij[i] = model.addVar(lb=Pij_l[i],
                              ub=Pij_u[i],
                              vtype=GRB.CONTINUOUS,
                              name="Pij{0}".format(i))
        Qij[i] = model.addVar(lb=Qij_l[i],
                              ub=Qij_u[i],
                              vtype=GRB.CONTINUOUS,
                              name="Qij{0}".format(i))
        Iij[i] = model.addVar(lb=Iij_l[i],
                              ub=Iij_u[i],
                              vtype=GRB.CONTINUOUS,
                              name="Iij{0}".format(i))

    for i in range(nb):
        Vi[i] = model.addVar(lb=Vm_l[i],
                             ub=Vm_u[i],
                             vtype=GRB.CONTINUOUS,
                             name="V{0}".format(i))

    for i in range(ng):
        Pg[i] = model.addVar(lb=Pg_l[i],
                             ub=Pg_u[i],
                             vtype=GRB.CONTINUOUS,
                             name="Pg{0}".format(i))
        Qg[i] = model.addVar(lb=Qg_l[i],
                             ub=Qg_u[i],
                             vtype=GRB.CONTINUOUS,
                             name="Qg{0}".format(i))
    for i in range(nb):
        Pi[i] = model.addVar(lb=Pi_l[i],
                             ub=Pi_u[i],
                             vtype=GRB.CONTINUOUS,
                             name="Pi{0}".format(i))
        Qi[i] = model.addVar(lb=Qi_l[i],
                             ub=Qi_u[i],
                             vtype=GRB.CONTINUOUS,
                             name="Qi{0}".format(i))
    # For each area, before decomposition
    # Add system level constraints
    for i in range(nb):
        # If the bus is the root bus, only the children information is required.
        if len(area[i]["Ai"]) == 0:
            print(i)
            expr = 0
            for j in range(len(area[i]["Cbranch"][0])):
                expr += Pij[area[i]["Cbranch"][0][j]]

            model.addConstr(lhs=expr - Pi[i], sense=GRB.EQUAL, rhs=0)

            expr = 0
            for j in range(len(area[i]["Cbranch"][0])):
                expr += Qij[area[i]["Cbranch"][0][j]]

            model.addConstr(lhs=expr - Qi[i], sense=GRB.EQUAL, rhs=0)

        elif len(area[i]["Cbranch"]) == 0:  # This bus is the lead node

            model.addConstr(lhs=Pij[area[i]["Abranch"][0][0]] -
                            Iij[area[i]["Abranch"][0][0]] *
                            Branch_R[area[i]["Abranch"][0][0]] + Pi[i],
                            sense=GRB.EQUAL,
                            rhs=0)

            model.addConstr(lhs=Qij[area[i]["Abranch"][0][0]] -
                            Iij[area[i]["Abranch"][0][0]] *
                            Branch_X[area[i]["Abranch"][0][0]] + Qi[i],
                            sense=GRB.EQUAL,
                            rhs=0)

            model.addConstr(lhs=Vi[int(area[i]["Ai"][0])] - Vi[i] -
                            2 * Branch_R[area[i]["Abranch"][0][0]] *
                            Pij[area[i]["Abranch"][0][0]] -
                            2 * Branch_X[area[i]["Abranch"][0][0]] *
                            Qij[area[i]["Abranch"][0][0]] +
                            Iij[area[i]["Abranch"][0][0]] *
                            (Branch_R[area[i]["Abranch"][0][0]]**2 +
                             Branch_X[area[i]["Abranch"][0][0]]**2),
                            sense=GRB.EQUAL,
                            rhs=0)

            model.addConstr(
                Pij[area[i]["Abranch"][0][0]] * Pij[area[i]["Abranch"][0][0]] +
                Qij[area[i]["Abranch"][0][0]] * Qij[area[i]["Abranch"][0][0]]
                <= Vi[int(area[i]["Ai"][0])] * Iij[area[i]["Abranch"][0][0]])

        else:
            expr = 0
            for j in range(len(area[i]["Cbranch"][0])):
                expr += Pij[area[i]["Cbranch"][0][j]]
            model.addConstr(lhs=Pij[area[i]["Abranch"][0][0]] -
                            Iij[area[i]["Abranch"][0][0]] *
                            Branch_R[area[i]["Abranch"][0][0]] + Pi[i] - expr,
                            sense=GRB.EQUAL,
                            rhs=0)

            expr = 0
            for j in range(len(area[i]["Cbranch"][0])):
                expr += Qij[area[i]["Cbranch"][0][j]]

            model.addConstr(Qij[area[i]["Abranch"][0][0]] -
                            Iij[area[i]["Abranch"][0][0]] *
                            Branch_X[area[i]["Abranch"][0][0]] + Qi[i] - expr,
                            sense=GRB.EQUAL,
                            rhs=0)

            model.addConstr(lhs=Vi[int(area[i]["Ai"][0])] - Vi[i] -
                            2 * Branch_R[area[i]["Abranch"][0][0]] *
                            Pij[area[i]["Abranch"][0][0]] -
                            2 * Branch_X[area[i]["Abranch"][0][0]] *
                            Qij[area[i]["Abranch"][0][0]] +
                            Iij[area[i]["Abranch"][0][0]] *
                            (Branch_R[area[i]["Abranch"][0][0]]**2 +
                             Branch_X[area[i]["Abranch"][0][0]]**2),
                            sense=GRB.EQUAL,
                            rhs=0)
            model.addConstr(
                Pij[area[i]["Abranch"][0][0]] * Pij[area[i]["Abranch"][0][0]] +
                Qij[area[i]["Abranch"][0][0]] * Qij[area[i]["Abranch"][0][0]]
                <= Vi[int(area[i]["Ai"][0])] * Iij[area[i]["Abranch"][0][0]])
    obj = 0
    for i in range(ng):
        model.addConstr(lhs=Pg[i] - Pi[int(gen[i, GEN_BUS])],
                        sense=GRB.EQUAL,
                        rhs=bus[int(gen[i, GEN_BUS]), PD] / baseMVA)
        model.addConstr(lhs=Qg[i] - Qi[int(gen[i, GEN_BUS])],
                        sense=GRB.EQUAL,
                        rhs=bus[int(gen[i, GEN_BUS]), QD] / baseMVA)
        obj += gencost[i, 4] * Pg[i] * Pg[i] * baseMVA * baseMVA + gencost[
            i, 5] * Pg[i] * baseMVA + gencost[i, 6]

    model.setObjective(obj)
    model.Params.OutputFlag = 0
    model.Params.LogToConsole = 0
    model.Params.DisplayInterval = 1
    model.optimize()

    Pij = []
    Qij = []
    Iij = []
    Vi = []
    Pg = []
    Qg = []
    Pi = []
    Qi = []

    for i in range(nl):
        Pij.append(model.getVarByName("Pij{0}".format(i)).X)
        Qij.append(model.getVarByName("Qij{0}".format(i)).X)
        Iij.append(model.getVarByName("Iij{0}".format(i)).X)

    for i in range(nb):
        Vi.append(model.getVarByName("V{0}".format(i)).X)
        Pi.append(model.getVarByName("Pi{0}".format(i)).X)
        Qi.append(model.getVarByName("Qi{0}".format(i)).X)

    for i in range(ng):
        Pg.append(model.getVarByName("Pg{0}".format(i)).X)
        Qg.append(model.getVarByName("Qg{0}".format(i)).X)

    obj = obj.getValue()

    primal_residual = []

    for i in range(nl):
        primal_residual.append(Pij[i] * Pij[i] + Qij[i] * Qij[i] -
                               Iij[i] * Vi[int(f[i])])

    return obj, primal_residual
Exemple #16
0
def opf(*args):
    """Solves an optimal power flow.

    Returns a C{results} dict.

    The data for the problem can be specified in one of three ways:
      1. a string (ppc) containing the file name of a PYPOWER case
      which defines the data matrices baseMVA, bus, gen, branch, and
      gencost (areas is not used at all, it is only included for
      backward compatibility of the API).
      2. a dict (ppc) containing the data matrices as fields.
      3. the individual data matrices themselves.

    The optional user parameters for user constraints (C{A, l, u}), user costs
    (C{N, fparm, H, Cw}), user variable initializer (C{z0}), and user variable
    limits (C{zl, zu}) can also be specified as fields in a case dict,
    either passed in directly or defined in a case file referenced by name.

    When specified, C{A, l, u} represent additional linear constraints on the
    optimization variables, C{l <= A*[x z] <= u}. If the user specifies an C{A}
    matrix that has more columns than the number of "C{x}" (OPF) variables,
    then there are extra linearly constrained "C{z}" variables. For an
    explanation of the formulation used and instructions for forming the
    C{A} matrix, see the MATPOWER manual.

    A generalized cost on all variables can be applied if input arguments
    C{N}, C{fparm}, C{H} and C{Cw} are specified. First, a linear transformation
    of the optimization variables is defined by means of C{r = N * [x z]}.
    Then, to each element of C{r} a function is applied as encoded in the
    C{fparm} matrix (see MATPOWER manual). If the resulting vector is named
    C{w}, then C{H} and C{Cw} define a quadratic cost on w:
    C{(1/2)*w'*H*w + Cw * w}. C{H} and C{N} should be sparse matrices and C{H}
    should also be symmetric.

    The optional C{ppopt} vector specifies PYPOWER options. If the OPF
    algorithm is not explicitly set in the options PYPOWER will use the default
    solver, based on a primal-dual interior point method. For the AC OPF this
    is C{OPF_ALG = 560}. For the DC OPF, the default is C{OPF_ALG_DC = 200}.
    See L{ppoption} for more details on the available OPF solvers and other OPF
    options and their default values.

    The solved case is returned in a single results dict (described
    below). Also returned are the final objective function value (C{f}) and a
    flag which is C{True} if the algorithm was successful in finding a solution
    (success). Additional optional return values are an algorithm specific
    return status (C{info}), elapsed time in seconds (C{et}), the constraint
    vector (C{g}), the Jacobian matrix (C{jac}), and the vector of variables
    (C{xr}) as well as the constraint multipliers (C{pimul}).

    The single results dict is a PYPOWER case struct (ppc) with the
    usual baseMVA, bus, branch, gen, gencost fields, along with the
    following additional fields:

        - C{order}      see 'help ext2int' for details of this field
        - C{et}         elapsed time in seconds for solving OPF
        - C{success}    1 if solver converged successfully, 0 otherwise
        - C{om}         OPF model object, see 'help opf_model'
        - C{x}          final value of optimization variables (internal order)
        - C{f}          final objective function value
        - C{mu}         shadow prices on ...
            - C{var}
                - C{l}  lower bounds on variables
                - C{u}  upper bounds on variables
            - C{nln}
                - C{l}  lower bounds on nonlinear constraints
                - C{u}  upper bounds on nonlinear constraints
            - C{lin}
                - C{l}  lower bounds on linear constraints
                - C{u}  upper bounds on linear constraints
        - C{g}          (optional) constraint values
        - C{dg}         (optional) constraint 1st derivatives
        - C{df}         (optional) obj fun 1st derivatives (not yet implemented)
        - C{d2f}        (optional) obj fun 2nd derivatives (not yet implemented)
        - C{raw}        raw solver output in form returned by MINOS, and more
            - C{xr}     final value of optimization variables
            - C{pimul}  constraint multipliers
            - C{info}   solver specific termination code
            - C{output} solver specific output information
               - C{alg} algorithm code of solver used
        - C{var}
            - C{val}    optimization variable values, by named block
                - C{Va}     voltage angles
                - C{Vm}     voltage magnitudes (AC only)
                - C{Pg}     real power injections
                - C{Qg}     reactive power injections (AC only)
                - C{y}      constrained cost variable (only if have pwl costs)
                - (other) any user defined variable blocks
            - C{mu}     variable bound shadow prices, by named block
                - C{l}  lower bound shadow prices
                    - C{Va}, C{Vm}, C{Pg}, C{Qg}, C{y}, (other)
                - C{u}  upper bound shadow prices
                    - C{Va}, C{Vm}, C{Pg}, C{Qg}, C{y}, (other)
        - C{nln}    (AC only)
            - C{mu}     shadow prices on nonlinear constraints, by named block
                - C{l}  lower bounds
                    - C{Pmis}   real power mismatch equations
                    - C{Qmis}   reactive power mismatch equations
                    - C{Sf}     flow limits at "from" end of branches
                    - C{St}     flow limits at "to" end of branches
                - C{u}  upper bounds
                    - C{Pmis}, C{Qmis}, C{Sf}, C{St}
        - C{lin}
            - C{mu}     shadow prices on linear constraints, by named block
                - C{l}  lower bounds
                    - C{Pmis}   real power mistmatch equations (DC only)
                    - C{Pf}     flow limits at "from" end of branches (DC only)
                    - C{Pt}     flow limits at "to" end of branches (DC only)
                    - C{PQh}    upper portion of gen PQ-capability curve(AC only)
                    - C{PQl}    lower portion of gen PQ-capability curve(AC only)
                    - C{vl}     constant power factor constraint for loads
                    - C{ycon}   basin constraints for CCV for pwl costs
                    - (other) any user defined constraint blocks
                - C{u}  upper bounds
                    - C{Pmis}, C{Pf}, C{Pf}, C{PQh}, C{PQl}, C{vl}, C{ycon},
                    - (other)
        - C{cost}       user defined cost values, by named block

    @see: L{runopf}, L{dcopf}, L{uopf}, L{caseformat}

    @author: Ray Zimmerman (PSERC Cornell)
    @author: Carlos E. Murillo-Sanchez (PSERC Cornell & Universidad
    Autonoma de Manizales)
    @author: Richard Lincoln
    """
    ##----- initialization -----
    t0 = time()  ## start timer

    ## process input arguments
    ppc, ppopt = opf_args2(*args)

    ## add zero columns to bus, gen, branch for multipliers, etc if needed
    nb = shape(ppc['bus'])[0]  ## number of buses
    nl = shape(ppc['branch'])[0]  ## number of branches
    ng = shape(ppc['gen'])[0]  ## number of dispatchable injections
    if shape(ppc['bus'])[1] < MU_VMIN + 1:
        ppc['bus'] = c_[ppc['bus'],
                        zeros((nb, MU_VMIN + 1 - shape(ppc['bus'])[1]))]

    if shape(ppc['gen'])[1] < MU_QMIN + 1:
        ppc['gen'] = c_[ppc['gen'],
                        zeros((ng, MU_QMIN + 1 - shape(ppc['gen'])[1]))]

    if shape(ppc['branch'])[1] < MU_ANGMAX + 1:
        ppc['branch'] = c_[ppc['branch'],
                           zeros(
                               (nl, MU_ANGMAX + 1 - shape(ppc['branch'])[1]))]

    ##-----  convert to internal numbering, remove out-of-service stuff  -----
    ppc = ext2int(ppc)

    ##-----  construct OPF model object  -----
    om = opf_setup(ppc, ppopt)

    ##-----  execute the OPF  -----
    results, success, raw = opf_execute(om, ppopt)

    ##-----  revert to original ordering, including out-of-service stuff  -----
    results = int2ext(results)

    ## zero out result fields of out-of-service gens & branches
    if len(results['order']['gen']['status']['off']) > 0:
        results['gen'][ix_(results['order']['gen']['status']['off'],
                           [PG, QG, MU_PMAX, MU_PMIN])] = 0

    if len(results['order']['branch']['status']['off']) > 0:
        results['branch'][ix_(
            results['order']['branch']['status']['off'],
            [PF, QF, PT, QT, MU_SF, MU_ST, MU_ANGMIN, MU_ANGMAX])] = 0

    ##-----  finish preparing output  -----
    et = time() - t0  ## compute elapsed time

    results['et'] = et
    results['success'] = success
    results['raw'] = raw

    return results
Exemple #17
0
def runpf(casedata=None, ppopt=None, fname='', solvedcase=''):
    """Runs a power flow.

    Runs a power flow [full AC Newton's method by default] and optionally
    returns the solved values in the data matrices, a flag which is C{True} if
    the algorithm was successful in finding a solution, and the elapsed
    time in seconds. All input arguments are optional. If C{casename} is
    provided it specifies the name of the input data file or dict
    containing the power flow data. The default value is 'case9'.

    If the ppopt is provided it overrides the default PYPOWER options
    vector and can be used to specify the solution algorithm and output
    options among other things. If the 3rd argument is given the pretty
    printed output will be appended to the file whose name is given in
    C{fname}. If C{solvedcase} is specified the solved case will be written
    to a case file in PYPOWER format with the specified name. If C{solvedcase}
    ends with '.mat' it saves the case as a MAT-file otherwise it saves it
    as a Python-file.

    If the C{ENFORCE_Q_LIMS} options is set to C{True} [default is false] then
    if any generator reactive power limit is violated after running the AC
    power flow, the corresponding bus is converted to a PQ bus, with Qg at
    the limit, and the case is re-run. The voltage magnitude at the bus
    will deviate from the specified value in order to satisfy the reactive
    power limit. If the reference bus is converted to PQ, the first
    remaining PV bus will be used as the slack bus for the next iteration.
    This may result in the real power output at this generator being
    slightly off from the specified values.

    Enforcing of generator Q limits inspired by contributions from Mu Lin,
    Lincoln University, New Zealand (1/14/05).

    @author: Ray Zimmerman (PSERC Cornell)
    """
    ## default arguments
    if casedata is None:
        casedata = join(dirname(__file__), 'case9')
    ppopt = ppoption(ppopt)

    ## options
    verbose = ppopt["VERBOSE"]
    qlim = ppopt["ENFORCE_Q_LIMS"]  ## enforce Q limits on gens?
    dc = ppopt["PF_DC"]             ## use DC formulation?

    ## read data
    ppc = loadcase(casedata)

    ## add zero columns to branch for flows if needed
    if ppc["branch"].shape[1] < QT:
        ppc["branch"] = c_[ppc["branch"],
                           zeros((ppc["branch"].shape[0],
                                  QT - ppc["branch"].shape[1] + 1))]

    ## convert to internal indexing
    ppc = ext2int(ppc)
    baseMVA, bus, gen, branch = \
        ppc["baseMVA"], ppc["bus"], ppc["gen"], ppc["branch"]

    ## get bus index lists of each type of bus
    ref, pv, pq = bustypes(bus, gen)

    ## generator info
    on = find(gen[:, GEN_STATUS] > 0)      ## which generators are on?
    gbus = gen[on, GEN_BUS].astype(int)    ## what buses are they at?

    ##-----  run the power flow  -----
    t0 = time()
    if verbose > 0:
        v = ppver('all')
        stdout.write('PYPOWER Version %s, %s' % (v["Version"], v["Date"]))

    if dc:                               # DC formulation
        if verbose:
            stdout.write(' -- DC Power Flow\n')

        ## initial state
        Va0 = bus[:, VA] * (pi / 180)

        ## build B matrices and phase shift injections
        B, Bf, Pbusinj, Pfinj = makeBdc(baseMVA, bus, branch)

        ## compute complex bus power injections [generation - load]
        ## adjusted for phase shifters and real shunts
        Pbus = makeSbus(baseMVA, bus, gen).real - Pbusinj - bus[:, GS] / baseMVA

        ## "run" the power flow
        Va = dcpf(B, Pbus, Va0, ref, pv, pq)

        ## update data matrices with solution
        branch[:, [QF, QT]] = zeros((branch.shape[0], 2))
        branch[:, PF] = (Bf * Va + Pfinj) * baseMVA
        branch[:, PT] = -branch[:, PF]
        bus[:, VM] = ones(bus.shape[0])
        bus[:, VA] = Va * (180 / pi)
        ## update Pg for slack generator (1st gen at ref bus)
        ## (note: other gens at ref bus are accounted for in Pbus)
        ##      Pg = Pinj + Pload + Gs
        ##      newPg = oldPg + newPinj - oldPinj
        refgen = zeros(len(ref), dtype=int)
        for k in range(len(ref)):
            temp = find(gbus == ref[k])
            refgen[k] = on[temp[0]]
        gen[refgen, PG] = gen[refgen, PG] + (B[ref, :] * Va - Pbus[ref]) * baseMVA

        success = 1
    else:                                ## AC formulation
        alg = ppopt['PF_ALG']
        if verbose > 0:
            if alg == 1:
                solver = 'Newton'
            elif alg == 2:
                solver = 'fast-decoupled, XB'
            elif alg == 3:
                solver = 'fast-decoupled, BX'
            elif alg == 4:
                solver = 'Gauss-Seidel'
            else:
                solver = 'unknown'
            print(' -- AC Power Flow (%s)\n' % solver)

        ## initial state
        # V0    = ones(bus.shape[0])            ## flat start
        V0  = bus[:, VM] * exp(1j * pi/180 * bus[:, VA])
        V0[gbus] = gen[on, VG] / abs(V0[gbus]) * V0[gbus]

        if qlim:
            ref0 = ref                         ## save index and angle of
            Varef0 = bus[ref0, VA]             ##   original reference bus(es)
            limited = []                       ## list of indices of gens @ Q lims
            fixedQg = zeros(gen.shape[0])      ## Qg of gens at Q limits

        repeat = True
        while repeat:
            ## build admittance matrices
            Ybus, Yf, Yt = makeYbus(baseMVA, bus, branch)

            ## compute complex bus power injections [generation - load]
            Sbus = makeSbus(baseMVA, bus, gen)

            ## run the power flow
            alg = ppopt["PF_ALG"]
            if alg == 1:
                V, success, _ = newtonpf(Ybus, Sbus, V0, ref, pv, pq, ppopt)
            elif alg == 2 or alg == 3:
                Bp, Bpp = makeB(baseMVA, bus, branch, alg)
                V, success, _ = fdpf(Ybus, Sbus, V0, Bp, Bpp, ref, pv, pq, ppopt)
            elif alg == 4:
                V, success, _ = gausspf(Ybus, Sbus, V0, ref, pv, pq, ppopt)
            else:
                stderr.write('Only Newton''s method, fast-decoupled, and '
                             'Gauss-Seidel power flow algorithms currently '
                             'implemented.\n')

            ## update data matrices with solution
            bus, gen, branch = pfsoln(baseMVA, bus, gen, branch, Ybus, Yf, Yt, V, ref, pv, pq)

            if qlim:             ## enforce generator Q limits
                ## find gens with violated Q constraints
                gen_status = gen[:, GEN_STATUS] > 0
                qg_max_lim = gen[:, QG] > gen[:, QMAX]
                qg_min_lim = gen[:, QG] < gen[:, QMIN]
                
                mx = find( gen_status & qg_max_lim )
                mn = find( gen_status & qg_min_lim )
                
                if len(mx) > 0 or len(mn) > 0:  ## we have some Q limit violations
                    # No PV generators
                    if len(pv) == 0:
                        if verbose:
                            if len(mx) > 0:
                                print('Gen %d [only one left] exceeds upper Q limit : INFEASIBLE PROBLEM\n' % mx + 1)
                            else:
                                print('Gen %d [only one left] exceeds lower Q limit : INFEASIBLE PROBLEM\n' % mn + 1)

                        success = 0
                        break

                    ## one at a time?
                    if qlim == 2:    ## fix largest violation, ignore the rest
                        k = argmax(r_[gen[mx, QG] - gen[mx, QMAX],
                                      gen[mn, QMIN] - gen[mn, QG]])
                        if k > len(mx):
                            mn = mn[k - len(mx)]
                            mx = []
                        else:
                            mx = mx[k]
                            mn = []

                    if verbose and len(mx) > 0:
                        for i in range(len(mx)):
                            print('Gen ' + str(mx[i] + 1) + ' at upper Q limit, converting to PQ bus\n')

                    if verbose and len(mn) > 0:
                        for i in range(len(mn)):
                            print('Gen ' + str(mn[i] + 1) + ' at lower Q limit, converting to PQ bus\n')

                    ## save corresponding limit values
                    fixedQg[mx] = gen[mx, QMAX]
                    fixedQg[mn] = gen[mn, QMIN]
                    mx = r_[mx, mn].astype(int)

                    ## convert to PQ bus
                    gen[mx, QG] = fixedQg[mx]      ## set Qg to binding 
                    for i in range(len(mx)):            ## [one at a time, since they may be at same bus]
                        gen[mx[i], GEN_STATUS] = 0        ## temporarily turn off gen,
                        bi = gen[mx[i], GEN_BUS]   ## adjust load accordingly,
                        bus[bi, [PD, QD]] = (bus[bi, [PD, QD]] - gen[mx[i], [PG, QG]])
                    
                    if len(ref) > 1 and any(bus[gen[mx, GEN_BUS], BUS_TYPE] == REF):
                        raise ValueError('Sorry, PYPOWER cannot enforce Q '
                                         'limits for slack buses in systems '
                                         'with multiple slacks.')
                    
                    bus[gen[mx, GEN_BUS].astype(int), BUS_TYPE] = PQ   ## & set bus type to PQ

                    ## update bus index lists of each type of bus
                    ref_temp = ref
                    ref, pv, pq = bustypes(bus, gen)
                    if verbose and ref != ref_temp:
                        print('Bus %d is new slack bus\n' % ref)

                    limited = r_[limited, mx].astype(int)
                else:
                    repeat = 0 ## no more generator Q limits violated
            else:
                repeat = 0     ## don't enforce generator Q limits, once is enough

        if qlim and len(limited) > 0:
            ## restore injections from limited gens [those at Q limits]
            gen[limited, QG] = fixedQg[limited]    ## restore Qg value,
            for i in range(len(limited)):               ## [one at a time, since they may be at same bus]
                bi = gen[limited[i], GEN_BUS]           ## re-adjust load,
                bus[bi, [PD, QD]] = bus[bi, [PD, QD]] + gen[limited[i], [PG, QG]]
                gen[limited[i], GEN_STATUS] = 1           ## and turn gen back on
            
            if ref != ref0:
                ## adjust voltage angles to make original ref bus correct
                bus[:, VA] = bus[:, VA] - bus[ref0, VA] + Varef0

    ppc["et"] = time() - t0
    ppc["success"] = success

    ##-----  output results  -----
    ## convert back to original bus numbering & print results
    ppc["bus"], ppc["gen"], ppc["branch"] = bus, gen, branch
    results = int2ext(ppc)

    ## zero out result fields of out-of-service gens & branches
    if len(results["order"]["gen"]["status"]["off"]) > 0:
        results["gen"][ix_(results["order"]["gen"]["status"]["off"], [PG, QG])] = 0

    if len(results["order"]["branch"]["status"]["off"]) > 0:
        results["branch"][ix_(results["order"]["branch"]["status"]["off"], [PF, QF, PT, QT])] = 0

    if fname:
        fd = None
        try:
            fd = open(fname, "a")
        except Exception as detail:
            stderr.write("Error opening %s: %s.\n" % (fname, detail))
        finally:
            if fd is not None:
                printpf(results, fd, ppopt)
                fd.close()
    else:
        printpf(results, stdout, ppopt)

    ## save solved case
    if solvedcase:
        savecase(solvedcase, results)

    return results, success
Exemple #18
0
def t_ext2int2ext(quiet=False):
    """Tests C{ext2int} and C{int2ext}.

    @author: Ray Zimmerman (PSERC Cornell)
    @author: Richard Lincoln
    """
    t_begin(85, quiet)

    ##-----  ppc = e2i_data/i2e_data(ppc)  -----
    t = 'ppc = e2i_data(ppc) : '
    ppce = loadcase(t_case_ext())
    ppci = loadcase(t_case_int())
    ppc = e2i_data(ppce)
    t_is(ppc['bus'], ppci['bus'], 12, [t, 'bus'])
    t_is(ppc['branch'], ppci['branch'], 12, [t, 'branch'])
    t_is(ppc['gen'], ppci['gen'], 12, [t, 'gen'])
    t_is(ppc['gencost'], ppci['gencost'], 12, [t, 'gencost'])
    t_is(ppc['areas'], ppci['areas'], 12, [t, 'areas'])
    t_is(ppc['A'], ppci['A'], 12, [t, 'A'])
    t_is(ppc['N'], ppci['N'], 12, [t, 'N'])
    t = 'ppc = e2i_data(ppc) - repeat : '
    ppc = e2i_data(ppc)
    t_is(ppc['bus'], ppci['bus'], 12, [t, 'bus'])
    t_is(ppc['branch'], ppci['branch'], 12, [t, 'branch'])
    t_is(ppc['gen'], ppci['gen'], 12, [t, 'gen'])
    t_is(ppc['gencost'], ppci['gencost'], 12, [t, 'gencost'])
    t_is(ppc['areas'], ppci['areas'], 12, [t, 'areas'])
    t_is(ppc['A'], ppci['A'], 12, [t, 'A'])
    t_is(ppc['N'], ppci['N'], 12, [t, 'N'])
    t = 'ppc = i2e_data(ppc) : '
    ppc = i2e_data(ppc)
    t_is(ppc['bus'], ppce['bus'], 12, [t, 'bus'])
    t_is(ppc['branch'], ppce['branch'], 12, [t, 'branch'])
    t_is(ppc['gen'], ppce['gen'], 12, [t, 'gen'])
    t_is(ppc['gencost'], ppce['gencost'], 12, [t, 'gencost'])
    t_is(ppc['areas'], ppce['areas'], 12, [t, 'areas'])
    t_is(ppc['A'], ppce['A'], 12, [t, 'A'])
    t_is(ppc['N'], ppce['N'], 12, [t, 'N'])

    ##-----  val = e2i_data/i2e_data(ppc, val, ...)  -----
    t = 'val = e2i_data(ppc, val, \'bus\')'
    ppc = e2i_data(ppce)
    got = e2i_data(ppc, ppce['xbus'], 'bus')
    ex = ppce['xbus']
    ex = delete(ex, 5, 0)
    t_is(got, ex, 12, t)
    t = 'val = i2e_data(ppc, val, oldval, \'bus\')'
    tmp = ones(ppce['xbus'].shape)
    tmp[5, :] = ppce['xbus'][5, :]
    got = i2e_data(ppc, ex, tmp, 'bus')
    t_is(got, ppce['xbus'], 12, t)

    t = 'val = e2i_data(ppc, val, \'bus\', 1)'
    got = e2i_data(ppc, ppce['xbus'], 'bus', 1)
    ex = ppce['xbus']
    ex = delete(ex, 5, 1)
    t_is(got, ex, 12, t)
    t = 'val = i2e_data(ppc, val, oldval, \'bus\', 1)'
    tmp = ones(ppce['xbus'].shape)
    tmp[:, 5] = ppce['xbus'][:, 5]
    got = i2e_data(ppc, ex, tmp, 'bus', 1)
    t_is(got, ppce['xbus'], 12, t)

    t = 'val = e2i_data(ppc, val, \'gen\')'
    got = e2i_data(ppc, ppce['xgen'], 'gen')
    ex = ppce['xgen'][[3, 1, 0], :]
    t_is(got, ex, 12, t)
    t = 'val = i2e_data(ppc, val, oldval, \'gen\')'
    tmp = ones(ppce['xgen'].shape)
    tmp[2, :] = ppce['xgen'][2, :]
    got = i2e_data(ppc, ex, tmp, 'gen')
    t_is(got, ppce['xgen'], 12, t)

    t = 'val = e2i_data(ppc, val, \'gen\', 1)'
    got = e2i_data(ppc, ppce['xgen'], 'gen', 1)
    ex = ppce['xgen'][:, [3, 1, 0]]
    t_is(got, ex, 12, t)
    t = 'val = i2e_data(ppc, val, oldval, \'gen\', 1)'
    tmp = ones(ppce['xgen'].shape)
    tmp[:, 2] = ppce['xgen'][:, 2]
    got = i2e_data(ppc, ex, tmp, 'gen', 1)
    t_is(got, ppce['xgen'], 12, t)

    t = 'val = e2i_data(ppc, val, \'branch\')'
    got = e2i_data(ppc, ppce['xbranch'], 'branch')
    ex = ppce['xbranch']
    ex = delete(ex, 6, 0)
    t_is(got, ex, 12, t)
    t = 'val = i2e_data(ppc, val, oldval, \'branch\')'
    tmp = ones(ppce['xbranch'].shape)
    tmp[6, :] = ppce['xbranch'][6, :]
    got = i2e_data(ppc, ex, tmp, 'branch')
    t_is(got, ppce['xbranch'], 12, t)

    t = 'val = e2i_data(ppc, val, \'branch\', 1)'
    got = e2i_data(ppc, ppce['xbranch'], 'branch', 1)
    ex = ppce['xbranch']
    ex = delete(ex, 6, 1)
    t_is(got, ex, 12, t)
    t = 'val = i2e_data(ppc, val, oldval, \'branch\', 1)'
    tmp = ones(ppce['xbranch'].shape)
    tmp[:, 6] = ppce['xbranch'][:, 6]
    got = i2e_data(ppc, ex, tmp, 'branch', 1)
    t_is(got, ppce['xbranch'], 12, t)

    t = 'val = e2i_data(ppc, val, {\'branch\', \'gen\', \'bus\'})'
    got = e2i_data(ppc, ppce['xrows'], ['branch', 'gen', 'bus'])
    ex = r_[ppce['xbranch'][list(range(6)) + list(range(7, 10)), :4],
            ppce['xgen'][[3, 1, 0], :],
            ppce['xbus'][list(range(5)) + list(range(6, 10)), :4],
            -1 * ones((2, 4))]
    t_is(got, ex, 12, t)
    t = 'val = i2e_data(ppc, val, oldval, {\'branch\', \'gen\', \'bus\'})'
    tmp1 = ones(ppce['xbranch'][:, :4].shape)
    tmp1[6, :4] = ppce['xbranch'][6, :4]
    tmp2 = ones(ppce['xgen'].shape)
    tmp2[2, :] = ppce['xgen'][2, :]
    tmp3 = ones(ppce['xbus'][:, :4].shape)
    tmp3[5, :4] = ppce['xbus'][5, :4]
    tmp = r_[tmp1, tmp2, tmp3]
    got = i2e_data(ppc, ex, tmp, ['branch', 'gen', 'bus'])
    t_is(got, ppce['xrows'], 12, t)

    t = 'val = e2i_data(ppc, val, {\'branch\', \'gen\', \'bus\'}, 1)'
    got = e2i_data(ppc, ppce['xcols'], ['branch', 'gen', 'bus'], 1)
    ex = r_[ppce['xbranch'][list(range(6)) + list(range(7, 10)), :4],
            ppce['xgen'][[3, 1, 0], :],
            ppce['xbus'][list(range(5)) + list(range(6, 10)), :4],
            -1 * ones((2, 4))].T
    t_is(got, ex, 12, t)
    t = 'val = i2e_data(ppc, val, oldval, {\'branch\', \'gen\', \'bus\'}, 1)'
    tmp1 = ones(ppce['xbranch'][:, :4].shape)
    tmp1[6, :4] = ppce['xbranch'][6, :4]
    tmp2 = ones(ppce['xgen'].shape)
    tmp2[2, :] = ppce['xgen'][2, :]
    tmp3 = ones(ppce['xbus'][:, :4].shape)
    tmp3[5, :4] = ppce['xbus'][5, :4]
    tmp = r_[tmp1, tmp2, tmp3].T
    got = i2e_data(ppc, ex, tmp, ['branch', 'gen', 'bus'], 1)
    t_is(got, ppce['xcols'], 12, t)

    ##-----  ppc = e2i_field/i2e_field(ppc, field, ...)  -----
    t = 'ppc = e2i_field(ppc, field, \'bus\')'
    ppc = e2i_field(ppce)
    ex = ppce['xbus']
    ex = delete(ex, 5, 0)
    got = e2i_field(ppc, 'xbus', 'bus')
    t_is(got['xbus'], ex, 12, t)
    t = 'ppc = i2e_field(ppc, field, \'bus\')'
    got = i2e_field(got, 'xbus', ordering='bus')
    t_is(got['xbus'], ppce['xbus'], 12, t)

    t = 'ppc = e2i_field(ppc, field, \'bus\', 1)'
    ex = ppce['xbus']
    ex = delete(ex, 5, 1)
    got = e2i_field(ppc, 'xbus', 'bus', 1)
    t_is(got['xbus'], ex, 12, t)
    t = 'ppc = i2e_field(ppc, field, \'bus\', 1)'
    got = i2e_field(got, 'xbus', ordering='bus', dim=1)
    t_is(got['xbus'], ppce['xbus'], 12, t)

    t = 'ppc = e2i_field(ppc, field, \'gen\')'
    ex = ppce['xgen'][[3, 1, 0], :]
    got = e2i_field(ppc, 'xgen', 'gen')
    t_is(got['xgen'], ex, 12, t)
    t = 'ppc = i2e_field(ppc, field, \'gen\')'
    got = i2e_field(got, 'xgen', ordering='gen')
    t_is(got['xgen'], ppce['xgen'], 12, t)

    t = 'ppc = e2i_field(ppc, field, \'gen\', 1)'
    ex = ppce['xgen'][:, [3, 1, 0]]
    got = e2i_field(ppc, 'xgen', 'gen', 1)
    t_is(got['xgen'], ex, 12, t)
    t = 'ppc = i2e_field(ppc, field, \'gen\', 1)'
    got = i2e_field(got, 'xgen', ordering='gen', dim=1)
    t_is(got['xgen'], ppce['xgen'], 12, t)

    t = 'ppc = e2i_field(ppc, field, \'branch\')'
    ex = ppce['xbranch']
    ex = delete(ex, 6, 0)
    got = e2i_field(ppc, 'xbranch', 'branch')
    t_is(got['xbranch'], ex, 12, t)
    t = 'ppc = i2e_field(ppc, field, \'branch\')'
    got = i2e_field(got, 'xbranch', ordering='branch')
    t_is(got['xbranch'], ppce['xbranch'], 12, t)

    t = 'ppc = e2i_field(ppc, field, \'branch\', 1)'
    ex = ppce['xbranch']
    ex = delete(ex, 6, 1)
    got = e2i_field(ppc, 'xbranch', 'branch', 1)
    t_is(got['xbranch'], ex, 12, t)
    t = 'ppc = i2e_field(ppc, field, \'branch\', 1)'
    got = i2e_field(got, 'xbranch', ordering='branch', dim=1)
    t_is(got['xbranch'], ppce['xbranch'], 12, t)

    t = 'ppc = e2i_field(ppc, field, {\'branch\', \'gen\', \'bus\'})'
    ex = r_[ppce['xbranch'][list(range(6)) + list(range(7, 10)), :4],
            ppce['xgen'][[3, 1, 0], :],
            ppce['xbus'][list(range(5)) + list(range(6, 10)), :4],
            -1 * ones((2, 4))]
    got = e2i_field(ppc, 'xrows', ['branch', 'gen', 'bus'])
    t_is(got['xrows'], ex, 12, t)
    t = 'ppc = i2e_field(ppc, field, {\'branch\', \'gen\', \'bus\'})'
    got = i2e_field(got, 'xrows', ordering=['branch', 'gen', 'bus'])
    t_is(got['xrows'], ppce['xrows'], 12, t)

    t = 'ppc = e2i_field(ppc, field, {\'branch\', \'gen\', \'bus\'}, 1)'
    ex = r_[ppce['xbranch'][list(range(6)) + list(range(7, 10)), :4],
            ppce['xgen'][[3, 1, 0], :],
            ppce['xbus'][list(range(5)) + list(range(6, 10)), :4],
            -1 * ones((2, 4))].T
    got = e2i_field(ppc, 'xcols', ['branch', 'gen', 'bus'], 1)
    t_is(got['xcols'], ex, 12, t)
    t = 'ppc = i2e_field(ppc, field, {\'branch\', \'gen\', \'bus\'})'
    got = i2e_field(got, 'xcols', ordering=['branch', 'gen', 'bus'], dim=1)
    t_is(got['xcols'], ppce['xcols'], 12, t)

    t = 'ppc = e2i_field(ppc, {\'field1\', \'field2\'}, ordering)'
    ex = ppce['x']['more'][[3, 1, 0], :]
    got = e2i_field(ppc, ['x', 'more'], 'gen')
    t_is(got['x']['more'], ex, 12, t)
    t = 'ppc = i2e_field(ppc, {\'field1\', \'field2\'}, ordering)'
    got = i2e_field(got, ['x', 'more'], ordering='gen')
    t_is(got['x']['more'], ppce['x']['more'], 12, t)

    t = 'ppc = e2i_field(ppc, {\'field1\', \'field2\'}, ordering, 1)'
    ex = ppce['x']['more'][:, [3, 1, 0]]
    got = e2i_field(ppc, ['x', 'more'], 'gen', 1)
    t_is(got['x']['more'], ex, 12, t)
    t = 'ppc = i2e_field(ppc, {\'field1\', \'field2\'}, ordering, 1)'
    got = i2e_field(got, ['x', 'more'], ordering='gen', dim=1)
    t_is(got['x']['more'], ppce['x']['more'], 12, t)

    ##-----  more ppc = ext2int/int2ext(ppc)  -----
    t = 'ppc = ext2int(ppc) - bus/gen/branch only : '
    ppce = loadcase(t_case_ext())
    ppci = loadcase(t_case_int())
    del ppce['gencost']
    del ppce['areas']
    del ppce['A']
    del ppce['N']
    del ppci['gencost']
    del ppci['areas']
    del ppci['A']
    del ppci['N']
    ppc = ext2int(ppce)
    t_is(ppc['bus'], ppci['bus'], 12, [t, 'bus'])
    t_is(ppc['branch'], ppci['branch'], 12, [t, 'branch'])
    t_is(ppc['gen'], ppci['gen'], 12, [t, 'gen'])

    t = 'ppc = ext2int(ppc) - no areas/A : '
    ppce = loadcase(t_case_ext())
    ppci = loadcase(t_case_int())
    del ppce['areas']
    del ppce['A']
    del ppci['areas']
    del ppci['A']
    ppc = ext2int(ppce)
    t_is(ppc['bus'], ppci['bus'], 12, [t, 'bus'])
    t_is(ppc['branch'], ppci['branch'], 12, [t, 'branch'])
    t_is(ppc['gen'], ppci['gen'], 12, [t, 'gen'])
    t_is(ppc['gencost'], ppci['gencost'], 12, [t, 'gencost'])
    t_is(ppc['N'], ppci['N'], 12, [t, 'N'])

    t = 'ppc = ext2int(ppc) - Qg cost, no N : '
    ppce = loadcase(t_case_ext())
    ppci = loadcase(t_case_int())
    del ppce['N']
    del ppci['N']
    ppce['gencost'] = c_[ppce['gencost'], ppce['gencost']]
    ppci['gencost'] = c_[ppci['gencost'], ppci['gencost']]
    ppc = ext2int(ppce)
    t_is(ppc['bus'], ppci['bus'], 12, [t, 'bus'])
    t_is(ppc['branch'], ppci['branch'], 12, [t, 'branch'])
    t_is(ppc['gen'], ppci['gen'], 12, [t, 'gen'])
    t_is(ppc['gencost'], ppci['gencost'], 12, [t, 'gencost'])
    t_is(ppc['areas'], ppci['areas'], 12, [t, 'areas'])
    t_is(ppc['A'], ppci['A'], 12, [t, 'A'])

    t = 'ppc = ext2int(ppc) - A, N are DC sized : '
    ppce = loadcase(t_case_ext())
    ppci = loadcase(t_case_int())
    eVmQgcols = list(range(10, 20)) + list(range(24, 28))
    iVmQgcols = list(range(9, 18)) + list(range(21, 24))
    ppce['A'] = delete(ppce['A'], eVmQgcols, 1)
    ppce['N'] = delete(ppce['N'], eVmQgcols, 1)
    ppci['A'] = delete(ppci['A'], iVmQgcols, 1)
    ppci['N'] = delete(ppci['N'], iVmQgcols, 1)
    ppc = ext2int(ppce)
    t_is(ppc['bus'], ppci['bus'], 12, [t, 'bus'])
    t_is(ppc['branch'], ppci['branch'], 12, [t, 'branch'])
    t_is(ppc['gen'], ppci['gen'], 12, [t, 'gen'])
    t_is(ppc['gencost'], ppci['gencost'], 12, [t, 'gencost'])
    t_is(ppc['areas'], ppci['areas'], 12, [t, 'areas'])
    t_is(ppc['A'], ppci['A'], 12, [t, 'A'])
    t_is(ppc['N'], ppci['N'], 12, [t, 'N'])
    t = 'ppc = int2ext(ppc) - A, N are DC sized : '
    ppc = int2ext(ppc)
    t_is(ppc['bus'], ppce['bus'], 12, [t, 'bus'])
    t_is(ppc['branch'], ppce['branch'], 12, [t, 'branch'])
    t_is(ppc['gen'], ppce['gen'], 12, [t, 'gen'])
    t_is(ppc['gencost'], ppce['gencost'], 12, [t, 'gencost'])
    t_is(ppc['areas'], ppce['areas'], 12, [t, 'areas'])
    t_is(ppc['A'], ppce['A'], 12, [t, 'A'])
    t_is(ppc['N'], ppce['N'], 12, [t, 'N'])

    t_end()
Exemple #19
0
def opf(*args):
    """Solves an optimal power flow.

    Returns a C{results} dict.

    The data for the problem can be specified in one of three ways:
      1. a string (ppc) containing the file name of a PYPOWER case
      which defines the data matrices baseMVA, bus, gen, branch, and
      gencost (areas is not used at all, it is only included for
      backward compatibility of the API).
      2. a dict (ppc) containing the data matrices as fields.
      3. the individual data matrices themselves.

    The optional user parameters for user constraints (C{A, l, u}), user costs
    (C{N, fparm, H, Cw}), user variable initializer (C{z0}), and user variable
    limits (C{zl, zu}) can also be specified as fields in a case dict,
    either passed in directly or defined in a case file referenced by name.

    When specified, C{A, l, u} represent additional linear constraints on the
    optimization variables, C{l <= A*[x z] <= u}. If the user specifies an C{A}
    matrix that has more columns than the number of "C{x}" (OPF) variables,
    then there are extra linearly constrained "C{z}" variables. For an
    explanation of the formulation used and instructions for forming the
    C{A} matrix, see the MATPOWER manual.

    A generalized cost on all variables can be applied if input arguments
    C{N}, C{fparm}, C{H} and C{Cw} are specified. First, a linear transformation
    of the optimization variables is defined by means of C{r = N * [x z]}.
    Then, to each element of C{r} a function is applied as encoded in the
    C{fparm} matrix (see MATPOWER manual). If the resulting vector is named
    C{w}, then C{H} and C{Cw} define a quadratic cost on w:
    C{(1/2)*w'*H*w + Cw * w}. C{H} and C{N} should be sparse matrices and C{H}
    should also be symmetric.

    The optional C{ppopt} vector specifies PYPOWER options. If the OPF
    algorithm is not explicitly set in the options PYPOWER will use the default
    solver, based on a primal-dual interior point method. For the AC OPF this
    is C{OPF_ALG = 560}. For the DC OPF, the default is C{OPF_ALG_DC = 200}.
    See L{ppoption} for more details on the available OPF solvers and other OPF
    options and their default values.

    The solved case is returned in a single results dict (described
    below). Also returned are the final objective function value (C{f}) and a
    flag which is C{True} if the algorithm was successful in finding a solution
    (success). Additional optional return values are an algorithm specific
    return status (C{info}), elapsed time in seconds (C{et}), the constraint
    vector (C{g}), the Jacobian matrix (C{jac}), and the vector of variables
    (C{xr}) as well as the constraint multipliers (C{pimul}).

    The single results dict is a PYPOWER case struct (ppc) with the
    usual baseMVA, bus, branch, gen, gencost fields, along with the
    following additional fields:

        - C{order}      see 'help ext2int' for details of this field
        - C{et}         elapsed time in seconds for solving OPF
        - C{success}    1 if solver converged successfully, 0 otherwise
        - C{om}         OPF model object, see 'help opf_model'
        - C{x}          final value of optimization variables (internal order)
        - C{f}          final objective function value
        - C{mu}         shadow prices on ...
            - C{var}
                - C{l}  lower bounds on variables
                - C{u}  upper bounds on variables
            - C{nln}
                - C{l}  lower bounds on nonlinear constraints
                - C{u}  upper bounds on nonlinear constraints
            - C{lin}
                - C{l}  lower bounds on linear constraints
                - C{u}  upper bounds on linear constraints
        - C{g}          (optional) constraint values
        - C{dg}         (optional) constraint 1st derivatives
        - C{df}         (optional) obj fun 1st derivatives (not yet implemented)
        - C{d2f}        (optional) obj fun 2nd derivatives (not yet implemented)
        - C{raw}        raw solver output in form returned by MINOS, and more
            - C{xr}     final value of optimization variables
            - C{pimul}  constraint multipliers
            - C{info}   solver specific termination code
            - C{output} solver specific output information
               - C{alg} algorithm code of solver used
        - C{var}
            - C{val}    optimization variable values, by named block
                - C{Va}     voltage angles
                - C{Vm}     voltage magnitudes (AC only)
                - C{Pg}     real power injections
                - C{Qg}     reactive power injections (AC only)
                - C{y}      constrained cost variable (only if have pwl costs)
                - (other) any user defined variable blocks
            - C{mu}     variable bound shadow prices, by named block
                - C{l}  lower bound shadow prices
                    - C{Va}, C{Vm}, C{Pg}, C{Qg}, C{y}, (other)
                - C{u}  upper bound shadow prices
                    - C{Va}, C{Vm}, C{Pg}, C{Qg}, C{y}, (other)
        - C{nln}    (AC only)
            - C{mu}     shadow prices on nonlinear constraints, by named block
                - C{l}  lower bounds
                    - C{Pmis}   real power mismatch equations
                    - C{Qmis}   reactive power mismatch equations
                    - C{Sf}     flow limits at "from" end of branches
                    - C{St}     flow limits at "to" end of branches
                - C{u}  upper bounds
                    - C{Pmis}, C{Qmis}, C{Sf}, C{St}
        - C{lin}
            - C{mu}     shadow prices on linear constraints, by named block
                - C{l}  lower bounds
                    - C{Pmis}   real power mistmatch equations (DC only)
                    - C{Pf}     flow limits at "from" end of branches (DC only)
                    - C{Pt}     flow limits at "to" end of branches (DC only)
                    - C{PQh}    upper portion of gen PQ-capability curve(AC only)
                    - C{PQl}    lower portion of gen PQ-capability curve(AC only)
                    - C{vl}     constant power factor constraint for loads
                    - C{ycon}   basin constraints for CCV for pwl costs
                    - (other) any user defined constraint blocks
                - C{u}  upper bounds
                    - C{Pmis}, C{Pf}, C{Pf}, C{PQh}, C{PQl}, C{vl}, C{ycon},
                    - (other)
        - C{cost}       user defined cost values, by named block

    @see: L{runopf}, L{dcopf}, L{uopf}, L{caseformat}

    @author: Ray Zimmerman (PSERC Cornell)
    @author: Carlos E. Murillo-Sanchez (PSERC Cornell & Universidad
    Autonoma de Manizales)
    @author: Richard Lincoln
    """
    ##----- initialization -----
    t0 = time()         ## start timer

    ## process input arguments
    ppc, ppopt = opf_args2(*args)

    ## add zero columns to bus, gen, branch for multipliers, etc if needed
    nb   = shape(ppc['bus'])[0]    ## number of buses
    nl   = shape(ppc['branch'])[0] ## number of branches
    ng   = shape(ppc['gen'])[0]    ## number of dispatchable injections
    if shape(ppc['bus'])[1] < MU_VMIN + 1:
        ppc['bus'] = c_[ppc['bus'], zeros((nb, MU_VMIN + 1 - shape(ppc['bus'])[1]))]

    if shape(ppc['gen'])[1] < MU_QMIN + 1:
        ppc['gen'] = c_[ppc['gen'], zeros((ng, MU_QMIN + 1 - shape(ppc['gen'])[1]))]

    if shape(ppc['branch'])[1] < MU_ANGMAX + 1:
        ppc['branch'] = c_[ppc['branch'], zeros((nl, MU_ANGMAX + 1 - shape(ppc['branch'])[1]))]

    ##-----  convert to internal numbering, remove out-of-service stuff  -----
    ppc = ext2int(ppc)

    ##-----  construct OPF model object  -----
    om = opf_setup(ppc, ppopt)

    ##-----  execute the OPF  -----
    results, success, raw = opf_execute(om, ppopt)

    ##-----  revert to original ordering, including out-of-service stuff  -----
    results = int2ext(results)

    ## zero out result fields of out-of-service gens & branches
    if len(results['order']['gen']['status']['off']) > 0:
        results['gen'][ ix_(results['order']['gen']['status']['off'], [PG, QG, MU_PMAX, MU_PMIN]) ] = 0

    if len(results['order']['branch']['status']['off']) > 0:
        results['branch'][ ix_(results['order']['branch']['status']['off'], [PF, QF, PT, QT, MU_SF, MU_ST, MU_ANGMIN, MU_ANGMAX]) ] = 0

    ##-----  finish preparing output  -----
    et = time() - t0      ## compute elapsed time

    results['et'] = et
    results['success'] = success
    results['raw'] = raw

    return results
Exemple #20
0
def solveropfnlp_4(ppc, solver="ipopt"):
    if solver == "ipopt":
        opt = SolverFactory(
            "ipopt",
            executable=
            "/home/iso/PycharmProjects/opfLC_python3/Python3/py_solvers/ipopt-linux64/ipopt"
        )
    if solver == "bonmin":
        opt = SolverFactory(
            "bonmin",
            executable=
            "/home/iso/PycharmProjects/opfLC_python3/Python3/py_solvers/bonmin-linux64/bonmin"
        )
    if solver == "knitro":
        opt = SolverFactory(
            "knitro",
            executable="D:/ICT/Artelys/Knitro 10.2.1/knitroampl/knitroampl")

    ppc = ext2int(ppc)  # convert to continuous indexing starting from 0

    # Gather information about the system
    # =============================================================
    baseMVA, bus, gen, branch = \
        ppc["baseMVA"], ppc["bus"], ppc["gen"], ppc["branch"]

    nb = bus.shape[0]  # number of buses
    ng = gen.shape[0]  # number of generators
    nl = branch.shape[0]  # number of lines

    # generator buses
    gb = tolist(np.array(gen[:, GEN_BUS]).astype(int))

    sb = find((bus[:, BUS_TYPE] == REF))  # slack bus index
    fr = branch[:, F_BUS].astype(int)  # from bus indices
    to = branch[:, T_BUS].astype(int)  # to bus indices

    tr0 = copy(branch[:, TAP])  # transformation ratios
    tr0[find(tr0 == 0)] = 1  # set to 1 transformation ratios that are 0
    tp = find(branch[:, TAP] != 0)  # lines with tap changers
    ntp = find(branch[:, TAP] == 0)  # lines without tap changers

    # Tap changer settings
    dudtap = 0.01  # Voltage per unit variation with tap changes
    tapmax = 10  # Highest tap changer setting
    tapmin = -10  # Lowest tap changer setting

    # Shunt element options
    stepmax = 1  # maximum step of the shunt element

    Bs0 = bus[:, BS] / baseMVA  # shunt elements susceptance
    sd = find(bus[:, BS] != 0)  # buses with shunt devices

    r = branch[:, BR_R]  # branch resistances
    x = branch[:, BR_X]  # branch reactances
    b = branch[:, BR_B]  # branch susceptances

    start_time = time.clock()

    # Admittance matrix computation
    # =============================================================
    # Set tap ratios and shunt elements to neutral position
    branch[:, TAP] = 1
    bus[:, BS] = 0

    y = makeYbus(baseMVA, bus, branch)[0]  # admittance matrix
    yk = 1. / (r + x * 1j)  # branch admittance
    yft = yk + 0.5j * b  # branch admittance + susceptance
    gk = yk.real  # branch resistance

    # Optimization
    # =============================================================
    branch[find(branch[:, RATE_A] == 0),
           RATE_A] = 9999  # set undefined Sflow limit to 9999
    Smax = branch[:, RATE_A] / baseMVA  # Max. Sflow

    # Power demand parameters
    Pd = bus[:, PD] / baseMVA
    Qd = bus[:, QD] / baseMVA

    # Max and min Pg and Qg
    Pg_max = zeros(nb)
    Pg_max[gb] = gen[:, PMAX] / baseMVA
    Pg_min = zeros(nb)
    Pg_min[gb] = gen[:, PMIN] / baseMVA
    Qg_max = zeros(nb)
    Qg_max[gb] = gen[:, QMAX] / baseMVA
    Qg_min = zeros(nb)
    Qg_min[gb] = gen[:, QMIN] / baseMVA

    # Vmax and Vmin vectors
    Vmax = bus[:, VMAX]
    Vmin = bus[:, VMIN]

    vm = bus[:, VM]
    va = bus[:, VA] * pi / 180

    # create a new optimization model
    model = ConcreteModel()

    # Define sets
    # ------------
    model.bus = Set(ordered=True, initialize=range(nb))  # Set of all buses
    model.gen = Set(ordered=True,
                    initialize=gb)  # Set of buses with generation
    model.line = Set(ordered=True, initialize=range(nl))  # Set of all lines
    model.taps = Set(ordered=True,
                     initialize=tp)  # Set of all lines with tap changers
    model.shunt = Set(ordered=True,
                      initialize=sd)  # Set of buses with shunt elements

    # Define variables
    # -----------------
    # Voltage magnitudes vector (vm)
    model.vm = Var(model.bus)

    # Voltage angles vector (va)
    model.va = Var(model.bus)

    # Reactive power generation, synchronous machines(SM) (Qg)
    model.Qg = Var(model.gen)
    Qg0 = zeros(nb)
    Qg0[gb] = gen[:, QG] / baseMVA

    # Active power generation, synchronous machines(SM) (Pg)
    model.Pg = Var(model.gen)
    Pg0 = zeros(nb)
    Pg0[gb] = gen[:, PG] / baseMVA

    # Active and reactive power from at all branches
    model.Pf = Var(model.line)
    model.Qf = Var(model.line)

    # Active and reactive power to at all branches
    model.Pt = Var(model.line)
    model.Qt = Var(model.line)

    # Transformation ratios
    model.tr = Var(model.taps)

    # Tap changer positions + their bounds
    model.tap = Var(model.taps, bounds=(tapmin, tapmax))

    # Shunt susceptance
    model.Bs = Var(model.shunt)

    # Shunt positions + their bounds
    model.s = Var(model.shunt, bounds=(0, stepmax))

    # Warm start the problem
    # ------------------------
    for i in range(nb):
        model.vm[i] = vm[i]
        model.va[i] = va[i]
        if i in gb:
            model.Pg[i] = Pg0[i]
            model.Qg[i] = Qg0[i]
    for i in range(nl):
        model.Pf[i] = vm[fr[i]] ** 2 * abs(yft[i]) / (tr0[i] ** 2) * np.cos(-ang(yft[i])) -\
            vm[fr[i]] * vm[to[i]] * abs(yk[i]) / tr0[i] * np.cos(va[fr[i]] - va[to[i]] - ang(yk[i]))
        model.Qf[i] = vm[fr[i]] ** 2 * abs(yft[i]) / (tr0[i] ** 2) * np.sin(-ang(yft[i])) -\
            vm[fr[i]] * vm[to[i]] * abs(yk[i]) / tr0[i] * np.sin(va[fr[i]] - va[to[i]] - ang(yk[i]))
        model.Pt[i] = vm[to[i]] ** 2 * abs(yft[i]) * np.cos(-ang(yft[i])) -\
            vm[to[i]] * vm[fr[i]] * abs(yk[i]) / tr0[i] * np.cos(va[to[i]] - va[fr[i]] - ang(yk[i]))
        model.Qt[i] = vm[to[i]] ** 2 * abs(yft[i]) * np.sin(-ang(yft[i])) -\
            vm[to[i]] * vm[fr[i]] * abs(yk[i]) / tr0[i] * np.sin(va[to[i]] - va[fr[i]] - ang(yk[i]))
    for i in tp:
        model.tr[i] = tr0[i]
    for i in sd:
        model.Bs[i] = Bs0[i]

    # Define constraints
    # ----------------------------

    # Equalities:
    # ------------

    # Active power flow equalities
    def powerflowact(model, i):
        bfrom_i = tp[find(fr[tp] == i)]  # branches from bus i with transformer
        bto_i = tp[find(to[tp] == i)]  # branches to bus i with transformer
        allbut_i = find(bus[:, BUS_I] != i)  # Set of other buses
        if i in gb:
            return model.Pg[i]-Pd[i] == sum(model.vm[i] * model.vm[j] * abs(y[i, j]) *
                                            cos(model.va[i] - model.va[j] - ang(y[i, j])) for j in allbut_i) - \
                   sum(model.vm[i] * model.vm[to[j]] * abs(yk[j]) * cos(model.va[i] -
                       model.va[to[j]] - ang(yk[j])) * (1 / model.tr[j] - 1) for j in bfrom_i) - \
                   sum(model.vm[i] * model.vm[fr[j]] * abs(yk[j]) * cos(model.va[i] -
                       model.va[fr[j]] - ang(yk[j])) * (1 / model.tr[j] - 1) for j in bto_i) + \
                   model.vm[i] ** 2 * (sum(abs(yk[j]) * (1 / model.tr[j]**2 - 1) *
                                           np.cos(- ang(yk[j])) for j in bfrom_i) + real(y[i, i]))
        else:
            return sum(model.vm[i] * model.vm[j] * abs(y[i, j]) *
                       cos(model.va[i] - model.va[j] - ang(y[i, j])) for j in allbut_i) - \
                   sum(model.vm[i] * model.vm[to[j]] * abs(yk[j]) * cos(model.va[i] -
                       model.va[to[j]] - ang(yk[j])) * (1 / model.tr[j] - 1) for j in bfrom_i) - \
                   sum(model.vm[i] * model.vm[fr[j]] * abs(yk[j]) * cos(model.va[i] -
                       model.va[fr[j]] - ang(yk[j])) * (1 / model.tr[j] - 1) for j in bto_i) + \
                   model.vm[i] ** 2 * (sum(abs(yk[j]) * (1 / model.tr[j]**2 - 1) *
                                           np.cos(- ang(yk[j])) for j in bfrom_i) + real(y[i, i])) == -Pd[i]

    model.const1 = Constraint(model.bus, rule=powerflowact)

    # Reactive power flow equalities
    def powerflowreact(model, i):
        bfrom_i = tp[find(fr[tp] == i)]  # branches from bus i with transformer
        bto_i = tp[find(to[tp] == i)]  # branches to bus i with transformer
        allbut_i = find(bus[:, BUS_I] != i)  # Set of other buses
        sh = sd[find(sd == i)]  # Detect shunt elements
        if i in gb:
            return model.Qg[i]-Qd[i] == \
                   sum(model.vm[i] * model.vm[j] * abs(y[i, j]) *
                       sin(model.va[i] - model.va[j] - ang(y[i, j])) for j in allbut_i) - \
                   sum(model.vm[i] * model.vm[to[j]] * abs(yk[j]) * sin(model.va[i] - model.va[to[j]] - ang(yk[j]))
                       * (1 / model.tr[j] - 1) for j in bfrom_i) - \
                   sum(model.vm[i] * model.vm[fr[j]] * abs(yk[j]) * sin(model.va[i] - model.va[fr[j]] - ang(yk[j]))
                       * (1 / model.tr[j] - 1) for j in bto_i) + \
                   model.vm[i] ** 2 * (sum(abs(yk[j]) * (1 / model.tr[j] ** 2 - 1) * np.sin(- ang(yk[j]))
                                           for j in bfrom_i) - imag(y[i, i]) - sum(model.Bs[j] for j in sh))
        else:
            return sum(model.vm[i] * model.vm[j] * abs(y[i, j]) *
                       sin(model.va[i] - model.va[j] - ang(y[i, j])) for j in allbut_i) - \
                   sum(model.vm[i] * model.vm[to[j]] * abs(yk[j]) * sin(model.va[i] -
                       model.va[to[j]] - ang(yk[j])) * (1 / model.tr[j] - 1) for j in bfrom_i) - \
                   sum(model.vm[i] * model.vm[fr[j]] * abs(yk[j]) * sin(model.va[i] -
                       model.va[fr[j]] - ang(yk[j])) * (1 / model.tr[j] - 1) for j in bto_i) + \
                   model.vm[i] ** 2 * (sum(abs(yk[j]) * (1 / model.tr[j] ** 2 - 1) * np.sin(- ang(yk[j]))
                                           for j in bfrom_i) - imag(y[i, i]) - sum(model.Bs[j] for j in sh)) == - Qd[i]

    model.const2 = Constraint(model.bus, rule=powerflowreact)

    # Active power from
    def pfrom(model, i):
        if i in tp:
            return model.Pf[i] == model.vm[fr[i]] ** 2 * abs(yft[i]) / (model.tr[i] ** 2) * np.cos(-ang(yft[i])) - \
                                  model.vm[fr[i]] * model.vm[to[i]] * abs(yk[i]) / model.tr[i] * \
                                  cos(model.va[fr[i]] - model.va[to[i]] - ang(yk[i]))
        else:
            return model.Pf[i] == model.vm[fr[i]] ** 2 * abs(yft[i]) / tr0[i] ** 2 * np.cos(-ang(yft[i])) - \
                                  model.vm[fr[i]] * model.vm[to[i]] * abs(yk[i]) / tr0[i] * \
                                  cos(model.va[fr[i]] - model.va[to[i]] - ang(yk[i]))

    model.const3 = Constraint(model.line, rule=pfrom)

    # Reactive power from
    def qfrom(model, i):
        if i in tp:
            return model.Qf[i] == model.vm[fr[i]] ** 2 * abs(yft[i]) / (model.tr[i] ** 2) * np.sin(-ang(yft[i])) - \
                                  model.vm[fr[i]] * model.vm[to[i]] * abs(yk[i]) / model.tr[i] * \
                                  sin(model.va[fr[i]] - model.va[to[i]] - ang(yk[i]))
        else:
            return model.Qf[i] == model.vm[fr[i]] ** 2 * abs(yft[i]) / tr0[i] ** 2 * np.sin(-ang(yft[i])) - \
                                  model.vm[fr[i]] * model.vm[to[i]] * abs(yk[i]) / tr0[i] * \
                                  sin(model.va[fr[i]] - model.va[to[i]] - ang(yk[i]))

    model.const4 = Constraint(model.line, rule=qfrom)

    # Active power to
    def pto(model, i):
        if i in tp:
            return model.Pt[i] == model.vm[to[i]] ** 2 * abs(yft[i]) * np.cos(-ang(yft[i])) - \
                                  model.vm[to[i]] * model.vm[fr[i]] * abs(yk[i]) / model.tr[i] * \
                                  cos(model.va[to[i]] - model.va[fr[i]] - ang(yk[i]))
        else:
            return model.Pt[i] == model.vm[to[i]] ** 2 * abs(yft[i]) * np.cos(-ang(yft[i])) - \
                                  model.vm[to[i]] * model.vm[fr[i]] * abs(yk[i]) / tr0[i] * \
                                  cos(model.va[to[i]] - model.va[fr[i]] - ang(yk[i]))

    model.const5 = Constraint(model.line, rule=pto)

    # Reactive power to
    def qto(model, i):
        if i in tp:
            return model.Qt[i] == model.vm[to[i]] ** 2 * abs(yft[i]) * np.sin(-ang(yft[i])) - \
                                  model.vm[to[i]] * model.vm[fr[i]] * abs(yk[i]) / model.tr[i] * \
                                  sin(model.va[to[i]] - model.va[fr[i]] - ang(yk[i]))
        else:
            return model.Qt[i] == model.vm[to[i]] ** 2 * abs(yft[i]) * np.sin(-ang(yft[i])) - \
                                  model.vm[to[i]] * model.vm[fr[i]] * abs(yk[i]) / tr0[i] * \
                                  sin(model.va[to[i]] - model.va[fr[i]] - ang(yk[i]))

    model.const6 = Constraint(model.line, rule=qto)

    # Slack bus phase angle
    model.const7 = Constraint(expr=model.va[sb[0]] == 0)

    # Transformation ratio equalities
    def trfunc(model, i):
        return model.tr[i] == 1 + dudtap * model.tap[i]

    model.const8 = Constraint(model.taps, rule=trfunc)

    # Shunt susceptance equality
    def shuntfunc(model, i):
        return model.Bs[i] == model.s[i] / stepmax * Bs0[i]

    model.const9 = Constraint(model.shunt, rule=shuntfunc)

    # Inequalities:
    # ----------------

    # Active power generator limits Pg_min <= Pg <= Pg_max
    def genplimits(model, i):
        return Pg_min[i] <= model.Pg[i] <= Pg_max[i]

    model.const10 = Constraint(model.gen, rule=genplimits)

    # Reactive power generator limits Qg_min <= Qg <= Qg_max
    def genqlimits(model, i):
        return Qg_min[i] <= model.Qg[i] <= Qg_max[i]

    model.const11 = Constraint(model.gen, rule=genqlimits)

    # Voltage constraints ( Vmin <= V <= Vmax )
    def vlimits(model, i):
        return Vmin[i] <= model.vm[i] <= Vmax[i]

    model.const12 = Constraint(model.bus, rule=vlimits)

    # Sfrom line limit
    def sfrommax(model, i):
        return model.Pf[i]**2 + model.Qf[i]**2 <= Smax[i]**2

    model.const13 = Constraint(model.line, rule=sfrommax)

    # Sto line limit
    def stomax(model, i):
        return model.Pt[i]**2 + model.Qt[i]**2 <= Smax[i]**2

    model.const14 = Constraint(model.line, rule=stomax)

    # Set objective function
    # ------------------------
    def obj_fun(model):
        return sum(gk[i] * ((model.vm[fr[i]] / model.tr[i])**2 + model.vm[to[i]]**2 -
                            2 / model.tr[i] * model.vm[fr[i]] * model.vm[to[i]] *
                            cos(model.va[fr[i]] - model.va[to[i]])) for i in tp) + \
               sum(gk[i] * ((model.vm[fr[i]] / tr0[i]) ** 2 + model.vm[to[i]] ** 2 -
                            2 / tr0[i] * model.vm[fr[i]] * model.vm[to[i]] *
                            cos(model.va[fr[i]] - model.va[to[i]])) for i in ntp)

    model.obj = Objective(rule=obj_fun, sense=minimize)

    mt = time.clock() - start_time  # Modeling time

    # Execute solve command with the selected solver
    # ------------------------------------------------
    start_time = time.clock()
    results = opt.solve(model, tee=True)
    et = time.clock() - start_time  # Elapsed time
    print(results)

    # Update the case info with the optimized variables and approximate the continuous variables to discrete values
    # ==============================================================================================================
    for i in range(nb):
        if i in sd:
            bus[i, BS] = round(model.s[i].value) * Bs0[i] * baseMVA
        bus[i, VM] = model.vm[i].value  # Bus voltage magnitudes
        bus[i, VA] = model.va[i].value * 180 / pi  # Bus voltage angles
    # Update transformation ratios
    for i in range(nl):
        if i in tp:
            branch[i, TAP] = 1 + dudtap * round(model.tap[i].value)
    # Update gen matrix variables
    for i in range(ng):
        gen[i, PG] = model.Pg[gb[i]].value * baseMVA
        gen[i, QG] = model.Qg[gb[i]].value * baseMVA
        gen[i, VG] = bus[gb[i], VM]
    # Convert to external (original) numbering and save case results
    ppc = int2ext(ppc)
    ppc['bus'][:, 1:] = bus[:, 1:]
    branch[:, 0:2] = ppc['branch'][:, 0:2]
    ppc['branch'] = branch
    ppc['gen'][:, 1:] = gen[:, 1:]

    # Execute a second optimization with only the discrete approximated values (requires solveropfnlp_2)
    sol = solveropfnlp_2(ppc)
    sol['mt'] = sol['mt'] + mt
    sol['et'] = sol['et'] + et
    sol['tap'] = zeros((tp.shape[0], 1))
    for i in range(tp.shape[0]):
        sol['tap'][i] = round(model.tap[tp[i]].value)
    sol['shunt'] = zeros((sd.shape[0], 1))
    for i in range(sd.shape[0]):
        sol['shunt'][i] = round(model.s[sd[i]].value)

    # ppc solved case is returned
    return sol
if __name__ == "__main__":
    from unit_commitment.test_cases import case118

    test_case = case118.case118()

    from pypower.case118 import case118
    from pypower.idx_brch import F_BUS, T_BUS, BR_X, TAP, SHIFT, BR_STATUS, RATE_A
    from pypower.idx_cost import MODEL, NCOST, PW_LINEAR, COST, POLYNOMIAL
    from pypower.idx_bus import BUS_TYPE, REF, VA, VM, PD, GS, VMAX, VMIN, BUS_I
    from pypower.idx_gen import GEN_BUS, VG, PG, QG, PMAX, PMIN, QMAX, QMIN
    from numpy import flatnonzero as find

    casedata = case118()
    mpc = loadcase.loadcase(casedata)
    mpc = ext2int.ext2int(mpc)
    baseMVA, bus, gen, branch, gencost = mpc["baseMVA"], mpc["bus"], mpc[
        "gen"], mpc["branch"], mpc["gencost"]  #

    nb = shape(mpc['bus'])[0]  ## number of buses
    nl = shape(mpc['branch'])[0]  ## number of branches
    ng = shape(mpc['gen'])[0]  ## number of dispatchable injections

    ## Formualte the
    stat = branch[:, BR_STATUS]  ## ones at in-service branches
    b = stat / branch[:, BR_X]  ## series susceptance
    tap = ones(nl)  ## default tap ratio = 1
    i = find(branch[:, TAP])  ## indices of non-zero tap ratios
    tap[i] = branch[i, TAP]  ## assign non-zero tap ratios

    ## build connection matrix Cft = Cf - Ct for line and from - to buses
Exemple #22
0
def runcpf(basecasedata=None,
           targetcasedata=None,
           ppopt=None,
           fname='',
           solvedcase=''):

    # default arguments
    if basecasedata is None:
        basecasedata = join(dirname(__file__), 'case9')
    if targetcasedata is None:
        targetcasedata = join(dirname(__file__), 'case9target')
    ppopt = ppoption(ppopt)

    # options
    verbose = ppopt["VERBOSE"]
    step = ppopt["CPF_STEP"]
    parameterization = ppopt["CPF_PARAMETERIZATION"]
    adapt_step = ppopt["CPF_ADAPT_STEP"]
    cb_args = ppopt["CPF_USER_CALLBACK_ARGS"]

    # set up callbacks
    callback_names = ["cpf_default_callback"]
    if len(ppopt["CPF_USER_CALLBACK"]) > 0:
        if isinstance(ppopt["CPF_USER_CALLBACK"], list):
            callback_names = r_[callback_names, ppopt["CPF_USER_CALLBACK"]]
        else:
            callback_names.append(ppopt["CPF_USER_CALLBACK"])
    callbacks = []
    for callback_name in callback_names:
        callbacks.append(getattr(cpf_callbacks, callback_name))

    # read base case data
    ppcbase = loadcase(basecasedata)
    nb = ppcbase["bus"].shape[0]

    # add zero columns to branch for flows if needed
    if ppcbase["branch"].shape[1] < QT:
        ppcbase["branch"] = c_[ppcbase["branch"],
                               zeros((ppcbase["branch"].shape[0],
                                      QT - ppcbase["branch"].shape[1] + 1))]

    # convert to internal indexing
    ppcbase = ext2int(ppcbase)
    baseMVAb, busb, genb, branchb = \
        ppcbase["baseMVA"], ppcbase["bus"], ppcbase["gen"], ppcbase["branch"]

    # get bus index lists of each type of bus
    ref, pv, pq = bustypes(busb, genb)

    # generator info
    onb = find(genb[:, GEN_STATUS] > 0)  # which generators are on?
    gbusb = genb[onb, GEN_BUS].astype(int)  # what buses are they at?

    # read target case data
    ppctarget = loadcase(targetcasedata)

    # add zero columns to branch for flows if needed
    if ppctarget["branch"].shape[1] < QT:
        ppctarget["branch"] = c_[ppctarget["branch"],
                                 zeros(
                                     (ppctarget["branch"].shape[0],
                                      QT - ppctarget["branch"].shape[1] + 1))]

    # convert to internal indexing
    ppctarget = ext2int(ppctarget)
    baseMVAt, bust, gent, brancht = \
        ppctarget["baseMVA"], ppctarget["bus"], ppctarget["gen"], ppctarget["branch"]

    # get bus index lists of each type of bus
    # ref, pv, pq = bustypes(bust, gent)

    # generator info
    ont = find(gent[:, GEN_STATUS] > 0)  # which generators are on?
    gbust = gent[ont, GEN_BUS].astype(int)  # what buses are they at?

    # -----  run the power flow  -----
    t0 = time()
    if verbose > 0:
        v = ppver('all')
        stdout.write('PYPOWER Version %s, %s' % (v["Version"], v["Date"]))
        stdout.write(' -- AC Continuation Power Flow\n')

    # initial state
    # V0    = ones(bus.shape[0])            ## flat start
    V0 = busb[:, VM] * exp(1j * pi / 180 * busb[:, VA])
    vcb = ones(V0.shape)  # create mask of voltage-controlled buses
    vcb[pq] = 0  # exclude PQ buses
    k = find(vcb[gbusb])  # in-service gens at v-c buses
    V0[gbusb[k]] = genb[onb[k], VG] / abs(V0[gbusb[k]]) * V0[gbusb[k]]

    # build admittance matrices
    Ybus, Yf, Yt = makeYbus(baseMVAb, busb, branchb)

    # compute base case complex bus power injections (generation - load)
    Sbusb = makeSbus(baseMVAb, busb, genb)
    # compute target case complex bus power injections (generation - load)
    Sbust = makeSbus(baseMVAt, bust, gent)

    # scheduled transfer
    Sxfr = Sbust - Sbusb

    # Run the base case power flow solution
    if verbose > 2:
        ppopt_pf = ppoption(ppopt, VERBOSE=max(0, verbose - 1))
    else:
        ppopt_pf = ppoption(ppopt, VERBOSE=max(0, verbose - 2))

    lam = 0
    V, success, iterations = newtonpf(Ybus, Sbusb, V0, ref, pv, pq, ppopt_pf)
    if verbose > 2:
        print('step %3d : lambda = %6.3f\n' % (0, 0))
    elif verbose > 1:
        print('step %3d : lambda = %6.3f, %2d Newton steps\n',
              (0, 0, iterations))

    lamprv = lam  # lam at previous step
    Vprv = V  # V at previous step
    continuation = 1
    cont_steps = 0

    # input args for callbacks
    cb_data = {
        "ppc_base": ppcbase,
        "ppc_target": ppctarget,
        "Sxfr": Sxfr,
        "Ybus": Ybus,
        "Yf": Yf,
        "Yt": Yt,
        "ref": ref,
        "pv": pv,
        "pq": pq,
        "ppopt": ppopt
    }
    cb_state = {}

    # invoke callbacks
    for k in range(len(callbacks)):
        cb_state, _ = callbacks[k](cont_steps, V, lam, V, lam, cb_data,
                                   cb_state, cb_args)

    if linalg.norm(Sxfr) == 0:
        if verbose:
            print(
                'base case and target case have identical load and generation\n'
            )

        continuation = 0
        V0 = V
        lam0 = lam

    # tangent predictor z = [dx;dlam]
    z = zeros(2 * len(V) + 1)
    z[-1] = 1.0
    while continuation:
        cont_steps = cont_steps + 1
        # prediction for next step
        V0, lam0, z = cpf_predictor(V, lam, Ybus, Sxfr, pv, pq, step, z, Vprv,
                                    lamprv, parameterization)

        # save previous voltage, lambda before updating
        Vprv = V
        lamprv = lam

        # correction
        V, success, i, lam = cpf_corrector(Ybus, Sbusb, V0, ref, pv, pq, lam0,
                                           Sxfr, Vprv, lamprv, z, step,
                                           parameterization, ppopt_pf)

        if not success:
            continuation = 0
            if verbose:
                print(
                    'step %3d : lambda = %6.3f, corrector did not converge in %d iterations\n'
                    % (cont_steps, lam, i))
            break

        if verbose > 2:
            print('step %3d : lambda = %6.3f\n' % (cont_steps, lam))
        elif verbose > 1:
            print('step %3d : lambda = %6.3f, %2d corrector Newton steps\n' %
                  (cont_steps, lam, i))

        # invoke callbacks
        for k in range(len(callbacks)):
            cb_state, _ = callbacks[k](cont_steps, V, lam, V0, lam0, cb_data,
                                       cb_state, cb_args)

        if isinstance(ppopt["CPF_STOP_AT"], str):
            if ppopt["CPF_STOP_AT"].upper() == "FULL":
                if abs(lam) < 1e-8:  # traced the full continuation curve
                    if verbose:
                        print(
                            '\nTraced full continuation curve in %d continuation steps\n'
                            % cont_steps)
                    continuation = 0
                elif lam < lamprv and lam - step < 0:  # next step will overshoot
                    step = lam  # modify step-size
                    parameterization = 1  # change to natural parameterization
                    adapt_step = False  # disable step-adaptivity

            else:  # == 'NOSE'
                if lam < lamprv:  # reached the nose point
                    if verbose:
                        print(
                            '\nReached steady state loading limit in %d continuation steps\n'
                            % cont_steps)
                    continuation = 0

        else:
            if lam < lamprv:
                if verbose:
                    print(
                        '\nReached steady state loading limit in %d continuation steps\n'
                        % cont_steps)
                continuation = 0
            elif abs(ppopt["CPF_STOP_AT"] -
                     lam) < 1e-8:  # reached desired lambda
                if verbose:
                    print(
                        '\nReached desired lambda %3.2f in %d continuation steps\n'
                        % (ppopt["CPF_STOP_AT"], cont_steps))
                continuation = 0
            # will reach desired lambda in next step
            elif lam + step > ppopt["CPF_STOP_AT"]:
                step = ppopt["CPF_STOP_AT"] - lam  # modify step-size
                parameterization = 1  # change to natural parameterization
                adapt_step = False  # disable step-adaptivity

        if adapt_step and continuation:
            pvpq = r_[pv, pq]
            # Adapt stepsize
            cpf_error = linalg.norm(
                r_[angle(V[pq]), abs(V[pvpq]), lam] -
                r_[angle(V0[pq]), abs(V0[pvpq]), lam0], inf)
            if cpf_error < ppopt["CPF_ERROR_TOL"]:
                # Increase stepsize
                step = step * ppopt["CPF_ERROR_TOL"] / cpf_error
                if step > ppopt["CPF_STEP_MAX"]:
                    step = ppopt["CPF_STEP_MAX"]
            else:
                # decrese stepsize
                step = step * ppopt["CPF_ERROR_TOL"] / cpf_error
                if step < ppopt["CPF_STEP_MIN"]:
                    step = ppopt["CPF_STEP_MIN"]

    # invoke callbacks
    if success:
        cpf_results = {}
        for k in range(len(callbacks)):
            cb_state, cpf_results = callbacks[k](cont_steps,
                                                 V,
                                                 lam,
                                                 V0,
                                                 lam0,
                                                 cb_data,
                                                 cb_state,
                                                 cb_args,
                                                 results=cpf_results,
                                                 is_final=True)
    else:
        cpf_results["iterations"] = i

    # update bus and gen matrices to reflect the loading and generation
    # at the noise point
    bust[:, PD] = busb[:, PD] + lam * (bust[:, PD] - busb[:, PD])
    bust[:, QD] = busb[:, QD] + lam * (bust[:, QD] - busb[:, QD])
    gent[:, PG] = genb[:, PG] + lam * (gent[:, PG] - genb[:, PG])

    # update data matrices with solution
    bust, gent, brancht = pfsoln(baseMVAt, bust, gent, brancht, Ybus, Yf, Yt,
                                 V, ref, pv, pq)

    ppctarget["et"] = time() - t0
    ppctarget["success"] = success

    # -----  output results  -----
    # convert back to original bus numbering & print results
    ppctarget["bus"], ppctarget["gen"], ppctarget[
        "branch"] = bust, gent, brancht
    if success:
        n = cpf_results["iterations"] + 1
        cpf_results["V_p"] = i2e_data(ppctarget, cpf_results["V_p"],
                                      full((nb, n), nan), "bus", 0)
        cpf_results["V_c"] = i2e_data(ppctarget, cpf_results["V_c"],
                                      full((nb, n), nan), "bus", 0)
    results = int2ext(ppctarget)
    results["cpf"] = cpf_results

    # zero out result fields of out-of-service gens & branches
    if len(results["order"]["gen"]["status"]["off"]) > 0:
        results["gen"][ix_(results["order"]["gen"]["status"]["off"],
                           [PG, QG])] = 0

    if len(results["order"]["branch"]["status"]["off"]) > 0:
        results["branch"][ix_(results["order"]["branch"]["status"]["off"],
                              [PF, QF, PT, QT])] = 0

    if fname:
        fd = None
        try:
            fd = open(fname, "a")
        except Exception as detail:
            stderr.write("Error opening %s: %s.\n" % (fname, detail))
        finally:
            if fd is not None:
                printpf(results, fd, ppopt)
                fd.close()
    else:
        printpf(results, stdout, ppopt)

    # save solved case
    if solvedcase:
        savecase(solvedcase, results)

    return results, success
Exemple #23
-1
def run_sim(ppc, elements, dynopt = None, events = None, recorder = None):
    """
    Run a time-domain simulation
    
    Inputs:
        ppc         PYPOWER load flow case
        elements    Dictionary of dynamic model objects (machines, controllers, etc) with Object ID as key
        events      Events object
        recorder    Recorder object (empty)
    
    Outputs:
        recorder    Recorder object (with data)
    """
    
    #########
    # SETUP #
    #########
    
    # Get version information
    ver = pydyn_ver()
    print('PYPOWER-Dynamics ' + ver['Version'] + ', ' + ver['Date'])
    
    # Program options
    if dynopt:
        h = dynopt['h']             
        t_sim = dynopt['t_sim']           
        max_err = dynopt['max_err']        
        max_iter = dynopt['max_iter']
        verbose = dynopt['verbose']
    else:
        # Default program options
        h = 0.01                # step length (s)
        t_sim = 5               # simulation time (s)
        max_err = 0.0001        # Maximum error in network iteration (voltage mismatches)
        max_iter = 25           # Maximum number of network iterations
        verbose = False
        
    # Make lists of current injection sources (generators, external grids, etc) and controllers
    sources = []
    controllers = []
    for element in elements.values():
        if element.__module__ in ['pydyn.sym_order6a', 'pydyn.sym_order6b', 'pydyn.sym_order4', 'pydyn.ext_grid', 'pydyn.vsc_average', 'pydyn.asym_1cage', 'pydyn.asym_2cage']:
            sources.append(element)
            
        if element.__module__ == 'pydyn.controller':
            controllers.append(element)
    
    # Set up interfaces
    interfaces = init_interfaces(elements)
    
    ##################
    # INITIALISATION #
    ##################
    print('Initialising models...')
    
    # Run power flow and update bus voltages and angles in PYPOWER case object
    results, success = runpf(ppc) 
    ppc["bus"][:, VM] = results["bus"][:, VM]
    ppc["bus"][:, VA] = results["bus"][:, VA]
    
    # Build Ybus matrix
    ppc_int = ext2int(ppc)
    baseMVA, bus, branch = ppc_int["baseMVA"], ppc_int["bus"], ppc_int["branch"]
    Ybus, Yf, Yt = makeYbus(baseMVA, bus, branch)
    
    # Build modified Ybus matrix
    Ybus = mod_Ybus(Ybus, elements, bus, ppc_int['gen'], baseMVA)
    
    # Calculate initial voltage phasors
    v0 = bus[:, VM] * (np.cos(np.radians(bus[:, VA])) + 1j * np.sin(np.radians(bus[:, VA])))
    
    # Initialise sources from load flow
    for source in sources:
        if source.__module__ in ['pydyn.asym_1cage', 'pydyn.asym_2cage']:
            # Asynchronous machine
            source_bus = ppc_int['bus'][source.bus_no,0]
            v_source = v0[source_bus]
            source.initialise(v_source,0)
        else:
            # Generator or VSC
            source_bus = ppc_int['gen'][source.gen_no,0]
            S_source = np.complex(results["gen"][source.gen_no, 1] / baseMVA, results["gen"][source.gen_no, 2] / baseMVA)
            v_source = v0[source_bus]
            source.initialise(v_source,S_source)
    
    # Interface controllers and machines (for initialisation)
    for intf in interfaces:
        int_type = intf[0]
        var_name = intf[1]
        if int_type == 'OUTPUT':
            # If an output, interface in the reverse direction for initialisation
            intf[2].signals[var_name] = intf[3].signals[var_name]
        else:
            # Inputs are interfaced in normal direction during initialisation
            intf[3].signals[var_name] = intf[2].signals[var_name]
    
    # Initialise controllers
    for controller in controllers:
        controller.initialise()
    
    #############
    # MAIN LOOP #
    #############
    
    if events == None:
        print('Warning: no events!')
    
    # Factorise Ybus matrix
    Ybus_inv = splu(Ybus)
    
    y1 = []
    v_prev = v0
    print('Simulating...')
    for t in range(int(t_sim / h) + 1):
        if np.mod(t,1/h) == 0:
            print('t=' + str(t*h) + 's')
            
        # Interface controllers and machines
        for intf in interfaces:
            var_name = intf[1]
            intf[3].signals[var_name] = intf[2].signals[var_name]
        
        # Solve differential equations
        for j in range(4):
            # Solve step of differential equations
            for element in elements.values():
                element.solve_step(h,j) 
            
            # Interface with network equations
            v_prev = solve_network(sources, v_prev, Ybus_inv, ppc_int, len(bus), max_err, max_iter)
        
        if recorder != None:
            # Record signals or states
            recorder.record_variables(t*h, elements)
        
        if events != None:
            # Check event stack
            ppc, refactorise = events.handle_events(np.round(t*h,5), elements, ppc, baseMVA)
            
            if refactorise == True:
                # Rebuild Ybus from new ppc_int
                ppc_int = ext2int(ppc)
                baseMVA, bus, branch = ppc_int["baseMVA"], ppc_int["bus"], ppc_int["branch"]
                Ybus, Yf, Yt = makeYbus(baseMVA, bus, branch)
                
                # Rebuild modified Ybus
                Ybus = mod_Ybus(Ybus, elements, bus, ppc_int['gen'], baseMVA)
                
                # Refactorise Ybus
                Ybus_inv = splu(Ybus)
                
                # Solve network equations
                v_prev = solve_network(sources, v_prev, Ybus_inv, ppc_int, len(bus), max_err, max_iter)
                
    return recorder