Skip to content

Parametrically driven resonator

One of the most famous effects displaced by nonlinear oscillators is parametric resonance, where the frequency of the linear resonator is modulated in time Phys. Rev. E 94, 022201 (2016). In the following we analyse this system, governed by the equations

x¨(t)+γx˙(t)+Ω2(1λcos(2ωt+ψ))x+αx3+ηx2x˙+Fd(t)=0

where for completeness we also considered an external drive term Fd(t)=Fcos(ωt+θ) and a nonlinear damping term ηx2x˙

To implement this system in Harmonic Balance, we first import the library

julia
using HarmonicBalance

Subsequently, we type define parameters in the problem and the oscillating amplitude function x(t) using the variables macro from Symbolics.jl

julia
@variables ω₀ γ λ F η α ω t x(t)

natural_equation =
    d(d(x, t), t) +
    γ * d(x, t) +
    (ω₀^2 - λ * cos(2 * ω * t)) * x +
    α * x^3 +
    η * d(x, t) * x^2
forces = F * cos* t)
diff_eq = DifferentialEquation(natural_equation + forces, x)
System of 1 differential equations
Variables:       x(t)
Harmonic ansatz: x(t) => ;   

Differential(t)(Differential(t)(x(t))) + F*cos(t*ω) + Differential(t)(x(t))*γ + x(t)*(-cos(2t*ω)*λ + ω₀^2) + (x(t)^3)*α + (x(t)^2)*Differential(t)(x(t))*η ~ 0

Note that an equation of the form

mx¨+mω02(1λcos(2ωt+ψ))x+γx˙+αx3+ηx2x˙=Fcosωt

can be brought to dimensionless form by rescaling the units as described in Phys. Rev. E 94, 022201 (2016).

We are interested in studying the response of the oscillator to parametric driving and forcing. In particular, we focus on the first parametric resonance of the system, i.e. operating around twice the bare frequency of the undriven oscillator ω while the frequency of the external drive is also ω. For this purpose, we consider a harmonic ansatz which contains a single frequency: x(t)ucos(ωt)+vsin(ωt). In HarmonicBalance, we can do this via add_harmonic command:

julia
add_harmonic!(diff_eq, x, ω);

and replacing this by the time independent (averaged) equations of motion. This can be simply done by writing

julia
harmonic_eq = get_harmonic_equations(diff_eq)
A set of 2 harmonic equations
Variables: u1(T), v1(T)
Parameters: ω, α, γ, ω₀, λ, F, η

Harmonic ansatz: 
x(t) = u1(T)*cos(ωt) + v1(T)*sin(ωt)

Harmonic equations:

