Main Content

Using Intermediate Terms in Equations

Why Use Intermediate Terms?

Textbooks often define certain equation terms in separate equations, and then substitute these intermediate equations into the main one. For example, for fully developed flow in ducts, the Darcy friction factor can be used to compute pressure loss:

P=f·L·ρ·V22D

where P is pressure, f is the Darcy friction factor, L is length, ρ is density, V is flow velocity, and D is hydraulic area.

These terms are further defined by:

f=0.316Re1/4

Re=D·Vν

D=4Aπ

V=qA

where Re is the Reynolds number, A is the area, q is volumetric flow rate, and ν is the kinematic viscosity.

In Simscape™ language, there are two ways that you can define intermediate terms for use in equations:

  • intermediates section — Declare reusable named intermediate terms in the intermediates section in a component or domain file. You can reuse these intermediate terms in any equations section within the same component file, in an enclosing composite component file, or in any component that has nodes of that domain type.

  • let expressions in the equations section — Declare intermediate terms in the declaration clause and use them in the expression clause of the same let expression. Use this method if you need to define intermediate terms of limited scope, for use in a single group of equations. This way, the declarations and equations are close together, which improves code readability.

Another advantage of using named intermediate terms instead of let expressions is that you can include named intermediate terms in simulation data logs.

The following example shows the same Darcy-Weisbach equation with intermediate terms written out in Simscape language:

component MyComponent
  [...]
  parameters
    L   = { 1,    'm' };      % Length
    rho = { 1e3,  'kg/m^3' }; % Density
    nu  = { 1e-6, 'm^2/s' };  % Kinematic viscosity
  end
  variables
    p    = { 0, 'Pa' };       % Pressure
    q    = { 0, 'm^3/s' };    % Volumetric flow rate
    A    = { 0, 'm^2' };      % Area
  end
  intermediates
    f    = 0.316 / Re_d^0.25;    % Darcy friction factor
    Re_d = D_h * V / nu;         % Reynolds number
    D_h  = sqrt( 4.0 * A / pi ); % Hydraulic diameter
    V    = q / A;                % Flow velocity
  end
  equations
      p == f * L * rho * V^2 / (2 * D_h); % final equation
    end
  end
end

After substitution of all intermediate terms, the final equation becomes:

p==0.316/(sqrt(4.0 * A / pi) * q / A / nu)^0.25 * L * rho * (q / A)^2 / (2 * sqrt(4.0 * A / pi));

When you use this component in a model and log simulation data, the logs will include data for the four intermediate terms, with their descriptive names (such as Darcy friction factor) shown in the Simscape Results Explorer.

Declaring and Using Named Intermediate Terms

The intermediates section in a component file lets you define named intermediate terms for use in equations. Think of named intermediate terms as of defining an alias for an expression. You can reuse it in any equations section within the same file or an enclosing composite component. When an intermediate term is used in an equation, it is ultimately substituted with the expression that it refers to.

You can also include an intermediates section in a domain file and reuse these intermediate terms in any component that has nodes of that domain type.

Syntax Rules and Restrictions

You declare an intermediate term by assigning a unique identifier on the left-hand side of the equal sign (=) to an expression on the right-hand side of the equal sign.

The expression on the right-hand side of the equal sign:

  • Can refer to other intermediate terms. For example, in the Darcy-Weisbach equation, the identifier Re_d (Reynolds number) is used in the expression declaring the identifier f (Darcy friction factor). The only requirement is that these references are acyclic.

  • Can refer to parameters, variables, inputs, outputs, member components and their parameters, variables, inputs, and outputs, as well as Across variables of domains used by the component nodes.

  • Cannot refer to Through variables of domains used by the component nodes.

You can use intermediate terms in equations, as described in Use in Equations. However, you cannot access intermediate terms in the setup function.

Intermediate terms can appear in simulation data logs and Simscape Results Explorer, as described in Data Logging. However, intermediate terms do not appear in:

  • Variable Viewer

  • Statistics Viewer

  • Operating Point data

  • Block dialog boxes and Property Inspector

Use in Equations

After declaring an intermediate term, you can refer to it by its identifier anywhere in the equations section of the same component. For example:

component A
  [...]
  parameters
    p1 = { 1, 'm' };   
  end
  variables
    v1 = { 0, 'm' };  
    v2 = { 0, 'm^2' };   
  end
  intermediates
    int_expr = v1^2 * pi / p1;             
  end
  equations
      v2 == v1^2 + int_expr; 
  end
