2

I want to teach myself about solving PDEs with Julia and I am trying to solve the complex Ginzburg Landau equation (CGLE) with a pseudospectral method in Julia now. However, I struggle with it and I am a bit of ideas what to try.

The CGLE reads: cgle

With Fourier transform F and its inverse F-1, I can transform into the spectral form:

cgle-spectral

This is for example also given in this old script I found (https://www.uni-muenster.de/Physik.TP/archive/fileadmin/lehre/NumMethoden/SoSe2009/Skript/script.pdf) From the same source I know, that alpha=1, beta=2 and initial conditions with small noise of order 0.01 around 0 should result in plane waves as solutions. Thats what I want to test first.

Following the very nice tutorial from Chris Rackauckas (https://youtu.be/okGybBmihOE), I tried to use ApproxFun and DifferentialEquations in the following way to solve this problem:

EDIT: I corrected two mistakes from the original post, a missing dot and minus sign, but the code is still not giving the correct results

EDIT2: Figured out that I computed the wavenumber k completely wrong

using ApproxFun
using DifferentialEquations

F = Fourier()
n = 512
L = 100
T = ApproxFun.plan_transform(F, n)
Ti = ApproxFun.plan_itransform(F, n)
x = collect(range(-L/2,stop=L/2, length=n))
k = points(F, n)

alpha = 1im
beta = 2im
u0 = 0.01*(rand(ComplexF64, n) .- 0.5)
Fu0 = T*u0

function cgle!(du, u, p, t)
    a, b, k, T, Ti = p

    invu = Ti*u
    du .= (1.0 .- k.^2*(1.0 .+a)).*u .- T*( (1.0 .+b) .* (abs.(invu)).^2 .* invu)
end

pars = alpha, beta, k, T, Ti
prob = ODEProblem(cgle!, Fu0, (0.,50.), pars)
u = solve(prob, Rodas5(autodiff=false))

# plotting on a equidistant time stepping
t = collect(range(0, stop=50, length=1000))
sol = zeros(eltype(u),(n, length(t)))
for it in eachindex(t)
   sol[:,it] = Ti*u(t[it])
end

IM = PyPlot.imshow(real.(sol))
cb = PyPlot.colorbar(IM, orientation="horizontal")
gcf()

(edited) I tried different solvers, as also recommended in the video, some apparently wont work for complex numbers, some do, but when I run this code it does not give the expected results. The solution remain very small in value and it wont result in the plane waves that actually should be the result. I also tested other intial conditions that should result in chaos, but those result in the same very small solutions as well. I also alternativly used an explicit Laplace Operator with ApproxFun, but the results are the same. My problem here, is that I am neither really an expert with PDE mathemitacaly, nor with their numerical treatment, so far I mainly worked with ODEs.

EDIT2 This now seems to work more or less. I am still wondering about some things though

  • How can I compute this on a specified domain like x\in[-L/2;L/2], I am seriously confused about how this works with ApproxFun, as far as I can see the wavenumbers k should be (2pi/L)*[-N/2+1 ; N/2 -1], but I am not so sure about how to do this with ApproxFun
  • https://codeinthehole.com/tutorial/coherent.html shows the different dynamic regimes / phase portrait of the equation. While I can reproduce some of them, some don't seem to work, like the Spatio-temporal intermittency

EDIT 3: I solved these issues by using FFTW directly instead of ApproxFun. In case somebody knows how to this with ApproxFun, I would still be interessted though. Below follows the code with FFTW (it is also a bit more optimized for performance)

begin
   using FFTW
   using DifferentialEquations
   using PyPlot
end

begin
   n = 512
   L = 200
   n2 = Int(n/2)
   alpha = 2im
   beta = 1im
   x = range(-L/2,stop=L/2,length=n)
   u0 = 0.01*(rand(ComplexF64, n) .- 0.5)

   k = [0:n/2-1; 0; -n/2+1:-1] .*(2*pi/L);
   k2 = k.*k
   k2[n2 + 1] = (n2*(2*pi/L))^2 

   T = plan_fft(u0)
   Ti = plan_ifft(T*u0)

   LinOp = (1.0 .- k2.*(1.0 .+alpha))
   Fu0 = T*u0
end

function cgle!(du, u, p, t)
    LinOp, b, T, Ti = p

    invu = Ti*u
    du .= LinOp.*u .- T*( (1.0 .+b) .* (abs.(invu)).^2 .* invu)
end

pars = LinOp, beta, T, Ti
prob = ODEProblem(cgle!, Fu0, (0.,100.), pars)
@time u = solve(prob)

t = collect(range(0, stop=50, length=1000))
sol = zeros(eltype(u),(n, length(t)))
for it in eachindex(t)
   sol[:,it] = Ti*u(t[it])
end

IM = PyPlot.imshow(abs.(sol))
cb = PyPlot.colorbar(IM, orientation="horizontal") 
gcf()

EDIT 4: Rodas turned out to be a extremly slow solver for this case, just using the default works out nicely for me.

Any help is appreciated.

max xilian
  • 463
  • 2
  • 11
  • This looks like a case where you should use `@.` instead of dotting manually. You appear to have missed several dots, and also it looks messy, particularly in combination with the unfortunate notation `1.` instead of `1.0`, or better, just `1` – DNF Jul 03 '19 at 19:29
  • 1
    You don't want to fully `@.` here since `T*`, that `*` shouldn't be `.`'d. – Chris Rackauckas Jul 03 '19 at 20:16
  • I dont think a `.` is missing now that I ve inserted one before the `=` or am I wrong? – max xilian Jul 04 '19 at 07:47

1 Answers1

1
du = (1. .- k.^2*(1. .+(im*a))).*u + T*( (1. .+(im*b)) .* abs.(invu).^2 .* invu)

Notice that is replacing the pointer to du, not updating it. Use something like .= instead:

du .= (1. .- k.^2*(1. .+(im*a))).*u + T*( (1. .+(im*b)) .* abs.(invu).^2 .* invu)

Otherwise your derivative is just 0.

Chris Rackauckas
  • 18,645
  • 3
  • 50
  • 81
  • Argh, that was quite a stupid mistake. Thank you for your help. I also spotted another easy mistake. I corrected the code in the original post. However this doesnt change that it is still not working. The solver detects an unstability and the solutions grow very quickly, even though the correct result should be a planar wave. – max xilian Jul 04 '19 at 07:40
  • I did another mistake in mistaken a + with a - in the formula. It is now not unstable anymore, but this still does not seem to resolve the issue that the results are not correct. – max xilian Jul 04 '19 at 09:46
  • 1
    There's another error at k.^2*..., which should be k.^2 .* ... . There's quite a few errors around, but when you fix those it should work. – Chris Rackauckas Jul 04 '19 at 11:30
  • I actually already had this `.` in my code, I just forgot it here. I figured out though, that the actual problem is/was related to `k`. I had a wrong understanding of how to set up the wavenumbers. Doing it with `k = points(F, n)` works. What I am wondering now is though, how exactly I have to set it up, so that I can specify a certain domain size. Like lets say: $x\in[-L/2 ; L/2]$. Is there a way with ApproxFun without doing it manually? And even if I do it manually, I struggle to find information of the exact order (first negative wavenumber (in which order?), then positive numbers?) – max xilian Jul 05 '19 at 12:02
  • I mean the order that is expected/used by ApproxFun. Unfortunately the ApproxFun documentation is not particually helpful for me – max xilian Jul 05 '19 at 12:11
  • 1
    You can use the Derivative(S,2) operator from ApproxFun there instead, which would be a diagonal matrix of the `-k^2`? – Chris Rackauckas Jul 05 '19 at 12:21
  • With `n` being the number of points used, this results just in an array `[0,1,1,2,2,3,3,..... n/2 - 1, n/2 -1, n/2]`. Or do I have to change the interval in the `Fourier()` call to specify the x-domain size? – max xilian Jul 05 '19 at 12:47
  • I mean, `k` should be something like (2*pi/L) * [ - N/2 ..... N/2 ], shouldn't it? Just not sure in which order ApproxFun wants that with the negative and positive wavenumbers. – max xilian Jul 05 '19 at 12:56
  • ApproxFun defines the Fourier space to be from 0 to 2pi, so you'll need to adjust it if that's not true I guess. – Chris Rackauckas Jul 05 '19 at 14:06
  • Mh... I dont get it. The ApproxFun documentation is unfortunately also not particullary helpful. Thank you for help, though! The equations solves now on the unit-domain. So far I am not able to reproduce all possible regimes with their different dynamics but some look ok. I will update the original post later. – max xilian Jul 05 '19 at 15:20
  • I tried `F = Fourier(-(n/2+1)*(2pi)/L..(n/2-1)*(2pi)/L)`, but the results look wrong, they have some discontinuities – max xilian Jul 05 '19 at 15:47
  • Or is there any good resource on ApproxFun in general? I can't get a lot of insights from the documentation. – max xilian Jul 05 '19 at 16:17