The emphasis of this chapter was on how component models could be organized into reusable subsystems. As we saw in numerous examples in this chapter, there is a common pattern that emerges. Let’s review various aspects of subsystem models to understand this pattern better.
Generally, parameters for a subsystem should be public
. Normally,
all declarations in any model
or block
definition will be
public unless they come after the protected
keyword. In such
cases, it is still possible to declare something as public by adding
the public
keyword and placing the declarations after it. In
other words:
model PublicAndProtected
Real x; // This is public (because that is the default)
protected
Real y1; // This is protected
Real y2; // This is *also* protected
public
Real z1; // This is public
Real z2; // This is *also* public
equation
// ...
end PublicAndProtected;
In the examples from this chapter, it was common to see the parameters
either at the start of the definition (where they are public
by
default) or in a collection of declarations explicitly marked public
.
Obviously, parameters are made public so that users of the subsystem can have access to them. We will see shortly, in our discussions on Modifications and Propagation, how the values of these parameters should be cascaded to lower level components. But for now, the main point is to recognize that parameter declarations are part of the subsystem pattern.
In some sense, connectors are what differentiates a subsystem from a
system model. A system model is something that is complete and ready
to simulate and, as such, has no connectors because it does not expect
to be influenced by anything external. A subsystem can encapsulate a
large hierarchy of components (and ultimately, equations). But the
fact that it includes connectors indicates that it is really meant to
be used as part of some larger system. Furthermore, as we have seen
in the examples of this chapter, because these connectors are meant to
be connected to, they should be public
.
Because subsystems are typically composed exclusively of components or
other subsystems, any physical interaction with that subsystem is
usually redirected to some nested component or subsystem. We’ve seen
this pattern many times in this chapter where connectors at the
subsystem boundary really act as “proxies” for internal connectors. A
simple example of this can be seen in the GearWithBacklash
model:
connect(flange_a, inertia_a.flange_a)
Note that this connect
statement connects flange_a
, a
connector instance that belongs to the subsystem, with
inertia_a.flange_a
, a connector instance belonging to the
subcomponent inertia_a
. This is a common pattern in subsystem
models and it can be easily recognized because one of the connectors
named in the connect
statement includes a “.
” and the other
one does not. All the “internal” connections that wire together
internal components directly to other internal components will have a
“.
” for both of the named connectors in the connect
statement,
e.g.,
connect(idealGear.flange_b, inertia_b.flange_a)
In this book, the point has been made repeatedly (e.g., in our
discussion of Acausal Connections) that the sign convention for
flow
variables is such that a positive value for the flow
variable represents a flow into the component or subsystem. At the
same time, we also pointed out that Connections generate
equations that sum all the corresponding flow
variables in the
connection set to zero.
But, when dealing with hierarchical subsystem definitions there is a
modification to this rule. For a subsystem, the sign convention for
flow
variables remains intact. So, a positive value for the
flow
variable on a connector still represents a flow of a
conserved quantity into that component. And it is still the case that
a conservation equation will be generated for each flow
variable
in a connection set. However, in that conservation equation, the sign
for flow
variables on connectors belonging to internal components
will have the opposite sign as flow
variables on connectors
belonging to the subsystem itself.
To understand the implications of this, let us consider the following
two connect
statements from the GearWithBacklash
model:
connect(flange_a, inertia_a.flange_a)
annotation ...
connect(idealGear.flange_b, inertia_b.flange_a)
annotation ...
From these equations, we get the following two connection sets:
- Connection Set #1:
flange_a
,inertia_a.flange_a
- Connection Set #2:
idealGear.flange_b
,inertia_b.flange_a
In each of these connection sets, there is a flow
variable,
tau
. Using the rules for Connections described
previously, we might expect the following two equations to be
generated for the flow
variables on these connectors:
flange_a.tau + inertia_a.flange_a.tau = 0;
idealGear.flange_b.tau + inertia_b.flange_a = 0;
However, in our previous discussion on Connections, all
the components were at the same hierarchical level (i.e., as
idealGear.flange_b
and inertia_b.flange_a
are). But with
subsystem models, this isn’t always the case. And, as described a
moment ago, for cases where the connections are at different levels,
we need to introduce a sign difference for contributions at different
levels. Taking that into account, the actual equations that will be
generated will be:
-flange_a.tau + inertia_a.flange_a.tau = 0;
idealGear.flange_b.tau + inertia_b.flange_a = 0;
Note the minus sign in front of the flange_a.tau
. Remember that
flange_a
is meant to be acting as a proxy for
inertia_a.flange_a
. If that is the case, then by changing the
sign of flange_a.tau
, the first equation above can be transformed
into:
inertia_a.flange_a.tau = flange_a.tau;
In other words, by introducing the sign change on flange_a.tau
,
any conserved quantity the flows in through flange_a
also
flows into inertia_a.flange_a
, which is exactly what we expect from
this kind of “proxy” relationship.
A subsystem model is typically just a wrapper around a collection of
components. As we’ve discussed already in this section, the
parameters and connectors exposed by the subsystem are public
because that is how users of the subsystem will interact with it.
The actual internal details of the subsystem represent the implementation of the subsystem. This implementation is generally a collection of components and other subsystems that are connected together with one or more of the their connectors connected to the subsystem’s connectors.
Normally, these implementation details are best hidden from the end
user of the subsystem. To accomplish this, all non-parameter
declarations in a subsystem are typically marked as protected
.
There are two main reasons for doing this. First, it hides
implementation details from the users of the subsystem model. This
has the effect of simplifying the interface down to just parameters
and connectors and avoids mixing things the user really needs to know
with things that they do not need to know (or even should not know).
Another reason to make the implementation details protected
is to
provide the flexibility to improve or refactor the implementation in
the future. If users are allowed to reference the implementation
details, that means they will then (perhaps even unintentionally)
become dependent on them. As a result, if they change in the
future it will break the end user’s models.