Linear response
In HarmonicBalance.jl, the stability and linear response are treated using the LinearResponse
module.
Here we calculate the white noise response of a simple nonlinear system. A set of reference results may be found in Huber et al. in Phys. Rev. X 10, 021066 (2020). We start by defining the Duffing oscillator
using HarmonicBalance, Plots
using Plots.Measures: mm
@variables α, ω, ω0, F, γ, t, x(t); # declare constant variables and a function x(t)
# define ODE
diff_eq = DifferentialEquation(d(x,t,2) + ω0*x + α*x^3 + γ*d(x,t) ~ F*cos(ω*t), x)
# specify the ansatz x = u(T) cos(ω*t) + v(T) sin(ω*t)
add_harmonic!(diff_eq, x, ω)
# implement ansatz to get harmonic equations
harmonic_eq = get_harmonic_equations(diff_eq)
A set of 2 harmonic equations
Variables: u1(T), v1(T)
Parameters: ω, α, γ, ω0, F
Harmonic ansatz:
x(t) = u1(T)*cos(ωt) + v1(T)*sin(ωt)
Harmonic equations:
u1(T)*ω0 + (2//1)*Differential(T)(v1(T))*ω + Differential(T)(u1(T))*γ - u1(T)*(ω^2) + v1(T)*γ*ω + (3//4)*(u1(T)^3)*α + (3//4)*u1(T)*(v1(T)^2)*α ~ F
Differential(T)(v1(T))*γ + v1(T)*ω0 - (2//1)*Differential(T)(u1(T))*ω - u1(T)*γ*ω - v1(T)*(ω^2) + (3//4)*(u1(T)^2)*v1(T)*α + (3//4)*(v1(T)^3)*α ~ 0//1
Linear regime
When driven weakly, the Duffing resonator behaves quasi-linearly, i.e, its response to noise is independent of the applied drive. We see that for weak driving,
fixed = (α => 1, ω0 => 1.0, γ => 0.005, F => 0.0001) # fixed parameters
varied = ω => range(0.95, 1.05, 100) # range of parameter values
result = get_steady_states(harmonic_eq, varied, fixed)
plot(result, "sqrt(u1^2 + v1^2)")
To find the fluctuation on the top of the steady state one often employs a Bogoliubov-de Gennes analyses. Here, we compute the eigenvalues
One can plot the eigenvalues as follows
plot(
plot_eigenvalues(result, branch=1),
plot_eigenvalues(result, branch=1, type=:real, ylims=(-0.003, 0)),
)
We find a single pair of complex conjugate eigenvalues linearly changing with the driving frequency. Both real parts are negative, indicating stability.
As discussed in background section on linear response, the excitation manifest itself as a lorentenzian peak in a power spectral density (PSD) measurement. The PSD can be plotted using plot_linear_response
:
plot_linear_response(result, x, Ω_range=range(0.95, 1.05, 300), branch=1, logscale=true)
The response has a peak at
Note the slight "bending" of the noise peak with
Nonlinear regime
For strong driving, matters get more complicated. Let us now use a drive
fixed = (α => 1, ω0 => 1.0, γ => 0.005, F => 0.002) # fixed parameters
varied = ω => range(0.95, 1.05, 100) # range of parameter values
result = get_steady_states(harmonic_eq, varied, fixed)
plot(result, x="ω", y="sqrt(u1^2 + v1^2)");
The amplitude is the well-known Duffing curve. Let's look at the eigenvalues of the two stable branches, 1 and 2.
plot(
plot_eigenvalues(result, branch=1),
plot_eigenvalues(result, branch=1, type=:real, ylims=(-0.003, 0)),
plot_eigenvalues(result, branch=2),
plot_eigenvalues(result, branch=2, type=:real, ylims=(-0.003, 0)),
)
Again every branch gives a single pair of complex conjugate eigenvalues. However, for branch 1, the characteristic frequencies due not change linearly with the driving frequency around
The same can be seen in the PSD:
plot(
plot_linear_response(result, x, branch=1, Ω_range=range(0.95,1.1,300), logscale=true),
plot_linear_response(result, x, branch=2, Ω_range=range(0.9,1.1,300), logscale=true),
size=(600, 250), margin=3mm
)
In branch 1 the linear response to white noise shows more than one peak. This is a distinctly nonlinear phenomenon, indicative if the squeezing of the steady state. Branch 2 is again quasi-linear, which stems from its low amplitude.
Following Huber et al., we may also fix
fixed = (α => 1., ω0 => 1.0, γ => 1e-2, ω => 1) # fixed parameters
swept = F => 10 .^ range(-6, -1, 200) # range of parameter values
result = get_steady_states(harmonic_eq, swept, fixed)
plot(
plot(result, "sqrt(u1^2 + v1^2)", xscale=:log),
plot_linear_response(result, x, branch=1, Ω_range=range(0.9,1.1,300), logscale=true, xscale=:log),
size=(600, 250), margin=3mm
)
We see that for low