# 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 Tuple
from VeraGridEngine.Devices.Parents.physical_device import PhysicalDevice, GCProp
from VeraGridEngine.Devices.Dynamic.var_factory import VarFactory
from VeraGridEngine.Utils.Symbolic.block import Block
from VeraGridEngine.Devices.Dynamic.rms_template import RmsModelTemplate
from VeraGridEngine.Devices.Dynamic.emt_template import EmtModelTemplate
from VeraGridEngine.Devices.Dynamic.fmu_template import FmuTemplate
from VeraGridEngine.enumerations import (DeviceType, BuildStatus, SubObjectType, FmuTemplateDomain, FmuTemplateMode,
PrpCat)
from VeraGridEngine.Utils.Symbolic.symbolic_io import duplicate_block
from VeraGridEngine.Utils.Symbolic.templates_common_functions import connect_bus_variables_rms, connect_bus_variables_emt
[docs]
class DynamicDevice(PhysicalDevice):
"""
Parent class for devices with dynamic models
"""
__slots__ = (
'_var_factory',
'_rms_model',
'_emt_model',
'_rms_template',
'_emt_template',
'_rms_fmu_template',
'_emt_fmu_template',
'_rms_fmu_import_config',
'_emt_fmu_import_config',
'_rms_fmu_me_import_config',
'_emt_fmu_me_import_config',
)
LOCAL_PROPERTY_DECLARATIONS: Tuple[GCProp, ...] = (
GCProp(
prop_name='rms_model',
units='',
tpe=SubObjectType.DaeBlockType,
definition='RMS dynamic model',
display=False,
cat=[PrpCat.RMS],
),
GCProp(
prop_name='emt_model',
units='',
tpe=SubObjectType.DaeBlockType,
definition='EMT dynamic model',
display=False,
cat=[PrpCat.EMT],
),
GCProp(
prop_name='rms_template',
units='',
tpe=DeviceType.RmsModelTemplateDevice,
definition='Native RMS template used. Assigning it clears rms_fmu_template.',
display=True,
cat=[PrpCat.RMS],
),
GCProp(
prop_name='emt_template',
units='',
tpe=DeviceType.EmtModelTemplateDevice,
definition='Native EMT template used. Assigning it clears emt_fmu_template.',
display=True,
cat=[PrpCat.EMT],
),
GCProp(
prop_name='rms_fmu_template',
units='',
tpe=DeviceType.FmuTemplateDevice,
definition='RMS FMU template used only by RMS simulations. Assigning it clears rms_template.',
display=True,
cat=[PrpCat.RMS],
),
GCProp(
prop_name='emt_fmu_template',
units='',
tpe=DeviceType.FmuTemplateDevice,
definition='EMT FMU template used only by EMT simulations. Assigning it clears emt_template.',
display=True,
cat=[PrpCat.EMT],
),
GCProp(
prop_name='rms_fmu_import_config',
units='',
tpe=str,
definition='Serialized FMU Co-Simulation RMS configuration',
display=False,
cat=[PrpCat.RMS],
),
GCProp(
prop_name='emt_fmu_import_config',
units='',
tpe=str,
definition='Serialized FMU Co-Simulation EMT configuration',
display=False,
cat=[PrpCat.EMT],
),
GCProp(
prop_name='rms_fmu_me_import_config',
units='',
tpe=str,
definition='Serialized FMU Model Exchange RMS configuration',
display=False,
cat=[PrpCat.RMS],
),
GCProp(
prop_name='emt_fmu_me_import_config',
units='',
tpe=str,
definition='Serialized FMU Model Exchange EMT configuration',
display=False,
cat=[PrpCat.EMT],
),
)
def __init__(self,
name: str,
idtag: str | None,
code: str,
device_type: DeviceType,
build_status: BuildStatus = BuildStatus.Commissioned):
"""
:param name:
:param idtag:
:param code:
:param device_type:
:param build_status:
"""
PhysicalDevice.__init__(self,
name=name,
idtag=idtag,
code=code,
device_type=device_type,
build_status=build_status)
self._var_factory: VarFactory | None = None
self._rms_model: Block = Block()
self._rms_template: RmsModelTemplate | None = None
self._rms_fmu_template: FmuTemplate | None = None
self._rms_fmu_import_config: str = ""
self._rms_fmu_me_import_config: str = ""
self._emt_model: Block = Block()
self._emt_template: EmtModelTemplate | None = None
self._emt_fmu_template: FmuTemplate | None = None
self._emt_fmu_import_config: str = ""
self._emt_fmu_me_import_config: str = ""
def _clear_rms_fmu_config(self) -> None:
"""
Clear all serialized RMS FMU runtime configurations.
:return: None.
"""
self._rms_fmu_import_config = ""
self._rms_fmu_me_import_config = ""
def _clear_emt_fmu_config(self) -> None:
"""
Clear all serialized EMT FMU runtime configurations.
:return: None.
"""
self._emt_fmu_import_config = ""
self._emt_fmu_me_import_config = ""
def _apply_rms_fmu_template(self, template: FmuTemplate) -> None:
"""
Copy the symbolic RMS shell and serialized FMU configuration from a reusable template.
:param template: Reusable FMU template.
:return: None.
"""
self._rms_template = None
self._clear_rms_fmu_config()
if self.auto_update_enabled:
template.block.unify_blocks()
self.rms_model = duplicate_block(template.block, self._var_factory)
else:
pass
if template.mode == FmuTemplateMode.CO_SIMULATION:
self._rms_fmu_import_config = template.serialized_config
else:
if template.mode == FmuTemplateMode.MODEL_EXCHANGE:
self._rms_fmu_me_import_config = template.serialized_config
else:
raise ValueError(f"Unsupported FMU template mode {template.mode}")
def _apply_emt_fmu_template(self, template: FmuTemplate) -> None:
"""
Copy the symbolic EMT shell and serialized FMU configuration from a reusable template.
:param template: Reusable FMU template.
:return: None.
"""
self._emt_template = None
self._clear_emt_fmu_config()
if self.auto_update_enabled:
template.block.unify_blocks()
self.emt_model = duplicate_block(template.block, self._var_factory)
else:
pass
if template.mode == FmuTemplateMode.CO_SIMULATION:
self._emt_fmu_import_config = template.serialized_config
else:
if template.mode == FmuTemplateMode.MODEL_EXCHANGE:
self._emt_fmu_me_import_config = template.serialized_config
else:
raise ValueError(f"Unsupported FMU template mode {template.mode}")
[docs]
def set_var_factory(self, val: VarFactory) -> None:
"""
Store the shared variable factory used by RMS and EMT symbolic blocks.
:param val: Shared variable factory.
:return: None.
"""
if isinstance(val, VarFactory):
self._var_factory = val
else:
raise ValueError(f"VarFactory cannot accept {val}")
@property
def rms_model(self) -> Block:
"""
Get the RMS model
"""
return self._rms_model
@rms_model.setter
def rms_model(self, val: Block) -> None:
if isinstance(val, Block):
self._rms_model = val
else:
raise ValueError(f"RMS model cannot accept {val}")
@property
def emt_model(self) -> Block:
"""
Get the EMT model
"""
return self._emt_model
@emt_model.setter
def emt_model(self, val: Block) -> None:
if isinstance(val, Block):
self._emt_model = val
else:
raise ValueError(f"EMT model cannot accept {val}")
@property
def rms_template(self) -> RmsModelTemplate | None:
"""
Get the RMS model
"""
return self._rms_template
@rms_template.setter
def rms_template(self, val: RmsModelTemplate | None) -> None:
if isinstance(val, RmsModelTemplate):
self._rms_template = val
self._rms_fmu_template = None
self._clear_rms_fmu_config()
if self.auto_update_enabled:
rms_mdl = duplicate_block(val.block, self._var_factory)
connect_bus_variables_rms(self, rms_mdl, self._var_factory)
self.rms_model = rms_mdl
elif val is None:
self._rms_template = None
self.rms_model = Block()
print("")
else:
raise ValueError(f"RMS template cannot accept {val}")
@property
def emt_template(self) -> EmtModelTemplate | None:
"""
Get the EMT template
"""
return self._emt_template
@emt_template.setter
def emt_template(self, val: EmtModelTemplate | None) -> None:
if isinstance(val, EmtModelTemplate):
self._emt_template = val
self._emt_fmu_template = None
self._clear_emt_fmu_config()
if self.auto_update_enabled:
# val.block.unify_blocks()
emt_mdl = duplicate_block(val.block, self._var_factory)
connect_bus_variables_emt(self, emt_mdl, self._var_factory, allow_deferred_connection=True)
self.emt_model = emt_mdl
elif val is None:
self._emt_template = None
else:
raise ValueError(f"EMT template cannot accept {val}")
@property
def rms_fmu_template(self) -> FmuTemplate | None:
"""
Get the reusable FMU template assigned to the RMS domain.
:return: RMS FMU template when available.
"""
return self._rms_fmu_template
@rms_fmu_template.setter
def rms_fmu_template(self, val: FmuTemplate | None) -> None:
"""
Set the reusable FMU template assigned to the RMS domain.
:param val: RMS FMU template or ``None``.
:return: None.
"""
if isinstance(val, FmuTemplate):
if val.domain == FmuTemplateDomain.RMS:
self._rms_fmu_template = val
self._apply_rms_fmu_template(val)
else:
raise ValueError(f"RMS FMU template must declare the RMS domain, got {val.domain}")
else:
if val is None:
self._rms_fmu_template = None
self._clear_rms_fmu_config()
else:
raise ValueError(f"RMS FMU template cannot accept {val}")
@property
def emt_fmu_template(self) -> FmuTemplate | None:
"""
Get the reusable FMU template assigned to the EMT domain.
:return: EMT FMU template when available.
"""
return self._emt_fmu_template
@emt_fmu_template.setter
def emt_fmu_template(self, val: FmuTemplate | None) -> None:
"""
Set the reusable FMU template assigned to the EMT domain.
:param val: EMT FMU template or ``None``.
:return: None.
"""
if isinstance(val, FmuTemplate):
if val.domain == FmuTemplateDomain.EMT:
self._emt_fmu_template = val
self._apply_emt_fmu_template(val)
else:
raise ValueError(f"EMT FMU template must declare the EMT domain, got {val.domain}")
else:
if val is None:
self._emt_fmu_template = None
self._clear_emt_fmu_config()
else:
raise ValueError(f"EMT FMU template cannot accept {val}")
@property
def rms_fmu_import_config(self) -> str:
"""
Get the serialized RMS FMU Co-Simulation configuration.
:return: Serialized configuration text.
"""
return self._rms_fmu_import_config
@rms_fmu_import_config.setter
def rms_fmu_import_config(self, val: str) -> None:
"""
Store the serialized RMS FMU Co-Simulation configuration.
:param val: Serialized configuration text.
:return: None.
"""
self._rms_fmu_import_config = str(val)
@property
def emt_fmu_import_config(self) -> str:
"""
Get the serialized EMT FMU Co-Simulation configuration.
:return: Serialized configuration text.
"""
return self._emt_fmu_import_config
@emt_fmu_import_config.setter
def emt_fmu_import_config(self, val: str) -> None:
"""
Store the serialized EMT FMU Co-Simulation configuration.
:param val: Serialized configuration text.
:return: None.
"""
self._emt_fmu_import_config = str(val)
@property
def rms_fmu_me_import_config(self) -> str:
"""
Get the serialized RMS FMU Model Exchange configuration.
:return: Serialized configuration text.
"""
return self._rms_fmu_me_import_config
@rms_fmu_me_import_config.setter
def rms_fmu_me_import_config(self, val: str) -> None:
"""
Store the serialized RMS FMU Model Exchange configuration.
:param val: Serialized configuration text.
:return: None.
"""
self._rms_fmu_me_import_config = str(val)
@property
def emt_fmu_me_import_config(self) -> str:
"""
Get the serialized EMT FMU Model Exchange configuration.
:return: Serialized configuration text.
"""
return self._emt_fmu_me_import_config
@emt_fmu_me_import_config.setter
def emt_fmu_me_import_config(self, val: str) -> None:
"""
Store the serialized EMT FMU Model Exchange configuration.
:param val: Serialized configuration text.
:return: None.
"""
self._emt_fmu_me_import_config = str(val)