Skip to content

Commit 19c6034

Browse files
authored
Merge branch 'master' into simulation
2 parents d4d5732 + c4dd795 commit 19c6034

32 files changed

+894
-508
lines changed

schema/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
install(DIRECTORY fmi2 fmi3 ssp
2-
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/OMSimulator/schema)
2+
DESTINATION ${CMAKE_INSTALL_LIBDIR}/OMSimulator/schema)

src/OMSimulatorPython/component.py

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,16 @@
66
from OMSimulator.elementgeometry import ElementGeometry
77
from OMSimulator import namespace
88

9-
109
class Component:
1110
def __init__(self, name: CRef, fmuPath: Path | str, connectors=None, unitDefinitions=None):
1211
self.name = CRef(name)
1312
self.fmuPath = Path(fmuPath)
1413
self.connectors = connectors or list()
1514
self.unitDefinitions = unitDefinitions or list()
1615
self.elementgeometry = None
17-
self.value = Values() ## TODO propogate Values
16+
self.description = None
17+
self.value = Values()
18+
self.solver = None
1819
self.parameterResources = []
1920

2021
def addConnector(self, connector):
@@ -26,7 +27,7 @@ def addSSV(self, resource: str):
2627
self.parameterResources.append(resource)
2728

2829
def list(self, prefix=""):
29-
print(f"{prefix} FMU: ({self.name})")
30+
print(f"{prefix} FMU: {self.name} '{self.description}'")
3031
prefix += ' |--'
3132
print(f"{prefix} path: {self.fmuPath}")
3233

@@ -56,11 +57,18 @@ def list(self, prefix=""):
5657
for resource in self.parameterResources:
5758
print(f"{prefix} Parameter Bindings: {resource}")
5859

60+
## list solver settings
61+
if self.solver:
62+
print(f"{prefix} Solver Settings:")
63+
print(f"{prefix} |-- name: {self.solver}")
64+
5965
def exportToSSD(self, node):
6066
component_node = ET.SubElement(node, namespace.tag("ssd", "Component"))
6167
component_node.set("name", str(self.name))
6268
component_node.set("type", "application/x-fmu-sharedlibrary")
6369
component_node.set("source", str(self.fmuPath))
70+
if self.description:
71+
component_node.set("description", self.description)
6472

6573
if len(self.connectors) > 0:
6674
connectors_node = ET.SubElement(component_node, namespace.tag("ssd", "Connectors"))
@@ -82,5 +90,13 @@ def exportToSSD(self, node):
8290
parameter_binding_node = ET.SubElement(parameter_bindings_node, namespace.tag("ssd", "ParameterBinding"))
8391
parameter_binding_node.set("source", resource)
8492

85-
def setValue(self, cref:str, value, unit=None):
86-
self.value.setValue(cref, value, unit)
93+
## export Annotations
94+
if self.solver:
95+
from OMSimulator import utils
96+
utils.exportAnnotations(component_node, self.solver)
97+
98+
def setValue(self, cref:str, value, unit=None, description = None):
99+
self.value.setValue(cref, value, unit, description)
100+
101+
def setSolver(self, name: str):
102+
self.solver = name

