Source code for VeraGridEngine.Templates.Rms.line_complex_rms_template

# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at https://mozilla.org/MPL/2.0/.
# SPDX-License-Identifier: MPL-2.0

from typing import List
import numpy as np

from VeraGridEngine.Devices.Dynamic.var_factory import VarFactory
from VeraGridEngine.enumerations import DeviceType, ParamPowerFlowReferenceType, VarPowerFlowReferenceType
from VeraGridEngine.Devices.Dynamic.rms_template import RmsModelTemplate
from VeraGridEngine.Utils.Symbolic.symbolic import Var


[docs] def get_line_complex_rms_template(vfactory: VarFactory, name="Line_complex_rms_template") -> RmsModelTemplate: """ Get the complex phasor-based RMS template model of the Line with TRULY MULTILINEAR equations. This template uses complex phasor representation where: - V = Vr + j*Vi is the complex voltage - I = Y * (Vf - Vt) is the complex current (LINEAR in V!) - Y = g + j*b is the complex admittance The key innovation is that current equations are LINEAR in voltage variables: I = (g + j*b) * ((Vrf + j*Vif) - (Vrt + j*Vit)) When expanded: Ir = g*(Vrf - Vrt) - b*(Vif - Vit) [Linear!] Ii = g*(Vif - Vit) + b*(Vrf - Vrt) [Linear!] This is truly multilinear because there are no products of voltage variables, unlike power equations S = V * conj(I) which are bilinear. The current balance at each node: Ξ£I = 0 is linear in all voltage variables. :param vfactory: Variable factory for creating variables :param name: Name of the template :return: RmsModelTemplate with complex multilinear line equations """ templ = RmsModelTemplate() templ.tpe = DeviceType.LineDevice templ.name = name # Inputs: complex voltages at from and to buses # We use separate real/imaginary inputs for compilation, # but conceptually they form complex voltages Vf and Vt inputs: List[Var] = [vfactory.add_var("Vrf_" + name), # From bus real vfactory.add_var("Vif_" + name), # From bus imag vfactory.add_var("Vrt_" + name), # To bus real vfactory.add_var("Vit_" + name)] # To bus imag # Complex current outputs (from and to buses) # These are calculated using LINEAR equations If_complex = vfactory.add_var("If_complex") # Complex current at from bus It_complex = vfactory.add_var("It_complex") # Complex current at to bus # Real/imaginary components of current (for compilation) Ir_f = vfactory.add_var("Ir_f") # Real current from Ii_f = vfactory.add_var("Ii_f") # Imag current from Ir_t = vfactory.add_var("Ir_t") # Real current to Ii_t = vfactory.add_var("Ii_t") # Imag current to # Power outputs (derived from complex current and voltage) Pf = vfactory.add_var("Pf") Qf = vfactory.add_var("Qf") Pt = vfactory.add_var("Pt") Qt = vfactory.add_var("Qt") # Parameters: line admittance Y = g + j*b g = vfactory.add_var("g") # Conductance (real part of Y) b = vfactory.add_var("b") # Susceptance (imag part of Y) bsh = vfactory.add_var("bsh") # Shunt susceptance # Set default parameter values templ.block.parameters[g] = vfactory.add_const(5) templ.block.parameters[b] = vfactory.add_const(-12) templ.block.parameters[bsh] = vfactory.add_const(0.03) # Algebraic variables include currents and powers templ.block.algebraic_vars = [Ir_f, Ii_f, Ir_t, Ii_t, Pf, Pt, Qf, Qt] # Voltage variables (inputs) Vrf = inputs[0] Vif = inputs[1] Vrt = inputs[2] Vit = inputs[3] # ============================================================================ # MULTILINEAR CURRENT EQUATIONS # ============================================================================ # Complex current: I = Y * (Vf - Vt) where Y = g + j*b # # Expanding: I = (g + j*b) * ((Vrf + j*Vif) - (Vrt + j*Vit)) # I = (g + j*b) * ((Vrf - Vrt) + j*(Vif - Vit)) # # Real part: Ir = g*(Vrf - Vrt) - b*(Vif - Vit) # Imag part: Ii = g*(Vif - Vit) + b*(Vrf - Vrt) # # These are TRULY LINEAR in voltage variables! No products, no squares! # ============================================================================ # Current at from bus: If = Y * (Vf - Vt) + j*(bsh/2)*Vf # (includes half shunt at from bus) Ir_f_eq = g * (Vrf - Vrt) - b * (Vif - Vit) - (bsh / 2) * Vif Ii_f_eq = g * (Vif - Vit) + b * (Vrf - Vrt) + (bsh / 2) * Vrf # Current at to bus: It = Y * (Vt - Vf) + j*(bsh/2)*Vt # (includes half shunt at to bus) Ir_t_eq = g * (Vrt - Vrf) - b * (Vit - Vif) - (bsh / 2) * Vit Ii_t_eq = g * (Vit - Vif) + b * (Vrt - Vrf) + (bsh / 2) * Vrt # ============================================================================ # POWER EQUATIONS (derived from complex quantities) # ============================================================================ # S = V * conj(I) = (Vr + j*Vi)*(Ir - j*Ii) # P = Vr*Ir + Vi*Ii # Q = Vi*Ir - Vr*Ii # # Note: These are bilinear (products of V and I), but since I is linear in V, # these become quadratic in V. However, we keep them for power balance. # For truly linear equations, use current balance instead! # ============================================================================ Pf_eq = Vrf * Ir_f + Vif * Ii_f Qf_eq = Vif * Ir_f - Vrf * Ii_f Pt_eq = Vrt * Ir_t + Vit * Ii_t Qt_eq = Vit * Ir_t - Vrt * Ii_t # Algebraic equations: currents calculated from voltages templ.block.algebraic_eqs = [ # Current equations (LINEAR!) Ir_f - Ir_f_eq, Ii_f - Ii_f_eq, Ir_t - Ir_t_eq, Ii_t - Ii_t_eq, # Power equations (derived) Pf - Pf_eq, Qf - Qf_eq, Pt - Pt_eq, Qt - Qt_eq, ] # External mapping for reference templ.block.external_mapping = { # Complex phasor mapping VarPowerFlowReferenceType.Vf_complex: If_complex, # Complex current from VarPowerFlowReferenceType.Vt_complex: It_complex, # Complex current to VarPowerFlowReferenceType.Sf_complex: Pf, # Will be combined with Qf VarPowerFlowReferenceType.St_complex: Pt, # Will be combined with Qt # Real/imaginary component mapping VarPowerFlowReferenceType.Vrf: inputs[0], VarPowerFlowReferenceType.Vif: inputs[1], VarPowerFlowReferenceType.Vrt: inputs[2], VarPowerFlowReferenceType.Vit: inputs[3], VarPowerFlowReferenceType.Pf: Pf, VarPowerFlowReferenceType.Pt: Pt, VarPowerFlowReferenceType.Qf: Qf, VarPowerFlowReferenceType.Qt: Qt, } templ.block.api_obj_mapping = { ParamPowerFlowReferenceType.g: g, ParamPowerFlowReferenceType.b: b, ParamPowerFlowReferenceType.bsh: bsh, } templ.block.in_vars = inputs return templ
[docs] def get_line_complex_current_equations(Vrf: Var, Vif: Var, Vrt: Var, Vit: Var, g: Var, b: Var, bsh: Var) -> tuple: """ Calculate complex current using MULTILINEAR equations. This is the core innovation: current is linear in voltage! :param Vrf: From bus voltage real part :param Vif: From bus voltage imaginary part :param Vrt: To bus voltage real part :param Vit: To bus voltage imaginary part :param g: Line conductance :param b: Line susceptance :param bsh: Shunt susceptance :return: Tuple of (Ir_f, Ii_f, Ir_t, Ii_t) current components """ # From bus current: If = Y*(Vf-Vt) + j*(bsh/2)*Vf Ir_f = g * (Vrf - Vrt) - b * (Vif - Vit) - (bsh / 2) * Vif Ii_f = g * (Vif - Vit) + b * (Vrf - Vrt) + (bsh / 2) * Vrf # To bus current: It = Y*(Vt-Vf) + j*(bsh/2)*Vt Ir_t = g * (Vrt - Vrf) - b * (Vit - Vif) - (bsh / 2) * Vit Ii_t = g * (Vit - Vif) + b * (Vrt - Vrf) + (bsh / 2) * Vrt return Ir_f, Ii_f, Ir_t, Ii_t
[docs] def calculate_complex_power(Vr: Var, Vi: Var, Ir: Var, Ii: Var) -> tuple: """ Calculate complex power from voltage and current. S = V * conj(I) = (Vr + j*Vi)*(Ir - j*Ii) P = Vr*Ir + Vi*Ii Q = Vi*Ir - Vr*Ii :param Vr: Voltage real part :param Vi: Voltage imaginary part :param Ir: Current real part :param Ii: Current imaginary part :return: Tuple of (P, Q) """ P = Vr * Ir + Vi * Ii Q = Vi * Ir - Vr * Ii return P, Q