1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ Example 1D Multigrid solver
4
+ Written by Lubos Brieda for
5
+ https://www.particleincell.com/2018/multigrid-solver/
6
+
7
+ Fri Mar 9 21:47:15 2018
8
+ """
9
+
10
+ import numpy as np
11
+ import pylab as pl
12
+
13
+ #standard GS+SOR solver
14
+ def GSsolve (phi ,b ):
15
+ print ("**** GS SOLVE ****" )
16
+
17
+ #plot for solution vector
18
+ fig_sol = pl .figure ()
19
+ #plot analytical solution
20
+ pl .plot (x ,phi_true ,LineWidth = 4 ,color = 'red' ,LineStyle = "dashed" )
21
+ pl .title ('GS Solver: phi vs. x' )
22
+
23
+ #plot for error
24
+ fig_err = pl .figure ()
25
+ pl .title ('GS Solver: (phi-phi_true) vs. x' )
26
+
27
+ R_freq = 100 #how often should residue be computed
28
+ w = 1.4 #SOR acceleration factor
29
+
30
+ for it in range (100001 ):
31
+ phi [0 ] = phi [1 ] #neumann BC on x=0
32
+ for i in range (1 ,ni - 1 ): #Dirichlet BC at last node so skipping
33
+ g = 0.5 * (phi [i - 1 ] + phi [i + 1 ] - dx * dx * b [i ])
34
+ phi [i ] = phi [i ] + w * (g - phi [i ])
35
+
36
+ #compute residue to check for convergence
37
+ if (it % R_freq == 0 ):
38
+ r_i = phi [1 ]- phi [0 ]
39
+ r_sum = r_i * r_i
40
+ for i in range (1 , ni - 1 ):
41
+ r_i = (phi [i - 1 ]- 2 * phi [i ]+ phi [i + 1 ])/ (dx * dx ) - b [i ]
42
+ r_sum += r_i * r_i
43
+
44
+ norm = np .sqrt (r_sum )/ ni
45
+ if (norm < 1e-4 ):
46
+ print ("Converged after %d iterations" % it )
47
+ op_count = it * ni * 5 + (it / R_freq )* 5 * ni
48
+ print ("Operation count: %d" % op_count )
49
+ pl .figure (fig_sol .number )
50
+ pl .plot (x ,phi )
51
+ return op_count
52
+ break
53
+
54
+ if (it % 250 == 0 ):
55
+ pl .figure (fig_sol .number )
56
+ pl .plot (x ,phi )
57
+ pl .figure (fig_err .number )
58
+ pl .plot (x ,phi - phi_true )
59
+ #print("it: %d, norm: %g"%(it, norm))
60
+ return - 1
61
+
62
+ #multigrid solver
63
+ def MGsolve (phi ,b ):
64
+ global eps_f , eps_c , R_f , R_c
65
+ print ("**** MULTIGRID SOLVE ****" )
66
+ ni = len (phi )
67
+ ni_c = ni >> 1 #divide by 2
68
+ dx_c = 2 * dx
69
+
70
+ R_f = np .zeros (ni )
71
+ R_c = np .zeros (ni_c )
72
+ eps_c = np .zeros (ni_c )
73
+ eps_f = np .zeros (ni )
74
+
75
+ pl .figure ()
76
+ #plot analytical solution
77
+ pl .plot (x ,phi_true ,LineWidth = 4 ,color = 'red' ,LineStyle = "dashed" )
78
+ pl .title ('Multigrid Solver: phi vs. x' )
79
+
80
+ for it in range (10001 ):
81
+
82
+ #number of steps to iterate at the finest level
83
+ inner_its = 1
84
+
85
+ #number of steps to iterate at the coarse level
86
+ inner2_its = 50
87
+ w = 1.4
88
+
89
+ #1) perform one or more iterations on fine mesh
90
+ for n in range (inner_its ):
91
+ phi [0 ] = phi [1 ]
92
+ for i in range (1 ,ni - 1 ):
93
+ g = 0.5 * (phi [i - 1 ] + phi [i + 1 ] - dx * dx * b [i ])
94
+ phi [i ] = phi [i ] + w * (g - phi [i ])
95
+
96
+ #2) compute residue on the fine mesh, R = A*phi - b
97
+ for i in range (1 ,ni - 1 ):
98
+ R_f [i ] = (phi [i - 1 ]- 2 * phi [i ]+ phi [i + 1 ])/ (dx * dx ) - b [i ]
99
+ R_f [0 ] = (phi [0 ] - phi [1 ])/ dx #neumann boundary
100
+ R_f [- 1 ] = phi [- 1 ] - 0 #dirichlet boundary
101
+
102
+ #2b) check for termination
103
+ r_sum = 0
104
+ for i in range (ni ):
105
+ r_sum += R_f [i ]* R_f [i ]
106
+ norm = np .sqrt (r_sum )/ ni
107
+ if (norm < 1e-4 ):
108
+ print ("Converged after %d iterations" % it )
109
+ print ("This is %d solution iterations on the fine mesh" % (it * inner_its ))
110
+ op_count_single = (inner_its * ni * 5 + ni * 5 + (ni >> 1 ) +
111
+ inner2_its * (ni >> 1 )* 5 + ni + ni )
112
+ print ("Operation count: %d" % (op_count_single * it ))
113
+ pl .plot (x ,phi )
114
+ return op_count_single * it
115
+ break
116
+
117
+ #3) restrict residue to the coarse mesh
118
+ for i in range (2 ,ni - 1 ,2 ):
119
+ R_c [i >> 1 ] = 0.25 * (R_f [i - 1 ] + 2 * R_f [i ] + R_f [i + 1 ])
120
+ R_c [0 ] = R_f [0 ]
121
+
122
+ #4) perform few iteration of the correction vector on the coarse mesh
123
+ eps_c [:] = 0
124
+ for n in range (inner2_its ):
125
+ eps_c [0 ] = eps_c [1 ] + dx_c * R_c [0 ]
126
+ for i in range (1 ,ni_c - 1 ):
127
+ g = 0.5 * (eps_c [i - 1 ] + eps_c [i + 1 ] - dx_c * dx_c * R_c [i ])
128
+ eps_c [i ] = eps_c [i ] + w * (g - eps_c [i ])
129
+
130
+ #5) interpolate eps to fine mesh
131
+ for i in range (1 ,ni - 1 ):
132
+ if (i % 2 == 0 ): #even nodes, overlapping coarse mesh
133
+ eps_f [i ] = eps_c [i >> 1 ]
134
+ else :
135
+ eps_f [i ] = 0.5 * (eps_c [i >> 1 ]+ eps_c [(i >> 1 )+ 1 ])
136
+ eps_f [0 ] = eps_c [0 ]
137
+
138
+ #6) update solution on the fine mesh
139
+ for i in range (0 ,ni - 1 ):
140
+ phi [i ] = phi [i ] - eps_f [i ]
141
+
142
+ if (it % int (25 / inner_its ) == 0 ):
143
+ pl .plot (x ,phi )
144
+ # print("it: %d, norm: %g"%(it, norm))
145
+
146
+ return - 1
147
+
148
+ ni = 128
149
+
150
+ #domain length and mesh spacing
151
+ L = 1
152
+ dx = L / (ni - 1 )
153
+
154
+ x = np .arange (ni )* dx
155
+
156
+ phi = np .ones (ni )* 0
157
+ phi [0 ] = 0
158
+ phi [ni - 1 ] = 0
159
+ phi_bk = np .zeros_like (phi )
160
+ phi_bk [:] = phi [:]
161
+
162
+
163
+ #set RHS
164
+ A = 10
165
+ k = 4
166
+ b = A * np .sin (k * 2 * np .pi * x / L )
167
+
168
+ #analytical solution
169
+ C1 = A / (k * 2 * np .pi / L )
170
+ C2 = - C1 * L
171
+ phi_true = (- A * np .sin (k * 2 * np .pi * x / L )* (1 / (k * 2 * np .pi / L ))** 2 + C1 * x + C2 )
172
+
173
+ pl .figure (1 )
174
+ pl .title ('Right Hand Side' )
175
+ pl .plot (x ,b )
176
+
177
+ op_gs = GSsolve (phi ,b )
178
+
179
+ #reset solution
180
+ phi = phi_bk
181
+ op_mg = MGsolve (phi ,b )
182
+
183
+ print ("Operation count ratio (GS/MG): %g" % (op_gs / op_mg ))
184
+
185
+
186
+
0 commit comments