F - (1//2)*u1(T)*λ + (2//1)*Differential(T)(v1(T))*ω + Differential(T)(u1(T))*γ - u1(T)*(ω^2) + u1(T)*(ω₀^2) + v1(T)*γ*ω + (3//4)*(u1(T)^3)*α + (3//4)*(u1(T)^2)*Differential(T)(u1(T))*η + (1//2)*u1(T)*Differential(T)(v1(T))*v1(T)*η + (3//4)*u1(T)*(v1(T)^2)*α + (1//4)*(v1(T)^2)*Differential(T)(u1(T))*η + (1//4)*(u1(T)^2)*v1(T)*η*ω + (1//4)*(v1(T)^3)*η*ω ~ 0

Differential(T)(v1(T))*γ + (1//2)*v1(T)*λ - (2//1)*Differential(T)(u1(T))*ω - u1(T)*γ*ω - v1(T)*(ω^2) + v1(T)*(ω₀^2) + (1//4)*(u1(T)^2)*Differential(T)(v1(T))*η + (3//4)*(u1(T)^2)*v1(T)*α + (1//2)*u1(T)*v1(T)*Differential(T)(u1(T))*η + (3//4)*Differential(T)(v1(T))*(v1(T)^2)*η + (3//4)*(v1(T)^3)*α - (1//4)*(u1(T)^3)*η*ω - (1//4)*u1(T)*(v1(T)^2)*η*ω ~ 0

The output of these equations are consistent with the result found in the literature. Now we are interested in the linear response spectrum, which we can obtain from the solutions to the averaged equations (rotating frame) as a function of the external drive, after fixing all other parameters in the system. A call to get_steady_states then retrieves all steadystates found allong the sweep employing the homotopy continuation method, which occurs in a complex space (see the nice HomotopyContinuation.jl docs)

1D parameters

We start with a varied set containing one parameter, ω,

julia
fixed = (ω₀ => 1.0, γ => 1e-2, λ => 5e-2, F => 1e-3, α => 1.0, η => 0.3)
varied = ω => range(0.9, 1.1, 100)

result = get_steady_states(harmonic_eq, varied, fixed)
A steady state result for 100 parameter points

Solution branches:   5
   of which real:    5
   of which stable:  3

Classes: stable, physical, Hopf, binary_labels

In get_steady_states, the default value for the keyword method=:random_warmup initiates the homotopy in a generalised version of the harmonic equations, where parameters become random complex numbers. A parameter homotopy then follows to each of the frequency values ω in sweep. This offers speed-up, but requires to be tested in each scenario againts the method :total_degree, which initializes the homotopy in a total degree system (maximum number of roots), but needs to track significantly more homotopy paths and there is slower. The threading keyword enables parallel tracking of homotopy paths, and it's set to false simply because we are using a single core computer for now.

After solving the system, we can save the full output of the simulation and the model (e.g. symbolic expressions for the harmonic equations) into a file

julia
HarmonicBalance.save("parametron_result.jld2", result);
┌ Warning: Attempting to store typeof(cos).
│  Function types cannot be propertly stored in JLD2 files.
│  Loading may yield unexpected results.
└ @ JLD2 ~/.julia/packages/JLD2/JHhTf/src/data/writing_datatypes.jl:631
┌ Warning: Attempting to store typeof(cos).
│  Function types cannot be propertly stored in JLD2 files.
│  Loading may yield unexpected results.
└ @ JLD2 ~/.julia/packages/JLD2/JHhTf/src/data/writing_datatypes.jl:631
┌ Warning: Attempting to store typeof(cos).
│  Function types cannot be propertly stored in JLD2 files.
│  Loading may yield unexpected results.
└ @ JLD2 ~/.julia/packages/JLD2/JHhTf/src/data/writing_datatypes.jl:631
┌ Warning: Attempting to store typeof(cos).
│  Function types cannot be propertly stored in JLD2 files.
│  Loading may yield unexpected results.
└ @ JLD2 ~/.julia/packages/JLD2/JHhTf/src/data/writing_datatypes.jl:631
┌ Warning: Attempting to store typeof(cos).
│  Function types cannot be propertly stored in JLD2 files.
│  Loading may yield unexpected results.
└ @ JLD2 ~/.julia/packages/JLD2/JHhTf/src/data/writing_datatypes.jl:631
┌ Warning: Attempting to store typeof(cos).
│  Function types cannot be propertly stored in JLD2 files.
│  Loading may yield unexpected results.
└ @ JLD2 ~/.julia/packages/JLD2/JHhTf/src/data/writing_datatypes.jl:631
┌ Warning: Attempting to store typeof(identity).
│  Function types cannot be propertly stored in JLD2 files.
│  Loading may yield unexpected results.
└ @ JLD2 ~/.julia/packages/JLD2/JHhTf/src/data/writing_datatypes.jl:631
┌ Warning: Attempting to store typeof(identity).
│  Function types cannot be propertly stored in JLD2 files.
│  Loading may yield unexpected results.
└ @ JLD2 ~/.julia/packages/JLD2/JHhTf/src/data/writing_datatypes.jl:631

During the execution of get_steady_states, different solution branches are classified by their proximity in complex space, with subsequent filtering of real (physically accceptable solutions). In addition, the stability properties of each steady state is assesed from the eigenvalues of the Jacobian matrix. All this information can be succintly represented in a 1D plot via

julia
plot(result; x="ω", y="sqrt(u1^2 + v1^2)")

The user can also introduce custom clases based on parameter conditions via classify_solutions!. Plots can be overlaid and use keywords from Plots, MarkdownAST.LineBreak()

julia
classify_solutions!(result, "sqrt(u1^2 + v1^2) > 0.1", "large")
plot(result, "sqrt(u1^2 + v1^2)"; class=["physical", "large"], style=:dash)
plot!(result, "sqrt(u1^2 + v1^2)"; not_class="large")

Alternatively, we may visualise all underlying solutions, including complex ones,

julia
plot(result, "sqrt(u1^2 + v1^2)"; class="all")

2D parameters

The parametrically driven oscillator boasts a stability diagram called "Arnold's tongues" delineating zones where the oscillator is stable from those where it is exponentially unstable (if the nonlinearity was absence). We can retrieve this diagram by calculating the steady states as a function of external detuning δ=ωLω0 and the parametric drive strength λ.

To perform a 2D sweep over driving frequency ω and parametric drive strength λ, we keep fixed from before but include 2 variables in varied

julia
varied ==> range(0.8, 1.2, 50), λ => range(0.001, 0.6, 50))
result_2D = get_steady_states(harmonic_eq, varied, fixed);

Solving for 2500 parameters...  53%|██████████▋         |  ETA: 0:00:01
  # parameters solved:  1328
  # paths tracked:      6640






Solving for 2500 parameters...  83%|████████████████▋   |  ETA: 0:00:00
  # parameters solved:  2085
  # paths tracked:      10425






Solving for 2500 parameters... 100%|████████████████████| Time: 0:00:01
  # parameters solved:  2500
  # paths tracked:      12500

Now, we count the number of solutions for each point and represent the corresponding phase diagram in parameter space. This is done using plot_phase_diagram. Only counting stable solutions,

julia
plot_phase_diagram(result_2D; class="stable")

In addition to phase diagrams, we can plot functions of the solution. The syntax is identical to 1D plotting. Let us overlay 2 branches into a single plot,

julia
# overlay branches with different colors
plot(result_2D, "sqrt(u1^2 + v1^2)"; branch=1, class="stable", camera=(60, -40))
plot!(result_2D, "sqrt(u1^2 + v1^2)"; branch=2, class="stable", color=:red)

Note that solutions are ordered in parameter space according to their closest neighbors. Plots can again be limited to a given class (e.g stable solutions only) through the keyword argument class.


This page was generated using Literate.jl.