Source code for VeraGridEngine.Templates.Emt.bergeron_line_symbolic_runtime

# 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 __future__ import annotations

from typing import Any, List, Tuple

import numpy as np

from VeraGridEngine.Templates.Emt.bergeron_line_emt_template import BergeronHistoryRuntime


[docs] class BergeronHistorySymbolicRuntime(BergeronHistoryRuntime): """ Symbolic helper for Bergeron history runtime. This class reuses the existing numerical setup (phase reduction, Gc/H matrices, indexing) and exposes symbolic-friendly builders for: - branch nodal injections - history recursion update expressions """ __slots__ = ( "sym_v_f_tau", "sym_v_t_tau", "sym_i_f_tau", "sym_i_t_tau", "sym_ih_f", "sym_ih_t", ) def __init__(self, line: Any, line_block: Any, h: float, sbase: float, fbase: float) -> None: super().__init__(line=line, line_block=line_block, h=h, sbase=sbase, fbase=fbase) self.sym_v_f_tau: List[Any] | None = None self.sym_v_t_tau: List[Any] | None = None self.sym_i_f_tau: List[Any] | None = None self.sym_i_t_tau: List[Any] | None = None self.sym_ih_f: List[Any] | None = None self.sym_ih_t: List[Any] | None = None
[docs] def bind_symbolic_state( self, v_f_tau: List[Any], v_t_tau: List[Any], i_f_tau: List[Any], i_t_tau: List[Any], ih_f: List[Any], ih_t: List[Any], ) -> None: """ Bind reduced-phase symbolic vectors used by symbolic update/injection builders. :param v_f_tau: Delayed from-side voltages in reduced active-phase ordering. :param v_t_tau: Delayed to-side voltages in reduced active-phase ordering. :param i_f_tau: Delayed from-side currents in reduced active-phase ordering. :param i_t_tau: Delayed to-side currents in reduced active-phase ordering. :param ih_f: Current from-side Bergeron history terms in reduced active-phase ordering. :param ih_t: Current to-side Bergeron history terms in reduced active-phase ordering. :return: None. """ expected = self.m all_vectors = [v_f_tau, v_t_tau, i_f_tau, i_t_tau, ih_f, ih_t] if any(len(vec) != expected for vec in all_vectors): raise ValueError(f"All reduced symbolic vectors must have length m={expected}") self.sym_v_f_tau = list(v_f_tau) self.sym_v_t_tau = list(v_t_tau) self.sym_i_f_tau = list(i_f_tau) self.sym_i_t_tau = list(i_t_tau) self.sym_ih_f = list(ih_f) self.sym_ih_t = list(ih_t)
[docs] def get_symbolic_injections( self, v_f_vars: List[Any] | None = None, v_t_vars: List[Any] | None = None, ih_f_vars: List[Any] | None = None, ih_t_vars: List[Any] | None = None, ) -> Tuple[List[Any], List[Any]]: """ Build Bergeron nodal current injections in full NABC shape. Equations per active phase k: i_f[k] = sum_j Gc[k, j] * v_f[j] + Ih_f[k] i_t[k] = sum_j Gc[k, j] * v_t[j] + Ih_t[k] :param v_f_vars: Active-phase from-side voltages (reduced ordering). :param v_t_vars: Active-phase to-side voltages (reduced ordering). :param ih_f_vars: Active-phase from-side history terms (reduced ordering). :param ih_t_vars: Active-phase to-side history terms (reduced ordering). :return: Two lists (i_f_full, i_t_full) in full NABC layout using None on missing phases. """ if v_f_vars is None: v_f_vars = self.sym_v_f_tau if v_t_vars is None: v_t_vars = self.sym_v_t_tau if ih_f_vars is None: ih_f_vars = self.sym_ih_f if ih_t_vars is None: ih_t_vars = self.sym_ih_t if v_f_vars is None or v_t_vars is None or ih_f_vars is None or ih_t_vars is None: raise ValueError("Symbolic injection vectors are not bound. Call bind_symbolic_state(...) first.") if not (len(v_f_vars) == len(v_t_vars) == len(ih_f_vars) == len(ih_t_vars) == self.m): raise ValueError("Reduced Bergeron vectors must all have length m") i_f_red: List[Any] = list() i_t_red: List[Any] = list() for i in range(self.m): expr_f = sum(self.Gc_red[i, j] * v_f_vars[j] for j in range(self.m)) + ih_f_vars[i] expr_t = sum(self.Gc_red[i, j] * v_t_vars[j] for j in range(self.m)) + ih_t_vars[i] i_f_red.append(expr_f) i_t_red.append(expr_t) i_f_full: List[Any] = [None, None, None, None] i_t_full: List[Any] = [None, None, None, None] for k_red, k_full in enumerate(self.phase_idx): i_f_full[k_full] = i_f_red[k_red] i_t_full[k_full] = i_t_red[k_red] return i_f_full, i_t_full
[docs] def get_symbolic_update( self, v_f_tau: List[Any] | None = None, v_t_tau: List[Any] | None = None, i_f_tau: List[Any] | None = None, i_t_tau: List[Any] | None = None, ) -> Tuple[List[Any], List[Any]]: """ Build symbolic expressions for next Bergeron history terms. This mirrors the existing numerical update in ``update_history`` but accepts symbolic variables/expressions at delayed time tau: X_f = -Gc * v_t(t-tau) - i_t(t-tau) Y_f = -Gc * v_f(t-tau) - i_f(t-tau) X_t = -Gc * v_f(t-tau) - i_f(t-tau) Y_t = -Gc * v_t(t-tau) - i_t(t-tau) Ih_f_next = 0.5 * ((I + H) * X_f + (I - H) * Y_f) Ih_t_next = 0.5 * ((I + H) * X_t + (I - H) * Y_t) :param v_f_tau: Reduced delayed from-side voltages. :param v_t_tau: Reduced delayed to-side voltages. :param i_f_tau: Reduced delayed from-side currents. :param i_t_tau: Reduced delayed to-side currents. :return: (Ih_f_next, Ih_t_next) reduced-phase symbolic expressions. """ if v_f_tau is None: v_f_tau = self.sym_v_f_tau if v_t_tau is None: v_t_tau = self.sym_v_t_tau if i_f_tau is None: i_f_tau = self.sym_i_f_tau if i_t_tau is None: i_t_tau = self.sym_i_t_tau if v_f_tau is None or v_t_tau is None or i_f_tau is None or i_t_tau is None: raise ValueError("Symbolic update vectors are not bound. Call bind_symbolic_state(...) first.") if not (len(v_f_tau) == len(v_t_tau) == len(i_f_tau) == len(i_t_tau) == self.m): raise ValueError("Reduced delayed vectors must all have length m") h_plus = np.eye(self.m) + self.H_red h_minus = np.eye(self.m) - self.H_red x_f: List[Any] = list() y_f: List[Any] = list() x_t: List[Any] = list() y_t: List[Any] = list() for i in range(self.m): x_f.append(-sum(self.Gc_red[i, j] * v_t_tau[j] for j in range(self.m)) - i_t_tau[i]) y_f.append(-sum(self.Gc_red[i, j] * v_f_tau[j] for j in range(self.m)) - i_f_tau[i]) x_t.append(-sum(self.Gc_red[i, j] * v_f_tau[j] for j in range(self.m)) - i_f_tau[i]) y_t.append(-sum(self.Gc_red[i, j] * v_t_tau[j] for j in range(self.m)) - i_t_tau[i]) ih_f_next: List[Any] = list() ih_t_next: List[Any] = list() for i in range(self.m): ih_f_next.append( 0.5 * ( sum(h_plus[i, j] * x_f[j] for j in range(self.m)) + sum(h_minus[i, j] * y_f[j] for j in range(self.m)) ) ) ih_t_next.append( 0.5 * ( sum(h_plus[i, j] * x_t[j] for j in range(self.m)) + sum(h_minus[i, j] * y_t[j] for j in range(self.m)) ) ) return ih_f_next, ih_t_next