Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions src/OMSimulatorPython/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,11 +65,12 @@ def __repr__(self):
return "LinearTransformation (factor: %s, offset: %s)" % (self.factor, self.offset)

class Connection:
def __init__(self, startElement : str, startConnector : str, endElement : str, endConnector : str):
def __init__(self, startElement : str, startConnector : str, endElement : str, endConnector : str, suppressUnitConversion : bool = False):
self.startElement = startElement
self.startConnector = startConnector
self.endElement = endElement
self.endConnector = endConnector
self.suppressUnitConversion = suppressUnitConversion
self.description = None
self.connectionGeometry = None
## GTTransformation Optional support
Expand All @@ -78,6 +79,8 @@ def __init__(self, startElement : str, startConnector : str, endElement : str, e

def list(self, prefix=""):
print(f"{prefix} {self.startElement}.{self.startConnector} -> {self.endElement}.{self.endConnector}")
if self.suppressUnitConversion:
print(f"{prefix} |-- suppressUnitConversion: true")
if self.connectionGeometry:
self.connectionGeometry.list(prefix + " |--")
if self.linearTransformation:
Expand All @@ -89,6 +92,8 @@ def exportToSSD(self, node):
connection_node.set("startConnector", str(self.startConnector))
connection_node.set("endElement", str(self.endElement))
connection_node.set("endConnector", str(self.endConnector))
if self.suppressUnitConversion:
connection_node.set("suppressUnitConversion", "true")
if self.description:
connection_node.set("description", self.description)
if self.connectionGeometry:
Expand All @@ -107,7 +112,8 @@ def importFromNode(node, root):
endElement = connection.get("endElement", '')
endConnector = connection.get("endConnector")
description = connection.get("description")
root.addConnection(startElement, startConnector, endElement, endConnector)
suppressUnitConversion = connection.get("suppressUnitConversion", "false") in ("true", "1")
root.addConnection(startElement, startConnector, endElement, endConnector, suppressUnitConversion)
if description:
root.connections[-1].description = description
connection_geometry = connection.find("ssd:ConnectionGeometry", namespaces=namespace.ns)
Expand Down
5 changes: 3 additions & 2 deletions src/OMSimulatorPython/instantiated_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -232,8 +232,9 @@ def __init__(self, json_description, ssdName: str, system: System, resources: di

start = self.mappedCrefs[start_element]
end = self.mappedCrefs[end_element]
self.apiCall.append(f'oms_addConnection("{start}", "{end}")')
status = Capi.addConnection(start, end)
suppressUnitConversion = connection.get("suppress unit conversion", False)
self.apiCall.append(f'oms_addConnection("{start}", "{end}", {str(suppressUnitConversion).lower()})')
status = Capi.addConnection(start, end, suppressUnitConversion)
if status != Status.ok:
raise RuntimeError(f"Failed to add oms_addConnection: {status}")
## add linear transformation if exist
Expand Down
4 changes: 2 additions & 2 deletions src/OMSimulatorPython/ssd.py
Original file line number Diff line number Diff line change
Expand Up @@ -211,11 +211,11 @@ def removeSSVReference(self, cref: CRef, resource):
subcref = self._validateCref(cref)
self.system.removeSSVReference(subcref, resource)

def addConnection(self, cref1: CRef, cref2: CRef):
def addConnection(self, cref1: CRef, cref2: CRef, suppressUnitConversion: bool = False):
subcref1 = self._validateCref(cref1)
subcref2 = self._validateCref(cref2)
#logger.debug(f"Adding connection from {subcref1} to {subcref2}")
self.system._addConnection(subcref1, subcref2)
self.system._addConnection(subcref1, subcref2, suppressUnitConversion)

def deleteConnection(self, cref1: CRef, cref2: CRef):
subcref1 = self._validateCref(cref1)
Expand Down
4 changes: 2 additions & 2 deletions src/OMSimulatorPython/ssp.py
Original file line number Diff line number Diff line change
Expand Up @@ -304,11 +304,11 @@ def removeSSVReference(self, cref: CRef, resource: str):

self.activeVariant.removeSSVReference(cref, resource)

def addConnection(self, cref1: CRef, cref2: CRef):
def addConnection(self, cref1: CRef, cref2: CRef, suppressUnitConversion: bool = False):
if self.activeVariant is None:
raise ValueError("No active variant set in the SSP.")

self.activeVariant.addConnection(cref1, cref2)
self.activeVariant.addConnection(cref1, cref2, suppressUnitConversion)

def deleteConnection(self, cref1: CRef, cref2: CRef):
if self.activeVariant is None:
Expand Down
15 changes: 9 additions & 6 deletions src/OMSimulatorPython/system.py
Original file line number Diff line number Diff line change
Expand Up @@ -653,7 +653,7 @@ def exportSSMTemplate(self, cref: CRef, node):
case _:
raise ValueError(f"Element '{first}' in system '{self.name}' is neither a System nor a Component")

def _addConnection(self, cref1: CRef, cref2: CRef) -> None:
def _addConnection(self, cref1: CRef, cref2: CRef, suppressUnitConversion: bool = False) -> None:
first1 = cref1.first()
first2 = cref2.first()

Expand All @@ -663,7 +663,7 @@ def _addConnection(self, cref1: CRef, cref2: CRef) -> None:
raise ValueError(f"System '{first1}' not found in '{self.name}'")

# Add the connection inside the subsystem
self.elements[first1]._addConnection(cref1.pop_first(), cref2.pop_first())
self.elements[first1]._addConnection(cref1.pop_first(), cref2.pop_first(), suppressUnitConversion)
# Top level system connections
else:
(start_element, start_connector) = cref1.split()
Expand All @@ -674,7 +674,7 @@ def _addConnection(self, cref1: CRef, cref2: CRef) -> None:
# stores/compares startElement/startConnector as plain strings
# everywhere else (XML import, _deleteConnection) -- leaving these as
# CRef silently breaks deleteConnection's equality check later.
self.addConnection(str(start_element), str(start_connector), str(end_element), str(end_connector))
self.addConnection(str(start_element), str(start_connector), str(end_element), str(end_connector), suppressUnitConversion)

def _deleteConnection(self, crefA: CRef, crefB: CRef) -> None:
"""Deletes a connection from the system."""
Expand Down Expand Up @@ -718,7 +718,7 @@ def _findConnector(self, element_name, connector_name):

raise ValueError(f"Connector '{connector_name}' not found in element '{element_name or self.name}'")

def addConnection(self, startElement : str, startConnector : str, endElement : str, endConnector : str):
def addConnection(self, startElement : str, startConnector : str, endElement : str, endConnector : str, suppressUnitConversion : bool = False):
"""Adds a connection to the system."""
# Resolve source connector owner and causality
(source_owner, source_kind) = self._findConnector(startElement, startConnector)
Expand All @@ -727,11 +727,11 @@ def addConnection(self, startElement : str, startConnector : str, endElement : s

if Connection.is_validConnection(source_owner, source_kind, dest_owner, dest_kind):
self.isConnectorAlreadyConnected(startElement, startConnector, endElement, endConnector)
self.connections.append(Connection(startElement, startConnector, endElement, endConnector))
self.connections.append(Connection(startElement, startConnector, endElement, endConnector, suppressUnitConversion))
# flipped connection
elif Connection.is_validConnection(dest_owner, dest_kind, source_owner, source_kind):
self.isConnectorAlreadyConnected(endElement, endConnector, startElement, startConnector)
self.connections.append(Connection(endElement, endConnector, startElement, startConnector))
self.connections.append(Connection(endElement, endConnector, startElement, startConnector, suppressUnitConversion))
else:
raise ValueError(f"info: Invalid connection from '{startElement}.{startConnector}'->'{endElement}.{endConnector}' as causality are violated with '{source_owner}.{source_kind.name}' -> '{dest_owner}.{dest_kind.name}'")

Expand Down Expand Up @@ -1120,6 +1120,9 @@ def processElements(self, elements_dict: dict, connections: list, data: dict, so
elif endSolver is None and startSolver is not None:
solver = startSolver

if connection.suppressUnitConversion:
connection_info["suppress unit conversion"] = True

## add linear transformation info if available
if connection.linearTransformation:
connection_info["linear transformation"] = {
Expand Down
1 change: 1 addition & 0 deletions testsuite/tests/api/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ oms_add_test(
setValueSSV1.py
setValueSSV2.py
setValueSSV3.py
suppressUnitConversion1.py
swapSSV1.py
swapSSV2.py
swapSSV3.py
Expand Down
70 changes: 70 additions & 0 deletions testsuite/tests/api/suppressUnitConversion1.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
## status: correct
## linux: yes
## ucrt64: yes
## win: yes
## mac: yes
## asan: no
## the FMUs this test loads leak memory inside their own binaries

from OMSimulator import SSP, CRef, Settings

Settings.suppressPath = True

model = SSP()
model.addResource('../resources/Modelica.Blocks.Math.Gain.fmu', new_name='resources/Gain.fmu')
model.addComponent(CRef('default', 'Gain1'), 'resources/Gain.fmu')
model.addComponent(CRef('default', 'Gain2'), 'resources/Gain.fmu')
model.addComponent(CRef('default', 'Gain3'), 'resources/Gain.fmu')

model.addConnection(CRef('default', 'Gain1', 'y'), CRef('default', 'Gain2', 'u'), suppressUnitConversion=True)
model.addConnection(CRef('default', 'Gain2', 'y'), CRef('default', 'Gain3', 'u'))
model.export('suppressUnitConversion1.ssp')

model2 = SSP('suppressUnitConversion1.ssp')
model2.list()

instantiated_model = model2.instantiate()
instantiated_model.initialize()
instantiated_model.terminate()
instantiated_model.delete()

## Result:
## info: Result file: default_res.mat (bufferSize=10)
## <class 'OMSimulator.ssp.SSP'>
## |-- Resources:
## |-- resources/Gain.fmu
## |-- Active Variant: default
## |-- <class 'OMSimulator.ssd.SSD'>
## |-- Variant "default": <hidden>
## |-- |-- System: default 'None'
## |-- |-- |-- Connectors:
## |-- |-- |-- Elements:
## |-- |-- |-- |-- FMU: Gain1 'None'
## |-- |-- |-- |-- |-- path: resources/Gain.fmu
## |-- |-- |-- |-- |-- Connectors:
## |-- |-- |-- |-- |-- |-- (u, Causality.input, SignalType.Real, None, 'Input signal connector')
## |-- |-- |-- |-- |-- |-- (y, Causality.output, SignalType.Real, None, 'Output signal connector')
## |-- |-- |-- |-- |-- |-- (k, Causality.parameter, SignalType.Real, 1, 'Gain value multiplied with input signal')
## |-- |-- |-- |-- FMU: Gain2 'None'
## |-- |-- |-- |-- |-- path: resources/Gain.fmu
## |-- |-- |-- |-- |-- Connectors:
## |-- |-- |-- |-- |-- |-- (u, Causality.input, SignalType.Real, None, 'Input signal connector')
## |-- |-- |-- |-- |-- |-- (y, Causality.output, SignalType.Real, None, 'Output signal connector')
## |-- |-- |-- |-- |-- |-- (k, Causality.parameter, SignalType.Real, 1, 'Gain value multiplied with input signal')
## |-- |-- |-- |-- FMU: Gain3 'None'
## |-- |-- |-- |-- |-- path: resources/Gain.fmu
## |-- |-- |-- |-- |-- Connectors:
## |-- |-- |-- |-- |-- |-- (u, Causality.input, SignalType.Real, None, 'Input signal connector')
## |-- |-- |-- |-- |-- |-- (y, Causality.output, SignalType.Real, None, 'Output signal connector')
## |-- |-- |-- |-- |-- |-- (k, Causality.parameter, SignalType.Real, 1, 'Gain value multiplied with input signal')
## |-- |-- |-- Connections:
## |-- |-- |-- |-- Gain1.y -> Gain2.u
## |-- |-- |-- |-- |-- suppressUnitConversion: true
## |-- |-- |-- |-- Gain2.y -> Gain3.u
## |-- UnitDefinitions:
## |-- |-- Unit: 1
## |-- |-- |-- BaseUnit:
## |-- DefaultExperiment
## |-- |-- startTime: 0.0
## |-- |-- stopTime: 1.0
## endResult