When you use numeric MATLAB® variables to set block parameter values in a model, large models can accumulate many variables, increasing the effort of maintenance and causing the variable names to grow in length.
Instead, you can organize these parameter values into structures. Each structure is a single variable and each field of the structure stores a numeric parameter value. You can assign meaningful names to the structures, substructures, and fields to indicate the purpose of each value.
Use structures to:
Reduce the number of workspace variables that you must maintain.
Avoid name conflicts between workspace variables.
You cannot create two variables that have the same name in the same scope, such as in the base workspace. When you create structures, you must provide each field a name, but multiple structures can each contain a field that uses the same name. Therefore, you can use each structure and substructure as a namespace that prevents the field names from conflicting with each other and with other variable names in the same scope.
Logically group sets of block parameter values. For example, use nested structures to clearly identify the parameter values that each subsystem or referenced model uses.
If you use mask parameters or model arguments to pass parameter values to the components of a system, you can use structures to reduce the number of individual mask parameters or model arguments that you must maintain. Instead of passing multiple variables, you can pass a single structure variable.
For basic information about creating and manipulating MATLAB structures, see Structures. For basic information about setting block parameter values in a model, see Set Block Parameter Values.
To use structures to initialize bus signals, see Specify Initial Conditions for Bus Signals.
This example shows how to create and use a parameter structure in a model.
The example model f14
uses multiple variables
from the base workspace to set block parameter values. For example,
when you open the model, it creates the variables Zw
, Mw
,
and Mq
in the base workspace. To organize these
variables into a single structure variable:
At the command prompt, open the example model.
f14
At the command prompt, create the parameter structure myGains
.
Set the field values by using the values of the target variables.
myGains.Zw = Zw; myGains.Mw = Mw; myGains.Mq = Mq;
In the Model Explorer, on the Model Hierarchy pane,
click Base Workspace. In the Contents pane,
right-click the variable Mq
and select Find
Where Used.
In the Select a system dialog box, click the node f14 and click OK. Click OK when asked about updating the diagram.
In the Contents pane, right-click the row corresponding to the block labeled Gain1 and select Properties. The Gain1 block dialog box opens.
Change the value of the Gain parameter
from Mq
to myGains.Mq
and click OK.
In the Contents pane, right-click the row corresponding to the Transfer Fcn.1 block and select Properties.
Change the value of the Denominator coefficients parameter
from [1,-Mq]
to [1,-myGains.Mq]
and
click OK.
In the Model Hierarchy pane,
click Base Workspace. Use Find Where
Used to locate the blocks that use the variables Mw
and Zw
.
In the block dialog boxes, replace the references to the variable
names according to the table.
Variable Name | Replacement Name |
---|---|
Mw | myGains.Mw |
Zw | myGains.Zw |
Clear the old variables.
clear Zw Mw Mq
Each of the modified block parameters now uses a field of the myGains
structure.
The numeric value of each structure field is equal to the value of
the corresponding variable that you cleared.
You can migrate a model to use a single parameter structure instead of multiple workspace variables. For an example, see Migration to Structure Parameters.
To use a structure or array of structures to organize parameter
values that use a data type other than double
,
you can explicitly specify the type when you create the structure.
When you create the structure, use typed expressions such as single(15.23)
to
specify the field values.
myParams.Gain = single(15.23);
If you want to change the field value later, you must remember
to explicitly specify the type again. If you do not specify the type,
the field value uses the data type double
instead:
myParams.Gain = 15.23;
% The field 'Gain' now uses the data type 'double' instead of 'single'.
To preserve the type specification, you can use subscripted assignment to assign a new value to the field:
% Assign value of type 'single'. myParams.Gain = single(15.23); % Assign new value while retaining type 'single'. myParams.Gain(:) = 11.79;
To match a fixed-point data type, set the field value by using
an fi
(Fixed-Point Designer) object.
A Simulink.Parameter
object allows you to separate the value of a block
parameter from its data type. If you use a parameter object to store a structure or
array of structures, you can create a Simulink.Bus
object to use as the data type of the entire structure.
You can use the bus object and the parameter object to explicitly control:
The data type of each field. When you use this technique, you do not have to remember to use typed expressions or subscripted assignment to set the field values.
The complexity, dimensions, and units of each field.
The minimum and maximum value of each field if the field represents a tunable parameter value.
The shape of the entire structure. The shape of the structure is the number, names, and hierarchy of fields.
The tunability of the structure in the code that you generate from the model.
Create a parameter structure myParams
.
myParams = struct(... 'SubsystemA',struct(... 'Gain',15.23,... 'Offset',89,... 'Init',0.59),... 'SubsystemB',struct(... 'Coeffs',[5.32 7.99],... 'Offset',57,... 'Init1',1.76,... 'Init2',2.76)... );
Use the function Simulink.Bus.createObject
to
create Simulink.Bus
objects that represent the structure
and substructures.
Simulink.Bus.createObject(myParams)
Because myParams
contains two unique substructures,
the function creates three Simulink.Bus
objects:
one named slBus1
to represent the parent structure myParams
,
one named SubsystemA
for the substructure SubsystemA
,
and one named SubsystemB
for the substructure SubsystemB
.
Rename the bus object slBus1
as myParamsType
.
myParamsType = slBus1;
clear slBus1
Store the structure myParams
in a
Simulink.Parameter
object.
myParams = Simulink.Parameter(myParams);
The Value
property of the parameter object contains
the structure.
Set the data type of the parameter object to the bus object myParamsType
.
myParams.DataType = 'Bus: myParamsType';
Open the Bus Editor to view the bus objects.
buseditor
In the Model Hierarchy pane, click the node SubsystemA. In the Contents pane, set the field data types according to the figure.
Optionally, set the field data types for the substructure SubsystemB
.
The parameter object myParams
stores the
parameter structure. The data type of the parameter object is the
bus object myParamsType
. Prior to simulation and
code generation, the parameter object casts the field values to the
data types that you specified in the bus object.
To use one of the fields to set a block parameter value, specify
an expression such as myParams.SubsystemB.Init1
.
To access the field values at the command prompt, use the Value
property
of the parameter object. Because the bus object controls the field
data types, you do not need to use a typed expression to set the field
value.
myParams.Value.SubsystemA.Gain = 12.79;
The bus object strictly controls the field characteristics and
the shape of the structure. For example, if you set the value of the
two-element field myParams.SubsystemB.Coeffs
to
a three-element array, the model generates an error when you set a
block parameter value. To change the dimensions of the field, modify
the element Coeffs
in the bus object SubsystemB
.
To manipulate bus objects after you create them, see Create and Specify Simulink.Bus Objects and Save Simulink.Bus Objects.
Suppose that you use the field myParams.SubsystemA.Gain
to set the value of
the Gain parameter in a Gain block. If you
want the data type of the field to match the data type of the output signal of
the block, you cannot rely on context-sensitive data typing (see Context-Sensitive Data Typing). Consider using a Simulink.AliasType
or a Simulink.NumericType
object to set the data type of the field and
the signal. If you do not use a data type object, you must remember to change
the data type of the field whenever you change the data type of the
signal.
At the command prompt, create a Simulink.AliasType
object
that represents the data type single
.
myType = Simulink.AliasType; myType.BaseType = 'single';
In the Gain block dialog box, on the Signal
Attributes tab, set Output data type to myType
.
At the command prompt, open the Bus Editor.
buseditor
In the Model Hierarchy pane,
select the bus object SubsystemA
. In the Contents pane,
set the data type of the field Gain
to myType
.
Now, both the output signal of the Gain block
and the structure field myParams.SubsystemA.Gain
use
the data type that you specify by using the BaseType
property
of myType
.
For more information about data type objects, see Simulink.AliasType
and Simulink.NumericType
.
To create, modify, and inspect a variable whose value is a structure, you can use the Variable Editor. For more information, see Modify Structure and Array Variables Interactively.
To further organize block parameter values, create a hierarchy of nested structures.
For example, suppose that you create subsystems named SubsystemA
and SubsystemB
in
your model. You use variables such as Offset_SubsystemA
and Offset_SubsystemB
to
set block parameter values in the subsystems.
Gain_SubsystemA = 15.23; Offset_SubsystemA = 89; Init_SubsystemA = 0.59; Coeffs_SubsystemB = [5.32 7.99]; Offset_SubsystemB = 57; Init1_SubsystemB = 1.76; Init2_SubsystemB = 2.76;
Create a parameter structure that contains a substructure for each subsystem. Use the values of the existing variables to set the field values.
myParams = struct(... 'SubsystemA',struct(... 'Gain',Gain_SubsystemA,... 'Offset',Offset_SubsystemA,... 'Init',Init_SubsystemA),... 'SubsystemB',struct(... 'Coeffs',Coeffs_SubsystemB,... 'Offset',Offset_SubsystemB,... 'Init1',Init1_SubsystemB,... 'Init2',Init2_SubsystemB)... );
The single structure variable myParams
contains
all of the parameter information for the blocks in the subsystems.
Because each substructure acts as a namespace, you can define the Offset
field
more than once.
To use the Offset
field from the substructure SubsystemB
as
the value of a block parameter, specify the parameter value in the
block dialog box as the expression myParams.SubsystemB.Offset
.
To organize parameter structures that have similar characteristics, you can create a single variable whose value is an array of structures. This technique helps you to parameterize a model that contains multiple instances of an algorithm, such as a library subsystem or a referenced model that uses model arguments.
Suppose that you create two identical subsystems in a model.
Suppose that the blocks in each subsystem require three numeric values to set parameter values. Create an array of two structures to store the values.
myParams(1).Gain = 15.23; myParams(1).Offset = 89; myParams(1).Init = 0.59; myParams(2).Gain = 11.93; myParams(2).Offset = 57; myParams(2).Init = 2.76;
Each structure in the array stores the three parameter values for one of the subsystems.
To set the value of a block parameter in one of the subsystems,
specify an expression that references a field of one of the structures
in the array. For example, use the expression myParams(2).Init
.
You can also partition an array of structures in a For Each Subsystem block. This technique helps you to organize workspace variables when a model executes an algorithm repeatedly, for example by iterating the algorithm over a vector signal. For an example, see Repeat an Algorithm Using a For Each Subsystem.
If you use model arguments to specify different parameter values across multiple instances of a referenced model, you can use arrays of structures to organize the model argument values. In the referenced model workspace, create a structure variable and configure the model to use the structure as a model argument. Use the fields of the structure to set block parameter values in the model. Then, create an array of structures in the base workspace or a data dictionary to which the parent model or models are linked. In the parent model or models, use each of the structures in the array as the value of the model argument in a Model block. Each structure in the array stores the parameter values for one instance of the referenced model.
The example model sldemo_mdlref_datamngt
contains three instances
(Model blocks) of the masked referenced model
sldemo_mdlref_counter_datamngt
. The base workspace
variables IC1
, IC2
,
Param1
, and Param2
are
Simulink.Parameter
objects whose values are structures. The
parent model uses these variables to set the values of mask parameters on the
Model blocks. Since IC1
is structurally
identical to IC2
, and Param1
to
Param2
, you can combine these four structures into two
arrays of structures.
Open the example parent model.
sldemo_mdlref_datamngt
The model creates the four Simulink.Parameter
objects in the base
workspace.
Open the example referenced model.
sldemo_mdlref_counter_datamngt
The model workspace defines two model arguments, CounterICs
and CounterParams
,
whose values are structures. The blocks in the model use the fields
of these structures to set parameter values.
In the model sldemo_mdlref_datamngt
, open the Model
Data Editor (on the Modeling tab, click
Model Data Editor). In the Model Data Editor,
inspect the Parameters tab.
In the model, click one of the Model blocks.
The Model Data Editor highlights rows that correspond to two mask
parameters on the selected Model block. The block uses
the mask parameters to set the values of the two model arguments defined
by the referenced model,
sldemo_mdlref_counter_datamngt
. Each
Model block uses a different combination of the four
parameter objects from the base workspace to set the argument
values.
In the Model Data Editor Value column, click one
of the cells to begin editing the value of the corresponding mask
parameter (for example, IC1
). Next to the parameter
value, click the action button and select
Open. The property dialog box for the
parameter object opens.
In the property dialog box, next to the Value box, click the action button and select Open Variable Editor.
The Variable Editor shows that the parameter object stores a
structure. The structures in Param2
and
IC2
have the same fields as the structures in
Param1
and IC1
but different
field values.
At the command prompt, combine the four parameter objects into two parameter objects whose values are arrays of structures.
% Create a new parameter object by copying Param1. Param = Param1.copy; % Use the structure in Param2 as the second structure in the new object. Param.Value(2) = Param2.Value; % The value of Param is now an array of two structures. % Delete the old objects Param1 and Param2. clear Param1 Param2 % Create a new parameter object by copying IC1. % Use the structure in IC2 as the second structure in the new object. IC = IC1.copy; IC.Value(2) = IC2.Value; clear IC1 IC2
In the parent model, in the Model Data Editor, use the Value column to replace the values of the mask parameters according to the table
Previous Value | New Value |
---|---|
Param1 | Param(1) |
IC1 | IC(1) |
Param2 | Param(2) |
IC2 | IC(2) |
Each Model block sets the value of the model
argument CounterICs
by using one of the structures
in the array IC
. Similarly, each block sets the
value of CounterParams
by using one of the structures
in Param
.
All of the structures in an array of structures must have the same hierarchy of fields. Each field in the hierarchy must have the same characteristics throughout the array. You can use a parameter object and a bus object to enforce this uniformity among the structures.
To use a parameter object to represent an array of parameter structures, set the value of the object to the array of structures:
% Create array of structures. myParams(1).Gain = 15.23; myParams(1).Offset = 89; myParams(1).Init = 0.59; myParams(2).Gain = 11.93; myParams(2).Offset = 57; myParams(2).Init = 2.76; % Create bus object. Simulink.Bus.createObject(myParams); myParamsType = slBus1; clear slBus1 % Create parameter object and set data type. myParams = Simulink.Parameter(myParams); myParams.DataType = 'Bus: myParamsType';
To use one of the fields to set a block parameter value, specify
an expression such as myParams(2).Offset
.
To access the field values at the command prompt, use the Value
property
of the parameter object.
myParams.Value(2).Offset = 129;
You can use a structure in a Constant block to create a single bus signal that transmits multiple numeric constants. For more information, see Constant. For information about bus signals, see Virtual Bus.
Before you migrate a model to use parameter structures, discover all of the blocks in the target model and in other models that use the variables that you intend to replace.
For example, suppose two blocks in a model use the workspace
variable myVar
. If you create a structure myParams
with
a field myVar
, and set the parameter value in only
one of the blocks to myParams.myVar
, the other
block continues to use the variable myVar
. If you
delete myVar
, the model generates an error because
the remaining block requires the deleted variable.
To discover all of the blocks that use a variable:
Open all models that might use the variable. If the models are in a model reference hierarchy, you can open only the top model.
In the Model Data Editor or in the Model Explorer Contents pane, right-click the variable and select Find Where Used. The Model Explorer displays all of the blocks that use the variable.
You can discover variable usage only in models that are open. Before you migrate to parameter structures, open all models that might use the target variables. For more information about determining variable usage in a model, see Finding Blocks That Use a Specific Variable.
Alternatively, you can refrain from deleting myVar
.
However, if you change the value of the myParams.myVar
structure
field, you must remember to change the value of myVar
to
match.
You can combine multiple separate variables or parameter objects (such as
Simulink.Parameter
) into a structure that you store
in a single variable or parameter object (to combine parameter objects,
see Combine Existing Parameter Objects Into a Structure).
However, the resulting variable or object acts as a single entity. As a
result, you cannot apply different code generation settings, such as
storage classes, to individual fields in the structure.
When you use parameter objects to set block parameter values (for example, so you can apply storage classes), to combine the objects into a single structure:
Create a MATLAB structure and store it in a variable. To set the field values, use the parameter values that each existing parameter object stores.
Convert the variable to a parameter object. Create
and use a Simulink.Bus
object as the data type of
the parameter object (see Control Field Data Types and Characteristics by Creating Parameter Object).
Choose a storage class to apply to the resulting parameter object. You can choose only one storage class, which applies to the entire structure.
Transfer parameter metadata, such as the Min
and Max
properties
of the existing parameter objects, to the corresponding properties
of the Simulink.BusElement
objects in the bus object.
For example, suppose you have three individual parameter objects.
coeff = Simulink.Parameter(17.5); coeff.Min = 14.33; coeff.DataType = 'single'; coeff.StorageClass = 'ExportedGlobal'; init = Simulink.Parameter(0.00938); init.Min = -0.005; init.Max = 0.103; init.DataType = 'single'; init.StorageClass = 'Model default'; offset = Simulink.Parameter(199); offset.DataType = 'uint8'; offset.StorageClass = 'ExportedGlobal';
Create a structure variable.
myParams.coeff = coeff.Value; myParams.init = init.Value; myParams.offset = offset.Value;
Convert the variable to a parameter object.
myParams = Simulink.Parameter(myParams);
Create a bus object and use it as the data type of the parameter object.
Simulink.Bus.createObject(myParams.Value);
paramsDT = copy(slBus1);
myParams.DataType = 'Bus: paramsDT';
Transfer metadata from the old parameter objects to the bus elements in the bus object.
% coeff paramsDT.Elements(1).Min = coeff.Min; paramsDT.Elements(1).DataType = coeff.DataType; % init paramsDT.Elements(2).Min = init.Min; paramsDT.Elements(2).Max = init.Max; paramsDT.Elements(2).DataType = init.DataType; % offset paramsDT.Elements(3).DataType = offset.DataType;
To help you write a script that performs this transfer operation,
you can use the properties
function
to find the properties that the bus elements and the old parameter
objects have in common. To list the structure fields so that you can
iterate over them, use the fieldnames
function.
Apply a storage class to the parameter object.
myParams.StorageClass = 'ExportedGlobal';
Now, you can use the fields of myParams
, instead
of the old parameter objects, to set the block parameter values.
You can configure parameter structures to appear in the generated code as structures and arrays of structures. For information about generating code with parameter structures, see Organize Data into Structures in Generated Code (Simulink Coder).
The value of a field that you use to set a block parameter must be numeric or of an enumerated type. The value of a field can be a real or complex scalar, vector, or multidimensional array.
If the value of any of the fields of a structure is a multidimensional array, you cannot tune any of the field values during simulation.
All of the structures in an array of structures must have the same hierarchy of fields. Each field in the hierarchy must have the same characteristics throughout the array:
Field name
Numeric data type, such as single
or int32
Complexity
Dimensions
Suppose that you define an array of two structures.
paramStructArray = ... [struct('sensor1',int16(7),'sensor2',single(9.23)) ... struct('sensor1',int32(9),'sensor2',single(11.71))];
You cannot use any of the fields in a block parameter because
the field sensor1
uses a different data type in
each structure.
Parameter structures do not support context-sensitive
data typing in the generated code. If the parameter structure is tunable
in the code, the fields of the structure use the numeric data types
that you specify by using either typed expressions or a Simulink.Bus
object.
If you do not use typed expressions or a Simulink.Bus
object,
the fields of the structure use the double
data
type.
When you share data between lookup table blocks, consider using Simulink.LookupTable
and Simulink.Breakpoint
objects
instead of structures to store and group the data. This technique
improves model readability by clearly identifying the data as parts
of a lookup table and explicitly associating breakpoint data with
table data. See Package Shared Breakpoint and Table Data for Lookup Tables.
You can create a parameter structure that conforms to a struct
type
definition that your existing C code defines. Use this technique to:
Replace existing C code with a Simulink® model.
Integrate existing C code for simulation in Simulink (for example, by using the Legacy Code Tool). For an example, see Integrate C Function Whose Arguments Are Pointers to Structures.
Generate C code (Simulink Coder™) that you can compile with existing C code into a single application. For an example, see Exchange Structured and Enumerated Data Between Generated and External Code (Embedded Coder).
In MATLAB, store the parameter structure in a parameter
object and use a bus object as the data type (see Control Field Data Types and Characteristics by Creating Parameter Object).
To create the bus object according to your C-code struct
type,
use the Simulink.importExternalCTypes
function.