Engee documentation

Engee Function

Using Julia code in models.

blockType: EngeeFunction

Path in the library:

/Basic/User-Defined Functions/Engee Function

Description

Block Engee Function allows you to use Julia code in models. Engee.

Read more about the Julia programming language on the page Programming.
In the block Engee Function most of the features of the Julia language are supported. However, the use of the Pkg package manager in the block is not provided.

Using

To integrate the Julia code into the model Engee necessary:

  1. Add a block to the model Engee Function from the section Basic/User-Defined Functions block libraries blocks library icon;

  2. In settings window debug article icon 1 on the tab Main the block Engee Function Click on the button Edit source code to open the source code editor (EngeeFunctionCode):

    engee function code editor

Source code cells

The source code editor EngeeFunctionCode consists of functional cells with Julia code. By default, three cells are available: auxiliary (non-editable), Component struct code and Step method code (cells can be hidden):

engee function all start cell

To connect additional source code files, you can use the function include() in the cell Common code (for the description of the cell, see below):

include("/user/engeefunction/source.jl")
The entire block code Engee Function you can write in the cell Common code by gaining full control over the component structure, signatures, and number of functions.

To add/remove other function cells, click on «Method management» engee function all methods and check/uncheck the appropriate cells.:

engee function choose methodsengee function choose methods 1

Each cell is responsible for the unique functionality of the block Engee Function. A detailed description of each cell is given below.

Information cell (non-editable)

Details

Automatically displays block variables Engee Function, attributes of input and output signals (dimension, type, discreteness) and other user-defined parameters. Its content is updated dynamically depending on the block settings. The cell is always active, but it is not selected in the method management menu. engee function all methods. It contains semi-transparent text with hints.

Default cell code:

START_TIME = 0.0
STOP_TIME = 10
BLOCK_NAME = "Engee Function-1"
INPUT_SIGNAL_ATTRIBUTES[1] = (dimensions = -1, type = Inherit, sample_time = (period = -1.0, offset = 0.0), direct_feedthroughs = true)
OUTPUT_SIGNAL_ATTRIBUTES[1] = (dimensions = -1, type = Float64, sample_time = (period = -1.0, offset = 0.0))
gain = 2
const Dimensions = NTuple{N, Int} where N
const SampleTime = NamedTuple{(:period, :offset, :mode), Tuple{Rational{Int64}, Rational{Int64}, Symbol}}
const SAMPLE_TIME = (period = -1//1, offset = 0//1, mode = :Unknown)
Changing the block parameters affects not only the contents of the information cell, but also the hints in other cells, which are also displayed in translucent text.

Define component struct

Details

Adds a cell Component struct code, which defines the structure of the block component Engee Function (inherited from the type AbstractCausalComponent). The fields of the structure are defined between non-editable lines struct Block <: AbstractCausalComponent and end. By default, the parameter is created g, initialized by the block value gain, and the constructor Block() it does not accept arguments.

Default cell code:

struct Block <: AbstractCausalComponent
# Для того, чтобы создать свой блок, определите структуру, наследуемую от
# `AbstractCausalComponent`. Конструктор не должен принимать аргументов.
# Параметр структуры `g` инициализируется значением параметра блока `gain`.
    g::Real
    function Block()
        new(gain)
    end
end

Use common code

Details

Adds a cell Common code, in which the code is written in free form. By default, the cell is empty. For example, if the standard structure declaration in Component struct code not suitable (due to non-editability struct Block <: AbstractCausalComponent), then you can disable Define component struct to delete a cell, and define the component structure manually in Common code. The same applies to any functions from the method management menu. engee function all methods — instead of standard cells, you can write your own code in Common code.

engee function common code

Declaring a component and a functor is required to work. Engee Function. If Define component struct if the Define step method is disabled, then their code must be set in Common code otherwise, the block will not work.
To redefine inheritance functions (Override, see below), you first need to include the corresponding cell, erase its contents, and then write new code in Common code.

Define step method

Details

Adds a cell Step method code, in which the method is specified Step, which calculates the output signals of the block Engee Function. The method signature is generated automatically depending on the values of the corresponding labels (Label) ports on the tab Ports. The method is represented as a functor (for more information, see here) and is called at each step of the simulation. Fields are defined between non-editable lines function (c::Block)(t::Real, in1) and end. The first argument t::Real — simulation time, then input signal variables are transmitted. Calculated output values are returned via a keyword return.

Default cell code:

function (c::Block)(t::Real, in1)
# Функция для вычисления значений выходных сигналов блока.
# Первый аргумент `t` - время, остальные - значения входных сигналов.
# В данном примере выходом блока является результат умножения
# входного сигнала на внутренний параметр структуры `g`.
    return c.g .* in1
end

Define update method

Details

Adds a cell Update method code, in which the method update! updates the internal state of the block Engee Function at each step of the simulation. The first argument c::Block — block structure, second t::Real — simulation time, then the input signals are transmitted. If the block has no internal state, then the method can remain empty and simply return c.

Default cell code:

function update!(c::Block, t::Real, in1)
# В случае, если блок имеет внутреннее состояние, необходимо определить метод
# `update!`. Первый аргумент - структура блока, второй - время, остальные - значения
# входных сигналов. В данном случае блок не имеет внутреннего состояния и
# метод не имеет эффектов.
    return c
end
If you need to define multiple methods update! or set a method with a different signature, then a cell Update method code you can disable it and write the code in the cell Common code. The compiler will automatically detect the presence of the method update! and uses it for simulation.

Define terminate method code

Details

Adds a cell Terminate method code, which is executed at the end of the block simulation Engee Function (using the method terminate!). The first argument c::Block — block structure. By default, the method does not perform any additional actions and simply returns c.

Default cell code:

function terminate!(c::Block)
    return c
end

Override type inheritance method

Details

Adds a cell Types inheritance method, which overrides the type inheritance method.

  • If the check box is Override type inheritance method disabled (by default) — the types of input/output ports are inherited according to the rules specified in the description of input/output ports.

  • If the check box is Override type inheritance method installed — input/output port types are inherited according to the rules specified in the function propagate_types cells Types inheritance method in the source code. Function propagate_types accepts one argument, a vector of types, one type for each input signal, and returns a vector of output types.

Default cell code:

function propagate_types(inputs_types::Vector{DataType})::Vector{DataType}
# Функция, возвращающая массив типов сигналов на выходе.
# Игнорируется, если используется алгоритм по умолчанию.
# В данном случае учитываются тип входного сигнала и тип
# параметра блока `gain`.
    input_type = first(inputs_types)
    # promote_type возвращает тип, к которому приводятся типы-аргументы
    # при арифметических операциях с объектами этих типов.
    output_type = promote_type(input_type, eltype(gain))
    return [output_type]
end

Here, the common element of the input signal and the element of the parameter set in the block settings on the Parameters tab are taken to inherit types.

Override dimensions inheritance method

Details

Adds a cell Dimensions inheritance method, which redefines the method of inheritance of dimensions.

  • If the check box Override dimensions inheritance method disabled (by default) — the dimensions of the input/output ports are inherited according to the rules specified in the description of the input/output ports.

  • If the check box Override dimensions inheritance method set — the dimensions of the input/output ports are inherited according to the rules specified in the function propagate_dimensions cells Dimensions inheritance method in the source code. Function propagate_dimensions accepts an array of tuples (dimensions) for each input signal and returns an array of dimensions at the output.

Default cell code:

function propagate_dimensions(inputs_dimensions::Vector{<:Dimensions})::Vector{<:Dimensions}
# Функция, возвращающая массив размерностей сигналов на выходе.
# Игнорируется, если используется алгоритм по умолчанию.
# В данном случае учитываются размерности входного сигнала и
# параметра блока `gain`.
    input_dimensions = first(inputs_dimensions)
    mock_input = zeros(input_dimensions)
    mock_output = mock_input .* gain
    return [size(mock_output)]
end

Here, an array with the required dimensions is taken to inherit the dimensions (mock_input), consisting of zeros. It is multiplied by the element of the parameter set in the block settings on the Parameters tab, after which its dimension is taken.

Use common method for types and dimensions inheritance

Details

Adds a cell Common types and dimensions inheritance method, which uses a common method to redefine inheritance of types and dimensions at the same time. As opposed to specific methods for types (Types inheritance method) or dimensions (Dimensions inheritance method), the general method includes both types and dimensions at the same time:

  • If the check box Use common method for types and dimensions inheritance disabled (by default) — the general method is ignored, the dimensions and types of input/output ports are inherited according to the rules specified in the description of input/output ports or in inheritance methods. Override type inheritance method (if enabled) and Override dimensions inheritance method (if enabled).

  • If the check box is Use common method for types and dimensions inheritance set — the dimensions and types of input/output ports are inherited according to the rules specified in the function propagate_types_and_dimensions cells Common types and dimensions inheritance method in the source code.

Default cell code:

function propagate_types_and_dimensions(inputs_types::Vector{DataType}, inputs_dimensions::Vector{<:Dimensions})::Tuple{Vector{DataType}, Vector{<:Dimensions}}
# Функция, возвращающая массив типов сигналов и массив
# размерностей сигналов на выходе. Эту функцию можно использовать
# если необходимо одновременно переопределить и алгоритм наследования
# типов сигналов, и алгоритм наследования размерностей.
    outputs_types = propagate_types(inputs_types)
    outputs_dimensions = propagate_dimensions(inputs_dimensions)
    return outputs_types, outputs_dimensions
end

Dependencies

To use this cell, check the method boxes Override type inheritance method and Override dimensions inheritance method.

Override sample time inheritance method

Details

Adds a cell Sample times inheritance method, which redefines the inheritance method of the calculation step.

The structure code of the calculation step SampleTime and functions propagate_sample_times They will not be automatically added to the EngeeFunctionCode source code of older models. Engee. To refine the old models, add the calculation step structure and function.
  • If the check box Override sample time inheritance method unchecked (by default) — the preset method of inheritance of the calculation step is used (by default Default) from the parameter Sample time inheritance method tabs Advanced. Read more about the preset methods in the parameter description. Sample time inheritance method.

  • If the check box Override sample time inheritance method installed — preset tab methods Advanced are ignored (the parameter is unavailable), the inheritance method from the cell is used. Sample times inheritance method in the source code of EngeeFunctionCode.

For the inheritance method to work, you need to find the function string from the cell. propagate_sample_times and manually set the desired calculation step.

Default cell code:

function propagate_sample_times(inputs_sample_times::Vector{SampleTime}, fixed_solver::Bool)::SampleTime
# Функция, возвращающая время дискретизации блока.
# Используется только в режиме наследования `Custom`.
# Параметр fixed_solver говорит о том, используется решатель
# с постоянным шагом (true) или с переменным (false).
# Более сложный пример работы с наследованием времени
# дискретизации блока можно посмотреть в документации.
    return first(inputs_sample_times)
end

where the calculation step has the form:

const SampleTime = NamedTuple{(:period, :offset, :mode), Tuple{Rational{Int64}, Rational{Int64}, Symbol}}

Override direct feedthrough setting method

Details

Adds a cell Direct feedthrough setting method, which defines a direct end-to-end connection.

  • If the check box Override direct feedthrough setting method disabled (by default), the direct end-to-end connection is determined by the parameter value Direct feedthrough.

  • If the check box Override direct feedthrough setting method If installed, then a direct end-to-end connection is available. This means that the output signal is controlled directly by the value of the input port.

Default cell code:

function direct_feedthroughs()::Vector{Bool}
# Функция, возвращающая массив булевых значений, определяющих,
# сквозные соединения. Если i-ый элемент массива равен true,
# то i-ый порт имеет сквозное соединение.
# Игнорируется, если используется алгоритм по умолчанию.
    return [true]
end

example:

function direct_feedthroughs()::Vector{Bool}
    if gain == 2
        return [false]
    else
        return [true]
    end
end

Constants and functions for obtaining attributes

In order to find out the types, sizes, and other auxiliary information in the executable code of the block, use the following constants in the code Engee Function:

  • BLOCK_NAME — the name of the block. For each block added to the canvas Engee there is a name that can be accessed through this constant. For example, you can refer to BLOCK_NAME during error initialization, to print the block name in it.

  • START_TIME — start the simulation from the model settings.

  • STOP_TIME — end of the simulation from the model settings.

  • INPUT_SIGNAL_ATTRIBUTES — lists of attributes for each input port. For example, to find out the attributes of the first input signal, use INPUT_SIGNAL_ATTRIBUTES[1], where 1 — the first input port of the unit Engee Function.

  • OUTPUT_SIGNAL_ATTRIBUTES — lists of attributes for each output port. For example, to find out the attributes of the first output signal, use OUTPUT_SIGNAL_ATTRIBUTES[1], where 1 — the first output port of the unit Engee Function.

Variables START_TIME and STOP_TIME available in all source code cells Engee Function (including override inheritance methods). They contain the start and end times of the simulation set in the model settings. Common usage scenarios:

  • Calculating the duration of the simulation STOP_TIME - START_TIME.

  • Performing an action at the first/last step (initialization/finalization).

  • Specifying a discrete step as a fraction of the total duration (for example, to get N points on the interval).

To find out more information about a specific block port, you can refer to the attributes of its signal by adding a dot . after the constant INPUT_SIGNAL_ATTRIBUTES[i], where [i] — the number of the input port, and OUTPUT_SIGNAL_ATTRIBUTES[i], where [i] — the number of the output port, respectively. Additional information can be obtained through the following contact functions:

  • dimensions — the dimension of the signal. Can be shortened to dims.

  • type — the type of signal. Can be shortened to tp.

  • sample_time — the calculation step. It is a structure similar to the attributes of signals, which can be accessed through a dot. .. Two conversion functions are available:

    • period — the period of the calculation step. Full conversion function — sample_time.period. Can be shortened to st.p.

    • offset — offset of the calculation step. Full conversion function — sample_time.offset. Can be shortened to st.o.

  • direct_feedthrough — determines whether the port has a direct end-to-end connection. It is used only for input ports (checks attributes only for the input port). Can be shortened to df.

An example of a model with all constants and conversion functions

Details

An example of a model with Engee Function with all constants and conversion functions:

engee function constants

struct Block <: AbstractCausalComponent end

function (c::Block)(t::Real, x1, x2)
    y1 = [START_TIME, STOP_TIME]
    y2 = collect(INPUT_SIGNAL_ATTRIBUTES[1].dimensions)
    y3 = OUTPUT_SIGNAL_ATTRIBUTES[1].dims[1]
    y4 = (INPUT_SIGNAL_ATTRIBUTES[2].type == Int64)
    y5 = (OUTPUT_SIGNAL_ATTRIBUTES[4].tp == Bool)
    y6 = INPUT_SIGNAL_ATTRIBUTES[1].sample_time.period
    y7 = OUTPUT_SIGNAL_ATTRIBUTES[1].st.p
    y8 = INPUT_SIGNAL_ATTRIBUTES[1].sample_time.offset
    y9 = OUTPUT_SIGNAL_ATTRIBUTES[2].st.o
    y10 = INPUT_SIGNAL_ATTRIBUTES[1].direct_feedthrough
    y11 = INPUT_SIGNAL_ATTRIBUTES[2].df
    return (y1, y2, y3, y4, y5, y6, y7, y8, y9, y10, y11)
end

Using parameters as variables

Global variables available in the Variables window Engee variables icon, can be set on the Parameters tab for insertion into the block source code Engee Function. If the parameter name and the global variable match, the parameter value will be automatically substituted from the global variable.

It is important to distinguish the block parameters (set on the tab Parameters and they are global variables in the source code. Engee Function) from global variables Engee in the variables window variables icon. Although you can use the values and names of global variables in the block parameters. Engee these entities are completely different. Global variables cannot be controlled Engee through the block parameters or from its source code. However, for the source code, the block parameters (from the tab Parameters) are global variables and can be used in any part of it without reference to a specific function or code block.

Example of using parameters as variables

Details

Let’s consider the case when global variables fully correspond to variables in the source code. For example, three global variables were set a, b, c with values 1, 2, 3 accordingly. All three global variables are used as block parameters. Engee Function:

engee function param explain 1

Then the source code with the added parameters will look like this:

struct Block <: AbstractCausalComponent
    a::Real
    b::Real
    c::Real

    function Block()
        new(a_param, b_param, c_param)
    end
end

function (c::Block)(t::Real, x::Vector{<:Real})
    return (c.a .* x .+ c.b) ./ c.c
end

This code defines the structure Block, which adjusts the behavior of the component. The example uses the names of the block parameters. a_param, b_param and c_param to set the structure parameters a, b and c accordingly. The code also defines a method function(c::Block)(t::Real, x::Vector{<:Real}), which scales each element of the vector x to the block parameter a, adds b and divides the result by c. This allows you to flexibly change and normalize the vector. x according to the values of the block parameters.

Let’s consider the case when only tab parameters are used. Parameters:

struct Block <: AbstractCausalComponent end

function (c::Block)(t::Real, x::Vector{<:Real})
    return (a_param .* x .+ b_param) ./ c_param
end

These parameters will be global variables in the block code. This means that they will always be available in any part of the block code without having to define them repeatedly in each function or block of code. This greatly simplifies the code and makes it easy to change the settings on the tab. Parameters without affecting the source code.

Let’s consider the case when the parameters do not fully match the source code. For example, there is a parameter a_param, equal to 100. There is a structure field in the source code a:

struct Block <: AbstractCausalComponent
    a::Real

    function Block()
        new(a_param/10)
    end
end

function (c::Block)(t::Real, x::Vector{<:Real})
    c.a
end

In this code, the parameter a_param used to initialize the field a structures struct Block through its constructor, which divides the parameter value into 10. In this case, the field is returned in the block’s functor a.

Variables can be made global right in the source code.:

a = 1;
b = 2;
с = 3;

struct Block <: AbstractCausalComponent; end

function (c::Block)(t::Real, x::Vector{<:Real})
    return (a .* x .+ b) ./ c
end

A structure is created in the code Block and a function is defined that performs mathematical operations with global variables. a, b and c by applying them to a variable x.

If you don’t want to use the options from the tab Parameters then you can initialize variables directly in the source code .:

struct Block <: AbstractCausalComponent; end

function (c::Block)(t::Real, x)
    a = 1;
    b = 2;
    c = 3;
    return (a .* x .+ b) ./ c
end

A structure is created in the code Block, defining a function that performs mathematical operations on local variables a, b and c by applying them to a variable x.

Let’s consider the most effective and correct way of doing it. To block Engee Function worked correctly, check the box for the option Use external cache for non-scalar output on the Main tab of the block Engee Function. The source code will look like this:

struct Block{Ta, Tb, Tc} <: AbstractCausalComponent
    a::Ta
    b::Tb
    c::Tc

    function Block()
        Ta = typeof(a_param); Tb = typeof(b_param); Tc = typeof(c_param)
        all(isreal, (a_param, b_param, c_param)) ||
          error("Параметры блока должны быть вещественными")
        all(x->isempty(size(x)), (a_param, b_param, c_param)) ||
          error("Параметры блока должны быть скалярами")
        new{Ta, Tb, Tc}(a_param, b_param, c_param)
    end
end

function (c::Block)(t::Real, cache::Vector{<:Real}, x::Vector{<:Real})
    cache .= (c.a .* x .+ c.b) ./ c.c
    nothing
end
This code can only be written in a cell. Common code, since the standard cells do not allow editing the definition of the component structure and the signature of the functor.

Fields a, b and c structures are block parameters that are strictly checked for types in the constructor. Each of these parameters must be a real scalar (Real), which ensures the accuracy of calculations during execution.

Designer Block() checks the types of passed parameters , and . If at least one of them is not a real scalar or has inappropriate dimensions (they must be scalars), the constructor generates an error with the corresponding message. After verification, the constructor initializes the fields of the structure with the values , and the specified types Ta, Tb and Tc.

The calculated function defined for Block instances takes time t, external cache and vector x real numbers. Fields are used in this function a, b and c structures Block to calculate the values, which are then written to the cache. This avoids unnecessary memory allocations by reusing the provided cache.

Thus, the structure Block provides strict management of data types and efficient use of resources by using an external cache to store the results of calculations.

If you want to change the block parameters, you must use mutable the structure. Consider an example:

mutable struct Counter{T} <: AbstractCausalComponent
    limit::T
    iter::T

    function Counter()
      isempty(size(limit)) || error("Предел блока $BLOCK_NAME должен быть скаляром")
      isreal(limit) || error("Предел блока $BLOCK_NAME должен быть вещественным числом")
      T = typeof(limit)
      iter = zero(T)
      new{T}(limit, iter)
    end
end

function (c::Counter)(t::Real)
    return c.iter
end

function update!(c::Counter, t::Real)
  c.iter += 1
  if c.iter > c.limit
    c.iter = zero(c.iter)
  end
  c
end

Structure Counter — This is mutable the data type for counting iterations with a specified limit. It is strongly typed. Fields limit and iter the structures represent the block parameters:

  • limit — this is the limit value of the counter;

  • iter — the current value of the counter.

The parameter is checked in the structure constructor. limit — it must be a scalar and have a real data type. After that, the field is initialized iter with a zero value of the corresponding type T. Function update! updates the tag status c, increasing the value iter by one for each call. If the current value is iter exceeds limit, then it is reset to zero, which allows the counter to cycle back to its initial state.

In the source code of the block Engee Function you can use include, referring to the external code. This allows you to initialize variables from external code (if any) in the source code.
The actual data type, as well as the support for possible data types, depends on the user code inside the block.

Ports

Entrance

Input Port — input port

+ scalar | vector | the matrix | array

Details

An input signal specified as a scalar, vector, matrix, or array.

The number of input ports is set in the parameter Number of input ports.

For each input port, you can configure the settings on the tab Ports. For more information, see Input port.

Output

Output port — output port

+ scalar | vector | the matrix | array

Details

The output signal returned as a scalar, vector, matrix, or array.

The number of output ports is set in the parameter Number of output ports.

For each output port, you can configure the settings on the tab Ports. For more information, see Output port.

Parameters

Main

Number of input ports — number of input ports

+ Integer

Details

Specify the number of input ports in the block.

Default value

1

A name for programmatic use

Inputs

Configurable

None

Calculated

Yes

Number of output ports — number of output ports

+ Integer

Details

Specify the number of output ports of the block.

Default value

1

A name for programmatic use

Outputs

Configurable

None

Calculated

Yes

Sample time — the interval between the calculation steps

+ SampleTime (real number / vector of two real numbers)

Details

Specify the interval between the calculation steps as a non-negative number. To inherit the calculation step, set this parameter to −1.

Default value

-1

A name for programmatic use

SampleTime

Configurable

None

Calculated

Yes

Parameters

Number of parameters — the number of parameters

+ Integer

Details

Specify the number of parameters used in the block.

Default value

1

A name for programmatic use

Parameters

Configurable

None

Calculated

Yes

Name — parameter name

+ String

Details

Specify the parameter name to use in the block source code. Engee Function.

The name of the first parameter (present by default) — gain, the value of which is 2. The new parameters are called parameter2 and then ascending. The default value of the new parameters is 0.

For more information, see Using parameters as variables.

Default value

gain

A name for programmatic use

Parameter1Name

Configurable

None

Calculated

None

Value — parameter value

+ Arbitrary type

Details

Specify the value of the parameter with the name Name. Any Julia expressions can be set as values.

To set the bus parameter, set for the parameter Value the bus value in the form of a named tuple, for example (s1 = 5, s2 = 4).

For more information, see Using parameters as variables.

Advanced

Use external cache for non-scalar output — external cache for non-scalar output signals

+ Logical

Details

Check the box to use an external cache for a non-scalar (multidimensional) output signal to save RAM. Engee. Used if the block has Engee Function only one output port:

  • If the checkbox is unchecked (by default), the external cache is not used.

  • If the flag is checked, the output signal can accept an additional argument. cache, which must be taken into account in the source code of EngeeFunctionCode. If the unit has one output port, then in the cell Step method code argument cache it is automatically added to the list of function arguments, except when the port dimension is explicitly specified as () (scalar). In the source code, it is necessary to define the functors depending on the dimension of the output signal. The functors can be defined in a cell Common code:

    • If the output signal is scalar:

      function (c::Block)(t::Real, x)
          return c.g * x
      end
    • If the output signal is non-scalar:

      function (c::Block)(t::Real, cache, x)
          cache .= c.g .* x
          nothing
      end

      where t — time, x — the argument (information from the input ports). The time parameter must be specified, even if it is not included in the block parameters.

Default value

false (disabled)

A name for programmatic use

UseExternalCache

Configurable

None

Calculated

None

Sample time inheritance method — method of inheritance of the calculation step

+ Default | Discrete | Continuous

Details
Customization Sample time inheritance method disappears from the tab Advanced if the checkbox is selected in the source code of EngeeFunctionCode Override sample time inheritance method. In this case, the method of inheritance of the calculation step is determined by the code of the functional cell. Sample times inheritance method.

Defines the method of inheritance of the calculation step depending on the selected value:

  • Default — the default method of inheritance of the calculation step. The method of inheritance Default used when the block Engee Function it is not discrete or continuous. It allows for any kind of calculation step. When choosing this method, the block Engee Function It will inherit the calculation step according to the following principles:

    • If the block has no input ports, there is a continuous calculation step at the output.

    • If all the calculation steps are the same at the input, the calculation step at the output is the same as the input.

    • If there are continuous calculation steps among the input steps, there are also continuous calculation steps at the output.

    • If there is a fixed minor (FiM, Fixed-in-Minor) among the input calculation steps, there is no continuous calculation step and solver with variable pitch — the output is a fixed small one.

    • If there is no continuous and fixed small calculation step at the input and not all calculation steps are equal, only discrete calculation steps at the input are considered, for which one of the options is valid.:

      • If the largest common divisor of the discrete calculation steps coincides with one of the input calculation steps or a constant—step solver is used, the output is a discrete calculation step with the step of the largest common divisor.

      • If the variable—step solver and the largest common divisor of the input discrete calculation steps do not match any of the input calculation steps, the output is a fixed small one.

  • Discrete — an inheritance method for obtaining a discrete calculation step. When choosing this method, the block Engee Function It will inherit the calculation step according to the following principles:

    • If the input has a continuous or fixed small calculation step, the output is a discrete calculation step with a solver step (even if the solver is with a variable step).

    • If there are discrete calculation steps at the input, the output is a discrete calculation step with the largest common divisor from the input discrete calculation steps.

  • Continuous — an inheritance method for obtaining a continuous calculation step regardless of the input calculation steps.

If in the field Sample time the value is specified −1, then the calculation step is inherited according to the method specified in the field Sample time inheritance method depending on the selected value (Default, Discrete, Continuous). In all other cases (Sample time not equal to −1 and Sample time greater than or equal to 0) — block Engee Function works with the specified field value Sample time, ignoring Sample time inheritance method.

An example of redefining a function propagate_sample_times with a job similar to the inheritance method Default:

function propagate_sample_times(inputs_sample_times::Vector{SampleTime}, fixed_solver::Bool)::SampleTime
    nonnegative_sample_times = filter(
        st -> st.period >= 0,
        collect(values(inputs_sample_times)),
    )
    finite_periods = filter(
        st -> !isinf(st.period),
        nonnegative_sample_times,
    ) .|> (st -> st.period)
    output_sample_time = if !isempty(nonnegative_sample_times)
        if allequal(nonnegative_sample_times)
            first(nonnegative_sample_times)
        elseif any(st -> st.period == 0 // 1 && st.mode == :Continuous, nonnegative_sample_times)
            (period = 0 // 1, offset = 0 // 1, mode = :Continuous)
        elseif any(st -> st.mode == :FiM, nonnegative_sample_times) && !fixed_solver
            (period = 0 // 1, offset = 0 // 1, mode = :FiM)
        elseif (
            all(x -> x.period > 0 // 1, nonnegative_sample_times) &&
            (fixed_solver || gcd(finite_periods) in finite_periods)
            )
            (period = gcd(finite_periods), offset = 0 // 1, mode = :Discrete)
        else
            (period = 0 // 1, offset = 0 // 1, mode = :FiM)
        end
    else
        (period = 0 // 1, offset = 0 // 1, mode = :Continuous)
    end
    return output_sample_time
end
If the function propagate_sample_times returns (period = 0 // 1, offset = 0 // 1, mode = :Discrete), then such a calculation step will be perceived as discrete with a solver step.
Values

Default | Discrete | Continuous

Default value

Default

A name for programmatic use

SampleTimeInheritanceMethod

Configurable

None

Calculated

None

Ports

Input port

Label — name of the input port

+ String

Details

Specify the name of the input port.

Default value

A name for programmatic use

InputPort1Label

Configurable

None

Calculated

None

Type — type of input data

+ Inherit | Float64 | Float32 | Float16 | ComplexF64 | ComplexF32 | Bool | Int8 | UInt8 | Int16 | UInt16 | Int32 | UInt32 | Int64 | UInt64 | Int128 | UInt128 | BusSignal

Details

Choose one of the options:

  • A certain type (all except Inherit) — it is checked that a certain type of signal is being sent to the input port. Select a specific data type for the input port.

  • Inherit (by default) — inherits the data type from the associated block. It can be any type of data.

Values

Inherit | Float64 | Float32 | Float16 | ComplexF64 | ComplexF32 | Bool | Int8 | UInt8 | Int16 | UInt16 | Int32 | UInt32 | Int64 | UInt64 | Int128 | UInt128 | BusSignal

Default value

Inherit

A name for programmatic use

InputPort1Type

Configurable

None

Calculated

None

Output bus type — input bus type

+ Data type

Details

Input bus type. To block Engee Function I figured out which tire will come to the entrance, it’s enough to install Type in the value Inherit. Explicit type indication is only necessary for the output signal.

Block Engee Function It does not inherit buses to the output ports, although it can receive them to the inputs. To inherit buses on output ports (for transmission to other blocks), you must explicitly specify the bus type in the parameter Output bus type.

Dependencies

To use this parameter, set for the parameter Type meaning BusSignal.

Default value

BusSignal((), (), (), :EngeeFunctionInput1BusType)

A name for programmatic use

InputPort1BusType

Configurable

None

Calculated

Yes

Size — the dimension of the input signal

+ Int64 or tuple of Int64

Details

The dimension of the input signal can be set as:

  • −1 (by default) — inherits the dimension of the signal applied to the input port (the signal can have any dimension).

  • An integer or a tuple of integers — the input signal must have a specified number of elements. Dimensions are specified in Julia notation, for example, () for a scalar, (2,) for a one-dimensional signal consisting of two elements, or (2, 3, 4) for the multidimensional.

  • A tuple of integers and −1 — inheritance of certain dimensions. For example, (-1, 2) — the first dimension is inherited, and the second dimension is set explicitly.

Default value

-1

A name for programmatic use

InputPort1Size

Configurable

None

Calculated

Yes

Direct feedthrough — direct end-to-end connection

+ Logical

Details

Defines a direct end-to-end connection:

  • If the checkbox is selected (by default), then a direct end-to-end connection is available. This means that the output signal is controlled directly by the value of the input port.

  • If the checkbox is unchecked, then a direct end-to-end connection is not available. This means that the output signal will not be controlled by the value of the input port, which allows the unit to open loops.

Default value

true (enabled)

A name for programmatic use

InputPort1DirectFeedthrough

Configurable

None

Calculated

None

Output port

Label — The name of the output port

+ String

Details

Specify the name of the output port.

Default value

A name for programmatic use

OutputPort1Label

Configurable

None

Calculated

None

Type — type of output data

+ Inherit | Float64 | Float32 | Float16 | ComplexF64 | ComplexF32 | Bool | Int8 | UInt8 | Int16 | UInt16 | Int32 | UInt32 | Int64 | UInt64 | Int128 | UInt128 | BusSignal

Details

Choose one of the options:

  • A certain type (all except Inherit) is the specific data type for the output signal.

  • Inherit (by default) — inherits the data type of the output signal. Calculates the smallest common type when there are several input signals of different types. It can be any type of data.

Values

Inherit | Float64 | Float32 | Float16 | ComplexF64 | ComplexF32 | Bool | Int8 | UInt8 | Int16 | UInt16 | Int32 | UInt32 | Int64 | UInt64 | Int128 | UInt128 | BusSignal

Default value

Float64

A name for programmatic use

OutputPort1Type

Configurable

None

Calculated

None

Output bus type — type of output bus

+ Data type

Details

Type of output bus. To block Engee Function If you have issued a tire to the outlet, you must explicitly specify its type.

Dependencies

To use this parameter, set for the parameter Type meaning BusSignal.

Default value

BusSignal((), (), (), :EngeeFunctionOutput1BusType)

A name for programmatic use

OutputPort1BusType

Configurable

None

Calculated

Yes

Size — the dimension of the output signal

+ Int64 or tuple of Int64

Details

The dimension of the output signal can be set as:

  • −1 (by default) — inherits the dimension of the signal sent to the input port. If there are several input signals, then dimension inheritance follows the rules broadcast of the Julia mechanism. Special cases:

    • if the input signals have the same dimension, then the output port inherits it.;

    • If the input signals are scalars and non-scalars (vectors, matrices, or multidimensional arrays) that have the same dimension, then it is inherited.;

    • if broadcast cannot be applied to the input signals (for example, the input signals are vectors of different lengths), then automatic dimension inheritance is not possible. In this case, you will need to explicitly set the output signal dimension.

  • An integer or a tuple of integers — the output signal must have a specified number of elements. Dimensions are specified in Julia notation, for example, () for a scalar, (2,) for a one-dimensional signal consisting of two elements, or (2, 3, 4) for the multidimensional.

  • A tuple of integers and −1 — inheritance of certain dimensions. For example, (-1, 2) — the first dimension is inherited, and the second dimension is set explicitly.

Default value

-1

A name for programmatic use

OutputPort1Size

Configurable

None

Calculated

Yes

Working with fixed point and custom buses

Block Engee Function, and also command prompt img 41 1 2 Engee they support fixed-point operation (Fixed) and custom tire types (BusSignal). These constructs help to control the behavior of operations when working with integer, real, fixed, and complex data types.

Fixed point types and functions (Fixed-Point)

Details

In the article Fixed-Point arithmetic in Engee describes how to work with a fixed point in Engee. The constructions given in the article are also valid for the block Engee Function, this is how the block supports:

  • FixedPoint — the abstract type of all fixed numbers;

  • Fixed — a specific type of fixed number, which is created manually with an indication of the bit representation;

  • fi(…​) — creating a type value Fixed using a numeric value and representation parameters;

  • fixdt(…​) — creation of the Fixed type with indication of the sign, width and number of fractional bits.

For example:

a = fi(5, 1, 16, 4)     # Знаковое число, 16 бит, 4 дробных
b = fi(10, 0, 8, 0)     # Беззнаковое целое 8 бит
T = fixdt(1, 24, 8)     # Тип Fixed с 24 битами, 8 из них — дробные
c = Fixed(0x01ff, T)    # Создание фиксированного числа напрямую по битовому представлению

Fixed-point numbers in Engee Function may:

Working with custom tire types (BusSignal)

Details

Custom bus types allow you to specify the types of input and output signals in the form of structured tuples (NamedTuple) with a description of the names, types, and dimensions inside Engee Function. Available functions:

  • BusSignal{Names, Types, Dimensions, :BusName} — a bus with the names of the signals (Names), basic types (BaseTypes), dimensions (Dims) and the name of the bus (BusName);

  • get_bus_names(type) — get a list of signal names;

  • get_bus_types(type) — get the types of signals;

  • get_bus_dimensions(type) — get the dimensions of the signals;

  • get_names_types_dims(type) — get everything at once;

  • get_bus_signal_type(::NamedTuple) — identify the type BusSignal by value.

An example of determining and analyzing the type of tire:

bus_type = BusSignal{(:s1, :s2, :s3), Tuple{Int64, Float64, Int8}, ((), (2,), (2, 2)), :MyBus}
get_names_types_dims(bus_type)  # => ((:s1, :s2, :s3), (Int64, Float64, Int8), ((), (2,), (2, 2)))

get_bus_names(bus_type)       # => (:s1, :s2, :s3)
get_bus_types(bus_type)       # => (Int64, Float64, Int8)
get_bus_dimensions(bus_type)  # => ((), (2,), (2, 2))

You can explicitly describe the tires:

bus_obj = BusSignal{(:a, :b), Tuple{Int, Float64}, ((), (3,)), :MyBus}((a=1, b=[1.0, 2.0, 3.0]))

Nested buses are also supported.:

Inner = BusSignal{(:x, :y), Tuple{Int, Int}, ((), ()), :InnerBus}
Outer = BusSignal{(:a, :b), Tuple{Float64, Inner}, ((), ()), :OuterBus}

Usage in the block Engee Function

Details

Types Fixed and BusSignal It can be used in different parts of the block. Engee Function:

  • In the block parameters, you can set fixed point values using fi(…​), as well as transfer bus structures BusSignal{…​}.

  • In the cell Common code — data types can be defined (Fixed, BusSignal{…​}), component structures (struct Block), create auxiliary functions or initialize values. You can also move the main logic here if disabled Step method code or Component struct code.

  • In the cell Component struct code — when describing the fields of the structure, you can use values like Fixed, as well as the types BusSignal if the fields are composite signals.

  • In the cell Step method code — the main logic of the block is implemented. Here, fixed numbers and bus signals can participate in calculations, comparisons, and structure processing.

  • In the cell Types inheritance method — types can be used Fixed and BusSignal to analyze the inputs and specify the output type of the component.

  • In the cell Dimensions inheritance method — data can be used Fixed and the values from the buses to determine the dimensions of the output signals.

  • In the cell Update method code — if the block has an internal state, fields like Fixed or BusSignal It can be used to store or change this state at each step of the simulation.

  • In the cell Terminate method code — these types can be used to record the final state if the structure contains the appropriate fields.

  • In the cell Common types and dimensions inheritance method — if it is required to process both types and dimensions at the same time., Fixed and BusSignal They can also participate in the calculation logic.

Therefore, Fixed, fi(…​), fixdt(…​), BusSignal and get_bus The functions are applicable in all aspects of the configuration and operation of the unit. Engee Function — both at the execution stage and at the generation stage of the type and dimension of the signal. They can be freely used both in parameters and in the source code in all cells (subject to the rules of their operation).

Annotations

Annotations in Engee Function they allow you to display the block parameters directly under its name in the model. To add them, open settings window debug article icon 1 the block Engee Function and go to the Annotation tab. Select the desired block property markers and add them to the text editor.

engee function annotations

On this tab:

  • A list of available options (except hidden ones) is displayed on the left.

  • On the right is a text editor where you can set an annotation with markers in the format %<Parameter Name>.

  • The parameters can be transferred manually, via auto-completion, or using the button engee function annotations 1.

After exiting the editor (for example, when clicking outside of it), the annotation is applied: the markers are automatically replaced with the actual parameter values and the final text is displayed under the block name (or above it, if the name is placed on top).

To delete annotations, you must delete the corresponding marker in the editor.

Available Markers

The property marker is automatically replaced with the current parameter value. The following markers are available:

  • Ports: %<Inputs>, %<Outputs> — number of input and output ports; %<InputPort1Type>, %<OutputPort1Type>, %<InputPort1Size>, %<OutputPort1Size> — the type of data and the dimension of the signals.

  • Temporary characteristics: %<SampleTime> — discreteness; %<SampleTimeInheritanceMethod> — the method of inheritance of discreteness.

  • Code cells: %<ComponentStructCode>, %<StepMethodCode> — the code of the step structure and method.

  • Parameters: %<Parameters>, %<Parameter1Name>, %<Parameter1Value> — names and values of the parameters.

  • Enable flags: %<DefineComponentStruct>, %<UseCommonCode>, %<DefineStepMethod>, %<DefineUpdateMethod>, %<DefineTerminateMethod> — inclusion of the relevant sections of the code.

  • Redefinition methods: %<OverrideTypesInhMethod>, %<OverrideDimsInhMethod>, %<OverrideSampleTimeInhMethod> — type inheritance settings.

  • Other: %<UseExternalCache> — using an external cache.

Conversion functions and typed arithmetic operations: econvert, esum, emul, ediv

In the block Engee Function, as well as in command line img 41 1 2 Engee functions are available that allow you to perform arithmetic operations specifying the output type, convert values between types with rounding and overflow control, and pre-determine the result type using specialized rules (_promote_type). These functions are used in logic Engee Function written in the source code cells Step method code, Common code and others.

Available functions:

  • econvert — conversion of values between types;

  • esum, esub, emul, ediv — arithmetic operations with the output type assignment;

  • emul! — matrix multiplication with result caching;

  • *_promote_type — output functions based on input types.

These functions can be applied to both scalars and arrays. The result is always returned in a type explicitly specified by the user (or output automatically), which makes the block behavior stable and expected.

Supported types

Details

All the functions in this set work with the following types:

  • The real ones: Float16, Float32, Float64;

  • Integers: Int8, Int16, Int32, Int64, Int128, UInt8, UInt16, UInt32, UInt64, UInt128;

  • Logical: Bool;

  • Fixed: Fixed (created via fi(…​) or fixdt(…​));

  • Comprehensive: Complex{T}, where T — any of the above types.

Functions can also be used with arrays and vectorized expressions using dot syntax (.). Next, let’s look at the functions in more detail.

Converting numbers using econvert

Details

Function econvert allows you to convert a value to a specified type by specifying the rounding method and the overflow handling method.:

econvert(type::Type{T1}, var::T2; rounding_mode::RoundingMode=RoundDown, overflow::Bool=true)

Here:

  • type — the type to convert the value to. If var — a complex number, the basic real type is indicated. For example, econvert(Float32, Complex{Int16}(1 + 2im)) it will return Complex{Float32};

  • var — value for conversion;

  • rounding_mode — the rounding method (RoundDown, RoundUp, RoundNearest, RoundNearestTiesAway, RoundToZero);

  • overflow — if true, then the overflow behavior (wrap) is used; if false saturation is enabled — the value is limited by the type range.

Function econvert It can be used for type conversion before comparisons, inside arithmetic expressions, when processing signals in conditions and other scenarios.

Arithmetic with type control using esum, esub, emul, ediv, emul!

Details

Functions of typed arithmetic operations esum, esub, emul, ediv, emul! they have the same interface:

esum(x1, x2, out_type, overflow=true, rm=RoundDown)
esub(x1, x2, out_type, overflow=true, rm=RoundDown)
emul(x1, x2, out_type, overflow=true, rm=RoundDown)
ediv(x1, x2, out_type, overflow=true, rm=RoundDown)
emul!(cache, x1, x2, out_type, overflow=true, rm=RoundDown)

Arguments:

  • x1, x2 — operation arguments (scalars or arrays);

  • out_type — the type in which the result is produced and returned;

  • overflow — enabling overflow (true) or saturation (false);

  • rm — the rounding method (see the list above).

Functions esum, esub, emul, ediv returns the value corresponding to the specified out_type.

Function emul! It is used to speed up when working with matrices: it does not create a new array, but writes the result to an already allocated one. cache. It is important that eltype(cache) coincided with out_type.

The result depends on the type out_type: it affects not only the final result, but also the behavior of intermediate calculations — which is especially important for Fixed and Complex.

Inferring the type of result using *_promote_type

Details

If the block parameters (for example, SumType or MulType) are set as a string "Inherit", the type of result is determined automatically based on the types of input data using the functions *_promote_type.

If you need to calculate the result type of an operation, then use the listed functions:

  • Defines the output type when adding n values of the same type T:

    sum_promote_type(::Type{T}, n::Integer=1, hdlsetup::Bool=false)
  • Defines the output type when two different types are added together T1 and T2 by n values:

    sum_promote_type(::Type{T1}, ::Type{T2}, n::Integer=1, hdlsetup::Bool=false)
  • Defines the output type when adding an arbitrary number of arguments with different types.:

    sum_promote_type_from_inputs(types::Type...; hdlsetup::Bool=false)
  • Determines the type of accumulator when adding up n values of the same type T:

    sum_accumulator_promote_type(::Type{T}, n::Integer=1, hdlsetup::Bool=false)
  • Determines the type of battery when two different types are added together. T1 and T2 by n values:

    sum_accumulator_promote_type(::Type{T1}, ::Type{T2}, n::Integer=1, hdlsetup::Bool=false)
  • Defines the type of accumulator when adding an arbitrary number of arguments with different types.:

    sum_accumulator_promote_type_from_inputs(types::Type...; hdlsetup::Bool=false)
  • Defines the output type when multiplying values of the same type T:

    mul_promote_type(::Type{T}, hdlsetup::Bool=false)
  • Defines the output type when two different types are multiplied. T1 and T2:

    mul_promote_type(::Type{T1}, ::Type{T2}, hdlsetup::Bool=false)
  • Defines the output type when dividing a single type value T:

    div_promote_type(::Type{T}, hdlsetup::Bool=false)
  • Defines the output type when dividing the type value T1 on the type value T2:

    div_promote_type(::Type{T1}, ::Type{T2}, hdlsetup::Bool=false)

    Here hdlsetup — a logical parameter that determines whether to take into account the specifics of the hardware platform (TargetHardware). By default:

    hdlsetup = TargetHardware == "C" ? false : true

The functions are convenient to use in a cell Types inheritance method to accurately determine the output block type based on inputs and parameters.

Usage example

Details

Below is an example of a component that implements the expression a*x + b. The types of intermediate multiplication and final addition are set using the parameters MulType and SumType. If specified "Inherit", then the type is output automatically.

engee function example 3

Parameters Engee Function:

a = fi(5, 1, 16, 4)
b = fi(11, 1, 24, 7)
MulType = "Inherit"
SumType = "Inherit"

The block code (in the cell Common code, Dimensions inheritance method and Types inheritance method):

  • Cell Common code:

    struct Block{SumType, MulType, aType, bType} <: AbstractCausalComponent
        a::aType
        b::bType
        function Block()
            sum_type = OUTPUT_SIGNAL_ATTRIBUTES[1].type
            mul_type = if MulType == "Inherit"
                mul_promote_type(INPUT_SIGNAL_ATTRIBUTES[1].type, eltype(a))
            else
                MulType
            end
            new{sum_type, mul_type, typeof(a), typeof(b)}(a, b)
        end
    end
  • Cell Dimensions inheritance method:

    function propagate_dimensions(inputs_dimensions::Vector{<:Dimensions})::Vector{<:Dimensions}
        input_dimensions = first(inputs_dimensions)
        mock_input = zeros(input_dimensions)
        mock_output = mock_input .* a .* b
        return [size(mock_output)]
    end
  • Cell Types inheritance method:

    function propagate_types(inputs_types::Vector{DataType})::Vector{DataType}
        mul_type = if MulType == "Inherit"
            mul_promote_type(inputs_types[1], eltype(a))
        else
            MulType
        end
        sum_type = if SumType == "Inherit"
            sum_promote_type(mul_type, eltype(b))
        else
            SumType
        end
        return [sum_type]
    end

Thus, the functions econvert, esum, emul, ediv And the rules promote_type they allow you to control the types and behavior of calculations within a block. Engee Function. They support all major numeric types, including fixed point and complex values, and can be used in calculations, comparisons, inheritance logic, and component behavior tuning using a block. Engee Function.

Diagnostic functions warning, stop_simulation, pause_simulation, info

In the source code of the block Engee Function functions are also available warning, stop_simulation, pause_simulation and info, which allow you to interact with the diagnostic system of the model at the simulation stage and display messages in diagnostic window model diagnosis main. These functions can be used inside the following source code cells:

  • Component struct code

  • Step method code

  • Update method code

  • Terminate method code

  • Common code

Functions warning, stop_simulation, pause_simulation, info it can be used only in those parts of the code that are executed during the simulation of the model.

So, although these functions are formally valid in a cell Component struct code in practice, their placement there does not make sense: this cell is intended solely to describe the block structure, and not for executable logic. In order for diagnostic functions to work correctly, they must be placed inside supported simulation methods.: Step method code, Update method code, Terminate method code or in Common code if the corresponding method is redefined there. Example of correct placement:

  • Component struct code:

    #
    struct Block <: AbstractCausalComponent
        g::Real
        function Block()
            new(gain)
        end
    end
  • Common code, the functor function (c::Block)(t::Real, in1) taken from Step method code, the method itself is disabled:

    function (c::Block)(t::Real, in1)
        if t == 5.0
            warning("time == $t")
        end
        return c.g .* in1
    end

By analogy, you can override any supported method called during the simulation (for example, update!, terminate, step), in the cell Common code manually, if you disable the corresponding standard cell (for example, Define update method, Define step method, etc.).

For correct operation in the cell Common code functions warning, stop_simulation, pause_simulation and info can be used:

  • Inside functions that redefine the behavior of methods responsible for executing the model, such as step, update!, terminate!. For example, if the functor is usually specified in a cell Step method code, defined in Common code, then the diagnostic functions inside it will work correctly.

  • Inside auxiliary functions that are called from the methods responsible for executing the model. That is, if the auxiliary function is defined in Common code and it is used, for example, in step, then calls to diagnostic functions inside it will also be executed correctly.

    Redefinition update! or other methods do not replace the required implementation. step (function (c::Block)(t, in…​)). Engee requires this function as an entry point to the simulation.

An example of a correct redefinition update! in Common code:

# Структура с Component struct code (если отключена, то должна быть обязательно вынесена в Common code)
struct Block <: AbstractCausalComponent
    g::Real
    function Block()
        new(gain)
    end
end

# Step method (обязательный метод, вызываемый на каждом шаге симуляции)
function (c::Block)(t::Real, in1)
    c = update!(c, t, in1)
    return c.g .* in1
end

# Переопределенный метод update!
function update!(c::Block, t::Real, in1)
    if t == 5.0
        info("update triggered at t = $t")  # диагностическое сообщение
    end
    return c
end

As a result, the following messages will be received in the diagnostic window of the model:

engee function continue 1

Next, let’s take a closer look at the diagnostic functions themselves.:

  • warning — function warning(msg::String) displays a warning message. The simulation continues. This can be useful for pointing out non-critical issues or conditions that require attention but do not stop execution. Example:

    if t == 5.0 || t == 7.5
        warning("time == $t")
    end
  • stop_simulation — function stop_simulation(msg::String) immediately ends the simulation and displays a completion message. It is used to indicate a critical condition under which continued modeling is impossible or undesirable. Example:

    if t == 5.0
        stop_simulation("time == $t")
    end
  • pause_simulation — function pause_simulation(msg::String) pauses the simulation and displays the specified pause message. The simulation can be resumed manually using the button Continue:

    engee function continue

    This function can be useful for analyzing the state of the model at a given time. Example:

    if t == 5.0
        pause_simulation("time == $t")
    end
  • info — function info(msg::String) displays an information message. It is used to display intermediate values without affecting the execution of the simulation. Example:

    if t == 5.0 || t == 7.5
        info("time == $t")
    end

Sample code

This example shows a simplified implementation of the block. Discrete-Time Integrator based on the integration of Julia code into the model Engee. The direct Euler method is chosen as the integration method. On the tab Advanced the block Engee Function set the value Discrete for the parameter Sample time inheritance method. Next, fill in the source code cells as follows:

  • In the cell Common code:

    mutable struct Block{T} <: AbstractCausalComponent
        const dt::Float64
        state::T
        gain::Float64
    
        function Block()
            dt = OUTPUT_SIGNAL_ATTRIBUTES[1].sample_time.period
            state = initial_condition
            gain = k
            new{typeof(state)}(dt, state, gain)
        end
    end
  • In the cell Step method code:

        return c.state
  • In the cell Update method code:

        c.state += in1 * c.dt * c.gain
        return c

As a result, the following source code will be obtained:

engee function example 1

Parameters initial_condition and k they are initialized on the Parameters tab of the block settings Engee Function:

engee function example 2

At the first step of the model simulation, the internal state of the block is c.state initialized by the parameter value initial_condition.

Then, at each calculation step, the block returns the internal state. c.state as an output signal and recalculates its value in the method update!.

The component structure is redefined in the cell Common code, and not in Component struct code, since a more flexible definition is required: the structure must be mutable and parameterized by the type T corresponding to the type of state. The standard definition in Component struct code It is suitable only for immutable and nonparametrized structures.

Advanced usage

Block Engee Function allows you to set the behavior of a component using your own code without having to assemble it from ready-made blocks. This makes it possible to manually control the types and dimensions of the output signals, use caching to improve performance, disable direct transfer of inputs to outputs, and set your own data update period.

So, on the page posted in Community Engee by link provides practical examples of advanced usage of the block Engee Function:

  • Converting input data into a vector with redefinition of output parameters;

  • Using caching and strongly typed structures to improve performance;

  • Implementation of blocks without direct transmission (Direct feedthrough) for breaking algebraic loops;

  • Setting the user sampling period of the output signal.