end

You can refer to a public intermediate term declared in a member component in the equations of an enclosing composite component. For example:

component B
  [...]
  components
    comp1 = MyNamespace.A;   
  end
  variables
    v1 = { 0, 'm^2' };     
  end
  [...]
  equations
      v1 == comp1.int_expr; 
  end
end

Similarly, you can refer to an intermediate term declared in a domain in the equations section of any component that has nodes of this domain type. For example:

domain D
  [...]
  intermediates
    int_expr = v1 / sqrt(2);             
  end
  [...]
end
component C
  [...]
  nodes
    n = D;   
  end
  variables
    v1 = { 0, 'V' };     
  end
  [...]
  equations
      v1 == n.int_expr; 
  end
end

Accessibility of intermediate terms outside of the file where they are declared is governed by their Access attribute value. For mode information, see Attribute Lists.

Data Logging

Intermediate terms with ExternalAccess attribute values of modify or observe are included in simulation data logs. For mode information, see Attribute Lists.

If you specify a descriptive name for an intermediate term, this name appears in the status panel of the Simscape Results Explorer.

For example, you declare the intermediate term D_h (hydraulic diameter) as a function of the orifice area:

component E
  [...]
  intermediates
    D_h  = sqrt( 4.0 * A / pi ); % Hydraulic diameter
  end
  [...]
end

When you use a block based on this component in a model and log simulation data, selecting D_h in the Simscape Results Explorer tree on the left displays a plot of the values of the hydraulic diameter over time in the right pane and the name Hydraulic diameter in the status panel at the bottom. For more information, see Simscape Results Explorer.

Using the let Expressions

let expressions provide another way to define intermediate terms for use in one or more equations. Use this method if you need to define intermediate terms of limited scope, for use in a single group of equations. This way, the declarations and equations are close together, which improves file readability.

The following example shows the same Darcy-Weisbach equation as in the beginning of this topic but with intermediate terms written out using the let expression:

component MyComponent
  [...]
  parameters
    L   = { 1,    'm' };      % Length
    rho = { 1e3,  'kg/m^3' }; % Density
    nu  = { 1e-6, 'm^2/s' };  % Kinematic viscosity
  end
  variables
    p    = { 0, 'Pa' };       % Pressure
    q    = { 0, 'm^3/s' };    % Volumetric flow rate
    A    = { 0, 'm^2' };      % Area
  end
  equations
    let
      f    = 0.316 / Re_d^0.25;    % Darcy friction factor
      Re_d = D_h * V / nu;         % Reynolds number
      D_h  = sqrt( 4.0 * A / pi ); % Hydraulic diameter
      V    = q / A;                % Flow velocity
    in
      p == f * L * rho * V^2 / (2 * D_h); % final equation
    end
  end
end

After substitution of all intermediate terms, the final equation becomes:

p==0.316/(sqrt(4.0 * A / pi) * q / A / nu)^0.25 * L * rho * (q / A)^2 / (2 * sqrt(4.0 * A / pi));

However, in this case the four intermediate terms do not appear in logged simulation data.

Syntax Rules of let Expressions

A let expression consists of two clauses, the declaration clause and the expression clause.

equations
  [...]
  let 
    declaration clause
  in
    expression clause
  end
  [...]
end

The declaration clause assigns an identifier, or set of identifiers, on the left-hand side of the equal sign (=) to an equation expression on the right-hand side of the equal sign:

  LetValue = EquationExpression

The expression clause defines the scope of the substitution. It starts with the keyword in, and may contain one or more equation expressions. All the expressions assigned to the identifiers in the declaration clause are substituted into the equations in the expression clause during parsing.

Note

The end keyword is required at the end of a let-in-end statement.

Here is a simple example:

component MyComponent
  [...]
  variables
    x = 0;
    y = 0;
  end
  equations
    let
      z = y + 1;
    in
      x == z;
    end
  end
end

In this example, the declaration clause of the let expression sets the value of the identifier z to be the expression y + 1. Thus, substituting y + 1 for z in the expression clause in the let statement, the code above is equivalent to:

component MyComponent
  [...]
  variables
    x = 0;
    y = 0;
  end
  equations
    x == y + 1;
  end
  end
end

There may be multiple declarations in the declaration clause. These declarations are order independent. The identifiers declared in one declaration may be referred to by the expressions for identifiers in other declarations in the same declaration clause. Thus, in the example with the Darcy-Weisbach equation, the identifier Re_d (Reynolds number) is used in the expression declaring the identifier f (Darcy friction factor). The only requirement is that the expression references are acyclic.

