Engee documentation
Notebook

We study solvers with variable pitch

In this example, we will analyze how the choice of a solver affects the calculation of the trajectory of the Foucault pendulum using a mathematical model. We will demonstrate the work of the solvers DP5 and Tsit5 (ode45), Rodas4 (ode15s), BS3 (ode23) and Trapezoid (ode23t). The names of similar solvers adopted in Simulink are given in parentheses.

The solution of the problem will be given in the form of differential equations having the features of a rigid system. Explicit integration methods (using data from pairs of neighboring points to find the derivative) are poorly suited for such systems. An incomparably better result is usually given by implicit methods that find a solution based on the data of several neighboring points from the desired one. The definition of rigid systems is rather vague, but there is one common feature of their behavior – they may require a very small integration step, without which calculations are unstable (for example, the result goes to infinity).

Rigidity is often inherent in systems of equations consisting of two components, the state of which changes at different rates: very fast and very slow.

Foucault's Pendulum

Foucault's pendulum model is an example of a rigid system. The pendulum oscillates once every few seconds (* a rapidly changing component*), but it is also affected by the rotation of the Earth (* a slowly changing component*), the frequency of which is calculated in days, years, etc. The movement of the pendulum is dictated by inertia and gravity. And the plane in which the oscillations occur rotates synchronously with the daily rotation of the Earth.

Let's define the model through two differential equations that describe the motion of the load at the end of the pendulum in the x-y plane (top view). We will evaluate the work of different solvers by studying the law of conservation of the total energy of the system.

The model does not take into account friction and air resistance, so the equations of motion look very concise.:

where – the angular velocity of the Earth's rotation around the axis, – geographical latitude, – acceleration of free fall, and – coordinates of the center of mass of the load at the end of the pendulum.

In [ ]:
Pkg.add(["Measures", "Serialization"])
In [ ]:
using Plots
gr( fmt=:png )

# Параметры модели и начальные условия
Ω = 2*pi/86400;    # Угловая скорость вращения Земли вокруг оси (рад/с)
λ = 59.93/180*pi;  # Широта (рад)
g = 9.8195;        # Ускорение свободного падения (м/с^2)
L = 97;            # Длина маятника (м)

initial_x = L/100; # Начальное значение координаты x (м)
initial_y = 0;     # Начальное значение координаты y (м)
initial_xdot = 0;  # Начальное значение скорости по оси x (м/с)
initial_ydot = 0;  # Начальное значение скорости по оси y (м/с)

TotalEnergy = 1/2*(initial_xdot^2 + initial_ydot^2) + g*(L-sqrt( L^2-initial_x^2-initial_y^2));

The model that implements these equations is shown below.:

image.png

Variable-pitch solvers

We are going to compare the following integrators (solvers):

Name (Julia) Name (Simulink) Type of tasks Accuracy When to use it
DP5, but better Tsit5 ode45 Not rigid Average Default. Try it first
BS3 ode23 Not rigid Low If large errors are allowed in the task or for medium-hard systems
VCABM, but better Vern7 ode113 Not rigid Low to high Low error, suitable for high-volume computing tasks
QNDF, FBDF, but better Rodas4, KenCarp4, TRBDF2, RadauIIA5 ode15s Hard From Low to medium If ode45 it works too slowly due to the rigidity of the task
Rosenbrock23, but better Rodas4 ode23s Hard Low With low accuracy requirements for solving difficult tasks and with a constant matrix масс
Trapezoid ode23t Moderately hard Low For moderately harsh tasks, if a solution without numerical damping is needed
TRBDF2 ode23tb Hard Low With low requirements for the accuracy of the solution, for solving tough tasks

