Defining Component Variants
Physical modeling often requires incremental modeling approach. It is a good practice to start with a simple model, run and troubleshoot it, then add the desired special effects, like fluid compressibility or fluid inertia. Another example is modeling a diode with different levels of complexity: linear, zener diode, or exponential. Composite components often require conditional inclusion of a certain member component and a flexible connection scheme.
Including different modeling variants within a single component requires applying control logic to determine the model configuration. You achieve this goal by using conditional sections in a component file.
Conditional Sections
A conditional section is a top-level section guarded by an if
clause.
Conditional sections are parallel to other top-level sections of a
component file, such as declaration or equations sections.
A conditional section starts with an if
keyword
and ends with an end
keyword. It can have optional elseif
and else
branches.
The body of each branch of a conditional section can contain declaration
blocks, equations, structure sections, and so on, but cannot contain
the setup
function.
The if
and elseif
branches
start with a predicate expression. If a predicate is true, the branch
gets activated. When all predicates are false, the else
branch
(if present) gets activated. The compiled model includes elements
(such as declarations, equations, and so on) from active branches
only.
component MyComp
[...]
if Predicate1
[...] % body of branch1
elseif Predicate2
[...] % body of branch2
else
[...] % body of branch3
end
[...]
end
Unlike the if
statements in the equations
section, different branches of a conditional section can have different
variables, different number of equations, and so on. For example,
you can have two variants of a pipe, one that accounts for resistive
properties only and the second that also models fluid compressibility:
component MyPipe
parameters
fl_c = 0; % Model compressibility? (0 - no, 1 - yes)
end
[...] % other parameters, variables, branches
if fl_c == 0
equations
% first set of equations, resistive properties only
end
else
variables
% additional variable declarations, needed to account for fluid compressibility
end
equations
% second set of equations, including fluid compressibility
end
end
end
In this example, if the block parameter Model compressibility?
(0 - no, 1 - yes) is set to 0, the first set of equations
gets activated and the block models only the resistive properties
of the pipe. If the block user changes the value of the parameter,
then the else
branch gets activated, and the compiled
model includes the additional variables and equations that account
for fluid compressibility.
Note
Enumerations are very useful in defining component variants, because they let you specify a discrete set of acceptable parameter values. For an example of how this component can use enumeration, see Using Enumeration in Predicates.
Rules and Restrictions
Nested conditional sections are allowed. For example:
component A
parameters
p1 = 0;
p2 = 0;
p3 = 0;
end
if p1 > 0
[...]
if p2 > 0
[...]
end
if p3 > 0
[...]
end
[...]
end
end
Predicates must be parametric expressions, because the structure of a model must be fixed at compile time and cannot change once the model is compiled. Using a variable in a predicate results in a compile-time error:
component A
[...]
variables
v = 0;
end
if v > 0 % error: v>0 is not a parametric expression because v is a variable
[...]
else
[...]
end
end
Predicates may depend on parameters of the parent (enclosing) component. They may not depend, directly or indirectly, on parameters of member (embedded) components or on domain parameters:
component A
parameters
p = 1;
end
parameters(Access=private)
pp = c.p;
end
components
c = MyComp;
end
nodes
n = MyDomain;
end
if p > 0 % ok
[...]
elseif c.p > 0 % error: may not depend on parameters of embedded component
[...]
elseif n.p > 0 % error: may not depend on domain parameters
[...]
elseif pp > 0 % error: pp depends on c.p
[...]
end
end
Accessibility of class members declared inside conditional sections
is equivalent to private class members (Access=private)
.
They are not accessible from outside the component class, even if
their branch is active.
The scope of the class members declared inside a conditional section is the entire component class. For example:
component A
nodes
p = foundation.electrical.electrical;
n = foundation.electrical.electrical;
end
parameters
p1 = 1;
end
if p1 > 0
components
r1 = MyComponentVariant1;
end
else
components
r1 = MyComponentVariant2;
end
end
connections
connect(p, r1.p);
connect(n, r1.n);
end
end
However, using a conditional member outside the conditional section when the branch is not active results in a compilation error:
component A
nodes
p = foundation.electrical.electrical;
n = foundation.electrical.electrical;
end
parameters
p1 = 0;
end
if p1 > 0
components
r1 = MyComponentVariant1;
end
end
connections
connect(p, r1.p); % error if p1=0 and the predicate is false
end
end
Parameters that are referenced by predicates of conditional
sections, directly and indirectly, must be compile-time parameters.
The setup
function may not write to these parameters,
for example:
component A
parameters
p1 = 1;
end
if p1 > 0 % p1 is a compile-time parameter
[...]
else
[...]
end
function setup
tmp = p1; % ok to read from p1
p1 = 10; % error: may not write to p1 here
end
end
Example
This simple example shows a component containing two resistors. The resistors can be connected either in series or in parallel, depending on the value of the control parameter:
component TwoResistors
nodes
p = foundation.electrical.electrical; % +:left
n = foundation.electrical.electrical; % -:right
end
parameters
p1 = {1, 'Ohm'}; % Resistor 1
p2 = {1, 'Ohm'}; % Resistor 2
ct = 0; % Connection type (0 - series, 1 - parallel)
end
components(ExternalAccess=observe)
r1 = foundation.electrical.elements.resistor(R=p1);
r2 = foundation.electrical.elements.resistor(R=p2);
end
if ct == 0 % linear connection
connections
connect(p, r1.p);
connect(r1.n, r2.p);
connect(r2.n, n);
end
else % parallel connection
connections
connect(r1.p, r2.p, p);
connect(r1.n, r2.n, n);
end
end
end
To test the correct behavior of the conditional section, point a Simscape Component block to this component file. Place the block in a circuit with a 10V DC voltage source and a current sensor. With the default parameter values, the resistors are connected in series, and the current is 5A.
If you change the value of the Connection type (0
- series, 1 - parallel) parameter to 1
,
the resistors are connected in parallel, and the current is 20A.