# 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.001--.0-
"""
Static EMT API-object parameter mapping.
This module contains the static-device to EMT-parameter assignment layer used
by ``EmtProblemDae``. The contract is intentionally narrow:
* ``Block.api_obj_mapping`` maps ``ParamPowerFlowReferenceType`` keys to
constant symbolic EMT parameters.
* Only values that come from the static grid device, or from static network
bases such as ``Sbase`` and ``fBase``, are assigned here.
* ``Block.event_dict`` is never written by this module. Runtime/event
parameters belong to explicit/native initialization and runtime event logic.
The EMT problem parser should call ``assign_static_api_object_mapping_for_device``
after processing each device block. The EMT model template chooses which static
values it consumes by exposing only the desired enum keys in ``api_obj_mapping``.
"""
from __future__ import annotations
from typing import Any, Dict, List, Tuple, TYPE_CHECKING
import numpy as np
from VeraGridEngine.Devices.Branches.overhead_line_type import OverheadLineType
from VeraGridEngine.Devices import MultiCircuit
from VeraGridEngine.Devices.Branches.dc_line import DcLine
from VeraGridEngine.Devices.Branches.transformer import Transformer2W
from VeraGridEngine.Templates.Emt.line_matrix_conversion import build_physical_line_matrices_from_stored_admittances
from VeraGridEngine.Utils.Symbolic.block import Block
from VeraGridEngine.Utils.Symbolic.symbolic import Const, Var, Expr
from VeraGridEngine.basic_structures import Logger, Vec
from VeraGridEngine.enumerations import (
ConverterControlType,
DeviceType,
ExternalGridMode,
ParamPowerFlowReferenceType,
ShuntConnectionType,
ShuntControlMode,
WindingType,
)
if TYPE_CHECKING:
import VeraGridEngine.Devices as dev
from VeraGridEngine.Devices.types import ALL_DEV_TYPES
devices_static_params_mapping: Dict[DeviceType, List[ParamPowerFlowReferenceType]] = {
DeviceType.LoadDevice: [
ParamPowerFlowReferenceType.device_active,
ParamPowerFlowReferenceType.injection_connection_type,
ParamPowerFlowReferenceType.Pl0,
ParamPowerFlowReferenceType.Ql0,
ParamPowerFlowReferenceType.Pl0_A,
ParamPowerFlowReferenceType.Pl0_B,
ParamPowerFlowReferenceType.Pl0_C,
ParamPowerFlowReferenceType.Ql0_A,
ParamPowerFlowReferenceType.Ql0_B,
ParamPowerFlowReferenceType.Ql0_C,
ParamPowerFlowReferenceType.load_g_pu,
ParamPowerFlowReferenceType.load_b_pu,
ParamPowerFlowReferenceType.load_ga_pu,
ParamPowerFlowReferenceType.load_gb_pu,
ParamPowerFlowReferenceType.load_gc_pu,
ParamPowerFlowReferenceType.load_ba_pu,
ParamPowerFlowReferenceType.load_bb_pu,
ParamPowerFlowReferenceType.load_bc_pu,
ParamPowerFlowReferenceType.load_ir_pu,
ParamPowerFlowReferenceType.load_ii_pu,
ParamPowerFlowReferenceType.load_ira_pu,
ParamPowerFlowReferenceType.load_irb_pu,
ParamPowerFlowReferenceType.load_irc_pu,
ParamPowerFlowReferenceType.load_iia_pu,
ParamPowerFlowReferenceType.load_iib_pu,
ParamPowerFlowReferenceType.load_iic_pu,
ParamPowerFlowReferenceType.load_contract_power_pu,
ParamPowerFlowReferenceType.Pl0,
ParamPowerFlowReferenceType.g,
ParamPowerFlowReferenceType.Pl0_A,
ParamPowerFlowReferenceType.Pl0_B,
ParamPowerFlowReferenceType.Pl0_C,
ParamPowerFlowReferenceType.Ql0_A,
ParamPowerFlowReferenceType.Ql0_B,
ParamPowerFlowReferenceType.Ql0_C,
ParamPowerFlowReferenceType.omega_base,
],
DeviceType.StaticGeneratorDevice: [
ParamPowerFlowReferenceType.device_active,
ParamPowerFlowReferenceType.injection_connection_type,
ParamPowerFlowReferenceType.Pl0,
ParamPowerFlowReferenceType.Ql0,
ParamPowerFlowReferenceType.Pl0_A,
ParamPowerFlowReferenceType.Pl0_B,
ParamPowerFlowReferenceType.Pl0_C,
ParamPowerFlowReferenceType.Ql0_A,
ParamPowerFlowReferenceType.Ql0_B,
ParamPowerFlowReferenceType.Ql0_C,
ParamPowerFlowReferenceType.Pl0_A,
ParamPowerFlowReferenceType.Pl0_B,
ParamPowerFlowReferenceType.Pl0_C,
ParamPowerFlowReferenceType.Ql0_A,
ParamPowerFlowReferenceType.Ql0_B,
ParamPowerFlowReferenceType.Ql0_C,
ParamPowerFlowReferenceType.omega_base,
ParamPowerFlowReferenceType.static_generator_snom_mva,
],
DeviceType.ExternalGridDevice: [
ParamPowerFlowReferenceType.device_active,
ParamPowerFlowReferenceType.injection_connection_type,
ParamPowerFlowReferenceType.Pl0,
ParamPowerFlowReferenceType.Pl0_A,
ParamPowerFlowReferenceType.Pl0_B,
ParamPowerFlowReferenceType.Pl0_C,
ParamPowerFlowReferenceType.Ql0_A,
ParamPowerFlowReferenceType.Ql0_B,
ParamPowerFlowReferenceType.Ql0_C,
ParamPowerFlowReferenceType.omega_base,
ParamPowerFlowReferenceType.Ql0,
ParamPowerFlowReferenceType.external_grid_vm_pu,
ParamPowerFlowReferenceType.external_grid_va_rad,
ParamPowerFlowReferenceType.external_grid_mode_code,
],
DeviceType.ShuntDevice: [
ParamPowerFlowReferenceType.device_active,
ParamPowerFlowReferenceType.injection_connection_type,
ParamPowerFlowReferenceType.shunt_g_pu,
ParamPowerFlowReferenceType.shunt_b_pu,
ParamPowerFlowReferenceType.shunt_g0_pu,
ParamPowerFlowReferenceType.shunt_b0_pu,
ParamPowerFlowReferenceType.shunt_ga_pu,
ParamPowerFlowReferenceType.shunt_gb_pu,
ParamPowerFlowReferenceType.shunt_gc_pu,
ParamPowerFlowReferenceType.shunt_ba_pu,
ParamPowerFlowReferenceType.shunt_bb_pu,
ParamPowerFlowReferenceType.shunt_bc_pu,
],
DeviceType.ControllableShuntDevice: [
ParamPowerFlowReferenceType.device_active,
ParamPowerFlowReferenceType.injection_connection_type,
ParamPowerFlowReferenceType.shunt_g_pu,
ParamPowerFlowReferenceType.shunt_b_pu,
ParamPowerFlowReferenceType.shunt_g0_pu,
ParamPowerFlowReferenceType.shunt_b0_pu,
ParamPowerFlowReferenceType.shunt_ga_pu,
ParamPowerFlowReferenceType.shunt_gb_pu,
ParamPowerFlowReferenceType.shunt_gc_pu,
ParamPowerFlowReferenceType.shunt_ba_pu,
ParamPowerFlowReferenceType.shunt_bb_pu,
ParamPowerFlowReferenceType.shunt_bc_pu,
ParamPowerFlowReferenceType.controllable_shunt_step,
ParamPowerFlowReferenceType.controllable_shunt_vset_pu,
ParamPowerFlowReferenceType.controllable_shunt_vmin_pu,
ParamPowerFlowReferenceType.controllable_shunt_vmax_pu,
ParamPowerFlowReferenceType.controllable_shunt_gmin_pu,
ParamPowerFlowReferenceType.controllable_shunt_gmax_pu,
ParamPowerFlowReferenceType.controllable_shunt_bmin_pu,
ParamPowerFlowReferenceType.controllable_shunt_bmax_pu,
ParamPowerFlowReferenceType.controllable_shunt_control_mode_code,
],
DeviceType.CurrentInjectionDevice: [
ParamPowerFlowReferenceType.device_active,
ParamPowerFlowReferenceType.injection_connection_type,
ParamPowerFlowReferenceType.current_injection_ir_pu,
ParamPowerFlowReferenceType.current_injection_ii_pu,
ParamPowerFlowReferenceType.current_injection_ira_pu,
ParamPowerFlowReferenceType.current_injection_irb_pu,
ParamPowerFlowReferenceType.current_injection_irc_pu,
ParamPowerFlowReferenceType.current_injection_iia_pu,
ParamPowerFlowReferenceType.current_injection_iib_pu,
ParamPowerFlowReferenceType.current_injection_iic_pu,
],
DeviceType.GeneratorDevice: [
ParamPowerFlowReferenceType.device_active,
ParamPowerFlowReferenceType.injection_connection_type,
ParamPowerFlowReferenceType.omega_base,
ParamPowerFlowReferenceType.fn,
ParamPowerFlowReferenceType.ws,
ParamPowerFlowReferenceType.R1,
ParamPowerFlowReferenceType.X1,
ParamPowerFlowReferenceType.X0,
ParamPowerFlowReferenceType.generator_p_pu,
ParamPowerFlowReferenceType.generator_q_pu,
ParamPowerFlowReferenceType.Pmin,
ParamPowerFlowReferenceType.Pmax,
ParamPowerFlowReferenceType.generator_qmin_pu,
ParamPowerFlowReferenceType.generator_qmax_pu,
ParamPowerFlowReferenceType.generator_power_factor,
ParamPowerFlowReferenceType.generator_vset_pu,
ParamPowerFlowReferenceType.generator_snom_mva,
ParamPowerFlowReferenceType.generator_r0_pu,
ParamPowerFlowReferenceType.generator_r2_pu,
ParamPowerFlowReferenceType.generator_x2_pu,
ParamPowerFlowReferenceType.generator_control_mode,
ParamPowerFlowReferenceType.generator_enabled_dispatch,
ParamPowerFlowReferenceType.generator_must_run,
ParamPowerFlowReferenceType.generator_use_reactive_power_curve,
ParamPowerFlowReferenceType.generator_device_sbase_mva,
ParamPowerFlowReferenceType.freq,
],
DeviceType.BatteryDevice: [
ParamPowerFlowReferenceType.device_active,
ParamPowerFlowReferenceType.injection_connection_type,
ParamPowerFlowReferenceType.omega_base,
ParamPowerFlowReferenceType.fn,
ParamPowerFlowReferenceType.ws,
ParamPowerFlowReferenceType.R1,
ParamPowerFlowReferenceType.X1,
ParamPowerFlowReferenceType.X0,
ParamPowerFlowReferenceType.generator_p_pu,
ParamPowerFlowReferenceType.generator_q_pu,
ParamPowerFlowReferenceType.Pmin,
ParamPowerFlowReferenceType.Pmax,
ParamPowerFlowReferenceType.generator_qmin_pu,
ParamPowerFlowReferenceType.generator_qmax_pu,
ParamPowerFlowReferenceType.generator_power_factor,
ParamPowerFlowReferenceType.generator_vset_pu,
ParamPowerFlowReferenceType.generator_snom_mva,
ParamPowerFlowReferenceType.generator_r0_pu,
ParamPowerFlowReferenceType.generator_r2_pu,
ParamPowerFlowReferenceType.generator_x2_pu,
ParamPowerFlowReferenceType.generator_control_mode,
ParamPowerFlowReferenceType.generator_enabled_dispatch,
ParamPowerFlowReferenceType.generator_must_run,
ParamPowerFlowReferenceType.generator_use_reactive_power_curve,
ParamPowerFlowReferenceType.generator_device_sbase_mva,
ParamPowerFlowReferenceType.freq,
ParamPowerFlowReferenceType.battery_enom_mwh,
ParamPowerFlowReferenceType.battery_soc_0_pu,
ParamPowerFlowReferenceType.battery_max_soc_pu,
ParamPowerFlowReferenceType.battery_min_soc_pu,
ParamPowerFlowReferenceType.battery_charge_efficiency_pu,
ParamPowerFlowReferenceType.battery_discharge_efficiency_pu,
ParamPowerFlowReferenceType.battery_charge_per_cycle_pu,
ParamPowerFlowReferenceType.battery_discharge_per_cycle_pu,
],
DeviceType.VscDevice: [
ParamPowerFlowReferenceType.device_active,
ParamPowerFlowReferenceType.branch_rate_mva,
ParamPowerFlowReferenceType.branch_temp_base_deg_c,
ParamPowerFlowReferenceType.branch_temp_oper_deg_c,
ParamPowerFlowReferenceType.branch_alpha_per_deg_c,
ParamPowerFlowReferenceType.Sbase,
ParamPowerFlowReferenceType.omega_base,
ParamPowerFlowReferenceType.converter_control_mode_1,
ParamPowerFlowReferenceType.converter_control_mode_2,
ParamPowerFlowReferenceType.converter_control_target_1,
ParamPowerFlowReferenceType.converter_control_target_2,
ParamPowerFlowReferenceType.alpha1,
ParamPowerFlowReferenceType.alpha2,
ParamPowerFlowReferenceType.alpha3,
ParamPowerFlowReferenceType.vsc_kdp_pu,
ParamPowerFlowReferenceType.vsc_min_ac_voltage_pu,
],
DeviceType.DCLineDevice: [
ParamPowerFlowReferenceType.device_active,
ParamPowerFlowReferenceType.branch_rate_mva,
ParamPowerFlowReferenceType.branch_temp_base_deg_c,
ParamPowerFlowReferenceType.branch_temp_oper_deg_c,
ParamPowerFlowReferenceType.branch_alpha_per_deg_c,
ParamPowerFlowReferenceType.g,
ParamPowerFlowReferenceType.b,
ParamPowerFlowReferenceType.bsh,
ParamPowerFlowReferenceType.dc_line_r_pu,
ParamPowerFlowReferenceType.dc_line_length_km,
],
DeviceType.Transformer2WDevice: [
ParamPowerFlowReferenceType.device_active,
ParamPowerFlowReferenceType.branch_rate_mva,
ParamPowerFlowReferenceType.branch_temp_base_deg_c,
ParamPowerFlowReferenceType.branch_temp_oper_deg_c,
ParamPowerFlowReferenceType.branch_alpha_per_deg_c,
ParamPowerFlowReferenceType.omega_base,
ParamPowerFlowReferenceType.transformer_rated_power_mva,
ParamPowerFlowReferenceType.transformer_winding1_rated_voltage_ll_kv,
ParamPowerFlowReferenceType.transformer_winding2_rated_voltage_ll_kv,
ParamPowerFlowReferenceType.transformer_connection_clock,
ParamPowerFlowReferenceType.transformer_open_circuit_current_pct,
ParamPowerFlowReferenceType.transformer_open_circuit_loss_kw,
ParamPowerFlowReferenceType.transformer_short_circuit_voltage_pct,
ParamPowerFlowReferenceType.transformer_short_circuit_resistance_pct,
ParamPowerFlowReferenceType.transformer_short_circuit_loss_kw,
ParamPowerFlowReferenceType.tap_module,
ParamPowerFlowReferenceType.transformer_nominal_voltage_ratio,
ParamPowerFlowReferenceType.transformer_total_voltage_ratio,
ParamPowerFlowReferenceType.transformer_tap_ratio,
ParamPowerFlowReferenceType.transformer_winding1_resistance_pu,
ParamPowerFlowReferenceType.transformer_winding2_resistance_pu,
ParamPowerFlowReferenceType.transformer_winding1_inductance_pu_s,
ParamPowerFlowReferenceType.transformer_winding2_inductance_pu_s,
ParamPowerFlowReferenceType.transformer_mutual_inductance_pu_s,
ParamPowerFlowReferenceType.transformer_magnetizing_conductance_pu,
],
DeviceType.WindingDevice: [
ParamPowerFlowReferenceType.device_active,
ParamPowerFlowReferenceType.branch_rate_mva,
ParamPowerFlowReferenceType.branch_temp_base_deg_c,
ParamPowerFlowReferenceType.branch_temp_oper_deg_c,
ParamPowerFlowReferenceType.branch_alpha_per_deg_c,
ParamPowerFlowReferenceType.omega_base,
ParamPowerFlowReferenceType.transformer_rated_power_mva,
ParamPowerFlowReferenceType.transformer_winding1_rated_voltage_ll_kv,
ParamPowerFlowReferenceType.transformer_winding2_rated_voltage_ll_kv,
ParamPowerFlowReferenceType.transformer_connection_clock,
ParamPowerFlowReferenceType.transformer_open_circuit_current_pct,
ParamPowerFlowReferenceType.transformer_open_circuit_loss_kw,
ParamPowerFlowReferenceType.transformer_short_circuit_voltage_pct,
ParamPowerFlowReferenceType.transformer_short_circuit_resistance_pct,
ParamPowerFlowReferenceType.transformer_short_circuit_loss_kw,
ParamPowerFlowReferenceType.tap_module,
ParamPowerFlowReferenceType.transformer_nominal_voltage_ratio,
ParamPowerFlowReferenceType.transformer_total_voltage_ratio,
ParamPowerFlowReferenceType.transformer_tap_ratio,
ParamPowerFlowReferenceType.transformer_winding1_resistance_pu,
ParamPowerFlowReferenceType.transformer_winding2_resistance_pu,
ParamPowerFlowReferenceType.transformer_winding1_inductance_pu_s,
ParamPowerFlowReferenceType.transformer_winding2_inductance_pu_s,
ParamPowerFlowReferenceType.transformer_mutual_inductance_pu_s,
ParamPowerFlowReferenceType.transformer_magnetizing_conductance_pu,
],
DeviceType.LineDevice: [
ParamPowerFlowReferenceType.device_active,
ParamPowerFlowReferenceType.branch_rate_mva,
ParamPowerFlowReferenceType.branch_temp_base_deg_c,
ParamPowerFlowReferenceType.branch_temp_oper_deg_c,
ParamPowerFlowReferenceType.branch_alpha_per_deg_c,
ParamPowerFlowReferenceType.line_length_km,
],
DeviceType.HVDCLineDevice: [],
DeviceType.SeriesReactanceDevice: [],
DeviceType.SwitchDevice: [],
DeviceType.UpfcDevice: [],
}
[docs]
def to_static_mapping_const(value: float | Const) -> Const:
"""
Convert a static numerical mapping value into a symbolic constant.
:param value: Numerical value or an already-created ``Const``.
:return: Constant symbolic expression used by EMT parameters.
"""
out: Const
if isinstance(value, Const):
# Existing constants are preserved so callers can pre-build symbolic
# constants without being wrapped a second time.
out = value
else:
# Static API-object values are scalar real constants. The explicit float
# conversion keeps parameter values homogeneous in the EMT parameter map.
out = Const(float(value))
return out
[docs]
def add_static_mapping_warning(
logger: Logger | None,
device_name: str,
message: str,
value: str,
expected_value: str,
device_property: str,
) -> None:
"""
Add one diagnostic warning for a static mapping issue.
:param logger: Optional EMT logger.
:param device_name: Device name used in diagnostics.
:param message: Warning message.
:param value: Actual problematic value.
:param expected_value: Expected value or condition.
:param device_property: Device property involved in the warning.
:return: None.
"""
if logger is None:
# The mapper can be used in small unit tests without a full EMT logger.
# In that case, invalid optional mappings are skipped silently.
pass
else:
logger.add_warning(
msg=message,
device=device_name,
value=value,
expected_value=expected_value,
device_class="EMT",
device_property=device_property,
)
[docs]
def assign_api_mapping_value_if_present(
mdl: Block,
key: ParamPowerFlowReferenceType,
value: float,
logger: Logger | None,
device_name: str,
) -> bool:
"""
Assign one static API-object value to ``mdl.parameters`` if exposed.
This function is the only low-level writer used by this module. It never
writes into ``mdl.event_dict`` because event parameters are model-dynamic
or runtime parameters initialized by the explicit/native initialization path.
:param mdl: EMT symbolic block receiving the static parameter.
:param key: Static API-object reference key.
:param value: Static numerical value to assign.
:param logger: Optional logger used to report invalid mapping contracts.
:param device_name: Device name used in diagnostics.
:return: ``True`` if the value was assigned, ``False`` otherwise.
"""
assigned: bool = False
api_obj_mapping: Dict[ParamPowerFlowReferenceType, Var] = mdl.api_obj_mapping
# The mapping is optional: a model receives only the static parameters it
# explicitly requests by exposing the enum key.
target: Var | None = api_obj_mapping.get(key, None)
if target is None:
assigned = False
else:
event_dict: Dict[Var, Expr | Const] = mdl.event_dict
if target in event_dict:
# A target present in event_dict means the model declared a
# runtime parameter as a static API-object parameter. That is
# a contract error unless the caller explicitly opted into
# seeding runtime parameters from static device data.
add_static_mapping_warning(
logger=logger,
device_name=device_name,
message="Static api_obj_mapping points to an event_dict parameter.",
value=str(key),
expected_value="api_obj_mapping target must be a constant parameter",
device_property="api_obj_mapping",
)
assigned = False
else:
# The static mapping declares the symbolic parameter target.
# The target does not need to pre-exist in mdl.parameters.
mdl.parameters[target] = to_static_mapping_const(value)
assigned = True
return assigned
[docs]
def api_mapping_has_any_key(
mdl: Block,
keys: List[ParamPowerFlowReferenceType],
) -> bool:
"""
Return whether the block exposes at least one of the provided static keys.
:param mdl: EMT symbolic block.
:param keys: API-object enum keys to test.
:return: ``True`` if any key is present in ``mdl.api_obj_mapping``.
"""
has_key: bool = False
api_obj_mapping: Dict[ParamPowerFlowReferenceType, Var] = mdl.api_obj_mapping
if isinstance(api_obj_mapping, dict):
key_index: int = 0
while key_index < len(keys):
key: ParamPowerFlowReferenceType = keys[key_index]
if key in api_obj_mapping:
# Do not break the loop: keeping the loop explicit avoids control
# flow surprises and follows the local style constraints.
has_key = True
else:
pass
key_index += 1
else:
has_key = False
return has_key
[docs]
def boolean_to_float(value: bool) -> float:
"""
Convert a static boolean into the EMT scalar convention.
:param value: Static boolean value.
:return: ``1.0`` when ``True``, ``0.0`` when ``False``.
"""
out: float
if value:
out = 1.0
else:
out = 0.0
return out
[docs]
def omega_base_from_grid(grid: MultiCircuit) -> float:
"""
Return the angular base frequency of the network.
:param grid: Static network model.
:return: Angular base frequency in rad/s.
"""
return 2.0 * np.pi * float(grid.fBase)
[docs]
def frequency_base_hz_from_grid(grid: MultiCircuit) -> float:
"""
Return the nominal frequency base of the network.
:param grid: Static network model.
:return: Nominal frequency in Hz.
"""
return float(grid.fBase)
[docs]
def mw_to_pu_on_grid_base(value_mw: float, grid: MultiCircuit) -> float:
"""
Convert MW-like static power to p.u. on ``grid.Sbase``.
:param value_mw: Static active-power-like quantity in MW.
:param grid: Static network model.
:return: Per-unit value on ``grid.Sbase``.
"""
return float(value_mw) / float(grid.Sbase)
[docs]
def mvar_to_pu_on_grid_base(value_mvar: float, grid: MultiCircuit) -> float:
"""
Convert MVAr-like static power to p.u. on ``grid.Sbase``.
:param value_mvar: Static reactive-power-like quantity in MVAr.
:param grid: Static network model.
:return: Per-unit value on ``grid.Sbase``.
"""
return float(value_mvar) / float(grid.Sbase)
def _float_or_zero(value: float | None) -> float:
"""
Return one scalar value, replacing ``None`` with zero.
:param value: Optional scalar value.
:return: Float scalar with ``None`` mapped to ``0.0``.
"""
out: float
if value is None:
out = 0.0
else:
out = float(value)
return out
def _abc_values_are_meaningful(
phase_a: float | None,
phase_b: float | None,
phase_c: float | None,
) -> bool:
"""
Return whether explicit ABC values should override balanced fallback.
The static-device classes do not expose a dedicated "phase values are
provided" flag. The practical contract used here is therefore explicit and
conservative: if any phase value is numerically non-zero, the phase triplet is
treated as meaningful and is preserved.
:param phase_a: Phase-A scalar.
:param phase_b: Phase-B scalar.
:param phase_c: Phase-C scalar.
:return: ``True`` when explicit phase values should be used.
"""
value_a: float = _float_or_zero(phase_a)
value_b: float = _float_or_zero(phase_b)
value_c: float = _float_or_zero(phase_c)
meaningful: bool
if abs(value_a) > 0.0:
meaningful = True
else:
if abs(value_b) > 0.0:
meaningful = True
else:
if abs(value_c) > 0.0:
meaningful = True
else:
meaningful = False
return meaningful
[docs]
def balanced_to_abc_power(total_value: float | None) -> np.ndarray:
"""
Split one balanced total scalar equally into explicit ABC values.
:param total_value: Balanced total scalar.
:return: ABC array with one third of the total in each phase.
"""
total_scalar: float = _float_or_zero(total_value)
phase_scalar: float = total_scalar / 3.0
phase_values: np.ndarray = np.zeros(3, dtype=np.float64)
phase_values[0] = phase_scalar
phase_values[1] = phase_scalar
phase_values[2] = phase_scalar
return phase_values
def _resolve_explicit_or_balanced_abc_values(
phase_a: float | None,
phase_b: float | None,
phase_c: float | None,
total_value: float | None,
) -> np.ndarray:
"""
Resolve one ABC quantity from explicit phase values or from a balanced total.
:param phase_a: Phase-A scalar.
:param phase_b: Phase-B scalar.
:param phase_c: Phase-C scalar.
:param total_value: Balanced total scalar used as fallback.
:return: Explicit ABC array.
"""
phase_values: np.ndarray = np.zeros(3, dtype=np.float64)
if _abc_values_are_meaningful(phase_a=phase_a, phase_b=phase_b, phase_c=phase_c):
phase_values[0] = _float_or_zero(phase_a)
phase_values[1] = _float_or_zero(phase_b)
phase_values[2] = _float_or_zero(phase_c)
else:
phase_values = balanced_to_abc_power(total_value=total_value)
return phase_values
[docs]
def abc_to_nabc(phase_values_abc: np.ndarray) -> np.ndarray:
"""
Expand an ABC phase array into fixed NABC order.
:param phase_values_abc: Explicit ABC array.
:return: NABC array with a zero neutral entry.
"""
phase_values_nabc: np.ndarray = np.zeros(4, dtype=np.float64)
phase_values_nabc[0] = 0.0
phase_values_nabc[1] = float(phase_values_abc[0])
phase_values_nabc[2] = float(phase_values_abc[1])
phase_values_nabc[3] = float(phase_values_abc[2])
return phase_values_nabc
[docs]
def get_load_power_phase_values_pu_abc(load: dev.Load, grid: MultiCircuit) -> Tuple[np.ndarray, np.ndarray]:
"""
Return explicit ABC active/reactive load powers in p.u. on ``grid.Sbase``.
Explicit phase values are used when they are meaningfully populated.
Otherwise, the balanced total values are split equally among phases.
:param load: Load-like static device.
:param grid: Static network model.
:return: Pair ``(p_abc_pu, q_abc_pu)``.
"""
p_mw_abc: np.ndarray = _resolve_explicit_or_balanced_abc_values(
phase_a=load.Pa,
phase_b=load.Pb,
phase_c=load.Pc,
total_value=load.P,
)
q_mvar_abc: np.ndarray = _resolve_explicit_or_balanced_abc_values(
phase_a=load.Qa,
phase_b=load.Qb,
phase_c=load.Qc,
total_value=load.Q,
)
p_pu_abc: np.ndarray = p_mw_abc / float(grid.Sbase)
q_pu_abc: np.ndarray = q_mvar_abc / float(grid.Sbase)
return p_pu_abc, q_pu_abc
[docs]
def get_load_impedance_phase_values_pu_abc(load: dev.Load, grid: MultiCircuit) -> Tuple[np.ndarray, np.ndarray]:
"""
Return explicit ABC ZIP-impedance powers in p.u. on ``grid.Sbase``.
:param load: Load-like static device exposing ``G``/``B`` totals and
``G1``/``G2``/``G3`` plus ``B1``/``B2``/``B3`` phase values.
:param grid: Static network model.
:return: Pair ``(g_abc_pu, b_abc_pu)``.
"""
g_mw_abc: np.ndarray = _resolve_explicit_or_balanced_abc_values(
phase_a=load.G1,
phase_b=load.G2,
phase_c=load.G3,
total_value=load.G,
)
b_mvar_abc: np.ndarray = _resolve_explicit_or_balanced_abc_values(
phase_a=load.B1,
phase_b=load.B2,
phase_c=load.B3,
total_value=load.B,
)
g_pu_abc: np.ndarray = g_mw_abc / float(grid.Sbase)
b_pu_abc: np.ndarray = b_mvar_abc / float(grid.Sbase)
return g_pu_abc, b_pu_abc
[docs]
def get_load_current_phase_values_pu_abc(load: dev.Load, grid: MultiCircuit) -> Tuple[np.ndarray, np.ndarray]:
"""
Return explicit ABC ZIP-current powers in p.u. on ``grid.Sbase``.
:param load: Load-like static device exposing ``Ir``/``Ii`` totals and
``Ir1``/``Ir2``/``Ir3`` plus ``Ii1``/``Ii2``/``Ii3`` phase values.
:param grid: Static network model.
:return: Pair ``(ir_abc_pu, ii_abc_pu)``.
"""
ir_mw_abc: np.ndarray = _resolve_explicit_or_balanced_abc_values(
phase_a=load.Ir1,
phase_b=load.Ir2,
phase_c=load.Ir3,
total_value=load.Ir,
)
ii_mvar_abc: np.ndarray = _resolve_explicit_or_balanced_abc_values(
phase_a=load.Ii1,
phase_b=load.Ii2,
phase_c=load.Ii3,
total_value=load.Ii,
)
ir_pu_abc: np.ndarray = ir_mw_abc / float(grid.Sbase)
ii_pu_abc: np.ndarray = ii_mvar_abc / float(grid.Sbase)
return ir_pu_abc, ii_pu_abc
[docs]
def get_shunt_phase_values_pu_abc(shunt: dev.Shunt, grid: MultiCircuit) -> Tuple[np.ndarray, np.ndarray]:
"""
Return explicit ABC shunt conductance/susceptance powers in p.u.
Explicit phase values are used when meaningfully populated. Otherwise the
balanced totals are split equally among phases.
:param shunt: Shunt-like static device.
:param grid: Static network model.
:return: Pair ``(g_abc_pu, b_abc_pu)``.
"""
g_mw_abc: np.ndarray = _resolve_explicit_or_balanced_abc_values(
phase_a=shunt.Ga,
phase_b=shunt.Gb,
phase_c=shunt.Gc,
total_value=shunt.G,
)
b_mvar_abc: np.ndarray = _resolve_explicit_or_balanced_abc_values(
phase_a=shunt.Ba,
phase_b=shunt.Bb,
phase_c=shunt.Bc,
total_value=shunt.B,
)
g_pu_abc: np.ndarray = g_mw_abc / float(grid.Sbase)
b_pu_abc: np.ndarray = b_mvar_abc / float(grid.Sbase)
return g_pu_abc, b_pu_abc
[docs]
def get_current_injection_phase_values_pu_abc(
current_injection: dev.CurrentInjection,
grid: MultiCircuit,
) -> Tuple[np.ndarray, np.ndarray]:
"""
Return explicit ABC current-injection powers in p.u. on ``grid.Sbase``.
:param current_injection: Current-injection static device.
:param grid: Static network model.
:return: Pair ``(ir_abc_pu, ii_abc_pu)``.
"""
ir_mw_abc: np.ndarray = _resolve_explicit_or_balanced_abc_values(
phase_a=current_injection.Ir1,
phase_b=current_injection.Ir2,
phase_c=current_injection.Ir3,
total_value=current_injection.Ir,
)
ii_mvar_abc: np.ndarray = _resolve_explicit_or_balanced_abc_values(
phase_a=current_injection.Ii1,
phase_b=current_injection.Ii2,
phase_c=current_injection.Ii3,
total_value=current_injection.Ii,
)
ir_pu_abc: np.ndarray = ir_mw_abc / float(grid.Sbase)
ii_pu_abc: np.ndarray = ii_mvar_abc / float(grid.Sbase)
return ir_pu_abc, ii_pu_abc
[docs]
def shunt_connection_code(connection_type: ShuntConnectionType) -> float:
"""
Convert a static shunt connection enum into a stable EMT code.
:param connection_type: Static connection enum.
:return: Numerical code used by static EMT parameters.
"""
code: float
if connection_type == ShuntConnectionType.GroundedStar:
code = 1.0
elif connection_type == ShuntConnectionType.FloatingStar:
code = 2.0
elif connection_type == ShuntConnectionType.NeutralStar:
code = 3.0
elif connection_type == ShuntConnectionType.Delta:
code = 4.0
else:
code = 0.0
return code
[docs]
def external_grid_mode_code(mode: ExternalGridMode) -> float:
"""
Convert a static external-grid mode enum into a stable EMT code.
:param mode: External-grid mode.
:return: Numerical code used by static EMT parameters.
"""
code: float
if mode == ExternalGridMode.PQ:
code = 1.0
elif mode == ExternalGridMode.PV:
code = 2.0
elif mode == ExternalGridMode.VD:
code = 3.0
else:
code = 0.0
return code
[docs]
def controllable_shunt_mode_code(mode: ShuntControlMode) -> float:
"""
Convert a controllable-shunt control mode into a stable EMT code.
:param mode: Static shunt control mode.
:return: Numerical code used by static EMT parameters.
"""
code: float
if mode == ShuntControlMode.Locked:
code = 1.0
elif mode == ShuntControlMode.Continuous:
code = 2.0
elif mode == ShuntControlMode.Discrete:
code = 3.0
else:
code = 0.0
return code
[docs]
def assign_static_api_object_mapping_for_device(
grid: MultiCircuit,
device: ALL_DEV_TYPES,
mdl: Block,
logger: Logger | None,
) -> None:
"""
Assign static API-object parameters for one EMT device block.
The dispatcher is generic with respect to RMS/EMT templates. A template receives
values only for the ``ParamPowerFlowReferenceType`` keys it exposes in
``mdl.api_obj_mapping``. Adding a new EMT model for an already-supported
``DeviceType`` should not require modifying ``EmtProblemDae``.
:param grid: Static network model containing base values.
:param device: Static grid device owning the EMT model.
:param mdl: EMT block receiving static parameter constants.
:param logger: Optional logger used for diagnostics.
:return: None.
"""
api_obj_mapping: Dict[ParamPowerFlowReferenceType, Var] = mdl.api_obj_mapping
if isinstance(api_obj_mapping, dict):
if len(api_obj_mapping) == 0:
pass
else:
# The dispatch is based on the static device type, not on the EMT
# template name. The template only selects a subset of keys.
device_type: DeviceType = device.device_type
if device_type == DeviceType.LoadDevice:
assign_load_static_api_mapping(
grid=grid,
load=device,
mdl=mdl,
logger=logger,
)
elif device_type == DeviceType.StaticGeneratorDevice:
assign_static_generator_static_api_mapping(
grid=grid,
static_generator=device,
mdl=mdl,
logger=logger,
)
elif device_type == DeviceType.ExternalGridDevice:
assign_external_grid_static_api_mapping(
grid=grid,
external_grid=device,
mdl=mdl,
logger=logger,
)
elif device_type == DeviceType.ShuntDevice:
assign_shunt_static_api_mapping(
grid=grid,
shunt=device,
mdl=mdl,
logger=logger,
)
elif device_type == DeviceType.ControllableShuntDevice:
assign_controllable_shunt_static_api_mapping(
grid=grid,
controllable_shunt=device,
mdl=mdl,
logger=logger,
)
elif device_type == DeviceType.CurrentInjectionDevice:
assign_current_injection_static_api_mapping(
grid=grid,
current_injection=device,
mdl=mdl,
logger=logger,
)
elif device_type == DeviceType.GeneratorDevice:
assign_generator_static_api_mapping(
grid=grid,
generator=device,
mdl=mdl,
logger=logger,
)
elif device_type == DeviceType.BatteryDevice:
assign_battery_static_api_mapping(
grid=grid,
battery=device,
mdl=mdl,
logger=logger,
)
elif device_type == DeviceType.VscDevice:
assign_vsc_static_api_mapping(
grid=grid,
vsc=device,
mdl=mdl,
logger=logger,
)
elif device_type == DeviceType.DCLineDevice:
if isinstance(device, DcLine):
assign_dc_line_static_api_mapping(
dc_line=device,
mdl=mdl,
logger=logger,
)
else:
add_static_mapping_warning(
logger=logger,
device_name=str(device.name),
message="DCLineDevice static mapping skipped because the object type is not DcLine.",
value=str(device_type),
expected_value="DcLine instance",
device_property="device_type",
)
elif device_type == DeviceType.Transformer2WDevice:
if isinstance(device, Transformer2W):
assign_transformer2w_static_api_mapping(
grid=grid,
transformer=device,
mdl=mdl,
logger=logger,
)
else:
add_static_mapping_warning(
logger=logger,
device_name=str(device.name),
message="Transformer2WDevice static mapping skipped because the object type is not Transformer2W.",
value=str(device_type),
expected_value="Transformer2W instance",
device_property="device_type",
)
elif device_type == DeviceType.WindingDevice:
if isinstance(device, Transformer2W):
assign_transformer2w_static_api_mapping(
grid=grid,
transformer=device,
mdl=mdl,
logger=logger,
)
else:
add_static_mapping_warning(
logger=logger,
device_name=str(device.name),
message="WindingDevice static mapping skipped because the object type is not Transformer2W-compatible.",
value=str(device_type),
expected_value="Transformer2W-compatible instance",
device_property="device_type",
)
elif device_type == DeviceType.LineDevice:
assign_line_static_api_mapping(
grid=grid,
line=device,
mdl=mdl,
logger=logger,
)
elif device_type == DeviceType.HVDCLineDevice:
assign_hvdc_line_static_api_mapping(
hvdc_line=device,
mdl=mdl,
logger=logger,
)
elif device_type == DeviceType.SeriesReactanceDevice:
assign_series_reactance_static_api_mapping(
series_reactance=device,
mdl=mdl,
logger=logger,
)
elif device_type == DeviceType.SwitchDevice:
assign_switch_static_api_mapping(
switch=device,
mdl=mdl,
logger=logger,
)
elif device_type == DeviceType.UpfcDevice:
assign_upfc_static_api_mapping(
upfc=device,
mdl=mdl,
logger=logger,
)
else:
# Compatibility fallback: older branch-like models may expose
# line matrix keys even if their device_type is more generic.
# The fallback activates only when the EMT template explicitly
# requests line-matrix keys.
if api_mapping_has_any_key(mdl=mdl, keys=get_all_line_matrix_keys()):
assign_line_static_api_mapping(
grid=grid,
line=device,
mdl=mdl,
logger=logger,
)
else:
pass
else:
pass
[docs]
def converter_control_code(control_tpe: ConverterControlType | None) -> float:
"""
Convert a static VSC control enum to the EMT template numerical code.
:param control_tpe: Converter control enum or ``None``.
:return: Numerical control code used by existing EMT templates.
"""
code: float
if control_tpe is None:
code = 0.0
elif control_tpe == ConverterControlType.Vm_dc:
code = 1.0
elif control_tpe == ConverterControlType.Vm_ac:
code = 2.0
elif control_tpe == ConverterControlType.Va_ac:
code = 3.0
elif control_tpe == ConverterControlType.Qac:
code = 4.0
elif control_tpe == ConverterControlType.Pdc:
code = 5.0
elif control_tpe == ConverterControlType.Pac:
code = 6.0
elif control_tpe == ConverterControlType.Pdc_angle_droop:
code = 7.0
elif control_tpe == ConverterControlType.Imax:
code = 8.0
else:
code = 0.0
return code
[docs]
def xfmr_connection_matrix(winding_tpe: WindingType) -> np.ndarray:
"""
Return the 3x3 transformer winding connection matrix.
:param winding_tpe: Transformer winding connection type.
:return: 3x3 winding connection matrix.
"""
matrix: np.ndarray
if winding_tpe in (
WindingType.GroundedStar,
WindingType.NeutralStar,
WindingType.FloatingStar,
):
matrix = np.eye(3, dtype=float)
else:
if winding_tpe == WindingType.Delta:
matrix = np.array(
[
[1.0, 0.0, -1.0],
[-1.0, 1.0, 0.0],
[0.0, -1.0, 1.0],
],
dtype=float,
) / np.sqrt(3.0)
else:
matrix = np.eye(3, dtype=float)
return matrix
[docs]
def xfmr_phase_permutation_matrix(clock: int) -> np.ndarray:
"""
Return the transformer phase permutation matrix from vector-group clock.
:param clock: Transformer vector-group clock number.
:return: 3x3 phase permutation matrix.
"""
matrix: np.ndarray
shift: int = (int(clock) // 4) % 3
if shift == 0:
matrix = np.eye(3, dtype=float)
else:
if shift == 1:
matrix = np.array(
[
[0.0, 1.0, 0.0],
[0.0, 0.0, 1.0],
[1.0, 0.0, 0.0],
],
dtype=float,
)
else:
matrix = np.array(
[
[0.0, 0.0, 1.0],
[1.0, 0.0, 0.0],
[0.0, 1.0, 0.0],
],
dtype=float,
)
return matrix
[docs]
def assign_3x3_static_matrix(
mdl: Block,
matrix: np.ndarray,
keys: List[List[ParamPowerFlowReferenceType]],
logger: Logger | None,
device_name: str,
) -> None:
"""
Assign a 3x3 static matrix through a 3x3 API-object key matrix.
:param mdl: EMT block.
:param matrix: Numerical 3x3 matrix.
:param keys: API-object key matrix.
:param logger: Optional logger.
:param device_name: Device name used in diagnostics.
:return: None.
"""
row_index: int = 0
while row_index < 3:
col_index: int = 0
while col_index < 3:
assign_api_mapping_value_if_present(
mdl=mdl,
key=keys[row_index][col_index],
value=float(matrix[row_index, col_index]),
logger=logger,
device_name=device_name,
)
col_index += 1
row_index += 1
[docs]
def get_line_r_keys() -> List[List[ParamPowerFlowReferenceType]]:
"""
Return the fixed 4x4 NABC resistance mapping keys.
:return: Matrix of resistance API-object keys.
"""
keys: List[List[ParamPowerFlowReferenceType]] = list([
list([
ParamPowerFlowReferenceType.Rnn,
ParamPowerFlowReferenceType.Rna,
ParamPowerFlowReferenceType.Rnb,
ParamPowerFlowReferenceType.Rnc,
]),
list([
ParamPowerFlowReferenceType.Ran,
ParamPowerFlowReferenceType.Raa,
ParamPowerFlowReferenceType.Rab,
ParamPowerFlowReferenceType.Rac,
]),
list([
ParamPowerFlowReferenceType.Rbn,
ParamPowerFlowReferenceType.Rba,
ParamPowerFlowReferenceType.Rbb,
ParamPowerFlowReferenceType.Rbc,
]),
list([
ParamPowerFlowReferenceType.Rcn,
ParamPowerFlowReferenceType.Rca,
ParamPowerFlowReferenceType.Rcb,
ParamPowerFlowReferenceType.Rcc,
]),
])
return keys
[docs]
def get_line_linv_keys() -> List[List[ParamPowerFlowReferenceType]]:
"""
Return the fixed 4x4 NABC inverse-inductance mapping keys.
:return: Matrix of inverse-inductance API-object keys.
"""
keys: List[List[ParamPowerFlowReferenceType]] = list([
list([
ParamPowerFlowReferenceType.Linv_nn,
ParamPowerFlowReferenceType.Linv_na,
ParamPowerFlowReferenceType.Linv_nb,
ParamPowerFlowReferenceType.Linv_nc,
]),
list([
ParamPowerFlowReferenceType.Linv_an,
ParamPowerFlowReferenceType.Linv_aa,
ParamPowerFlowReferenceType.Linv_ab,
ParamPowerFlowReferenceType.Linv_ac,
]),
list([
ParamPowerFlowReferenceType.Linv_bn,
ParamPowerFlowReferenceType.Linv_ba,
ParamPowerFlowReferenceType.Linv_bb,
ParamPowerFlowReferenceType.Linv_bc,
]),
list([
ParamPowerFlowReferenceType.Linv_cn,
ParamPowerFlowReferenceType.Linv_ca,
ParamPowerFlowReferenceType.Linv_cb,
ParamPowerFlowReferenceType.Linv_cc,
]),
])
return keys
[docs]
def get_line_c_keys() -> List[List[ParamPowerFlowReferenceType]]:
"""
Return the fixed 4x4 NABC capacitance mapping keys.
:return: Matrix of capacitance API-object keys.
"""
keys: List[List[ParamPowerFlowReferenceType]] = list([
list([
ParamPowerFlowReferenceType.Cnn,
ParamPowerFlowReferenceType.Cna,
ParamPowerFlowReferenceType.Cnb,
ParamPowerFlowReferenceType.Cnc,
]),
list([
ParamPowerFlowReferenceType.Can,
ParamPowerFlowReferenceType.Caa,
ParamPowerFlowReferenceType.Cab,
ParamPowerFlowReferenceType.Cac,
]),
list([
ParamPowerFlowReferenceType.Cbn,
ParamPowerFlowReferenceType.Cba,
ParamPowerFlowReferenceType.Cbb,
ParamPowerFlowReferenceType.Cbc,
]),
list([
ParamPowerFlowReferenceType.Ccn,
ParamPowerFlowReferenceType.Cca,
ParamPowerFlowReferenceType.Ccb,
ParamPowerFlowReferenceType.Ccc,
]),
])
return keys
[docs]
def flatten_matrix_keys(
matrix_keys: List[List[ParamPowerFlowReferenceType]],
) -> List[ParamPowerFlowReferenceType]:
"""
Flatten a matrix of API-object enum keys.
:param matrix_keys: Matrix of API-object keys.
:return: Flat list of keys.
"""
flat_keys: List[ParamPowerFlowReferenceType] = list()
row_index: int = 0
while row_index < len(matrix_keys):
col_index: int = 0
while col_index < len(matrix_keys[row_index]):
flat_keys.append(matrix_keys[row_index][col_index])
col_index += 1
row_index += 1
return flat_keys
[docs]
def get_all_line_matrix_keys() -> List[ParamPowerFlowReferenceType]:
"""
Return every line R, Linv and C API-object key.
:return: Flat list of all line matrix mapping keys.
"""
all_keys: List[ParamPowerFlowReferenceType] = list()
key_matrices: List[List[List[ParamPowerFlowReferenceType]]] = list([
get_line_r_keys(),
get_line_linv_keys(),
get_line_c_keys(),
])
matrix_index: int = 0
while matrix_index < len(key_matrices):
flat_keys: List[ParamPowerFlowReferenceType] = flatten_matrix_keys(key_matrices[matrix_index])
key_index: int = 0
while key_index < len(flat_keys):
all_keys.append(flat_keys[key_index])
key_index += 1
matrix_index += 1
return all_keys
[docs]
def get_line_active_global_indices(line: dev.Line) -> List[int]:
"""
Return active NABC phase indices for a line-like device.
:param line: Line-like branch device.
:return: Active indices in fixed NABC order.
"""
phase_mask: List[bool] = list([
bool(line.ys.phN),
bool(line.ys.phA),
bool(line.ys.phB),
bool(line.ys.phC),
])
active_indices: List[int] = list()
phase_index: int = 0
while phase_index < len(phase_mask):
if phase_mask[phase_index]:
active_indices.append(phase_index)
else:
pass
phase_index += 1
return active_indices
[docs]
def build_uncoupled_line_static_matrices(
line: dev.Line,
omega_base: float,
) -> Tuple[np.ndarray, np.ndarray, np.ndarray]:
"""
Build uncoupled fallback line matrices.
This preserves the historical fallback used when no tower/template matrix is
available. The historical code uses ``X = line.B`` and ``B = line.X`` in
this fallback; that convention is preserved here to avoid a hidden numerical
behavior change in the mapping refactor.
:param line: Line-like branch device.
:param omega_base: Base angular frequency.
:return: Tuple ``(R, L, C)`` in EMT per-unit form.
"""
r_value: float = float(line.R)
# Historical fallback naming intentionally preserved for the common case.
# Some saved grids still rely on ``line.B`` feeding the series-reactance slot.
# However, when that historical slot is zero while the direct line reactance is
# non-zero, the EMT pi-line becomes singular and loses all series dynamics.
# In that narrow case we prefer the physical direct interpretation.
x_value: float = float(line.B)
b_value: float = float(line.X)
if abs(x_value) <= 1.0e-15 and abs(float(line.X)) > 1.0e-15:
x_value = float(line.X)
b_value = float(line.B)
else:
pass
r_full: np.ndarray = np.zeros((3, 3), dtype=np.float64)
x_full: np.ndarray = np.zeros((3, 3), dtype=np.float64)
bsh_full: np.ndarray = np.zeros((3, 3), dtype=np.float64)
phase_index: int = 0
while phase_index < 3:
r_full[phase_index, phase_index] = r_value
x_full[phase_index, phase_index] = x_value
bsh_full[phase_index, phase_index] = b_value
phase_index += 1
l_full: np.ndarray = x_full / (omega_base + 1.0e-20)
c_full: np.ndarray = (bsh_full / (omega_base + 1.0e-20)) / 2.0
return r_full, l_full, c_full
[docs]
def build_line_static_matrices(
grid: MultiCircuit,
line: dev.Line,
) -> Tuple[np.ndarray, np.ndarray, np.ndarray]:
"""
Build line R, L and C matrices in EMT per-unit form.
:param grid: Static network model.
:param line: Line-like branch device.
:return: Tuple ``(R, L, C)`` in reduced active-phase coordinates.
"""
frequency_base: float = float(grid.fBase)
omega_base: float = 2.0 * np.pi * frequency_base
voltage_base: float = float(line.bus_from.Vnom) * 1.0e3
sbase_va: float = float(grid.Sbase) * 1.0e6
zbase: float = (voltage_base * voltage_base) / sbase_va
ybase: float = 1.0 / zbase
line_template: dev.OverheadLineType | dev.UndergroundLineType | dev.SequenceLineType = line.template
if isinstance(line_template, OverheadLineType):
r_full: np.ndarray
l_full: np.ndarray
c_full: np.ndarray
r_full, l_full, c_full = build_uncoupled_line_static_matrices(
line=line,
omega_base=omega_base,
)
# Persisted line objects already store the total branch matrices after the
# template application. EMT should therefore rebuild physical matrices
# from the stored branch data instead of reading the original template.
z_phys: np.ndarray | None
y_phys: np.ndarray | None
z_phys, y_phys = build_physical_line_matrices_from_stored_admittances(
line=line,
sbase_mva=float(grid.Sbase),
)
if z_phys is None or y_phys is None:
# Some legacy objects may still miss the stored matrices. In that case
# the original template Carson matrices remain a safe fallback path.
z_phys = line_template.z_nabc * float(line.length)
y_phys = line_template.y_nabc * float(line.length)
else:
pass
z_pu: np.ndarray = z_phys / zbase
y_pu: np.ndarray = y_phys / ybase
r_full = np.real(z_pu)
x_full: np.ndarray = np.imag(z_pu)
l_full = x_full / (omega_base + 1.0e-20)
bsh_full: np.ndarray = np.imag(y_pu)
c_full = (bsh_full / (omega_base + 1.0e-20)) / 2.0
else:
r_full: np.ndarray
l_full: np.ndarray
c_full: np.ndarray
r_full, l_full, c_full = build_uncoupled_line_static_matrices(
line=line,
omega_base=omega_base,
)
active_indices: List[int] = get_line_active_global_indices(line)
matrix_size: int = int(r_full.shape[0])
if matrix_size == len(active_indices):
return r_full, l_full, c_full
else:
pass
if matrix_size == 4:
reduced_selector: np.ndarray = np.array(active_indices, dtype=int)
r_reduced: np.ndarray = r_full[np.ix_(reduced_selector, reduced_selector)]
l_reduced: np.ndarray = l_full[np.ix_(reduced_selector, reduced_selector)]
c_reduced: np.ndarray = c_full[np.ix_(reduced_selector, reduced_selector)]
return r_reduced, l_reduced, c_reduced
else:
return r_full, l_full, c_full
[docs]
def validate_line_static_matrices(
line: dev.Line,
r_full: Vec,
l_full: Vec,
c_full: Vec,
n_active: int,
logger: Logger | None,
) -> bool:
"""
Validate reduced line matrix dimensions before assignment.
:param line: Line-like branch device.
:param r_full: Reduced resistance matrix.
:param l_full: Reduced inductance matrix.
:param c_full: Reduced capacitance matrix.
:param n_active: Number of active physical phases.
:param logger: Optional logger.
:return: ``True`` if all matrices match the active-phase shape.
"""
valid: bool = True
expected_shape: Tuple[int, int] = (n_active, n_active)
device_name: str = str(line.name)
if r_full.shape != expected_shape:
add_static_mapping_warning(
logger=logger,
device_name=device_name,
message="Line static mapping skipped because R matrix shape is inconsistent.",
value=str(r_full.shape),
expected_value=str(expected_shape),
device_property="line_static_matrix_shape",
)
valid = False
else:
pass
if l_full.shape != expected_shape:
add_static_mapping_warning(
logger=logger,
device_name=device_name,
message="Line static mapping skipped because L matrix shape is inconsistent.",
value=str(l_full.shape),
expected_value=str(expected_shape),
device_property="line_static_matrix_shape",
)
valid = False
else:
pass
if c_full.shape != expected_shape:
add_static_mapping_warning(
logger=logger,
device_name=device_name,
message="Line static mapping skipped because C matrix shape is inconsistent.",
value=str(c_full.shape),
expected_value=str(expected_shape),
device_property="line_static_matrix_shape",
)
valid = False
else:
pass
return valid
[docs]
def assign_common_injection_static_api_mapping(
grid: MultiCircuit,
device: ALL_DEV_TYPES,
mdl: Block,
logger: Logger | None,
) -> None:
"""
Assign static parameters inherited from ``InjectionParent``.
``injection_use_kw`` is a static boolean flag and
``injection_connection_type`` is a stable numeric encoding of the static
connection enum.
:param grid: Static network model.
:param device: Injection-like static device.
:param mdl: EMT block receiving constants.
:param logger: Optional logger.
:return: None.
"""
device_name: str = str(device.name)
# if the device is active or not
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.device_active,
value=boolean_to_float(bool(device.active)),
logger=logger,
device_name=device_name,
)
# connection type
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.injection_connection_type,
value=shunt_connection_code(device.conn),
logger=logger,
device_name=device_name,
)
[docs]
def assign_common_branch_static_api_mapping(
device: ALL_DEV_TYPES,
mdl: Block,
logger: Logger | None,
) -> None:
"""
Assign static parameters inherited from ``BranchParent``.
:param device: Branch-like static device.
:param mdl: EMT block receiving constants.
:param logger: Optional logger.
:return: None.
"""
device_name: str = str(device.name)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.device_active,
value=boolean_to_float(bool(device.active)),
logger=logger,
device_name=device_name,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.branch_rate_mva,
value=float(device.rate),
logger=logger,
device_name=device_name,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.branch_temp_base_deg_c,
value=float(device.temp_base),
logger=logger,
device_name=device_name,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.branch_temp_oper_deg_c,
value=float(device.temp_oper),
logger=logger,
device_name=device_name,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.branch_alpha_per_deg_c,
value=float(device.alpha),
logger=logger,
device_name=device_name,
)
[docs]
def assign_load_static_api_mapping(
grid: MultiCircuit,
load: dev.Load,
mdl: Block,
logger: Logger | None,
) -> None:
"""
Assign static load parameters exposed by ``mdl.api_obj_mapping``.
:param grid: Static network model.
:param load: Load device.
:param mdl: Load EMT block.
:param logger: Optional logger.
:return: None.
"""
device_name: str = str(load.name)
sbase: float = float(grid.Sbase)
phase_p_pu_abc: np.ndarray
phase_q_pu_abc: np.ndarray
phase_g_pu_abc: np.ndarray
phase_b_pu_abc: np.ndarray
phase_ir_pu_abc: np.ndarray
phase_ii_pu_abc: np.ndarray
phase_p_pu_abc, phase_q_pu_abc = get_load_power_phase_values_pu_abc(load=load, grid=grid)
phase_g_pu_abc, phase_b_pu_abc = get_load_impedance_phase_values_pu_abc(load=load, grid=grid)
phase_ir_pu_abc, phase_ii_pu_abc = get_load_current_phase_values_pu_abc(load=load, grid=grid)
assign_common_injection_static_api_mapping(
grid=grid,
device=load,
mdl=mdl,
logger=logger,
)
# Load ZIP quantities are static power-like data expressed in MW/MVAr at
# 1.0 p.u. voltage on the network base. They are exported in p.u. on
# ``grid.Sbase``.
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.Pl0,
value=mw_to_pu_on_grid_base(float(load.P), grid),
logger=logger,
device_name=device_name,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.Ql0,
value=mvar_to_pu_on_grid_base(float(load.Q), grid),
logger=logger,
device_name=device_name,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.Pl0_A,
value=float(phase_p_pu_abc[0]),
logger=logger,
device_name=device_name,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.Pl0_B,
value=float(phase_p_pu_abc[1]),
logger=logger,
device_name=device_name,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.Pl0_C,
value=float(phase_p_pu_abc[2]),
logger=logger,
device_name=device_name,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.Ql0_A,
value=float(phase_q_pu_abc[0]),
logger=logger,
device_name=device_name,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.Ql0_B,
value=float(phase_q_pu_abc[1]),
logger=logger,
device_name=device_name,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.Ql0_C,
value=float(phase_q_pu_abc[2]),
logger=logger,
device_name=device_name,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.load_g_pu,
value=mw_to_pu_on_grid_base(float(load.G), grid),
logger=logger,
device_name=device_name,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.load_b_pu,
value=mvar_to_pu_on_grid_base(float(load.B), grid),
logger=logger,
device_name=device_name,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.load_ga_pu,
value=float(phase_g_pu_abc[0]),
logger=logger,
device_name=device_name,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.load_gb_pu,
value=float(phase_g_pu_abc[1]),
logger=logger,
device_name=device_name,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.load_gc_pu,
value=float(phase_g_pu_abc[2]),
logger=logger,
device_name=device_name,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.load_ba_pu,
value=float(phase_b_pu_abc[0]),
logger=logger,
device_name=device_name,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.load_bb_pu,
value=float(phase_b_pu_abc[1]),
logger=logger,
device_name=device_name,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.load_bc_pu,
value=float(phase_b_pu_abc[2]),
logger=logger,
device_name=device_name,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.load_ir_pu,
value=mw_to_pu_on_grid_base(float(load.Ir), grid),
logger=logger,
device_name=device_name,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.load_ii_pu,
value=mvar_to_pu_on_grid_base(float(load.Ii), grid),
logger=logger,
device_name=device_name,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.load_ira_pu,
value=float(phase_ir_pu_abc[0]),
logger=logger,
device_name=device_name,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.load_irb_pu,
value=float(phase_ir_pu_abc[1]),
logger=logger,
device_name=device_name,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.load_irc_pu,
value=float(phase_ir_pu_abc[2]),
logger=logger,
device_name=device_name,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.load_iia_pu,
value=float(phase_ii_pu_abc[0]),
logger=logger,
device_name=device_name,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.load_iib_pu,
value=float(phase_ii_pu_abc[1]),
logger=logger,
device_name=device_name,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.load_iic_pu,
value=float(phase_ii_pu_abc[2]),
logger=logger,
device_name=device_name,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.load_contract_power_pu,
value=mw_to_pu_on_grid_base(float(load._contract_power), grid),
logger=logger,
device_name=device_name,
)
if load.bus.is_dc:
# DC loads use the historical EMT convention: explicit conductance is
# used when available; otherwise active power is represented as an
# equivalent conductance on the DC base.
dc_p_value: float = float(load.P) / sbase
dc_g_value: float = float(load.G) / sbase
if dc_g_value == 0.0:
dc_p_value = 0.0
dc_g_value = float(load.P) / sbase
else:
pass
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.Pl0,
value=dc_p_value,
logger=logger,
device_name=device_name,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.g,
value=dc_g_value,
logger=logger,
device_name=device_name,
)
else:
omega_base: float = omega_base_from_grid(grid)
p_values: np.ndarray = phase_p_pu_abc
q_values: np.ndarray = phase_q_pu_abc
p_keys: List[ParamPowerFlowReferenceType] = list([
ParamPowerFlowReferenceType.Pl0_A,
ParamPowerFlowReferenceType.Pl0_B,
ParamPowerFlowReferenceType.Pl0_C,
])
q_keys: List[ParamPowerFlowReferenceType] = list([
ParamPowerFlowReferenceType.Ql0_A,
ParamPowerFlowReferenceType.Ql0_B,
ParamPowerFlowReferenceType.Ql0_C,
])
# TODO: static mapping should not put anything in event_dict! allow_event_parameters_target should not exist (ALWAYS FALSE)
phase_index: int = 0
while phase_index < 3:
assign_api_mapping_value_if_present(
mdl=mdl,
key=p_keys[phase_index],
value=float(p_values[phase_index]),
logger=logger,
device_name=device_name,
# allow_event_parameter_target=True,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=q_keys[phase_index],
value=float(q_values[phase_index]),
logger=logger,
device_name=device_name,
# allow_event_parameter_target=True,
)
phase_index += 1
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.omega_base,
value=omega_base,
logger=logger,
device_name=device_name,
# allow_event_parameter_target=True,
)
[docs]
def assign_static_generator_static_api_mapping(
grid: MultiCircuit,
static_generator: dev.StaticGenerator,
mdl: Block,
logger: Logger | None,
) -> None:
"""
Assign static StaticGenerator parameters exposed by ``mdl.api_obj_mapping``.
:param grid: Static network model.
:param static_generator: Static-generator device.
:param mdl: EMT block.
:param logger: Optional logger.
:return: None.
"""
device_name: str = str(static_generator.name)
omega_base: float = omega_base_from_grid(grid)
phase_p_pu_abc: np.ndarray
phase_q_pu_abc: np.ndarray
phase_p_pu_abc, phase_q_pu_abc = get_load_power_phase_values_pu_abc(load=static_generator, grid=grid)
assign_common_injection_static_api_mapping(
grid=grid,
device=static_generator,
mdl=mdl,
logger=logger,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.Pl0,
value=mw_to_pu_on_grid_base(float(static_generator.P), grid),
logger=logger,
device_name=device_name,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.Ql0,
value=mvar_to_pu_on_grid_base(float(static_generator.Q), grid),
logger=logger,
device_name=device_name,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.Pl0_A,
value=float(phase_p_pu_abc[0]),
logger=logger,
device_name=device_name,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.Pl0_B,
value=float(phase_p_pu_abc[1]),
logger=logger,
device_name=device_name,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.Pl0_C,
value=float(phase_p_pu_abc[2]),
logger=logger,
device_name=device_name,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.Ql0_A,
value=float(phase_q_pu_abc[0]),
logger=logger,
device_name=device_name,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.Ql0_B,
value=float(phase_q_pu_abc[1]),
logger=logger,
device_name=device_name,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.Ql0_C,
value=float(phase_q_pu_abc[2]),
logger=logger,
device_name=device_name,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.Pl0_A,
value=float(phase_p_pu_abc[0]),
logger=logger,
device_name=device_name,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.Pl0_B,
value=float(phase_p_pu_abc[1]),
logger=logger,
device_name=device_name,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.Pl0_C,
value=float(phase_p_pu_abc[2]),
logger=logger,
device_name=device_name,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.Ql0_A,
value=float(phase_q_pu_abc[0]),
logger=logger,
device_name=device_name,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.Ql0_B,
value=float(phase_q_pu_abc[1]),
logger=logger,
device_name=device_name,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.Ql0_C,
value=float(phase_q_pu_abc[2]),
logger=logger,
device_name=device_name,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.omega_base,
value=omega_base,
logger=logger,
device_name=device_name,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.static_generator_snom_mva,
value=float(static_generator.Snom),
logger=logger,
device_name=device_name,
)
[docs]
def assign_external_grid_static_api_mapping(
grid: MultiCircuit,
external_grid: dev.ExternalGrid,
mdl: Block,
logger: Logger | None,
) -> None:
"""
Assign static ExternalGrid parameters exposed by ``mdl.api_obj_mapping``.
:param grid: Static network model.
:param external_grid: External-grid device.
:param mdl: EMT block.
:param logger: Optional logger.
:return: None.
"""
device_name: str = str(external_grid.name)
omega_base: float = omega_base_from_grid(grid)
phase_p_pu_abc: np.ndarray
phase_q_pu_abc: np.ndarray
phase_p_pu_abc, phase_q_pu_abc = get_load_power_phase_values_pu_abc(load=external_grid, grid=grid)
assign_common_injection_static_api_mapping(
grid=grid,
device=external_grid,
mdl=mdl,
logger=logger,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.Pl0,
value=mw_to_pu_on_grid_base(float(external_grid.P), grid),
logger=logger,
device_name=device_name,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.Pl0_A,
value=float(phase_p_pu_abc[0]),
logger=logger,
device_name=device_name,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.Pl0_B,
value=float(phase_p_pu_abc[1]),
logger=logger,
device_name=device_name,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.Pl0_C,
value=float(phase_p_pu_abc[2]),
logger=logger,
device_name=device_name,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.Ql0_A,
value=float(phase_q_pu_abc[0]),
logger=logger,
device_name=device_name,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.Ql0_B,
value=float(phase_q_pu_abc[1]),
logger=logger,
device_name=device_name,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.Ql0_C,
value=float(phase_q_pu_abc[2]),
logger=logger,
device_name=device_name,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.omega_base,
value=omega_base,
logger=logger,
device_name=device_name,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.Ql0,
value=mvar_to_pu_on_grid_base(float(external_grid.Q), grid),
logger=logger,
device_name=device_name,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.external_grid_vm_pu,
value=float(external_grid.Vm),
logger=logger,
device_name=device_name,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.external_grid_va_rad,
value=float(external_grid.Va),
logger=logger,
device_name=device_name,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.external_grid_mode_code,
value=external_grid_mode_code(external_grid.mode),
logger=logger,
device_name=device_name,
)
[docs]
def assign_shunt_static_api_mapping(
grid: MultiCircuit,
shunt: dev.Shunt,
mdl: Block,
logger: Logger | None,
) -> None:
"""
Assign static Shunt parameters exposed by ``mdl.api_obj_mapping``.
:param grid: Static network model.
:param shunt: Shunt device.
:param mdl: EMT block.
:param logger: Optional logger.
:return: None.
"""
device_name: str = str(shunt.name)
phase_g_pu_abc: np.ndarray
phase_b_pu_abc: np.ndarray
phase_g_pu_abc, phase_b_pu_abc = get_shunt_phase_values_pu_abc(shunt=shunt, grid=grid)
assign_common_injection_static_api_mapping(
grid=grid,
device=shunt,
mdl=mdl,
logger=logger,
)
assign_api_mapping_value_if_present(mdl=mdl,
key=ParamPowerFlowReferenceType.shunt_g_pu,
value=mw_to_pu_on_grid_base(float(shunt.G), grid),
logger=logger,
device_name=device_name
)
assign_api_mapping_value_if_present(mdl=mdl,
key=ParamPowerFlowReferenceType.shunt_b_pu,
value=mvar_to_pu_on_grid_base(float(shunt.B), grid),
logger=logger,
device_name=device_name
)
assign_api_mapping_value_if_present(mdl=mdl,
key=ParamPowerFlowReferenceType.shunt_g0_pu,
value=mw_to_pu_on_grid_base(float(shunt.G0), grid),
logger=logger,
device_name=device_name
)
assign_api_mapping_value_if_present(mdl=mdl,
key=ParamPowerFlowReferenceType.shunt_b0_pu,
value=mvar_to_pu_on_grid_base(float(shunt.B0), grid),
logger=logger,
device_name=device_name
)
assign_api_mapping_value_if_present(mdl=mdl,
key=ParamPowerFlowReferenceType.shunt_ga_pu,
value=float(phase_g_pu_abc[0]),
logger=logger,
device_name=device_name
)
assign_api_mapping_value_if_present(mdl=mdl,
key=ParamPowerFlowReferenceType.shunt_gb_pu,
value=float(phase_g_pu_abc[1]),
logger=logger,
device_name=device_name
)
assign_api_mapping_value_if_present(mdl=mdl,
key=ParamPowerFlowReferenceType.shunt_gc_pu,
value=float(phase_g_pu_abc[2]),
logger=logger,
device_name=device_name
)
assign_api_mapping_value_if_present(mdl=mdl,
key=ParamPowerFlowReferenceType.shunt_ba_pu,
value=float(phase_b_pu_abc[0]),
logger=logger,
device_name=device_name
)
assign_api_mapping_value_if_present(mdl=mdl,
key=ParamPowerFlowReferenceType.shunt_bb_pu,
value=float(phase_b_pu_abc[1]),
logger=logger,
device_name=device_name
)
assign_api_mapping_value_if_present(mdl=mdl,
key=ParamPowerFlowReferenceType.shunt_bc_pu,
value=float(phase_b_pu_abc[2]),
logger=logger,
device_name=device_name
)
[docs]
def assign_controllable_shunt_static_api_mapping(
grid: MultiCircuit,
controllable_shunt: dev.ControllableShunt,
mdl: Block,
logger: Logger | None,
) -> None:
"""
Assign static ControllableShunt parameters exposed by ``mdl.api_obj_mapping``.
:param grid: Static network model.
:param controllable_shunt: Controllable shunt device.
:param mdl: EMT block.
:param logger: Optional logger.
:return: None.
"""
device_name: str = str(controllable_shunt.name)
assign_shunt_static_api_mapping(
grid=grid,
shunt=controllable_shunt,
mdl=mdl,
logger=logger,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.controllable_shunt_step,
value=float(controllable_shunt.step),
logger=logger,
device_name=device_name,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.controllable_shunt_vset_pu,
value=float(controllable_shunt.Vset),
logger=logger,
device_name=device_name,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.controllable_shunt_vmin_pu,
value=float(controllable_shunt.Vmin),
logger=logger,
device_name=device_name,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.controllable_shunt_vmax_pu,
value=float(controllable_shunt.Vmax),
logger=logger,
device_name=device_name,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.controllable_shunt_gmin_pu,
value=mw_to_pu_on_grid_base(float(controllable_shunt.Gmin), grid),
logger=logger,
device_name=device_name,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.controllable_shunt_gmax_pu,
value=mw_to_pu_on_grid_base(float(controllable_shunt.Gmax), grid),
logger=logger,
device_name=device_name,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.controllable_shunt_bmin_pu,
value=mvar_to_pu_on_grid_base(float(controllable_shunt.Bmin), grid),
logger=logger,
device_name=device_name,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.controllable_shunt_bmax_pu,
value=mvar_to_pu_on_grid_base(float(controllable_shunt.Bmax), grid),
logger=logger,
device_name=device_name,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.controllable_shunt_control_mode_code,
value=float(controllable_shunt.control_mode.idx()),
logger=logger,
device_name=device_name,
)
[docs]
def assign_current_injection_static_api_mapping(
grid: MultiCircuit,
current_injection: dev.CurrentInjection,
mdl: Block,
logger: Logger | None,
) -> None:
"""
Assign static CurrentInjection parameters exposed by ``mdl.api_obj_mapping``.
:param grid: Static network model.
:param current_injection: Current-injection device.
:param mdl: EMT block.
:param logger: Optional logger.
:return: None.
"""
device_name: str = str(current_injection.name)
phase_ir_pu_abc: np.ndarray
phase_ii_pu_abc: np.ndarray
phase_ir_pu_abc, phase_ii_pu_abc = get_current_injection_phase_values_pu_abc(
current_injection=current_injection,
grid=grid,
)
assign_common_injection_static_api_mapping(
grid=grid,
device=current_injection,
mdl=mdl,
logger=logger,
)
assign_api_mapping_value_if_present(mdl=mdl,
key=ParamPowerFlowReferenceType.current_injection_ir_pu,
value=mw_to_pu_on_grid_base(float(current_injection.Ir), grid),
logger=logger,
device_name=device_name
)
assign_api_mapping_value_if_present(mdl=mdl,
key=ParamPowerFlowReferenceType.current_injection_ii_pu,
value=mvar_to_pu_on_grid_base(float(current_injection.Ii), grid),
logger=logger,
device_name=device_name
)
assign_api_mapping_value_if_present(mdl=mdl,
key=ParamPowerFlowReferenceType.current_injection_ira_pu,
value=float(phase_ir_pu_abc[0]),
logger=logger,
device_name=device_name
)
assign_api_mapping_value_if_present(mdl=mdl,
key=ParamPowerFlowReferenceType.current_injection_irb_pu,
value=float(phase_ir_pu_abc[1]),
logger=logger,
device_name=device_name
)
assign_api_mapping_value_if_present(mdl=mdl,
key=ParamPowerFlowReferenceType.current_injection_irc_pu,
value=float(phase_ir_pu_abc[2]),
logger=logger,
device_name=device_name
)
assign_api_mapping_value_if_present(mdl=mdl,
key=ParamPowerFlowReferenceType.current_injection_iia_pu,
value=float(phase_ii_pu_abc[0]),
logger=logger,
device_name=device_name
)
assign_api_mapping_value_if_present(mdl=mdl,
key=ParamPowerFlowReferenceType.current_injection_iib_pu,
value=float(phase_ii_pu_abc[1]),
logger=logger,
device_name=device_name
)
assign_api_mapping_value_if_present(mdl=mdl,
key=ParamPowerFlowReferenceType.current_injection_iic_pu,
value=float(phase_ii_pu_abc[2]),
logger=logger,
device_name=device_name
)
[docs]
def assign_generator_static_api_mapping(
grid: MultiCircuit,
generator: dev.Generator,
mdl: Block,
logger: Logger | None,
) -> None:
"""
Assign static generator parameters exposed by ``mdl.api_obj_mapping``.
:param grid: Static network model.
:param generator: Generator device.
:param mdl: Generator EMT block.
:param logger: Optional logger.
:return: None.
"""
device_name: str = str(generator.name)
omega_base: float = omega_base_from_grid(grid)
assign_common_injection_static_api_mapping(
grid=grid,
device=generator,
mdl=mdl,
logger=logger,
)
# These quantities are static generator object data or system base data.
# PF-derived sharing targets are intentionally handled outside this module.
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.omega_base,
value=omega_base,
logger=logger,
device_name=device_name,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.fn,
value=frequency_base_hz_from_grid(grid),
logger=logger,
device_name=device_name,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.ws,
value=omega_base,
logger=logger,
device_name=device_name,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.R1,
value=float(generator.R1),
logger=logger,
device_name=device_name,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.X1,
value=float(generator.X1),
logger=logger,
device_name=device_name,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.X0,
value=float(generator.X0),
logger=logger,
device_name=device_name,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.generator_p_pu,
value=mw_to_pu_on_grid_base(float(generator.P), grid),
logger=logger,
device_name=device_name,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.generator_q_pu,
value=mvar_to_pu_on_grid_base(float(generator.Q), grid),
logger=logger,
device_name=device_name,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.Pmin,
value=mw_to_pu_on_grid_base(float(generator.Pmin), grid),
logger=logger,
device_name=device_name,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.Pmax,
value=mw_to_pu_on_grid_base(float(generator.Pmax), grid),
logger=logger,
device_name=device_name,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.generator_qmin_pu,
value=mvar_to_pu_on_grid_base(float(generator.Qmin), grid),
logger=logger,
device_name=device_name,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.generator_qmax_pu,
value=mvar_to_pu_on_grid_base(float(generator.Qmax), grid),
logger=logger,
device_name=device_name,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.generator_power_factor,
value=float(generator.Pf),
logger=logger,
device_name=device_name,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.generator_vset_pu,
value=float(generator.Vset),
logger=logger,
device_name=device_name,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.generator_snom_mva,
value=float(generator.Snom),
logger=logger,
device_name=device_name,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.generator_r0_pu,
value=float(generator.R0),
logger=logger,
device_name=device_name,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.generator_r2_pu,
value=float(generator.R2),
logger=logger,
device_name=device_name,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.generator_x2_pu,
value=float(generator.X2),
logger=logger,
device_name=device_name,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.generator_control_mode,
value=float(generator.control_mode.idx()),
logger=logger,
device_name=device_name,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.generator_enabled_dispatch,
value=boolean_to_float(bool(generator.enabled_dispatch)),
logger=logger,
device_name=device_name,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.generator_must_run,
value=boolean_to_float(bool(generator.must_run)),
logger=logger,
device_name=device_name,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.generator_use_reactive_power_curve,
value=boolean_to_float(bool(generator.use_reactive_power_curve)),
logger=logger,
device_name=device_name,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.generator_device_sbase_mva,
value=float(generator.Sbase),
logger=logger,
device_name=device_name,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.freq,
value=float(generator.freq),
logger=logger,
device_name=device_name,
)
[docs]
def assign_battery_static_api_mapping(
grid: MultiCircuit,
battery: dev.Battery,
mdl: Block,
logger: Logger | None,
) -> None:
"""
Assign static Battery parameters exposed by ``mdl.api_obj_mapping``.
:param grid: Static network model.
:param battery: Battery device.
:param mdl: EMT block.
:param logger: Optional logger.
:return: None.
"""
device_name: str = str(battery.name)
assign_generator_static_api_mapping(
grid=grid,
generator=battery,
mdl=mdl,
logger=logger,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.battery_enom_mwh,
value=float(battery.Enom),
logger=logger,
device_name=device_name,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.battery_soc_0_pu,
value=float(battery.soc_0),
logger=logger,
device_name=device_name,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.battery_max_soc_pu,
value=float(battery.max_soc),
logger=logger,
device_name=device_name,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.battery_min_soc_pu,
value=float(battery.min_soc),
logger=logger,
device_name=device_name,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.battery_charge_efficiency_pu,
value=float(battery.charge_efficiency),
logger=logger,
device_name=device_name,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.battery_discharge_efficiency_pu,
value=float(battery.discharge_efficiency),
logger=logger,
device_name=device_name,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.battery_charge_per_cycle_pu,
value=float(battery.charge_per_cycle),
logger=logger,
device_name=device_name,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.battery_discharge_per_cycle_pu,
value=float(battery.discharge_per_cycle),
logger=logger,
device_name=device_name,
)
[docs]
def assign_vsc_static_api_mapping(
grid: MultiCircuit,
vsc: dev.VSC,
mdl: Block,
logger: Logger | None,
) -> None:
"""
Assign static VSC parameters exposed by ``mdl.api_obj_mapping``.
PF-derived quantities such as converter initial active power or converter
losses are intentionally not assigned here. They are initialization data, not
static API-object data.
:param grid: Static network model.
:param vsc: VSC device.
:param mdl: VSC EMT block.
:param logger: Optional logger.
:return: None.
"""
device_name: str = str(vsc.name)
omega_base: float = omega_base_from_grid(grid)
assign_common_branch_static_api_mapping(
device=vsc,
mdl=mdl,
logger=logger,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.Sbase,
value=float(grid.Sbase),
logger=logger,
device_name=device_name,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.omega_base,
value=omega_base,
logger=logger,
device_name=device_name,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.converter_control_mode_1,
value=converter_control_code(vsc.control1),
logger=logger,
device_name=device_name,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.converter_control_mode_2,
value=converter_control_code(vsc.control2),
logger=logger,
device_name=device_name,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.converter_control_target_1,
value=float(vsc.control1_val),
logger=logger,
device_name=device_name,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.converter_control_target_2,
value=float(vsc.control2_val),
logger=logger,
device_name=device_name,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.alpha1,
value=float(vsc.alpha1),
logger=logger,
device_name=device_name,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.alpha2,
value=float(vsc.alpha2),
logger=logger,
device_name=device_name,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.alpha3,
value=float(vsc.alpha3),
logger=logger,
device_name=device_name,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.vsc_kdp_pu,
value=float(vsc.control1_val_droop),
logger=logger,
device_name=device_name,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.vsc_min_ac_voltage_pu,
value=float(vsc.min_ac_voltage),
logger=logger,
device_name=device_name,
)
[docs]
def assign_dc_line_static_api_mapping(
dc_line: DcLine,
mdl: Block,
logger: Logger | None,
) -> None:
"""
Assign static DC-line parameters exposed by ``mdl.api_obj_mapping``.
:param dc_line: DC line device.
:param mdl: DC line EMT block.
:param logger: Optional logger.
:return: None.
"""
device_name: str = str(dc_line.name)
eps_value: float = 1.0e-12
resistance_value: float = float(dc_line.R_corrected)
conductance_value: float = 1.0 / max(abs(resistance_value), eps_value)
assign_common_branch_static_api_mapping(
device=dc_line,
mdl=mdl,
logger=logger,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.g,
value=conductance_value,
logger=logger,
device_name=device_name,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.b,
value=0.0,
logger=logger,
device_name=device_name,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.bsh,
value=0.0,
logger=logger,
device_name=device_name,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.dc_line_r_pu,
value=resistance_value,
logger=logger,
device_name=device_name,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.dc_line_length_km,
value=float(dc_line.length),
logger=logger,
device_name=device_name,
)
[docs]
def assign_line_static_api_mapping(
grid: MultiCircuit,
line: dev.Line,
mdl: Block,
logger: Logger | None,
) -> None:
"""
Assign static line R, Linv and C parameters exposed by ``api_obj_mapping``.
The physical reduced matrix is projected into the fixed 4x4 NABC API-object
contract. Exposed inactive-phase entries are explicitly zeroed, preserving the
previous template semantics while allowing partial key subsets.
:param grid: Static network model.
:param line: Line-like branch device.
:param mdl: Line EMT block.
:param logger: Optional logger.
:return: None.
"""
# todo: make sure it also works for rms
device_name: str = str(line.name)
assign_common_branch_static_api_mapping(
device=line,
mdl=mdl,
logger=logger,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=ParamPowerFlowReferenceType.line_length_km,
value=float(line.length),
logger=logger,
device_name=device_name,
)
active_indices: List[int] = get_line_active_global_indices(line)
n_active: int = len(active_indices)
if n_active == 0:
add_static_mapping_warning(
logger=logger,
device_name=device_name,
message="Line static mapping skipped because no physical phases are active.",
value="0 active phases",
expected_value="at least one active phase",
device_property="line_phases",
)
else:
r_full: np.ndarray
l_full: np.ndarray
c_full: np.ndarray
r_full, l_full, c_full = build_line_static_matrices(
grid=grid,
line=line,
)
matrices_are_valid: bool = validate_line_static_matrices(
line=line,
r_full=r_full,
l_full=l_full,
c_full=c_full,
n_active=n_active,
logger=logger,
)
if matrices_are_valid:
linv_full: np.ndarray
try:
# The physically meaningful criterion is invertibility, not the
# absolute determinant magnitude. Small but diagonal inductance
# matrices are valid after per-unit scaling and should not be
# rejected only because their determinant is numerically tiny.
linv_full = np.linalg.inv(l_full)
except np.linalg.LinAlgError:
determinant_value: float = float(np.linalg.det(l_full))
add_static_mapping_warning(
logger=logger,
device_name=device_name,
message="Line static mapping skipped because inductance matrix is singular.",
value=str(determinant_value),
expected_value="invertible inductance matrix",
device_property="line_inductance_matrix",
)
else:
r_keys: List[List[ParamPowerFlowReferenceType]] = get_line_r_keys()
linv_keys: List[List[ParamPowerFlowReferenceType]] = get_line_linv_keys()
c_keys: List[List[ParamPowerFlowReferenceType]] = get_line_c_keys()
# First zero every exposed slot of the fixed NABC map. This is
# important for templates that expose inactive neutral/phase keys.
global_row: int = 0
while global_row < 4:
global_col: int = 0
while global_col < 4:
assign_api_mapping_value_if_present(
mdl=mdl,
key=r_keys[global_row][global_col],
value=0.0,
logger=logger,
device_name=device_name,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=linv_keys[global_row][global_col],
value=0.0,
logger=logger,
device_name=device_name,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=c_keys[global_row][global_col],
value=0.0,
logger=logger,
device_name=device_name,
)
global_col += 1
global_row += 1
# Then project reduced active-phase values into the fixed NABC
# contract. Only keys requested by the EMT model are written.
reduced_row: int = 0
while reduced_row < n_active:
mapped_global_row: int = active_indices[reduced_row]
reduced_col: int = 0
while reduced_col < n_active:
mapped_global_col: int = active_indices[reduced_col]
assign_api_mapping_value_if_present(
mdl=mdl,
key=r_keys[mapped_global_row][mapped_global_col],
value=float(r_full[reduced_row, reduced_col]),
logger=logger,
device_name=device_name,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=linv_keys[mapped_global_row][mapped_global_col],
value=float(linv_full[reduced_row, reduced_col]),
logger=logger,
device_name=device_name,
)
assign_api_mapping_value_if_present(
mdl=mdl,
key=c_keys[mapped_global_row][mapped_global_col],
value=float(c_full[reduced_row, reduced_col]),
logger=logger,
device_name=device_name,
)
reduced_col += 1
reduced_row += 1
else:
pass
[docs]
def assign_hvdc_line_static_api_mapping(
hvdc_line: dev.HvdcLine,
mdl: Block,
logger: Logger | None,
) -> None:
"""
Intentionally leave HVDC-line static mapping as a no-op.
Reachability exists in the EMT builder, but there is no established EMT
template/static-key contract for ``HvdcLine`` yet.
:param hvdc_line: HVDC-line device.
:param mdl: EMT block.
:param logger: Optional logger.
:return: None.
"""
if logger is None:
pass
else:
pass
[docs]
def assign_series_reactance_static_api_mapping(
series_reactance: dev.SeriesReactance,
mdl: Block,
logger: Logger | None,
) -> None:
"""
Intentionally leave series-reactance static mapping as a no-op.
Reachability exists in the EMT builder, but there is no established EMT
template/static-key contract for ``SeriesReactance`` yet.
:param series_reactance: Series-reactance device.
:param mdl: EMT block.
:param logger: Optional logger.
:return: None.
"""
if logger is None:
pass
else:
pass
[docs]
def assign_switch_static_api_mapping(
switch: dev.Switch,
mdl: Block,
logger: Logger | None,
) -> None:
"""
Intentionally leave switch static mapping as a no-op.
Reachability exists in the EMT builder, but there is no established EMT
template/static-key contract for ``Switch`` yet.
:param switch: Switch device.
:param mdl: EMT block.
:param logger: Optional logger.
:return: None.
"""
if logger is None:
pass
else:
pass
[docs]
def assign_upfc_static_api_mapping(
upfc: dev.UPFC,
mdl: Block,
logger: Logger | None,
) -> None:
"""
Intentionally leave UPFC static mapping as a no-op.
Reachability exists in the EMT builder, but there is no established EMT
template/static-key contract for ``Upfc`` yet.
:param upfc: UPFC device.
:param mdl: EMT block.
:param logger: Optional logger.
:return: None.
"""
if logger is None:
pass
else:
pass