Lagrangian Systems
The Euler-Lagrange equations, that is the dynamical equations of a Lagrangian system, are given in terms of the Lagrangian $L(x,v)$ by
\[\frac{d}{dt} \frac{\partial L}{\partial v} - \frac{\partial L}{\partial x} = 0 .\]
For regular (i.e. non-degenerate) Lagrangians, this is a set of second-order ordinary differential equations. In many numerical applications, it is advantageous to solve the implicit form of these equations, given by
\[\begin{align*} \frac{d \vartheta}{dt} &= f , & \vartheta &= \frac{\partial L}{\partial v} , & f = \frac{\partial L}{\partial x} . \end{align*}\]
In the following, we show how these equations can be obtained for the example of a particle in a square potential.
Particle in a potential
Before any use, we need to load EulerLagrange:
using EulerLagrangeNext, we generate symbolic variables for a two-dimensional system:
t, x, v = lagrangian_variables(2)(t, (x(t))[1:2], (v(t))[1:2])With those variables, we can construct a Lagrangian
using LinearAlgebra
L = v ⋅ v / 2 - x ⋅ x / 2\[ \begin{equation} \frac{1}{2} \left( v\_{1}\left( t \right)^{2} + v\_{2}\left( t \right)^{2} \right) - \frac{1}{2} \left( x\_{1}\left( t \right)^{2} + x\_{2}\left( t \right)^{2} \right) \end{equation} \]
This Lagrangian together with the symbolic variables is then used to construct a LagrangianSystem:
lag_sys = LagrangianSystem(L, t, x, v)
Lagrangian system with
L = (1//2)*((v(t))[1]^2 + (v(t))[2]^2) - (1//2)*((x(t))[1]^2 + (x(t))[2]^2)The constructor computes the Euler-Lagrange equations and generates the corresponding Julia code. In the last step, we can now construct a LODEProblem from the LagrangianSystem and some appropriate initial conditions, a time span to integrate over and a time step:
tspan = (0.0, 10.0)
tstep = 0.01
q₀ = [1.0, 1.0]
p₀ = [0.5, 2.0]
lprob = LODEProblem(lag_sys, tspan, tstep, q₀, p₀)Geometric Equation Problem for Lagrangian Ordinary Differential Equation (LODE)
 with vector fields
   ϑ = RuntimeGeneratedFunctions.RuntimeGeneratedFunction{(:ˍ₋out, :t, :X, :V, :params), EulerLagrange.var"#_RGF_ModTag", EulerLagrange.var"#_RGF_ModTag", (0x600f0e62, 0xb5de2968, 0x05f3f7bb, 0xcd7da120, 0x86c52545), Expr}(quote
    #= none:1 =#
    #= none:2 =#
    #= none:2 =# @inbounds begin
            #= none:4 =#
            begin
                #= none:8 =#
                #= none:8 =# @inbounds begin
                        #= none:10 =#
                        ˍ₋out[1] = getindex(V, 1)
                        #= none:11 =#
                        ˍ₋out[2] = getindex(V, 2)
                        #= none:13 =#
                        ˍ₋out
                    end
            end
        end
end)
   f = RuntimeGeneratedFunctions.RuntimeGeneratedFunction{(:ˍ₋out, :t, :X, :V, :params), EulerLagrange.var"#_RGF_ModTag", EulerLagrange.var"#_RGF_ModTag", (0x64292ef8, 0x35dc1a36, 0xcc1401e1, 0x6d72af4b, 0x44cbf395), Expr}(quote
    #= none:1 =#
    #= none:2 =#
    #= none:2 =# @inbounds begin
            #= none:4 =#
            begin
                #= none:8 =#
                #= none:8 =# @inbounds begin
                        #= none:10 =#
                        ˍ₋out[1] = -1 // 1 * getindex(X, 1)
                        #= none:11 =#
                        ˍ₋out[2] = -1 // 1 * getindex(X, 2)
                        #= none:13 =#
                        ˍ₋out
                    end
            end
        end
end)
   g = RuntimeGeneratedFunctions.RuntimeGeneratedFunction{(:ˍ₋out, :t, :X, :Λ, :V, :params), EulerLagrange.var"#_RGF_ModTag", EulerLagrange.var"#_RGF_ModTag", (0x37822bb7, 0x13cbf0b4, 0xd4e17d90, 0x855c156c, 0x7e452673), Expr}(quote
    #= none:1 =#
    #= none:2 =#
    #= none:2 =# @inbounds begin
            #= none:4 =#
            begin
                #= none:8 =#
                #= none:8 =# @inbounds begin
                        #= none:10 =#
                        ˍ₋out[1] = (Differential(t))(getindex(V, 1))
                        #= none:11 =#
                        ˍ₋out[2] = (Differential(t))(getindex(V, 2))
                        #= none:13 =#
                        ˍ₋out
                    end
            end
        end
end)
 Lagrangian: L = RuntimeGeneratedFunctions.RuntimeGeneratedFunction{(:t, :X, :V, :params), EulerLagrange.var"#_RGF_ModTag", EulerLagrange.var"#_RGF_ModTag", (0x5f0152f9, 0x4cbdd61b, 0x7404e13b, 0x1295fb6f, 0x710ca7aa), Expr}(quote
    #= none:1 =#
    #= none:2 =#
    #= none:2 =# @inbounds begin
            #= none:4 =#
            begin
                #= none:8 =#
                1 // 2 * (getindex(V, 1) ^ 2 + getindex(V, 2) ^ 2) + -1 // 2 * (getindex(X, 1) ^ 2 + getindex(X, 2) ^ 2)
            end
        end
end)
 Invariants: 
   GeometricBase.NullInvariants()
 Timespan: (0.0, 10.0) 
 Timestep: 0.01 
 Initial conditions: 
   (t = 0.0, q = [1.0, 1.0], p = [0.5, 2.0], v = [0.0, 0.0])
 Parameters: 
   GeometricBase.NullParameters()We can integrate this system using GeometricIntegrators:
using GeometricIntegrators
sol = integrate(lprob, Gauss(1))
using CairoMakie
fig = lines(parent(sol.q[:,1]), parent(sol.q[:,2]);
    axis = (; xlabel = "x₁", ylabel = "x₂", title = "Particle moving in a square potential"),
    figure = (; size = (800,600), fontsize = 22))┌ Warning: Hermite Extrapolation: q's history[1] and history[2] are identical!
└ @ GeometricIntegrators.Extrapolators ~/.julia/packages/GeometricIntegrators/eVTaF/src/extrapolation/hermite.jl:211Parameters
We can also include parametric dependencies in the Lagrangian. Consider, for example, a parameter α that determines the strength of the potential.
The easiest way, to account for parameters, is to create a named tuple with typical values for each parameter, e.g.,
params = (α = 5.0,)(α = 5.0,)In the next step, we use the function symbolize to generate a symbolic version of the parameters:
sparams = symbolize(params)(α = αₚ,)Now we modify the Lagrangian to account for the parameter:
L = v ⋅ v / 2 - sparams.α * (x ⋅ x) / 2\[ \begin{equation} \frac{1}{2} \left( v\_{1}\left( t \right)^{2} + v\_{2}\left( t \right)^{2} \right) - \frac{1}{2} \left( x\_{1}\left( t \right)^{2} + x\_{2}\left( t \right)^{2} \right) \mathtt{\alpha{_p}} \end{equation} \]
From here on, everything follows along the same lines as before, the only difference being that we also need to pass the symbolic parameters sparams to the LagrangianSystem constructor:
lag_sys = LagrangianSystem(L, t, x, v, sparams)
Lagrangian system with
L = (1//2)*((v(t))[1]^2 + (v(t))[2]^2) - (1//2)*((x(t))[1]^2 + (x(t))[2]^2)*αₚAnalogously, we need to pass actual parameter values params to the LODEProblem constructor via the parameters keyword argument:
lprob = LODEProblem(lag_sys, tspan, tstep, q₀, p₀; parameters = params)Geometric Equation Problem for Lagrangian Ordinary Differential Equation (LODE)
 with vector fields
   ϑ = RuntimeGeneratedFunctions.RuntimeGeneratedFunction{(:ˍ₋out, :t, :X, :V, :params), EulerLagrange.var"#_RGF_ModTag", EulerLagrange.var"#_RGF_ModTag", (0x600f0e62, 0xb5de2968, 0x05f3f7bb, 0xcd7da120, 0x86c52545), Expr}(quote
    #= none:1 =#
    #= none:2 =#
    #= none:2 =# @inbounds begin
            #= none:4 =#
            begin
                #= none:8 =#
                #= none:8 =# @inbounds begin
                        #= none:10 =#
                        ˍ₋out[1] = getindex(V, 1)
                        #= none:11 =#
                        ˍ₋out[2] = getindex(V, 2)
                        #= none:13 =#
                        ˍ₋out
                    end
            end
        end
end)
   f = RuntimeGeneratedFunctions.RuntimeGeneratedFunction{(:ˍ₋out, :t, :X, :V, :params), EulerLagrange.var"#_RGF_ModTag", EulerLagrange.var"#_RGF_ModTag", (0x8dbd992d, 0x92708b83, 0x7fbb7d7e, 0x01bbcdf0, 0x178eafc7), Expr}(quote
    #= none:1 =#
    #= none:2 =#
    #= none:2 =# @inbounds begin
            #= none:4 =#
            begin
                #= none:8 =#
                #= none:8 =# @inbounds begin
                        #= none:10 =#
                        ˍ₋out[1] = (-1 // 1 * getindex(X, 1)) * params.α
                        #= none:11 =#
                        ˍ₋out[2] = (-1 // 1 * getindex(X, 2)) * params.α
                        #= none:13 =#
                        ˍ₋out
                    end
            end
        end
end)
   g = RuntimeGeneratedFunctions.RuntimeGeneratedFunction{(:ˍ₋out, :t, :X, :Λ, :V, :params), EulerLagrange.var"#_RGF_ModTag", EulerLagrange.var"#_RGF_ModTag", (0x37822bb7, 0x13cbf0b4, 0xd4e17d90, 0x855c156c, 0x7e452673), Expr}(quote
    #= none:1 =#
    #= none:2 =#
    #= none:2 =# @inbounds begin
            #= none:4 =#
            begin
                #= none:8 =#
                #= none:8 =# @inbounds begin
                        #= none:10 =#
                        ˍ₋out[1] = (Differential(t))(getindex(V, 1))
                        #= none:11 =#
                        ˍ₋out[2] = (Differential(t))(getindex(V, 2))
                        #= none:13 =#
                        ˍ₋out
                    end
            end
        end
end)
 Lagrangian: L = RuntimeGeneratedFunctions.RuntimeGeneratedFunction{(:t, :X, :V, :params), EulerLagrange.var"#_RGF_ModTag", EulerLagrange.var"#_RGF_ModTag", (0x7ea5e6bb, 0x28329de2, 0x7e03d9e6, 0xce49ded9, 0xc905b540), Expr}(quote
    #= none:1 =#
    #= none:2 =#
    #= none:2 =# @inbounds begin
            #= none:4 =#
            begin
                #= none:8 =#
                1 // 2 * (getindex(V, 1) ^ 2 + getindex(V, 2) ^ 2) + (-1 // 2 * (getindex(X, 1) ^ 2 + getindex(X, 2) ^ 2)) * params.α
            end
        end
end)
 Invariants: 
   GeometricBase.NullInvariants()
 Timespan: (0.0, 10.0) 
 Timestep: 0.01 
 Initial conditions: 
   (t = 0.0, q = [1.0, 1.0], p = [0.5, 2.0], v = [0.0, 0.0])
 Parameters: 
   (α = 5.0,)This problem can again be integrated using GeometricIntegrators:
sol = integrate(lprob, Gauss(1))
fig = lines(parent(sol.q[:,1]), parent(sol.q[:,2]);
    axis = (; xlabel = "x₁", ylabel = "x₂", title = "Particle moving in a square potential"),
    figure = (; size = (800,600), fontsize = 22))┌ Warning: Hermite Extrapolation: q's history[1] and history[2] are identical!
└ @ GeometricIntegrators.Extrapolators ~/.julia/packages/GeometricIntegrators/eVTaF/src/extrapolation/hermite.jl:211