-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathlorenz_example.py
160 lines (126 loc) · 5.48 KB
/
lorenz_example.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
# -*- coding: utf-8 -*-
#
# Usage example: solve the Lorenz system using a custom Python kernel.
from __future__ import division, print_function
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d.axes3d import Axes3D
from pydgq.solver.types import DTYPE
from pydgq.solver.galerkin import init
from pydgq.solver.kernel_interface import PythonKernel
import pydgq.solver.odesolve
from pydgq.utils.discontify import discontify # for plotting dG results
#####################
# config
#####################
q = 2 # degree of basis for dG and cG
# How many visualization (interpolation) points to use within each timestep for Galerkin methods.
#
# Note that the dG solution has the best accuracy at the endpoint of the timestep;
# to compare apples-to-apples with classical integrators, this should be set to 1.
#
# Larger values (e.g. 11) are useful for visualizing the behavior of the dG solution inside
# the timestep (something the classical integrators do not model at all).
#
nt_vis_galerkin = 11
nt = 3500 # number of timesteps
dt = 0.1 # timestep size
save_from = 0 # see pydgq.solver.odesolve.ivp()
#####################
# custom kernel
#####################
# A kernel for the Lorenz system.
#
# The custom kernel only needs to override callback(); even __init__ is not strictly needed,
# unless adding some custom parameters (like here).
#
class LorenzKernel(PythonKernel):
def __init__(self, rho, sigma, beta):
# super
PythonKernel.__init__(self, n=3)
# custom init
self.rho = rho
self.sigma = sigma
self.beta = beta
def callback(self, t):
# dxdt = sigma (y - x)
# dydt = x (rho - z) - y
# dzdt = x y - beta z
self.out[0] = self.sigma * (self.w[1] - self.w[0])
self.out[1] = self.w[0] * (self.rho - self.w[2]) - self.w[1]
self.out[2] = self.w[0] * self.w[1] - self.beta * self.w[2] # this is nonlinear, so we can't use a built-in linear kernel
#####################
# main program
#####################
def test(integrator, nt_vis):
n_saved_timesteps = pydgq.solver.odesolve.n_saved_timesteps( nt, save_from )
result_len = pydgq.solver.odesolve.result_len( nt, save_from, interp=nt_vis )
startj,endj = pydgq.solver.odesolve.timestep_boundaries( nt, save_from, interp=nt_vis )
n = 3 # the Lorenz system has 3 DOFs
# we use the same values as in the example at https://en.wikipedia.org/wiki/Lorenz_system
#
rho = 28.
sigma = 10.
beta = 8./3.
# set IC
#
w0 = np.empty( (n,), dtype=DTYPE, order="C" )
w0[0] = 0.
w0[1] = 2.
w0[2] = 20.
# instantiate kernel
rhs = LorenzKernel(rho=rho, sigma=sigma, beta=beta)
# create output arrays
ww = None #np.empty( (result_len,n), dtype=DTYPE, order="C" ) # result array for w; if None, will be created by ivp()
ff = np.empty( (result_len,n), dtype=DTYPE, order="C" ) # optional, result array for w', could be None
fail = np.empty( (n_saved_timesteps,), dtype=np.intc, order="C" ) # optional, fail flag for each timestep, could be None
# solve problem
ww,tt = pydgq.solver.odesolve.ivp( integrator=integrator, allow_denormals=False,
w0=w0, dt=dt, nt=nt,
save_from=save_from, interp=nt_vis,
rhs=rhs,
ww=ww, ff=ff, fail=fail,
maxit=10 )
# visualize
#
# http://matplotlib.org/mpl_toolkits/mplot3d/tutorial.html
#
print( "** Plotting solution **" )
fig = plt.figure(1)
plt.clf()
# Axes3D has a tendency to underestimate how much space it needs; it draws its labels
# outside the window area in certain orientations.
#
# This causes the labels to be clipped, which looks bad. We prevent this by creating the axes
# in a slightly smaller rect (leaving a margin). This way the labels will show - outside the Axes3D,
# but still inside the figure window.
#
# The final touch is to set the window background to a matching white, so that the
# background of the figure appears uniform.
#
fig.patch.set_color( (1,1,1) )
fig.patch.set_alpha( 1.0 )
x0y0wh = [ 0.02, 0.02, 0.96, 0.96 ] # left, bottom, width, height (here as fraction of subplot area)
ax = Axes3D(fig, rect=x0y0wh)
# show the discontinuities at timestep boundaries if using dG (and actually have something to draw within each timestep)
if integrator == "dG" and nt_vis > 1:
tt = discontify( tt, endj - 1, fill="nan" )
wtmp = np.empty( (tt.shape[0],n), dtype=DTYPE, order="C" )
for j in range(n):
# we need the copy() to get memory-contiguous data for discontify() to process
wtmp[:,j] = discontify( ww[:,j].copy(), endj - 1, fill="nan" )
ax.plot( wtmp[:,0], wtmp[:,1], wtmp[:,2], linewidth=0.5 )
else:
ax.plot( ww[:,0], ww[:,1], ww[:,2], linewidth=0.5 )
plt.grid(b=True, which="both")
plt.axis("tight")
ax.set_xlabel(r"$x$")
ax.set_ylabel(r"$y$")
ax.set_zlabel(r"$z$")
plt.suptitle(r"Lorenz system: $\rho = %g$, $\sigma = %g$, $\beta = %g$, $x_0 = %g$, $y_0 = %g$, $z_0 = %g$" % (rho, sigma, beta, w0[0], w0[1], w0[2]))
if __name__ == '__main__':
print("** Solving the Lorenz system **")
nt_vis = nt_vis_galerkin
init(q=q, method="dG", nt_vis=nt_vis, rule=None)
test(integrator="dG", nt_vis=nt_vis)
plt.show()