A sample project: Model Unit Validation

With this post I’m going to walk through a sample project demonstrating how Units in Simulink Models can be validated. Demonstrating each stage of the project development, providing insight into how and what I was thinking at each step along the way.

Stages of project development

There are 5 stages to project development

  1. Identification of need: What is the problem you are trying to solve?
  2. Review of existing solutions: Is there an existing solution that meets needs?
  3. Task identification/estimation: What are the tasks / steps that need to be completed and what is the effort (risk) to complete them?
  4. Implementation and testing: Following a ‘test as you go’ strategy develop the modules that you need
  5. Validation of solution: Does what you did in 4 solve what you needed in 1?

Stage 1: Identification of need

As of release 2019A Simulink provides

  • Unit specification at the boundaries and through Simulink.Signal and Simulink.Parameter objects
  • Unit checking at the boundaries of models (inports and outports)

While this is very useful (and you should always define your boundary conditions) for large models knowing what the units of interior signals would be beneficial.

Problem to be solved: determine the unit of all blocks internal to the model and use that information to validate the units at the boundaries / internal to the model.
†

Unit checking at the Subsystem Boundary

What I wanted to be able to do was specify the unit types on the inports and outports and propagate the units through the model.

At this stage, depending on the complexity of the project, the high level requirements should be written; derived requirements will be written at the start of task 3.

Stage 2: Review of existing solutions

There are two classes of “existing solutions”. The first are solutions to “base” problem you are trying to solve; the second are solutions to the sub-tasks in the problem. In this instance we have already identified the extent of the solution to the “base” problem, the ability to check units at the boundaries of the model; for what we want this is insufficient.

Examples of “Sub-Tasks”

For the sub-tasks the Simulink API’s provide the interface required to “decode” the model to propagate the information through the model.

Stage 3: Task identification/estimation

Depending on the size of the project task identification can be decomposed to a “function” based level or lower. For large projects the “function” may in fact be a collection of functions. As you start to identify the functions required reuse of the components should be taken into account. My first pass (almost always on paper) is in a “operation required / issues” format.

  1. Propagate unit information in the model:
    1. How to determine data/unit flow
    2. How to handle data merging blocks (buses and mux blocks)
    3. How to handle subsystems
  2. Calculate output units based on inputs:
    1. Define categories of block transformations
      • Matching
      • Canceling
      • Integration / derivative
      • Null
    2. Handle effects of Parameter data types
  3. Apply information to the blocks
    1. Units (note most blocks don’t have a “unit” field
    2. Status of block (assigned, unsigned, invalid)
  4. Check calculated units against assigned
    1. Outports
    2. Simulink Signal objects

Having identified the tasks I now think about what goes into each step and if I have experience or near experience in coding it; in this case all of the tasks involved are close to things I have done before so I have faith in my estimates…. (For the record, I estimated 8 hours and it ended up taking me 7)

Step 4: Implementation and Testing

Step 4.1: Unit propagation

My first question was how to propagate the units through the model. I decided that a first, reasonable requirement was that all of the inports must have units defined. If I enforced that I could simple “walk forward” from the inports to the outport of the system.

The once that approach was selected it the implementation becomes clear; create an array of inports and trace outwards from them. As each new block is found tack it onto the end of the array removing the most recent from the list.

(Note: Since this is a demo project I am not, currently, doing anything with Mux or Bus blocks).

Step 4.2: Calculating units

The key to calculating the units is to realize that there are really only 3 fundmental operations.

  • Cancellation: through either multiplication or division units are canceled.
  • Matching: the operation (addition or subtraction) requires the input units to match
  • Integration / derivation: a “cancellation” of time.

As you look at the code you will see that I have created “num” and “den” variables, these are for the numerator and denominators of the inputs. Additional, for the sake of a demo, I have restricted the number of block inputs to 2.

Notes:

  • In hindsight I didn’t actually need to find the numerator and denominator since much of what happens is through string clean up. However, conceptually, it was a required task).
  • In this example I am not handling feedback loops through unit delays, however they could be treated as a special case of a “sum” or matching required block.

As I developed the code I also wrote some basic test points to validate that the behavior was correct. The most basic tests were to determine if the units, stored as strings by Simulink, could be converted to the num/den strings

In this case you can see that I tried to cover all of the ways in which data could be encoded. One thing to note, I did not implement “compound units”. E.g. if you put in N (for Newtons) I do not cancel that in the same way you would kg*m/s^2. To do that I would, I think, first expand all the compound units to their fundamental components and then cancel.

Cancellation at the root

The final step in this process will be to validate the units at the outports. To indicate passing / failing outports I color code them “green” for pass and “red” for failed.

Step 5: Validation of the solution

The code, as implemented, provides a “taste” of the final solution. However, the way in which it was code is modular so as I want to add in new capabilities (support for additional blocks, muxs, buses, subsystems) the extensions will be easy to add. If you are interested I have included a copy of the code and sample model here.