# 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 Dict
import VeraGridEngine.Utils.Symbolic.symbolic as sym
from VeraGridEngine.Devices.Dynamic.emt_template import EmtModelTemplate
from VeraGridEngine.Devices.Dynamic.var_factory import VarFactory
from VeraGridEngine.Templates.Emt.xfmr_emt_template import _project_currents
from VeraGridEngine.Utils.Symbolic.block import Expr, Var
from VeraGridEngine.enumerations import DeviceType, ParamPowerFlowReferenceType, VarPowerFlowReferenceType
[docs]
def get_xfmr_emt_template_multilinear(
vf: VarFactory,
name: str = "xfmr_emt_template_ml",
) -> EmtModelTemplate:
"""
Build the EMT DAE template of a basic ATP-like transformer model.
Multilinear variant notes
-------------------------
This function mirrors ``get_xfmr_emt_template`` structure/order/comments and
keeps the same physics. The only intended reformulation is:
* squared terms are represented with auxiliary variables, and
* nonlinear constitutive equations are written in multiplied form.
"""
templ = EmtModelTemplate()
templ.tpe = DeviceType.TransformerTypeDevice
templ.name = name
templ.block.name = name
c0: Expr = vf.add_const(0.0)
c1: Expr = vf.add_const(1.0)
c2: Expr = vf.add_const(2.0)
c3: Expr = vf.add_const(3.0)
c10: Expr = vf.add_const(10.0)
c100: Expr = vf.add_const(100.0)
c1000: Expr = vf.add_const(1000.0)
c_eps: Expr = vf.add_const(1e-9)
c_smooth: Expr = vf.add_const(1e-8)
c_sqrt2: Expr = sym.sqrt(vf.add_const(2.0))
# ------------------------------------------------------------------
# Static parameters mapped from the transformer API object.
# ------------------------------------------------------------------
omega_base: Var = vf.add_var(name=f"omega_base_{name}")
xfmr_s_rated_mva: Var = vf.add_var(name=f"xfmr_s_rated_mva_{name}")
xfmr_oc_current_pct: Var = vf.add_var(name=f"xfmr_oc_current_pct_{name}")
xfmr_oc_loss_kw: Var = vf.add_var(name=f"xfmr_oc_loss_kw_{name}")
xfmr_sc_voltage_pct: Var = vf.add_var(name=f"xfmr_sc_voltage_pct_{name}")
xfmr_sc_loss_kw: Var = vf.add_var(name=f"xfmr_sc_loss_kw_{name}")
xfmr_tap_module: Var = vf.add_var(name=f"xfmr_tap_module_{name}")
templ.block.api_obj_mapping[ParamPowerFlowReferenceType.omega_base] = omega_base
templ.block.api_obj_mapping[ParamPowerFlowReferenceType.transformer_rated_power_mva] = xfmr_s_rated_mva
templ.block.api_obj_mapping[ParamPowerFlowReferenceType.transformer_open_circuit_current_pct] = xfmr_oc_current_pct
templ.block.api_obj_mapping[ParamPowerFlowReferenceType.transformer_open_circuit_loss_kw] = xfmr_oc_loss_kw
templ.block.api_obj_mapping[ParamPowerFlowReferenceType.transformer_short_circuit_voltage_pct] = xfmr_sc_voltage_pct
templ.block.api_obj_mapping[ParamPowerFlowReferenceType.transformer_short_circuit_loss_kw] = xfmr_sc_loss_kw
templ.block.api_obj_mapping[ParamPowerFlowReferenceType.transformer_tap_ratio] = xfmr_tap_module
# ------------------------------------------------------------------
# Mapped winding-connection matrices.
# ------------------------------------------------------------------
cf_enums: list[list[ParamPowerFlowReferenceType]] = [
[
ParamPowerFlowReferenceType.transformer_from_connection_aa,
ParamPowerFlowReferenceType.transformer_from_connection_ab,
ParamPowerFlowReferenceType.transformer_from_connection_ac,
],
[
ParamPowerFlowReferenceType.transformer_from_connection_ba,
ParamPowerFlowReferenceType.transformer_from_connection_bb,
ParamPowerFlowReferenceType.transformer_from_connection_bc,
],
[
ParamPowerFlowReferenceType.transformer_from_connection_ca,
ParamPowerFlowReferenceType.transformer_from_connection_cb,
ParamPowerFlowReferenceType.transformer_from_connection_cc,
],
]
ct_enums: list[list[ParamPowerFlowReferenceType]] = [
[
ParamPowerFlowReferenceType.transformer_to_connection_aa,
ParamPowerFlowReferenceType.transformer_to_connection_ab,
ParamPowerFlowReferenceType.transformer_to_connection_ac,
],
[
ParamPowerFlowReferenceType.transformer_to_connection_ba,
ParamPowerFlowReferenceType.transformer_to_connection_bb,
ParamPowerFlowReferenceType.transformer_to_connection_bc,
],
[
ParamPowerFlowReferenceType.transformer_to_connection_ca,
ParamPowerFlowReferenceType.transformer_to_connection_cb,
ParamPowerFlowReferenceType.transformer_to_connection_cc,
],
]
c_f_expr: list[list[Expr]] = []
c_t_expr: list[list[Expr]] = []
for i in range(3):
c_f_row: list[Expr] = []
c_t_row: list[Expr] = []
for j in range(3):
c_f_var: Var = vf.add_var(name=f"xfmr_cf_{i}_{j}_{name}")
c_t_var: Var = vf.add_var(name=f"xfmr_ct_{i}_{j}_{name}")
templ.block.api_obj_mapping[cf_enums[i][j]] = c_f_var
templ.block.api_obj_mapping[ct_enums[i][j]] = c_t_var
c_f_row.append(c_f_var)
c_t_row.append(c_t_var)
c_f_expr.append(c_f_row)
c_t_expr.append(c_t_row)
# ------------------------------------------------------------------
# Local dynamic-model parameters.
# ------------------------------------------------------------------
xfmr_core_topology_code: Var = vf.add_var(name=f"xfmr_core_topology_code_{name}")
xfmr_yoke_area_rel: Var = vf.add_var(name=f"xfmr_yoke_area_rel_{name}")
xfmr_yoke_length_rel: Var = vf.add_var(name=f"xfmr_yoke_length_rel_{name}")
xfmr_outer_leg_area_rel: Var = vf.add_var(name=f"xfmr_outer_leg_area_rel_{name}")
xfmr_outer_leg_length_rel: Var = vf.add_var(name=f"xfmr_outer_leg_length_rel_{name}")
xfmr_c_term: Var = vf.add_var(name=f"xfmr_c_term_{name}")
xfmr_use_linear_core: Var = vf.add_var(name=f"xfmr_use_linear_core_{name}")
xfmr_core_knee_flux_mult: Var = vf.add_var(name=f"xfmr_core_knee_flux_mult_{name}")
xfmr_core_knee_current_mult: Var = vf.add_var(name=f"xfmr_core_knee_current_mult_{name}")
xfmr_sc_resistance_pct: Var = vf.add_var(name=f"xfmr_sc_resistance_pct_{name}")
xfmr_core_linear_l_pu: Var = vf.add_var(name=f"xfmr_core_linear_l_pu_{name}")
xfmr_core_a_prime: Var = vf.add_var(name=f"xfmr_core_a_prime_{name}")
xfmr_core_b_prime: Var = vf.add_var(name=f"xfmr_core_b_prime_{name}")
templ.block.event_dict[xfmr_core_topology_code] = vf.add_const(3.0)
templ.block.event_dict[xfmr_yoke_area_rel] = vf.add_const(1.0)
templ.block.event_dict[xfmr_yoke_length_rel] = vf.add_const(1.0)
templ.block.event_dict[xfmr_outer_leg_area_rel] = vf.add_const(1.0)
templ.block.event_dict[xfmr_outer_leg_length_rel] = vf.add_const(1.0)
templ.block.event_dict[xfmr_c_term] = vf.add_const(0.0)
templ.block.event_dict[xfmr_use_linear_core] = vf.add_const(1.0)
templ.block.event_dict[xfmr_core_knee_flux_mult] = vf.add_const(1.05)
templ.block.event_dict[xfmr_core_knee_current_mult] = vf.add_const(8.0)
# ------------------------------------------------------------------
# Derived local parameters of the dynamic model.
# ------------------------------------------------------------------
templ.block.event_dict[xfmr_sc_resistance_pct] = xfmr_sc_loss_kw / (c10 * xfmr_s_rated_mva + c_eps)
oc_loss_pu: Expr = (xfmr_oc_loss_kw / c1000) / (xfmr_s_rated_mva + c_eps)
oc_current_pu: Expr = xfmr_oc_current_pct / c100
i_mag_pu: Expr = sym.sqrt(oc_current_pu * oc_current_pu - oc_loss_pu * oc_loss_pu + c_eps)
templ.block.event_dict[xfmr_core_linear_l_pu] = c1 / (i_mag_pu + c_eps)
lambda_nom_peak: Expr = c_sqrt2
i_nom_peak_from_linear: Expr = lambda_nom_peak / (xfmr_core_linear_l_pu + c_eps)
a_prime_auto: Expr = c1 / (xfmr_core_linear_l_pu + c_eps)
lambda_knee: Expr = xfmr_core_knee_flux_mult * lambda_nom_peak
i_knee: Expr = xfmr_core_knee_current_mult * i_nom_peak_from_linear
b_prime_raw: Expr = (i_knee / (lambda_knee + c_eps) - a_prime_auto) / (i_knee + c_eps)
b_prime_auto: Expr = b_prime_raw * b_prime_raw / (sym.sqrt(b_prime_raw * b_prime_raw + c_smooth) + c_eps)
templ.block.event_dict[xfmr_core_a_prime] = a_prime_auto
templ.block.event_dict[xfmr_core_b_prime] = b_prime_auto
# ------------------------------------------------------------------
# Leakage-branch parameters.
# ------------------------------------------------------------------
z_sc_pu: Expr = xfmr_sc_voltage_pct / c100
r_sc_pu: Expr = xfmr_sc_resistance_pct / c100
x_sc_pu: Expr = sym.sqrt(z_sc_pu * z_sc_pu - r_sc_pu * r_sc_pu + c_eps)
l_sigma: Expr = x_sc_pu / (omega_base + c_eps)
l_inv: Expr = c1 / (l_sigma + c_eps)
# ------------------------------------------------------------------
# Core-loss conductance from open-circuit losses.
# ------------------------------------------------------------------
g_core_total: Expr = (xfmr_oc_loss_kw / c1000) / (xfmr_s_rated_mva + c_eps)
g_core_leg: Expr = g_core_total / c3
# ------------------------------------------------------------------
# Terminal electrical interface variables in abc frame.
# ------------------------------------------------------------------
vf_keys: Dict[str, VarPowerFlowReferenceType] = {
"N": VarPowerFlowReferenceType.vf_N,
"A": VarPowerFlowReferenceType.vf_A,
"B": VarPowerFlowReferenceType.vf_B,
"C": VarPowerFlowReferenceType.vf_C,
}
vt_keys: Dict[str, VarPowerFlowReferenceType] = {
"N": VarPowerFlowReferenceType.vt_N,
"A": VarPowerFlowReferenceType.vt_A,
"B": VarPowerFlowReferenceType.vt_B,
"C": VarPowerFlowReferenceType.vt_C,
}
v_f: list[Var] = [
vf.add_var(name=f"vf_A_{name}", reference=vf_keys["A"]),
vf.add_var(name=f"vf_B_{name}", reference=vf_keys["B"]),
vf.add_var(name=f"vf_C_{name}", reference=vf_keys["C"]),
]
v_t: list[Var] = [
vf.add_var(name=f"vt_A_{name}", reference=vt_keys["A"]),
vf.add_var(name=f"vt_B_{name}", reference=vt_keys["B"]),
vf.add_var(name=f"vt_C_{name}", reference=vt_keys["C"]),
]
# ------------------------------------------------------------------
# Dynamic states and derivatives.
# ------------------------------------------------------------------
i_leak: list[Var] = [vf.add_var(name=f"i_leak_{ph}_{name}") for ph in ("A", "B", "C")]
lam_leg: list[Var] = [vf.add_var(name=f"lam_leg_{ph}_{name}") for ph in ("A", "B", "C")]
q_f: list[Var] = [vf.add_var(name=f"qf_{ph}_{name}") for ph in ("A", "B", "C")]
q_t: list[Var] = [vf.add_var(name=f"qt_{ph}_{name}") for ph in ("A", "B", "C")]
di_leak: list[Var] = [vf.add_diff_var(name=f"di_leak_{ph}_{name}", base_var=i_leak[k]) for k, ph in enumerate(("A", "B", "C"))]
dlam_leg: list[Var] = [vf.add_diff_var(name=f"dlam_leg_{ph}_{name}", base_var=lam_leg[k]) for k, ph in enumerate(("A", "B", "C"))]
dq_f: list[Var] = [vf.add_diff_var(name=f"dqf_{ph}_{name}", base_var=q_f[k]) for k, ph in enumerate(("A", "B", "C"))]
dq_t: list[Var] = [vf.add_diff_var(name=f"dqt_{ph}_{name}", base_var=q_t[k]) for k, ph in enumerate(("A", "B", "C"))]
# ------------------------------------------------------------------
# Algebraic variables.
# ------------------------------------------------------------------
i_leg_core: list[Var] = [vf.add_var(name=f"i_leg_core_{ph}_{name}") for ph in ("A", "B", "C")]
i_mag: list[Var] = [vf.add_var(name=f"i_mag_{ph}_{name}") for ph in ("A", "B", "C")]
i_loss_leg: list[Var] = [vf.add_var(name=f"i_loss_leg_{ph}_{name}") for ph in ("A", "B", "C")]
i_cap_f: list[Var] = [vf.add_var(name=f"i_cap_f_{ph}_{name}") for ph in ("A", "B", "C")]
i_cap_t: list[Var] = [vf.add_var(name=f"i_cap_t_{ph}_{name}") for ph in ("A", "B", "C")]
if_act: list[Var] = [vf.add_var(name=f"if_{ph}_{name}") for ph in ("A", "B", "C")]
it_act: list[Var] = [vf.add_var(name=f"it_{ph}_{name}") for ph in ("A", "B", "C")]
i_return_path: Var = vf.add_var(name=f"i_return_path_{name}")
i_return_total: Var = vf.add_var(name=f"i_return_total_{name}")
# Multilinear auxiliaries.
i_leg_core_sq_aux: list[Var] = [vf.add_var(name=f"i_leg_core_sq_aux_{ph}_{name}") for ph in ("A", "B", "C")]
i_return_path_sq_aux: Var = vf.add_var(name=f"i_return_path_sq_aux_{name}")
i_plus_smoothing_root: Var = vf.add_var(name=f"i_plus_smoothing_root{name}")
i_plus_smoothing_root_aux: Var = vf.add_var(name=f"i_plus_smoothing_root_aux{name}")
i_leg_plus_smoothing_sq: list[Var] = [vf.add_var(name=f"i_leg_plus_smoothing_sq_{ph}_{name}") for ph in ("A", "B", "C")]
i_leg_plus_smoothing_sq_aux: list[Var] = [vf.add_var(name=f"i_leg_plus_smoothing_sq_aux{ph}_{name}") for ph in ("A", "B", "C")]
templ.block.in_vars = v_f + v_t
templ.block.state_vars = i_leak + lam_leg + q_f + q_t
templ.block.diff_vars = di_leak + dlam_leg + dq_f + dq_t
templ.block.algebraic_vars = (
i_leg_core
+ [i_return_path, i_return_total]
+ i_mag
+ i_loss_leg
+ i_cap_f
+ i_cap_t
+ if_act
+ it_act
+ i_leg_core_sq_aux
+ [i_return_path_sq_aux]
+ [i_plus_smoothing_root, i_plus_smoothing_root_aux]
+ i_leg_plus_smoothing_sq
+ i_leg_plus_smoothing_sq_aux
)
# ------------------------------------------------------------------
# Voltage transformation from terminal abc frame to winding frame.
# ------------------------------------------------------------------
c_f_expr_t: list[list[Expr]] = [[c_f_expr[j][i] for j in range(3)] for i in range(3)]
c_t_expr_t: list[list[Expr]] = [[c_t_expr[j][i] for j in range(3)] for i in range(3)]
v_f_w: list[Expr] = []
v_t_w: list[Expr] = []
for i in range(3):
expr_vfw: Expr = c0
expr_vtw: Expr = c0
for j in range(3):
expr_vfw = expr_vfw + c_f_expr_t[i][j] * v_f[j]
expr_vtw = expr_vtw + c_t_expr_t[i][j] * v_t[j]
v_f_w.append(expr_vfw)
v_t_w.append(expr_vtw)
# ------------------------------------------------------------------
# Internal winding-side voltages.
#
# Leakage branch:
# v_leak = v_f_w - n_tap * v_t_w
#
# Core branch:
# referred only to the from side
# ------------------------------------------------------------------
n_tap: Expr = xfmr_tap_module
v_leak: list[Expr] = [v_f_w[k] - n_tap * v_t_w[k] for k in range(3)]
v_core_w: list[Expr] = [v_f_w[k] for k in range(3)]
# ------------------------------------------------------------------
# State equations.
# ------------------------------------------------------------------
state_eqs: list[Expr] = []
# Leakage-current dynamics.
for k in range(3):
state_eqs.append(l_inv * (v_leak[k] - r_sc_pu * i_leak[k]))
# Core-flux dynamics referred to the from side.
for k in range(3):
state_eqs.append(omega_base * v_core_w[k])
# Charge dynamics of terminal capacitances.
for k in range(3):
state_eqs.append(i_cap_f[k])
for k in range(3):
state_eqs.append(i_cap_t[k])
templ.block.state_eqs = state_eqs
# ------------------------------------------------------------------
# Core topology selectors.
#
# Supported values:
# 3.0 -> three-legged core
# 5.0 -> five-legged core
# ------------------------------------------------------------------
lam_return_total: Expr = -(lam_leg[0] + lam_leg[1] + lam_leg[2])
five_leg_selector: Expr = (xfmr_core_topology_code - c3) / c2
three_leg_selector: Expr = c1 - five_leg_selector
yoke_area_rel: Expr = xfmr_yoke_area_rel
yoke_length_rel: Expr = xfmr_yoke_length_rel
outer_leg_area_rel: Expr = xfmr_outer_leg_area_rel
outer_leg_length_rel: Expr = xfmr_outer_leg_length_rel
yoke_linear_coeff: Expr = xfmr_core_linear_l_pu * yoke_area_rel / (yoke_length_rel + c_eps)
outer_linear_coeff: Expr = xfmr_core_linear_l_pu * outer_leg_area_rel / (outer_leg_length_rel + c_eps)
i_plus_smoothing_equation: Expr = i_plus_smoothing_root*i_plus_smoothing_root_aux - (i_return_path * i_return_path_sq_aux + c_smooth)
yoke_frolich_lam_denom: Expr = (
xfmr_core_a_prime * yoke_length_rel
+ xfmr_core_b_prime * i_plus_smoothing_root
+ c_eps
)
outer_frolich_lam_denom: Expr = (
xfmr_core_a_prime * outer_leg_length_rel
+ xfmr_core_b_prime * i_plus_smoothing_root
+ c_eps
)
yoke_frolich_lam_nom: Expr = yoke_area_rel * i_return_path
outer_frolich_lam_nom: Expr = outer_leg_area_rel * i_return_path
# ------------------------------------------------------------------
# Projection of winding-frame currents to terminal-frame currents.
# ------------------------------------------------------------------
i_series_from: list[Expr] = _project_currents(c_f_expr, i_leak, c0)
i_series_to: list[Expr] = _project_currents(c_t_expr, i_leak, c0)
# Core branch referred only to the from side.
i_core_from_w: list[Expr] = [i_mag[k] + i_loss_leg[k] for k in range(3)]
i_core_to_w: list[Expr] = [c0, c0, c0]
i_core_from_term: list[Expr] = _project_currents(c_f_expr, i_core_from_w, c0)
i_core_to_term: list[Expr] = _project_currents(c_t_expr, i_core_to_w, c0)
# ------------------------------------------------------------------
# Algebraic equations.
# ------------------------------------------------------------------
alg_eqs: list[Expr] = []
# Multilinear auxiliary variable ties.
for k in range(3):
alg_eqs.append(i_leg_core_sq_aux[k] - i_leg_core[k])
alg_eqs.append(i_leg_plus_smoothing_sq[k] - i_leg_plus_smoothing_sq_aux[k])
alg_eqs.append(i_return_path_sq_aux - i_return_path)
alg_eqs.append(i_plus_smoothing_root - i_plus_smoothing_root_aux)
#Multilinear sqrt ties
alg_eqs.append(i_plus_smoothing_equation)
for k in range(3):
alg_eqs.append(i_leg_plus_smoothing_sq[k]*i_leg_plus_smoothing_sq_aux[k] - (i_leg_core[k] * i_leg_core_sq_aux[k] + c_smooth))
# Leg constitutive laws.
for k in range(3):
linear_leg_eq: Expr = lam_leg[k] - xfmr_core_linear_l_pu * i_leg_core[k]
nonlinear_leg_denom: Expr = (
xfmr_core_a_prime
+ xfmr_core_b_prime * i_leg_plus_smoothing_sq[k]
+ c_eps
)
nonlinear_leg_eq: Expr = lam_leg[k] * nonlinear_leg_denom - i_leg_core[k]
alg_eqs.append(
xfmr_use_linear_core * linear_leg_eq
+ (c1 - xfmr_use_linear_core) * nonlinear_leg_eq
)
# Return-path constitutive law.
five_leg_eq_linear: Expr = (c1 / c2) * lam_return_total - (
c2 * yoke_linear_coeff * i_return_path + outer_linear_coeff * i_return_path
)
five_leg_eq_nonlinear: Expr = yoke_frolich_lam_denom*outer_frolich_lam_denom*(
(c1 / c2) * lam_return_total) - (
c2 * yoke_frolich_lam_nom*outer_frolich_lam_denom + outer_frolich_lam_nom*yoke_frolich_lam_denom
)
three_leg_eq_linear: Expr = lam_return_total - c2 * yoke_linear_coeff * i_return_path
three_leg_eq_nonlinear: Expr = yoke_frolich_lam_denom*lam_return_total - c2 * yoke_frolich_lam_nom
alg_eqs.append(
five_leg_selector * (
xfmr_use_linear_core * five_leg_eq_linear
+ (c1 - xfmr_use_linear_core) * five_leg_eq_nonlinear
)
+ three_leg_selector * (
xfmr_use_linear_core * three_leg_eq_linear
+ (c1 - xfmr_use_linear_core) * three_leg_eq_nonlinear
)
)
alg_eqs.append(
i_return_total - (
five_leg_selector * c2 * i_return_path
+ three_leg_selector * i_return_path
)
)
# Magnetizing currents.
for k in range(3):
alg_eqs.append(i_mag[k] - (i_leg_core[k] - i_return_total))
# Core-loss currents referred only to the from side.
for k in range(3):
alg_eqs.append(i_loss_leg[k] - g_core_leg * v_f_w[k])
# Charge-voltage relations of terminal capacitances.
for k in range(3):
alg_eqs.append(q_f[k] - xfmr_c_term * v_f[k])
alg_eqs.append(q_t[k] - xfmr_c_term * v_t[k])
# Terminal current assembly.
for k in range(3):
alg_eqs.append(
if_act[k] - (
i_series_from[k]
+ i_core_from_term[k]
+ i_cap_f[k]
)
)
alg_eqs.append(
it_act[k] - (
-n_tap * i_series_to[k]
+ i_cap_t[k]
)
)
templ.block.algebraic_eqs = alg_eqs
templ.block.out_vars = if_act + it_act + i_mag
# ------------------------------------------------------------------
# External mapping.
# ------------------------------------------------------------------
if_keys: Dict[str, VarPowerFlowReferenceType] = {
"N": VarPowerFlowReferenceType.if_N,
"A": VarPowerFlowReferenceType.if_A,
"B": VarPowerFlowReferenceType.if_B,
"C": VarPowerFlowReferenceType.if_C,
}
it_keys: Dict[str, VarPowerFlowReferenceType] = {
"N": VarPowerFlowReferenceType.it_N,
"A": VarPowerFlowReferenceType.it_A,
"B": VarPowerFlowReferenceType.it_B,
"C": VarPowerFlowReferenceType.it_C,
}
Sf_keys: Dict[str, VarPowerFlowReferenceType] = {
"A": VarPowerFlowReferenceType.Sf_A,
"B": VarPowerFlowReferenceType.Sf_B,
"C": VarPowerFlowReferenceType.Sf_C,
}
St_keys: Dict[str, VarPowerFlowReferenceType] = {
"A": VarPowerFlowReferenceType.St_A,
"B": VarPowerFlowReferenceType.St_B,
"C": VarPowerFlowReferenceType.St_C,
}
d_vf_keys: Dict[str, VarPowerFlowReferenceType] = {
"N": VarPowerFlowReferenceType.d_v_N_f,
"A": VarPowerFlowReferenceType.d_v_A_f,
"B": VarPowerFlowReferenceType.d_v_B_f,
"C": VarPowerFlowReferenceType.d_v_C_f,
}
d_vt_keys: Dict[str, VarPowerFlowReferenceType] = {
"N": VarPowerFlowReferenceType.d_v_N_t,
"A": VarPowerFlowReferenceType.d_v_A_t,
"B": VarPowerFlowReferenceType.d_v_B_t,
"C": VarPowerFlowReferenceType.d_v_C_t,
}
mapping: Dict[VarPowerFlowReferenceType, Var | None] = {
if_keys["N"]: None,
if_keys["A"]: None,
if_keys["B"]: None,
if_keys["C"]: None,
it_keys["N"]: None,
it_keys["A"]: None,
it_keys["B"]: None,
it_keys["C"]: None,
vf_keys["N"]: None,
vf_keys["A"]: None,
vf_keys["B"]: None,
vf_keys["C"]: None,
vt_keys["N"]: None,
vt_keys["A"]: None,
vt_keys["B"]: None,
vt_keys["C"]: None,
Sf_keys["A"]: None,
Sf_keys["B"]: None,
Sf_keys["C"]: None,
St_keys["A"]: None,
St_keys["B"]: None,
St_keys["C"]: None,
d_vf_keys["N"]: None,
d_vf_keys["A"]: None,
d_vf_keys["B"]: None,
d_vf_keys["C"]: None,
d_vt_keys["N"]: None,
d_vt_keys["A"]: None,
d_vt_keys["B"]: None,
d_vt_keys["C"]: None,
}
mapping[vf_keys["A"]] = v_f[0]
mapping[vf_keys["B"]] = v_f[1]
mapping[vf_keys["C"]] = v_f[2]
mapping[vt_keys["A"]] = v_t[0]
mapping[vt_keys["B"]] = v_t[1]
mapping[vt_keys["C"]] = v_t[2]
mapping[if_keys["A"]] = if_act[0]
mapping[if_keys["B"]] = if_act[1]
mapping[if_keys["C"]] = if_act[2]
mapping[it_keys["A"]] = it_act[0]
mapping[it_keys["B"]] = it_act[1]
mapping[it_keys["C"]] = it_act[2]
templ.block.external_mapping = mapping
# ------------------------------------------------------------------
# Initialization equations.
#
# PF-derived terminal currents if_act / it_act are initialized before
# init_eqs through external_mapping. The magnetic branch is initialized
# from the from-side winding voltage only.
# ------------------------------------------------------------------
c_inv_sqrt3: Expr = c1 / sym.sqrt(c3)
lam_leg_init: list[Expr] = [
-(v_f_w[2] - v_f_w[1]) * c_inv_sqrt3,
-(v_f_w[0] - v_f_w[2]) * c_inv_sqrt3,
-(v_f_w[1] - v_f_w[0]) * c_inv_sqrt3,
]
init_eqs: Dict[Var, Expr] = {}
# Charge states.
for k in range(3):
init_eqs[q_f[k]] = xfmr_c_term * v_f[k]
init_eqs[q_t[k]] = xfmr_c_term * v_t[k]
# Core flux-linkage states from the from-side winding voltage set.
for k in range(3):
init_eqs[lam_leg[k]] = lam_leg_init[k]
# Core leg currents from the constitutive law.
for k in range(3):
init_eqs[i_leg_core[k]] = (
xfmr_use_linear_core * (lam_leg[k] / (xfmr_core_linear_l_pu + c_eps))
+ (c1 - xfmr_use_linear_core) * (lam_leg[k] / (xfmr_core_a_prime + c_eps))
)
init_eqs[i_leg_core_sq_aux[k]] = i_leg_core[k]
# Return-path currents from initialized fluxes.
init_eqs[i_return_path] = (
five_leg_selector * (lam_return_total / (c2 * yoke_linear_coeff + outer_linear_coeff + c_eps))
+ three_leg_selector * (lam_return_total / (c2 * yoke_linear_coeff + c_eps))
)
init_eqs[i_return_path_sq_aux] = i_return_path
init_eqs[i_plus_smoothing_root] = sym.sqrt(i_return_path * i_return_path_sq_aux + c_smooth)
init_eqs[i_plus_smoothing_root_aux] = i_plus_smoothing_root
init_eqs[i_return_total] = (
five_leg_selector * c2 * i_return_path
+ three_leg_selector * i_return_path
)
# Net magnetizing currents.
for k in range(3):
init_eqs[i_mag[k]] = i_leg_core[k] - i_return_total
init_eqs[i_leg_plus_smoothing_sq[k]] = sym.sqrt(i_leg_core[k] * i_leg_core_sq_aux[k] + c_smooth)
init_eqs[i_leg_plus_smoothing_sq_aux[k]] = i_leg_plus_smoothing_sq[k]
# Core-loss currents referred to the from side.
for k in range(3):
init_eqs[i_loss_leg[k]] = g_core_leg * v_f_w[k]
# Leakage-current seed from PF-initialized from-side terminal current.
for k in range(3):
init_eqs[i_leak[k]] = if_act[k]
# Capacitor-current seeds from terminal current balance.
for k in range(3):
init_eqs[i_cap_f[k]] = if_act[k] - (i_series_from[k] + i_core_from_term[k])
init_eqs[i_cap_t[k]] = it_act[k] - (-n_tap * i_series_to[k])
templ.block.init_eqs = init_eqs
# ------------------------------------------------------------------
# Differential initialization equations.
# ------------------------------------------------------------------
diff_init_eqs: Dict[Var, Expr] = {}
for k in range(3):
diff_init_eqs[di_leak[k]] = l_inv * (v_leak[k] - r_sc_pu * i_leak[k])
diff_init_eqs[dlam_leg[k]] = omega_base * v_f_w[k]
diff_init_eqs[dq_f[k]] = i_cap_f[k]
diff_init_eqs[dq_t[k]] = i_cap_t[k]
templ.block.diff_init_eqs = diff_init_eqs
return templ