{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# MatProcessor for Sensitivity Matrices Calculation\n", "\n", "This notebook demonstrates how to build sensitivity matrices:\n", "\n", "- Power Transfer Distribution Factors (PTDF)\n", "\n", "- Line Outage Distribution Factors (LODF)\n", "\n", "- Outage Distribution Transfer Factors (ODTF)" ] }, { "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('matpower/case300.m'),\n", " no_output=True)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## PTDF" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The $\\mathbf{PTDF}[i, j]$ represents the additional flow on $\\text{Line}_i$ resulting from a 1 p.u. power injection at $\\text{Bus}_j$.\n", "\n", "PTDF can be calculated using the ``build_ptdf`` method of the ``MatProcessor`` class.\n", "The calculated matrix will be stored in the ``MParam`` attribute ``PTDF``.\n", "The method also returns the PTDF matrix.\n", "\n", "> **Note:** When the memory is limited to calculate PTDF at once, set ``incremental=True`` to incrementally calculate the PTDF matrix." ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "Building system matrices\n" ] } ], "source": [ "PTDF = sp.mats.build_ptdf()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The PTDF matrix is in the shape of (n_lines, n_buses)." ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(411, 300)" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "PTDF.shape" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## LODF" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The $\\mathbf{LODF}[i, j]$ represents the additional flow on $\\text{Line}_i$ resulting from a 1 p.u. power reduction on $\\text{Line}_j$ caused by the outage of $\\text{Line}_j$.\n", "\n", "Similarly, LODF can also be calculated using the ``build_lodf`` method of the ``MatProcessor`` class.\n", "The calculated matrix will be stored in the ``MParam`` attribute ``LODF``." ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [], "source": [ "LODF = sp.mats.build_lodf()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The LODF matrix is in the shape of (n_lines, n_lines)." ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(411, 411)" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "LODF.shape" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## OTDF" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The $\\mathbf{OTDF}_k[i, j]$ represents the additional flow on $\\text{Line}_i$ resulting from a 1 p.u. power injection at $\\text{Bus}_j$ during the outage of $\\text{Line}_k$.\n", "\n", "Keep in mind that OTDF is linked to specific line outages, which means there can be multiple OTDF matrices corresponding to different line outages." ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [], "source": [ "OTDF7 = sp.mats.build_otdf('Line_7')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The OTDF matrix is in the shape of (n_lines, n_buses)." ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(411, 300)" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "OTDF7.shape" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Quick Contingency Assessment" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "These matrices are useful for quick contingency assessment." ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "Parsing OModel for \n", "Evaluating OModel for \n", "Finalizing OModel for \n", " solved as optimal in 0.0247 seconds, converged in 13 iterations with CLARABEL.\n" ] }, { "data": { "text/plain": [ "True" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sp.DCOPF.run(solver='CLARABEL')" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [], "source": [ "Pbus = sp.DCOPF.Cg.v @ sp.DCOPF.pg.v \n", "Pbus -= sp.DCOPF.Cl.v @ sp.DCOPF.pd.v \n", "Pbus -= sp.DCOPF.Csh.v @ sp.DCOPF.gsh.v\n", "plf = PTDF @ Pbus" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.allclose(sp.DCOPF.plf.v, plf, atol=0.001)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The line flow ``plf`` here is calculed using the ``PTDF`` matrix, and it is close to the line flow from the DCOPF." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Next, let's check it again when Line 7 is outaged." ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [], "source": [ "sp.Line.alter(src='u', idx='Line_7', value=0)" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "Building system matrices\n", " reinit OModel due to non-parametric change.\n", "Evaluating OModel for \n", "Finalizing OModel for \n" ] }, { "data": { "text/plain": [ "True" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sp.DCOPF.update()" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ " solved as optimal in 0.0223 seconds, converged in 13 iterations with CLARABEL.\n" ] }, { "data": { "text/plain": [ "True" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sp.DCOPF.run(solver='CLARABEL')" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [], "source": [ "Pbus2 = sp.DCOPF.Cg.v @ sp.DCOPF.pg.v \n", "Pbus2 -= sp.DCOPF.Cl.v @ sp.DCOPF.pd.v \n", "Pbus2 -= sp.DCOPF.Csh.v @ sp.DCOPF.gsh.v\n", "plf2 = OTDF7 @ Pbus2" ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.allclose(sp.DCOPF.plf.v, plf2, atol=0.001)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We observe that the line flow calculated using OTDF closely matches the line flow obtained from DCOPF.\n", "\n", "Since matrix calculations are significantly faster than DCOPF, they are frequently used for quick contingency assessments." ] } ], "metadata": { "kernelspec": { "display_name": "andesre", "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 }