{ "cells": [ { "cell_type": "markdown", "source": [ "# Creating and Handling Data for Dynamic Simulations" ], "metadata": {} }, { "cell_type": "markdown", "source": [ "**Originally Contributed by**: Rodrigo Henriquez and José Daniel Lara" ], "metadata": {} }, { "cell_type": "markdown", "source": [ "## Introduction" ], "metadata": {} }, { "cell_type": "markdown", "source": [ "This tutorial briefly introduces how to create a system using `PowerSystems.jl` data\n", "structures. The tutorial will guide you to create the JSON data file for the tutorial 1.\n", "Start by calling `PowerSystems.jl`:" ], "metadata": {} }, { "outputs": [ { "output_type": "execute_result", "data": { "text/plain": "PowerSystems" }, "metadata": {}, "execution_count": 1 } ], "cell_type": "code", "source": [ "using SIIPExamples\n", "using PowerSystems\n", "const PSY = PowerSystems" ], "metadata": {}, "execution_count": 1 }, { "cell_type": "markdown", "source": [ "# Step 1: System description" ], "metadata": {} }, { "cell_type": "markdown", "source": [ "Next we need to define the different elements required to run a simulation. To run a\n", "simulation in `PowerSimulationsDynamics`, it is required to define a `System` that contains\n", "the following components:" ], "metadata": {} }, { "cell_type": "markdown", "source": [ "## Static Components:" ], "metadata": {} }, { "cell_type": "markdown", "source": [ "We called static components to those that are used to run a Power Flow problem." ], "metadata": {} }, { "cell_type": "markdown", "source": [ "- Vector of `Bus` elements, that define all the buses in the network.\n", "- Vector of `Branch` elements, that define all the branches elements (that connect two buses)\n", " in the network.\n", "- Vector of `StaticInjection` elements, that define all the devices connected to buses that\n", " can inject (or withdraw) power. These static devices, typically generators, in\n", " `PowerSimulationsDynamics` are used to solve the Power Flow problem that determines the\n", " active and reactive power provided for each device.\n", "- Vector of `PowerLoad` elements, that define all the loads connected to buses that can\n", " withdraw current. These are also used to solve the Power Flow.\n", "- Vector of `Source` elements, that define source components behind a reactance that can\n", " inject or withdraw current.\n", "- The base of power used to define per unit values, in MVA as a `Float64` value.\n", "- The base frequency used in the system, in Hz as a `Float64` value." ], "metadata": {} }, { "cell_type": "markdown", "source": [ "## Dynamic Components:" ], "metadata": {} }, { "cell_type": "markdown", "source": [ "Dynamic components are those that define differential equations to run a transient simulation." ], "metadata": {} }, { "cell_type": "markdown", "source": [ "- Vector of `DynamicInjection` elements. These components must be attached to a\n", "`StaticInjection` that connects the power flow solution to the dynamic formulation of such\n", "device. `DynamicInjection` can be `DynamicGenerator` or `DynamicInverter`, and its specific\n", "formulation (i.e. differential equations) will depend on the specific components that define such device.\n", "- (Optional) Selecting which of the `Lines` (of the `Branch` vector) elements must be modeled of\n", "`DynamicLines` elements, that can be used to model lines with differential equations." ], "metadata": {} }, { "cell_type": "markdown", "source": [ "To start we will define the data structures for the network." ], "metadata": {} }, { "cell_type": "markdown", "source": [ "## One Machine Infinite Bus case manual data creation" ], "metadata": {} }, { "cell_type": "markdown", "source": [ "The following describes the system creation for the OMIB case." ], "metadata": {} }, { "cell_type": "markdown", "source": [ "## Static System creation" ], "metadata": {} }, { "cell_type": "markdown", "source": [ "To create the system you need to pass the location of the RAW file" ], "metadata": {} }, { "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[ Info: The PSS(R)E parser currently supports buses, loads, shunts, generators, branches, transformers, and dc lines\n", "[ Info: The PSS(R)E parser currently supports buses, loads, shunts, generators, branches, transformers, and dc lines\n", "[ Info: angmin and angmax values are 0, widening these values on branch 1 to +/- 60.0 deg.\n", "[ Info: angmin and angmax values are 0, widening these values on branch 2 to +/- 60.0 deg.\n", "[ Info: the voltage setpoint on generator 1 does not match the value at bus 102\n", "┌ Info: Constructing System from Power Models\n", "│ data[\"name\"] = \"omib\"\n", "└ data[\"source_type\"] = \"pti\"\n", "[ Info: Reading bus data\n", "[ Info: Reading generator data\n", "[ Info: Reading branch data\n", "[ Info: Reading branch data\n", "[ Info: Reading DC Line data\n", "[ Info: Reading storage data\n", "┌ Warning: There are no PowerSystems.ElectricLoad Components in the System\n", "└ @ PowerSystems ~/.julia/packages/PowerSystems/gGFFl/src/utils/IO/system_checks.jl:59\n" ] }, { "output_type": "execute_result", "data": { "text/plain": "System\n┌───────────────────┬─────────────┐\n│ Property │ Value │\n├───────────────────┼─────────────┤\n│ System Units Base │ SYSTEM_BASE │\n│ Base Power │ 100.0 │\n│ Base Frequency │ 60.0 │\n│ Num Components │ 8 │\n└───────────────────┴─────────────┘\n\nStatic Components\n┌─────────────────┬───────┬────────────────────────┬───────────────┐\n│ Type │ Count │ Has Static Time Series │ Has Forecasts │\n├─────────────────┼───────┼────────────────────────┼───────────────┤\n│ Arc │ 1 │ false │ false │\n│ Area │ 1 │ false │ false │\n│ Bus │ 2 │ false │ false │\n│ Line │ 2 │ false │ false │\n│ LoadZone │ 1 │ false │ false │\n│ ThermalStandard │ 1 │ false │ false │\n└─────────────────┴───────┴────────────────────────┴───────────────┘\n\n", "text/html": [ "\n", "\n", "\n", "\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
System
PropertyValue
System Units BaseSYSTEM_BASE
Base Power100.0
Base Frequency60.0
Num Components8
\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Static Components
TypeCountHas Static Time SeriesHas Forecasts
Arc1falsefalse
Area1falsefalse
Bus2falsefalse
Line2falsefalse
LoadZone1falsefalse
ThermalStandard1falsefalse
\n", "\n", "\n", "\n" ] }, "metadata": {}, "execution_count": 2 } ], "cell_type": "code", "source": [ "file_dir = joinpath(\n", " dirname(dirname(pathof(SIIPExamples))),\n", " \"script\",\n", " \"4_PowerSimulationsDynamics_examples\",\n", " \"Data\",\n", ")\n", "omib_sys = System(joinpath(file_dir, \"OMIB.raw\"))" ], "metadata": {}, "execution_count": 2 }, { "cell_type": "markdown", "source": [ "This system does not have an injection device in bus 1 (the reference bus).\n", "We can add a source with small impedance directly as follows:" ], "metadata": {} }, { "outputs": [], "cell_type": "code", "source": [ "slack_bus = [b for b in get_components(Bus, omib_sys) if b.bustype == BusTypes.REF][1]\n", "inf_source = Source(\n", " name = \"InfBus\", #name\n", " available = true, #availability\n", " active_power = 0.0,\n", " reactive_power = 0.0,\n", " bus = slack_bus, #bus\n", " R_th = 0.0, #Rth\n", " X_th = 5e-6, #Xth\n", ")\n", "add_component!(omib_sys, inf_source)" ], "metadata": {}, "execution_count": 3 }, { "cell_type": "markdown", "source": [ "We just added a infinite source with $X_{th} = 5\\cdot 10^{-6}$ pu" ], "metadata": {} }, { "cell_type": "markdown", "source": [ "The system can be explored directly using functions like:" ], "metadata": {} }, { "outputs": [ { "output_type": "execute_result", "data": { "text/plain": "PowerSystems.Generator Counts: \nPowerSystems.ThermalStandard: 1\n" }, "metadata": {}, "execution_count": 4 } ], "cell_type": "code", "source": [ "get_components(Source, omib_sys)\n", "\n", "get_components(Generator, omib_sys)" ], "metadata": {}, "execution_count": 4 }, { "cell_type": "markdown", "source": [ "By exploring those it can be seen that the generators are named as: `generator-bus_number-id`.\n", "Then, the generator attached at bus 2 is named `generator-102-1`." ], "metadata": {} }, { "cell_type": "markdown", "source": [ "### Dynamic Injections" ], "metadata": {} }, { "cell_type": "markdown", "source": [ "We are now interested in attaching to the system the dynamic component that will be modeling\n", "our dynamic generator." ], "metadata": {} }, { "cell_type": "markdown", "source": [ "Dynamic generator devices are composed by 5 components, namely, `machine`, `shaft`,\n", "`avr`, `tg` and `pss`. So we will be adding functions to create all of its components and\n", "the generator itself:" ], "metadata": {} }, { "cell_type": "markdown", "source": [ "*Machine*" ], "metadata": {} }, { "outputs": [ { "output_type": "execute_result", "data": { "text/plain": "machine_classic (generic function with 1 method)" }, "metadata": {}, "execution_count": 5 } ], "cell_type": "code", "source": [ "machine_classic() = BaseMachine(\n", " 0.0, #R\n", " 0.2995, #Xd_p\n", " 0.7087, #eq_p\n", ")" ], "metadata": {}, "execution_count": 5 }, { "cell_type": "markdown", "source": [ "*Shaft*" ], "metadata": {} }, { "outputs": [ { "output_type": "execute_result", "data": { "text/plain": "shaft_damping (generic function with 1 method)" }, "metadata": {}, "execution_count": 6 } ], "cell_type": "code", "source": [ "shaft_damping() = SingleMass(\n", " 3.148, #H\n", " 2.0, #D\n", ")" ], "metadata": {}, "execution_count": 6 }, { "cell_type": "markdown", "source": [ "*AVR: No AVR*" ], "metadata": {} }, { "outputs": [ { "output_type": "execute_result", "data": { "text/plain": "avr_none (generic function with 1 method)" }, "metadata": {}, "execution_count": 7 } ], "cell_type": "code", "source": [ "avr_none() = AVRFixed(0.0)" ], "metadata": {}, "execution_count": 7 }, { "cell_type": "markdown", "source": [ "*TG: No TG*" ], "metadata": {} }, { "outputs": [ { "output_type": "execute_result", "data": { "text/plain": "tg_none (generic function with 1 method)" }, "metadata": {}, "execution_count": 8 } ], "cell_type": "code", "source": [ "tg_none() = TGFixed(1.0) #efficiency" ], "metadata": {}, "execution_count": 8 }, { "cell_type": "markdown", "source": [ "*PSS: No PSS*" ], "metadata": {} }, { "outputs": [ { "output_type": "execute_result", "data": { "text/plain": "pss_none (generic function with 1 method)" }, "metadata": {}, "execution_count": 9 } ], "cell_type": "code", "source": [ "pss_none() = PSSFixed(0.0)" ], "metadata": {}, "execution_count": 9 }, { "cell_type": "markdown", "source": [ "The next lines receives a static generator name, and creates a `DynamicGenerator` based on\n", "that specific static generator, with the specific components defined previously. This is\n", "a classic machine model without AVR, Turbine Governor and PSS." ], "metadata": {} }, { "outputs": [ { "output_type": "execute_result", "data": { "text/plain": "\ngenerator-102-1 (PowerSystems.DynamicGenerator{PowerSystems.BaseMachine, PowerSystems.SingleMass, PowerSystems.AVRFixed, PowerSystems.TGFixed, PowerSystems.PSSFixed}):\n name: generator-102-1\n ω_ref: 1.0\n machine: PowerSystems.BaseMachine\n shaft: PowerSystems.SingleMass\n avr: PowerSystems.AVRFixed\n prime_mover: PowerSystems.TGFixed\n pss: PowerSystems.PSSFixed\n base_power: 100.0\n n_states: 2\n states: [:δ, :ω]\n ext: Dict{String, Any}()\n internal: InfrastructureSystems.InfrastructureSystemsInternal" }, "metadata": {}, "execution_count": 10 } ], "cell_type": "code", "source": [ "static_gen = get_component(Generator, omib_sys, \"generator-102-1\")\n", "\n", "dyn_gen = DynamicGenerator(\n", " name = get_name(static_gen),\n", " ω_ref = 1.0,\n", " machine = machine_classic(),\n", " shaft = shaft_damping(),\n", " avr = avr_none(),\n", " prime_mover = tg_none(),\n", " pss = pss_none(),\n", ")" ], "metadata": {}, "execution_count": 10 }, { "cell_type": "markdown", "source": [ "The dynamic generator is added to the system by specifying the dynamic and static generator" ], "metadata": {} }, { "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "┌ Warning: struct DynamicGenerator does not exist in validation configuration file, validation skipped\n", "└ @ InfrastructureSystems ~/.julia/packages/InfrastructureSystems/Z74ym/src/validation.jl:51\n" ] } ], "cell_type": "code", "source": [ "add_component!(omib_sys, dyn_gen, static_gen)" ], "metadata": {}, "execution_count": 11 }, { "cell_type": "markdown", "source": [ "Then we can serialize our system data to a json file that can be later read as:" ], "metadata": {} }, { "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[ Info: Serialized System to /home/runner/work/SIIPExamples.jl/SIIPExamples.jl/script/4_PowerSimulationsDynamics_examples/Data/omib_sys.json\n" ] } ], "cell_type": "code", "source": [ "to_json(omib_sys, joinpath(file_dir, \"omib_sys.json\"), force = true)" ], "metadata": {}, "execution_count": 12 }, { "cell_type": "markdown", "source": [ "## Dynamic Lines case: Data creation" ], "metadata": {} }, { "cell_type": "markdown", "source": [ "We will now create a three bus system with one inverter and one generator.\n", "In order to do so, we will parse the following `ThreebusInverter.raw` network:" ], "metadata": {} }, { "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[ Info: The PSS(R)E parser currently supports buses, loads, shunts, generators, branches, transformers, and dc lines\n", "[ Info: The PSS(R)E parser currently supports buses, loads, shunts, generators, branches, transformers, and dc lines\n", "[ Info: angmin and angmax values are 0, widening these values on branch 1 to +/- 60.0 deg.\n", "[ Info: angmin and angmax values are 0, widening these values on branch 2 to +/- 60.0 deg.\n", "[ Info: angmin and angmax values are 0, widening these values on branch 3 to +/- 60.0 deg.\n", "┌ Info: Constructing System from Power Models\n", "│ data[\"name\"] = \"threebusinverter\"\n", "└ data[\"source_type\"] = \"pti\"\n", "[ Info: Reading bus data\n", "[ Info: Reading generator data\n", "[ Info: Reading branch data\n", "[ Info: Reading branch data\n", "[ Info: Reading DC Line data\n", "[ Info: Reading storage data\n" ] } ], "cell_type": "code", "source": [ "sys_file_dir = joinpath(file_dir, \"ThreeBusInverter.raw\")\n", "threebus_sys = System(sys_file_dir)\n", "slack_bus = [b for b in get_components(Bus, threebus_sys) if b.bustype == BusTypes.REF][1]\n", "inf_source = Source(\n", " name = \"InfBus\", #name\n", " available = true, #availability\n", " active_power = 0.0,\n", " reactive_power = 0.0,\n", " bus = slack_bus, #bus\n", " R_th = 0.0, #Rth\n", " X_th = 5e-6, #Xth\n", ")\n", "add_component!(threebus_sys, inf_source)" ], "metadata": {}, "execution_count": 13 }, { "cell_type": "markdown", "source": [ "We will connect a One-d-one-q machine at bus 102, and a Virtual Synchronous Generator\n", "Inverter at bus 103. An inverter is composed by a `converter`, `outer control`,\n", "`inner control`, `dc source`, `frequency estimator` and a `filter`." ], "metadata": {} }, { "cell_type": "markdown", "source": [ "## Dynamic Inverter definition" ], "metadata": {} }, { "cell_type": "markdown", "source": [ "We will create specific functions to create the components of the inverter as follows:" ], "metadata": {} }, { "outputs": [ { "output_type": "execute_result", "data": { "text/plain": "filt (generic function with 1 method)" }, "metadata": {}, "execution_count": 14 } ], "cell_type": "code", "source": [ "#Define converter as an AverageConverter\n", "converter_high_power() = AverageConverter(rated_voltage = 138.0, rated_current = 100.0)\n", "\n", "#Define Outer Control as a composition of Virtual Inertia + Reactive Power Droop\n", "outer_control() = OuterControl(\n", " VirtualInertia(Ta = 2.0, kd = 400.0, kω = 20.0),\n", " ReactivePowerDroop(kq = 0.2, ωf = 1000.0),\n", ")\n", "\n", "#Define an Inner Control as a Voltage+Current Controler with Virtual Impedance:\n", "inner_control() = CurrentControl(\n", " kpv = 0.59, #Voltage controller proportional gain\n", " kiv = 736.0, #Voltage controller integral gain\n", " kffv = 0.0, #Binary variable enabling the voltage feed-forward in output of current controllers\n", " rv = 0.0, #Virtual resistance in pu\n", " lv = 0.2, #Virtual inductance in pu\n", " kpc = 1.27, #Current controller proportional gain\n", " kic = 14.3, #Current controller integral gain\n", " kffi = 0.0, #Binary variable enabling the current feed-forward in output of current controllers\n", " ωad = 50.0, #Active damping low pass filter cut-off frequency\n", " kad = 0.2, #Active damping gain\n", ")\n", "\n", "#Define DC Source as a FixedSource:\n", "dc_source_lv() = FixedDCSource(voltage = 600.0)\n", "\n", "#Define a Frequency Estimator as a PLL based on Vikram Kaura and Vladimir Blaskoc 1997 paper:\n", "pll() = KauraPLL(\n", " ω_lp = 500.0, #Cut-off frequency for LowPass filter of PLL filter.\n", " kp_pll = 0.084, #PLL proportional gain\n", " ki_pll = 4.69, #PLL integral gain\n", ")\n", "\n", "#Define an LCL filter:\n", "filt() = LCLFilter(lf = 0.08, rf = 0.003, cf = 0.074, lg = 0.2, rg = 0.01)" ], "metadata": {}, "execution_count": 14 }, { "cell_type": "markdown", "source": [ "We will construct the inverter later by specifying to which static device is assigned." ], "metadata": {} }, { "cell_type": "markdown", "source": [ "## Dynamic Generator definition" ], "metadata": {} }, { "cell_type": "markdown", "source": [ "Similarly we will construct a dynamic generator as follows:\n", "Create the machine" ], "metadata": {} }, { "outputs": [ { "output_type": "execute_result", "data": { "text/plain": "machine_oneDoneQ (generic function with 1 method)" }, "metadata": {}, "execution_count": 15 } ], "cell_type": "code", "source": [ "machine_oneDoneQ() = OneDOneQMachine(\n", " 0.0, #R\n", " 1.3125, #Xd\n", " 1.2578, #Xq\n", " 0.1813, #Xd_p\n", " 0.25, #Xq_p\n", " 5.89, #Td0_p\n", " 0.6, #Tq0_p\n", ")" ], "metadata": {}, "execution_count": 15 }, { "cell_type": "markdown", "source": [ "Shaft" ], "metadata": {} }, { "outputs": [ { "output_type": "execute_result", "data": { "text/plain": "shaft_no_damping (generic function with 1 method)" }, "metadata": {}, "execution_count": 16 } ], "cell_type": "code", "source": [ "shaft_no_damping() = SingleMass(\n", " 3.01, #H (M = 6.02 -> H = M/2)\n", " 0.0, #D\n", ")" ], "metadata": {}, "execution_count": 16 }, { "cell_type": "markdown", "source": [ "AVR: Type I: Resembles a DC1 AVR" ], "metadata": {} }, { "outputs": [ { "output_type": "execute_result", "data": { "text/plain": "pss_none (generic function with 1 method)" }, "metadata": {}, "execution_count": 17 } ], "cell_type": "code", "source": [ "avr_type1() = AVRTypeI(\n", " 20.0, #Ka - Gain\n", " 0.01, #Ke\n", " 0.063, #Kf\n", " 0.2, #Ta\n", " 0.314, #Te\n", " 0.35, #Tf\n", " 0.001, #Tr\n", " (min = -5.0, max = 5.0),\n", " 0.0039, #Ae - 1st ceiling coefficient\n", " 1.555, #Be - 2nd ceiling coefficient\n", ")\n", "\n", "#No TG\n", "tg_none() = TGFixed(1.0) #efficiency\n", "\n", "#No PSS\n", "pss_none() = PSSFixed(0.0) #Vs" ], "metadata": {}, "execution_count": 17 }, { "cell_type": "markdown", "source": [ "Now we will construct the dynamic generator and inverter.\n", "## Add the components to the system" ], "metadata": {} }, { "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "┌ Warning: struct DynamicGenerator does not exist in validation configuration file, validation skipped\n", "└ @ InfrastructureSystems ~/.julia/packages/InfrastructureSystems/Z74ym/src/validation.jl:51\n", "┌ Warning: struct DynamicInverter does not exist in validation configuration file, validation skipped\n", "└ @ InfrastructureSystems ~/.julia/packages/InfrastructureSystems/Z74ym/src/validation.jl:51\n" ] } ], "cell_type": "code", "source": [ "for g in get_components(Generator, threebus_sys)\n", " #Find the generator at bus 102\n", " if get_number(get_bus(g)) == 102\n", " #Create the dynamic generator\n", " case_gen = DynamicGenerator(\n", " get_name(g),\n", " 1.0, # ω_ref,\n", " machine_oneDoneQ(), #machine\n", " shaft_no_damping(), #shaft\n", " avr_type1(), #avr\n", " tg_none(), #tg\n", " pss_none(), #pss\n", " )\n", " #Attach the dynamic generator to the system by specifying the dynamic and static part\n", " add_component!(threebus_sys, case_gen, g)\n", " #Find the generator at bus 103\n", " elseif get_number(get_bus(g)) == 103\n", " #Create the dynamic inverter\n", " case_inv = DynamicInverter(\n", " get_name(g),\n", " 1.0, # ω_ref,\n", " converter_high_power(), #converter\n", " outer_control(), #outer control\n", " inner_control(), #inner control voltage source\n", " dc_source_lv(), #dc source\n", " pll(), #pll\n", " filt(), #filter\n", " )\n", " #Attach the dynamic inverter to the system\n", " add_component!(threebus_sys, case_inv, g)\n", " end\n", "end" ], "metadata": {}, "execution_count": 18 }, { "cell_type": "markdown", "source": [ "### Save the system in a JSON file" ], "metadata": {} }, { "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[ Info: Serialized System to /home/runner/work/SIIPExamples.jl/SIIPExamples.jl/script/4_PowerSimulationsDynamics_examples/Data/threebus_sys.json\n" ] } ], "cell_type": "code", "source": [ "to_json(threebus_sys, joinpath(file_dir, \"threebus_sys.json\"), force = true)" ], "metadata": {}, "execution_count": 19 }, { "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 }