Skip to content

Commit cc66507

Browse files
Multigrid solver
Python 1D multigrid solver for https://www.particleincell.com/2018/multigrid-solver/
1 parent ce0e9b9 commit cc66507

File tree

1 file changed

+186
-0
lines changed

1 file changed

+186
-0
lines changed

multigrid/multigrid.py

+186
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
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

Comments
 (0)