{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Wrapper Routines\n", "\n", "Since v1.0.10, following wrapper routines are introduced for easy access to other power flow and optimal power flow solvers. The wrapper routines are:\n", "- PYPOWER: ``DCPF1``, ``PFlow1``, ``DCOPF1``, and ``ACOPF1``\n", "- gurobi_optimods: ``OPF``\n", "\n", "Following packages are required to call them:\n", "- ``pypower``\n", "- ``gurobi_optimods``" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", "\n", "import ams" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "sp = ams.load(ams.get_case('5bus/pjm5bus_demo.json'),\n", " no_output=True)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## PYPOWER" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Power Flow" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "Building system matrices\n", "Parsing OModel for \n", "Evaluating OModel for \n", "Finalizing OModel for \n", " converged in 0.0075 seconds, -1 iteration with PYPOWER.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "PYPOWER Version 5.1.18, 10-Apr-2025 -- AC Power Flow (Newton)\n", "\n", "\n", "Newton's method power flow converged in 3 iterations.\n" ] }, { "data": { "text/plain": [ "True" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sp.PFlow1.run()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can switch to other power flow algorithms.\n", "\n", "Note that it is recommended to use ``update`` to update the config values.\n", "\n", "There is a known issue with this routine.\n", "Fast-Decoupled (XB version) ``pf_alg=2`` and Fast-Decoupled (BX version) ``pf_alg=3`` are not fully supported yet." ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [], "source": [ "sp.PFlow1.config.update(pf_alg=4)" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ " converged in 0.0175 seconds, -1 iteration with PYPOWER.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "PYPOWER Version 5.1.18, 10-Apr-2025 -- AC Power Flow (Gauss-Seidel)\n", "\n", "\n", "Gauss-Seidel power flow converged in 69 iterations.\n" ] }, { "data": { "text/plain": [ "True" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sp.PFlow1.run()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "After successful solving, the results are mapped back to routine variables." ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([1.02944468, 1. , 3.2349 , 4.6651 , 0.1 ])" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sp.PFlow1.pg.v" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([ 1.67217961, -0.02705768, 0.95026549, -0.40953028, 1.31140954])" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sp.PFlow1.qg.v" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Below is the active power on line flow" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([ 1.34144848, 1.16636881, -2.84926578, -0.22728223, 0.00756141,\n", " -1.80078873, 1.34144848])" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sp.PFlow1.plf.v" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A ``tcost`` object (an instance of ``ExpressionCalc``) is created in the power flow routines for quick evaluation of the total generation cost." ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "3.0854544680774185" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sp.PFlow1.tcost.v" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Optimal Power Flow\n", "\n", "Similarly, both DCOPF1 and ACOPF1 are wrapped to solve optimal power flow." ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "Parsing OModel for \n", "Evaluating OModel for \n", "Finalizing OModel for \n", " converged in 0.0137 seconds, 10 iterations with PYPOWER.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "PYPOWER Version 5.1.18, 10-Apr-2025 -- DC Optimal Power Flow\n", "Python Interior Point Solver - PIPS, Version 1.0, 07-Feb-2011\n", "Converged!\n" ] }, { "data": { "text/plain": [ "True" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sp.DCOPF1.run()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In PYPOWER, the ``c0`` term (the constant coefficient in the generator cost\n", "function) is always included in the objective, regardless of the generator's\n", "commitment status.\n", "This means ``tcosts`` and ``obj.v`` can be different when some generators are\n", "not committed.\n", "\n", "See `pypower/opf_costfcn.py` for implementation details:\n", "" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'sum(c2 * pg**2 + c1 * pg + c0)'" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sp.DCOPF1.obj.e_str" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "0.9585953752140786" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sp.DCOPF1.obj.v" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'sum(mul(c2, pg**2))+ sum(mul(c1, pg))+ sum(mul(ug, c0))'" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sp.DCOPF1.tcost.e_str" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "0.9585953752140786" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sp.DCOPF1.tcost.v" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "LMP at each bus is stored in variable ``pi``" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([0.00000776, 0.000001 , 0.00003 , 0.00001571, 0.00000917])" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sp.DCOPF1.pi.v" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Kuhn-Tucker multiplier on upper and lower Pg limits are stored in ``mu1`` and ``mu2`` respectively." ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([0. , 0. , 0. , 0.0000342, 0. , 0. ,\n", " 0. ])" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sp.DCOPF1.mu1.v" ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([0., 0., 0., 0., 0., 0., 0.])" ] }, "execution_count": 17, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sp.DCOPF1.mu2.v" ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "Parsing OModel for \n", "Evaluating OModel for \n", "Finalizing OModel for \n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "PYPOWER Version 5.1.18, 10-Apr-2025 -- AC Optimal Power Flow\n", "Python Interior Point Solver - PIPS, Version 1.0, 07-Feb-2011\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ " converged in 0.1843 seconds, 15 iterations with PYPOWER.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Converged!\n" ] } ], "source": [ "sp.ACOPF1.run()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In ACOPF1, both active and reactive power prices are available." ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([0.00000782, 0.000001 , 0.00003 , 0.00001561, 0.00000921])" ] }, "execution_count": 19, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sp.ACOPF1.pi.v" ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([ 0.00000021, -0. , 0. , 0.0000001 , 0. ])" ] }, "execution_count": 20, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sp.ACOPF1.piq.v" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## gurobi-optimods\n", "\n", "In this library, optimal power flow is modeled and solved using Gurobi.\n", "\n", "" ] }, { "cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Help on method run in module ams.routines.grbopt:\n", "\n", "run(**kwargs) method of ams.routines.grbopt.OPF instance\n", " Run the OPF routine using gurobi-optimods.\n", "\n", " This method invokes `gurobi-optimods.opf.solve_opf` to solve the OPF problem.\n", "\n", " Parameters\n", " ----------\n", " - opftype : str\n", " Type of OPF to solve (default: 'AC').\n", " - branch_switching : bool\n", " Enable branch switching (default: False).\n", " - min_active_branches : float\n", " Defines the minimum number of branches that must be turned on when\n", " branch switching is active, i.e. the minimum number of turned on\n", " branches is equal to ``numbranches * min_active_branches``. Has no\n", " effect if ``branch_switching`` is set to False.\n", " - use_mip_start : bool\n", " Use MIP start (default: False).\n", " - time_limit : float\n", " Time limit for the solver (default: 0.0, no limit).\n", "\n" ] } ], "source": [ "help(sp.OPF.run)" ] }, { "cell_type": "code", "execution_count": 22, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "Parsing OModel for \n", "Evaluating OModel for \n", "Finalizing OModel for \n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Set parameter Username\n", "Set parameter LicenseID to value 2617183\n", "Set parameter TimeLimit to value 5\n", "Set parameter OptimalityTol to value 0.0001\n", "Academic license - for non-commercial use only - expires 2026-02-02\n", "Building case data structures from dictionary.\n", "Buses.\n", " Bus 4 ID 3 is the reference bus.\n", " sumloadPd 1000.0 numPload 3\n", " sumloadQd 328.69\n", " 5 buses\n", "Branches.\n", " Numbranches: 7 active: 7\n", "Generators.\n", " Number of generators: 5\n", " Number of buses with gens: 5\n", " summaxPg 11430.0 summaxQg 10657.5\n", "Generator cost vectors.\n", "Running DCOPF formulation.\n", "In bound_zs constraint, N=6\n", "DCOPF model constructed (0.00s).\n", "Statistics for model 'DC_Formulation_Model':\n", " Problem type : MIP\n", " Linear constraint matrix : 47 rows, 38 columns, 126 nonzeros\n", " Variable types : 31 continuous, 7 integer (7 binary)\n", " Matrix range : [1e-02, 2e+02]\n", " Objective range : [1e+00, 1e+00]\n", " Bounds range : [2e-01, 1e+02]\n", " RHS range : [2e+00, 6e+00]\n", "Using mip start with all branches kept on.\n", "Gurobi Optimizer version 12.0.1 build v12.0.1rc0 (mac64[arm] - Darwin 23.6.0 23G93)\n", "\n", "CPU model: Apple M3 Pro\n", "Thread count: 12 physical cores, 12 logical processors, using up to 12 threads\n", "\n", "Non-default parameters:\n", "TimeLimit 5\n", "OptimalityTol 0.0001\n", "\n", "Optimize a model with 47 rows, 38 columns and 126 nonzeros\n", "Model fingerprint: 0xbf6f625b\n", "Variable types: 31 continuous, 7 integer (7 binary)\n", "Coefficient statistics:\n", " Matrix range [1e-02, 2e+02]\n", " Objective range [1e+00, 1e+00]\n", " Bounds range [2e-01, 1e+02]\n", " RHS range [2e+00, 6e+00]\n", "\n", "User MIP start produced solution with objective 0.958595 (0.01s)\n", "Loaded user MIP start with objective 0.958595\n", "\n", "Presolve removed 14 rows and 16 columns\n", "Presolve time: 0.00s\n", "Presolved: 33 rows, 22 columns, 111 nonzeros\n", "Variable types: 15 continuous, 7 integer (7 binary)\n", "\n", "Root relaxation: objective 6.802167e-01, 29 iterations, 0.00 seconds (0.00 work units)\n", "\n", " Nodes | Current Node | Objective Bounds | Work\n", " Expl Unexpl | Obj Depth IntInf | Incumbent BestBd Gap | It/Node Time\n", "\n", " 0 0 0.68022 0 2 0.95860 0.68022 29.0% - 0s\n", "H 0 0 0.7341163 0.68022 7.34% - 0s\n", " 0 0 0.73412 0 2 0.73412 0.73412 0.00% - 0s\n", "\n", "Cutting planes:\n", " MIR: 3\n", "\n", "Explored 1 nodes (33 simplex iterations) in 0.03 seconds (0.00 work units)\n", "Thread count was 12 (of 12 available processors)\n", "\n", "Solution count 2: 0.734116 0.958595 \n", "\n", "Optimal solution found (tolerance 1.00e-04)\n", "Best objective 7.341162855941e-01, best bound 7.341162855940e-01, gap 0.0000%\n", "Objective value = 0.7341162855940513.\n", "Solution quality statistics for model 'DC_Formulation_Model' :\n", " Maximum violation:\n", " Bound : 0.00000000e+00\n", " Constraint : 1.62758695e-13 (Pdef_3_0_4)\n", " Integrality : 0.00000000e+00\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ " converged in 0.0588 seconds, -1 iteration with gurobi-optimods.\n" ] }, { "data": { "text/plain": [ "True" ] }, "execution_count": 22, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sp.OPF.run(opftype='DC', branch_switching=True, time_limit=5)" ] }, { "cell_type": "code", "execution_count": 23, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([0.92351428, 0.2 , 1. , 0.6 , 7.27648572])" ] }, "execution_count": 23, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sp.OPF.pg.v" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "As of v2.3.2, gurobi-optimods does not expose LMP" ] }, { "cell_type": "code", "execution_count": 24, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([0., 0., 0., 0., 0.])" ] }, "execution_count": 24, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sp.OPF.pi.v" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In this routine, a decision variable ``uld`` is introduced to store the branch_switching decision." ] }, { "cell_type": "code", "execution_count": 25, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([1., 1., 1., 1., 0., 1., 1.])" ] }, "execution_count": 25, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sp.OPF.uld.v" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This variable values will also update the target model variables." ] }, { "cell_type": "code", "execution_count": 26, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([1., 1., 1., 1., 0., 1., 1.])" ] }, "execution_count": 26, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sp.Line.u.v" ] } ], "metadata": { "kernelspec": { "display_name": "ams", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.12.0" } }, "nbformat": 4, "nbformat_minor": 2 }