The expression clause of a let expression defines the scope of the substitution for the declaration clause. Other equations, that do not require these substitutions, may appear in the equation section outside of the expression clause. In the following example, the equation section contains the equation expression c == b + 2 outside the scope of the let expression before it.

component MyComponent
  [...]
  variables
    a = 0;
    b = 0;
    c = 0;
  end
  equations
    let
      x = a + 1;
    in
      b == x;
    end
    c == b + 2;
  end
end

These expressions are treated as peers. They are order independent, so this example is equivalent to

component MyComponent
  [...]
  variables
    a = 0;
    b = 0;
    c = 0;
  end
  equations
    c == b + 2;
    let
      x = a + 1;
    in
      b == x;
    end
  end
end

and, after the substitution, to

component MyComponent
  [...]
  variables
    a = 0;
    b = 0;
    c = 0;
  end
  equations
    b == a + 1;
    c == b + 2;
  end
end

Nested let Expressions

You can nest let expressions, for example:

component MyComponent
  [...]
  variables
    a = 0;
    b = 0;
    c = 0;
  end
  equations
    let
      w = a + 1;
    in
      let
        z = w + 1;
      in
        b == z;
        c == w;
      end
    end
  end
end

In case of nesting, substitutions are performed based on both of the declaration clauses. After the substitutions, the code above becomes:

component MyComponent
  [...]
  variables
    a = 0;
    b = 0;
    c = 0;
  end
  equations
    b == a + 1 + 1;
    c == a + 1;
  end
end

The innermost declarations take precedence. The following example illustrates a nested let expression where the inner declaration clause overrides the value declared in the outer one:

component MyComponent
  [...]
  variables
    a = 0;
    b = 0;
  end
  equations
    let
      w = a + 1;
    in
      let
        w = a + 2;
      in
        b == w;
      end
    end
  end
end

Performing substitution on this example yields:

  component MyComponent
  [...]
  variables
    a = 0;
    b = 0;
  end
  equations
    b == a + 2;
  end
end

Conditional let Expressions

You can use if statements within both declarative and expression clause of let expressions, for example:

component MyComponent
  [...]
  variables
    a = 0;
    b = 0;
    c = 0;
  end
  equations
    let
      x = if a < 0, a else b end;
    in
      c == x;
    end
  end
end

Here x is declared as the conditional expression based on a < 0. Performing substitution on this example yields:

component MyComponent
  [...]
  variables
    a = 0;
    b = 0;
    c = 0;
  end
  equations
    c == if a < 0, a else b end;
  end
end

The next example illustrates how you can use let expressions within conditional expressions. The two let expressions on either side of the conditional expression are independent:

component MyComponent
  [...]
  variables
    a = 0;
    b = 0;
    c = 0;
  end
  equations
    if a < 0
      let
        z = b + 1;
      in
        c == z;
      end
    else
      let
        z = b + 2;
      in
        c == z;
      end
    end
  end
end

This code is equivalent to:

  component MyComponent
  [...]
  variables
    a = 0;
    b = 0;
    c = 0;
  end
  equations
    if a < 0
      c == b + 1;
    else
      c == b + 2;
    end
  end
end

Identifier List in the Declarative Clause

This example shows using an identifier list, rather than a single identifier, in the declarative clause of a let expression:

component MyComponent
  [...]
  variables
    a = 0;
    b = 0;
    c = 0;
    d = 0;
  end
  equations
    let
      [x, y] = if a < 0, a; -a else -b; b end;
    in
      c == x;
      d == y;
    end
  end
end

Here x and y are declared as the conditional expression based on a < 0. Notice that each side of the if statement defines a list of two expressions. A first semantic translation of this example separates the if statement into

if a < 0, a; -a else -b; b end => 
   { if a < 0, a else -b end; if a < 0, -a else b end }

then the second semantic translation becomes

[x, y] = { if a < 0, a else -b end; if a < 0, -a else b end } => 
   x = if a < 0, a else -b end; y = if a < 0, -a else b end;

and the final substitution on this example yields:

component MyComponent
  [...]
  variables
    a = 0;
    b = 0;
    c = 0;
    d = 0;
  end
  equations
    c == if a < 0, a else -b end;
    d == if a < 0, -a else b end;
  end
end

See Also

Related Topics