Engee documentation
Notebook

Using Variant Source and Variant Sink blocks

In this example we will show the work of blocks Variant Source and Variant Sink. These blocks allow you to switch between different realisations of models that are generators of input data or consumers of output data of some subsystem.

Introduction

Suppose you need to calculate the performance of a model with different input sensors, different cyclograms, or different output data filtering algorithms. For such testing, you could build several separate models, the number of which would quickly become impractically large. Or you can collect all the models into one and switch conditions using variables.

In this simplified example, the model configuration will be specified by two scalar variables:

In [ ]:
Pkg.add(["Combinatorics"])
In [ ]:
V = 1;
W = 1;

The model has several signal line switches controlled by external variables.

image.png

The signal selection conditions in each block Variant Source or Variant Sink are specified using expressions.

The expression that first takes the true value determines the port number through which the data will go from or to some subsystem. Let us interpret the different variants of the systems that are realised in this diagram:

  • If W==1 is a true expression then Variant Source1 activates input Sine3, and at V==4 activates input Sine4.
  • If V==1 block Variant Source 2 skips the signal from block Sine1, and if V==2 skips the signal from block Add1.
  • If Add1 block is inactive, Variant Source 1 will also be inactive, and then Sine3 and Sine4 blocks will be disabled ( Sine3 block is active at V==2 && W==1, and Sine4 block is active at V==2 && W==2).
  • The Gain3 block is active either at V==1 or at V==2; under other conditions (for example at V=4) we will get an error because no output signal will be supplied to the output port Out1.
  • The blocks at the output Variant Sink1 become active at W==1 (Gain5) or at W==2 (Gain2, Substract, Terminator).
  • Finally, the blocks Sine6, Sum and Out2 are always active and are not subject to the system option conditions. To ensure that these blocks are only executed under certain conditions, you can precede and follow them with the blocks Variant Source/Sink both with a single input and output.

Under some combinations of conditions, this model returns an error because the output ports of a subsystem must receive an active signal regardless of the subsystem option. It is not possible to supply any block with both active and inactive signals at the same time, you must explicitly design the output ports of such a system using Variant Source/Sink blocks to set default values.

Running the scenario

In [ ]:
mName = "variant_source_sink"
model = mName in [m.name for m in engee.get_all_models()] ? engee.open( mName ) : engee.load( "$(@__DIR__)/$(mName).engee" );
data = engee.run( mName )
Out[0]:
Dict{String, DataFrames.DataFrame} with 1 entry:
  "Gain3.1" => 51×2 DataFrame
In [ ]:
plot( data["Gain3.1"].time, data["Gain3.1"].value )
Out[0]:

Running multiple scenarios

We will now run several variations of the model in a loop, switching state variables to show how the system performs in different scenarios using a combination of input conditions.

In [ ]:
using Combinatorics
In [ ]:
# Зададим все интересующие нас входные условия
vV = [1,2,4];
vW = [1,2,4];

# Загрузим модель
mName = "variant_source_sink"
model = mName in [m.name for m in engee.get_all_models()] ? engee.open( mName ) : engee.load( "$(@__DIR__)/$(mName).engee" );

# Успех выполнения мы будем хранить в матрице, которую инициализируем пропущенными значениями
run_success = NaN .* zeros(length(vV), length(vW))

# Пройдемся по всем комбинациям входных условий и проверим, выполнится ли модель
for c in combinations( vcat( vV,vW ), 2  )
    V,W = c[1],c[2]
    i = findfirst( vV.==c[1] )
    j = findfirst( vW.==c[2] )
    
    try
        # Запустим модель на выполнение
        data = engee.run( mName )
    catch e
        # Если модель выполнилась с ошибкой, запишем 0 в выходную матрицу
        run_success[i,j] = 0
    else
        # Запишем единицу только если условие catch не сработало
        run_success[i,j] = 1
    end

end

We will display a graph that will show the statistics on the runs of different variants of our model.

In [ ]:
gr()
# Вывод не интерактивной карты, отражающей успех выполнения модели
heatmap( run_success, xticks=(range(1,length(vV)), vV), yticks=(range(1,length(vW)), vW),
         xlabel = 'V', ylabel='W', title="Успех выполнения модели", yflip = true, cbar=false, c=:Blues )
# Аннотация поверх каждой ячейки
annotate!( vec(tuple.((1:length(vV))', (1:length(vW)), string.(Int32.(run_success)))) )
Out[0]:

Conclusion

We have seen that it is possible to build multiple variants of a system on a single canvas. This method simplifies testing and allows generating many code variants at once to further automate semi-natural modelling.