Engee documentation
Notebook

Construction of Lissajous curves

Modelling basic mathematical objects helps to better understand the workings of the processes that generate them. In this example, we will study two ways of building a model that creates Lissajous figures, as well as set up these models and compare their operation using the software control commands described on the documentation page "Software Control Modelling".

Process being modelled

Lissajous figures are tractories drawn by a point making two harmonic oscillations simultaneously. They are usually generated using a system of two trigonometric functions. The dependence of coordinates $x$ and $y$ on time $t$ has the following form:

$$\left\{\begin{align*} x(t) &= A \, sin(\alpha t + \beta) \\ y(t) &= B \, sin(\beta t) \end{align*}\right.$$

here $A, B$ are the amplitudes of each harmonic process (we will have them equal to 1)

$\alpha, \beta$ - frequencies of both processes,

$\delta$ - the displacement between the harmonic processes.

Model created from sinusoid generators

Our first model will consist of two blocks Sine Wave.

lissajous_curve_blocks_im.png

Model defined as a code

The second model will consist of one block Engee Function and several blocks Constant, specifying the system parameters of interest.

lissajous_curve_function_im.png

In both cases the output parameters are the signals x and y, and they are marked as logged signals. This allows us to read them when we run the simulation programmatically.

Loading models

The first thing to do is to get control of these models. When a model is already open (e.g. on the canvas), one command is used for this, when it needs to be loaded, another command is needed. Let's get the list of loaded models:

In [ ]:
all_model_names = [m.name for m in engee.get_all_models()]
Out[0]:
1-element Vector{String}:
 "engee_function_moving_average"

If the model is already open, we'll use the function open to get control on the days. If not, we use the function load.

In [ ]:
# Модель, состоящая из блоков
if "lissajous_curve_blocks" in all_model_names m1 = engee.open( "lissajous_curve_blocks" )
else m1 = engee.load( "/user/start/examples/edu/lissajous_curves/lissajous_curve_blocks.engee" )
end;

# Модель, состоящая из кода
if "lissajous_curve_function" in all_model_names m2 = engee.open( "lissajous_curve_function" )
else m2 = engee.load( "/user/start/examples/edu/lissajous_curves/lissajous_curve_function.engee" )
end;

Now we have two objects, m1 and m2, through which we can control the parameters and operation of both models we are studying.

Synchronising the parameters of the models

The ratio of the parameters $\alpha$ and $\beta$ determines the shape of the curve (number of lobes). Now we will set both models to the same parameters using the software control commands. We want the models to use the following parameters:

In [ ]:
alpha = 3;
beta = 4;

Let's study the parameter structure of the Sine Wave block of the model lissajous_curve_blocks.

In [ ]:
param = engee.get_param( "lissajous_curve_blocks/Sine Wave" )
Out[0]:
BlockParameters(
  SineType => Time based,
  Amplitude => 1,
  Bias => 0.0,
  TimeSource => Use simulation time,
  Frequency => 4,
  Phase => 0,
  SampleTime => 0.0,
)

This is a structure of type Pair, which can be changed and passed as a whole as new parameters of block lissajous_curve_blocks/Sine Wave:

In [ ]:
param.Frequency = alpha
engee.set_param!( "lissajous_curve_blocks/Sine Wave", param )

A shorter syntax can also be used. Let's do the same for the second block and for the second model:

In [ ]:
engee.set_param!( "lissajous_curve_blocks/Sine Wave-1", "Frequency"=>beta )
engee.set_param!( "lissajous_curve_function/Constant", "Value"=>alpha )
engee.set_param!( "lissajous_curve_function/Constant-1", "Value"=>beta )

The structure of the simulation parameters can be obtained with the command engee.get_param( "lissajous_curve_function" ).

Set both models to the same simulation step:

In [ ]:
param = engee.get_param( "lissajous_curve_blocks" )
Out[0]:
ModelParameters(
  :EnableMultiTasking => false
  :GenerateComments => true

  #Параметры интегратора:
  :StartTime => 0.0
  :StopTime => 10
  :SolverType => fixed-step
  :SolverName => Euler
  :FixedStep => 0.01
)
In [ ]:
# Установим обеим моделям временной шаг 0.001
engee.set_param!( "lissajous_curve_function", "FixedStep"=>0.001 )
engee.set_param!( "lissajous_curve_blocks", "FixedStep"=>0.001 )

We can now execute these models using the command run:

In [ ]:
r1 = engee.run( m1; verbose=false );

# Иногда нужно дождаться, пока модель закончит выполнение
# while( engee.get_status() != Engee.Types.READY ) end;
In [ ]:
r2 = engee.run( m2; verbose=false );

The output we get is a structure Dict (dictionary) which contains a set of tables DataFrame. Each table has columns vector time and value, corresponding to the time and output value of each stored model signal.

Modelling results

The output curves can be easily compared using a graph:

In [ ]:
using Plots
plot( r1["x"].value, r1["y"].value, lc=:red, lw=10, label="Блоки Sine Wave" )
plot!( r2["x"].value, r2["y"].value, lc=:orange, lw=4, label="Блоки Engee Function" )
Out[0]:
No description has been provided for this image

Conclusions

We have studied the operation of two models of one of the basic mathematical objects, which is considered in a wide variety of technical disciplines - from physics to circuitry. We have also applied the program control mechanism by setting the models to the same parameters and analysed the simulation results on a graph without leaving the script.

Blocks used in example