Arrays of Component

Several of the examples in this chapter used arrays of components. Arrays of components are useful when the user might want to “scale” the number of components used using a parameter (as we saw in our discussions of both Spatially Distributed Heat Transfer and Harmonic Motion of Pendulums).

Creating an array of components is really no different from creating an array of scalar variables like we did in our previous discussion on Vectors and Arrays. As we can see in this example,

  HTC.HeatCapacitor capacitance[n](each final C=C/n, each T(start=T0, fixed=true))
    annotation ...
  HTC.ThermalConductor wall[n](each final G=G_wall/n)
    annotation ...
  HTC.ThermalConductor rod_conduction[n-1](each final G=G_rod*(n-1))
    annotation ...

the syntax for creating an array of components is identical to the syntax used for other types. All that is required is to follow the name of the variable being declared by a set of dimensions.

However, unlike scalars, components have other declarations inside them. So whenever an array of components is created, the structure of that component is replicated for each component in the array. Modelica imposes a restriction that in every array, every element must be the same type. This may seem obvious, but that is partly because we haven’t discussed replaceable components yet. We’ll learn more about replaceable components in the next chapter when we talk about Modelica’s Configuration Management features. But for now we will simply point out that it is not possible to redeclare only one element in an array.

As we touched on briefly in our discussion of Harmonic Motion of Pendulums, when we make Modifications to arrays of components, each variable inside the component is implicitly treated as an array. For example, consider the following record definition:

record Point
  Real x;
  Real y;
  Real z;
end Point;

If we were to declare an array of Point components, e.g., Point p[5], any reference to p.x is treated as an array of 5 Real variables, i.e., p[1].x, p[2].x, p[3].x, p[4].x and p[5].x. This is called slicing. The bottom line is that if we leave off a subscript (e.g., p.x), it gets “pushed to the end” (or more technically, it is left “unbound” and can be “bound” later). Also, if a subscript is supplied as a range (e.g., :, 1:end, 2:3), then the expression resolves to a subset of the array corresponding to the indices in the range. All of this holds even for arrays of components containing arrays of components and so on.

The following example, demonstrates some of the more common cases:

record Vector3D
  Real x[3];
end Vector3D;

model ArrayExample
  Point p[2];
  Point q[2,3];
  Vector3D v[4];
  p.x = {1, 2}; // p[1].x = 1, p[2].x = 2
  q[:,3].y = {4, 5}; // q[1,3].y = 4, q[2, 3].y = 5;
  q.x = [1, 2, 3; 4, 5, 6] // q[1,1].x = 1,
                           // q[1,2].x = 2,
                           // q[2,3].x = 6
  v.x[1] = {1, 2, 3, 4}; // v[1].x[1] = 1, v[2].x[1] = 2,
                         // v[3].x[1] = 3, v[4].x[1] = 4
  v[:].x[1] = {1, 2, 3, 4}; // v[1].x[1] = 1, v[2].x[1] = 2,
                            // v[3].x[1] = 3, v[4].x[1] = 4
  v[2:3].x[1] = {2, 3}; // v[2].x[1] = 2, v[3].x[1] = 3
  v[1].x = {1, 2, 3};   // v[1].x[1] = 1, v[1].x[2] = 2,
                        // v[1].x[3] = 3
end ArrayExample;