If you don't find the right solver when solving problems, and you're more familiar with the names of Simulink solvers, pay attention to the comparison from [this article](https://docs.sciml.ai/DiffEqDocs/stable/solvers/ode_solve /). Engee mainly uses the names of solvers accepted in Julia packages.

Comparison criteria

The quality of the solution of each integration algorithm can be assessed in several ways. First, if the problem has an analytical solution, then you can compare the result of numerical integration with the theoretical result. You can also estimate the time it takes for an algorithm to solve a problem and use the time cost as a basis for comparison.

Unfortunately, there is no exact analytical solution for Foucault's pendulum, and the approximate ones have their own inherent errors.

Saving the total energy of the system

We will evaluate the quality of each solver's solution by checking compliance with the law of conservation of energy in the system.

In the absence of friction, the total energy of the pendulum must remain constant. Numerical solutions will not show us the ideal conservation of energy due to the accumulation of errors.

So, in the example, we will calculate the total energy of the system at each integration step. Ideally, the total energy should not change at all and the difference should be zero. Total energy is the sum of potential and kinetic energy. This is exactly the value that the block calculates. NormalizeEnergy:

where – total energy at the initial moment of time.

The change in the total energy from the moment of the beginning to the end of the calculations will allow us to evaluate the quality of the numerical solution.

In [ ]:
modelName = "foucault_pendulum_model"
model = modelName in [m.name for m in engee.get_all_models()] ? engee.open( modelName ) : engee.load( "$(@__DIR__)/$modelName.engee" );

engee.set_param!( modelName, "SolverName"=>"BS3", "RelTol"=>1e-5 ) # Установим решатель, аналогичный ode23
dt_ode23 = @elapsed begin x_ode23 = engee.run( modelName ); end;

engee.set_param!( modelName, "SolverName"=>"Trapezoid", "RelTol"=>1e-5 ) # Установим решатель, аналогичный ode23t
dt_ode23t = @elapsed begin x_ode23t = engee.run( modelName ); end;

Let's compare two integrators by this parameter.:

  • BS3 (ode23) is a rather crude algorithm unsuitable for rigid systems,
  • Trapezoid (ode23t) is also a rather crude algorithm, but it is suitable for rigid systems.
In [ ]:
using Plots

plot( x_ode23["nE"].time, x_ode23["nE"].value, label="ode23", title="Сравнение качества решений при RelTol = 1e-5", lw=2 )
plot!( x_ode23t["nE"].time, x_ode23t["nE"].value, ylimits=(0,1.1), label="ode23t", leg=:bottomleft, lw=2 )
plot!( xlabel="Время (с)", ylabel="Относительная полная энергия",
       titlefont=11, guidefontsize=10, tickfontsize=9, legendfontsize=10 )
Out[0]:
No description has been provided for this image

As it is easy to see, the "losses" of total energy associated with inaccuracies in the integrator are almost imperceptible in the first case, and about 20% per day in the second case.

As the total energy of the pendulum decreases, the oscillation amplitude decreases, as we will see by plotting the following graphs. We will also see that the plane of oscillation of the pendulum changes with time (unfolds).

In [ ]:
using Measures

bar_w = 150 # "Толщина" линий фиксирующих плоскость колебания маятника в разные моменты

lastPlane_y = maximum( x_ode23["y"].value[end-bar_w:end] )
lastPlane_x = maximum( x_ode23["x"].value[end-bar_w:end] )
plot( x_ode23["x"].value[1:2:end], x_ode23["y"].value[1:2:end],
      st=:scatter, ms=.8, markerstrokecolor=:blue, markercolor=:blue, 
      title="Положение груза маятника Фуко (вид сверху)\nРешатель: ode23; RelTol = 1e-5; Время выполнения=$(round(dt_ode23,digits=2)) с",
      titlefont=9, guidefontsize=8, tickfontsize=8, leg=:none, xlabel="Координата x (м)", ylabel="Координата y (м)",
      left_margin=13mm, bottom_margin=13mm, top_margin=5mm, aspect_ratio = :equal )
scatter!( [initial_x], [0], mc=:black, ms=8 )                                                # Черный шар
plot!( x_ode23["x"].value[1:bar_w], x_ode23["y"].value[1:bar_w], lc=:black, lw=3 )           # Черная линия
plot!( x_ode23["x"].value[end-bar_w:end], x_ode23["y"].value[end-bar_w:end], lc=:red, lw=3 )

vlen = length(x_ode23["x"].value)
half_vec = ceil(Int32, vlen/2);
plot!( x_ode23["x"].value[(half_vec-bar_w):half_vec], x_ode23["y"].value[(half_vec-bar_w):half_vec], lc=:yellow, lw=3 )

annotate!( initial_x, 0.05, text("Начальное положение", :black, rotation = 90, 7, :left) )
plot!( [lastPlane_x-0.1, lastPlane_x-0.8], [lastPlane_y, lastPlane_y], lw=13, lc=:white ) # Белая линия
p1 = annotate!( lastPlane_x-0.1, lastPlane_y, text("Конечная плоскость", :red, 7, :right) )

plot( x_ode23t["x"].value[1:2:end], x_ode23t["y"].value[1:2:end], st=:scatter, ms=.8, markerstrokecolor=:blue, markercolor=:blue,
          title="Положение груза маятника Фуко (вид сверху)\nРешатель: ode23t; RelTol = 1e-5; Время выполнения=$(round(dt_ode23t,digits=2)) с", 
          titlefont=9, guidefontsize=8, tickfontsize=8, leg=:none, xlabel="Координата x (м)", ylabel="Координата y (м)",
          left_margin=13mm, bottom_margin=13mm, top_margin=5mm, aspect_ratio = :equal )
plot!( x_ode23t["x"].value[1:bar_w], x_ode23t["y"].value[1:bar_w], lc=:black, lw=3 )           # Черная линия
plot!( x_ode23t["x"].value[end-bar_w:end], x_ode23t["y"].value[end-bar_w:end], lc=:red, lw=3 )

vlen = length(x_ode23t["x"].value)
half_vec = ceil(Int32, vlen/2);
plot!( x_ode23t["x"].value[(half_vec-bar_w):half_vec], x_ode23t["y"].value[(half_vec-bar_w):half_vec], lc=:yellow, lw=3 )

p2 = plot!( [initial_x], [0], mc=:black, st=:scatter, ms=8 )

plot( p1, p2, size = (1000,450) )
Out[0]:
No description has been provided for this image

We have observed how the quality of the solution for rigid and non-rigid systems depends on the choice of a solver. The plane in which the pendulum oscillates does not make a complete turn during the day: the angle of rotation depends on the geographical latitude.

Let's build a graph that will allow us to study in detail the speed and quality of work of different solvers on the Foucault pendulum simulation problem. We will compare the results for different numerical integration methods and see how the energy of the system is conserved at different relative error values.

It may take several minutes to execute this script, so to save time when running the script again, you have the option to download data from a previously saved file. results.bin.

In [ ]:
using Serialization

# Чтобы обновить результаты, удалите файл "results"
if !isfile( "$(@__DIR__)/results.bin" )

    solvers = ["Tsit5" "DP5" "Rosenbrock23" "Trapezoid" "BS3"]
    reltols = 10 .^( collect(range(-7,-3,length=9)) )

    nE_results = zeros( length(solvers), length(reltols) )
    dt_results = zeros( length(solvers), length(reltols) )

    # Изучим все комбинации (решатель; относительная точность)
    for (i,solver) in enumerate(solvers)
        print( solver, ": ")
        for (j,reltol) in enumerate(reltols)
            print( round(reltol, sigdigits=3), ", " )
            # Установим для модели новый решатель и относительную точность
            engee.set_param!( modelName, "SolverName"=>solver, "RelTol"=>reltol )
            # Вычисляем модель и заодно измеряем время
            dt_results[i,j] = @elapsed begin sim_data = engee.run( modelName ); end;
            # Вычисляем соотношение 
            nE_results[i,j] = abs( sim_data["nE"].value[1] - sim_data["nE"].value[end] )
        end
        println()
    end
    
    serialize( "$(@__DIR__)/results.bin", (solvers, reltols, nE_results, dt_results) )
else
    (solvers, reltols, nE_results, dt_results) = deserialize( "$(@__DIR__)/results.bin" );
end
Out[0]:
(["Tsit5" "DP5" … "Trapezoid" "BS3"], [1.0000000000000001e-7, 3.162277660168379e-7, 1.0e-6, 3.162277660168379e-6, 1.0e-5, 3.1622776601683795e-5, 0.0001, 0.00031622776601683794, 0.001], [7.929647437576737e-5 7.993461600175333e-5 … 0.0008342080580365785 0.0010495257229566901; 0.006522783592120329 0.006803880028364184 … 0.0432470795415133 0.04324716090734526; … ; 0.00020914013837636247 0.0002237451074058594 … 0.008214673902898095 0.01263204365201287; 0.07957109906963433 0.08391745090197478 … 0.8201637378884246 0.957540689453349], [25.203170908 25.070636927 … 24.543856697 24.405600441; 24.731999073 24.538390433 … 23.893544084 24.32454816; … ; 30.82383809 31.923476917 … 24.9366935 24.482693973; 28.296808969 28.368053586 … 25.189954714 24.714920212])

Now we can build and analyze the graph.

In [ ]:
markerShapes_ = [ :circle :rect :star5 :diamond :hexagon ]
markerStrokeColors_ = [1 2 3 4 5]

plot(
      plot( reltols, nE_results', xscale=:log10, yscale=:log10,
            label=solvers, ylabel="Макс. относительная\nошибка полной энергии",
            titlefont=11, guidefontsize=8, tickfontsize=9, legendfontsize=7, left_margin=13mm, legend=:bottomleft,
            marker=markerShapes_, markerstrokecolor=markerStrokeColors_, markercolor=RGBA(1,1,1,0) ),
      plot( reltols, dt_results', xscale=:log10,
            label=solvers, ylabel="Время выполнения\nмодели (с)", xlabel="Относительная точность алгоритма численного интегрирования",
            titlefont=11, guidefontsize=8, tickfontsize=9, legendfontsize=7, left_margin=13mm, bottom_margin=13mm,
            marker=markerShapes_, markerstrokecolor=markerStrokeColors_, markercolor=RGBA(1,1,1,0) ),
      size=(800,500),
      layout=(2,1)
    )
Out[0]:
No description has been provided for this image

We see a zone where the criterion of under-forecasting the total energy of the system practically ceases to change (at relative_tolerance < 1e-6).

This is due to the behavior of a particular model. But, in general, it is worth considering that a decrease in the relative accuracy of the solver does not necessarily lead to an increase in accuracy. When we set the desired accuracy limit for the solution and choose other solver settings, we should always think about the acceptable time spent on simulation.

We are most likely satisfied with the accuracy of estimating the position of the load at the end of the pendulum by several centimeters. And it is no longer advisable to perform calculations with an accuracy of microns, since it will be very difficult to conduct an equally accurate real experiment to validate the model.

Conclusion

We have studied how the choice of a numerical method for solving the equations of a system (the choice of a solver) affects the results and course of solving a rigid system. A lot depends on the solver: after all, the algorithm should not only allow us to achieve the desired accuracy of the solution, but also be completed in a reasonable time.

The relative accuracy of the variable-step solver (as well as the integration step of the constant-step solver) should be small enough so that the solution does not become unstable.

All solvers have their advantages and disadvantages, but some obviously work better when solving tough problems. And variable-step solvers are generally more stable than constant-step solvers.

Blocks used in example