Here is a minimum working example of some expandable connector usage occuring in my models:
model TestExpandableConnector
expandable connector ControlBus
extends Modelica.Icons.SignalBus;
Real signal1;
Real signal2;
end ControlBus;
ControlBus controlBus;
// example models to connect signals to
Modelica.Blocks.Math.Gain gain1;
Modelica.Blocks.Math.Gain gain2;
// and so on
equation
connect(controlBus.signal1, gain1.u);
connect(controlBus.signal2, gain2.u);
// and so on
end TestExpandableConnector;
This works fine and no concerns here.
Note that normally this model would be created in the diagram layer with graphical objects and connections between the bus and the components (gains in this case).
While the above example is trivial, in many real world examples I have many connections emerging from that one expandable connector. This quickly can become messy in the diagram layer and I'm trying to learn/develop some best practices here for cleaning up the diagrams.
One option seems to be to use the RealExpression block in a way almost equivalent to Simulink's From/Goto elements. For example:
model TestExpandableConnectorRevised
expandable connector ControlBus
extends Modelica.Icons.SignalBus;
Real signal1;
Real signal2;
end ControlBus;
ControlBus controlBus;
// example models to connect signals to
Modelica.Blocks.Math.Gain gain1;
Modelica.Blocks.Math.Gain gain2;
// and so on
// using RealEpressions like goto tags
Modelica.Blocks.Sources.RealExpression realExpression1(y=controlBus.signal1);
Modelica.Blocks.Sources.RealExpression realExpression2(y=controlBus.signal2);
// and so on
equation
connect(realExpression1.y, gain1.u);
connect(realExpression2.y, gain2.u);
// and so on
end TestExpandableConnectorRevised;
Now with this change, Dymola complains about this being illegal since causality cannot be determined. I seem to be able to resolve this last issue by either 1) adding the "input" prefix to the signal1 and signal2 declarations in the bus, or 2) positioning the declaration for the realExpressions before the contolBus declaration (this 2nd solution is a bit odd to me).
Overall, I'm reasonably happy with these solutions from a decluttering my diagram point of view, but they also feel at least a little bit "hacky". My basic goal in this question is to inquire if this approach is OK or if it's a bad idea? Additionally, if there are any other suggestions regarding how to handle the organization of all the connections in a big model (especially with expandable connectors), I'm all ears. As an additional thought, it seems to a me that a more dedicated "From/Goto" feature for the Modelica language might be really nice in Modelica, purely for the purpose of decluttering diagrams but being exactly equivalent to a connect statement under the hood.
Dymola complains that
The variable controlBus.signal1 is part of an expandable connector, and was only used outside of connect. That is not legal since we cannot determine its causality.
Your revised solution works, as soon as you write the signal somewhere using a connect
statement. Below I further reduced your example to contain only signal1
. An additional real expression is used to set its value.
model TestExpandableConnectorRevised
expandable connector ControlBus
Real signal1;
end ControlBus;
ControlBus controlBus;
Modelica.Blocks.Math.Gain gain1;
Modelica.Blocks.Sources.RealExpression realExpression1(y=controlBus.signal1);
// Added to write the bus signal
Modelica.Blocks.Sources.RealExpression realExpression3(y=1);
equation
connect(realExpression1.y, gain1.u);
// Added to write the bus signal
connect(realExpression3.y, controlBus.signal1);
end TestExpandableConnectorRevised;
This example compiles in Dymola pedantic mode and OpenModelica, so it should be perfectly fine.
As you see, expandable connectors are full of pitfalls. The problem above can also happen easily if you decide to rename signal1
to mysignal
on the expandable connector, but forget to update the connect statement to connect(realExpression3.y, controlBus.mysignal)
.
Therefore some Modelica libraries decided read and write bus signals only via bus adapters. You have to create 2 additional blocks for every variable: one to read and one to write its value. This is a lot of boring work, but it avoids the problem above.
Here is a minimal example to read and write signal1
.
package BusAdapters
partial block BusWriter
// Dialog allows to set the value of y in the parameter window, like for the real expression
Modelica.Blocks.Interfaces.RealInput u annotation (Dialog);
ControlBus controlBus;
end BusWriter;
block Write_signal1
extends BusWriter;
equation
connect(u, controlBus.signal1);
end Write_signal1;
partial block BusReader
Modelica.Blocks.Interfaces.RealOutput y;
ControlBus controlBus;
end BusReader;
block Read_signal1
extends BusReader;
equation
connect(y, controlBus.signal1);
end Read_signal1;
expandable connector ControlBus
extends Modelica.Icons.SignalBus;
Real signal1;
end ControlBus;
model TestBusConnectors
ControlBus controlBus;
Modelica.Blocks.Math.Gain gain1;
// setting bus variables: using modifiers in write blocks
Write_signal1 write1(u=sin(time));
// accessing bus variables part 1: creating instance of reader
Read_signal1 read1;
equation
// connect all read and write blocks to the same bus instance
connect(write1.controlBus, controlBus);
connect(read1.controlBus, controlBus);
// accessing bus variables part 2: connecting reader with component of interest
connect(read1.y, gain1.u);
end TestBusConnectors;
end BusAdapters;
Graphically this will look something like below. x
is written directly using the bus adapters. For y
real expressions are used, to reduce the number of lines in larger models.