src/OMSimulatorPython/connection.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ def __init__(self, startElement : str, startConnector : str, endElement : str, e
2222
self.startConnector = startConnector
2323
self.endElement = endElement
2424
self.endConnector = endConnector
25+
self.description = None
2526
self.connectionGeometry = None
2627

2728
def list(self, prefix=""):
@@ -35,5 +36,30 @@ def exportToSSD(self, node):
3536
connection_node.set("startConnector", self.startConnector)
3637
connection_node.set("endElement", self.endElement)
3738
connection_node.set("endConnector", self.endConnector)
39+
if self.description:
40+
connection_node.set("description", self.description)
3841
if self.connectionGeometry:
3942
self.connectionGeometry.exportToSSD(connection_node)
43+
44+
@staticmethod
45+
def importFromNode(node, root):
46+
connections_node = node.find("ssd:Connections", namespaces=namespace.ns)
47+
if connections_node is None:
48+
return
49+
for connection in connections_node.findall("ssd:Connection", namespaces=namespace.ns):
50+
startElement = connection.get("startElement", '')
51+
startConnector = connection.get("startConnector")
52+
endElement = connection.get("endElement", '')
53+
endConnector = connection.get("endConnector")
54+
description = connection.get("description")
55+
root.addConnection(startElement, startConnector, endElement, endConnector)
56+
if description:
57+
root.connections[-1].description = description
58+
connection_geometry = connection.find("ssd:ConnectionGeometry", namespaces=namespace.ns)
59+
if connection_geometry is not None:
60+
pointsX = connection_geometry.get("pointsX")
61+
pointsY = connection_geometry.get("pointsY")
62+
# print (f"ConnectionGeometry: pointsX: {pointsX}, pointsY: {pointsY}")
63+
if pointsX and pointsY:
64+
connectionGeometry = ConnectionGeometry([float(x) for x in pointsX.split()], [float(y) for y in pointsY.split()])
65+
root.connections[-1].connectionGeometry = connectionGeometry

src/OMSimulatorPython/connector.py

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ def __init__(self, name : Union[str, CRef], causality : Causality, signal_type :
4242
self.signal_type = signal_type if isinstance(signal_type, SignalType) else SignalType[signal_type]
4343
self.unit = None
4444
self.connectorGeometry = None
45+
self.description = None
4546

4647
def getCref(self):
4748
return self.name
@@ -56,16 +57,67 @@ def setUnit(self, unit: str):
5657
self.unit = unit
5758

5859
def list(self, prefix=""):
59-
print(f"{prefix} ({self.name}, {self.causality}, {self.signal_type}, {self.unit})")
60+
print(f"{prefix} ({self.name}, {self.causality}, {self.signal_type}, {self.unit}, '{self.description}')")
6061
if self.connectorGeometry:
6162
self.connectorGeometry.list(prefix)
6263

6364
def exportToSSD(self, node):
6465
connector_node = ET.SubElement(node, namespace.tag("ssd", "Connector"))
6566
connector_node.set("name", str(self.name))
6667
connector_node.set("kind", self.causality.name)
68+
if self.description:
69+
connector_node.set("description", self.description)
6770
connectors_type = ET.SubElement(connector_node, namespace.tag("ssc", self.signal_type.name))
6871
if self.unit is not None:
6972
connectors_type.set("unit", self.unit)
7073
if self.connectorGeometry is not None:
7174
self.connectorGeometry.exportToSSD(connector_node)
75+
76+
@staticmethod
77+
def importFromNode(node):
78+
"""Extract and print system connectors"""
79+
connectors = []
80+
connectors_node = node.find("ssd:Connectors", namespaces=namespace.ns)
81+
82+
# No connectors found
83+
if connectors_node is None:
84+
return connectors
85+
86+
for connector in connectors_node.findall("ssd:Connector", namespaces=namespace.ns):
87+
name = connector.get("name")
88+
kind = connector.get("kind")
89+
description = connector.get("description")
90+
# Convert kind string to enum type
91+
kind = Causality[kind]
92+
93+
# Find the connector type (Real, Integer, Boolean)
94+
con = None
95+
for connectortype in ["ssc:Real", "ssc:Integer", "ssc:Boolean"]: # Expected connector types
96+
type_element = connector.find(connectortype, namespaces=namespace.ns)
97+
if type_element is not None:
98+
signal_type = connectortype.split(":")[-1] # Extracts 'Real', 'Integer', or 'Boolean'
99+
con = Connector(name, kind, SignalType[signal_type])
100+
unit = type_element.get("unit")
101+
if description:
102+
con.description = description
103+
# Set unit if it exists
104+
if unit:
105+
con.setUnit(unit)
106+
# print(f"Connector: {name}, Kind: {kind}, SignalType: {signal_type}, Unit: {unit}")
107+
break # Stop after the first valid type is found
108+
109+
# Check if connector has geometry information
110+
connector_geometry = connector.find("ssd:ConnectorGeometry", namespaces=namespace.ns)
111+
if connector_geometry is not None:
112+
x = connector_geometry.get("x")
113+
y = connector_geometry.get("y")
114+
if x and y:
115+
connectorGeometry = ConnectorGeometry(float(x), float(y))
116+
connectorGeometry.x = float(x) # Set x coordinate
117+
connectorGeometry.y = float(y) # Set y coordinate
118+
con.connectorGeometry = connectorGeometry
119+
120+
if con:
121+
connectors.append(con)
122+
123+
return connectors

src/OMSimulatorPython/fmu.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ def _parse_variables(self, model_description):
102102
scalar_variables = model_description.xpath('//ModelVariables/ScalarVariable')
103103
for scalar_var in scalar_variables:
104104
name = scalar_var.get('name')
105+
description = scalar_var.get('description')
105106
value_reference = scalar_var.get('valueReference')
106107
causality = scalar_var.get('causality', 'local')
107108
variability = scalar_var.get('variability', 'continuous')
@@ -125,7 +126,7 @@ def _parse_variables(self, model_description):
125126
self._states.append(self._variables[derivative_index - 1])
126127

127128
# Create and store the variable
128-
variable = Variable(name, value_reference, causality, variability, var_type, unit, start)
129+
variable = Variable(name, description, value_reference, causality, variability, var_type, unit, start)
129130

130131
# Assign unit definitions if applicable
131132
if unit:
@@ -161,6 +162,7 @@ def makeConnectors(self):
161162
if var.isInput() or var.isOutput() or var.isParameter() or var.isCalculatedParameter():
162163
connector = Connector(var.name, var.causality, var.signal_type)
163164
connector.setUnit(var.unit)
165+
connector.description = var.description
164166
connectors.append(connector)
165167
return connectors
166168

src/OMSimulatorPython/ssd.py

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,15 @@
22
from pathlib import Path
33

44
from lxml import etree as ET
5+
from OMSimulator import capi
56
from OMSimulator.cref import CRef
67
from OMSimulator.fmu import FMU
78
from OMSimulator.settings import suppress_path_to_str
89
from OMSimulator.system import System
910
from OMSimulator.ssv import SSV
1011
from OMSimulator.unit import Unit
1112
from OMSimulator import namespace, utils
13+
from datetime import datetime
1214

1315
logger = logging.getLogger(__name__)
1416

@@ -34,6 +36,7 @@ def importFromFile(filename: Path, resources = None):
3436
try:
3537
tree = ET.parse(filename)
3638
root = tree.getroot()
39+
utils.validateSSP(root, filename, "SystemStructureDescription.xsd")
3740
variant_name = root.get("name")
3841
ssd = SSD(variant_name)
3942
ssd._filename = Path(filename).resolve()
@@ -43,9 +46,8 @@ def importFromFile(filename: Path, resources = None):
4346
raise ValueError(f"Invalid SSD file: Missing <ssd:System> in {filename}")
4447

4548
ssd.system = System.importFromNode(system, ssd, resources)
46-
4749
utils.parseDefaultExperiment(root, ssd)
48-
utils.parseUnitDefinitions(root, ssd)
50+
Unit.importFromNode(root, ssd)
4951
logger.debug(f"SSD '{variant_name}' successfully imported from {filename}")
5052
return ssd
5153

@@ -79,6 +81,13 @@ def addSSV(self, cref: CRef, resource):
7981
subcref = self._validateCref(cref)
8082
self.system.addSSV(subcref, resource)
8183

84+
def newSolver(self, options: dict):
85+
self.system.solvers.append(options)
86+
87+
def setSolver(self, cref: CRef, name: str):
88+
subcref = self._validateCref(cref)
89+
self.system.setSolver(subcref, name)
90+
8291
def _getComponentResourcePath(self, cref: CRef):
8392
subcref = self._validateCref(cref)
8493
return self.system._getComponentResourcePath(subcref)
@@ -120,6 +129,8 @@ def export(self, filename: str):
120129
nsmap=namespace.ns,
121130
name=self._name,
122131
version="2.0",
132+
generationTool= capi.capi().getVersion(),
133+
generationDateAndTime=datetime.now().isoformat()
123134
)
124135

125136
self.system.export(root)
@@ -137,9 +148,10 @@ def export(self, filename: str):
137148

138149
def _exportUnitDefinitions(self, node):
139150
'''Exports unit definitions to the given XML node.'''
140-
unit_definitions_node = ET.SubElement(node, namespace.tag("ssd", "Units"))
141-
for unit in self.unitDefinitions:
142-
unit.exportToSSD(unit_definitions_node)
151+
if self.unitDefinitions:
152+
unit_definitions_node = ET.SubElement(node, namespace.tag("ssd", "Units"))
153+
for unit in self.unitDefinitions:
154+
unit.exportToSSD(unit_definitions_node)
143155

144156
def _exportDefaultExperiment(self, node):
145157
'''Exports default experiment settings.'''

src/OMSimulatorPython/ssp.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,11 @@ def addComponent(self, cref: CRef, resource: str):
129129

130130
return self.activeVariant.addComponent(cref, resource, inst=fmu_inst)
131131

132+
def newSolver(self, options: dict):
133+
if self.activeVariant is None:
134+
raise ValueError("No active variant set in the SSP.")
135+
self.activeVariant.newSolver(options)
136+
132137
def addSSV(self, cref: CRef, resource: str):
133138
if self.activeVariant is None:
134139
raise ValueError("No active variant set in the SSP.")
@@ -155,6 +160,13 @@ def setValue(self, cref: CRef, value, unit = None):
155160

156161
self.activeVariant.setValue(cref, value, unit)
157162

163+
def setSolver(self, cref: CRef, name: str):
164+
if self.activeVariant is None:
165+
raise ValueError("No active variant set in the SSP.")
166+
167+
self.activeVariant.setSolver(cref, name)
168+
169+
158170
def addSystem(self, cref: CRef):
159171
if self.activeVariant is None:
160172
raise ValueError("No active variant set in the SSP.")

src/OMSimulatorPython/ssv.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ def __init__(self, ssv_path : str | None = None):
1414
self.filename = Path(ssv_path)
1515
self.importFromSSV(self.filename)
1616

17-
def setValue(self, cref:str, value, unit = None):
18-
self.value.setValue(cref, value, unit)
17+
def setValue(self, cref:str, value, unit = None, description = None):
18+
self.value.setValue(cref, value, unit, description)
1919

2020
def list(self, prefix = ""):
2121
self.value.list(prefix)
@@ -40,5 +40,5 @@ def export(self, filename = str):
4040

4141
def importFromSSV(self, filename):
4242
parameterValues = utils.parseSSV(filename)
43-
for key, (value, unit) in parameterValues.items():
44-
self.setValue(key, value, unit)
43+
for key, (value, unit, description) in parameterValues.items():
44+
self.setValue(key, value, unit, description)

0 commit comments

Comments
 (0)