Skip to content

Commit e0eb51e

Browse files
authored
Fix connections in nested systems (#1534)
* check for valid connections with export name
1 parent b3cd458 commit e0eb51e

5 files changed

Lines changed: 168 additions & 2 deletions

File tree

src/OMSimulatorLib/Connection.cpp

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,44 @@ bool oms::Connection::isValid(const ComRef& crefA, const ComRef& crefB, const Co
205205
return connectorA && connectorB;
206206
}
207207

208+
/*! Checks the validity of connections based on export connector names it is possible that connections
209+
are valid even though the causality check based on SSP-1.0 fails. This is the case when connecting
210+
system connectors to element connectors based on export names and how the decomposition of the top
211+
level system is done.
212+
*/
213+
bool oms::Connection::isValidExportConnectorName(const Connector& conA, const Connector& conB)
214+
{
215+
bool connectorA, connectorB;
216+
// Check connector A
217+
if (ComRef(conA.getExportName()).isValidIdent()) // this is a system
218+
{
219+
// Connector A of a systems must be input or parameter
220+
connectorA = conA.isInput() || conA.isParameter();
221+
}
222+
else // this is an element
223+
{
224+
// Connector A of an element must be output, calculated parameter, or inout
225+
// TODO: check for inout, neither FMI-1.0 nor FMI-2.0 do support inout
226+
connectorA = conA.isOutput() || conA.isCalculatedParameter();
227+
}
228+
229+
// Check connector B
230+
if (ComRef(conB.getExportName()).isValidIdent()) // this is a system
231+
{
232+
// Connector B of a systems must be output or calculated parameter
233+
connectorB = conB.isOutput() || conB.isCalculatedParameter();
234+
}
235+
else // this is an element
236+
{
237+
// Connector A of an element must be input, parameter, or inout
238+
// TODO: check for inout, neither FMI-1.0 nor FMI-2.0 do support inout
239+
connectorB = conB.isParameter() || conB.isInput();
240+
}
241+
242+
// both connectors must be valid in order to make the connection valid
243+
return connectorA && connectorB;
244+
}
245+
208246
bool oms::Connection::isValidUnits(const ComRef& crefA, const ComRef& crefB, const Connector& conA, const Connector& conB)
209247
{
210248
if (conA.connectorUnits.empty() || conB.connectorUnits.empty())

src/OMSimulatorLib/Connection.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,8 @@ namespace oms
7575
* \brief Checks a connection based on SSP-1.0 connection table
7676
*/
7777
static bool isValid(const ComRef& crefA, const ComRef& crefB, const Connector& conA, const Connector& conB);
78+
static bool isValidExportConnectorName(const Connector& conA, const Connector& conB);
79+
7880
static bool isValidUnits(const ComRef& crefA, const ComRef& crefB, const Connector& conA, const Connector& conB);
7981
static void getSIUnits(const Connector& connector, std::map<std::string, std::string>& baseUnits);
8082
oms_status_enu_t rename(const oms::ComRef& cref, const oms::ComRef& newCref);

src/OMSimulatorLib/System.cpp

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1223,6 +1223,17 @@ oms_status_enu_t oms::System::addConnection(const oms::ComRef& crefA, const oms:
12231223
connections.back() = new oms::Connection(crefB, crefA, suppressUnitConversion);
12241224
connections.push_back(NULL);
12251225
}
1226+
else if (oms::Connection::isValidExportConnectorName(*conA, *conB))
1227+
{
1228+
connections.back() = new oms::Connection(crefA, crefB, suppressUnitConversion);
1229+
connections.push_back(NULL);
1230+
}
1231+
// flipped causality check
1232+
else if (oms::Connection::isValidExportConnectorName(*conB, *conA))
1233+
{
1234+
connections.back() = new oms::Connection(crefB, crefA, suppressUnitConversion);
1235+
connections.push_back(NULL);
1236+
}
12261237
else
12271238
return logError("Causality mismatch in connection: " + std::string(crefA) + " -> " + std::string(crefB));
12281239

@@ -1653,7 +1664,8 @@ oms_status_enu_t oms::System::updateDependencyGraphs()
16531664
Connector* varB = getConnector(connection->getSignalB());
16541665
if (varA && varB)
16551666
{
1656-
bool validConnection = oms::Connection::isValid(connection->getSignalA(), connection->getSignalB(), *varA, *varB);
1667+
bool validConnection = oms::Connection::isValid(connection->getSignalA(), connection->getSignalB(), *varA, *varB) ||
1668+
oms::Connection::isValidExportConnectorName(*varA, *varB);
16571669
if (validConnection)
16581670
{
16591671
initializationGraph.addEdge(Connector(varA->getCausality(), varA->getType(), connection->getSignalA(), this->getFullCref()), Connector(varB->getCausality(), varB->getType(), connection->getSignalB(), this->getFullCref()));
@@ -1684,7 +1696,8 @@ oms_status_enu_t oms::System::updateDependencyGraphs()
16841696
Connector *varB = getConnector(connection->getSignalB());
16851697
if (varA && varB)
16861698
{
1687-
bool validConnection = oms::Connection::isValid(connection->getSignalA(), connection->getSignalB(), *varA, *varB);
1699+
bool validConnection = oms::Connection::isValid(connection->getSignalA(), connection->getSignalB(), *varA, *varB) ||
1700+
oms::Connection::isValidExportConnectorName(*varA, *varB);
16881701
if (validConnection)
16891702
{
16901703
initializationGraph.setUnits(varA, varB, connection->getSuppressUnitConversion());

testsuite/simulation/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ SimpleSimulation11.py \
2323
SimpleSimulation12.py \
2424
SimpleSimulation13.py \
2525
SimpleSimulation14.py \
26+
SimpleSimulation15.py \
2627
stepUntil.py \
2728

2829
# Run make failingtest
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
## status: correct
2+
## teardown_command: rm -rf SimpleSimulation15_res.mat
3+
## linux: yes
4+
## ucrt64: yes
5+
## win: yes
6+
## mac: yes
7+
8+
from OMSimulator import SSP, CRef, Connector, Causality, SignalType
9+
10+
model = SSP()
11+
model.addResource('../resources/Modelica.Blocks.Math.Add.fmu', new_name='resources/Add.fmu')
12+
model.addComponent(CRef("default", "Add1"), "resources/Add.fmu")
13+
model.addComponent(CRef("default", "Add3"), "resources/Add.fmu")
14+
15+
model.activeVariant.system.addConnector(Connector('input1', Causality.input, SignalType.Real))
16+
17+
18+
model.addSystem(CRef("default", "sub-system"))
19+
subsystem = model.activeVariant.system.elements[CRef("sub-system")]
20+
21+
model.addComponent(CRef("default", "sub-system", "Add2"), "resources/Add.fmu")
22+
23+
24+
subsystem.addConnector(Connector(CRef("u"), Causality.input, SignalType.Real))
25+
subsystem.addConnector(Connector(CRef("y"), Causality.output, SignalType.Real))
26+
27+
subsystem.addConnection("", "u", "Add2", "u1")
28+
subsystem.addConnection("", "u", "Add2", "u2")
29+
subsystem.addConnection("", "y", "Add2", "y")
30+
31+
32+
model.addConnection(CRef("default", "Add1", "y"), CRef("default", "sub-system", "u"))
33+
model.addConnection(CRef("default", "sub-system", "y"), CRef("default", "Add3", "u1"))
34+
model.addConnection(CRef("default", "sub-system", "y"), CRef("default", "Add3", "u2"))
35+
36+
## check failing connection and fix it
37+
## model.addConnection(CRef("default", "input1"), CRef("default", "Add1", "y"))
38+
39+
model.list()
40+
instantiated_model = model.instantiate()
41+
#print(instantiated_model.dumpApiCalls())
42+
43+
instantiated_model.setResultFile("SimpleSimulation15_res.mat")
44+
instantiated_model.setValue(CRef('default', 'sub-system', 'u'), 400.0)
45+
46+
instantiated_model.initialize()
47+
instantiated_model.simulate()
48+
print(f"info: default.sub-system.u: {instantiated_model.getValue(CRef('default', 'sub-system', 'u'))}", flush=True)
49+
print(f"info: default.sub-system.Add2.u1: {instantiated_model.getValue(CRef('default', 'sub-system', 'Add2', 'u1'))}", flush=True)
50+
print(f"info: default.sub-system.Add2.u2: {instantiated_model.getValue(CRef('default', 'sub-system', 'Add2', 'u2'))}", flush=True)
51+
print(f"info: default.sub-system.Add2.y : {instantiated_model.getValue(CRef('default', 'sub-system', 'Add2', 'y'))}", flush=True)
52+
53+
instantiated_model.terminate()
54+
instantiated_model.delete()
55+
56+
## Result:
57+
## info: Result file: SimpleSimulation15_res.mat (bufferSize=1)
58+
## <class 'OMSimulator.ssp.SSP'>
59+
## |-- Resources:
60+
## |-- resources/Add.fmu
61+
## |-- Active Variant: default
62+
## |-- <class 'OMSimulator.ssd.SSD'>
63+
## |-- Variant "default": None
64+
## |-- |-- System: default 'None'
65+
## |-- |-- |-- Connectors:
66+
## |-- |-- |-- |-- (input1, Causality.input, SignalType.Real, None, 'None')
67+
## |-- |-- |-- Elements:
68+
## |-- |-- |-- |-- FMU: Add1 'None'
69+
## |-- |-- |-- |-- |-- path: resources/Add.fmu
70+
## |-- |-- |-- |-- |-- Connectors:
71+
## |-- |-- |-- |-- |-- |-- (u1, Causality.input, SignalType.Real, None, 'Connector of Real input signal 1')
72+
## |-- |-- |-- |-- |-- |-- (u2, Causality.input, SignalType.Real, None, 'Connector of Real input signal 2')
73+
## |-- |-- |-- |-- |-- |-- (y, Causality.output, SignalType.Real, None, 'Connector of Real output signal')
74+
## |-- |-- |-- |-- |-- |-- (k1, Causality.parameter, SignalType.Real, None, 'Gain of input signal 1')
75+
## |-- |-- |-- |-- |-- |-- (k2, Causality.parameter, SignalType.Real, None, 'Gain of input signal 2')
76+
## |-- |-- |-- |-- FMU: Add3 'None'
77+
## |-- |-- |-- |-- |-- path: resources/Add.fmu
78+
## |-- |-- |-- |-- |-- Connectors:
79+
## |-- |-- |-- |-- |-- |-- (u1, Causality.input, SignalType.Real, None, 'Connector of Real input signal 1')
80+
## |-- |-- |-- |-- |-- |-- (u2, Causality.input, SignalType.Real, None, 'Connector of Real input signal 2')
81+
## |-- |-- |-- |-- |-- |-- (y, Causality.output, SignalType.Real, None, 'Connector of Real output signal')
82+
## |-- |-- |-- |-- |-- |-- (k1, Causality.parameter, SignalType.Real, None, 'Gain of input signal 1')
83+
## |-- |-- |-- |-- |-- |-- (k2, Causality.parameter, SignalType.Real, None, 'Gain of input signal 2')
84+
## |-- |-- |-- |-- System: sub-system 'None'
85+
## |-- |-- |-- |-- |-- Connectors:
86+
## |-- |-- |-- |-- |-- |-- (u, Causality.input, SignalType.Real, None, 'None')
87+
## |-- |-- |-- |-- |-- |-- (y, Causality.output, SignalType.Real, None, 'None')
88+
## |-- |-- |-- |-- |-- Elements:
89+
## |-- |-- |-- |-- |-- |-- FMU: Add2 'None'
90+
## |-- |-- |-- |-- |-- |-- |-- path: resources/Add.fmu
91+
## |-- |-- |-- |-- |-- |-- |-- Connectors:
92+
## |-- |-- |-- |-- |-- |-- |-- |-- (u1, Causality.input, SignalType.Real, None, 'Connector of Real input signal 1')
93+
## |-- |-- |-- |-- |-- |-- |-- |-- (u2, Causality.input, SignalType.Real, None, 'Connector of Real input signal 2')
94+
## |-- |-- |-- |-- |-- |-- |-- |-- (y, Causality.output, SignalType.Real, None, 'Connector of Real output signal')
95+
## |-- |-- |-- |-- |-- |-- |-- |-- (k1, Causality.parameter, SignalType.Real, None, 'Gain of input signal 1')
96+
## |-- |-- |-- |-- |-- |-- |-- |-- (k2, Causality.parameter, SignalType.Real, None, 'Gain of input signal 2')
97+
## |-- |-- |-- |-- |-- Connections:
98+
## |-- |-- |-- |-- |-- |-- .u -> Add2.u1
99+
## |-- |-- |-- |-- |-- |-- .u -> Add2.u2
100+
## |-- |-- |-- |-- |-- |-- .y -> Add2.y
101+
## |-- |-- |-- Connections:
102+
## |-- |-- |-- |-- Add1.y -> sub-system.u
103+
## |-- |-- |-- |-- sub-system.y -> Add3.u1
104+
## |-- |-- |-- |-- sub-system.y -> Add3.u2
105+
## |-- DefaultExperiment
106+
## |-- |-- startTime: 0.0
107+
## |-- |-- stopTime: 1.0
108+
## info: default.sub-system.u: 400.0
109+
## info: default.sub-system.Add2.u1: 400.0
110+
## info: default.sub-system.Add2.u2: 400.0
111+
## info: default.sub-system.Add2.y : 800.0
112+
## endResult

0 commit comments

Comments
 (0)