{ "cells": [ { "cell_type": "markdown", "source": [ "# PTDF with [PowerSimulations.jl](https://github.com/NREL-SIIP/PowerSimulations.jl)" ], "metadata": {} }, { "cell_type": "markdown", "source": [ "**Originally Contributed by**: Sourabh Dalvi" ], "metadata": {} }, { "cell_type": "markdown", "source": [ "## Introduction" ], "metadata": {} }, { "cell_type": "markdown", "source": [ "PowerSimulations.jl supports linear PTDF optimal power flow formulation. This example shows a\n", "single multi-period optimization of economic dispatch with a linearized DC-OPF representation of\n", "using PTDF power flow and how to extract duals values or locational marginal prices for energy." ], "metadata": {} }, { "cell_type": "markdown", "source": [ "## Dependencies" ], "metadata": {} }, { "outputs": [], "cell_type": "code", "source": [ "using SIIPExamples\n", "using PowerSystems\n", "using PowerSimulations\n", "const PSI = PowerSimulations\n", "using PowerSystemCaseBuilder\n", "using DataFrames" ], "metadata": {}, "execution_count": null }, { "cell_type": "markdown", "source": [ "Since we'll be retrieving duals, we need a solver that returns duals values\n", "here we use HiGHS." ], "metadata": {} }, { "outputs": [], "cell_type": "code", "source": [ "using HiGHS # mip solver\n", "solver = optimizer_with_attributes(HiGHS.Optimizer, \"mip_rel_gap\" => 0.05)" ], "metadata": {}, "execution_count": null }, { "cell_type": "markdown", "source": [ "We can use the same RTS data and some of the initialization as in\n", "[OperationsProblem example](https://nbviewer.jupyter.org/github/NREL-SIIP/SIIPExamples.jl/blob/master/notebook/3_PowerSimulations_examples/01_operations_problems.ipynb)" ], "metadata": {} }, { "outputs": [], "cell_type": "code", "source": [ "sys = build_system(PSITestSystems, \"modified_RTS_GMLC_DA_sys\")" ], "metadata": {}, "execution_count": null }, { "cell_type": "markdown", "source": [ "Here, we want do define an economic dispatch (linear generation decisions) with\n", "linear DC-OPF using PTDF network representation.\n", "So, starting with the network, we can select from _almost_ any of the endpoints on this\n", "tree:" ], "metadata": {} }, { "outputs": [], "cell_type": "code", "source": [ "print_tree(PowerSimulations.PM.AbstractPowerModel)" ], "metadata": {}, "execution_count": null }, { "cell_type": "markdown", "source": [ "Calculate the PTDF matrix." ], "metadata": {} }, { "outputs": [], "cell_type": "code", "source": [ "PTDF_matrix = PTDF(sys)" ], "metadata": {}, "execution_count": null }, { "cell_type": "markdown", "source": [ "For now, let's just choose a standard PTDF formulation." ], "metadata": {} }, { "outputs": [], "cell_type": "code", "source": [ "template = template_unit_commitment(\n", " network = NetworkModel(\n", " StandardPTDFModel,\n", " PTDF = PTDF_matrix,\n", " duals = [CopperPlateBalanceConstraint],\n", " use_slacks = false,\n", " ),\n", " use_slacks = true,\n", ")\n", "for (k, v) in template.branches\n", " v.duals = [NetworkFlowConstraint]\n", "end" ], "metadata": {}, "execution_count": null }, { "cell_type": "markdown", "source": [ "Now we can build a 4-hour economic dispatch / OPF problem with the RTS data.\n", "Here, we have to pass the keyword argument `constraint_duals` to OperationsProblem\n", "with the name of the constraint for which duals are required for them to be returned in the results." ], "metadata": {} }, { "outputs": [], "cell_type": "code", "source": [ "problem = DecisionModel(template, sys, horizon = 24, optimizer = solver)\n", "build!(problem, output_dir = mktempdir())" ], "metadata": {}, "execution_count": null }, { "cell_type": "markdown", "source": [ "And solve the problem and collect the results" ], "metadata": {} }, { "outputs": [], "cell_type": "code", "source": [ "solve!(problem)" ], "metadata": {}, "execution_count": null }, { "cell_type": "markdown", "source": [ "Here we collect the dual values from the results for the `:CopperPlateBalance` and `:network_flow`\n", "constraints. In the case of PTDF network formulation we need to compute the final LMP for each bus in the system by\n", "subtracting the duals (μ) of `:network_flow` constraints multiplied by the PTDF matrix\n", "from the dual (λ) of `:CopperPlateBalance` constraint." ], "metadata": {} }, { "outputs": [], "cell_type": "code", "source": [ "res = ProblemResults(problem)\n", "duals = read_duals(\n", " res,\n", " [k for k in list_dual_keys(res) if PSI.get_entry_type(k) == NetworkFlowConstraint],\n", ")\n", "λ = read_dual(res, \"CopperPlateBalanceConstraint__System\")[:, 2]\n", "flow_duals = outerjoin(values(duals)..., on = :DateTime)\n", "μ = Matrix(flow_duals[:, PTDF_matrix.axes[1]])" ], "metadata": {}, "execution_count": null }, { "cell_type": "markdown", "source": [ "Here we calculate LMP as λ + congestion component of the LMP which is a product of μ and the PTDF matrix." ], "metadata": {} }, { "outputs": [], "cell_type": "code", "source": [ "LMP = flow_duals[:, [:DateTime]]\n", "for bus in get_components(Bus, sys)\n", " LMP[:, get_name(bus)] = λ .+ μ * PTDF_matrix[:, get_number(bus)]\n", "end" ], "metadata": {}, "execution_count": null }, { "cell_type": "markdown", "source": [ "Finally here we have the LMPs" ], "metadata": {} }, { "outputs": [], "cell_type": "code", "source": [ "LMP" ], "metadata": {}, "execution_count": null }, { "cell_type": "markdown", "source": [ "---\n", "\n", "*This notebook was generated using [Literate.jl](https://github.com/fredrikekre/Literate.jl).*" ], "metadata": {} } ], "nbformat_minor": 3, "metadata": { "language_info": { "file_extension": ".jl", "mimetype": "application/julia", "name": "julia", "version": "1.7.2" }, "kernelspec": { "name": "julia-1.7", "display_name": "Julia 1.7.2", "language": "julia" } }, "nbformat": 4 }