Subsystem Interface

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.

Parameters

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.

Connectors

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.

Hierarchical Connections

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)

Equation Generation for Hierarchical Connections

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 (Line(points={{-80,0},{-100,0}},
      color={0,0,0}, smooth=Smooth.None));
  connect(idealGear.flange_b, inertia_b.flange_a)
    annotation (Line(points={{10,0},{40,0}},
      color={0,0,0}, smooth=Smooth.None));

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.

Implementation

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.