Source code for VeraGridEngine.Utils.Symbolic.templates_common_functions

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

from __future__ import annotations
#
from typing import Any
#
from VeraGridEngine.enumerations import VarPowerFlowReferenceType
from VeraGridEngine.Devices.Dynamic.var_factory import VarFactory

from VeraGridEngine.Utils.Symbolic.block import Block
from VeraGridEngine.enumerations import DeviceType
from VeraGridEngine.Utils.Symbolic.bus_rms_template import initialize_bus_rms
from VeraGridEngine.Utils.Symbolic.bus_emt_template import BusEmtTemplate

[docs] def connect_line_rms_from(mdl1: Block, mdl2: Block, var_factory:VarFactory): """ This function substitutes input variables for output variables to connect two rms models :param mdl1: :param mdl2: :param var_factory: :return: """ # connect Vm pairs = [ (outp, inpt) for outp in mdl1.out_vars for inpt in mdl2.in_vars if outp.ref == VarPowerFlowReferenceType.Vm and inpt.ref == VarPowerFlowReferenceType.Vmf ] for outp, inpt in pairs: var_factory.add_connections([inpt], [outp]) # connect Va pairs = [ (outp, inpt) for outp in mdl1.out_vars for inpt in mdl2.in_vars if outp.ref == VarPowerFlowReferenceType.Va and inpt.ref == VarPowerFlowReferenceType.Vaf ] for outp, inpt in pairs: var_factory.add_connections([inpt], [outp])
[docs] def connect_models_power_flow(mdl1: Block, mdl2: Block, var_factory: VarFactory): """ This function substitutes input variables for output variables to connect two rms models :param mdl1: :param mdl2: :param var_factory: :return: """ # connect inputs mdl2 with outputs mdl1 pairs = [ (outp, inpt) for outp in mdl1.out_vars for inpt in mdl2.in_vars if outp.ref == inpt.ref ] for pair in pairs: # Todo: change connection method to use var_factory var_factory.add_connections([pair[1]], [pair[0]])
[docs] def connect_line_rms_to(mdl1: Block, mdl2: Block, var_factory:VarFactory): """ This function substitutes input variables for output variables to connect two rms models :param mdl1: :param mdl2: :param var_factory: :return: """ # connect Vm pairs = [ (outp, inpt) for outp in mdl1.out_vars for inpt in mdl2.in_vars if outp.ref == VarPowerFlowReferenceType.Vm and inpt.ref == VarPowerFlowReferenceType.Vmt ] for outp, inpt in pairs: var_factory.add_connections([inpt], [outp]) # connect Va pairs = [ (outp, inpt) for outp in mdl1.out_vars for inpt in mdl2.in_vars if outp.ref == VarPowerFlowReferenceType.Va and inpt.ref == VarPowerFlowReferenceType.Vat ] for outp, inpt in pairs: var_factory.add_connections([inpt], [outp])
[docs] def connect_line_phasor_rms_from(mdl1: Block, mdl2: Block, var_factory:VarFactory): """ Connect phasor RMS models for the 'from' end of a line. Connects Vr, Vi from bus to line inputs. """ # connect Vr pairs = [ (outp, inpt) for outp in mdl1.out_vars for inpt in mdl2.in_vars if outp.ref == VarPowerFlowReferenceType.Vr and inpt.ref == VarPowerFlowReferenceType.Vrf ] for outp, inpt in pairs: var_factory.add_connections([inpt], [outp]) # connect Vi pairs = [ (outp, inpt) for outp in mdl1.out_vars for inpt in mdl2.in_vars if outp.ref == VarPowerFlowReferenceType.Vi and inpt.ref == VarPowerFlowReferenceType.Vif ] for outp, inpt in pairs: var_factory.add_connections([inpt], [outp])
[docs] def connect_line_phasor_rms_to(mdl1: Block, mdl2: Block, var_factory:VarFactory): """ Connect phasor RMS models for the 'to' end of a line. Connects Vr, Vi from bus to line inputs. """ # connect Vr pairs = [ (outp, inpt) for outp in mdl1.out_vars for inpt in mdl2.in_vars if outp.ref == VarPowerFlowReferenceType.Vr and inpt.ref == VarPowerFlowReferenceType.Vrt ] for outp, inpt in pairs: var_factory.add_connections([inpt], [outp]) # connect Vi pairs = [ (outp, inpt) for outp in mdl1.out_vars for inpt in mdl2.in_vars if outp.ref == VarPowerFlowReferenceType.Vi and inpt.ref == VarPowerFlowReferenceType.Vit ] for outp, inpt in pairs: var_factory.add_connections([inpt], [outp])
[docs] def connect_models(mdl1: Block, mdl2: Block, var_factory:VarFactory): """ This function substitutes input variables for output variables to connect two rms models :param mdl1: :param mdl2: :param var_factory: :return: """ # connect inputs mdl2 with outputs mdl1 pairs = [ (outp, inpt) for outp in mdl1.out_vars for inpt in mdl2.in_vars if outp.shared_ref == inpt.shared_ref and outp.shared_ref is not None and inpt.shared_ref is not None ] for outp, inpt in pairs: var_factory.add_connections([inpt], [outp]) # connect inputs mdl1 with outputs mdl2 pairs = [ (outp, inpt) for outp in mdl2.out_vars for inpt in mdl1.in_vars if outp.shared_ref == inpt.shared_ref and outp.shared_ref is not None and inpt.shared_ref is not None ] for outp, inpt in pairs: var_factory.add_connections([inpt], [outp])
#print("")
[docs] def connect_bus_variables_rms(device: Any, model:Block, var_factory: VarFactory | None): """ connect the bus variables of the model with the api_object bus variables :param device: :type device: :param model: :type model: :param var_factory: :type var_factory: :return: :rtype: """ if device.device_type in [DeviceType.BranchDevice, DeviceType.LineDevice, DeviceType.Transformer2WDevice, DeviceType.Transformer3WDevice]: # Check if using phasor or polar coordinates by looking at bus model outputs bus_model = device.bus_from.rms_model if bus_model.empty(): if var_factory is not None: initialize_bus_rms(device.bus_from, var_factory) else: raise ValueError("var factory not associated to grid element") # Check if bus has Vr/Vi (phasor) or Vm/Va (polar) outputs has_phasor = any(v.ref == VarPowerFlowReferenceType.Vr for v in bus_model.out_vars) if has_phasor: if var_factory is not None: connect_line_phasor_rms_from(device.bus_from.rms_model, model, var_factory) else: if var_factory is not None: connect_line_rms_from(device.bus_from.rms_model, model, var_factory) bus_model = device.bus_to.rms_model if bus_model.empty(): if var_factory is not None: initialize_bus_rms(device.bus_to, var_factory) else: raise ValueError("var factory not associated to grid element") # Check if bus has Vr/Vi (phasor) or Vm/Va (polar) outputs has_phasor = any(v.ref == VarPowerFlowReferenceType.Vr for v in bus_model.out_vars) if has_phasor: if var_factory is not None: connect_line_phasor_rms_to(device.bus_to.rms_model, model, var_factory) else: if var_factory is not None: connect_line_rms_to(device.bus_to.rms_model, model, var_factory) else: if device.bus.rms_model.empty(): if var_factory is not None: initialize_bus_rms(device.bus, var_factory) else: raise ValueError("var factory not associated to grid element") if var_factory is not None: connect_models_power_flow(device.bus.rms_model, model, var_factory)
[docs] def connect_pending_injection_bus_variables_emt(bus: Any) -> None: """ Connect deferred single-bus EMT devices once one bus shell exists. The GUI can assign one injection EMT template before any branch template has created the bus EMT shell. In that case the device model is duplicated and queued on the bus. When one branch later materializes the bus shell, this helper completes the pending bus-to-device EMT connections. :param bus: Bus whose pending EMT injection devices must be connected. :return: None. """ pending_devices = bus.get_pending_emt_devices() pending_device: Any for pending_device in pending_devices: if bus.emt_model.empty(): pass else: if pending_device.emt_model.empty(): pass else: connect_injection_emt(bus.emt_model, pending_device.emt_model) bus.remove_pending_emt_device(pending_device)
[docs] def connect_bus_variables_emt(device: Any, model: Block, var_factory: VarFactory | None, allow_deferred_connection: bool = False) -> None: """ connect the bus variables of the model with the api_object bus variables :param device: :type device: :param model: :type model: :param var_factory: :type var_factory: :return: :rtype: """ branch_devices = [DeviceType.BranchDevice, DeviceType.LineDevice, DeviceType.Transformer2WDevice, DeviceType.Transformer3WDevice, DeviceType.DCLineDevice, DeviceType.VscDevice] if device.device_type in branch_devices: # Check if using phasor or polar coordinates by looking at bus model outputs bus_from = device.bus_from # create bus mask to know number of phases mask = [False, False, False, False] # [N, A, B, C] if not bus_from.is_dc: ys_phase_device_types = { DeviceType.LineDevice, } transformer_phase_device_types = { DeviceType.Transformer2WDevice, DeviceType.WindingDevice, } abc_phase_device_types = { DeviceType.VscDevice, DeviceType.SeriesReactanceDevice, DeviceType.UpfcDevice, DeviceType.SwitchDevice, DeviceType.HVDCLineDevice, DeviceType.DCLineDevice, } if device.device_type in ys_phase_device_types and device.ys is not None: mask[0] |= bool(device.ys.phN) mask[1] |= bool(device.ys.phA) mask[2] |= bool(device.ys.phB) mask[3] |= bool(device.ys.phC) elif device.device_type in transformer_phase_device_types: mask[1] = True mask[2] = True mask[3] = True elif device.device_type in abc_phase_device_types: mask[1] = True mask[2] = True mask[3] = True else: mask[1] = True mask[2] = True mask[3] = True # modify/create bus block if var_factory is not None: if bus_from.emt_model.empty(): bus_from.emt_model = BusEmtTemplate(vf=var_factory, mask = mask, is_dc = bus_from.is_dc).block else: if not bus_from.is_dc: # Get the external mapping of the already existing bus EMT model external_mapping = bus_from.emt_model.external_mapping # Detect which AC phases are already present in the existing bus model existing_mask = [False, False, False, False] # [N, A, B, C] existing_mask[0] = external_mapping.get(VarPowerFlowReferenceType.v_N, None) is not None existing_mask[1] = external_mapping.get(VarPowerFlowReferenceType.v_A, None) is not None existing_mask[2] = external_mapping.get(VarPowerFlowReferenceType.v_B, None) is not None existing_mask[3] = external_mapping.get(VarPowerFlowReferenceType.v_C, None) is not None # Merge the phases required by the already existing bus model and the new device required_mask = [ existing_mask[0] or mask[0], existing_mask[1] or mask[1], existing_mask[2] or mask[2], existing_mask[3] or mask[3], ] # Recreate the bus EMT model only if the required phase layout changed if required_mask != existing_mask: bus_from.emt_model = BusEmtTemplate( vf=var_factory, mask=required_mask, is_dc=bus_from.is_dc ).block else: pass else: raise ValueError("var factory not associated to grid element") # connect inputs and outputs connect_line_emt_from(device.bus_from.emt_model, model, var_factory) connect_pending_injection_bus_variables_emt(device.bus_from) # bus to bus_to = device.bus_to # create bus mask to know number of phases mask = [False, False, False, False] # [N, A, B, C] if not bus_to.is_dc: ys_phase_device_types = { DeviceType.LineDevice, } transformer_phase_device_types = { DeviceType.Transformer2WDevice, DeviceType.WindingDevice, } abc_phase_device_types = { DeviceType.VscDevice, DeviceType.SeriesReactanceDevice, DeviceType.UpfcDevice, DeviceType.SwitchDevice, DeviceType.HVDCLineDevice, DeviceType.DCLineDevice, } if device.device_type in ys_phase_device_types and device.ys is not None: mask[0] |= bool(device.ys.phN) mask[1] |= bool(device.ys.phA) mask[2] |= bool(device.ys.phB) mask[3] |= bool(device.ys.phC) elif device.device_type in transformer_phase_device_types: mask[1] = True mask[2] = True mask[3] = True elif device.device_type in abc_phase_device_types: mask[1] = True mask[2] = True mask[3] = True else: mask[1] = True mask[2] = True mask[3] = True # modify/create bus block if var_factory is not None: if bus_to.emt_model.empty(): bus_to.emt_model = BusEmtTemplate(vf=var_factory, mask=mask, is_dc=bus_to.is_dc).block else: if not bus_to.is_dc: # Get the external mapping of the already existing bus EMT model external_mapping = bus_to.emt_model.external_mapping # Detect which AC phases are already present in the existing bus model existing_mask = [False, False, False, False] # [N, A, B, C] existing_mask[0] = external_mapping.get(VarPowerFlowReferenceType.v_N, None) is not None existing_mask[1] = external_mapping.get(VarPowerFlowReferenceType.v_A, None) is not None existing_mask[2] = external_mapping.get(VarPowerFlowReferenceType.v_B, None) is not None existing_mask[3] = external_mapping.get(VarPowerFlowReferenceType.v_C, None) is not None # Merge the phases required by the already existing bus model and the new device required_mask = [ existing_mask[0] or mask[0], existing_mask[1] or mask[1], existing_mask[2] or mask[2], existing_mask[3] or mask[3], ] # Recreate the bus EMT model only if the required phase layout changed if required_mask != existing_mask: bus_to.emt_model = BusEmtTemplate( vf=var_factory, mask=required_mask, is_dc=bus_to.is_dc ).block else: pass else: raise ValueError("var factory not associated to grid element") # connect inputs and outputs connect_line_emt_to(device.bus_to.emt_model, model, var_factory) connect_pending_injection_bus_variables_emt(device.bus_to) else: if device.bus.emt_model.empty(): if var_factory is not None: if device.bus.is_dc: device.bus.emt_model = BusEmtTemplate( vf=var_factory, mask=[False, False, False, False], is_dc=True, name=f"{device.bus.name}_emt_template", ).block else: # Template assignment must materialize the real bus shell at # assignment time so the rest of the workflow sees the same # persistent EMT bus variables. device.bus.emt_model = BusEmtTemplate( vf=var_factory, mask=[False, True, True, True], is_dc=False, name=f"{device.bus.name}_emt_template", ).block else: raise ValueError("var factory not associated to grid element") if device.bus.emt_model.empty(): pass else: if var_factory is not None: connect_injection_emt(device.bus.emt_model, model, var_factory)
[docs] def set_rms_model(device: Any, model:Block, var_factory: VarFactory): """ Set the RMS model :return: :rtype: """ # connect bus variables if device.device_type in [DeviceType.BranchDevice, DeviceType.LineDevice, DeviceType.Transformer2WDevice, DeviceType.Transformer3WDevice]: # Check if using phasor or polar coordinates by looking at bus model outputs bus_model = device.bus_from.rms_model if not bus_model.empty(): # Check if bus has Vr/Vi (phasor) or Vm/Va (polar) outputs has_phasor = any(v.ref == VarPowerFlowReferenceType.Vr for v in bus_model.out_vars) if has_phasor: connect_line_phasor_rms_from(device.bus_from.rms_model, model, var_factory) else: connect_line_rms_from(device.bus_from.rms_model, model, var_factory) else: raise ValueError(f"Connection Bus RMS model cannot be empty, initialize {device.bus_from.name} RMS model") bus_model = device.bus_to.rms_model if not bus_model.empty(): # Check if bus has Vr/Vi (phasor) or Vm/Va (polar) outputs has_phasor = any(v.ref == VarPowerFlowReferenceType.Vr for v in bus_model.out_vars) if has_phasor: connect_line_phasor_rms_to(device.bus_to.rms_model, model, var_factory) else: connect_line_rms_to(device.bus_to.rms_model, model, var_factory) else: raise ValueError(f"Connection Bus RMS model cannot be empty, initialize {device.bus_to.name} RMS model") else: if not device.bus.rms_model.empty(): for mdl in model.get_all_blocks(): connect_models_power_flow(device.bus.rms_model, mdl, var_factory) else: raise ValueError(f"Connection Bus RMS model cannot be empty, initialize {device.bus.name} RMS model") # fill var factoru dict[Dev, List[Var]] with model variables # model.unify_blocks() # for vr in model.algebraic_vars: # var_factory.register_var(device, vr) # for vr in model.state_vars: # var_factory.register_var(device, vr) # set the model to the device device.rms_model = model
[docs] def connect_line_emt_from(mdl1: Block, mdl2: Block, var_factory:VarFactory): """ Connects the bus voltages (mdl1) to the "from" side of the line (mdl2) for simulations with explicit phases (e.g., EMT). It only connects the phases present in both models (any NABC combination). :param mdl1: Bus model (Block) :param mdl2: Line model (Block) :param var_factory: :return: None """ # Create a dictionary to map the bus output variable # to the corresponding line input variable ("from" side) phase_map = { VarPowerFlowReferenceType.v_N: VarPowerFlowReferenceType.vf_N, VarPowerFlowReferenceType.v_A: VarPowerFlowReferenceType.vf_A, VarPowerFlowReferenceType.v_B: VarPowerFlowReferenceType.vf_B, VarPowerFlowReferenceType.v_C: VarPowerFlowReferenceType.vf_C, VarPowerFlowReferenceType.Vdc: VarPowerFlowReferenceType.Vf_dc, } # Find the variable pairs that match our mapping pairs = [ (outp, inpt) for outp in mdl1.out_vars for inpt in mdl2.in_vars if outp.ref in phase_map and phase_map[outp.ref] == inpt.ref ] # Connect all found pairs for outp, inpt in pairs: var_factory.add_connections([inpt], [outp])
[docs] def connect_line_emt_to(mdl1: Block, mdl2: Block, var_factory:VarFactory): """ Connects the bus voltages (mdl1) to the "to" side of the line (mdl2) for simulations with explicit phases (e.g., EMT). It only connects the phases present in both models (any NABC combination). :param mdl1: Bus model (Block) :param mdl2: Line model (Block) :param var_factory: :return: None """ # Create a dictionary to map the bus output variable # to the corresponding line input variable ("to" side) phase_map = { VarPowerFlowReferenceType.v_N: VarPowerFlowReferenceType.vt_N, VarPowerFlowReferenceType.v_A: VarPowerFlowReferenceType.vt_A, VarPowerFlowReferenceType.v_B: VarPowerFlowReferenceType.vt_B, VarPowerFlowReferenceType.v_C: VarPowerFlowReferenceType.vt_C, VarPowerFlowReferenceType.Vdc: VarPowerFlowReferenceType.Vt_dc, } # Find the variable pairs that match our mapping pairs = [ (outp, inpt) for outp in mdl1.out_vars for inpt in mdl2.in_vars if outp.ref in phase_map and phase_map[outp.ref] == inpt.ref ] # Connect all found pairs for outp, inpt in pairs: var_factory.add_connections([inpt], [outp])
[docs] def connect_injection_emt(mdl1: Block, mdl2: Block, var_factory:VarFactory) -> None: """ Connect one bus EMT shell to one single-bus EMT injection model. The bus shell exposes EMT bus voltages as outputs. Single-bus injection templates consume those same voltage references directly on their inputs, so the connection rule is one direct reference match per supported node. :param mdl1: Bus EMT model. :param mdl2: EMT model of one single-bus injection device. :param var_factory: :return: None. """ # Single-bus EMT devices use the same enum references as the bus shell, so # the algorithm only needs to match equal EMT voltage references. phase_map = { VarPowerFlowReferenceType.v_N: VarPowerFlowReferenceType.v_N, VarPowerFlowReferenceType.v_A: VarPowerFlowReferenceType.v_A, VarPowerFlowReferenceType.v_B: VarPowerFlowReferenceType.v_B, VarPowerFlowReferenceType.v_C: VarPowerFlowReferenceType.v_C, VarPowerFlowReferenceType.Vdc: VarPowerFlowReferenceType.Vdc, } pairs = [ (outp, inpt) for outp in mdl1.out_vars for inpt in mdl2.in_vars if outp.ref in phase_map and phase_map[outp.ref] == inpt.ref ] for outp, inpt in pairs: var_factory.add_connections([inpt], [outp])
[docs] def connect_vsc_emt_from(mdl1: Block, mdl2: Block, var_factory:VarFactory,is_dc_bus: bool = False): """ Connects the bus voltages to the "from" side of the VSC model. For DC buses, connects Vdc. For AC buses, connects abc voltages. :param mdl1: Bus model (Block) :param mdl2: VSC model (Block) :param var_factory: :param is_dc_bus: True if bus_from is DC :return: None """ if is_dc_bus: # DC bus: connect Vdc dc_map = { VarPowerFlowReferenceType.Vdc: VarPowerFlowReferenceType.Vdc } pairs = [ (outp, inpt) for outp in mdl1.out_vars for inpt in mdl2.in_vars if outp.ref in dc_map and dc_map[outp.ref] == inpt.ref ] else: # AC bus: connect abc voltages phase_map = { VarPowerFlowReferenceType.v_A: VarPowerFlowReferenceType.v_A, VarPowerFlowReferenceType.v_B: VarPowerFlowReferenceType.v_B, VarPowerFlowReferenceType.v_C: VarPowerFlowReferenceType.v_C } pairs = [ (outp, inpt) for outp in mdl1.out_vars for inpt in mdl2.in_vars if outp.ref in phase_map and phase_map[outp.ref] == inpt.ref ] for outp, inpt in pairs: var_factory.add_connections([inpt], [outp])
[docs] def connect_vsc_emt_to(mdl1: Block, mdl2: Block, var_factory:VarFactory, is_dc_bus: bool = False): """ Connects the bus voltages to the "to" side of the VSC model. For DC buses, connects Vdc. For AC buses, connects abc voltages. :param mdl1: Bus model (Block) :param mdl2: VSC model (Block) :param var_factory: :param is_dc_bus: True if bus_to is DC :return: None """ if is_dc_bus: # DC bus: connect Vdc dc_map = { VarPowerFlowReferenceType.Vdc: VarPowerFlowReferenceType.Vdc } pairs = [ (outp, inpt) for outp in mdl1.out_vars for inpt in mdl2.in_vars if outp.ref in dc_map and dc_map[outp.ref] == inpt.ref ] else: # AC bus: connect abc voltages phase_map = { VarPowerFlowReferenceType.v_A: VarPowerFlowReferenceType.v_A, VarPowerFlowReferenceType.v_B: VarPowerFlowReferenceType.v_B, VarPowerFlowReferenceType.v_C: VarPowerFlowReferenceType.v_C } pairs = [ (outp, inpt) for outp in mdl1.out_vars for inpt in mdl2.in_vars if outp.ref in phase_map and phase_map[outp.ref] == inpt.ref ] for outp, inpt in pairs: var_factory.add_connections([inpt], [outp])
[docs] def set_emt_model(device: Any, model: Block, var_factory: VarFactory): """ Sets the EMT model for a given device, connects it to the bus(es), and registers all its variables in the VarFactory. :param device: The power system device (Line, Transformer, Generator, etc.) :param model: The mathematical block model of the device :param var_factory: Factory to register simulation variables :return: None """ # 1. Connect bus variables depending on the device type if device.device_type in [DeviceType.BranchDevice, DeviceType.LineDevice, DeviceType.DCLineDevice, DeviceType.HVDCLineDevice, DeviceType.SwitchDevice, DeviceType.SeriesReactanceDevice, DeviceType.UpfcDevice, DeviceType.Transformer2WDevice, DeviceType.Transformer3WDevice]: # Bus FROM connection bus_from_model = device.bus_from.emt_model if not bus_from_model.empty(): connect_line_emt_from(bus_from_model, model, var_factory) else: raise ValueError(f"Connection Bus EMT model cannot be empty, initialize {device.bus_from.name} EMT model") # Bus TO connection bus_to_model = device.bus_to.emt_model if not bus_to_model.empty(): connect_line_emt_to(bus_to_model, model, var_factory) else: raise ValueError(f"Connection Bus EMT model cannot be empty, initialize {device.bus_to.name} EMT model") elif device.device_type == DeviceType.VscDevice: # VSC: bus_from is DC, bus_to is AC bus_from_model = device.bus_from.emt_model if not bus_from_model.empty(): connect_vsc_emt_from(bus_from_model, model, var_factory, is_dc_bus=device.bus_from.is_dc) else: raise ValueError(f"Connection Bus EMT model cannot be empty, initialize {device.bus_from.name} EMT model") bus_to_model = device.bus_to.emt_model if not bus_to_model.empty(): connect_vsc_emt_to(bus_to_model, model, var_factory, is_dc_bus=device.bus_to.is_dc) else: raise ValueError(f"Connection Bus EMT model cannot be empty, initialize {device.bus_to.name} EMT model") else: # Generic device connection (e.g., Loads, Generators connected to a single bus) if not device.bus.emt_model.empty(): connect_injection_emt(device.bus.emt_model, model, var_factory) else: raise ValueError(f"Connection Bus EMT model cannot be empty, initialize {device.bus.name} EMT model") # 2. Unify blocks and register all variables in the VarFactory model.unify_blocks() for vr in model.algebraic_vars: var_factory.register_var(device, vr) for vr in model.state_vars: var_factory.register_var(device, vr) # Added diff_vars for EMT simulations for vr in model.diff_vars: var_factory.register_var(device, vr) # 3. Assign the configured model to the device device.emt_model = model